@objectstack/service-automation 9.8.0 → 9.9.1
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/dist/index.cjs +32 -6
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +19 -0
- package/dist/index.d.ts +19 -0
- package/dist/index.js +32 -6
- package/dist/index.js.map +1 -1
- package/package.json +4 -4
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/engine.ts","../src/suspended-run-store.ts","../src/sys-automation-run.object.ts","../src/builtin/logic-nodes.ts","../src/builtin/loop-node.ts","../src/builtin/template.ts","../src/builtin/parallel-node.ts","../src/builtin/try-catch-node.ts","../src/builtin/crud-nodes.ts","../src/builtin/screen-nodes.ts","../src/builtin/http-nodes.ts","../src/builtin/connector-nodes.ts","../src/builtin/notify-node.ts","../src/builtin/wait-node.ts","../src/builtin/subflow-node.ts","../src/builtin/map-node.ts","../src/builtin/index.ts","../src/plugin.ts"],"sourcesContent":["// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\nimport type { FlowParsed, FlowNodeParsed, FlowEdgeParsed } from '@objectstack/spec/automation';\nimport type { ExecutionLog, ActionDescriptor } from '@objectstack/spec/automation';\nimport type { AutomationContext, AutomationResult, ResumeSignal, IAutomationService, ScreenSpec } from '@objectstack/spec/contracts';\nimport type { Logger } from '@objectstack/spec/contracts';\nimport { FlowSchema, FLOW_STRUCTURAL_NODE_TYPES, validateControlFlow, findRegionEntry, defineActionDescriptor } from '@objectstack/spec/automation';\nimport type { FlowRegionParsed } from '@objectstack/spec/automation';\nimport type { Connector } from '@objectstack/spec/integration';\nimport { ConnectorSchema } from '@objectstack/spec/integration';\n// Static import (not a lazy `require`): the engine ships as ESM (\"type\":\"module\"),\n// where a CommonJS `require('@objectstack/formula')` resolves to tsup's throwing\n// `__require` stub. That threw on every CEL evaluation and the catch below\n// silently returned `false`, so EVERY start-node / edge condition (record-change\n// `previous.*`, `budget > 100000`, …) skipped its flow. A static import binds the\n// engine at module load in both ESM and CJS builds.\nimport { ExpressionEngine, validateExpression } from '@objectstack/formula';\n\n// ─── Node Executor Interface (Plugin Extension Point) ───────────────\n\n/**\n * Each node type corresponds to a NodeExecutor.\n * Third-party plugins only need to implement this interface and register\n * it with the engine to extend automation capabilities.\n */\nexport interface NodeExecutor {\n /** Registry node type (built-in id or plugin-defined) */\n readonly type: string;\n\n /**\n * Optional ADR-0018 action descriptor. When present, it is published into\n * the engine's action registry and surfaced via {@link AutomationEngine.getActionDescriptors}\n * — feeding flow validation and the designer palette. Plugins SHOULD publish\n * one so their node appears in the palette and validates as a legal flow node.\n */\n readonly descriptor?: ActionDescriptor;\n\n /**\n * Execute a node\n * @param node - Current node definition\n * @param variables - Flow variable context (read/write)\n * @param context - Trigger context\n * @returns Execution result (may include output data, branch conditions, etc.)\n */\n execute(\n node: FlowNodeParsed,\n variables: Map<string, unknown>,\n context: AutomationContext,\n ): Promise<NodeExecutionResult>;\n}\n\nexport interface NodeExecutionResult {\n success: boolean;\n output?: Record<string, unknown>;\n error?: string;\n /** Used by decision nodes — returns the selected branch label */\n branchLabel?: string;\n /**\n * ADR-0019 durable pause. When `true`, the node has done its on-entry work\n * (e.g. opened an approval request) and the run should **suspend** here: the\n * engine persists a continuation, stops traversal, and `execute()` returns\n * `{ status: 'paused', runId }`. The run is continued later via\n * {@link AutomationEngine.resume}. Any `output` is written to variables\n * before suspending. The node reads its own run id from the `$runId`\n * flow variable so it can map the run to external state.\n */\n suspend?: boolean;\n /**\n * Optional correlation key surfaced on the suspended-run record (e.g. an\n * approval request id). For observability / lookup; not required to resume.\n */\n correlation?: string;\n /**\n * Screen to render — set by a `screen` node that suspends to collect input.\n * Surfaced on the paused {@link AutomationResult} so a UI runner can render\n * the form and `resume()` with the values.\n */\n screen?: ScreenSpec;\n /**\n * #1479: step logs produced inside the node's structured region(s). A\n * container node (`loop` / `parallel` / `try_catch`) collects the\n * {@link AutomationEngine.runRegion} return value(s) here; {@link AutomationEngine.executeNode}\n * appends them to the parent run log right after the container's own step,\n * so per-iteration / per-branch body steps surface in run observability.\n */\n childSteps?: StepLogEntry[];\n}\n\n// ─── Trigger Interface (Plugin Extension Point) ─────────────────────\n\n/**\n * A normalized description of *what* fires a flow, derived by the engine from\n * the flow's `start` node and handed to the matching {@link FlowTrigger} when a\n * flow is activated. Concrete triggers (record-change, schedule, …) read the\n * fields they care about and ignore the rest.\n *\n * The engine — not the trigger — owns parsing the start node, so trigger\n * plugins stay decoupled from flow-definition internals (mirrors how\n * `connector_action` keeps connectors decoupled from node config).\n */\nexport interface FlowTriggerBinding {\n /** Flow this binding activates. */\n readonly flowName: string;\n /** record-change: the object whose mutations fire the flow. */\n readonly object?: string;\n /** record-change: the start node's `triggerType` (e.g. 'record-after-update'). */\n readonly event?: string;\n /**\n * Optional trigger predicate copied from the start node's `condition`. The\n * engine evaluates it before running the flow; triggers may ignore it.\n */\n readonly condition?: string | { dialect?: string; source?: string; ast?: unknown };\n /** schedule: cron/interval descriptor (parsed but not yet acted on here). */\n readonly schedule?: unknown;\n /** The raw start-node `config`, for trigger-specific fields not modeled above. */\n readonly config?: Record<string, unknown>;\n}\n\n/**\n * Trigger interface. Schedule/Event/API triggers are registered via plugins.\n *\n * The engine completes the wiring: when a flow whose start node maps to this\n * trigger's {@link type} is registered (or when this trigger is registered\n * after such flows already exist), the engine calls {@link start} with the\n * parsed {@link FlowTriggerBinding} and a `callback` that runs the flow. The\n * trigger subscribes to its event source (e.g. an ObjectQL lifecycle hook) and\n * invokes `callback(ctx)` when it fires. {@link stop} tears that subscription\n * down when the flow is unregistered/disabled or the trigger is removed.\n */\nexport interface FlowTrigger {\n readonly type: string;\n start(binding: FlowTriggerBinding, callback: (ctx: AutomationContext) => Promise<void>): void;\n stop(flowName: string): void;\n}\n\n// ─── Connector Registry (Plugin Extension Point) ────────────────────\n\n/**\n * Context handed to a connector action handler. Carries the live flow variable\n * map and the trigger context so a handler can read prior-node output, plus a\n * logger. The platform ships the registry + the `connector_action` dispatch\n * node (baseline, ADR-0018 §Addendum); *concrete* connectors — `connector-rest`,\n * `connector-slack`, … — are plugins that register handlers here.\n */\nexport interface ConnectorActionContext {\n readonly variables: Map<string, unknown>;\n readonly automation: AutomationContext;\n readonly logger: Logger;\n}\n\n/**\n * A handler for one connector action. Receives the (already-resolved) input\n * mapped from the flow node and returns the action's output, which the\n * `connector_action` node writes back into flow variables.\n */\nexport type ConnectorActionHandler = (\n input: Record<string, unknown>,\n ctx: ConnectorActionContext,\n) => Promise<Record<string, unknown>>;\n\n/**\n * A connector registered on the engine: its validated {@link Connector}\n * definition plus the handler for each action it declares.\n */\nexport interface RegisteredConnector {\n readonly def: Connector;\n readonly handlers: Record<string, ConnectorActionHandler>;\n}\n\n/**\n * Context handed to a named handler function invoked from a `script` node\n * (#1870). Mirrors {@link ConnectorActionContext} but carries the node's mapped\n * `input` so the function reads its arguments without reaching into the raw\n * variable map. The function's return value becomes the node output.\n */\nexport interface FlowFunctionContext {\n /** Inputs mapped from the node's `config.inputs` (already in scope). */\n readonly input: Record<string, unknown>;\n /** Live flow variable map — read prior-node output / write results. */\n readonly variables: Map<string, unknown>;\n /** The flow execution / trigger context. */\n readonly automation: AutomationContext;\n readonly logger: Logger;\n}\n\n/**\n * A named handler function callable from a `script` node. Returns the node's\n * output (any JSON-serializable value); returning `undefined` yields an empty\n * output. Authored packages contribute these via `defineStack({ functions })`,\n * which the host bridges in through {@link AutomationEngine.setFunctionResolver}.\n */\nexport type FlowFunctionHandler = (ctx: FlowFunctionContext) => unknown | Promise<unknown>;\n\n/**\n * Resolves a function name to its handler. Injected by the host (the automation\n * plugin bridges it to ObjectQL's `resolveFunction`, fed by `bundle.functions`),\n * so the engine stays decoupled from any specific function registry. Returns\n * `undefined` for an unknown name, letting the `script` node fail the step\n * loudly instead of silently no-op'ing (#1870).\n */\nexport type FlowFunctionResolver = (name: string) => FlowFunctionHandler | undefined;\n\n/**\n * A designer-facing view of one connector action — identity + its JSON-Schema\n * input/output. The runtime handler is intentionally omitted; this is metadata.\n */\nexport interface ConnectorActionDescriptor {\n readonly key: string;\n readonly label: string;\n readonly description?: string;\n readonly inputSchema?: Record<string, unknown>;\n readonly outputSchema?: Record<string, unknown>;\n}\n\n/**\n * A designer-facing descriptor for a registered connector: its identity plus\n * the actions it exposes. Served by `GET /api/v1/automation/connectors` so the\n * flow designer can populate the `connector_action` node's connector → action\n * → input pickers (ADR-0018 §Addendum, ADR-0022). Mirrors `ActionDescriptor`'s\n * role for node types, but for the connector registry.\n */\nexport interface ConnectorDescriptor {\n readonly name: string;\n readonly label: string;\n readonly type: string;\n readonly description?: string;\n readonly icon?: string;\n readonly actions: ConnectorActionDescriptor[];\n}\n\n// ─── Core Automation Engine ─────────────────────────────────────────\n\n/**\n * Default ceiling on the in-memory execution-log ring buffer. Execution logs are\n * process-local and diagnostic only (launch-readiness.md P1-2); the buffer keeps\n * the most recent N entries and evicts the oldest, so memory is bounded\n * regardless of throughput. Operators tune the window via\n * {@link AutomationServicePluginOptions.maxLogSize}. Durable, queryable run\n * history is the DB-backed `sys_automation_run` store (a post-GA HA item), not\n * this buffer.\n */\nexport const DEFAULT_MAX_EXECUTION_LOG_SIZE = 1000;\n\n/** Construction options for {@link AutomationEngine}. */\nexport interface AutomationEngineOptions {\n /**\n * Max in-memory execution-log entries retained (ring buffer; oldest evicted).\n * Defaults to {@link DEFAULT_MAX_EXECUTION_LOG_SIZE}. Must be > 0.\n */\n maxLogSize?: number;\n}\n\n/**\n * Execution step log entry. Part of a {@link SuspendedRun}'s persisted state, so\n * it survives serialization to a durable {@link SuspendedRunStore}.\n */\nexport interface StepLogEntry {\n nodeId: string;\n nodeType: string;\n nodeLabel?: string;\n status: 'success' | 'failure' | 'skipped';\n startedAt: string;\n completedAt?: string;\n durationMs?: number;\n error?: { code: string; message: string; stack?: string };\n /**\n * #1479: structured-region grouping. When a step ran inside a `loop` /\n * `parallel` / `try_catch` body region, these tag it with its **immediate**\n * container so run observability can distinguish per-iteration / per-branch\n * body steps from top-level ones. Set by {@link AutomationEngine.runRegion}\n * (innermost wins — never overwritten as steps bubble through nested regions).\n */\n parentNodeId?: string;\n /** Zero-based loop iteration or parallel branch index of the enclosing region. */\n iteration?: number;\n /** Which region kind the step ran in: `loop-body` | `parallel-branch` | `try` | `catch`. */\n regionKind?: string;\n}\n\n/**\n * Internal execution log entry — compatible with ExecutionLog from spec.\n */\ninterface ExecutionLogEntry {\n id: string;\n flowName: string;\n flowVersion?: number;\n status: ExecutionLog['status'];\n startedAt: string;\n completedAt?: string;\n durationMs?: number;\n trigger: { type: string; userId?: string; object?: string; recordId?: string };\n steps: StepLogEntry[];\n variables?: Record<string, unknown>;\n output?: unknown;\n error?: string;\n}\n\n/**\n * Internal sentinel thrown by {@link AutomationEngine.executeNode} when a node\n * signals `suspend`. It unwinds the synchronous DAG recursion up to\n * `execute()` / `resume()`, which converts it into a persisted continuation\n * rather than a failed run. (Not exported — callers see `status: 'paused'`.)\n *\n * NOTE: suspend is supported on the serial / main execution path. A node that\n * suspends inside a `Promise.all` parallel branch will unwind that branch, but\n * sibling parallel branches already in flight are not cancelled — durable\n * pause across parallel gateways is out of scope for ADR-0019 M1.\n */\nclass FlowSuspendSignal {\n readonly __flowSuspend = true as const;\n constructor(readonly nodeId: string, readonly correlation?: string, readonly screen?: ScreenSpec) {}\n}\n\nfunction isSuspendSignal(err: unknown): err is FlowSuspendSignal {\n return typeof err === 'object' && err !== null && (err as FlowSuspendSignal).__flowSuspend === true;\n}\n\n/**\n * A run paused at a node, awaiting {@link AutomationEngine.resume} (ADR-0019).\n *\n * Held in an in-memory hot cache and — when a {@link SuspendedRunStore} is\n * configured — mirrored to durable storage so the pause survives a process\n * restart. Every field is JSON-serializable (the engine's variable `Map` is\n * snapshotted as a plain object) so the whole record round-trips through a\n * store.\n */\nexport interface SuspendedRun {\n runId: string;\n flowName: string;\n flowVersion?: number;\n /** The node the run paused at; resume continues from its out-edges. */\n nodeId: string;\n /** Snapshot of the flow variable map at suspend time. */\n variables: Record<string, unknown>;\n steps: StepLogEntry[];\n context: AutomationContext;\n startedAt: string;\n startTime: number;\n correlation?: string;\n /** Screen the run paused on (screen-flow runtime), for re-fetch + UI render. */\n screen?: ScreenSpec;\n}\n\n/**\n * Pluggable durable store for suspended runs (ADR-0019). The engine persists a\n * {@link SuspendedRun} on suspend and deletes it on terminal completion; on\n * {@link AutomationEngine.resume} of a run not in the in-memory cache (e.g.\n * after a process restart) it rehydrates from here.\n *\n * The default is purely in-memory (no store); a host wires a DB-backed store\n * (`ObjectStoreSuspendedRunStore`, on `sys_automation_run`) for production /\n * serverless deployments where the process hibernates between suspend and\n * resume.\n */\nexport interface SuspendedRunStore {\n /** Persist (insert or replace) a suspended run. */\n save(run: SuspendedRun): Promise<void>;\n /** Load a suspended run by id, or `null` if not stored. */\n load(runId: string): Promise<SuspendedRun | null>;\n /** Remove a suspended run's durable record (idempotent). */\n delete(runId: string): Promise<void>;\n /** List all currently-stored suspended runs. */\n list(): Promise<SuspendedRun[]>;\n}\n\nexport class AutomationEngine implements IAutomationService {\n /**\n * ADR-0044: maximum times a single node may be (re-)entered at the top\n * level of one run before the engine aborts it as a runaway back-edge\n * loop. Generous on purpose — the product guard (`maxRevisions`) sits\n * orders of magnitude lower.\n */\n static readonly MAX_NODE_REENTRIES = 100;\n\n private flows = new Map<string, FlowParsed>();\n private flowEnabled = new Map<string, boolean>();\n private flowVersionHistory = new Map<string, Array<{ version: number; definition: FlowParsed; createdAt: string }>>();\n private nodeExecutors = new Map<string, NodeExecutor>();\n private actionDescriptors = new Map<string, ActionDescriptor>();\n private triggers = new Map<string, FlowTrigger>();\n /**\n * Flows currently wired to a trigger, keyed by flow name → the trigger\n * `type` that owns the binding. Used to avoid double-binding and to know\n * which trigger to `stop()` when a flow is unregistered/disabled.\n */\n private boundFlowTriggers = new Map<string, string>();\n /** Connectors registered by integration plugins, keyed by connector name (ADR-0018 §Addendum). */\n private connectors = new Map<string, RegisteredConnector>();\n /** Bridge to the host function registry for `script`-node calls (#1870), if wired. */\n private functionResolver: FlowFunctionResolver | null = null;\n private executionLogs: ExecutionLogEntry[] = [];\n private readonly maxLogSize: number;\n private logger: Logger;\n /**\n * Runs paused at a node, keyed by runId (ADR-0019). In-memory hot cache —\n * mirrored to {@link store} when one is configured, so a pause survives a\n * process restart. See {@link SuspendedRun}.\n */\n private suspendedRuns = new Map<string, SuspendedRun>();\n /**\n * Optional durable backing for {@link suspendedRuns}. When set, suspended\n * runs are persisted on suspend and rehydrated on resume after a restart;\n * when absent, behaviour is purely in-memory (the historical default).\n */\n private store?: SuspendedRunStore;\n /**\n * Run ids currently mid-resume — an in-process idempotency guard so a\n * duplicate `resume(runId)` can't re-enter and double-run side effects.\n */\n private resuming = new Set<string>();\n\n constructor(logger: Logger, store?: SuspendedRunStore, options?: AutomationEngineOptions) {\n this.logger = logger;\n this.store = store;\n this.maxLogSize = options?.maxLogSize ?? DEFAULT_MAX_EXECUTION_LOG_SIZE;\n }\n\n /**\n * Attach (or replace) the durable {@link SuspendedRunStore}. Used by the\n * service plugin to upgrade the engine to DB-backed persistence once the\n * ObjectQL engine is available (the engine is constructed earlier, during\n * `init`, before services are wired).\n */\n setSuspendedRunStore(store: SuspendedRunStore): void {\n this.store = store;\n }\n\n /**\n * Generate a process-unique run id. Includes a random component so ids do\n * not collide with runs persisted by a previous process lifetime (a plain\n * incrementing counter would reissue `run_1` after a restart, clashing with\n * a still-suspended durable run).\n */\n private nextRunId(): string {\n const g = globalThis as { crypto?: { randomUUID?: () => string } };\n const rand = g.crypto?.randomUUID\n ? g.crypto.randomUUID()\n : `${Date.now().toString(36)}${Math.random().toString(36).slice(2, 10)}`;\n return `run_${rand}`;\n }\n\n /**\n * Persist a suspended run to the in-memory cache and (best-effort) the\n * durable store. A store failure is logged but does not fail the run — the\n * in-memory copy still allows in-process resume; only cross-restart\n * durability is lost.\n */\n private async persistSuspendedRun(run: SuspendedRun): Promise<void> {\n this.suspendedRuns.set(run.runId, run);\n if (this.store) {\n try {\n await this.store.save(run);\n } catch (err) {\n this.logger.warn(\n `[automation] failed to persist suspended run '${run.runId}' to durable store (kept in memory only): ${(err as Error).message}`,\n );\n }\n }\n }\n\n /**\n * Drop a suspended run from the in-memory cache and (best-effort) the\n * durable store. Called once the run is claimed for resume or reaches a\n * terminal state.\n */\n private async forgetSuspendedRun(runId: string): Promise<void> {\n this.suspendedRuns.delete(runId);\n if (this.store) {\n try {\n await this.store.delete(runId);\n } catch (err) {\n this.logger.warn(\n `[automation] failed to delete suspended run '${runId}' from durable store: ${(err as Error).message}`,\n );\n }\n }\n }\n\n // ── Plugin Extension API ──────────────────────────────\n\n /** Register a node executor (called by plugins) */\n registerNodeExecutor(executor: NodeExecutor): void {\n if (this.nodeExecutors.has(executor.type)) {\n this.logger.warn(`Node executor '${executor.type}' replaced`);\n }\n this.nodeExecutors.set(executor.type, executor);\n\n // Publish the ADR-0018 action descriptor into the registry, so the\n // type validates as a legal flow node and appears in the designer\n // palette. A descriptor's `type` should match the executor's; we key\n // on the descriptor's `type` and warn on mismatch rather than silently\n // diverging.\n if (executor.descriptor) {\n const descriptorType = executor.descriptor.type;\n if (descriptorType !== executor.type) {\n this.logger.warn(\n `Node executor '${executor.type}' publishes a descriptor for type '${descriptorType}' — registering under both.`,\n );\n }\n this.actionDescriptors.set(descriptorType, executor.descriptor);\n }\n\n this.logger.info(`Node executor registered: ${executor.type}`);\n }\n\n /**\n * Register a **deprecated alias** of a canonical node type (ADR-0018 M3).\n *\n * The alias is a real registered executor, so old saved flows whose nodes\n * use the alias type keep validating and running with no migration. At\n * execute time it delegates to the canonical executor (resolved live, so the\n * canonical may be registered before or after the alias), logging a one-time\n * deprecation warning. Its published descriptor is flagged `deprecated` +\n * `aliasOf` so the designer palette can hide or mark it while the canonical\n * type is the one offered for new authoring.\n *\n * This is how ADR-0018 collapses the five outbound verbs onto `http` /\n * `notify`: `http_request` / `http_call` / `webhook` become aliases of\n * `http`.\n */\n registerNodeAlias(\n alias: string,\n canonicalType: string,\n meta?: { name?: string; category?: ActionDescriptor['category']; paradigms?: ActionDescriptor['paradigms']; needsOutbox?: boolean },\n ): void {\n const engine = this;\n let warned = false;\n this.registerNodeExecutor({\n type: alias,\n descriptor: defineActionDescriptor({\n type: alias,\n version: '1.0.0',\n name: meta?.name ?? alias,\n description: `Deprecated alias of '${canonicalType}' (ADR-0018 M3). Author new flows with '${canonicalType}'.`,\n category: meta?.category ?? 'io',\n source: 'builtin',\n paradigms: meta?.paradigms ?? ['flow', 'approval'],\n supportsRetry: true,\n needsOutbox: meta?.needsOutbox ?? false,\n deprecated: true,\n aliasOf: canonicalType,\n }),\n async execute(node, variables, context) {\n if (!warned) {\n warned = true;\n engine.logger.warn(\n `Node type '${alias}' is deprecated; use '${canonicalType}' (ADR-0018 M3). Existing flows keep running via the alias.`,\n );\n }\n const target = engine.nodeExecutors.get(canonicalType);\n if (!target) {\n return {\n success: false,\n error: `alias '${alias}' → '${canonicalType}': canonical executor not registered`,\n };\n }\n return target.execute(node, variables, context);\n },\n });\n this.logger.info(`Node alias registered: ${alias} → ${canonicalType} (deprecated)`);\n }\n\n /** Unregister a node executor (hot-unplug) */\n unregisterNodeExecutor(type: string): void {\n const executor = this.nodeExecutors.get(type);\n this.nodeExecutors.delete(type);\n // Drop the published descriptor (keyed by descriptor.type, which may\n // differ from the executor type).\n this.actionDescriptors.delete(type);\n if (executor?.descriptor) {\n this.actionDescriptors.delete(executor.descriptor.type);\n }\n this.logger.info(`Node executor unregistered: ${type}`);\n }\n\n /** Register a trigger (called by plugins) */\n registerTrigger(trigger: FlowTrigger): void {\n this.triggers.set(trigger.type, trigger);\n this.logger.info(`Trigger registered: ${trigger.type}`);\n // A trigger may be registered *after* its flows (e.g. AutomationServicePlugin\n // pulls flows at start(); a trigger plugin wires up on kernel:ready, which\n // fires later). Activate any already-registered flow that maps to this type.\n for (const name of this.flows.keys()) {\n if (this.boundFlowTriggers.has(name)) continue;\n const resolved = this.resolveTriggerBinding(name);\n if (resolved?.triggerType === trigger.type) {\n this.activateFlowTrigger(name);\n }\n }\n }\n\n /** Unregister a trigger (hot-unplug) */\n unregisterTrigger(type: string): void {\n // Tear down every flow bound to this trigger before dropping it.\n for (const [name, boundType] of [...this.boundFlowTriggers]) {\n if (boundType !== type) continue;\n try {\n this.triggers.get(type)?.stop(name);\n } catch (err) {\n this.logger.warn(`Trigger '${type}' stop('${name}') failed: ${(err as Error).message}`);\n }\n this.boundFlowTriggers.delete(name);\n }\n this.triggers.delete(type);\n this.logger.info(`Trigger unregistered: ${type}`);\n }\n\n /**\n * Derive a flow's trigger binding from its `start` node, or `undefined` if\n * the flow has no auto-trigger (manual / screen). The convention —\n * established by the showcase flows — is that the start node carries the\n * trigger details in its `config`: `{ objectName, triggerType, condition }`\n * for record-change, or a `schedule` descriptor for time-based flows.\n */\n private resolveTriggerBinding(\n flowName: string,\n ): { triggerType: string; binding: FlowTriggerBinding } | undefined {\n const flow = this.flows.get(flowName);\n if (!flow) return undefined;\n const startNode = flow.nodes.find(n => n.type === 'start');\n const config = (startNode?.config ?? {}) as Record<string, unknown>;\n const triggerType = typeof config.triggerType === 'string' ? config.triggerType : undefined;\n\n if (triggerType && triggerType.startsWith('record-')) {\n return {\n triggerType: 'record_change',\n binding: {\n flowName,\n object: typeof config.objectName === 'string' ? config.objectName : undefined,\n event: triggerType,\n condition: (config.condition as FlowTriggerBinding['condition']) ?? undefined,\n config,\n },\n };\n }\n\n if (config.schedule != null || flow.type === 'schedule') {\n return {\n triggerType: 'schedule',\n binding: { flowName, schedule: config.schedule, condition: (config.condition as FlowTriggerBinding['condition']) ?? undefined, config },\n };\n }\n\n // Inbound HTTP (ADR-0041 Tier 1): an `api` flow waits for an external\n // POST. The concrete trigger (`@objectstack/trigger-api`) mounts the\n // endpoint and enqueues; the binding's `config` carries the hook\n // details (`hookId`, `secret`) from the start node.\n if (flow.type === 'api' || triggerType === 'api') {\n return {\n triggerType: 'api',\n binding: { flowName, condition: (config.condition as FlowTriggerBinding['condition']) ?? undefined, config },\n };\n }\n\n return undefined;\n }\n\n /**\n * Bind a flow to its matching registered trigger (idempotent). No-op when\n * the flow has no trigger binding or no trigger is registered for its type\n * yet — {@link registerTrigger} re-attempts activation when one arrives.\n */\n private activateFlowTrigger(flowName: string): void {\n if (this.boundFlowTriggers.has(flowName)) return;\n const resolved = this.resolveTriggerBinding(flowName);\n if (!resolved) return;\n const trigger = this.triggers.get(resolved.triggerType);\n if (!trigger) return;\n try {\n trigger.start(resolved.binding, (ctx: AutomationContext) => this.execute(flowName, ctx).then(() => undefined));\n this.boundFlowTriggers.set(flowName, resolved.triggerType);\n this.logger.info(`Flow '${flowName}' bound to trigger '${resolved.triggerType}'`);\n } catch (err) {\n this.logger.warn(`Failed to bind flow '${flowName}' to trigger '${resolved.triggerType}': ${(err as Error).message}`);\n }\n }\n\n /** Unbind a flow from its trigger, if bound. */\n private deactivateFlowTrigger(flowName: string): void {\n const boundType = this.boundFlowTriggers.get(flowName);\n if (!boundType) return;\n try {\n this.triggers.get(boundType)?.stop(flowName);\n } catch (err) {\n this.logger.warn(`Trigger '${boundType}' stop('${flowName}') failed: ${(err as Error).message}`);\n }\n this.boundFlowTriggers.delete(flowName);\n }\n\n /** Active flow→trigger bindings (observability / tests). */\n getActiveTriggerBindings(): Array<{ flowName: string; triggerType: string }> {\n return [...this.boundFlowTriggers].map(([flowName, triggerType]) => ({ flowName, triggerType }));\n }\n\n /**\n * Register a connector (called by integration plugins, ADR-0018 §Addendum).\n * Validates the definition against {@link ConnectorSchema} and asserts every\n * declared action has a handler, so a half-wired connector fails loudly at\n * registration rather than silently at dispatch. Re-registering the same\n * name replaces (mirrors {@link registerNodeExecutor}).\n */\n registerConnector(def: Connector, handlers: Record<string, ConnectorActionHandler>): void {\n const parsed = ConnectorSchema.parse(def);\n for (const action of parsed.actions ?? []) {\n if (typeof handlers[action.key] !== 'function') {\n throw new Error(\n `Connector '${parsed.name}': action '${action.key}' is declared but no handler was provided`,\n );\n }\n }\n if (this.connectors.has(parsed.name)) {\n this.logger.warn(`Connector '${parsed.name}' replaced`);\n }\n this.connectors.set(parsed.name, { def: parsed, handlers });\n this.logger.info(\n `Connector registered: ${parsed.name} (${Object.keys(handlers).length} action handlers)`,\n );\n }\n\n /** Unregister a connector (hot-unplug). */\n unregisterConnector(name: string): void {\n this.connectors.delete(name);\n this.logger.info(`Connector unregistered: ${name}`);\n }\n\n /**\n * Resolve the handler for a connector action, used by the baseline\n * `connector_action` node. Returns `undefined` when the connector or action\n * is not registered, so the node can fail the step with a clear error.\n */\n resolveConnectorAction(connectorId: string, actionId: string): ConnectorActionHandler | undefined {\n return this.connectors.get(connectorId)?.handlers[actionId];\n }\n\n /**\n * Wire the engine to the host's named-function registry (#1870). The\n * automation plugin calls this in `start()` with a resolver backed by\n * ObjectQL's `resolveFunction` (populated from `bundle.functions` /\n * `defineStack({ functions })`), so a `script` node can invoke an\n * authored function by name. Passing `null` detaches the bridge.\n */\n setFunctionResolver(resolver: FlowFunctionResolver | null): void {\n this.functionResolver = resolver;\n }\n\n /**\n * Resolve a named function for a `script` node. Returns `undefined` when no\n * resolver is wired or the name is unregistered — the node then fails the\n * step with a clear error rather than silently no-op'ing.\n */\n resolveFunction(name: string): FlowFunctionHandler | undefined {\n return this.functionResolver?.(name) ?? undefined;\n }\n\n /** Get all registered connector names. */\n getRegisteredConnectors(): string[] {\n return [...this.connectors.keys()];\n }\n\n /**\n * Get a designer-facing descriptor for every registered connector — its\n * identity plus the actions it exposes (input/output JSON Schema). Backs\n * `GET /api/v1/automation/connectors` so the designer can fill the\n * `connector_action` node's connector / action / input pickers (ADR-0022).\n * Handlers are omitted — they are runtime code, not metadata.\n */\n getConnectorDescriptors(): ConnectorDescriptor[] {\n return [...this.connectors.values()].map(({ def }) => ({\n name: def.name,\n label: def.label,\n type: def.type,\n description: def.description,\n icon: def.icon,\n actions: (def.actions ?? []).map((a) => ({\n key: a.key,\n label: a.label,\n description: a.description,\n inputSchema: a.inputSchema,\n outputSchema: a.outputSchema,\n })),\n }));\n }\n\n /** Get all registered node types */\n getRegisteredNodeTypes(): string[] {\n return [...this.nodeExecutors.keys()];\n }\n\n /**\n * Get all published action descriptors (ADR-0018). Backs both flow\n * validation and the designer palette (`GET /api/v1/automation/actions`).\n * Only executors that published a descriptor appear here.\n */\n getActionDescriptors(): ActionDescriptor[] {\n return [...this.actionDescriptors.values()];\n }\n\n /** Get the action descriptor for a single node type, if published. */\n getActionDescriptor(type: string): ActionDescriptor | undefined {\n return this.actionDescriptors.get(type);\n }\n\n /** Get all registered trigger types */\n getRegisteredTriggerTypes(): string[] {\n return [...this.triggers.keys()];\n }\n\n // ── IAutomationService Contract Implementation ────────\n\n registerFlow(name: string, definition: unknown): void {\n const parsed = FlowSchema.parse(definition);\n\n // DAG cycle detection\n this.detectCycles(parsed);\n\n // ADR-0031 — validate structured control-flow constructs (loop bodies,\n // parallel branches, try/catch regions) are well-formed (single-entry/\n // single-exit, acyclic). Reject the malformed before it can run.\n validateControlFlow(parsed);\n\n // ADR-0018 §M1 — validate node types against the live action registry.\n // The protocol no longer gates `type` with a closed enum; membership is\n // checked here instead. Soft-fail (warn, don't throw): a flow authored\n // against a plugin that is currently disabled should still register, and\n // executeNode() already throws NO_EXECUTOR at run time for unknown types.\n this.validateNodeTypes(name, parsed);\n\n // ADR-0032 §Decision 1a — parse-validate every predicate at registration,\n // so a malformed condition (e.g. the #1491 `{record.x}` template-brace-in-\n // CEL mistake) is a LOUD registration error with the offending source,\n // not a silent runtime `false`. Hard-fail: a broken predicate is never\n // safe to run.\n this.validateFlowExpressions(name, parsed);\n\n // Version history management\n const history = this.flowVersionHistory.get(name) ?? [];\n history.push({\n version: parsed.version,\n definition: parsed,\n createdAt: new Date().toISOString(),\n });\n this.flowVersionHistory.set(name, history);\n\n this.flows.set(name, parsed);\n if (!this.flowEnabled.has(name)) {\n this.flowEnabled.set(name, true);\n }\n this.logger.info(`Flow registered: ${name} (version ${parsed.version})`);\n\n // Re-bind in case the definition changed its trigger, then (re)activate.\n this.deactivateFlowTrigger(name);\n if (this.flowEnabled.get(name) !== false) {\n this.activateFlowTrigger(name);\n }\n }\n\n unregisterFlow(name: string): void {\n this.deactivateFlowTrigger(name);\n this.flows.delete(name);\n this.flowEnabled.delete(name);\n this.flowVersionHistory.delete(name);\n this.logger.info(`Flow unregistered: ${name}`);\n }\n\n async listFlows(): Promise<string[]> {\n return [...this.flows.keys()];\n }\n\n async getFlow(name: string): Promise<FlowParsed | null> {\n return this.flows.get(name) ?? null;\n }\n\n async toggleFlow(name: string, enabled: boolean): Promise<void> {\n if (!this.flows.has(name)) {\n throw new Error(`Flow '${name}' not found`);\n }\n this.flowEnabled.set(name, enabled);\n this.logger.info(`Flow '${name}' ${enabled ? 'enabled' : 'disabled'}`);\n // A disabled flow should stop receiving trigger events; a re-enabled one\n // should resume. execute() also guards disabled flows, but unbinding\n // avoids firing the trigger (and its event-source subscription) at all.\n if (enabled) {\n this.activateFlowTrigger(name);\n } else {\n this.deactivateFlowTrigger(name);\n }\n }\n\n /** Get flow version history */\n getFlowVersionHistory(name: string): Array<{ version: number; definition: FlowParsed; createdAt: string }> {\n return this.flowVersionHistory.get(name) ?? [];\n }\n\n /** Rollback flow to a specific version */\n rollbackFlow(name: string, version: number): void {\n const history = this.flowVersionHistory.get(name);\n if (!history) {\n throw new Error(`Flow '${name}' has no version history`);\n }\n const entry = history.find(h => h.version === version);\n if (!entry) {\n throw new Error(`Version ${version} not found for flow '${name}'`);\n }\n this.flows.set(name, entry.definition);\n this.logger.info(`Flow '${name}' rolled back to version ${version}`);\n }\n\n async listRuns(flowName: string, options?: { limit?: number; cursor?: string }): Promise<ExecutionLogEntry[]> {\n const limit = options?.limit ?? 20;\n const logs = this.executionLogs.filter(l => l.flowName === flowName);\n return logs.slice(-limit).reverse();\n }\n\n async getRun(runId: string): Promise<ExecutionLogEntry | null> {\n return this.executionLogs.find(l => l.id === runId) ?? null;\n }\n\n async execute(flowName: string, context?: AutomationContext): Promise<AutomationResult> {\n const startTime = Date.now();\n const flow = this.flows.get(flowName);\n\n if (!flow) {\n return { success: false, error: `Flow '${flowName}' not found` };\n }\n\n // Check if flow is disabled\n if (this.flowEnabled.get(flowName) === false) {\n return { success: false, error: `Flow '${flowName}' is disabled` };\n }\n\n // Initialize variable context\n const variables = new Map<string, unknown>();\n if (flow.variables) {\n for (const v of flow.variables) {\n if (v.isInput && context?.params?.[v.name] !== undefined) {\n variables.set(v.name, context.params[v.name]);\n }\n }\n }\n // Inject trigger record. `$record` is the canonical handle; `record` is a\n // friendlier alias so templates/conditions can write `{record.title}` and\n // `record.status`. We also flatten the record's own fields to top-level\n // variables (so bare references like `status`/`budget` resolve in start\n // conditions and edge predicates) WITHOUT clobbering flow inputs already\n // seeded above. `previous` exposes the pre-update row for transition gates.\n if (context?.record) {\n variables.set('$record', context.record);\n variables.set('record', context.record);\n for (const [k, v] of Object.entries(context.record)) {\n if (!variables.has(k)) variables.set(k, v);\n }\n }\n if (context?.previous) {\n variables.set('previous', context.previous);\n }\n\n const runId = this.nextRunId();\n // Expose the run id to executors (ADR-0019): a pausing node (e.g. Approval)\n // reads `$runId` to map its external state back to this run for resume.\n variables.set('$runId', runId);\n // Expose flow identity to executors so externalized state (e.g. an\n // approval request row) can carry a human-readable origin. Captured in\n // the variable snapshot, so still present after a suspend/resume.\n variables.set('$flowName', flowName);\n variables.set('$flowLabel', flow.label ?? flowName);\n const startedAt = new Date().toISOString();\n const steps: StepLogEntry[] = [];\n\n try {\n // Find the start node\n const startNode = flow.nodes.find(n => n.type === 'start');\n if (!startNode) {\n return { success: false, error: 'Flow has no start node' };\n }\n\n // Trigger-condition gate. The start node's `condition` is the predicate\n // that decides whether the trigger event should launch this flow (e.g.\n // `status == \"done\" && previous.status != \"done\"`). The engine — not the\n // trigger — owns evaluating it, so every trigger type (record-change,\n // schedule, …) and a manual `execute()` share one gate. Plain-string\n // conditions are routed through CEL so bare field references resolve.\n const startCondition = (startNode.config as Record<string, unknown> | undefined)?.condition as\n | string\n | { dialect?: string; source?: string; ast?: unknown }\n | undefined;\n if (startCondition !== undefined && startCondition !== null && startCondition !== '') {\n const condExpr =\n typeof startCondition === 'string' ? { dialect: 'cel', source: startCondition } : startCondition;\n if (!this.evaluateCondition(condExpr, variables)) {\n this.logger.debug(`Flow '${flowName}' skipped: start condition not met`);\n return { success: true, output: { skipped: true, reason: 'condition_not_met' } };\n }\n }\n\n // Validate node input schemas before execution\n this.validateNodeInputSchemas(flow, variables);\n\n // DAG traversal execution\n await this.executeNode(startNode, flow, variables, context ?? {}, steps);\n\n // Collect output variables\n const output: Record<string, unknown> = {};\n if (flow.variables) {\n for (const v of flow.variables) {\n if (v.isOutput) {\n output[v.name] = variables.get(v.name);\n }\n }\n }\n\n const durationMs = Date.now() - startTime;\n\n // Record execution log\n this.recordLog({\n id: runId,\n flowName,\n flowVersion: flow.version,\n status: 'completed',\n startedAt,\n completedAt: new Date().toISOString(),\n durationMs,\n trigger: {\n type: context?.event ?? 'manual',\n userId: context?.userId,\n object: context?.object,\n },\n steps,\n output,\n });\n\n return {\n success: true,\n output,\n durationMs,\n };\n } catch (err: unknown) {\n // A node asked to suspend the run (ADR-0019 durable pause). Snapshot\n // the live state, record a `paused` log, and return the run id so the\n // caller can later `resume()` it. This is NOT a failure.\n if (isSuspendSignal(err)) {\n const durationMs = Date.now() - startTime;\n await this.persistSuspendedRun({\n runId,\n flowName,\n flowVersion: flow.version,\n nodeId: err.nodeId,\n variables: Object.fromEntries(variables),\n steps,\n context: context ?? {},\n startedAt,\n startTime,\n correlation: err.correlation,\n screen: err.screen,\n });\n this.recordLog({\n id: runId,\n flowName,\n flowVersion: flow.version,\n status: 'paused',\n startedAt,\n durationMs,\n trigger: {\n type: context?.event ?? 'manual',\n userId: context?.userId,\n object: context?.object,\n },\n steps,\n });\n return {\n success: true,\n status: 'paused',\n runId,\n durationMs,\n screen: err.screen,\n };\n }\n\n const errorMessage = err instanceof Error ? err.message : String(err);\n\n // Record failed execution log\n const durationMs = Date.now() - startTime;\n this.recordLog({\n id: runId,\n flowName,\n flowVersion: flow.version,\n status: 'failed',\n startedAt,\n completedAt: new Date().toISOString(),\n durationMs,\n trigger: {\n type: context?.event ?? 'manual',\n userId: context?.userId,\n object: context?.object,\n },\n steps,\n error: errorMessage,\n });\n\n // Error handling strategy\n if (flow.errorHandling?.strategy === 'retry') {\n return this.retryExecution(flowName, context, startTime, flow.errorHandling);\n }\n return {\n success: false,\n error: errorMessage,\n durationMs,\n };\n }\n }\n\n /**\n * Resume a run suspended at a node (ADR-0019 durable pause). Restores the\n * snapshotted variables, merges `signal.output` under the suspended node's\n * id, and continues traversal from that node's out-edges — optionally\n * restricted to the edge labelled `signal.branchLabel` (e.g. the approval\n * decision). The continuation may itself suspend again, in which case this\n * returns `{ status: 'paused', runId }` afresh.\n *\n * **Subflow chains (nested pause, linked-runs model).** A run paused at a\n * `subflow` node (correlation `subflow:<childRunId>`) DELEGATES the signal\n * down to the suspended child; a run that completes and carries\n * `$parentRunId` in its context BUBBLES its output up by auto-resuming the\n * parent. Both directions compose recursively, so arbitrarily nested\n * subflow pauses resolve from either end (UI holds the parent run id;\n * approval/wait infrastructure holds the child's).\n */\n async resume(runId: string, signal?: ResumeSignal): Promise<AutomationResult> {\n return this.resumeInternal(runId, signal, false);\n }\n\n /**\n * @param skipBubble - Set when the caller is the subflow DELEGATION path,\n * which continues the parent itself after the child completes — the\n * child's own up-bubble must stay off so the parent isn't resumed twice.\n */\n private async resumeInternal(runId: string, signal: ResumeSignal | undefined, skipBubble: boolean): Promise<AutomationResult> {\n // Idempotency guard (set synchronously, before any await): reject a\n // concurrent duplicate resume of the same run so side effects can't run\n // twice. A duplicate that arrives *after* this one finishes finds no\n // suspended run and returns the \"no suspended run\" error below.\n if (this.resuming.has(runId)) {\n return { success: false, error: `Run '${runId}' is already being resumed` };\n }\n this.resuming.add(runId);\n try {\n // Hot path: suspended in this process. Cold path: rehydrate from the\n // durable store (e.g. the process restarted since the pause, ADR-0019).\n let run = this.suspendedRuns.get(runId) ?? null;\n if (!run && this.store) {\n try {\n run = await this.store.load(runId);\n } catch (err) {\n this.logger.warn(\n `[automation] failed to load suspended run '${runId}' from durable store: ${(err as Error).message}`,\n );\n }\n }\n if (!run) {\n return { success: false, error: `No suspended run '${runId}'` };\n }\n const flow = this.flows.get(run.flowName);\n if (!flow) {\n return { success: false, error: `Flow '${run.flowName}' not found for run '${runId}'` };\n }\n const node = flow.nodes.find(n => n.id === run.nodeId);\n if (!node) {\n return { success: false, error: `Suspended node '${run.nodeId}' no longer exists in flow '${run.flowName}'` };\n }\n\n // ── Subflow delegation (nested pause): this run is paused at a\n // `subflow` node whose child run itself suspended. The caller's\n // signal is meant for the node the CHILD paused on (its screen /\n // approval / wait), so forward it down. The child resumes with\n // bubbling off — when it completes, *this* invocation continues the\n // parent from the subflow node with the child's output, using the\n // same mapping as the synchronous path.\n if (typeof run.correlation === 'string' && run.correlation.startsWith('subflow:')) {\n const childRunId = run.correlation.slice('subflow:'.length);\n // Capture the child's row BEFORE resuming consumes it — the\n // output-variable mapping rides on the child's context.\n const childRun =\n this.suspendedRuns.get(childRunId) ??\n (this.store ? await this.store.load(childRunId).catch(() => null) : null);\n if (childRun) {\n const childRes = await this.resumeInternal(childRunId, signal, true);\n if (childRes.status === 'paused') {\n // Child paused again (e.g. the next screen of a wizard).\n // This run stays suspended; refresh its surfaced screen\n // so a re-fetch (getSuspendedScreen) shows the new one.\n if (childRes.screen && childRes.screen !== run.screen) {\n await this.persistSuspendedRun({ ...run, screen: childRes.screen });\n }\n return {\n success: true,\n status: 'paused',\n runId,\n durationMs: Date.now() - run.startTime,\n screen: childRes.screen,\n };\n }\n if (!childRes.success) {\n const error = `subflow run '${childRunId}' (${childRun.flowName}) failed: ${childRes.error ?? 'unknown error'}`;\n await this.failSuspendedRun(run, error);\n return { success: false, error, durationMs: Date.now() - run.startTime };\n }\n // Child completed — continue below with its output as the\n // resume signal (replaces the caller's signal, which the\n // child already consumed).\n signal = this.buildSubflowResumeSignal(childRun.context, childRes.output);\n } else {\n this.logger.warn(\n `[automation] run '${runId}' is paused at subflow node '${run.nodeId}' but child run '${childRunId}' ` +\n `is gone — continuing without child output`,\n );\n }\n }\n\n // Consume the suspension *before* running downstream work — a run\n // resumes exactly once per pause, and a duplicate resume after a\n // partial restart must not double-run side effects.\n await this.forgetSuspendedRun(runId);\n\n // Restore variable context and apply the resume signal's output as if it\n // were the node's output, so downstream edges branch on it.\n const variables = new Map<string, unknown>(Object.entries(run.variables));\n if (signal?.output) {\n for (const [key, value] of Object.entries(signal.output)) {\n variables.set(`${run.nodeId}.${key}`, value);\n }\n }\n // Bare flow variables — a `screen` node's collected inputs land under\n // their plain names so downstream `{var}` interpolation / conditions\n // read them directly (e.g. `new_assignee` → update_record fields).\n if (signal?.variables) {\n for (const [key, value] of Object.entries(signal.variables)) {\n variables.set(key, value);\n }\n }\n\n const steps = run.steps;\n const context = run.context;\n\n try {\n // ── Map re-entry (sequential multi-instance, ADR-0037 A2).\n // A run paused at a `map` node (correlation `map:<childRunId>`)\n // does NOT continue past the node on resume — it RE-RUNS the\n // node so the executor can record the just-completed unit and\n // start the next item. The default path continues past the node.\n if (typeof run.correlation === 'string' && run.correlation.startsWith('map:')) {\n await this.executeNode(node, flow, variables, context, steps);\n } else {\n await this.traverseNext(node, flow, variables, context, steps, signal?.branchLabel);\n }\n\n // Collect output variables\n const output: Record<string, unknown> = {};\n if (flow.variables) {\n for (const v of flow.variables) {\n if (v.isOutput) output[v.name] = variables.get(v.name);\n }\n }\n const durationMs = Date.now() - run.startTime;\n this.recordLog({\n id: runId,\n flowName: run.flowName,\n flowVersion: run.flowVersion,\n status: 'completed',\n startedAt: run.startedAt,\n completedAt: new Date().toISOString(),\n durationMs,\n trigger: {\n type: context.event ?? 'manual',\n userId: context.userId,\n object: context.object,\n },\n steps,\n output,\n });\n\n // ── Subflow up-bubble (nested pause): this run was a subflow\n // child whose parent suspended awaiting it. Auto-resume the\n // parent with our output, mapped like the synchronous path.\n // Skipped when the DELEGATION path drives the chain (it\n // continues the parent itself). Best-effort: the child's own\n // completion stands even if the parent continuation fails.\n if (!skipBubble) {\n await this.bubbleToParent(run, output);\n }\n\n return { success: true, output, durationMs };\n } catch (err: unknown) {\n // Re-suspended at a downstream node: persist a fresh continuation.\n if (isSuspendSignal(err)) {\n const durationMs = Date.now() - run.startTime;\n await this.persistSuspendedRun({\n ...run,\n nodeId: err.nodeId,\n variables: Object.fromEntries(variables),\n steps,\n correlation: err.correlation,\n screen: err.screen,\n });\n this.recordLog({\n id: runId,\n flowName: run.flowName,\n flowVersion: run.flowVersion,\n status: 'paused',\n startedAt: run.startedAt,\n durationMs,\n trigger: {\n type: context.event ?? 'manual',\n userId: context.userId,\n object: context.object,\n },\n steps,\n });\n return { success: true, status: 'paused', runId, durationMs, screen: err.screen };\n }\n\n const errorMessage = err instanceof Error ? err.message : String(err);\n const durationMs = Date.now() - run.startTime;\n this.recordLog({\n id: runId,\n flowName: run.flowName,\n flowVersion: run.flowVersion,\n status: 'failed',\n startedAt: run.startedAt,\n completedAt: new Date().toISOString(),\n durationMs,\n trigger: {\n type: context.event ?? 'manual',\n userId: context.userId,\n object: context.object,\n },\n steps,\n error: errorMessage,\n });\n // Subflow chain: a child failing terminally fails every\n // ancestor awaiting it — they can never be resumed otherwise.\n // The delegation path handles its own level (skipBubble).\n if (!skipBubble) {\n await this.failAncestors(run.context, errorMessage);\n }\n return { success: false, error: errorMessage, durationMs };\n }\n } finally {\n this.resuming.delete(runId);\n }\n }\n\n /**\n * Build the resume signal that maps a completed subflow child's output\n * into its parent — mirroring the synchronous path exactly: the engine's\n * standard `signal.output` merge lands it under `${subflowNodeId}.output`,\n * and `signal.variables` writes the bare `config.outputVariable` when the\n * child's context carries one (`$parentOutputVariable`).\n */\n private buildSubflowResumeSignal(childContext: AutomationContext | undefined, childOutput: unknown): ResumeSignal {\n const outVar = (childContext as Record<string, unknown> | undefined)?.$parentOutputVariable;\n return {\n output: { output: childOutput ?? null },\n ...(typeof outVar === 'string' && outVar\n ? { variables: { [outVar]: childOutput ?? null } }\n : {}),\n };\n }\n\n /**\n * Up-bubble for the subflow chain: when a completed run carries\n * `$parentRunId`, resume that parent with this run's output. Recursion via\n * the parent's own completion bubbles multi-level chains. Best-effort —\n * a failed parent continuation is logged, never thrown back at the\n * caller who resumed the child.\n */\n private async bubbleToParent(run: SuspendedRun, output: Record<string, unknown>): Promise<void> {\n const ctx = run.context as Record<string, unknown> | undefined;\n const parentRunId = ctx?.$parentRunId;\n if (typeof parentRunId !== 'string' || !parentRunId) return;\n try {\n // A `map` child (ADR-0037 A2): hand the unit's output to the map\n // node + flag the completion, so on re-entry it records this item\n // and starts the next. A plain subflow child uses the 1:1 mapping.\n const mapNode = ctx?.$parentMapNode;\n const sig = typeof mapNode === 'string' && mapNode\n ? { variables: { [`${mapNode}.$mapItemOutput`]: output ?? null, [`${mapNode}.$mapItemDone`]: true } }\n : this.buildSubflowResumeSignal(run.context, output);\n const parentRes = await this.resumeInternal(parentRunId, sig, false);\n if (!parentRes.success) {\n this.logger.warn(\n `[automation] subflow run '${run.runId}' completed but resuming parent '${parentRunId}' failed: ${parentRes.error}`,\n );\n }\n } catch (err) {\n this.logger.warn(\n `[automation] subflow run '${run.runId}' completed but resuming parent '${parentRunId}' threw: ${(err as Error).message}`,\n );\n }\n }\n\n /**\n * Terminally fail a suspended run: consume its continuation and record a\n * `failed` log so it stops surfacing as resumable. Used when a subflow\n * descendant fails — the ancestor awaiting it can never be resumed.\n */\n private async failSuspendedRun(run: SuspendedRun, error: string): Promise<void> {\n await this.forgetSuspendedRun(run.runId);\n this.recordLog({\n id: run.runId,\n flowName: run.flowName,\n flowVersion: run.flowVersion,\n status: 'failed',\n startedAt: run.startedAt,\n completedAt: new Date().toISOString(),\n durationMs: Date.now() - run.startTime,\n trigger: {\n type: run.context?.event ?? 'manual',\n userId: run.context?.userId,\n object: run.context?.object,\n },\n steps: run.steps,\n error,\n });\n }\n\n /**\n * Cancel a suspended run (ADR-0044): consume its continuation and record a\n * terminal `cancelled` log so it stops surfacing as resumable. The\n * engine-level primitive behind \"the submitter abandoned the revision\n * window\" — recalling there leaves the run paused at a wait node with no\n * reject edge to resume down, so the run must end, not continue. Returns\n * `false` when no suspended run exists under the id (already terminal /\n * unknown), which callers treat as idempotent success.\n */\n async cancelRun(runId: string, reason?: string): Promise<boolean> {\n let run = this.suspendedRuns.get(runId) ?? null;\n if (!run && this.store) {\n try {\n run = await this.store.load(runId);\n } catch (err) {\n this.logger.warn(\n `[automation] cancelRun: failed to load suspended run '${runId}' from durable store: ${(err as Error).message}`,\n );\n }\n }\n if (!run) return false;\n await this.forgetSuspendedRun(runId);\n this.recordLog({\n id: run.runId,\n flowName: run.flowName,\n flowVersion: run.flowVersion,\n status: 'cancelled',\n startedAt: run.startedAt,\n completedAt: new Date().toISOString(),\n durationMs: Date.now() - run.startTime,\n trigger: {\n type: run.context?.event ?? 'manual',\n userId: run.context?.userId,\n object: run.context?.object,\n },\n steps: run.steps,\n error: reason,\n });\n return true;\n }\n\n /**\n * Walk a failed run's `$parentRunId` chain and fail each suspended\n * ancestor (see {@link failSuspendedRun}). Bounded so a corrupt context\n * can't loop forever.\n */\n private async failAncestors(context: AutomationContext | undefined, error: string): Promise<void> {\n let parentId = (context as Record<string, unknown> | undefined)?.$parentRunId;\n let hops = 0;\n while (typeof parentId === 'string' && parentId && hops++ < 32) {\n const parent =\n this.suspendedRuns.get(parentId) ??\n (this.store ? await this.store.load(parentId).catch(() => null) : null);\n if (!parent) return;\n await this.failSuspendedRun(parent, `subflow descendant failed: ${error}`);\n parentId = (parent.context as Record<string, unknown> | undefined)?.$parentRunId;\n }\n }\n\n /**\n * List the runs currently suspended awaiting {@link resume} (ADR-0019).\n * Backs operability surfaces such as a \"pending approvals\" view.\n *\n * Synchronous — reads the in-memory cache only, so after a process restart\n * runs that suspended in a prior lifetime are not listed here even though\n * they remain durably stored and resumable by id. Use\n * {@link listSuspendedRunsDurable} to include those.\n */\n listSuspendedRuns(): Array<{ runId: string; flowName: string; nodeId: string; correlation?: string }> {\n return [...this.suspendedRuns.values()].map(r => ({\n runId: r.runId,\n flowName: r.flowName,\n nodeId: r.nodeId,\n correlation: r.correlation,\n }));\n }\n\n /**\n * Like {@link listSuspendedRuns} but includes runs held only in the durable\n * {@link SuspendedRunStore} (e.g. suspended before a restart). The in-memory\n * cache takes precedence on id collisions. Falls back to the in-memory list\n * when no store is configured.\n */\n async listSuspendedRunsDurable(): Promise<Array<{ runId: string; flowName: string; nodeId: string; correlation?: string }>> {\n const byId = new Map<string, { runId: string; flowName: string; nodeId: string; correlation?: string }>();\n if (this.store) {\n try {\n for (const r of await this.store.list()) {\n byId.set(r.runId, { runId: r.runId, flowName: r.flowName, nodeId: r.nodeId, correlation: r.correlation });\n }\n } catch (err) {\n this.logger.warn(`[automation] failed to list suspended runs from durable store: ${(err as Error).message}`);\n }\n }\n // In-memory entries win — they are the freshest copy.\n for (const r of this.suspendedRuns.values()) {\n byId.set(r.runId, { runId: r.runId, flowName: r.flowName, nodeId: r.nodeId, correlation: r.correlation });\n }\n return [...byId.values()];\n }\n\n /**\n * The screen a paused run is currently waiting on (screen-flow runtime), or\n * `null` if the run isn't suspended / didn't pause at a screen node. Lets a\n * UI flow-runner re-fetch the form after a refresh.\n */\n getSuspendedScreen(runId: string): ScreenSpec | null {\n return this.suspendedRuns.get(runId)?.screen ?? null;\n }\n\n // ── DAG Traversal Core ──────────────────────────────────\n\n private recordLog(entry: ExecutionLogEntry): void {\n this.executionLogs.push(entry);\n // Evict oldest logs when exceeding max size\n if (this.executionLogs.length > this.maxLogSize) {\n this.executionLogs.splice(0, this.executionLogs.length - this.maxLogSize);\n }\n }\n\n /**\n * Validate each node's `type` against the live action registry (ADR-0018).\n * A type is known if it is structural (start/end), has a registered\n * executor, or has a published action descriptor. Unknown types are\n * warned about (not rejected) so flows authored against a temporarily\n * absent plugin still register; the runtime surfaces a hard NO_EXECUTOR\n * error if such a node is actually executed.\n */\n private validateNodeTypes(flowName: string, flow: FlowParsed): void {\n const known = new Set<string>([\n ...FLOW_STRUCTURAL_NODE_TYPES,\n ...this.nodeExecutors.keys(),\n ...this.actionDescriptors.keys(),\n ]);\n const unknown = [...new Set(\n flow.nodes.map(n => n.type).filter(t => !known.has(t)),\n )];\n if (unknown.length > 0) {\n this.logger.warn(\n `Flow '${flowName}' references node type(s) with no registered executor or descriptor: ` +\n `${unknown.join(', ')}. They will fail at execution time unless a plugin registers them. ` +\n `Registered types: ${[...known].join(', ') || '(none)'}`,\n );\n }\n }\n\n /**\n * ADR-0032 §Decision 1a — parse-validate every predicate in the flow at\n * registration. Predicates are bare CEL; this catches the #1491 class\n * (`{record.x}` template braces in a condition → CEL parse error) and any\n * other malformed predicate LOUDLY, with the offending location + source +\n * a corrective hint, instead of letting it fail silently at run time.\n *\n * Only the *predicate* surfaces are checked here (start/node `config.condition`\n * and `edge.condition`) — node string fields are templates (a different\n * dialect) and are validated by the template engine, not as CEL.\n */\n private validateFlowExpressions(flowName: string, flow: FlowParsed): void {\n const failures: string[] = [];\n\n const check = (where: string, raw: unknown): void => {\n if (raw == null) return;\n // Conditions are predicates (bare CEL). Delegate to the one shared\n // validator (ADR-0032 §5) so the corrective message matches the CLI\n // build and the agent `validate_expression` tool exactly.\n const result = validateExpression('predicate', raw as string | { dialect?: string; source?: string });\n for (const e of result.errors) {\n failures.push(` • ${where}: ${e.message}\\n source: \\`${e.source}\\``);\n }\n };\n\n for (const node of flow.nodes) {\n const cfg = (node.config ?? {}) as Record<string, unknown>;\n // start-node trigger gate + decision/branch predicates live in config.condition\n check(`node '${node.id}' (${node.type}) condition`, cfg.condition);\n }\n for (const edge of flow.edges) {\n check(`edge '${edge.id}' (${edge.source}→${edge.target}) condition`, edge.condition as unknown);\n }\n\n if (failures.length > 0) {\n throw new Error(\n `Flow '${flowName}' has ${failures.length} invalid condition${failures.length > 1 ? 's' : ''} (ADR-0032 §1a). ` +\n `Conditions are bare CEL — do not wrap field references in \\`{…}\\` template braces:\\n${failures.join('\\n')}`,\n );\n }\n }\n\n /**\n * Detect cycles in the flow graph (DAG validation).\n * Uses DFS with coloring (white/gray/black) to detect back edges.\n * Throws an error with cycle details if a cycle is found.\n *\n * ADR-0044: edges explicitly typed `back` (declared back-edges — e.g. a\n * revise/rework loop re-entering an approval node) are excluded from the\n * analysis: the graph **minus `back` edges** must be a DAG. An unmarked\n * cycle is still rejected — authors opt in edge by edge. At run time a\n * `back` edge traverses like any default edge; the re-entry runaway guard\n * lives in {@link executeNode}.\n */\n private detectCycles(flow: FlowParsed): void {\n const WHITE = 0, GRAY = 1, BLACK = 2;\n const color = new Map<string, number>();\n const parent = new Map<string, string>();\n\n // Build adjacency list from edges\n const adj = new Map<string, string[]>();\n for (const node of flow.nodes) {\n color.set(node.id, WHITE);\n adj.set(node.id, []);\n }\n for (const edge of flow.edges) {\n if (edge.type === 'back') continue; // ADR-0044 declared back-edge\n const targets = adj.get(edge.source);\n if (targets) targets.push(edge.target);\n }\n\n const dfs = (nodeId: string): string[] | null => {\n color.set(nodeId, GRAY);\n for (const neighbor of adj.get(nodeId) ?? []) {\n if (color.get(neighbor) === GRAY) {\n // Back edge found — reconstruct cycle\n const cycle = [neighbor, nodeId];\n let cur = nodeId;\n while (cur !== neighbor) {\n cur = parent.get(cur)!;\n if (cur) cycle.push(cur);\n else break;\n }\n return cycle.reverse();\n }\n if (color.get(neighbor) === WHITE) {\n parent.set(neighbor, nodeId);\n const result = dfs(neighbor);\n if (result) return result;\n }\n }\n color.set(nodeId, BLACK);\n return null;\n };\n\n for (const node of flow.nodes) {\n if (color.get(node.id) === WHITE) {\n const cycle = dfs(node.id);\n if (cycle) {\n throw new Error(\n `Flow contains a cycle: ${cycle.join(' → ')}. Only DAG flows are allowed — ` +\n `to author an intentional rework loop, mark the cycle-closing edge with type: 'back' (ADR-0044).`,\n );\n }\n }\n }\n }\n\n /**\n * Get the runtime type name of a value for schema validation.\n */\n private getValueType(value: unknown): string {\n if (Array.isArray(value)) return 'array';\n if (typeof value === 'object' && value !== null) return 'object';\n return typeof value;\n }\n\n /**\n * Validate node input schemas before execution.\n * Checks that node config matches declared inputSchema if present.\n */\n private validateNodeInputSchemas(flow: FlowParsed, _variables: Map<string, unknown>): void {\n for (const node of flow.nodes) {\n if (node.inputSchema && node.config) {\n for (const [paramName, paramDef] of Object.entries(node.inputSchema)) {\n if (paramDef.required && !(paramName in (node.config as Record<string, unknown>))) {\n throw new Error(\n `Node '${node.id}' missing required input parameter '${paramName}'`,\n );\n }\n const value = (node.config as Record<string, unknown>)[paramName];\n if (value !== undefined) {\n const actualType = this.getValueType(value);\n if (actualType !== paramDef.type) {\n throw new Error(\n `Node '${node.id}' parameter '${paramName}' expected type '${paramDef.type}' but got '${actualType}'`,\n );\n }\n }\n }\n }\n }\n }\n\n /**\n * Execute a node with timeout support, fault edge handling, and step logging.\n */\n private async executeNode(\n node: FlowNodeParsed,\n flow: FlowParsed,\n variables: Map<string, unknown>,\n context: AutomationContext,\n steps: StepLogEntry[],\n ): Promise<void> {\n if (node.type === 'end') return;\n\n // ADR-0044 runaway guard: declared back-edges make re-entering a node\n // legal, so a misauthored unconditional loop could otherwise spin\n // forever. Count this node's prior *top-level* visits in the run's step\n // log (region body steps carry `parentNodeId` and are excluded — a\n // 200-iteration `loop` region is legitimate) and fail the run loudly\n // past the cap. Product-level guards (e.g. an approval node's\n // `maxRevisions`) terminate far earlier; this is the engine backstop.\n const priorVisits = steps.reduce(\n (n, s) => (s.nodeId === node.id && s.parentNodeId === undefined ? n + 1 : n), 0,\n );\n if (priorVisits >= AutomationEngine.MAX_NODE_REENTRIES) {\n throw new Error(\n `Node '${node.id}' was entered ${priorVisits} times in one run — aborting as a runaway loop ` +\n `(back-edge cycles must terminate; see ADR-0044)`,\n );\n }\n\n const stepStart = Date.now();\n const stepStartedAt = new Date().toISOString();\n\n // Find executor\n const executor = this.nodeExecutors.get(node.type);\n if (!executor) {\n // start node without executor is fine — just skip\n if (node.type !== 'start') {\n steps.push({\n nodeId: node.id,\n nodeType: node.type,\n status: 'failure',\n startedAt: stepStartedAt,\n completedAt: new Date().toISOString(),\n durationMs: Date.now() - stepStart,\n error: { code: 'NO_EXECUTOR', message: `No executor registered for node type '${node.type}'` },\n });\n throw new Error(`No executor registered for node type '${node.type}'`);\n }\n // Log start node step\n steps.push({\n nodeId: node.id,\n nodeType: node.type,\n status: 'success',\n startedAt: stepStartedAt,\n completedAt: new Date().toISOString(),\n durationMs: Date.now() - stepStart,\n });\n } else {\n // Execute node with optional timeout\n let result: NodeExecutionResult;\n try {\n if (node.timeoutMs && node.timeoutMs > 0) {\n result = await this.executeWithTimeout(\n executor.execute(node, variables, context),\n node.timeoutMs,\n node.id,\n );\n } else {\n result = await executor.execute(node, variables, context);\n }\n } catch (execErr: unknown) {\n const errMsg = execErr instanceof Error ? execErr.message : String(execErr);\n steps.push({\n nodeId: node.id,\n nodeType: node.type,\n status: 'failure',\n startedAt: stepStartedAt,\n completedAt: new Date().toISOString(),\n durationMs: Date.now() - stepStart,\n error: { code: 'EXECUTION_ERROR', message: errMsg },\n });\n\n // Check for fault edges\n const faultEdge = flow.edges.find(e => e.source === node.id && e.type === 'fault');\n if (faultEdge) {\n variables.set('$error', { nodeId: node.id, message: errMsg });\n const faultTarget = flow.nodes.find(n => n.id === faultEdge.target);\n if (faultTarget) {\n await this.executeNode(faultTarget, flow, variables, context, steps);\n return;\n }\n }\n throw execErr;\n }\n\n if (!result.success) {\n const errMsg = result.error ?? 'Unknown error';\n steps.push({\n nodeId: node.id,\n nodeType: node.type,\n status: 'failure',\n startedAt: stepStartedAt,\n completedAt: new Date().toISOString(),\n durationMs: Date.now() - stepStart,\n error: { code: 'NODE_FAILURE', message: errMsg },\n });\n\n // Write error output to variable context for downstream nodes\n variables.set('$error', { nodeId: node.id, message: errMsg, output: result.output });\n\n // Check for fault edges\n const faultEdge = flow.edges.find(e => e.source === node.id && e.type === 'fault');\n if (faultEdge) {\n const faultTarget = flow.nodes.find(n => n.id === faultEdge.target);\n if (faultTarget) {\n await this.executeNode(faultTarget, flow, variables, context, steps);\n return;\n }\n }\n throw new Error(`Node '${node.id}' failed: ${errMsg}`);\n }\n\n // Log successful step\n steps.push({\n nodeId: node.id,\n nodeType: node.type,\n status: 'success',\n startedAt: stepStartedAt,\n completedAt: new Date().toISOString(),\n durationMs: Date.now() - stepStart,\n });\n\n // #1479: fold a structured-region container's body/branch/handler\n // steps into the run log, right after the container's own step.\n if (result.childSteps?.length) {\n steps.push(...result.childSteps);\n }\n\n // Write back output variables\n if (result.output) {\n for (const [key, value] of Object.entries(result.output)) {\n variables.set(`${node.id}.${key}`, value);\n }\n }\n\n // ADR-0019 durable pause: the node did its on-entry work and asked to\n // suspend here. Output is already written above; unwind the recursion\n // up to execute()/resume(), which persists a continuation. Traversal\n // of this node's out-edges happens on resume, not now.\n if (result.suspend) {\n throw new FlowSuspendSignal(node.id, result.correlation, result.screen);\n }\n }\n\n // Continue to the node's successors.\n await this.traverseNext(node, flow, variables, context, steps);\n }\n\n /**\n * Traverse a node's out-edges and execute its successors. Split out of\n * {@link executeNode} so {@link resume} can re-enter traversal from a\n * suspended node without re-running the node body.\n *\n * @param branchLabel - When set (e.g. from a resume signal), restrict\n * traversal to out-edges whose `label` matches — this is how an Approval\n * node's `approve`/`reject` decision selects its downstream branch. When\n * no edge carries the label, traversal falls back to the normal edge set.\n */\n private async traverseNext(\n node: FlowNodeParsed,\n flow: FlowParsed,\n variables: Map<string, unknown>,\n context: AutomationContext,\n steps: StepLogEntry[],\n branchLabel?: string,\n ): Promise<void> {\n // Find next nodes — separate conditional and unconditional edges\n let outEdges = flow.edges.filter(\n e => e.source === node.id && e.type !== 'fault',\n );\n\n // Branch selection (resume): prefer edges tagged with the decision label.\n if (branchLabel) {\n const labeled = outEdges.filter(e => e.label === branchLabel);\n if (labeled.length > 0) outEdges = labeled;\n }\n\n const conditionalEdges: FlowEdgeParsed[] = [];\n const unconditionalEdges: FlowEdgeParsed[] = [];\n for (const edge of outEdges) {\n if (edge.condition) {\n conditionalEdges.push(edge);\n } else {\n unconditionalEdges.push(edge);\n }\n }\n\n // Conditional edges: evaluate sequentially (mutually exclusive)\n for (const edge of conditionalEdges) {\n if (this.evaluateCondition(edge.condition!, variables)) {\n const nextNode = flow.nodes.find(n => n.id === edge.target);\n if (nextNode) {\n await this.executeNode(nextNode, flow, variables, context, steps);\n }\n }\n }\n\n // Unconditional edges: execute in parallel (Promise.all)\n if (unconditionalEdges.length > 0) {\n const parallelTasks = unconditionalEdges\n .map(edge => flow.nodes.find(n => n.id === edge.target))\n .filter((n): n is FlowNodeParsed => n != null)\n .map(nextNode => this.executeNode(nextNode, flow, variables, context, steps));\n\n await Promise.all(parallelTasks);\n }\n }\n\n /**\n * Execute a structured control-flow **region** (ADR-0031) — the nested\n * body of a `loop` container (or, later, a `parallel` branch / `try_catch`\n * region). The region is a self-contained single-entry/single-exit\n * sub-graph carried in the container's `config`; it runs in the **enclosing\n * variable scope** (the caller's `variables` map), so the iterator variable\n * and any body mutations are visible to the surrounding flow — a region is\n * NOT a separate `subflow` invocation.\n *\n * The region executes against a synthetic flow view of its own\n * nodes/edges, so the main DAG traversal (`traverseNext`) is never aware of\n * scope markers — keeping the shared traversal untouched.\n *\n * #1479: the executed body steps are **returned** (tagged with `grouping`)\n * so the calling container node can fold them into the parent run log via\n * `NodeExecutionResult.childSteps`. Tagging only fills fields left undefined,\n * so when regions nest, each step keeps its **innermost** container's\n * `parentNodeId` / `iteration` / `regionKind`. On failure the region throws\n * as before (preserving `try_catch` retry semantics); a failed attempt's\n * partial steps are not surfaced.\n *\n * Durable pause (`suspend`) inside a region is not supported in this\n * iteration — it is converted into a clear error (mirrors the `subflow`\n * nested-pause guard).\n */\n async runRegion(\n region: FlowRegionParsed,\n variables: Map<string, unknown>,\n context: AutomationContext,\n grouping?: { parentNodeId: string; iteration?: number; regionKind?: string },\n ): Promise<StepLogEntry[]> {\n const entryId = findRegionEntry(region);\n const entry = region.nodes.find(n => n.id === entryId);\n if (!entry) {\n throw new Error(`region entry node '${entryId}' not found`);\n }\n // A synthetic flow view — executeNode/traverseNext only read `nodes`/`edges`.\n const subFlow = { nodes: region.nodes, edges: region.edges ?? [] } as unknown as FlowParsed;\n const regionSteps: StepLogEntry[] = [];\n try {\n await this.executeNode(entry, subFlow, variables, context, regionSteps);\n } catch (err) {\n if (isSuspendSignal(err)) {\n throw new Error(\n `durable pause inside a structured region (node '${err.nodeId}') is not supported`,\n );\n }\n throw err;\n }\n // Tag this region's steps with their immediate container. Innermost wins:\n // a step that already carries a `parentNodeId` (set by a nested region)\n // is left untouched.\n if (grouping) {\n for (const step of regionSteps) {\n if (step.parentNodeId === undefined) {\n step.parentNodeId = grouping.parentNodeId;\n if (grouping.iteration !== undefined) step.iteration = grouping.iteration;\n if (grouping.regionKind !== undefined) step.regionKind = grouping.regionKind;\n }\n }\n }\n return regionSteps;\n }\n\n /**\n * Execute a promise with timeout using Promise.race.\n */\n private executeWithTimeout(\n promise: Promise<NodeExecutionResult>,\n timeoutMs: number,\n nodeId: string,\n ): Promise<NodeExecutionResult> {\n return Promise.race([\n promise,\n new Promise<NodeExecutionResult>((_, reject) =>\n setTimeout(() => reject(new Error(`Node '${nodeId}' timed out after ${timeoutMs}ms`)), timeoutMs),\n ),\n ]);\n }\n\n /**\n * Safe expression evaluator.\n * Uses simple operator-based parsing without `new Function`.\n * Supports: comparisons (>, <, >=, <=, ==, !=, ===, !==),\n * boolean literals (true, false), and basic arithmetic.\n */\n evaluateCondition(expression: string | { dialect?: string; source?: string; ast?: unknown }, variables: Map<string, unknown>): boolean {\n // M9.5+ wiring: route Expression envelopes through @objectstack/formula\n // ExpressionEngine. CEL is the default; legacy `{var}` template syntax\n // is preserved as a fallback for back-compat.\n const isEnvelope = typeof expression === 'object' && expression != null && 'dialect' in expression;\n const dialect = isEnvelope ? (expression as { dialect?: string }).dialect : undefined;\n const exprStr = typeof expression === 'string' ? expression : ((expression as { source?: string })?.source ?? '');\n\n if (isEnvelope && dialect && dialect !== 'cel' && dialect !== 'flow' && dialect !== 'template') {\n // Other dialects (cron, js) are not boolean predicates here.\n return false;\n }\n\n // CEL path — bind `vars` scope for `{step.result}` style references via\n // the equivalent `vars.step.result` CEL identifier path.\n if (dialect === 'cel' || (isEnvelope && !dialect)) {\n try {\n const vars: Record<string, unknown> = {};\n for (const [key, value] of variables) {\n // Convert \"step.result\" keys into nested object paths.\n const segs = key.split('.');\n let cursor = vars;\n for (let i = 0; i < segs.length - 1; i++) {\n if (typeof cursor[segs[i]] !== 'object' || cursor[segs[i]] === null) {\n cursor[segs[i]] = {};\n }\n cursor = cursor[segs[i]] as Record<string, unknown>;\n }\n cursor[segs[segs.length - 1]] = value;\n }\n // Expose variables two ways under `extra`: as a `vars` namespace\n // (so `vars.step.result` keeps working) AND spread to top level (so\n // bare identifiers like `status` / `previous.status` resolve — the\n // natural authoring style for record-change start conditions).\n const result = ExpressionEngine.evaluate(\n { dialect: 'cel', source: exprStr },\n { extra: { ...vars, vars }, record: vars },\n );\n // ADR-0032 §Decision 1c — NO silent fallback. A non-`ok` result is a\n // real fault (malformed predicate, or — pre build-validation — a\n // `{…}` template mistakenly written into a CEL condition). Surfacing\n // it as a thrown, attributed error makes execute()'s catch record a\n // loud flow failure, instead of the old `return false` that made a\n // broken condition indistinguishable from \"condition not met\" (#1491).\n if (!result.ok) {\n throw new Error(\n `condition failed to evaluate as CEL: ${result.error?.message ?? 'unknown error'} — ` +\n `source: \\`${exprStr}\\`. Conditions are bare CEL (e.g. \\`record.rating >= 4\\`); ` +\n `do not wrap field references in \\`{…}\\` template braces.`,\n );\n }\n return Boolean(result.value);\n } catch (err) {\n // Re-throw with the source attached (ADR-0032 §1d — errors written\n // for self-correction). Never swallow to `false`.\n const msg = (err as Error)?.message ?? String(err);\n throw new Error(\n msg.includes('source:') ? msg : `condition evaluation error: ${msg} — source: \\`${exprStr}\\``,\n );\n }\n }\n\n // Legacy template path: {varName} → value, then primitive compare.\n let resolved = exprStr;\n for (const [key, value] of variables) {\n resolved = resolved.split(`{${key}}`).join(String(value));\n }\n resolved = resolved.trim();\n\n try {\n // Boolean literals\n if (resolved === 'true') return true;\n if (resolved === 'false') return false;\n\n // Comparison operators (ordered by length to match longer operators first)\n const operators = ['===', '!==', '>=', '<=', '!=', '==', '>', '<'] as const;\n for (const op of operators) {\n const idx = resolved.indexOf(op);\n if (idx !== -1) {\n const left = resolved.slice(0, idx).trim();\n const right = resolved.slice(idx + op.length).trim();\n return this.compareValues(left, op, right);\n }\n }\n\n // Numeric truthy check\n const numVal = Number(resolved);\n if (!isNaN(numVal)) return numVal !== 0;\n\n return false;\n } catch {\n return false;\n }\n }\n\n /**\n * Compare two string-represented values with an operator.\n */\n private compareValues(left: string, op: string, right: string): boolean {\n const lNum = Number(left);\n const rNum = Number(right);\n const bothNumeric = !isNaN(lNum) && !isNaN(rNum) && left !== '' && right !== '';\n\n if (bothNumeric) {\n switch (op) {\n case '>': return lNum > rNum;\n case '<': return lNum < rNum;\n case '>=': return lNum >= rNum;\n case '<=': return lNum <= rNum;\n case '==': case '===': return lNum === rNum;\n case '!=': case '!==': return lNum !== rNum;\n default: return false;\n }\n }\n // String comparison\n switch (op) {\n case '==': case '===': return left === right;\n case '!=': case '!==': return left !== right;\n case '>': return left > right;\n case '<': return left < right;\n case '>=': return left >= right;\n case '<=': return left <= right;\n default: return false;\n }\n }\n\n /**\n * Retry execution with exponential backoff, jitter, and recursive protection.\n * Uses an iterative loop with an internal retry flag to prevent recursive call stacking.\n */\n private async retryExecution(\n flowName: string,\n context: AutomationContext | undefined,\n startTime: number,\n errorHandling: {\n maxRetries?: number;\n retryDelayMs?: number;\n backoffMultiplier?: number;\n maxRetryDelayMs?: number;\n jitter?: boolean;\n },\n ): Promise<AutomationResult> {\n const maxRetries = errorHandling.maxRetries ?? 3;\n const baseDelay = errorHandling.retryDelayMs ?? 1000;\n const multiplier = errorHandling.backoffMultiplier ?? 1;\n const maxDelay = errorHandling.maxRetryDelayMs ?? 30000;\n const useJitter = errorHandling.jitter ?? false;\n\n let lastError = 'Max retries exceeded';\n for (let i = 0; i < maxRetries; i++) {\n // Calculate delay with exponential backoff\n let delay = Math.min(baseDelay * Math.pow(multiplier, i), maxDelay);\n if (useJitter) {\n delay = delay * (0.5 + Math.random() * 0.5);\n }\n await new Promise(r => setTimeout(r, delay));\n\n // Execute directly without recursion into retryExecution again\n const result = await this.executeWithoutRetry(flowName, context);\n if (result.success) return result;\n lastError = result.error ?? 'Unknown error';\n }\n return { success: false, error: lastError, durationMs: Date.now() - startTime };\n }\n\n /**\n * Execute a flow without triggering retry logic (used by retryExecution to prevent recursion).\n */\n private async executeWithoutRetry(\n flowName: string,\n context?: AutomationContext,\n ): Promise<AutomationResult> {\n const startTime = Date.now();\n const flow = this.flows.get(flowName);\n\n if (!flow) {\n return { success: false, error: `Flow '${flowName}' not found` };\n }\n if (this.flowEnabled.get(flowName) === false) {\n return { success: false, error: `Flow '${flowName}' is disabled` };\n }\n\n const variables = new Map<string, unknown>();\n if (flow.variables) {\n for (const v of flow.variables) {\n if (v.isInput && context?.params?.[v.name] !== undefined) {\n variables.set(v.name, context.params[v.name]);\n }\n }\n }\n if (context?.record) {\n variables.set('$record', context.record);\n }\n\n const runId = this.nextRunId();\n const startedAt = new Date().toISOString();\n const steps: StepLogEntry[] = [];\n\n try {\n const startNode = flow.nodes.find(n => n.type === 'start');\n if (!startNode) {\n return { success: false, error: 'Flow has no start node' };\n }\n\n await this.executeNode(startNode, flow, variables, context ?? {}, steps);\n\n const output: Record<string, unknown> = {};\n if (flow.variables) {\n for (const v of flow.variables) {\n if (v.isOutput) {\n output[v.name] = variables.get(v.name);\n }\n }\n }\n\n const durationMs = Date.now() - startTime;\n this.recordLog({\n id: runId,\n flowName,\n flowVersion: flow.version,\n status: 'completed',\n startedAt,\n completedAt: new Date().toISOString(),\n durationMs,\n trigger: {\n type: context?.event ?? 'manual',\n userId: context?.userId,\n object: context?.object,\n },\n steps,\n output,\n });\n\n return { success: true, output, durationMs };\n } catch (err: unknown) {\n const errorMessage = err instanceof Error ? err.message : String(err);\n const durationMs = Date.now() - startTime;\n this.recordLog({\n id: runId,\n flowName,\n flowVersion: flow.version,\n status: 'failed',\n startedAt,\n completedAt: new Date().toISOString(),\n durationMs,\n trigger: {\n type: context?.event ?? 'manual',\n userId: context?.userId,\n object: context?.object,\n },\n steps,\n error: errorMessage,\n });\n return { success: false, error: errorMessage, durationMs };\n }\n }\n}\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\nimport type { Logger } from '@objectstack/spec/contracts';\nimport type { SuspendedRun, SuspendedRunStore } from './engine.js';\n\n/**\n * Durable persistence for suspended flow runs (ADR-0019).\n *\n * The engine keeps an in-memory map of paused runs; that map is lost on a\n * process restart (e.g. a hibernating Cloudflare Worker), so a run that paused\n * at an `approval` / `wait` / `screen` node can never be resumed afterwards.\n * A {@link SuspendedRunStore} backs the in-memory map with durable storage so a\n * cold-booted kernel can rehydrate and continue.\n *\n * Two implementations ship here:\n * - {@link InMemorySuspendedRunStore} — a Map (the default behaviour, for\n * tests / dev). It JSON round-trips on save/load so it faithfully exercises\n * the serialization boundary a DB store imposes.\n * - {@link ObjectStoreSuspendedRunStore} — persists to the `sys_automation_run`\n * object via the ObjectQL engine, for production / serverless hosts.\n */\n\nconst TABLE = 'sys_automation_run';\nconst SYSTEM_CTX = { isSystem: true, roles: [], permissions: [] } as const;\n\n/** Deep clone via JSON so a stored snapshot can't alias live engine state. */\nfunction jsonClone<T>(value: T): T {\n return JSON.parse(JSON.stringify(value)) as T;\n}\n\n/** Parse a JSON column that may already be an object (some drivers auto-parse). */\nfunction parseJson<T>(raw: unknown, fallback: T): T {\n if (raw == null || raw === '') return fallback;\n if (typeof raw === 'string') {\n try {\n return JSON.parse(raw) as T;\n } catch {\n return fallback;\n }\n }\n return raw as T;\n}\n\n/**\n * In-memory {@link SuspendedRunStore}. Snapshots are JSON-cloned on the way in\n * and out, matching the serialize/deserialize boundary of a DB-backed store —\n * so a unit test can share one instance across two engine instances to simulate\n * a process restart (suspend on engine A, resume on engine B).\n */\nexport class InMemorySuspendedRunStore implements SuspendedRunStore {\n private readonly runs = new Map<string, SuspendedRun>();\n\n async save(run: SuspendedRun): Promise<void> {\n this.runs.set(run.runId, jsonClone(run));\n }\n\n async load(runId: string): Promise<SuspendedRun | null> {\n const run = this.runs.get(runId);\n return run ? jsonClone(run) : null;\n }\n\n async delete(runId: string): Promise<void> {\n this.runs.delete(runId);\n }\n\n async list(): Promise<SuspendedRun[]> {\n return [...this.runs.values()].map(jsonClone);\n }\n}\n\n/**\n * Minimal ObjectQL engine surface the {@link ObjectStoreSuspendedRunStore} uses.\n * Matches the find/insert/update/delete shape exposed by the `objectql` service\n * (and mirrors `ApprovalEngine` in plugin-approvals).\n */\nexport interface SuspendedRunStoreEngine {\n find(object: string, options?: any): Promise<any[]>;\n insert(object: string, data: any, options?: any): Promise<any>;\n update(object: string, data: any, options?: any): Promise<any>;\n delete?(object: string, options?: any): Promise<any>;\n}\n\ninterface MinimalLogger {\n warn?: Logger['warn'];\n debug?: Logger['debug'];\n}\n\n/**\n * Durable {@link SuspendedRunStore} backed by the `sys_automation_run` object.\n *\n * Persists the resumable run state (`variables` / `steps` / `context` / `screen`)\n * JSON-serialized, so the engine's `Map`-based variable context round-trips. The\n * row is keyed by `runId` and removed on terminal completion; only live pauses\n * are stored. All access uses a system context — these are infrastructure rows,\n * not tenant data subject to RLS (the tenant is captured in `organization_id`\n * for scoping/observability).\n */\nexport class ObjectStoreSuspendedRunStore implements SuspendedRunStore {\n constructor(\n private readonly engine: SuspendedRunStoreEngine,\n private readonly logger?: MinimalLogger,\n ) {}\n\n async save(run: SuspendedRun): Promise<void> {\n const now = new Date().toISOString();\n const row = this.serialize(run);\n // Upsert: a re-suspend (the run paused again at a downstream node) updates\n // the existing row rather than inserting a duplicate.\n const existing = await this.engine.find(TABLE, {\n where: { id: run.runId }, limit: 1, context: SYSTEM_CTX,\n });\n if (Array.isArray(existing) && existing[0]) {\n await this.engine.update(\n TABLE,\n { ...row, updated_at: now },\n { where: { id: run.runId }, context: SYSTEM_CTX },\n );\n } else {\n await this.engine.insert(\n TABLE,\n { ...row, created_at: now, updated_at: now },\n { context: SYSTEM_CTX },\n );\n }\n }\n\n async load(runId: string): Promise<SuspendedRun | null> {\n const rows = await this.engine.find(TABLE, {\n where: { id: runId }, limit: 1, context: SYSTEM_CTX,\n });\n const row = Array.isArray(rows) ? rows[0] : null;\n return row ? this.deserialize(row) : null;\n }\n\n async delete(runId: string): Promise<void> {\n if (typeof this.engine.delete !== 'function') {\n this.logger?.warn?.(\n `[automation] ObjectStoreSuspendedRunStore: engine has no delete(); suspended run '${runId}' row not removed`,\n );\n return;\n }\n await this.engine.delete(TABLE, { where: { id: runId }, context: SYSTEM_CTX });\n }\n\n async list(): Promise<SuspendedRun[]> {\n const rows = await this.engine.find(TABLE, {\n where: { status: 'paused' }, limit: 1000, context: SYSTEM_CTX,\n });\n return (Array.isArray(rows) ? rows : []).map(r => this.deserialize(r));\n }\n\n /** Flatten a run into a `sys_automation_run` row (state columns JSON-encoded). */\n private serialize(run: SuspendedRun): Record<string, unknown> {\n const ctx = (run.context ?? {}) as Record<string, unknown>;\n const org = ctx.organizationId ?? ctx.tenantId ?? null;\n return {\n id: run.runId,\n organization_id: org,\n flow_name: run.flowName,\n flow_version: run.flowVersion ?? null,\n node_id: run.nodeId,\n status: 'paused',\n correlation: run.correlation ?? null,\n user_id: ctx.userId ?? null,\n variables_json: JSON.stringify(run.variables ?? {}),\n steps_json: JSON.stringify(run.steps ?? []),\n context_json: JSON.stringify(run.context ?? {}),\n screen_json: run.screen ? JSON.stringify(run.screen) : null,\n started_at: run.startedAt,\n start_time: run.startTime ?? null,\n };\n }\n\n /** Rebuild a run from a `sys_automation_run` row. */\n private deserialize(row: any): SuspendedRun {\n const startedAt = row.started_at ?? new Date().toISOString();\n return {\n runId: String(row.id),\n flowName: String(row.flow_name ?? ''),\n flowVersion: row.flow_version ?? undefined,\n nodeId: String(row.node_id ?? ''),\n variables: parseJson<Record<string, unknown>>(row.variables_json, {}),\n steps: parseJson<SuspendedRun['steps']>(row.steps_json, []),\n context: parseJson<SuspendedRun['context']>(row.context_json, {}),\n startedAt,\n startTime: typeof row.start_time === 'number' ? row.start_time : (Date.parse(startedAt) || Date.now()),\n correlation: row.correlation ?? undefined,\n screen: parseJson<SuspendedRun['screen']>(row.screen_json, undefined as any),\n };\n }\n}\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\nimport { ObjectSchema, Field } from '@objectstack/spec/data';\n\n/**\n * sys_automation_run — Durable state of a **suspended** automation flow run.\n *\n * ADR-0019: a flow that reaches a long-lived pause node (an `approval` node,\n * `wait`, `screen`, …) suspends. Without persistence the continuation lives\n * only in the engine's in-memory map, so a process restart (e.g. a hibernating\n * Cloudflare Worker) loses the run and `resume(runId)` fails even though the\n * approval record survives. Persisting the run here makes the pause **durable**:\n * the engine writes a row on suspend and deletes it on terminal completion, so a\n * cold-booted kernel can rehydrate and continue.\n *\n * Lifecycle: one row per *currently* suspended run. The row is removed when the\n * run resumes to completion or fails — only live pauses are stored. `id` is the\n * `runId`; `correlation` ties back to the pausing node's external state (e.g.\n * `sys_approval_request.id`, mirrored by `sys_approval_request.flow_run_id`).\n *\n * The resumable state (`variables` / `steps` / `context` / `screen`) is stored\n * JSON-serialized — the engine works with a `Map`, which round-trips through\n * these `*_json` columns.\n *\n * Writers: the automation engine's durable {@link SuspendedRunStore}.\n * Readers: operability surfaces (a \"pending/suspended runs\" view), the engine on\n * resume after a restart.\n *\n * @namespace sys\n */\nexport const SysAutomationRun = ObjectSchema.create({\n name: 'sys_automation_run',\n label: 'Automation Run',\n pluralLabel: 'Automation Runs',\n icon: 'pause-circle',\n isSystem: true,\n managedBy: 'system',\n description: 'Durable state of a suspended automation flow run (ADR-0019)',\n displayNameField: 'id',\n titleFormat: '{flow_name} · {node_id}',\n compactLayout: ['flow_name', 'node_id', 'status', 'correlation', 'started_at', 'updated_at'],\n\n fields: {\n id: Field.text({ label: 'Run ID', required: true, readonly: true, group: 'System' }),\n\n organization_id: Field.lookup('sys_organization', {\n label: 'Organization',\n required: false,\n group: 'System',\n description: 'Tenant that owns this run (propagated from the trigger context)',\n }),\n\n flow_name: Field.text({\n label: 'Flow',\n required: true,\n maxLength: 255,\n searchable: true,\n group: 'Identity',\n }),\n\n flow_version: Field.number({ label: 'Flow Version', required: false, group: 'Identity' }),\n\n node_id: Field.text({\n label: 'Paused Node',\n required: true,\n maxLength: 255,\n description: 'Node the run is suspended at; resume continues from its out-edges.',\n group: 'State',\n }),\n\n status: Field.select(\n ['paused'],\n {\n label: 'Status',\n required: true,\n defaultValue: 'paused',\n description: 'Only suspended runs are persisted; the row is deleted on terminal completion.',\n group: 'State',\n },\n ),\n\n correlation: Field.text({\n label: 'Correlation',\n required: false,\n maxLength: 255,\n description: 'Correlation key from the pausing node (e.g. approval request id).',\n group: 'State',\n }),\n\n user_id: Field.text({\n label: 'User',\n required: false,\n maxLength: 255,\n description: 'User who triggered the run (from context.userId).',\n group: 'State',\n }),\n\n variables_json: Field.textarea({\n label: 'Variables',\n required: false,\n description: 'JSON snapshot of the flow variable map at suspend time.',\n group: 'State',\n }),\n\n steps_json: Field.textarea({\n label: 'Steps',\n required: false,\n description: 'JSON snapshot of the executed step logs so far.',\n group: 'State',\n }),\n\n context_json: Field.textarea({\n label: 'Context',\n required: false,\n description: 'JSON snapshot of the trigger / automation context.',\n group: 'State',\n }),\n\n screen_json: Field.textarea({\n label: 'Screen',\n required: false,\n description: 'JSON snapshot of the screen spec the run is waiting on (screen-flow runtime).',\n group: 'State',\n }),\n\n started_at: Field.datetime({ label: 'Started At', required: true, group: 'State' }),\n\n start_time: Field.number({\n label: 'Start Time (epoch ms)',\n required: false,\n description: 'Epoch ms when the run started; used to compute duration on resume.',\n group: 'State',\n }),\n\n created_at: Field.datetime({\n label: 'Created At',\n required: true,\n defaultValue: 'NOW()',\n readonly: true,\n group: 'System',\n }),\n\n updated_at: Field.datetime({ label: 'Updated At', required: false, group: 'System' }),\n },\n\n indexes: [\n // \"Which runs are suspended for this flow?\" — operability / resume sweeps.\n { fields: ['flow_name', 'status'] },\n { fields: ['status', 'updated_at'] },\n // Look up a suspended run by the pausing node's correlation key.\n { fields: ['correlation'] },\n ],\n});\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\nimport type { PluginContext } from '@objectstack/core';\nimport { defineActionDescriptor } from '@objectstack/spec/automation';\nimport type { AutomationEngine } from '../engine.js';\n\n/**\n * Logic built-in nodes — decision / assignment.\n *\n * (The `loop` container is registered separately — see `loop-node.ts` — as a\n * structured iteration construct per ADR-0031.)\n *\n * Part of the automation engine's foundational vocabulary, so the core\n * {@link AutomationServicePlugin} seeds them directly (ADR-0018). These are NOT\n * shipped as a separately installable plugin — \"plugins are plugins; the\n * platform's foundational capabilities are built in.\" Third-party node types\n * are still registered via `engine.registerNodeExecutor()`.\n */\nexport function registerLogicNodes(engine: AutomationEngine, ctx: PluginContext): void {\n // decision node — conditional branching\n engine.registerNodeExecutor({\n type: 'decision',\n descriptor: defineActionDescriptor({\n type: 'decision', version: '1.0.0', name: 'Decision',\n description: 'Branch execution based on conditions.',\n icon: 'git-branch', category: 'logic', source: 'builtin',\n }),\n async execute(node, variables, _context) {\n const config = node.config as Record<string, unknown> | undefined;\n const conditions = (config?.conditions ?? []) as Array<{ label: string; expression: string }>;\n\n for (const cond of conditions) {\n if (engine.evaluateCondition(cond.expression, variables)) {\n return { success: true, branchLabel: cond.label };\n }\n }\n return { success: true, branchLabel: 'default' };\n },\n });\n\n // assignment node — set variables\n engine.registerNodeExecutor({\n type: 'assignment',\n descriptor: defineActionDescriptor({\n type: 'assignment', version: '1.0.0', name: 'Assignment',\n description: 'Set flow variables.',\n icon: 'variable', category: 'logic', source: 'builtin',\n }),\n async execute(node, variables, _context) {\n const config = (node.config ?? {}) as Record<string, unknown>;\n for (const [key, value] of Object.entries(config)) {\n variables.set(key, value);\n }\n return { success: true };\n },\n });\n\n ctx.logger.info('[Logic Nodes] 2 built-in node executors registered');\n}\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\nimport type { PluginContext } from '@objectstack/core';\nimport { defineActionDescriptor, LOOP_MAX_ITERATIONS_CEILING } from '@objectstack/spec/automation';\nimport type { FlowRegionParsed } from '@objectstack/spec/automation';\nimport type { AutomationContext } from '@objectstack/spec/contracts';\nimport type { AutomationEngine, StepLogEntry } from '../engine.js';\nimport { interpolate } from './template.js';\n\n/**\n * `loop` built-in node — a **structured iteration container** (ADR-0031).\n *\n * Replaces the previous no-op `loop` stub. The node owns a bounded **body\n * region** (`config.body`, a single-entry/single-exit sub-graph) and drives\n * iteration over a collection: for each item it binds the current value to\n * `config.iteratorVariable` (and the zero-based index to `config.indexVariable`,\n * when given) in the **enclosing variable scope** and runs the body region. A\n * **hard max-iteration guard** (`config.maxIterations`, clamped to\n * {@link LOOP_MAX_ITERATIONS_CEILING}) keeps termination analyzable.\n *\n * The body region runs as a unit via {@link AutomationEngine.runRegion}; the\n * loop node's *ordinary* out-edges in the main graph remain the \"after-loop\"\n * continuation, so the DAG invariant for ordinary edges is preserved.\n *\n * **Back-compat:** a `loop` node with no `config.body` keeps the legacy\n * flat-graph behavior (sets `$loopItems`/`$loopIndex` and falls through) — the\n * container construct is additive.\n */\nexport function registerLoopNode(engine: AutomationEngine, ctx: PluginContext): void {\n engine.registerNodeExecutor({\n type: 'loop',\n descriptor: defineActionDescriptor({\n type: 'loop',\n version: '2.0.0',\n name: 'Loop',\n description: 'Iterate a body region over a collection (bounded, structured container).',\n icon: 'repeat',\n category: 'logic',\n source: 'builtin',\n configSchema: {\n type: 'object',\n properties: {\n collection: { type: 'string', description: 'Template/variable resolving to the array to iterate' },\n iteratorVariable: { type: 'string', description: 'Loop variable holding the current item' },\n indexVariable: { type: 'string', description: 'Optional loop variable holding the current index' },\n maxIterations: { type: 'integer', minimum: 1, maximum: LOOP_MAX_ITERATIONS_CEILING },\n body: {\n type: 'object',\n description: 'Loop body region (single-entry/single-exit sub-graph)',\n properties: { nodes: { type: 'array' }, edges: { type: 'array' } },\n },\n },\n required: ['collection'],\n },\n }),\n async execute(node, variables, context) {\n const cfg = (node.config ?? {}) as Record<string, unknown>;\n const body = cfg.body as FlowRegionParsed | undefined;\n\n // ── Legacy flat-graph loop (no body) — preserve prior stub behavior. ──\n if (body == null) {\n const collectionName = typeof cfg.collection === 'string' ? cfg.collection : undefined;\n if (collectionName) {\n const legacy = variables.get(collectionName);\n if (Array.isArray(legacy)) {\n variables.set('$loopItems', legacy);\n variables.set('$loopIndex', 0);\n }\n }\n return { success: true };\n }\n\n // ── Structured loop container. ──\n const iteratorVariable = typeof cfg.iteratorVariable === 'string' && cfg.iteratorVariable\n ? cfg.iteratorVariable\n : 'item';\n const indexVariable = typeof cfg.indexVariable === 'string' && cfg.indexVariable\n ? cfg.indexVariable\n : undefined;\n\n // Resolve the collection: a `{token}` template, a bare variable name, or\n // (defensively) an already-resolved array.\n const rawCollection = cfg.collection;\n let collection: unknown;\n if (Array.isArray(rawCollection)) {\n collection = rawCollection;\n } else if (typeof rawCollection === 'string') {\n collection = interpolate(rawCollection, variables, context ?? ({} as AutomationContext));\n if ((collection == null) && variables.has(rawCollection)) {\n collection = variables.get(rawCollection);\n }\n }\n\n if (!Array.isArray(collection)) {\n return {\n success: false,\n error: `loop '${node.id}': collection '${String(rawCollection)}' did not resolve to an array`,\n };\n }\n\n // Hard iteration guard.\n const requested = typeof cfg.maxIterations === 'number' ? cfg.maxIterations : LOOP_MAX_ITERATIONS_CEILING;\n const maxIterations = Math.min(requested, LOOP_MAX_ITERATIONS_CEILING);\n if (collection.length > maxIterations) {\n return {\n success: false,\n error:\n `loop '${node.id}': collection length ${collection.length} exceeds maxIterations ${maxIterations}`,\n };\n }\n\n let iterations = 0;\n const childSteps: StepLogEntry[] = [];\n for (let i = 0; i < collection.length; i++) {\n variables.set(iteratorVariable, collection[i]);\n if (indexVariable) variables.set(indexVariable, i);\n // Body runs in the shared scope; iterator var + mutations are visible.\n // #1479: collect each iteration's body steps, tagged with the index.\n const iterSteps = await engine.runRegion(body, variables, context ?? ({} as AutomationContext), {\n parentNodeId: node.id,\n iteration: i,\n regionKind: 'loop-body',\n });\n childSteps.push(...iterSteps);\n iterations++;\n }\n\n return { success: true, output: { iterations }, childSteps };\n },\n });\n\n ctx.logger.info('[Loop Node] 1 built-in node executor registered');\n}\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\n/**\n * Template interpolation helpers shared across node executors.\n *\n * Supported syntax (intentionally minimal — no full expression language):\n *\n * {variable} → variables.get('variable')\n * {variable.path.segment} → walks dotted path on the resolved value\n * {list.0} / {rec.items.2} → numeric segments index into arrays\n * {$User.Id} → reads from context.userId\n * {$User.Email} → reads from context.user?.email\n * {NOW()} → ISO timestamp at evaluation time\n * {TODAY()} → YYYY-MM-DD at evaluation time\n * {TODAY() + 90} → date + N days (days only, integer)\n *\n * Anything that fails to resolve becomes the literal `null` value (for\n * single-token templates) or the empty string (for embedded substitution),\n * matching the behavior of common low-code formula engines.\n *\n * The interpolator walks objects, arrays, and primitives recursively so it\n * can be applied wholesale to a node's `config.fields`/`config.filter` blocks.\n */\n\nimport type { AutomationContext } from '@objectstack/spec/contracts';\n\nexport type VariableMap = Map<string, unknown>;\n\n/**\n * Resolve a dotted path against a base value.\n * Returns `undefined` for any missing intermediate node.\n */\nfunction resolvePath(base: unknown, path: string[]): unknown {\n let cur: unknown = base;\n for (const seg of path) {\n if (cur == null) return undefined;\n if (typeof cur !== 'object') return undefined;\n cur = (cur as Record<string, unknown>)[seg];\n }\n return cur;\n}\n\n/**\n * Resolve a single template token (without braces) to a value.\n * Returns `undefined` if the token cannot be resolved.\n */\nfunction resolveToken(token: string, variables: VariableMap, context: AutomationContext): unknown {\n const trimmed = token.trim();\n if (!trimmed) return undefined;\n\n // Built-in date helpers — `NOW()` / `TODAY()` with optional `+ N` day offset.\n // The offset may be a literal integer or any token resolvable from `variables`.\n const dateFnMatch = /^(NOW|TODAY)\\s*\\(\\s*\\)\\s*(?:([+\\-])\\s*(\\S+))?$/.exec(trimmed);\n if (dateFnMatch) {\n const fn = dateFnMatch[1];\n const sign = dateFnMatch[2] === '-' ? -1 : 1;\n const offsetRaw = dateFnMatch[3];\n let offset = 0;\n if (offsetRaw) {\n const asNum = Number(offsetRaw);\n if (!isNaN(asNum)) {\n offset = asNum;\n } else if (variables.has(offsetRaw)) {\n offset = Number(variables.get(offsetRaw)) || 0;\n }\n }\n const now = new Date();\n if (offset) now.setDate(now.getDate() + sign * offset);\n if (fn === 'NOW') return now.toISOString();\n return now.toISOString().slice(0, 10);\n }\n\n // $User.* shortcuts\n if (trimmed.startsWith('$User.')) {\n const path = trimmed.slice('$User.'.length).split('.');\n if (path[0] === 'Id') return context.userId;\n if (path[0] === 'Email') return resolvePath((context as any).user, ['email', ...path.slice(1)]) ?? undefined;\n return resolvePath((context as any).user, path);\n }\n\n // Direct variable / dotted path lookup (fast path, no arithmetic).\n // Path segments after the head may be identifiers OR array indices (`\\d+`),\n // so `{list.0}` / `{record.target_channels.0}` resolve into arrays (#1872).\n if (/^[A-Za-z_$][\\w$]*(?:\\.(?:[A-Za-z_$][\\w$]*|\\d+))*$/.test(trimmed)) {\n const segments = trimmed.split('.');\n const head = segments[0];\n if (variables.has(head)) {\n return resolvePath(variables.get(head), segments.slice(1));\n }\n if (variables.has(trimmed)) return variables.get(trimmed);\n return undefined;\n }\n\n // Arithmetic / mixed expression: substitute variable references (foo, foo.bar)\n // with their numeric/string literal forms, then evaluate via Function().\n // Restricted to a safe character set (digits, basic operators, parentheses,\n // dots and identifier characters) — never executed on raw user input.\n if (!/^[\\w\\s+\\-*/%().,?:<>=!&|\"'$]+$/.test(trimmed)) return undefined;\n let safe = trimmed;\n safe = safe.replace(/([A-Za-z_$][\\w$]*(?:\\.[A-Za-z_$][\\w$]*)*)/g, (match) => {\n // Don't substitute reserved literals\n if (match === 'true' || match === 'false' || match === 'null' || match === 'undefined') return match;\n const segs = match.split('.');\n const head = segs[0];\n let val: unknown;\n if (variables.has(head)) val = resolvePath(variables.get(head), segs.slice(1));\n else if (variables.has(match)) val = variables.get(match);\n if (val === undefined || val === null) return 'null';\n if (typeof val === 'number' || typeof val === 'boolean') return String(val);\n return JSON.stringify(String(val));\n });\n try {\n // eslint-disable-next-line @typescript-eslint/no-implied-eval, no-new-func\n const fn = new Function(`\"use strict\"; return (${safe});`);\n return fn();\n } catch {\n return undefined;\n }\n}\n\n/**\n * Replace `{...}` tokens in a string with resolved values.\n * - When the entire string is a single token, returns the raw value (preserving type).\n * - Otherwise concatenates string substitutions, with `null`/`undefined` rendered as ''.\n */\nexport function interpolateString(\n input: string,\n variables: VariableMap,\n context: AutomationContext,\n): unknown {\n if (!input.includes('{')) return input;\n const single = /^\\{([^{}]+)\\}$/.exec(input);\n if (single) {\n const value = resolveToken(single[1], variables, context);\n return value;\n }\n return input.replace(/\\{([^{}]+)\\}/g, (_match, expr) => {\n const value = resolveToken(expr, variables, context);\n if (value === undefined || value === null) return '';\n return String(value);\n });\n}\n\n/**\n * Recursively interpolate template tokens in arbitrary JSON-like values.\n */\nexport function interpolate<T = unknown>(\n value: T,\n variables: VariableMap,\n context: AutomationContext,\n): T {\n if (typeof value === 'string') {\n return interpolateString(value, variables, context) as unknown as T;\n }\n if (Array.isArray(value)) {\n return value.map(v => interpolate(v, variables, context)) as unknown as T;\n }\n if (value && typeof value === 'object') {\n const out: Record<string, unknown> = {};\n for (const [k, v] of Object.entries(value as Record<string, unknown>)) {\n out[k] = interpolate(v, variables, context);\n }\n return out as unknown as T;\n }\n return value;\n}\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\nimport type { PluginContext } from '@objectstack/core';\nimport { defineActionDescriptor } from '@objectstack/spec/automation';\nimport type { FlowRegionParsed } from '@objectstack/spec/automation';\nimport type { AutomationContext } from '@objectstack/spec/contracts';\nimport type { AutomationEngine, StepLogEntry } from '../engine.js';\n\n/** One branch of a parallel block — a region plus an optional label. */\ninterface ParallelBranch extends FlowRegionParsed {\n name?: string;\n}\n\n/**\n * `parallel` built-in node — a **structured parallel block** with an\n * **implicit join** (ADR-0031 §Decision 2).\n *\n * The node declares N branch regions in `config.branches[]`; each branch is a\n * self-contained single-entry/single-exit sub-graph (validated at\n * `registerFlow()`). The executor runs every branch concurrently\n * (`Promise.all`) in the **enclosing variable scope** and continues **once when\n * all branches complete** — the join is implicit at block end, engine\n * synchronized. There is no author-visible split/join gateway to mis-wire or\n * deadlock; the node's ordinary out-edges remain the after-block continuation.\n *\n * Concurrency model: JavaScript is single-threaded, so branches interleave only\n * at `await` points and the shared `variables` map is never torn. Branches\n * SHOULD write distinct variables; on a key collision the last writer to settle\n * wins (same semantics as the engine's existing unconditional-edge fan-out).\n *\n * If any branch fails (a node returns `success: false` or throws), the block\n * fails — surfaced as a node failure so the flow's fault edge / error handling\n * applies. Durable pause inside a branch is unsupported (a clear error), mirror-\n * ing the loop container.\n */\nexport function registerParallelNode(engine: AutomationEngine, ctx: PluginContext): void {\n engine.registerNodeExecutor({\n type: 'parallel',\n descriptor: defineActionDescriptor({\n type: 'parallel',\n version: '1.0.0',\n name: 'Parallel',\n description: 'Run N branch regions concurrently and join implicitly when all complete.',\n icon: 'git-fork',\n category: 'logic',\n source: 'builtin',\n configSchema: {\n type: 'object',\n properties: {\n branches: {\n type: 'array',\n minItems: 2,\n description: 'Branch regions executed concurrently; implicit join at block end',\n items: {\n type: 'object',\n properties: {\n name: { type: 'string' },\n nodes: { type: 'array' },\n edges: { type: 'array' },\n },\n },\n },\n },\n required: ['branches'],\n },\n }),\n async execute(node, variables, context) {\n const cfg = (node.config ?? {}) as Record<string, unknown>;\n const branches = cfg.branches as ParallelBranch[] | undefined;\n\n if (!Array.isArray(branches) || branches.length < 2) {\n return {\n success: false,\n error: `parallel '${node.id}': config.branches must declare at least 2 branch regions`,\n };\n }\n\n let branchSteps: StepLogEntry[][];\n try {\n // Implicit join: continue once when ALL branches have completed.\n // #1479: each branch returns its body steps, tagged with the branch index.\n branchSteps = await Promise.all(\n branches.map((branch, i) =>\n engine.runRegion(branch, variables, context ?? ({} as AutomationContext), {\n parentNodeId: node.id,\n iteration: i,\n regionKind: 'parallel-branch',\n }),\n ),\n );\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n return { success: false, error: `parallel '${node.id}': branch failed — ${message}` };\n }\n\n return { success: true, output: { branches: branches.length }, childSteps: branchSteps.flat() };\n },\n });\n\n ctx.logger.info('[Parallel Node] 1 built-in node executor registered');\n}\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\nimport type { PluginContext } from '@objectstack/core';\nimport { defineActionDescriptor } from '@objectstack/spec/automation';\nimport type { FlowRegionParsed } from '@objectstack/spec/automation';\nimport type { AutomationContext } from '@objectstack/spec/contracts';\nimport type { AutomationEngine } from '../engine.js';\n\ninterface RetryPolicy {\n maxRetries?: number;\n retryDelayMs?: number;\n backoffMultiplier?: number;\n maxRetryDelayMs?: number;\n jitter?: boolean;\n}\n\n/**\n * `try_catch` built-in node — **structured try/catch/retry** (ADR-0031 §Decision 3).\n *\n * Runs the protected `try` region; if it throws (a node fails), an optional\n * `retry` policy re-runs the `try` region with exponential backoff. If the\n * region still fails after retries, the optional `catch` region runs with the\n * caught error bound to `errorVariable` (default `$error`). Both regions are\n * self-contained single-entry/single-exit sub-graphs validated at\n * `registerFlow()`, executed in the **enclosing variable scope** via\n * {@link AutomationEngine.runRegion}.\n *\n * Outcome:\n * - `try` (or a retry) succeeds → the node succeeds, downstream continues.\n * - `try` exhausts retries, a `catch` is present and succeeds → the node\n * succeeds (the error was handled).\n * - `try` exhausts retries and there is **no** `catch` (or `catch` itself\n * fails) → the node fails, surfacing to the flow's fault edge / error handling.\n *\n * This is the low-code-native error model — the same `fault` + exponential-\n * backoff retry the engine already implements, surfaced as a construct rather\n * than BPMN boundary events.\n */\nexport function registerTryCatchNode(engine: AutomationEngine, ctx: PluginContext): void {\n engine.registerNodeExecutor({\n type: 'try_catch',\n descriptor: defineActionDescriptor({\n type: 'try_catch',\n version: '1.0.0',\n name: 'Try / Catch',\n description: 'Run a protected region with optional retry and a catch handler (structured error handling).',\n icon: 'shield-alert',\n category: 'logic',\n source: 'builtin',\n supportsRetry: true,\n configSchema: {\n type: 'object',\n properties: {\n try: {\n type: 'object',\n description: 'Protected region (single-entry/single-exit sub-graph)',\n properties: { nodes: { type: 'array' }, edges: { type: 'array' } },\n },\n catch: {\n type: 'object',\n description: 'Handler region run when the try region fails',\n properties: { nodes: { type: 'array' }, edges: { type: 'array' } },\n },\n errorVariable: { type: 'string', description: 'Variable holding the caught error in the catch region' },\n retry: {\n type: 'object',\n properties: {\n maxRetries: { type: 'integer', minimum: 0, maximum: 10 },\n retryDelayMs: { type: 'integer', minimum: 0 },\n backoffMultiplier: { type: 'number', minimum: 1 },\n maxRetryDelayMs: { type: 'integer', minimum: 0 },\n jitter: { type: 'boolean' },\n },\n },\n },\n required: ['try'],\n },\n }),\n async execute(node, variables, context) {\n const cfg = (node.config ?? {}) as Record<string, unknown>;\n const tryRegion = cfg.try as FlowRegionParsed | undefined;\n const catchRegion = cfg.catch as FlowRegionParsed | undefined;\n const errorVariable =\n typeof cfg.errorVariable === 'string' && cfg.errorVariable ? cfg.errorVariable : '$error';\n const retry = (cfg.retry ?? {}) as RetryPolicy;\n\n if (tryRegion == null) {\n return { success: false, error: `try_catch '${node.id}': config.try region is required` };\n }\n\n const ctxOrEmpty = context ?? ({} as AutomationContext);\n const maxRetries = typeof retry.maxRetries === 'number' ? retry.maxRetries : 0;\n const baseDelay = typeof retry.retryDelayMs === 'number' ? retry.retryDelayMs : 0;\n const multiplier = typeof retry.backoffMultiplier === 'number' ? retry.backoffMultiplier : 1;\n const maxDelay = typeof retry.maxRetryDelayMs === 'number' ? retry.maxRetryDelayMs : 30000;\n const useJitter = retry.jitter === true;\n\n // Run the try region, retrying with exponential backoff up to maxRetries.\n let lastError = 'unknown error';\n for (let attempt = 0; attempt <= maxRetries; attempt++) {\n if (attempt > 0) {\n let delay = Math.min(baseDelay * Math.pow(multiplier, attempt - 1), maxDelay);\n if (useJitter) delay = delay * (0.5 + Math.random() * 0.5);\n if (delay > 0) await new Promise(r => setTimeout(r, delay));\n }\n try {\n // #1479: surface the successful try region's steps.\n const trySteps = await engine.runRegion(tryRegion, variables, ctxOrEmpty, {\n parentNodeId: node.id,\n regionKind: 'try',\n });\n return { success: true, output: { attempts: attempt + 1, caught: false }, childSteps: trySteps };\n } catch (err) {\n lastError = err instanceof Error ? err.message : String(err);\n }\n }\n\n // The try region (and any retries) failed. Run the catch handler if present.\n if (catchRegion != null) {\n variables.set(errorVariable, { nodeId: node.id, message: lastError });\n try {\n // #1479: surface the catch handler region's steps.\n const catchSteps = await engine.runRegion(catchRegion, variables, ctxOrEmpty, {\n parentNodeId: node.id,\n regionKind: 'catch',\n });\n return {\n success: true,\n output: { attempts: maxRetries + 1, caught: true, error: lastError },\n childSteps: catchSteps,\n };\n } catch (catchErr) {\n const catchMsg = catchErr instanceof Error ? catchErr.message : String(catchErr);\n return { success: false, error: `try_catch '${node.id}': catch region failed — ${catchMsg}` };\n }\n }\n\n // No catch handler — surface the failure to the flow's fault edge / error handling.\n return { success: false, error: `try_catch '${node.id}': try region failed — ${lastError}` };\n },\n });\n\n ctx.logger.info('[TryCatch Node] 1 built-in node executor registered');\n}\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\nimport type { PluginContext } from '@objectstack/core';\nimport { defineActionDescriptor } from '@objectstack/spec/automation';\nimport type { IDataEngine } from '@objectstack/spec/contracts';\nimport type { AutomationEngine } from '../engine.js';\nimport { interpolate } from './template.js';\n\n/**\n * CRUD built-in nodes — `get_record` / `create_record` / `update_record` /\n * `delete_record`, wired to the runtime data layer (ObjectQL / IDataEngine).\n * Part of the platform baseline, so the core {@link AutomationServicePlugin}\n * seeds them directly (ADR-0018) rather than shipping a separate plugin.\n *\n * Each executor:\n * 1. Interpolates `{var}` / `{var.path}` / `{$User.*}` / `{NOW()}` tokens in\n * `node.config` against the running flow's variable context.\n * 2. Calls the resolved data engine via `ctx.getService('data')`.\n * 3. Writes the result back to the variable context under `outputVariable`\n * (or under `<nodeId>.id` / `<nodeId>.records` by default), so downstream\n * nodes can reference fields like `{leadRecord.company}`.\n *\n * If no data engine is registered, executors degrade to a no-op success so\n * test environments without ObjectQL still complete the flow without errors.\n */\nexport function registerCrudNodes(engine: AutomationEngine, ctx: PluginContext): void {\n const getData = (): IDataEngine | undefined => {\n try {\n return ctx.getService<IDataEngine>('data') ?? ctx.getService<IDataEngine>('objectql');\n } catch {\n return undefined;\n }\n };\n\n // ── get_record ────────────────────────────────────────\n engine.registerNodeExecutor({\n type: 'get_record',\n descriptor: defineActionDescriptor({\n type: 'get_record', version: '1.0.0', name: 'Get Records',\n description: 'Query records from an object.',\n icon: 'search', category: 'data', source: 'builtin',\n }),\n async execute(node, variables, context) {\n const cfg = (node.config ?? {}) as Record<string, unknown>;\n const objectName = String(cfg.objectName ?? cfg.object ?? '');\n if (!objectName) return { success: false, error: 'get_record: objectName required' };\n\n const filter = interpolate(cfg.filter ?? cfg.filters ?? {}, variables, context) as Record<string, unknown>;\n const fields = cfg.fields as string[] | undefined;\n const limit = typeof cfg.limit === 'number' ? cfg.limit : undefined;\n const outputVariable = cfg.outputVariable as string | undefined;\n\n const data = getData();\n if (!data) {\n ctx.logger.warn(`[get_record] no data engine; skipping ${objectName}`);\n return { success: true, output: { records: [], object: objectName } };\n }\n\n try {\n if (limit && limit > 1) {\n const records = await data.find(objectName, { where: filter, fields, limit });\n if (outputVariable) variables.set(outputVariable, records);\n return { success: true, output: { records, object: objectName } };\n }\n const record = await data.findOne(objectName, { where: filter, fields });\n if (outputVariable) variables.set(outputVariable, record);\n return { success: true, output: { record, id: record?.id, object: objectName } };\n } catch (err) {\n return { success: false, error: `get_record(${objectName}) failed: ${(err as Error).message}` };\n }\n },\n });\n\n // ── create_record ─────────────────────────────────────\n engine.registerNodeExecutor({\n type: 'create_record',\n descriptor: defineActionDescriptor({\n type: 'create_record', version: '1.0.0', name: 'Create Record',\n description: 'Insert a new record into an object.',\n icon: 'plus-circle', category: 'data', source: 'builtin',\n }),\n async execute(node, variables, context) {\n const cfg = (node.config ?? {}) as Record<string, unknown>;\n const objectName = String(cfg.objectName ?? cfg.object ?? '');\n if (!objectName) return { success: false, error: 'create_record: objectName required' };\n\n const fields = interpolate(cfg.fields ?? {}, variables, context) as Record<string, unknown>;\n const outputVariable = cfg.outputVariable as string | undefined;\n\n const data = getData();\n if (!data) {\n ctx.logger.warn(`[create_record] no data engine; skipping ${objectName}`);\n const mockId = `mock-${objectName}-${Date.now()}`;\n if (outputVariable) variables.set(outputVariable, { id: mockId });\n return { success: true, output: { id: mockId, object: objectName } };\n }\n\n try {\n const created = await data.insert(objectName, fields);\n const createdRecord = Array.isArray(created) ? created[0] : created;\n const insertedId =\n createdRecord && typeof createdRecord === 'object'\n ? (createdRecord as Record<string, unknown>).id\n : createdRecord;\n if (outputVariable) {\n // #1873 — expose the created RECORD so later nodes can reference\n // `{var.id}` (and other fields), not just the bare id string. When the\n // driver returns a bare id, wrap it as `{ id }` so `{var.id}` still works.\n variables.set(\n outputVariable,\n createdRecord && typeof createdRecord === 'object' ? createdRecord : { id: insertedId },\n );\n }\n return { success: true, output: { id: insertedId, record: createdRecord, object: objectName } };\n } catch (err) {\n return { success: false, error: `create_record(${objectName}) failed: ${(err as Error).message}` };\n }\n },\n });\n\n // ── update_record ─────────────────────────────────────\n engine.registerNodeExecutor({\n type: 'update_record',\n descriptor: defineActionDescriptor({\n type: 'update_record', version: '1.0.0', name: 'Update Records',\n description: 'Update records matching a filter.',\n icon: 'edit', category: 'data', source: 'builtin',\n }),\n async execute(node, variables, context) {\n const cfg = (node.config ?? {}) as Record<string, unknown>;\n const objectName = String(cfg.objectName ?? cfg.object ?? '');\n if (!objectName) return { success: false, error: 'update_record: objectName required' };\n\n const filter = interpolate(cfg.filter ?? cfg.filters ?? {}, variables, context) as Record<string, unknown>;\n const fields = interpolate(cfg.fields ?? {}, variables, context) as Record<string, unknown>;\n\n const data = getData();\n if (!data) {\n ctx.logger.warn(`[update_record] no data engine; skipping ${objectName}`);\n return { success: true };\n }\n\n try {\n const result = await data.update(objectName, fields, { where: filter });\n return { success: true, output: { result, object: objectName } };\n } catch (err) {\n return { success: false, error: `update_record(${objectName}) failed: ${(err as Error).message}` };\n }\n },\n });\n\n // ── delete_record ─────────────────────────────────────\n engine.registerNodeExecutor({\n type: 'delete_record',\n descriptor: defineActionDescriptor({\n type: 'delete_record', version: '1.0.0', name: 'Delete Records',\n description: 'Delete records matching a filter.',\n icon: 'trash', category: 'data', source: 'builtin',\n }),\n async execute(node, variables, context) {\n const cfg = (node.config ?? {}) as Record<string, unknown>;\n const objectName = String(cfg.objectName ?? cfg.object ?? '');\n if (!objectName) return { success: false, error: 'delete_record: objectName required' };\n\n const filter = interpolate(cfg.filter ?? cfg.filters ?? {}, variables, context) as Record<string, unknown>;\n\n const data = getData();\n if (!data) return { success: true };\n\n try {\n const result = await data.delete(objectName, { where: filter });\n return { success: true, output: { result, object: objectName } };\n } catch (err) {\n return { success: false, error: `delete_record(${objectName}) failed: ${(err as Error).message}` };\n }\n },\n });\n\n ctx.logger.info('[CRUD Nodes] 4 built-in node executors registered (data-backed)');\n}\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\nimport type { PluginContext } from '@objectstack/core';\nimport { defineActionDescriptor } from '@objectstack/spec/automation';\nimport type { AutomationEngine } from '../engine.js';\nimport { interpolate } from './template.js';\n\n/**\n * Screen / Script built-in nodes — 'screen' and 'script' executors.\n * Part of the core flow capability, so the {@link AutomationServicePlugin}\n * seeds them directly (ADR-0018) rather than shipping a separate plugin.\n *\n * - 'screen' nodes collect user input. A screen that declares `config.fields`\n * (or sets `config.waitForInput === true`) suspends the run on entry via the\n * engine's durable pause (ADR-0019), surfacing a `ScreenSpec` for the client\n * to render; the run continues via `resume()` with the collected values (set\n * as bare flow variables). A field-less screen — or one with\n * `waitForInput === false` — stays a server pass-through (input vars, if any,\n * are already injected from `context.params`).\n * - 'script' nodes name a callable to run (#1870):\n * - `config.actionType` selecting a built-in side-effect ('email', 'slack',\n * logger-backed), or\n * - `config.function` (or a bare `actionType` that matches no built-in)\n * naming a registered function — resolved via `engine.resolveFunction()`,\n * which the host bridges to `bundle.functions` / `defineStack({ functions })`.\n * A target that resolves to neither fails the step LOUDLY rather than the old\n * silent \"no-op handler\" success, so an unwired callable can't quietly skip.\n */\n\n/**\n * Built-in `script` side-effect action types with a (logger-backed) handler.\n * Anything else is treated as a registered-function name (#1870).\n */\nconst SCRIPT_BUILTIN_ACTION_TYPES = new Set(['email', 'slack']);\n\nexport function registerScreenNodes(engine: AutomationEngine, ctx: PluginContext): void {\n // screen — server-side pass-through (input vars already injected by engine).\n engine.registerNodeExecutor({\n type: 'screen',\n descriptor: defineActionDescriptor({\n type: 'screen', version: '1.0.0', name: 'Screen',\n description: 'Collect user input via a screen (human-input element).',\n icon: 'window', category: 'human', source: 'builtin',\n // Human-input nodes suspend the flow awaiting input.\n supportsPause: true, isAsync: true,\n }),\n async execute(node, _variables, _context) {\n const cfg = (node.config ?? {}) as Record<string, unknown>;\n const rawFields = Array.isArray(cfg.fields) ? (cfg.fields as Array<Record<string, unknown>>) : [];\n const hasFields = rawFields.length > 0;\n // Suspend to collect input when the screen declares fields, or opts in\n // explicitly. `waitForInput === false` forces a server pass-through.\n const shouldPause = cfg.waitForInput === true || (hasFields && cfg.waitForInput !== false);\n if (!shouldPause) {\n return { success: true };\n }\n const fields = rawFields.map((f) => ({\n name: String(f.name ?? ''),\n label: f.label != null ? String(f.label) : undefined,\n type: f.type != null ? String(f.type) : undefined,\n required: f.required === true,\n options: Array.isArray(f.options) ? (f.options as Array<{ value: unknown; label: string }>) : undefined,\n defaultValue: f.defaultValue,\n placeholder: f.placeholder != null ? String(f.placeholder) : undefined,\n })).filter((f) => f.name.length > 0);\n return {\n success: true,\n suspend: true,\n screen: {\n nodeId: node.id,\n title: (cfg.title as string | undefined) ?? node.label ?? 'Input',\n description: cfg.description as string | undefined,\n fields,\n },\n };\n },\n });\n\n // script — dispatch by actionType.\n engine.registerNodeExecutor({\n type: 'script',\n descriptor: defineActionDescriptor({\n type: 'script', version: '1.0.0', name: 'Script',\n description: 'Run a custom script action.',\n icon: 'code', category: 'logic', source: 'builtin',\n }),\n async execute(node, variables, context) {\n const cfg = (node.config ?? {}) as Record<string, unknown>;\n // `function` is canonical; `functionName` is an accepted alias — AI/templates\n // commonly emit it alongside `actionType: 'invoke_function'` (#1870 DX).\n const fnRaw = cfg.function ?? cfg.functionName;\n const fnName = typeof fnRaw === 'string' && fnRaw.trim() ? fnRaw.trim() : undefined;\n const actionType = typeof cfg.actionType === 'string' && cfg.actionType.trim() ? cfg.actionType.trim() : undefined;\n\n // Built-in side-effect actions keep their logger-backed behavior — but\n // only when an explicit `function` isn't set (that always wins).\n if (!fnName && actionType && SCRIPT_BUILTIN_ACTION_TYPES.has(actionType)) {\n ctx.logger.info(\n `[Script:${actionType}] template=${String(cfg.template)} ` +\n `recipients=${JSON.stringify(cfg.recipients)} ` +\n `vars=${JSON.stringify(cfg.variables)}`,\n );\n return {\n success: true,\n output: { actionType, template: cfg.template, recipients: cfg.recipients },\n };\n }\n\n // Inline `config.script` (a JS source body) is a distinct, recognized\n // form — but the built-in runtime has no server-side JS sandbox, so it\n // does not execute it. Warn loudly (not a silent success) and steer the\n // author to the supported path — a registered function — rather than\n // failing the flow. Executing inline scripts is a separate capability,\n // out of #1870's callable-resolution scope.\n const inlineScript = typeof cfg.script === 'string' && cfg.script.trim() ? cfg.script : undefined;\n if (!fnName && inlineScript) {\n ctx.logger.warn(\n `[Script] node '${node.id}': inline \\`config.script\\` is not executed by the built-in runtime ` +\n `(no server-side JS sandbox) — this node is a no-op. To run server logic, move it into a ` +\n `registered function and call it via \\`config.function\\` + \\`defineStack({ functions })\\`.`,\n );\n return { success: true, output: { script: 'not-executed' } };\n }\n\n // `actionType: 'invoke_function'` is a MARKER meaning \"call the named\n // function\" — the name lives in `function`/`functionName`, not in actionType\n // itself. A bare actionType that matched no built-in is still accepted as a\n // function name (shorthand).\n const target = fnName ?? (actionType === 'invoke_function' ? undefined : actionType);\n if (!target) {\n return {\n success: false,\n error:\n actionType === 'invoke_function'\n ? `script node '${node.id}': actionType 'invoke_function' requires \\`config.function\\` (or \\`functionName\\`) naming the function to call.`\n : `script node '${node.id}': declares neither \\`actionType\\` nor \\`function\\` — nothing to run.`,\n };\n }\n\n const handler = engine.resolveFunction(target);\n if (!handler) {\n return {\n success: false,\n error:\n `script node '${node.id}': '${target}' is not a built-in action ` +\n `(${[...SCRIPT_BUILTIN_ACTION_TYPES].join(', ')}) and no function named '${target}' is registered. ` +\n `Register it via \\`defineStack({ functions: { '${target}': fn } })\\`, or fix the name (#1870).`,\n };\n }\n\n // Map declared inputs (`config.inputs` | `config.input`) to the function,\n // interpolating `{var}` references against the live flow variables (so a\n // function can consume a prior node's output, e.g. `{aiResult.id}`).\n const input = interpolate(cfg.inputs ?? cfg.input ?? {}, variables, context) as Record<string, unknown>;\n const outputVariable =\n typeof cfg.outputVariable === 'string' && cfg.outputVariable.trim() ? cfg.outputVariable.trim() : undefined;\n try {\n const result = await handler({ input, variables, automation: context, logger: ctx.logger });\n // Pure-function pattern: the function RETURNS its result; `outputVariable`\n // exposes it as a flow variable so a later declarative node persists it\n // (e.g. `update_record fields: { ai_category: '{aiResult.ai_category}' }`).\n // Data I/O stays on the flow graph — the function itself does no writes.\n if (outputVariable) variables.set(outputVariable, result);\n return { success: true, output: { function: target, result } };\n } catch (err) {\n return {\n success: false,\n error: `script function '${target}' (node '${node.id}') failed: ${(err as Error).message}`,\n };\n }\n },\n });\n\n ctx.logger.info('[Screen/Script Nodes] 2 built-in node executors registered');\n}\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\nimport type { PluginContext } from '@objectstack/core';\nimport { defineActionDescriptor } from '@objectstack/spec/automation';\nimport { randomUUID } from 'node:crypto';\nimport type { AutomationEngine } from '../engine.js';\nimport { interpolate } from './template.js';\n\n/**\n * HTTP built-in node — canonical `http` (ADR-0018 M3) + deprecated aliases.\n *\n * `http` is the single outbound-callout verb the platform offers Flow, Workflow\n * Rules and Approval. It replaces the five divergent names (`http_request` /\n * `http_call` / `webhook` / …) which are kept as **deprecated aliases** of\n * `http` for back-compat (registered via {@link AutomationEngine.registerNodeAlias}).\n *\n * Two execution modes:\n *\n * - **Durable (`config.durable: true`)** — fire-and-forget callout enqueued\n * onto the `service-messaging` HTTP outbox (`sys_http_delivery`), inheriting\n * retry / idempotency / dead-letter. The flow gets back `{ deliveryId }` and\n * does NOT block on the response. This closes the \"`http_request` is a bare\n * fetch with no retry\" reliability gap (ADR-0018 §4). When no messaging HTTP\n * outbox is wired the node degrades to the inline call below.\n *\n * - **Request/response (default)** — a synchronous `fetch()` returning\n * `{ response, status }` to the flow, preserving the historical `http_request`\n * behavior so existing flows that read the response keep working. (The ADR's\n * `isAsync` suspend-and-resume variant is future work.)\n */\n\n/** Structural view of `service-messaging`'s HTTP outbox surface (ADR-0018 M3). */\ninterface MessagingHttpSurface {\n isHttpDeliveryReady?(): boolean;\n enqueueHttp?(input: {\n source: string;\n refId: string;\n dedupKey: string;\n label?: string;\n url: string;\n method?: string;\n headers?: Record<string, string>;\n signingSecret?: string;\n timeoutMs?: number;\n payload: unknown;\n }): Promise<string>;\n}\n\nconst HTTP_TYPE = 'http' as const;\n\nexport function registerHttpNodes(engine: AutomationEngine, ctx: PluginContext): void {\n const getMessaging = (): MessagingHttpSurface | undefined => {\n try {\n return ctx.getService<MessagingHttpSurface>('messaging');\n } catch {\n return undefined;\n }\n };\n\n engine.registerNodeExecutor({\n type: HTTP_TYPE,\n descriptor: defineActionDescriptor({\n type: HTTP_TYPE,\n version: '1.0.0',\n name: 'HTTP',\n description:\n 'Call an external HTTP endpoint. With `durable: true`, the call is enqueued on the '\n + 'messaging outbox with retry / dead-letter; otherwise it runs inline and returns the response.',\n icon: 'globe',\n category: 'io',\n source: 'builtin',\n // Capable of outbox-backed durable delivery (used when durable:true\n // and the messaging HTTP outbox is wired).\n needsOutbox: true,\n supportsRetry: true,\n paradigms: ['flow', 'approval'],\n configSchema: {\n type: 'object',\n required: ['url'],\n properties: {\n url: { type: 'string', description: 'Target URL' },\n method: { type: 'string', description: 'HTTP method (default GET; POST when durable)' },\n headers: { type: 'object', description: 'Request headers' },\n body: { description: 'Request body (JSON-serialised)' },\n durable: {\n type: 'boolean',\n description: 'Fire-and-forget via the durable outbox (retry/dead-letter) instead of inline request/response',\n },\n timeoutMs: { type: 'number', description: 'Per-request timeout (ms)' },\n signingSecret: { type: 'string', description: 'HMAC-SHA256 secret → X-Objectstack-Signature' },\n },\n },\n }),\n async execute(node, variables, context) {\n const raw = (node.config ?? {}) as Record<string, unknown>;\n const cfg = interpolate(raw, variables, context) as Record<string, unknown>;\n\n const url = cfg.url as string | undefined;\n if (!url) return { success: false, error: 'http: url is required' };\n\n const durable = cfg.durable === true;\n const headers = cfg.headers as Record<string, string> | undefined;\n const body = cfg.body;\n const timeoutMs = typeof cfg.timeoutMs === 'number' ? cfg.timeoutMs : undefined;\n const signingSecret = cfg.signingSecret as string | undefined;\n\n // ── Durable mode: enqueue onto the messaging HTTP outbox ──────────\n if (durable) {\n const messaging = getMessaging();\n if (messaging?.isHttpDeliveryReady?.() && messaging.enqueueHttp) {\n try {\n const deliveryId = await messaging.enqueueHttp({\n source: 'flow',\n refId: node.id,\n dedupKey: randomUUID(),\n label: `flow:${node.id}`,\n url,\n method: (cfg.method as string) ?? 'POST',\n headers,\n signingSecret,\n timeoutMs,\n payload: body ?? {},\n });\n return { success: true, output: { deliveryId, enqueued: true } };\n } catch (err) {\n return { success: false, error: `http (durable) failed to enqueue: ${(err as Error).message}` };\n }\n }\n // No outbox available — degrade to a best-effort inline call.\n ctx.logger.warn(\n `[http] node '${node.id}' requested durable delivery but no messaging HTTP outbox is wired; falling back to inline fetch`,\n );\n }\n\n // ── Request/response mode (default; preserves http_request) ───────\n const method = (cfg.method as string) ?? 'GET';\n const controller = new AbortController();\n const timer = timeoutMs ? setTimeout(() => controller.abort(), timeoutMs) : undefined;\n try {\n const response = await fetch(url, {\n method,\n headers,\n body: body !== undefined && body !== null ? JSON.stringify(body) : undefined,\n signal: controller.signal,\n });\n const data = await readBody(response);\n return {\n success: response.ok,\n output: { response: data, status: response.status },\n error: response.ok ? undefined : `HTTP ${response.status}`,\n };\n } catch (err) {\n const e = err as { name?: string; message?: string };\n const msg = e?.name === 'AbortError' ? `timeout after ${timeoutMs}ms` : e?.message ?? String(err);\n return { success: false, error: `http: ${msg}` };\n } finally {\n if (timer) clearTimeout(timer);\n }\n },\n });\n\n // ADR-0018 M3: collapse the divergent outbound verbs onto `http`. Old saved\n // flows / workflow rules / approval actions keep running via these aliases.\n engine.registerNodeAlias('http_request', HTTP_TYPE, { name: 'HTTP Request', needsOutbox: true });\n engine.registerNodeAlias('http_call', HTTP_TYPE, { name: 'HTTP Call', needsOutbox: true });\n engine.registerNodeAlias('webhook', HTTP_TYPE, { name: 'Webhook', needsOutbox: true });\n\n ctx.logger.info('[HTTP] http executor registered (+ deprecated aliases: http_request, http_call, webhook)');\n}\n\n/** Read a response body as JSON, falling back to text (empty body → null). */\nasync function readBody(response: { json(): Promise<unknown>; text(): Promise<string> }): Promise<unknown> {\n try {\n return await response.json();\n } catch {\n try {\n const text = await response.text();\n return text || null;\n } catch {\n return null;\n }\n }\n}\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\nimport type { PluginContext } from '@objectstack/core';\nimport { defineActionDescriptor } from '@objectstack/spec/automation';\nimport type { AutomationEngine, ConnectorActionContext } from '../engine.js';\n\n/**\n * Connector built-in node — `connector_action` (generic integration dispatch).\n *\n * Part of the platform baseline alongside `http_request` (ADR-0018 §Addendum):\n * where `http_request` calls *any raw URL*, `connector_action` invokes *any\n * registered connector's action*. The platform ships the generic dispatch node\n * + an (initially empty) connector registry on the engine; concrete connectors\n * — `connector-rest`, `connector-slack`, `connector-salesforce`, … — populate\n * the registry at runtime via `engine.registerConnector()`.\n *\n * Because the registry starts empty, a flow referencing a connector that no\n * plugin has registered fails the *step* with a clear error rather than failing\n * to register — graceful degradation matching `http_request`'s fail-soft style.\n */\nexport function registerConnectorNodes(engine: AutomationEngine, ctx: PluginContext): void {\n engine.registerNodeExecutor({\n type: 'connector_action',\n descriptor: defineActionDescriptor({\n type: 'connector_action',\n version: '1.0.0',\n name: 'Connector Action',\n description:\n 'Invoke an action on a registered connector (Slack, Salesforce, a REST API, …). '\n + 'The connector itself is contributed by an integration plugin via registerConnector().',\n icon: 'plug',\n category: 'io',\n source: 'builtin',\n supportsRetry: true,\n // Present in both authoring paradigms (ADR-0018 §registry table;\n // workflow_rule retired per ADR-0019).\n paradigms: ['flow', 'approval'],\n // Config contract — drives the Studio property form and flow validation.\n configSchema: {\n type: 'object',\n required: ['connectorId', 'actionId'],\n properties: {\n connectorId: { type: 'string', description: 'Registered connector name' },\n actionId: { type: 'string', description: 'Action key declared by the connector' },\n input: { type: 'object', description: 'Mapped inputs for the action' },\n },\n },\n }),\n async execute(node, variables, context) {\n const cfg = node.connectorConfig;\n if (!cfg?.connectorId || !cfg?.actionId) {\n return {\n success: false,\n error: `connector_action '${node.id}': connectorConfig.connectorId and .actionId are required`,\n };\n }\n\n const handler = engine.resolveConnectorAction(cfg.connectorId, cfg.actionId);\n if (!handler) {\n return {\n success: false,\n error:\n `connector_action '${node.id}': no handler for `\n + `'${cfg.connectorId}.${cfg.actionId}' — is the connector plugin registered?`,\n };\n }\n\n const handlerCtx: ConnectorActionContext = {\n variables,\n automation: context,\n logger: ctx.logger,\n };\n\n try {\n const output = await handler((cfg.input ?? {}) as Record<string, unknown>, handlerCtx);\n return { success: true, output };\n } catch (err) {\n return {\n success: false,\n error: `connector_action(${cfg.connectorId}.${cfg.actionId}) failed: ${(err as Error).message}`,\n };\n }\n },\n });\n\n ctx.logger.info('[Connector] 1 built-in node executor registered (connector_action)');\n}\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\nimport type { PluginContext } from '@objectstack/core';\nimport { defineActionDescriptor } from '@objectstack/spec/automation';\nimport type { AutomationEngine } from '../engine.js';\nimport { interpolate } from './template.js';\n\n/**\n * Structural view of `@objectstack/service-messaging`'s service (ADR-0012),\n * declared locally so service-automation does not take a runtime dependency on\n * it — mirrors the `ConnectorRegistrySurface` pattern. The `notify` node\n * resolves whatever object is registered under the `messaging` service and\n * dispatches through this shape; if no such service is present the node\n * degrades to a no-op success.\n */\nexport interface MessagingServiceSurface {\n emit(input: {\n topic: string;\n audience: string[];\n payload?: Record<string, unknown>;\n severity?: string;\n dedupKey?: string;\n source?: { object: string; id: string };\n actorId?: string;\n channels?: string[];\n }): Promise<{ notificationId: string; delivered: number; failed: number }>;\n}\n\n/** Coerce a config value (string | string[]) into a clean string[]. */\nfunction toStringList(value: unknown): string[] {\n if (Array.isArray(value)) return value.map((v) => String(v)).filter(Boolean);\n if (typeof value === 'string' && value.trim()) return [value.trim()];\n return [];\n}\n\n/**\n * `notify` built-in node (ADR-0012) — outbound notification.\n *\n * Baseline node and the human-notification counterpart to `http_request`\n * (\"raw call\") and `connector_action` (\"call a registered integration\"):\n * `notify` hands a topic + recipients + message to the platform's messaging\n * service, which fans it out across the user's channels (inbox by default).\n *\n * Like the CRUD nodes degrade without a data engine, `notify` degrades to a\n * warning + success when no `messaging` service is registered — the capability\n * simply isn't installed in that stack. Install `MessagingServicePlugin`\n * (`@objectstack/service-messaging`) and the same flow starts delivering, with\n * no flow edit. This is the seam that fixes the \"notify drops on the floor\"\n * gap (#1292) once messaging is present.\n */\nexport function registerNotifyNode(engine: AutomationEngine, ctx: PluginContext): void {\n const getMessaging = (): MessagingServiceSurface | undefined => {\n try {\n return ctx.getService<MessagingServiceSurface>('messaging');\n } catch {\n return undefined;\n }\n };\n\n engine.registerNodeExecutor({\n type: 'notify',\n descriptor: defineActionDescriptor({\n type: 'notify', version: '1.0.0', name: 'Notify',\n description: 'Send an outbound notification to users via the messaging service (inbox / email / push / …).',\n icon: 'bell', category: 'io', source: 'builtin',\n supportsRetry: true,\n // Delivery is outbox-backed inside the messaging service (ADR-0030\n // emit → sys_notification_delivery), so it inherits retry/dead-letter.\n needsOutbox: true,\n paradigms: ['flow', 'approval'],\n }),\n async execute(node, variables, context) {\n const cfg = (node.config ?? {}) as Record<string, unknown>;\n\n const recipients = toStringList(interpolate(cfg.recipients ?? cfg.to ?? [], variables, context));\n const title = String(interpolate(cfg.title ?? cfg.subject ?? '', variables, context) ?? '');\n const body = String(interpolate(cfg.message ?? cfg.body ?? '', variables, context) ?? '');\n const channels = toStringList(cfg.channels);\n const topic = cfg.topic ? String(cfg.topic) : undefined;\n const severity = cfg.severity ? String(cfg.severity) : undefined;\n const actionUrl = cfg.actionUrl\n ? String(interpolate(cfg.actionUrl, variables, context) ?? '')\n : undefined;\n const payload = cfg.payload\n ? (interpolate(cfg.payload, variables, context) as Record<string, unknown>)\n : undefined;\n\n if (!title) return { success: false, error: 'notify: title (or subject) is required' };\n if (recipients.length === 0) {\n return { success: false, error: 'notify: at least one recipient is required' };\n }\n\n const messaging = getMessaging();\n if (!messaging) {\n ctx.logger.warn(\n `[notify] no messaging service registered; notification \"${title}\" not delivered`,\n );\n return {\n success: true,\n output: { delivered: 0, failed: 0, skipped: true },\n };\n }\n\n try {\n // ADR-0030 single ingress: hand the messaging service a topic +\n // audience + payload; it writes the L2 event and materializes\n // per channel. title/body/url ride in the payload (templates in\n // a later phase fall back to these).\n const result = await messaging.emit({\n topic: topic ?? 'notify',\n audience: recipients,\n payload: { ...(payload ?? {}), title, body, url: actionUrl },\n severity,\n channels: channels.length ? channels : undefined,\n });\n return {\n success: true,\n output: {\n notificationId: result.notificationId,\n delivered: result.delivered,\n failed: result.failed,\n },\n };\n } catch (err) {\n return { success: false, error: `notify failed: ${(err as Error).message}` };\n }\n },\n });\n\n ctx.logger.info('[Notify] 1 built-in node executor registered (notify)');\n}\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\nimport type { PluginContext } from '@objectstack/core';\nimport { defineActionDescriptor } from '@objectstack/spec/automation';\nimport type { IJobService } from '@objectstack/spec/contracts';\nimport type { AutomationEngine, SuspendedRunStore } from '../engine.js';\n\n/**\n * `wait` built-in node — a durable pause (ADR-0019 suspend/resume), the timer /\n * signal sibling of the human-input `screen` and `approval` nodes.\n *\n * On entry the node *suspends* the run (the engine snapshots the continuation\n * and returns `{ status: 'paused', runId }`). How it resumes depends on\n * `waitEventConfig.eventType`:\n *\n * - **timer** — schedule a one-shot job (`IJobService`, `{ type: 'once', at }`)\n * that calls `engine.resume(runId)` when the duration elapses. With no job\n * service the node still suspends, but resumption must come from an external\n * `resume(runId)` (logged) — never silently no-ops or fails the flow,\n * matching the platform's degrade-don't-crash convention.\n * - **signal / webhook / manual / condition** — suspend with the signal name as\n * the correlation key; an external producer resumes the run when the event\n * arrives (`resume(runId)`), exactly like a decision-less approval.\n *\n * Reads its own run id from the `$runId` variable the engine injects at start\n * (same mechanism the approval node uses to map external state back to the run).\n */\nexport function registerWaitNode(engine: AutomationEngine, ctx: PluginContext): void {\n const getJobService = (): IJobService | undefined => {\n try {\n return ctx.getService<IJobService>('job');\n } catch {\n return undefined;\n }\n };\n\n engine.registerNodeExecutor({\n type: 'wait',\n descriptor: defineActionDescriptor({\n type: 'wait',\n version: '1.0.0',\n name: 'Wait',\n description: 'Pause the flow until a timer elapses or a named signal arrives.',\n icon: 'timer-reset',\n category: 'logic',\n source: 'builtin',\n // Durable pause — the run suspends and resumes later (timer/signal).\n supportsPause: true,\n isAsync: true,\n }),\n async execute(node, variables, _context) {\n // Prefer the spec-structured `waitEventConfig` block; fall back to a loose\n // `config` for hand-authored flows that put the same keys under config.\n const loose = (node.config ?? {}) as Record<string, unknown>;\n const wec = (node.waitEventConfig ?? {}) as Record<string, unknown>;\n const eventType = String(wec.eventType ?? loose.eventType ?? 'timer');\n const runId = variables.get('$runId');\n\n if (eventType === 'timer') {\n const durationMs =\n parseIsoDuration(wec.timerDuration ?? loose.timerDuration ?? loose.duration) ??\n (typeof wec.timeoutMs === 'number' ? wec.timeoutMs : undefined) ??\n (typeof loose.timeoutMs === 'number' ? (loose.timeoutMs as number) : undefined);\n\n // Persist the wake deadline as node output: the engine writes output\n // to variables (`<nodeId>.waitUntil`) *before* snapshotting the\n // suspended run, so a cold-booted kernel can re-arm the timer from the\n // durable store ({@link rearmSuspendedWaitTimers}).\n const at = durationMs && durationMs > 0 ? new Date(Date.now() + durationMs).toISOString() : undefined;\n const output = at ? { waitUntil: at } : undefined;\n\n const job = getJobService();\n if (job && runId != null && at) {\n const jobName = `flow-wait:${String(runId)}:${node.id}`;\n try {\n await job.schedule(jobName, { type: 'once', at }, async () => {\n try {\n await engine.resume(String(runId));\n } finally {\n // One-shot: drop the job so it never re-fires.\n try {\n await job.cancel?.(jobName);\n } catch {\n /* best-effort */\n }\n }\n });\n return { success: true, suspend: true, correlation: jobName, output };\n } catch (err) {\n ctx.logger.warn(\n `[wait] node '${node.id}': failed to schedule timer resume (${(err as Error)?.message ?? err}); ` +\n `suspending without auto-resume (resume it via resume(runId))`,\n );\n }\n } else if (!job) {\n ctx.logger.warn(\n `[wait] node '${node.id}': no job service registered — suspending without an auto-resume timer ` +\n `(resume it via resume(runId), or install the job service for durable timers)`,\n );\n }\n // Degrade: still suspend; resumption comes from an external resume()\n // (or a later boot's re-arm pass, when the deadline was persisted).\n return { success: true, suspend: true, correlation: `timer:${node.id}`, output };\n }\n\n // signal / webhook / manual / condition — suspend; an external producer\n // resumes the run when the named event arrives.\n const signal = String(wec.signalName ?? loose.signalName ?? loose.signal ?? `wait:${node.id}`);\n return { success: true, suspend: true, correlation: signal };\n },\n });\n\n ctx.logger.info('[Wait Node] 1 built-in node executor registered');\n}\n\n/** Minimal logger surface for {@link rearmSuspendedWaitTimers}. */\ninterface RearmLogger {\n info(msg: string, ...args: unknown[]): void;\n warn(msg: string, ...args: unknown[]): void;\n}\n\n/**\n * Re-arm auto-resume timers for suspended timer-`wait` runs after a cold boot\n * (ADR-0019 follow-up). The one-shot job a `wait` node schedules lives in the\n * job service's process memory unless that service is itself durable — so a\n * restart loses the wake-up while the suspended run survives in\n * `sys_automation_run`. This pass walks the durable store and:\n *\n * - **overdue** deadlines (`<nodeId>.waitUntil` in the past) → `resume()` now;\n * - **future** deadlines → re-schedule the same `flow-wait:<runId>:<nodeId>`\n * one-shot job (no job service → warn and leave it for an external resume);\n * - runs without a persisted `waitUntil` (approval / screen / signal pauses,\n * or pre-deadline-persistence rows) → skipped untouched.\n *\n * Double-fire safe: if the original job *did* survive (durable job store), the\n * second `resume(runId)` finds no suspended run — the engine's resume\n * idempotency absorbs it. Returns how many runs were resumed or re-armed.\n *\n * Called by `AutomationServicePlugin.start()` *after* the flow pull, because\n * `resume()` needs the flow definitions registered.\n */\nexport async function rearmSuspendedWaitTimers(\n engine: Pick<AutomationEngine, 'resume'>,\n store: SuspendedRunStore,\n job: IJobService | undefined,\n logger: RearmLogger,\n): Promise<number> {\n let runs;\n try {\n runs = await store.list();\n } catch (err) {\n logger.warn(`[wait] timer re-arm: failed to list suspended runs: ${(err as Error)?.message ?? err}`);\n return 0;\n }\n\n let rearmed = 0;\n for (const run of runs) {\n const wakeAt = run.variables?.[`${run.nodeId}.waitUntil`];\n if (typeof wakeAt !== 'string' || !wakeAt) continue; // not a timer wait\n const atMs = Date.parse(wakeAt);\n if (Number.isNaN(atMs)) continue;\n\n if (atMs <= Date.now()) {\n // Deadline elapsed while the process was down — resume immediately.\n try {\n await engine.resume(run.runId);\n rearmed++;\n } catch (err) {\n logger.warn(`[wait] timer re-arm: resume of overdue run '${run.runId}' failed: ${(err as Error)?.message ?? err}`);\n }\n continue;\n }\n\n if (!job) {\n logger.warn(\n `[wait] timer re-arm: run '${run.runId}' waits until ${wakeAt} but no job service is registered — ` +\n `resume it externally via resume(runId)`,\n );\n continue;\n }\n\n const jobName = `flow-wait:${run.runId}:${run.nodeId}`;\n try {\n await job.schedule(jobName, { type: 'once', at: wakeAt }, async () => {\n try {\n await engine.resume(run.runId);\n } finally {\n try {\n await job.cancel?.(jobName);\n } catch {\n /* best-effort */\n }\n }\n });\n rearmed++;\n } catch (err) {\n logger.warn(`[wait] timer re-arm: failed to re-schedule run '${run.runId}': ${(err as Error)?.message ?? err}`);\n }\n }\n return rearmed;\n}\n\n/**\n * Parse an ISO-8601 duration (the subset flows use — weeks/days + a time part\n * of hours/minutes/seconds, e.g. `PT1H`, `P3D`, `PT90M`, `P1DT12H`) into\n * milliseconds. A bare positive number is treated as milliseconds. Returns\n * `undefined` for anything unparseable / non-positive.\n */\nexport function parseIsoDuration(input: unknown): number | undefined {\n if (typeof input === 'number' && Number.isFinite(input)) return input > 0 ? input : undefined;\n if (typeof input !== 'string') return undefined;\n const s = input.trim();\n if (!s) return undefined;\n // Plain numeric string ⇒ milliseconds.\n if (/^\\d+(?:\\.\\d+)?$/.test(s)) {\n const n = Number(s);\n return n > 0 ? n : undefined;\n }\n const m = /^P(?:(\\d+)W)?(?:(\\d+)D)?(?:T(?:(\\d+)H)?(?:(\\d+)M)?(?:(\\d+(?:\\.\\d+)?)S)?)?$/.exec(s);\n if (!m) return undefined;\n const [, w, d, h, min, sec] = m;\n if (!w && !d && !h && !min && !sec) return undefined;\n const totalSec =\n Number(w ?? 0) * 7 * 86_400 +\n Number(d ?? 0) * 86_400 +\n Number(h ?? 0) * 3_600 +\n Number(min ?? 0) * 60 +\n Number(sec ?? 0);\n const ms = totalSec * 1000;\n return ms > 0 ? ms : undefined;\n}\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\nimport type { PluginContext } from '@objectstack/core';\nimport { defineActionDescriptor } from '@objectstack/spec/automation';\nimport type { AutomationContext } from '@objectstack/spec/contracts';\nimport type { AutomationEngine } from '../engine.js';\nimport { interpolate } from './template.js';\n\n/** Hard cap on subflow nesting — turns an accidental cycle into a clean error. */\nconst MAX_SUBFLOW_DEPTH = 16;\n\n/**\n * `subflow` built-in node — invoke another flow as a step (reuse / DRY).\n *\n * Resolves `config.input` (a `{token}` mapping) against the parent's variables,\n * runs `config.flowName` via the engine, and writes the child's output back to\n * the parent — under `${nodeId}.output`, and under `config.outputVariable` as a\n * bare variable when given.\n *\n * **Nested durable pause (linked-runs model).** If the child *suspends* (a\n * nested `approval` / `screen` / `wait`), the child's continuation is already\n * persisted by the engine as its own run; this node then suspends the PARENT\n * run at this node with `correlation: 'subflow:<childRunId>'`, so both rows\n * survive a restart and stay linked. The engine's resume boundary completes\n * the chain in both directions:\n *\n * - resuming the CHILD directly (approval service / wait timer hold the child\n * `$runId`) bubbles UP on completion — the engine auto-resumes the parent\n * with the child's output, mapped exactly like the synchronous path;\n * - resuming the PARENT (a UI holds the parent run id from the original\n * `execute()` response) delegates DOWN to the suspended child.\n *\n * The linkage rides on the child's context (`$parentRunId` / `$parentNodeId` /\n * `$parentOutputVariable`), which the engine persists with the child run — no\n * schema change. A depth guard ({@link MAX_SUBFLOW_DEPTH}) turns an accidental\n * recursive cycle into a clean error instead of a stack overflow.\n */\nexport function registerSubflowNode(engine: AutomationEngine, ctx: PluginContext): void {\n engine.registerNodeExecutor({\n type: 'subflow',\n descriptor: defineActionDescriptor({\n type: 'subflow',\n version: '1.0.0',\n name: 'Subflow',\n description: 'Invoke another flow as a reusable step and capture its output.',\n icon: 'workflow',\n category: 'logic',\n source: 'builtin',\n // A child that suspends (approval/screen/wait) suspends this node too —\n // the parent run pauses here and resumes when the child completes.\n supportsPause: true,\n }),\n async execute(node, variables, context) {\n const cfg = (node.config ?? {}) as Record<string, unknown>;\n const flowName =\n typeof cfg.flowName === 'string' ? cfg.flowName : typeof cfg.flow === 'string' ? cfg.flow : undefined;\n if (!flowName) {\n return { success: false, error: `subflow '${node.id}': config.flowName is required` };\n }\n\n // Cycle guard: depth rides on the context so it accumulates across nesting.\n const depth = Number((context as { $subflowDepth?: number } | undefined)?.$subflowDepth ?? 0);\n if (depth >= MAX_SUBFLOW_DEPTH) {\n return {\n success: false,\n error: `subflow '${flowName}': max nesting depth (${MAX_SUBFLOW_DEPTH}) exceeded — recursive subflow?`,\n };\n }\n\n // Map inputs (resolve `{var}` against the parent's variables/context).\n const rawInput = (cfg.input && typeof cfg.input === 'object' ? cfg.input : {}) as Record<string, unknown>;\n const params = interpolate(rawInput, variables, context ?? ({} as AutomationContext)) as Record<string, unknown>;\n\n const outVar = typeof cfg.outputVariable === 'string' && cfg.outputVariable ? cfg.outputVariable : undefined;\n\n // Parent linkage for nested durable pause: should the child suspend, the\n // engine persists these with the child run and uses them to bubble the\n // child's eventual completion back into THIS run (resume at this node).\n // `$runId` is injected by the engine at run start (ADR-0019).\n const parentRunId = variables.get('$runId');\n const childContext = {\n ...(context ?? {}),\n $subflowDepth: depth + 1,\n params,\n ...(parentRunId != null\n ? {\n $parentRunId: String(parentRunId),\n $parentNodeId: node.id,\n ...(outVar ? { $parentOutputVariable: outVar } : {}),\n }\n : {}),\n } as AutomationContext;\n\n const child = await engine.execute(flowName, childContext);\n\n if (child.status === 'paused') {\n // Nested durable pause: the child's continuation is persisted under its\n // own run id; suspend the parent here, linked via the correlation key.\n // A nested screen surfaces on the parent's paused result so a UI runner\n // can render it against the parent run id (the engine delegates the\n // parent's resume down to the child).\n if (!child.runId) {\n return { success: false, error: `subflow '${flowName}' paused without a run id — cannot link the runs` };\n }\n return {\n success: true,\n suspend: true,\n correlation: `subflow:${child.runId}`,\n screen: child.screen,\n };\n }\n if (!child.success) {\n return { success: false, error: `subflow '${flowName}' failed: ${child.error ?? 'unknown error'}` };\n }\n\n // Bare output variable (like the assignment node, the executor may write\n // directly to the parent variable map).\n if (outVar) variables.set(outVar, child.output ?? null);\n\n return { success: true, output: { output: child.output ?? null } };\n },\n });\n\n ctx.logger.info('[Subflow Node] 1 built-in node executor registered');\n}\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\nimport type { PluginContext } from '@objectstack/core';\nimport { defineActionDescriptor } from '@objectstack/spec/automation';\nimport type { AutomationContext } from '@objectstack/spec/contracts';\nimport type { AutomationEngine } from '../engine.js';\nimport { interpolate } from './template.js';\n\n/** Hard cap on map fan-out — turns a runaway collection into a clean error. */\nconst MAX_MAP_ITEMS = 10_000;\n\n/**\n * `map` built-in node — **sequential multi-instance** (ADR-0037 Track A2).\n *\n * Runs a per-item **subflow** for each element of a collection, **one item at a\n * time**, and continues once every item's subflow has completed — collecting\n * each result into `config.outputVariable`. Unlike `loop` (whose body region\n * runs synchronously and cannot pause), each item here is a full child run, so\n * the per-item subflow **may durably pause** (an `approval` / `screen` /\n * `wait`). This is the worked \"batch approval\" shape: *approve each item in\n * turn, then continue.*\n *\n * Mechanism (no token tree — one program counter, ADR-0037):\n * - The node tracks its progress in flow variables (`${nodeId}.$mapState`).\n * - For item *k* it invokes `config.flowName` via `engine.execute`, tagging the\n * child run with `$parentRunId` + `$parentMapNode` so the engine knows to\n * bubble the child's completion **back into this node** (not past it).\n * - If the child completes synchronously, the result is recorded and the loop\n * advances inline. If the child **pauses**, the parent suspends at this node\n * (`correlation: 'map:<childRunId>'`); when the child later completes, the\n * engine **re-enters** this node (it reads `$mapItemOutput` / `$mapItemDone`,\n * records the item, and starts the next).\n *\n * v1 is **sequential and fail-fast**: items run in order; the first item whose\n * subflow fails fails the map. Concurrent fan-out + partial aggregation is a\n * deliberate follow-up (ADR-0037 — needs N:1 aggregation + serialization).\n */\nexport function registerMapNode(engine: AutomationEngine, ctx: PluginContext): void {\n engine.registerNodeExecutor({\n type: 'map',\n descriptor: defineActionDescriptor({\n type: 'map',\n version: '1.0.0',\n name: 'Map',\n description: 'Run a per-item subflow for each element of a collection, one at a time (each item may pause).',\n icon: 'list-check',\n category: 'logic',\n source: 'builtin',\n // Each item's subflow may pause, so the map suspends and resumes per item.\n supportsPause: true,\n isAsync: true,\n }),\n async execute(node, variables, context) {\n const cfg = (node.config ?? {}) as Record<string, unknown>;\n const flowName =\n typeof cfg.flowName === 'string' ? cfg.flowName : typeof cfg.flow === 'string' ? cfg.flow : undefined;\n if (!flowName) {\n return { success: false, error: `map '${node.id}': config.flowName (the per-item subflow) is required` };\n }\n\n const iteratorVariable =\n typeof cfg.iteratorVariable === 'string' && cfg.iteratorVariable ? cfg.iteratorVariable : 'item';\n const indexVariable =\n typeof cfg.indexVariable === 'string' && cfg.indexVariable ? cfg.indexVariable : undefined;\n const outVar =\n typeof cfg.outputVariable === 'string' && cfg.outputVariable ? cfg.outputVariable : undefined;\n\n // Resolve the collection (template / bare var / already-an-array).\n const rawCollection = cfg.collection;\n let collection: unknown;\n if (Array.isArray(rawCollection)) {\n collection = rawCollection;\n } else if (typeof rawCollection === 'string') {\n collection = interpolate(rawCollection, variables, context ?? ({} as AutomationContext));\n if (collection == null && variables.has(rawCollection)) collection = variables.get(rawCollection);\n }\n if (!Array.isArray(collection)) {\n return { success: false, error: `map '${node.id}': collection '${String(rawCollection)}' did not resolve to an array` };\n }\n if (collection.length > MAX_MAP_ITEMS) {\n return { success: false, error: `map '${node.id}': collection length ${collection.length} exceeds the ${MAX_MAP_ITEMS} cap` };\n }\n\n // ── Progress state, carried across re-entries in the variable map. ──\n const stateKey = `${node.id}.$mapState`;\n const state = (variables.get(stateKey) as { started: number; results: unknown[] } | undefined) ?? {\n started: 0,\n results: [],\n };\n\n // Re-entry: the previously-started item's subflow just completed (the\n // engine bubbled its output here). Record it and clear the handoff vars.\n if (variables.get(`${node.id}.$mapItemDone`) === true) {\n state.results.push(variables.get(`${node.id}.$mapItemOutput`) ?? null);\n variables.delete(`${node.id}.$mapItemDone`);\n variables.delete(`${node.id}.$mapItemOutput`);\n }\n\n const parentRunId = variables.get('$runId');\n\n // Drive items in order. Synchronous items advance inline; a pausing item\n // suspends the run and is resumed via re-entry.\n while (state.started < collection.length) {\n const idx = state.started;\n const item = collection[idx];\n variables.set(iteratorVariable, item);\n if (indexVariable) variables.set(indexVariable, idx);\n\n const rawInput = (cfg.input && typeof cfg.input === 'object' ? cfg.input : {}) as Record<string, unknown>;\n const params = interpolate(rawInput, variables, context ?? ({} as AutomationContext)) as Record<string, unknown>;\n\n // When the mapped item IS a record (has an id), expose it as the\n // child's `record` + `object` so a per-item `approval` / `update_record`\n // targets that item — the natural \"approve each row\" shape. Otherwise\n // the item is just data, passed via `params`.\n const itemIsRecord = item != null && typeof item === 'object' && typeof (item as any).id === 'string';\n const itemObject = typeof cfg.itemObject === 'string' ? cfg.itemObject : (context as any)?.object;\n\n const childContext = {\n ...(context ?? {}),\n params,\n ...(itemIsRecord ? { record: item, object: itemObject } : {}),\n ...(parentRunId != null\n ? { $parentRunId: String(parentRunId), $parentMapNode: node.id }\n : {}),\n } as AutomationContext;\n\n const child = await engine.execute(flowName, childContext);\n\n if (child.status === 'paused') {\n if (!child.runId) {\n return { success: false, error: `map '${node.id}': item ${idx} paused without a run id — cannot link the runs` };\n }\n // Mark this item started and suspend; the engine re-enters on bubble.\n state.started = idx + 1;\n variables.set(stateKey, state);\n return { success: true, suspend: true, correlation: `map:${child.runId}` };\n }\n if (!child.success) {\n return { success: false, error: `map '${node.id}': item ${idx} (subflow '${flowName}') failed: ${child.error ?? 'unknown error'}` };\n }\n // Synchronous completion — record and advance.\n state.started = idx + 1;\n state.results.push(child.output ?? null);\n }\n\n // All items done.\n variables.set(stateKey, state);\n if (outVar) variables.set(outVar, state.results);\n return { success: true, output: { results: state.results, count: state.results.length } };\n },\n });\n\n ctx.logger.info('[Map Node] 1 built-in node executor registered');\n}\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\n/**\n * Built-in node executors — the automation engine's foundational vocabulary.\n *\n * Per ADR-0018 and the platform principle \"plugins are plugins; the platform's\n * foundational capabilities are built in,\" these node packs are seeded directly\n * by the core {@link AutomationServicePlugin} rather than shipped as separately\n * installable plugins. Each `register*Nodes(engine, ctx)` publishes its\n * descriptors with `source: 'builtin'`.\n *\n * Scope (built-in baseline):\n * - logic — decision / assignment (engine core)\n * - logic — loop (structured iteration container, ADR-0031)\n * - logic — parallel (structured parallel block, implicit join, ADR-0031)\n * - logic — try_catch (structured try/catch/retry, ADR-0031)\n * - data — get/create/update/delete_record (platform CRUD baseline)\n * - human — screen / script (core flow capability)\n * - io — http_request (foundational outbound I/O)\n * - io — connector_action (generic integration dispatch)\n * - io — notify (outbound notification via messaging service)\n *\n * `connector_action` is the *generic dispatch* counterpart to `http_request`\n * (ADR-0018 §Addendum): the platform ships the node + an (initially empty)\n * connector registry on the engine, and *concrete* connectors populate it at\n * runtime via `engine.registerConnector()`. Third-party node types continue to\n * extend the vocabulary via `engine.registerNodeExecutor()`, keeping the action\n * list open and marketplace-extensible.\n */\n\nimport type { PluginContext } from '@objectstack/core';\nimport type { AutomationEngine } from '../engine.js';\nimport { registerLogicNodes } from './logic-nodes.js';\nimport { registerLoopNode } from './loop-node.js';\nimport { registerParallelNode } from './parallel-node.js';\nimport { registerTryCatchNode } from './try-catch-node.js';\nimport { registerCrudNodes } from './crud-nodes.js';\nimport { registerScreenNodes } from './screen-nodes.js';\nimport { registerHttpNodes } from './http-nodes.js';\nimport { registerConnectorNodes } from './connector-nodes.js';\nimport { registerNotifyNode } from './notify-node.js';\nimport { registerWaitNode } from './wait-node.js';\nimport { registerSubflowNode } from './subflow-node.js';\nimport { registerMapNode } from './map-node.js';\n\nexport { registerLogicNodes } from './logic-nodes.js';\nexport { registerLoopNode } from './loop-node.js';\nexport { registerParallelNode } from './parallel-node.js';\nexport { registerTryCatchNode } from './try-catch-node.js';\nexport { registerCrudNodes } from './crud-nodes.js';\nexport { registerScreenNodes } from './screen-nodes.js';\nexport { registerHttpNodes } from './http-nodes.js';\nexport { registerConnectorNodes } from './connector-nodes.js';\nexport { registerNotifyNode } from './notify-node.js';\nexport { registerWaitNode, parseIsoDuration, rearmSuspendedWaitTimers } from './wait-node.js';\nexport { registerSubflowNode } from './subflow-node.js';\nexport { registerMapNode } from './map-node.js';\n\n/**\n * Seed every built-in node executor into the engine. Called by\n * {@link AutomationServicePlugin.init} so a bare `new AutomationServicePlugin()`\n * yields a fully-functional automation capability with no companion plugins.\n */\nexport function installBuiltinNodes(engine: AutomationEngine, ctx: PluginContext): void {\n registerLogicNodes(engine, ctx);\n registerLoopNode(engine, ctx);\n registerParallelNode(engine, ctx);\n registerTryCatchNode(engine, ctx);\n registerCrudNodes(engine, ctx);\n registerScreenNodes(engine, ctx);\n registerHttpNodes(engine, ctx);\n registerConnectorNodes(engine, ctx);\n registerNotifyNode(engine, ctx);\n registerWaitNode(engine, ctx);\n registerSubflowNode(engine, ctx);\n registerMapNode(engine, ctx);\n\n const types = engine.getRegisteredNodeTypes();\n ctx.logger.info(\n `[Automation] ${types.length} built-in node executors installed: ${types.join(', ')}`,\n );\n}\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\nimport type { Plugin, PluginContext } from '@objectstack/core';\nimport type { IJobService } from '@objectstack/spec/contracts';\nimport { AutomationEngine } from './engine.js';\nimport { installBuiltinNodes, rearmSuspendedWaitTimers } from './builtin/index.js';\nimport { SysAutomationRun } from './sys-automation-run.object.js';\nimport { ObjectStoreSuspendedRunStore, type SuspendedRunStoreEngine } from './suspended-run-store.js';\n\n/**\n * Configuration options for the AutomationServicePlugin.\n */\nexport interface AutomationServicePluginOptions {\n /** Enable debug logging for flow execution */\n debug?: boolean;\n /**\n * Durable suspended-run persistence (ADR-0019):\n * - `'auto'` (default): persist to `sys_automation_run` via the ObjectQL\n * engine when one is available, otherwise stay in-memory.\n * - `'memory'`: never persist — keep suspended runs in memory only (the\n * historical behaviour; suitable for tests / single-process dev).\n *\n * When persistence is on, a run paused at an approval / wait / screen node\n * survives a process restart and can be resumed after a cold boot.\n */\n suspendedRunStore?: 'auto' | 'memory';\n /**\n * Max in-memory execution-log entries retained per process (ring buffer;\n * oldest evicted). The buffer is diagnostic-only and already bounded\n * (launch-readiness.md P1-2); this just makes the window tunable. Defaults\n * to {@link DEFAULT_MAX_EXECUTION_LOG_SIZE} (1000).\n */\n maxLogSize?: number;\n}\n\n/**\n * AutomationServicePlugin — Core engine plugin\n *\n * Responsibilities:\n * 1. init phase: Create engine instance, register as 'automation' service, and\n * seed the platform's built-in node executors (logic / CRUD / screen-script /\n * http_request) via {@link installBuiltinNodes}. Per ADR-0018, foundational\n * capabilities are built into the core, not packaged as optional plugins.\n * 2. start phase: Trigger 'automation:ready' hook so third-party plugins can\n * register additional node types, then pull flow definitions from the\n * ObjectQL schema registry and register them with the engine.\n * 3. destroy phase: Clean up resources\n *\n * The engine's `registerNodeExecutor()` stays open so plugins extend the\n * node/action vocabulary at runtime — the marketplace-extensibility contract.\n *\n * @example\n * ```ts\n * import { LiteKernel } from '@objectstack/core';\n * import { AutomationServicePlugin } from '@objectstack/service-automation';\n *\n * const kernel = new LiteKernel();\n * kernel.use(new AutomationServicePlugin());\n * await kernel.bootstrap();\n *\n * const automation = kernel.getService('automation');\n * ```\n */\nexport class AutomationServicePlugin implements Plugin {\n name = 'com.objectstack.service-automation';\n version = '1.0.0';\n type = 'standard' as const;\n // Soft dependency on metadata: we look it up at start() and tolerate absence.\n // Do NOT declare a hard kernel dependency, so this plugin works in environments\n // where MetadataPlugin is not registered.\n dependencies: string[] = [];\n\n private engine?: AutomationEngine;\n private readonly options: AutomationServicePluginOptions;\n\n constructor(options: AutomationServicePluginOptions = {}) {\n this.options = options;\n }\n\n async init(ctx: PluginContext): Promise<void> {\n this.engine = new AutomationEngine(ctx.logger, undefined, {\n maxLogSize: this.options.maxLogSize,\n });\n\n // Register as global service — other plugins access via ctx.getService('automation')\n ctx.registerService('automation', this.engine);\n\n // Register the sys_automation_run object so suspended-run state migrates\n // like other sys_* tables (ADR-0019). Best-effort: a host without the\n // manifest service still runs in-memory. Skipped when persistence is off.\n if ((this.options.suspendedRunStore ?? 'auto') !== 'memory') {\n try {\n ctx.getService<{ register(m: unknown): void }>('manifest').register({\n id: 'com.objectstack.service-automation',\n name: 'Automation Service',\n version: '1.0.0',\n type: 'plugin',\n scope: 'system',\n defaultDatasource: 'cloud',\n namespace: 'sys',\n objects: [SysAutomationRun],\n });\n } catch (err) {\n ctx.logger.warn(\n `[Automation] manifest service unavailable; sys_automation_run not registered (suspended runs stay in-memory): ${(err as Error).message}`,\n );\n }\n }\n\n // Seed the platform's built-in node executors. A bare\n // `new AutomationServicePlugin()` is thus a self-contained automation\n // capability — no companion node-pack plugins required (ADR-0018).\n installBuiltinNodes(this.engine, ctx);\n\n if (this.options.debug) {\n ctx.hook('automation:beforeExecute', async (flowName: string) => {\n ctx.logger.debug(`[Automation] Before execute: ${flowName}`);\n });\n }\n\n ctx.logger.info('[Automation] Engine initialized');\n }\n\n async start(ctx: PluginContext): Promise<void> {\n if (!this.engine) {\n ctx.logger.warn('[Automation] start() called before init() — engine missing, skipping');\n return;\n }\n\n // Trigger hook to notify engine is ready — other plugins can start registering nodes\n await ctx.trigger('automation:ready', this.engine);\n\n const nodeTypes = this.engine.getRegisteredNodeTypes();\n ctx.logger.info(\n `[Automation] Engine started with ${nodeTypes.length} node types: ${nodeTypes.join(', ') || '(none)'}`,\n );\n\n // Upgrade to durable suspended-run persistence when an ObjectQL engine is\n // present (ADR-0019). The engine was constructed in init() before\n // services were wired, so we attach the DB-backed store here. Without an\n // engine (or with `suspendedRunStore: 'memory'`) the in-memory default\n // stands — suspended runs simply don't survive a restart.\n let durableStore: ObjectStoreSuspendedRunStore | null = null;\n if ((this.options.suspendedRunStore ?? 'auto') !== 'memory') {\n let dataEngine: SuspendedRunStoreEngine | null = null;\n try { dataEngine = ctx.getService<SuspendedRunStoreEngine>('objectql'); }\n catch { try { dataEngine = ctx.getService<SuspendedRunStoreEngine>('data'); } catch { /* none */ } }\n if (dataEngine && typeof dataEngine.find === 'function' && typeof dataEngine.insert === 'function') {\n durableStore = new ObjectStoreSuspendedRunStore(dataEngine, ctx.logger);\n this.engine.setSuspendedRunStore(durableStore);\n ctx.logger.info('[Automation] Suspended-run persistence enabled (sys_automation_run)');\n } else {\n ctx.logger.info('[Automation] No ObjectQL engine — suspended runs kept in-memory only');\n }\n }\n\n // #1870 — bridge `script`-node function calls to the host function\n // registry. ObjectQL holds the name→handler map populated from\n // `bundle.functions` / `defineStack({ functions })` (the same registry\n // hooks/actions resolve through). Wiring it here lets a `script` node\n // invoke an authored function by name; an unresolved name fails the step\n // loudly. Best-effort: without ObjectQL, function-calling script nodes\n // fail with a clear \"no function registered\" error when executed.\n try {\n const fnRegistry = ctx.getService<{\n resolveFunction?: (name: string) => ((c: unknown) => unknown) | undefined;\n }>('objectql');\n if (fnRegistry && typeof fnRegistry.resolveFunction === 'function') {\n this.engine.setFunctionResolver((name) => {\n const fn = fnRegistry.resolveFunction!(name);\n return typeof fn === 'function'\n ? (fnCtx) => (fn as (c: unknown) => unknown)(fnCtx)\n : undefined;\n });\n ctx.logger.debug('[Automation] script-node function registry bridged to objectql.resolveFunction');\n }\n } catch {\n ctx.logger.debug('[Automation] objectql not present — script-node function calls will fail loudly when used');\n }\n\n // Pull flow definitions from the ObjectQL schema registry. AppPlugin.init()\n // calls manifest.register(payload), which routes to ql.registerApp() and\n // stores each inline flow under type 'flow'. By the time start() runs,\n // every init() phase has completed, so the registry is fully populated.\n try {\n const ql = ctx.getService<{\n registry?: { listItems?: (type: string) => unknown[] };\n }>('objectql');\n if (!ql) {\n ctx.logger.debug('[Automation] objectql service not found at start()');\n } else if (!ql.registry) {\n ctx.logger.debug('[Automation] objectql.registry is undefined at start()');\n } else if (typeof ql.registry.listItems !== 'function') {\n ctx.logger.debug('[Automation] objectql.registry.listItems is not a function');\n }\n const flows = ql?.registry?.listItems?.('flow') ?? [];\n ctx.logger.debug(`[Automation] flow pull: registry returned ${flows.length} flow(s)`);\n let registered = 0;\n for (const f of flows) {\n const def = f as { name?: string };\n if (!def?.name) continue;\n try {\n this.engine.registerFlow(def.name, def as never);\n registered++;\n } catch (e) {\n const msg = e instanceof Error ? e.message : String(e);\n ctx.logger.warn(`[Automation] failed to register flow ${def.name}: ${msg}`);\n }\n }\n if (registered > 0) {\n ctx.logger.info(`[Automation] Pulled ${registered} flow(s) from ObjectQL registry`);\n }\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n ctx.logger.warn(`[Automation] flow pull from ObjectQL registry failed: ${msg}`);\n }\n\n // ADR-0019 follow-up: re-arm auto-resume timers for runs that were\n // suspended at a timer-`wait` node when the process went down. Must run\n // *after* the flow pull above — resume() needs the flow definitions\n // registered. Overdue deadlines resume immediately; future ones get\n // their one-shot job re-scheduled. Best-effort: a failure here only\n // means those runs wait for an external resume(runId).\n if (durableStore) {\n let job: IJobService | undefined;\n try { job = ctx.getService<IJobService>('job'); } catch { /* none */ }\n try {\n const rearmed = await rearmSuspendedWaitTimers(this.engine, durableStore, job, ctx.logger);\n if (rearmed > 0) {\n ctx.logger.info(`[Automation] Re-armed ${rearmed} suspended wait timer(s) after restart`);\n }\n } catch (err) {\n ctx.logger.warn(`[Automation] wait-timer re-arm failed: ${(err as Error).message}`);\n }\n }\n }\n\n async destroy(): Promise<void> {\n this.engine = undefined;\n }\n}\n"],"mappings":"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","names":["durationMs","defineActionDescriptor","defineActionDescriptor","defineActionDescriptor","defineActionDescriptor","defineActionDescriptor","defineActionDescriptor","defineActionDescriptor","defineActionDescriptor","defineActionDescriptor","defineActionDescriptor","defineActionDescriptor","defineActionDescriptor","defineActionDescriptor","defineActionDescriptor","defineActionDescriptor","defineActionDescriptor","defineActionDescriptor","defineActionDescriptor","defineActionDescriptor"]}
|
|
1
|
+
{"version":3,"sources":["../src/engine.ts","../src/suspended-run-store.ts","../src/sys-automation-run.object.ts","../src/builtin/logic-nodes.ts","../src/builtin/loop-node.ts","../src/builtin/template.ts","../src/builtin/parallel-node.ts","../src/builtin/try-catch-node.ts","../src/builtin/crud-nodes.ts","../src/builtin/screen-nodes.ts","../src/builtin/http-nodes.ts","../src/builtin/connector-nodes.ts","../src/builtin/notify-node.ts","../src/builtin/wait-node.ts","../src/builtin/subflow-node.ts","../src/builtin/map-node.ts","../src/builtin/index.ts","../src/plugin.ts"],"sourcesContent":["// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\nimport type { FlowParsed, FlowNodeParsed, FlowEdgeParsed } from '@objectstack/spec/automation';\nimport type { ExecutionLog, ActionDescriptor } from '@objectstack/spec/automation';\nimport type { AutomationContext, AutomationResult, ResumeSignal, IAutomationService, ScreenSpec } from '@objectstack/spec/contracts';\nimport type { Logger } from '@objectstack/spec/contracts';\nimport { FlowSchema, FLOW_STRUCTURAL_NODE_TYPES, validateControlFlow, findRegionEntry, defineActionDescriptor } from '@objectstack/spec/automation';\nimport type { FlowRegionParsed } from '@objectstack/spec/automation';\nimport type { Connector } from '@objectstack/spec/integration';\nimport { ConnectorSchema } from '@objectstack/spec/integration';\n// Static import (not a lazy `require`): the engine ships as ESM (\"type\":\"module\"),\n// where a CommonJS `require('@objectstack/formula')` resolves to tsup's throwing\n// `__require` stub. That threw on every CEL evaluation and the catch below\n// silently returned `false`, so EVERY start-node / edge condition (record-change\n// `previous.*`, `budget > 100000`, …) skipped its flow. A static import binds the\n// engine at module load in both ESM and CJS builds.\nimport { ExpressionEngine, validateExpression } from '@objectstack/formula';\n\n// ─── Node Executor Interface (Plugin Extension Point) ───────────────\n\n/**\n * Each node type corresponds to a NodeExecutor.\n * Third-party plugins only need to implement this interface and register\n * it with the engine to extend automation capabilities.\n */\nexport interface NodeExecutor {\n /** Registry node type (built-in id or plugin-defined) */\n readonly type: string;\n\n /**\n * Optional ADR-0018 action descriptor. When present, it is published into\n * the engine's action registry and surfaced via {@link AutomationEngine.getActionDescriptors}\n * — feeding flow validation and the designer palette. Plugins SHOULD publish\n * one so their node appears in the palette and validates as a legal flow node.\n */\n readonly descriptor?: ActionDescriptor;\n\n /**\n * Execute a node\n * @param node - Current node definition\n * @param variables - Flow variable context (read/write)\n * @param context - Trigger context\n * @returns Execution result (may include output data, branch conditions, etc.)\n */\n execute(\n node: FlowNodeParsed,\n variables: Map<string, unknown>,\n context: AutomationContext,\n ): Promise<NodeExecutionResult>;\n}\n\nexport interface NodeExecutionResult {\n success: boolean;\n output?: Record<string, unknown>;\n error?: string;\n /** Used by decision nodes — returns the selected branch label */\n branchLabel?: string;\n /**\n * ADR-0019 durable pause. When `true`, the node has done its on-entry work\n * (e.g. opened an approval request) and the run should **suspend** here: the\n * engine persists a continuation, stops traversal, and `execute()` returns\n * `{ status: 'paused', runId }`. The run is continued later via\n * {@link AutomationEngine.resume}. Any `output` is written to variables\n * before suspending. The node reads its own run id from the `$runId`\n * flow variable so it can map the run to external state.\n */\n suspend?: boolean;\n /**\n * Optional correlation key surfaced on the suspended-run record (e.g. an\n * approval request id). For observability / lookup; not required to resume.\n */\n correlation?: string;\n /**\n * Screen to render — set by a `screen` node that suspends to collect input.\n * Surfaced on the paused {@link AutomationResult} so a UI runner can render\n * the form and `resume()` with the values.\n */\n screen?: ScreenSpec;\n /**\n * #1479: step logs produced inside the node's structured region(s). A\n * container node (`loop` / `parallel` / `try_catch`) collects the\n * {@link AutomationEngine.runRegion} return value(s) here; {@link AutomationEngine.executeNode}\n * appends them to the parent run log right after the container's own step,\n * so per-iteration / per-branch body steps surface in run observability.\n */\n childSteps?: StepLogEntry[];\n}\n\n// ─── Trigger Interface (Plugin Extension Point) ─────────────────────\n\n/**\n * A normalized description of *what* fires a flow, derived by the engine from\n * the flow's `start` node and handed to the matching {@link FlowTrigger} when a\n * flow is activated. Concrete triggers (record-change, schedule, …) read the\n * fields they care about and ignore the rest.\n *\n * The engine — not the trigger — owns parsing the start node, so trigger\n * plugins stay decoupled from flow-definition internals (mirrors how\n * `connector_action` keeps connectors decoupled from node config).\n */\nexport interface FlowTriggerBinding {\n /** Flow this binding activates. */\n readonly flowName: string;\n /** record-change: the object whose mutations fire the flow. */\n readonly object?: string;\n /** record-change: the start node's `triggerType` (e.g. 'record-after-update'). */\n readonly event?: string;\n /**\n * Optional trigger predicate copied from the start node's `condition`. The\n * engine evaluates it before running the flow; triggers may ignore it.\n */\n readonly condition?: string | { dialect?: string; source?: string; ast?: unknown };\n /** schedule: cron/interval descriptor (parsed but not yet acted on here). */\n readonly schedule?: unknown;\n /** The raw start-node `config`, for trigger-specific fields not modeled above. */\n readonly config?: Record<string, unknown>;\n}\n\n/**\n * Trigger interface. Schedule/Event/API triggers are registered via plugins.\n *\n * The engine completes the wiring: when a flow whose start node maps to this\n * trigger's {@link type} is registered (or when this trigger is registered\n * after such flows already exist), the engine calls {@link start} with the\n * parsed {@link FlowTriggerBinding} and a `callback` that runs the flow. The\n * trigger subscribes to its event source (e.g. an ObjectQL lifecycle hook) and\n * invokes `callback(ctx)` when it fires. {@link stop} tears that subscription\n * down when the flow is unregistered/disabled or the trigger is removed.\n */\nexport interface FlowTrigger {\n readonly type: string;\n start(binding: FlowTriggerBinding, callback: (ctx: AutomationContext) => Promise<void>): void;\n stop(flowName: string): void;\n}\n\n// ─── Connector Registry (Plugin Extension Point) ────────────────────\n\n/**\n * Context handed to a connector action handler. Carries the live flow variable\n * map and the trigger context so a handler can read prior-node output, plus a\n * logger. The platform ships the registry + the `connector_action` dispatch\n * node (baseline, ADR-0018 §Addendum); *concrete* connectors — `connector-rest`,\n * `connector-slack`, … — are plugins that register handlers here.\n */\nexport interface ConnectorActionContext {\n readonly variables: Map<string, unknown>;\n readonly automation: AutomationContext;\n readonly logger: Logger;\n}\n\n/**\n * A handler for one connector action. Receives the (already-resolved) input\n * mapped from the flow node and returns the action's output, which the\n * `connector_action` node writes back into flow variables.\n */\nexport type ConnectorActionHandler = (\n input: Record<string, unknown>,\n ctx: ConnectorActionContext,\n) => Promise<Record<string, unknown>>;\n\n/**\n * A connector registered on the engine: its validated {@link Connector}\n * definition plus the handler for each action it declares.\n */\nexport interface RegisteredConnector {\n readonly def: Connector;\n readonly handlers: Record<string, ConnectorActionHandler>;\n}\n\n/**\n * Context handed to a named handler function invoked from a `script` node\n * (#1870). Mirrors {@link ConnectorActionContext} but carries the node's mapped\n * `input` so the function reads its arguments without reaching into the raw\n * variable map. The function's return value becomes the node output.\n */\nexport interface FlowFunctionContext {\n /** Inputs mapped from the node's `config.inputs` (already in scope). */\n readonly input: Record<string, unknown>;\n /** Live flow variable map — read prior-node output / write results. */\n readonly variables: Map<string, unknown>;\n /** The flow execution / trigger context. */\n readonly automation: AutomationContext;\n readonly logger: Logger;\n}\n\n/**\n * A named handler function callable from a `script` node. Returns the node's\n * output (any JSON-serializable value); returning `undefined` yields an empty\n * output. Authored packages contribute these via `defineStack({ functions })`,\n * which the host bridges in through {@link AutomationEngine.setFunctionResolver}.\n */\nexport type FlowFunctionHandler = (ctx: FlowFunctionContext) => unknown | Promise<unknown>;\n\n/**\n * Resolves a function name to its handler. Injected by the host (the automation\n * plugin bridges it to ObjectQL's `resolveFunction`, fed by `bundle.functions`),\n * so the engine stays decoupled from any specific function registry. Returns\n * `undefined` for an unknown name, letting the `script` node fail the step\n * loudly instead of silently no-op'ing (#1870).\n */\nexport type FlowFunctionResolver = (name: string) => FlowFunctionHandler | undefined;\n\n/**\n * A designer-facing view of one connector action — identity + its JSON-Schema\n * input/output. The runtime handler is intentionally omitted; this is metadata.\n */\nexport interface ConnectorActionDescriptor {\n readonly key: string;\n readonly label: string;\n readonly description?: string;\n readonly inputSchema?: Record<string, unknown>;\n readonly outputSchema?: Record<string, unknown>;\n}\n\n/**\n * A designer-facing descriptor for a registered connector: its identity plus\n * the actions it exposes. Served by `GET /api/v1/automation/connectors` so the\n * flow designer can populate the `connector_action` node's connector → action\n * → input pickers (ADR-0018 §Addendum, ADR-0022). Mirrors `ActionDescriptor`'s\n * role for node types, but for the connector registry.\n */\nexport interface ConnectorDescriptor {\n readonly name: string;\n readonly label: string;\n readonly type: string;\n readonly description?: string;\n readonly icon?: string;\n readonly actions: ConnectorActionDescriptor[];\n}\n\n// ─── Core Automation Engine ─────────────────────────────────────────\n\n/**\n * Default ceiling on the in-memory execution-log ring buffer. Execution logs are\n * process-local and diagnostic only (launch-readiness.md P1-2); the buffer keeps\n * the most recent N entries and evicts the oldest, so memory is bounded\n * regardless of throughput. Operators tune the window via\n * {@link AutomationServicePluginOptions.maxLogSize}. Durable, queryable run\n * history is the DB-backed `sys_automation_run` store (a post-GA HA item), not\n * this buffer.\n */\nexport const DEFAULT_MAX_EXECUTION_LOG_SIZE = 1000;\n\n/** Construction options for {@link AutomationEngine}. */\nexport interface AutomationEngineOptions {\n /**\n * Max in-memory execution-log entries retained (ring buffer; oldest evicted).\n * Defaults to {@link DEFAULT_MAX_EXECUTION_LOG_SIZE}. Must be > 0.\n */\n maxLogSize?: number;\n}\n\n/**\n * Execution step log entry. Part of a {@link SuspendedRun}'s persisted state, so\n * it survives serialization to a durable {@link SuspendedRunStore}.\n */\nexport interface StepLogEntry {\n nodeId: string;\n nodeType: string;\n nodeLabel?: string;\n status: 'success' | 'failure' | 'skipped';\n startedAt: string;\n completedAt?: string;\n durationMs?: number;\n error?: { code: string; message: string; stack?: string };\n /**\n * #1479: structured-region grouping. When a step ran inside a `loop` /\n * `parallel` / `try_catch` body region, these tag it with its **immediate**\n * container so run observability can distinguish per-iteration / per-branch\n * body steps from top-level ones. Set by {@link AutomationEngine.runRegion}\n * (innermost wins — never overwritten as steps bubble through nested regions).\n */\n parentNodeId?: string;\n /** Zero-based loop iteration or parallel branch index of the enclosing region. */\n iteration?: number;\n /** Which region kind the step ran in: `loop-body` | `parallel-branch` | `try` | `catch`. */\n regionKind?: string;\n}\n\n/**\n * Internal execution log entry — compatible with ExecutionLog from spec.\n */\ninterface ExecutionLogEntry {\n id: string;\n flowName: string;\n flowVersion?: number;\n status: ExecutionLog['status'];\n startedAt: string;\n completedAt?: string;\n durationMs?: number;\n trigger: { type: string; userId?: string; object?: string; recordId?: string };\n steps: StepLogEntry[];\n variables?: Record<string, unknown>;\n output?: unknown;\n error?: string;\n}\n\n/**\n * Internal sentinel thrown by {@link AutomationEngine.executeNode} when a node\n * signals `suspend`. It unwinds the synchronous DAG recursion up to\n * `execute()` / `resume()`, which converts it into a persisted continuation\n * rather than a failed run. (Not exported — callers see `status: 'paused'`.)\n *\n * NOTE: suspend is supported on the serial / main execution path. A node that\n * suspends inside a `Promise.all` parallel branch will unwind that branch, but\n * sibling parallel branches already in flight are not cancelled — durable\n * pause across parallel gateways is out of scope for ADR-0019 M1.\n */\nclass FlowSuspendSignal {\n readonly __flowSuspend = true as const;\n constructor(readonly nodeId: string, readonly correlation?: string, readonly screen?: ScreenSpec) {}\n}\n\nfunction isSuspendSignal(err: unknown): err is FlowSuspendSignal {\n return typeof err === 'object' && err !== null && (err as FlowSuspendSignal).__flowSuspend === true;\n}\n\n/**\n * A run paused at a node, awaiting {@link AutomationEngine.resume} (ADR-0019).\n *\n * Held in an in-memory hot cache and — when a {@link SuspendedRunStore} is\n * configured — mirrored to durable storage so the pause survives a process\n * restart. Every field is JSON-serializable (the engine's variable `Map` is\n * snapshotted as a plain object) so the whole record round-trips through a\n * store.\n */\nexport interface SuspendedRun {\n runId: string;\n flowName: string;\n flowVersion?: number;\n /** The node the run paused at; resume continues from its out-edges. */\n nodeId: string;\n /** Snapshot of the flow variable map at suspend time. */\n variables: Record<string, unknown>;\n steps: StepLogEntry[];\n context: AutomationContext;\n startedAt: string;\n startTime: number;\n correlation?: string;\n /** Screen the run paused on (screen-flow runtime), for re-fetch + UI render. */\n screen?: ScreenSpec;\n}\n\n/**\n * Pluggable durable store for suspended runs (ADR-0019). The engine persists a\n * {@link SuspendedRun} on suspend and deletes it on terminal completion; on\n * {@link AutomationEngine.resume} of a run not in the in-memory cache (e.g.\n * after a process restart) it rehydrates from here.\n *\n * The default is purely in-memory (no store); a host wires a DB-backed store\n * (`ObjectStoreSuspendedRunStore`, on `sys_automation_run`) for production /\n * serverless deployments where the process hibernates between suspend and\n * resume.\n */\nexport interface SuspendedRunStore {\n /** Persist (insert or replace) a suspended run. */\n save(run: SuspendedRun): Promise<void>;\n /** Load a suspended run by id, or `null` if not stored. */\n load(runId: string): Promise<SuspendedRun | null>;\n /** Remove a suspended run's durable record (idempotent). */\n delete(runId: string): Promise<void>;\n /** List all currently-stored suspended runs. */\n list(): Promise<SuspendedRun[]>;\n}\n\nexport class AutomationEngine implements IAutomationService {\n /**\n * ADR-0044: maximum times a single node may be (re-)entered at the top\n * level of one run before the engine aborts it as a runaway back-edge\n * loop. Generous on purpose — the product guard (`maxRevisions`) sits\n * orders of magnitude lower.\n */\n static readonly MAX_NODE_REENTRIES = 100;\n\n private flows = new Map<string, FlowParsed>();\n private flowEnabled = new Map<string, boolean>();\n private flowVersionHistory = new Map<string, Array<{ version: number; definition: FlowParsed; createdAt: string }>>();\n private nodeExecutors = new Map<string, NodeExecutor>();\n private actionDescriptors = new Map<string, ActionDescriptor>();\n private triggers = new Map<string, FlowTrigger>();\n /**\n * Flows currently wired to a trigger, keyed by flow name → the trigger\n * `type` that owns the binding. Used to avoid double-binding and to know\n * which trigger to `stop()` when a flow is unregistered/disabled.\n */\n private boundFlowTriggers = new Map<string, string>();\n /** Connectors registered by integration plugins, keyed by connector name (ADR-0018 §Addendum). */\n private connectors = new Map<string, RegisteredConnector>();\n /** Bridge to the host function registry for `script`-node calls (#1870), if wired. */\n private functionResolver: FlowFunctionResolver | null = null;\n private executionLogs: ExecutionLogEntry[] = [];\n private readonly maxLogSize: number;\n private logger: Logger;\n /**\n * Runs paused at a node, keyed by runId (ADR-0019). In-memory hot cache —\n * mirrored to {@link store} when one is configured, so a pause survives a\n * process restart. See {@link SuspendedRun}.\n */\n private suspendedRuns = new Map<string, SuspendedRun>();\n /**\n * Optional durable backing for {@link suspendedRuns}. When set, suspended\n * runs are persisted on suspend and rehydrated on resume after a restart;\n * when absent, behaviour is purely in-memory (the historical default).\n */\n private store?: SuspendedRunStore;\n /**\n * Run ids currently mid-resume — an in-process idempotency guard so a\n * duplicate `resume(runId)` can't re-enter and double-run side effects.\n */\n private resuming = new Set<string>();\n\n constructor(logger: Logger, store?: SuspendedRunStore, options?: AutomationEngineOptions) {\n this.logger = logger;\n this.store = store;\n this.maxLogSize = options?.maxLogSize ?? DEFAULT_MAX_EXECUTION_LOG_SIZE;\n }\n\n /**\n * Attach (or replace) the durable {@link SuspendedRunStore}. Used by the\n * service plugin to upgrade the engine to DB-backed persistence once the\n * ObjectQL engine is available (the engine is constructed earlier, during\n * `init`, before services are wired).\n */\n setSuspendedRunStore(store: SuspendedRunStore): void {\n this.store = store;\n }\n\n /**\n * Generate a process-unique run id. Includes a random component so ids do\n * not collide with runs persisted by a previous process lifetime (a plain\n * incrementing counter would reissue `run_1` after a restart, clashing with\n * a still-suspended durable run).\n */\n private nextRunId(): string {\n const g = globalThis as { crypto?: { randomUUID?: () => string } };\n const rand = g.crypto?.randomUUID\n ? g.crypto.randomUUID()\n : `${Date.now().toString(36)}${Math.random().toString(36).slice(2, 10)}`;\n return `run_${rand}`;\n }\n\n /**\n * Persist a suspended run to the in-memory cache and (best-effort) the\n * durable store. A store failure is logged but does not fail the run — the\n * in-memory copy still allows in-process resume; only cross-restart\n * durability is lost.\n */\n private async persistSuspendedRun(run: SuspendedRun): Promise<void> {\n this.suspendedRuns.set(run.runId, run);\n if (this.store) {\n try {\n await this.store.save(run);\n } catch (err) {\n this.logger.warn(\n `[automation] failed to persist suspended run '${run.runId}' to durable store (kept in memory only): ${(err as Error).message}`,\n );\n }\n }\n }\n\n /**\n * Drop a suspended run from the in-memory cache and (best-effort) the\n * durable store. Called once the run is claimed for resume or reaches a\n * terminal state.\n */\n private async forgetSuspendedRun(runId: string): Promise<void> {\n this.suspendedRuns.delete(runId);\n if (this.store) {\n try {\n await this.store.delete(runId);\n } catch (err) {\n this.logger.warn(\n `[automation] failed to delete suspended run '${runId}' from durable store: ${(err as Error).message}`,\n );\n }\n }\n }\n\n // ── Plugin Extension API ──────────────────────────────\n\n /** Register a node executor (called by plugins) */\n registerNodeExecutor(executor: NodeExecutor): void {\n if (this.nodeExecutors.has(executor.type)) {\n this.logger.warn(`Node executor '${executor.type}' replaced`);\n }\n this.nodeExecutors.set(executor.type, executor);\n\n // Publish the ADR-0018 action descriptor into the registry, so the\n // type validates as a legal flow node and appears in the designer\n // palette. A descriptor's `type` should match the executor's; we key\n // on the descriptor's `type` and warn on mismatch rather than silently\n // diverging.\n if (executor.descriptor) {\n const descriptorType = executor.descriptor.type;\n if (descriptorType !== executor.type) {\n this.logger.warn(\n `Node executor '${executor.type}' publishes a descriptor for type '${descriptorType}' — registering under both.`,\n );\n }\n this.actionDescriptors.set(descriptorType, executor.descriptor);\n }\n\n this.logger.info(`Node executor registered: ${executor.type}`);\n }\n\n /**\n * Register a **deprecated alias** of a canonical node type (ADR-0018 M3).\n *\n * The alias is a real registered executor, so old saved flows whose nodes\n * use the alias type keep validating and running with no migration. At\n * execute time it delegates to the canonical executor (resolved live, so the\n * canonical may be registered before or after the alias), logging a one-time\n * deprecation warning. Its published descriptor is flagged `deprecated` +\n * `aliasOf` so the designer palette can hide or mark it while the canonical\n * type is the one offered for new authoring.\n *\n * This is how ADR-0018 collapses the five outbound verbs onto `http` /\n * `notify`: `http_request` / `http_call` / `webhook` become aliases of\n * `http`.\n */\n registerNodeAlias(\n alias: string,\n canonicalType: string,\n meta?: { name?: string; category?: ActionDescriptor['category']; paradigms?: ActionDescriptor['paradigms']; needsOutbox?: boolean },\n ): void {\n const engine = this;\n let warned = false;\n this.registerNodeExecutor({\n type: alias,\n descriptor: defineActionDescriptor({\n type: alias,\n version: '1.0.0',\n name: meta?.name ?? alias,\n description: `Deprecated alias of '${canonicalType}' (ADR-0018 M3). Author new flows with '${canonicalType}'.`,\n category: meta?.category ?? 'io',\n source: 'builtin',\n paradigms: meta?.paradigms ?? ['flow', 'approval'],\n supportsRetry: true,\n needsOutbox: meta?.needsOutbox ?? false,\n deprecated: true,\n aliasOf: canonicalType,\n }),\n async execute(node, variables, context) {\n if (!warned) {\n warned = true;\n engine.logger.warn(\n `Node type '${alias}' is deprecated; use '${canonicalType}' (ADR-0018 M3). Existing flows keep running via the alias.`,\n );\n }\n const target = engine.nodeExecutors.get(canonicalType);\n if (!target) {\n return {\n success: false,\n error: `alias '${alias}' → '${canonicalType}': canonical executor not registered`,\n };\n }\n return target.execute(node, variables, context);\n },\n });\n this.logger.info(`Node alias registered: ${alias} → ${canonicalType} (deprecated)`);\n }\n\n /** Unregister a node executor (hot-unplug) */\n unregisterNodeExecutor(type: string): void {\n const executor = this.nodeExecutors.get(type);\n this.nodeExecutors.delete(type);\n // Drop the published descriptor (keyed by descriptor.type, which may\n // differ from the executor type).\n this.actionDescriptors.delete(type);\n if (executor?.descriptor) {\n this.actionDescriptors.delete(executor.descriptor.type);\n }\n this.logger.info(`Node executor unregistered: ${type}`);\n }\n\n /** Register a trigger (called by plugins) */\n registerTrigger(trigger: FlowTrigger): void {\n this.triggers.set(trigger.type, trigger);\n this.logger.info(`Trigger registered: ${trigger.type}`);\n // A trigger may be registered *after* its flows (e.g. AutomationServicePlugin\n // pulls flows at start(); a trigger plugin wires up on kernel:ready, which\n // fires later). Activate any already-registered flow that maps to this type.\n for (const name of this.flows.keys()) {\n if (this.boundFlowTriggers.has(name)) continue;\n const resolved = this.resolveTriggerBinding(name);\n if (resolved?.triggerType === trigger.type) {\n this.activateFlowTrigger(name);\n }\n }\n }\n\n /** Unregister a trigger (hot-unplug) */\n unregisterTrigger(type: string): void {\n // Tear down every flow bound to this trigger before dropping it.\n for (const [name, boundType] of [...this.boundFlowTriggers]) {\n if (boundType !== type) continue;\n try {\n this.triggers.get(type)?.stop(name);\n } catch (err) {\n this.logger.warn(`Trigger '${type}' stop('${name}') failed: ${(err as Error).message}`);\n }\n this.boundFlowTriggers.delete(name);\n }\n this.triggers.delete(type);\n this.logger.info(`Trigger unregistered: ${type}`);\n }\n\n /**\n * Derive a flow's trigger binding from its `start` node, or `undefined` if\n * the flow has no auto-trigger (manual / screen). The convention —\n * established by the showcase flows — is that the start node carries the\n * trigger details in its `config`: `{ objectName, triggerType, condition }`\n * for record-change, or a `schedule` descriptor for time-based flows.\n */\n private resolveTriggerBinding(\n flowName: string,\n ): { triggerType: string; binding: FlowTriggerBinding } | undefined {\n const flow = this.flows.get(flowName);\n if (!flow) return undefined;\n const startNode = flow.nodes.find(n => n.type === 'start');\n const config = (startNode?.config ?? {}) as Record<string, unknown>;\n const triggerType = typeof config.triggerType === 'string' ? config.triggerType : undefined;\n\n if (triggerType && triggerType.startsWith('record-')) {\n return {\n triggerType: 'record_change',\n binding: {\n flowName,\n object: typeof config.objectName === 'string' ? config.objectName : undefined,\n event: triggerType,\n condition: (config.condition as FlowTriggerBinding['condition']) ?? undefined,\n config,\n },\n };\n }\n\n if (config.schedule != null || flow.type === 'schedule') {\n return {\n triggerType: 'schedule',\n binding: { flowName, schedule: config.schedule, condition: (config.condition as FlowTriggerBinding['condition']) ?? undefined, config },\n };\n }\n\n // Inbound HTTP (ADR-0041 Tier 1): an `api` flow waits for an external\n // POST. The concrete trigger (`@objectstack/trigger-api`) mounts the\n // endpoint and enqueues; the binding's `config` carries the hook\n // details (`hookId`, `secret`) from the start node.\n if (flow.type === 'api' || triggerType === 'api') {\n return {\n triggerType: 'api',\n binding: { flowName, condition: (config.condition as FlowTriggerBinding['condition']) ?? undefined, config },\n };\n }\n\n return undefined;\n }\n\n /**\n * Bind a flow to its matching registered trigger (idempotent). No-op when\n * the flow has no trigger binding or no trigger is registered for its type\n * yet — {@link registerTrigger} re-attempts activation when one arrives.\n */\n private activateFlowTrigger(flowName: string): void {\n if (this.boundFlowTriggers.has(flowName)) return;\n const resolved = this.resolveTriggerBinding(flowName);\n if (!resolved) return;\n const trigger = this.triggers.get(resolved.triggerType);\n if (!trigger) return;\n try {\n trigger.start(resolved.binding, (ctx: AutomationContext) => this.execute(flowName, ctx).then(() => undefined));\n this.boundFlowTriggers.set(flowName, resolved.triggerType);\n this.logger.info(`Flow '${flowName}' bound to trigger '${resolved.triggerType}'`);\n } catch (err) {\n this.logger.warn(`Failed to bind flow '${flowName}' to trigger '${resolved.triggerType}': ${(err as Error).message}`);\n }\n }\n\n /** Unbind a flow from its trigger, if bound. */\n private deactivateFlowTrigger(flowName: string): void {\n const boundType = this.boundFlowTriggers.get(flowName);\n if (!boundType) return;\n try {\n this.triggers.get(boundType)?.stop(flowName);\n } catch (err) {\n this.logger.warn(`Trigger '${boundType}' stop('${flowName}') failed: ${(err as Error).message}`);\n }\n this.boundFlowTriggers.delete(flowName);\n }\n\n /** Active flow→trigger bindings (observability / tests). */\n getActiveTriggerBindings(): Array<{ flowName: string; triggerType: string }> {\n return [...this.boundFlowTriggers].map(([flowName, triggerType]) => ({ flowName, triggerType }));\n }\n\n /**\n * Register a connector (called by integration plugins, ADR-0018 §Addendum).\n * Validates the definition against {@link ConnectorSchema} and asserts every\n * declared action has a handler, so a half-wired connector fails loudly at\n * registration rather than silently at dispatch. Re-registering the same\n * name replaces (mirrors {@link registerNodeExecutor}).\n */\n registerConnector(def: Connector, handlers: Record<string, ConnectorActionHandler>): void {\n const parsed = ConnectorSchema.parse(def);\n for (const action of parsed.actions ?? []) {\n if (typeof handlers[action.key] !== 'function') {\n throw new Error(\n `Connector '${parsed.name}': action '${action.key}' is declared but no handler was provided`,\n );\n }\n }\n if (this.connectors.has(parsed.name)) {\n this.logger.warn(`Connector '${parsed.name}' replaced`);\n }\n this.connectors.set(parsed.name, { def: parsed, handlers });\n this.logger.info(\n `Connector registered: ${parsed.name} (${Object.keys(handlers).length} action handlers)`,\n );\n }\n\n /** Unregister a connector (hot-unplug). */\n unregisterConnector(name: string): void {\n this.connectors.delete(name);\n this.logger.info(`Connector unregistered: ${name}`);\n }\n\n /**\n * Resolve the handler for a connector action, used by the baseline\n * `connector_action` node. Returns `undefined` when the connector or action\n * is not registered, so the node can fail the step with a clear error.\n */\n resolveConnectorAction(connectorId: string, actionId: string): ConnectorActionHandler | undefined {\n return this.connectors.get(connectorId)?.handlers[actionId];\n }\n\n /**\n * Wire the engine to the host's named-function registry (#1870). The\n * automation plugin calls this in `start()` with a resolver backed by\n * ObjectQL's `resolveFunction` (populated from `bundle.functions` /\n * `defineStack({ functions })`), so a `script` node can invoke an\n * authored function by name. Passing `null` detaches the bridge.\n */\n setFunctionResolver(resolver: FlowFunctionResolver | null): void {\n this.functionResolver = resolver;\n }\n\n /**\n * Resolve a named function for a `script` node. Returns `undefined` when no\n * resolver is wired or the name is unregistered — the node then fails the\n * step with a clear error rather than silently no-op'ing.\n */\n resolveFunction(name: string): FlowFunctionHandler | undefined {\n return this.functionResolver?.(name) ?? undefined;\n }\n\n /** Get all registered connector names. */\n getRegisteredConnectors(): string[] {\n return [...this.connectors.keys()];\n }\n\n /**\n * Get a designer-facing descriptor for every registered connector — its\n * identity plus the actions it exposes (input/output JSON Schema). Backs\n * `GET /api/v1/automation/connectors` so the designer can fill the\n * `connector_action` node's connector / action / input pickers (ADR-0022).\n * Handlers are omitted — they are runtime code, not metadata.\n */\n getConnectorDescriptors(): ConnectorDescriptor[] {\n return [...this.connectors.values()].map(({ def }) => ({\n name: def.name,\n label: def.label,\n type: def.type,\n description: def.description,\n icon: def.icon,\n actions: (def.actions ?? []).map((a) => ({\n key: a.key,\n label: a.label,\n description: a.description,\n inputSchema: a.inputSchema,\n outputSchema: a.outputSchema,\n })),\n }));\n }\n\n /** Get all registered node types */\n getRegisteredNodeTypes(): string[] {\n return [...this.nodeExecutors.keys()];\n }\n\n /**\n * Get all published action descriptors (ADR-0018). Backs both flow\n * validation and the designer palette (`GET /api/v1/automation/actions`).\n * Only executors that published a descriptor appear here.\n */\n getActionDescriptors(): ActionDescriptor[] {\n return [...this.actionDescriptors.values()];\n }\n\n /** Get the action descriptor for a single node type, if published. */\n getActionDescriptor(type: string): ActionDescriptor | undefined {\n return this.actionDescriptors.get(type);\n }\n\n /** Get all registered trigger types */\n getRegisteredTriggerTypes(): string[] {\n return [...this.triggers.keys()];\n }\n\n // ── IAutomationService Contract Implementation ────────\n\n registerFlow(name: string, definition: unknown): void {\n const parsed = FlowSchema.parse(definition);\n\n // DAG cycle detection\n this.detectCycles(parsed);\n\n // ADR-0031 — validate structured control-flow constructs (loop bodies,\n // parallel branches, try/catch regions) are well-formed (single-entry/\n // single-exit, acyclic). Reject the malformed before it can run.\n validateControlFlow(parsed);\n\n // ADR-0018 §M1 — validate node types against the live action registry.\n // The protocol no longer gates `type` with a closed enum; membership is\n // checked here instead. Soft-fail (warn, don't throw): a flow authored\n // against a plugin that is currently disabled should still register, and\n // executeNode() already throws NO_EXECUTOR at run time for unknown types.\n this.validateNodeTypes(name, parsed);\n\n // ADR-0032 §Decision 1a — parse-validate every predicate at registration,\n // so a malformed condition (e.g. the #1491 `{record.x}` template-brace-in-\n // CEL mistake) is a LOUD registration error with the offending source,\n // not a silent runtime `false`. Hard-fail: a broken predicate is never\n // safe to run.\n this.validateFlowExpressions(name, parsed);\n\n // Version history management\n const history = this.flowVersionHistory.get(name) ?? [];\n history.push({\n version: parsed.version,\n definition: parsed,\n createdAt: new Date().toISOString(),\n });\n this.flowVersionHistory.set(name, history);\n\n this.flows.set(name, parsed);\n if (!this.flowEnabled.has(name)) {\n this.flowEnabled.set(name, true);\n }\n this.logger.info(`Flow registered: ${name} (version ${parsed.version})`);\n\n // Re-bind in case the definition changed its trigger, then (re)activate.\n this.deactivateFlowTrigger(name);\n if (this.flowEnabled.get(name) !== false) {\n this.activateFlowTrigger(name);\n }\n }\n\n unregisterFlow(name: string): void {\n this.deactivateFlowTrigger(name);\n this.flows.delete(name);\n this.flowEnabled.delete(name);\n this.flowVersionHistory.delete(name);\n this.logger.info(`Flow unregistered: ${name}`);\n }\n\n async listFlows(): Promise<string[]> {\n return [...this.flows.keys()];\n }\n\n async getFlow(name: string): Promise<FlowParsed | null> {\n return this.flows.get(name) ?? null;\n }\n\n async toggleFlow(name: string, enabled: boolean): Promise<void> {\n if (!this.flows.has(name)) {\n throw new Error(`Flow '${name}' not found`);\n }\n this.flowEnabled.set(name, enabled);\n this.logger.info(`Flow '${name}' ${enabled ? 'enabled' : 'disabled'}`);\n // A disabled flow should stop receiving trigger events; a re-enabled one\n // should resume. execute() also guards disabled flows, but unbinding\n // avoids firing the trigger (and its event-source subscription) at all.\n if (enabled) {\n this.activateFlowTrigger(name);\n } else {\n this.deactivateFlowTrigger(name);\n }\n }\n\n /** Get flow version history */\n getFlowVersionHistory(name: string): Array<{ version: number; definition: FlowParsed; createdAt: string }> {\n return this.flowVersionHistory.get(name) ?? [];\n }\n\n /** Rollback flow to a specific version */\n rollbackFlow(name: string, version: number): void {\n const history = this.flowVersionHistory.get(name);\n if (!history) {\n throw new Error(`Flow '${name}' has no version history`);\n }\n const entry = history.find(h => h.version === version);\n if (!entry) {\n throw new Error(`Version ${version} not found for flow '${name}'`);\n }\n this.flows.set(name, entry.definition);\n this.logger.info(`Flow '${name}' rolled back to version ${version}`);\n }\n\n async listRuns(flowName: string, options?: { limit?: number; cursor?: string }): Promise<ExecutionLogEntry[]> {\n const limit = options?.limit ?? 20;\n const logs = this.executionLogs.filter(l => l.flowName === flowName);\n return logs.slice(-limit).reverse();\n }\n\n async getRun(runId: string): Promise<ExecutionLogEntry | null> {\n return this.executionLogs.find(l => l.id === runId) ?? null;\n }\n\n async execute(flowName: string, context?: AutomationContext): Promise<AutomationResult> {\n const startTime = Date.now();\n const flow = this.flows.get(flowName);\n\n if (!flow) {\n return { success: false, error: `Flow '${flowName}' not found` };\n }\n\n // Check if flow is disabled\n if (this.flowEnabled.get(flowName) === false) {\n return { success: false, error: `Flow '${flowName}' is disabled` };\n }\n\n // Initialize variable context\n const variables = new Map<string, unknown>();\n if (flow.variables) {\n for (const v of flow.variables) {\n if (v.isInput && context?.params?.[v.name] !== undefined) {\n variables.set(v.name, context.params[v.name]);\n }\n }\n }\n // Inject trigger record. `$record` is the canonical handle; `record` is a\n // friendlier alias so templates/conditions can write `{record.title}` and\n // `record.status`. We also flatten the record's own fields to top-level\n // variables (so bare references like `status`/`budget` resolve in start\n // conditions and edge predicates) WITHOUT clobbering flow inputs already\n // seeded above. `previous` exposes the pre-update row for transition gates.\n if (context?.record) {\n variables.set('$record', context.record);\n variables.set('record', context.record);\n for (const [k, v] of Object.entries(context.record)) {\n if (!variables.has(k)) variables.set(k, v);\n }\n }\n if (context?.previous) {\n variables.set('previous', context.previous);\n }\n\n const runId = this.nextRunId();\n // Expose the run id to executors (ADR-0019): a pausing node (e.g. Approval)\n // reads `$runId` to map its external state back to this run for resume.\n variables.set('$runId', runId);\n // Expose flow identity to executors so externalized state (e.g. an\n // approval request row) can carry a human-readable origin. Captured in\n // the variable snapshot, so still present after a suspend/resume.\n variables.set('$flowName', flowName);\n variables.set('$flowLabel', flow.label ?? flowName);\n const startedAt = new Date().toISOString();\n const steps: StepLogEntry[] = [];\n\n try {\n // Find the start node\n const startNode = flow.nodes.find(n => n.type === 'start');\n if (!startNode) {\n return { success: false, error: 'Flow has no start node' };\n }\n\n // Trigger-condition gate. The start node's `condition` is the predicate\n // that decides whether the trigger event should launch this flow (e.g.\n // `status == \"done\" && previous.status != \"done\"`). The engine — not the\n // trigger — owns evaluating it, so every trigger type (record-change,\n // schedule, …) and a manual `execute()` share one gate. Plain-string\n // conditions are routed through CEL so bare field references resolve.\n const startCondition = (startNode.config as Record<string, unknown> | undefined)?.condition as\n | string\n | { dialect?: string; source?: string; ast?: unknown }\n | undefined;\n if (startCondition !== undefined && startCondition !== null && startCondition !== '') {\n const condExpr =\n typeof startCondition === 'string' ? { dialect: 'cel', source: startCondition } : startCondition;\n if (!this.evaluateCondition(condExpr, variables)) {\n this.logger.debug(`Flow '${flowName}' skipped: start condition not met`);\n return { success: true, output: { skipped: true, reason: 'condition_not_met' } };\n }\n }\n\n // Validate node input schemas before execution\n this.validateNodeInputSchemas(flow, variables);\n\n // DAG traversal execution\n await this.executeNode(startNode, flow, variables, context ?? {}, steps);\n\n // Collect output variables\n const output: Record<string, unknown> = {};\n if (flow.variables) {\n for (const v of flow.variables) {\n if (v.isOutput) {\n output[v.name] = variables.get(v.name);\n }\n }\n }\n\n const durationMs = Date.now() - startTime;\n\n // Record execution log\n this.recordLog({\n id: runId,\n flowName,\n flowVersion: flow.version,\n status: 'completed',\n startedAt,\n completedAt: new Date().toISOString(),\n durationMs,\n trigger: {\n type: context?.event ?? 'manual',\n userId: context?.userId,\n object: context?.object,\n },\n steps,\n output,\n });\n\n return {\n success: true,\n output,\n durationMs,\n };\n } catch (err: unknown) {\n // A node asked to suspend the run (ADR-0019 durable pause). Snapshot\n // the live state, record a `paused` log, and return the run id so the\n // caller can later `resume()` it. This is NOT a failure.\n if (isSuspendSignal(err)) {\n const durationMs = Date.now() - startTime;\n await this.persistSuspendedRun({\n runId,\n flowName,\n flowVersion: flow.version,\n nodeId: err.nodeId,\n variables: Object.fromEntries(variables),\n steps,\n context: context ?? {},\n startedAt,\n startTime,\n correlation: err.correlation,\n screen: err.screen,\n });\n this.recordLog({\n id: runId,\n flowName,\n flowVersion: flow.version,\n status: 'paused',\n startedAt,\n durationMs,\n trigger: {\n type: context?.event ?? 'manual',\n userId: context?.userId,\n object: context?.object,\n },\n steps,\n });\n return {\n success: true,\n status: 'paused',\n runId,\n durationMs,\n screen: err.screen,\n };\n }\n\n const errorMessage = err instanceof Error ? err.message : String(err);\n\n // Record failed execution log\n const durationMs = Date.now() - startTime;\n this.recordLog({\n id: runId,\n flowName,\n flowVersion: flow.version,\n status: 'failed',\n startedAt,\n completedAt: new Date().toISOString(),\n durationMs,\n trigger: {\n type: context?.event ?? 'manual',\n userId: context?.userId,\n object: context?.object,\n },\n steps,\n error: errorMessage,\n });\n\n // Error handling strategy\n if (flow.errorHandling?.strategy === 'retry') {\n return this.retryExecution(flowName, context, startTime, flow.errorHandling);\n }\n return {\n success: false,\n error: errorMessage,\n durationMs,\n };\n }\n }\n\n /**\n * Resume a run suspended at a node (ADR-0019 durable pause). Restores the\n * snapshotted variables, merges `signal.output` under the suspended node's\n * id, and continues traversal from that node's out-edges — optionally\n * restricted to the edge labelled `signal.branchLabel` (e.g. the approval\n * decision). The continuation may itself suspend again, in which case this\n * returns `{ status: 'paused', runId }` afresh.\n *\n * **Subflow chains (nested pause, linked-runs model).** A run paused at a\n * `subflow` node (correlation `subflow:<childRunId>`) DELEGATES the signal\n * down to the suspended child; a run that completes and carries\n * `$parentRunId` in its context BUBBLES its output up by auto-resuming the\n * parent. Both directions compose recursively, so arbitrarily nested\n * subflow pauses resolve from either end (UI holds the parent run id;\n * approval/wait infrastructure holds the child's).\n */\n async resume(runId: string, signal?: ResumeSignal): Promise<AutomationResult> {\n return this.resumeInternal(runId, signal, false);\n }\n\n /**\n * @param skipBubble - Set when the caller is the subflow DELEGATION path,\n * which continues the parent itself after the child completes — the\n * child's own up-bubble must stay off so the parent isn't resumed twice.\n */\n private async resumeInternal(runId: string, signal: ResumeSignal | undefined, skipBubble: boolean): Promise<AutomationResult> {\n // Idempotency guard (set synchronously, before any await): reject a\n // concurrent duplicate resume of the same run so side effects can't run\n // twice. A duplicate that arrives *after* this one finishes finds no\n // suspended run and returns the \"no suspended run\" error below.\n if (this.resuming.has(runId)) {\n return { success: false, error: `Run '${runId}' is already being resumed` };\n }\n this.resuming.add(runId);\n try {\n // Hot path: suspended in this process. Cold path: rehydrate from the\n // durable store (e.g. the process restarted since the pause, ADR-0019).\n let run = this.suspendedRuns.get(runId) ?? null;\n if (!run && this.store) {\n try {\n run = await this.store.load(runId);\n } catch (err) {\n this.logger.warn(\n `[automation] failed to load suspended run '${runId}' from durable store: ${(err as Error).message}`,\n );\n }\n }\n if (!run) {\n return { success: false, error: `No suspended run '${runId}'` };\n }\n const flow = this.flows.get(run.flowName);\n if (!flow) {\n return { success: false, error: `Flow '${run.flowName}' not found for run '${runId}'` };\n }\n const node = flow.nodes.find(n => n.id === run.nodeId);\n if (!node) {\n return { success: false, error: `Suspended node '${run.nodeId}' no longer exists in flow '${run.flowName}'` };\n }\n\n // ── Subflow delegation (nested pause): this run is paused at a\n // `subflow` node whose child run itself suspended. The caller's\n // signal is meant for the node the CHILD paused on (its screen /\n // approval / wait), so forward it down. The child resumes with\n // bubbling off — when it completes, *this* invocation continues the\n // parent from the subflow node with the child's output, using the\n // same mapping as the synchronous path.\n if (typeof run.correlation === 'string' && run.correlation.startsWith('subflow:')) {\n const childRunId = run.correlation.slice('subflow:'.length);\n // Capture the child's row BEFORE resuming consumes it — the\n // output-variable mapping rides on the child's context.\n const childRun =\n this.suspendedRuns.get(childRunId) ??\n (this.store ? await this.store.load(childRunId).catch(() => null) : null);\n if (childRun) {\n const childRes = await this.resumeInternal(childRunId, signal, true);\n if (childRes.status === 'paused') {\n // Child paused again (e.g. the next screen of a wizard).\n // This run stays suspended; refresh its surfaced screen\n // so a re-fetch (getSuspendedScreen) shows the new one.\n if (childRes.screen && childRes.screen !== run.screen) {\n await this.persistSuspendedRun({ ...run, screen: childRes.screen });\n }\n return {\n success: true,\n status: 'paused',\n runId,\n durationMs: Date.now() - run.startTime,\n screen: childRes.screen,\n };\n }\n if (!childRes.success) {\n const error = `subflow run '${childRunId}' (${childRun.flowName}) failed: ${childRes.error ?? 'unknown error'}`;\n await this.failSuspendedRun(run, error);\n return { success: false, error, durationMs: Date.now() - run.startTime };\n }\n // Child completed — continue below with its output as the\n // resume signal (replaces the caller's signal, which the\n // child already consumed).\n signal = this.buildSubflowResumeSignal(childRun.context, childRes.output);\n } else {\n this.logger.warn(\n `[automation] run '${runId}' is paused at subflow node '${run.nodeId}' but child run '${childRunId}' ` +\n `is gone — continuing without child output`,\n );\n }\n }\n\n // Consume the suspension *before* running downstream work — a run\n // resumes exactly once per pause, and a duplicate resume after a\n // partial restart must not double-run side effects.\n await this.forgetSuspendedRun(runId);\n\n // Restore variable context and apply the resume signal's output as if it\n // were the node's output, so downstream edges branch on it.\n const variables = new Map<string, unknown>(Object.entries(run.variables));\n if (signal?.output) {\n for (const [key, value] of Object.entries(signal.output)) {\n variables.set(`${run.nodeId}.${key}`, value);\n }\n }\n // Bare flow variables — a `screen` node's collected inputs land under\n // their plain names so downstream `{var}` interpolation / conditions\n // read them directly (e.g. `new_assignee` → update_record fields).\n if (signal?.variables) {\n for (const [key, value] of Object.entries(signal.variables)) {\n variables.set(key, value);\n }\n }\n\n const steps = run.steps;\n const context = run.context;\n\n try {\n // ── Map re-entry (sequential multi-instance, ADR-0037 A2).\n // A run paused at a `map` node (correlation `map:<childRunId>`)\n // does NOT continue past the node on resume — it RE-RUNS the\n // node so the executor can record the just-completed unit and\n // start the next item. The default path continues past the node.\n if (typeof run.correlation === 'string' && run.correlation.startsWith('map:')) {\n await this.executeNode(node, flow, variables, context, steps);\n } else {\n await this.traverseNext(node, flow, variables, context, steps, signal?.branchLabel);\n }\n\n // Collect output variables\n const output: Record<string, unknown> = {};\n if (flow.variables) {\n for (const v of flow.variables) {\n if (v.isOutput) output[v.name] = variables.get(v.name);\n }\n }\n const durationMs = Date.now() - run.startTime;\n this.recordLog({\n id: runId,\n flowName: run.flowName,\n flowVersion: run.flowVersion,\n status: 'completed',\n startedAt: run.startedAt,\n completedAt: new Date().toISOString(),\n durationMs,\n trigger: {\n type: context.event ?? 'manual',\n userId: context.userId,\n object: context.object,\n },\n steps,\n output,\n });\n\n // ── Subflow up-bubble (nested pause): this run was a subflow\n // child whose parent suspended awaiting it. Auto-resume the\n // parent with our output, mapped like the synchronous path.\n // Skipped when the DELEGATION path drives the chain (it\n // continues the parent itself). Best-effort: the child's own\n // completion stands even if the parent continuation fails.\n if (!skipBubble) {\n await this.bubbleToParent(run, output);\n }\n\n // Surface the flow's friendly completion message so a screen-flow\n // runner shows it instead of a generic \"Done\".\n return { success: true, output, durationMs, successMessage: flow.successMessage };\n } catch (err: unknown) {\n // Re-suspended at a downstream node: persist a fresh continuation.\n if (isSuspendSignal(err)) {\n const durationMs = Date.now() - run.startTime;\n await this.persistSuspendedRun({\n ...run,\n nodeId: err.nodeId,\n variables: Object.fromEntries(variables),\n steps,\n correlation: err.correlation,\n screen: err.screen,\n });\n this.recordLog({\n id: runId,\n flowName: run.flowName,\n flowVersion: run.flowVersion,\n status: 'paused',\n startedAt: run.startedAt,\n durationMs,\n trigger: {\n type: context.event ?? 'manual',\n userId: context.userId,\n object: context.object,\n },\n steps,\n });\n return { success: true, status: 'paused', runId, durationMs, screen: err.screen };\n }\n\n const errorMessage = err instanceof Error ? err.message : String(err);\n const durationMs = Date.now() - run.startTime;\n this.recordLog({\n id: runId,\n flowName: run.flowName,\n flowVersion: run.flowVersion,\n status: 'failed',\n startedAt: run.startedAt,\n completedAt: new Date().toISOString(),\n durationMs,\n trigger: {\n type: context.event ?? 'manual',\n userId: context.userId,\n object: context.object,\n },\n steps,\n error: errorMessage,\n });\n // Subflow chain: a child failing terminally fails every\n // ancestor awaiting it — they can never be resumed otherwise.\n // The delegation path handles its own level (skipBubble).\n if (!skipBubble) {\n await this.failAncestors(run.context, errorMessage);\n }\n // Surface the flow's friendly error message (the raw error stays\n // in `error` for logs/diagnostics).\n return { success: false, error: errorMessage, durationMs, errorMessage: flow.errorMessage };\n }\n } finally {\n this.resuming.delete(runId);\n }\n }\n\n /**\n * Build the resume signal that maps a completed subflow child's output\n * into its parent — mirroring the synchronous path exactly: the engine's\n * standard `signal.output` merge lands it under `${subflowNodeId}.output`,\n * and `signal.variables` writes the bare `config.outputVariable` when the\n * child's context carries one (`$parentOutputVariable`).\n */\n private buildSubflowResumeSignal(childContext: AutomationContext | undefined, childOutput: unknown): ResumeSignal {\n const outVar = (childContext as Record<string, unknown> | undefined)?.$parentOutputVariable;\n return {\n output: { output: childOutput ?? null },\n ...(typeof outVar === 'string' && outVar\n ? { variables: { [outVar]: childOutput ?? null } }\n : {}),\n };\n }\n\n /**\n * Up-bubble for the subflow chain: when a completed run carries\n * `$parentRunId`, resume that parent with this run's output. Recursion via\n * the parent's own completion bubbles multi-level chains. Best-effort —\n * a failed parent continuation is logged, never thrown back at the\n * caller who resumed the child.\n */\n private async bubbleToParent(run: SuspendedRun, output: Record<string, unknown>): Promise<void> {\n const ctx = run.context as Record<string, unknown> | undefined;\n const parentRunId = ctx?.$parentRunId;\n if (typeof parentRunId !== 'string' || !parentRunId) return;\n try {\n // A `map` child (ADR-0037 A2): hand the unit's output to the map\n // node + flag the completion, so on re-entry it records this item\n // and starts the next. A plain subflow child uses the 1:1 mapping.\n const mapNode = ctx?.$parentMapNode;\n const sig = typeof mapNode === 'string' && mapNode\n ? { variables: { [`${mapNode}.$mapItemOutput`]: output ?? null, [`${mapNode}.$mapItemDone`]: true } }\n : this.buildSubflowResumeSignal(run.context, output);\n const parentRes = await this.resumeInternal(parentRunId, sig, false);\n if (!parentRes.success) {\n this.logger.warn(\n `[automation] subflow run '${run.runId}' completed but resuming parent '${parentRunId}' failed: ${parentRes.error}`,\n );\n }\n } catch (err) {\n this.logger.warn(\n `[automation] subflow run '${run.runId}' completed but resuming parent '${parentRunId}' threw: ${(err as Error).message}`,\n );\n }\n }\n\n /**\n * Terminally fail a suspended run: consume its continuation and record a\n * `failed` log so it stops surfacing as resumable. Used when a subflow\n * descendant fails — the ancestor awaiting it can never be resumed.\n */\n private async failSuspendedRun(run: SuspendedRun, error: string): Promise<void> {\n await this.forgetSuspendedRun(run.runId);\n this.recordLog({\n id: run.runId,\n flowName: run.flowName,\n flowVersion: run.flowVersion,\n status: 'failed',\n startedAt: run.startedAt,\n completedAt: new Date().toISOString(),\n durationMs: Date.now() - run.startTime,\n trigger: {\n type: run.context?.event ?? 'manual',\n userId: run.context?.userId,\n object: run.context?.object,\n },\n steps: run.steps,\n error,\n });\n }\n\n /**\n * Cancel a suspended run (ADR-0044): consume its continuation and record a\n * terminal `cancelled` log so it stops surfacing as resumable. The\n * engine-level primitive behind \"the submitter abandoned the revision\n * window\" — recalling there leaves the run paused at a wait node with no\n * reject edge to resume down, so the run must end, not continue. Returns\n * `false` when no suspended run exists under the id (already terminal /\n * unknown), which callers treat as idempotent success.\n */\n async cancelRun(runId: string, reason?: string): Promise<boolean> {\n let run = this.suspendedRuns.get(runId) ?? null;\n if (!run && this.store) {\n try {\n run = await this.store.load(runId);\n } catch (err) {\n this.logger.warn(\n `[automation] cancelRun: failed to load suspended run '${runId}' from durable store: ${(err as Error).message}`,\n );\n }\n }\n if (!run) return false;\n await this.forgetSuspendedRun(runId);\n this.recordLog({\n id: run.runId,\n flowName: run.flowName,\n flowVersion: run.flowVersion,\n status: 'cancelled',\n startedAt: run.startedAt,\n completedAt: new Date().toISOString(),\n durationMs: Date.now() - run.startTime,\n trigger: {\n type: run.context?.event ?? 'manual',\n userId: run.context?.userId,\n object: run.context?.object,\n },\n steps: run.steps,\n error: reason,\n });\n return true;\n }\n\n /**\n * Walk a failed run's `$parentRunId` chain and fail each suspended\n * ancestor (see {@link failSuspendedRun}). Bounded so a corrupt context\n * can't loop forever.\n */\n private async failAncestors(context: AutomationContext | undefined, error: string): Promise<void> {\n let parentId = (context as Record<string, unknown> | undefined)?.$parentRunId;\n let hops = 0;\n while (typeof parentId === 'string' && parentId && hops++ < 32) {\n const parent =\n this.suspendedRuns.get(parentId) ??\n (this.store ? await this.store.load(parentId).catch(() => null) : null);\n if (!parent) return;\n await this.failSuspendedRun(parent, `subflow descendant failed: ${error}`);\n parentId = (parent.context as Record<string, unknown> | undefined)?.$parentRunId;\n }\n }\n\n /**\n * List the runs currently suspended awaiting {@link resume} (ADR-0019).\n * Backs operability surfaces such as a \"pending approvals\" view.\n *\n * Synchronous — reads the in-memory cache only, so after a process restart\n * runs that suspended in a prior lifetime are not listed here even though\n * they remain durably stored and resumable by id. Use\n * {@link listSuspendedRunsDurable} to include those.\n */\n listSuspendedRuns(): Array<{ runId: string; flowName: string; nodeId: string; correlation?: string }> {\n return [...this.suspendedRuns.values()].map(r => ({\n runId: r.runId,\n flowName: r.flowName,\n nodeId: r.nodeId,\n correlation: r.correlation,\n }));\n }\n\n /**\n * Like {@link listSuspendedRuns} but includes runs held only in the durable\n * {@link SuspendedRunStore} (e.g. suspended before a restart). The in-memory\n * cache takes precedence on id collisions. Falls back to the in-memory list\n * when no store is configured.\n */\n async listSuspendedRunsDurable(): Promise<Array<{ runId: string; flowName: string; nodeId: string; correlation?: string }>> {\n const byId = new Map<string, { runId: string; flowName: string; nodeId: string; correlation?: string }>();\n if (this.store) {\n try {\n for (const r of await this.store.list()) {\n byId.set(r.runId, { runId: r.runId, flowName: r.flowName, nodeId: r.nodeId, correlation: r.correlation });\n }\n } catch (err) {\n this.logger.warn(`[automation] failed to list suspended runs from durable store: ${(err as Error).message}`);\n }\n }\n // In-memory entries win — they are the freshest copy.\n for (const r of this.suspendedRuns.values()) {\n byId.set(r.runId, { runId: r.runId, flowName: r.flowName, nodeId: r.nodeId, correlation: r.correlation });\n }\n return [...byId.values()];\n }\n\n /**\n * The screen a paused run is currently waiting on (screen-flow runtime), or\n * `null` if the run isn't suspended / didn't pause at a screen node. Lets a\n * UI flow-runner re-fetch the form after a refresh.\n */\n getSuspendedScreen(runId: string): ScreenSpec | null {\n return this.suspendedRuns.get(runId)?.screen ?? null;\n }\n\n // ── DAG Traversal Core ──────────────────────────────────\n\n private recordLog(entry: ExecutionLogEntry): void {\n this.executionLogs.push(entry);\n // Evict oldest logs when exceeding max size\n if (this.executionLogs.length > this.maxLogSize) {\n this.executionLogs.splice(0, this.executionLogs.length - this.maxLogSize);\n }\n }\n\n /**\n * Validate each node's `type` against the live action registry (ADR-0018).\n * A type is known if it is structural (start/end), has a registered\n * executor, or has a published action descriptor. Unknown types are\n * warned about (not rejected) so flows authored against a temporarily\n * absent plugin still register; the runtime surfaces a hard NO_EXECUTOR\n * error if such a node is actually executed.\n */\n private validateNodeTypes(flowName: string, flow: FlowParsed): void {\n const known = new Set<string>([\n ...FLOW_STRUCTURAL_NODE_TYPES,\n ...this.nodeExecutors.keys(),\n ...this.actionDescriptors.keys(),\n ]);\n const unknown = [...new Set(\n flow.nodes.map(n => n.type).filter(t => !known.has(t)),\n )];\n if (unknown.length > 0) {\n this.logger.warn(\n `Flow '${flowName}' references node type(s) with no registered executor or descriptor: ` +\n `${unknown.join(', ')}. They will fail at execution time unless a plugin registers them. ` +\n `Registered types: ${[...known].join(', ') || '(none)'}`,\n );\n }\n }\n\n /**\n * ADR-0032 §Decision 1a — parse-validate every predicate in the flow at\n * registration. Predicates are bare CEL; this catches the #1491 class\n * (`{record.x}` template braces in a condition → CEL parse error) and any\n * other malformed predicate LOUDLY, with the offending location + source +\n * a corrective hint, instead of letting it fail silently at run time.\n *\n * Only the *predicate* surfaces are checked here (start/node `config.condition`\n * and `edge.condition`) — node string fields are templates (a different\n * dialect) and are validated by the template engine, not as CEL.\n */\n private validateFlowExpressions(flowName: string, flow: FlowParsed): void {\n const failures: string[] = [];\n\n const check = (where: string, raw: unknown): void => {\n if (raw == null) return;\n // Conditions are predicates (bare CEL). Delegate to the one shared\n // validator (ADR-0032 §5) so the corrective message matches the CLI\n // build and the agent `validate_expression` tool exactly.\n const result = validateExpression('predicate', raw as string | { dialect?: string; source?: string });\n for (const e of result.errors) {\n failures.push(` • ${where}: ${e.message}\\n source: \\`${e.source}\\``);\n }\n };\n\n for (const node of flow.nodes) {\n const cfg = (node.config ?? {}) as Record<string, unknown>;\n // start-node trigger gate + decision/branch predicates live in config.condition\n check(`node '${node.id}' (${node.type}) condition`, cfg.condition);\n }\n for (const edge of flow.edges) {\n check(`edge '${edge.id}' (${edge.source}→${edge.target}) condition`, edge.condition as unknown);\n }\n\n if (failures.length > 0) {\n throw new Error(\n `Flow '${flowName}' has ${failures.length} invalid condition${failures.length > 1 ? 's' : ''} (ADR-0032 §1a). ` +\n `Conditions are bare CEL — do not wrap field references in \\`{…}\\` template braces:\\n${failures.join('\\n')}`,\n );\n }\n }\n\n /**\n * Detect cycles in the flow graph (DAG validation).\n * Uses DFS with coloring (white/gray/black) to detect back edges.\n * Throws an error with cycle details if a cycle is found.\n *\n * ADR-0044: edges explicitly typed `back` (declared back-edges — e.g. a\n * revise/rework loop re-entering an approval node) are excluded from the\n * analysis: the graph **minus `back` edges** must be a DAG. An unmarked\n * cycle is still rejected — authors opt in edge by edge. At run time a\n * `back` edge traverses like any default edge; the re-entry runaway guard\n * lives in {@link executeNode}.\n */\n private detectCycles(flow: FlowParsed): void {\n const WHITE = 0, GRAY = 1, BLACK = 2;\n const color = new Map<string, number>();\n const parent = new Map<string, string>();\n\n // Build adjacency list from edges\n const adj = new Map<string, string[]>();\n for (const node of flow.nodes) {\n color.set(node.id, WHITE);\n adj.set(node.id, []);\n }\n for (const edge of flow.edges) {\n if (edge.type === 'back') continue; // ADR-0044 declared back-edge\n const targets = adj.get(edge.source);\n if (targets) targets.push(edge.target);\n }\n\n const dfs = (nodeId: string): string[] | null => {\n color.set(nodeId, GRAY);\n for (const neighbor of adj.get(nodeId) ?? []) {\n if (color.get(neighbor) === GRAY) {\n // Back edge found — reconstruct cycle\n const cycle = [neighbor, nodeId];\n let cur = nodeId;\n while (cur !== neighbor) {\n cur = parent.get(cur)!;\n if (cur) cycle.push(cur);\n else break;\n }\n return cycle.reverse();\n }\n if (color.get(neighbor) === WHITE) {\n parent.set(neighbor, nodeId);\n const result = dfs(neighbor);\n if (result) return result;\n }\n }\n color.set(nodeId, BLACK);\n return null;\n };\n\n for (const node of flow.nodes) {\n if (color.get(node.id) === WHITE) {\n const cycle = dfs(node.id);\n if (cycle) {\n throw new Error(\n `Flow contains a cycle: ${cycle.join(' → ')}. Only DAG flows are allowed — ` +\n `to author an intentional rework loop, mark the cycle-closing edge with type: 'back' (ADR-0044).`,\n );\n }\n }\n }\n }\n\n /**\n * Get the runtime type name of a value for schema validation.\n */\n private getValueType(value: unknown): string {\n if (Array.isArray(value)) return 'array';\n if (typeof value === 'object' && value !== null) return 'object';\n return typeof value;\n }\n\n /**\n * Validate node input schemas before execution.\n * Checks that node config matches declared inputSchema if present.\n */\n private validateNodeInputSchemas(flow: FlowParsed, _variables: Map<string, unknown>): void {\n for (const node of flow.nodes) {\n if (node.inputSchema && node.config) {\n for (const [paramName, paramDef] of Object.entries(node.inputSchema)) {\n if (paramDef.required && !(paramName in (node.config as Record<string, unknown>))) {\n throw new Error(\n `Node '${node.id}' missing required input parameter '${paramName}'`,\n );\n }\n const value = (node.config as Record<string, unknown>)[paramName];\n if (value !== undefined) {\n const actualType = this.getValueType(value);\n if (actualType !== paramDef.type) {\n throw new Error(\n `Node '${node.id}' parameter '${paramName}' expected type '${paramDef.type}' but got '${actualType}'`,\n );\n }\n }\n }\n }\n }\n }\n\n /**\n * Execute a node with timeout support, fault edge handling, and step logging.\n */\n private async executeNode(\n node: FlowNodeParsed,\n flow: FlowParsed,\n variables: Map<string, unknown>,\n context: AutomationContext,\n steps: StepLogEntry[],\n ): Promise<void> {\n if (node.type === 'end') return;\n\n // ADR-0044 runaway guard: declared back-edges make re-entering a node\n // legal, so a misauthored unconditional loop could otherwise spin\n // forever. Count this node's prior *top-level* visits in the run's step\n // log (region body steps carry `parentNodeId` and are excluded — a\n // 200-iteration `loop` region is legitimate) and fail the run loudly\n // past the cap. Product-level guards (e.g. an approval node's\n // `maxRevisions`) terminate far earlier; this is the engine backstop.\n const priorVisits = steps.reduce(\n (n, s) => (s.nodeId === node.id && s.parentNodeId === undefined ? n + 1 : n), 0,\n );\n if (priorVisits >= AutomationEngine.MAX_NODE_REENTRIES) {\n throw new Error(\n `Node '${node.id}' was entered ${priorVisits} times in one run — aborting as a runaway loop ` +\n `(back-edge cycles must terminate; see ADR-0044)`,\n );\n }\n\n const stepStart = Date.now();\n const stepStartedAt = new Date().toISOString();\n\n // Find executor\n const executor = this.nodeExecutors.get(node.type);\n if (!executor) {\n // start node without executor is fine — just skip\n if (node.type !== 'start') {\n steps.push({\n nodeId: node.id,\n nodeType: node.type,\n status: 'failure',\n startedAt: stepStartedAt,\n completedAt: new Date().toISOString(),\n durationMs: Date.now() - stepStart,\n error: { code: 'NO_EXECUTOR', message: `No executor registered for node type '${node.type}'` },\n });\n throw new Error(`No executor registered for node type '${node.type}'`);\n }\n // Log start node step\n steps.push({\n nodeId: node.id,\n nodeType: node.type,\n status: 'success',\n startedAt: stepStartedAt,\n completedAt: new Date().toISOString(),\n durationMs: Date.now() - stepStart,\n });\n } else {\n // Execute node with optional timeout\n let result: NodeExecutionResult;\n try {\n if (node.timeoutMs && node.timeoutMs > 0) {\n result = await this.executeWithTimeout(\n executor.execute(node, variables, context),\n node.timeoutMs,\n node.id,\n );\n } else {\n result = await executor.execute(node, variables, context);\n }\n } catch (execErr: unknown) {\n const errMsg = execErr instanceof Error ? execErr.message : String(execErr);\n steps.push({\n nodeId: node.id,\n nodeType: node.type,\n status: 'failure',\n startedAt: stepStartedAt,\n completedAt: new Date().toISOString(),\n durationMs: Date.now() - stepStart,\n error: { code: 'EXECUTION_ERROR', message: errMsg },\n });\n\n // Check for fault edges\n const faultEdge = flow.edges.find(e => e.source === node.id && e.type === 'fault');\n if (faultEdge) {\n variables.set('$error', { nodeId: node.id, message: errMsg });\n const faultTarget = flow.nodes.find(n => n.id === faultEdge.target);\n if (faultTarget) {\n await this.executeNode(faultTarget, flow, variables, context, steps);\n return;\n }\n }\n throw execErr;\n }\n\n if (!result.success) {\n const errMsg = result.error ?? 'Unknown error';\n steps.push({\n nodeId: node.id,\n nodeType: node.type,\n status: 'failure',\n startedAt: stepStartedAt,\n completedAt: new Date().toISOString(),\n durationMs: Date.now() - stepStart,\n error: { code: 'NODE_FAILURE', message: errMsg },\n });\n\n // Write error output to variable context for downstream nodes\n variables.set('$error', { nodeId: node.id, message: errMsg, output: result.output });\n\n // Check for fault edges\n const faultEdge = flow.edges.find(e => e.source === node.id && e.type === 'fault');\n if (faultEdge) {\n const faultTarget = flow.nodes.find(n => n.id === faultEdge.target);\n if (faultTarget) {\n await this.executeNode(faultTarget, flow, variables, context, steps);\n return;\n }\n }\n throw new Error(`Node '${node.id}' failed: ${errMsg}`);\n }\n\n // Log successful step\n steps.push({\n nodeId: node.id,\n nodeType: node.type,\n status: 'success',\n startedAt: stepStartedAt,\n completedAt: new Date().toISOString(),\n durationMs: Date.now() - stepStart,\n });\n\n // #1479: fold a structured-region container's body/branch/handler\n // steps into the run log, right after the container's own step.\n if (result.childSteps?.length) {\n steps.push(...result.childSteps);\n }\n\n // Write back output variables\n if (result.output) {\n for (const [key, value] of Object.entries(result.output)) {\n variables.set(`${node.id}.${key}`, value);\n }\n }\n\n // ADR-0019 durable pause: the node did its on-entry work and asked to\n // suspend here. Output is already written above; unwind the recursion\n // up to execute()/resume(), which persists a continuation. Traversal\n // of this node's out-edges happens on resume, not now.\n if (result.suspend) {\n throw new FlowSuspendSignal(node.id, result.correlation, result.screen);\n }\n }\n\n // Continue to the node's successors.\n await this.traverseNext(node, flow, variables, context, steps);\n }\n\n /**\n * Traverse a node's out-edges and execute its successors. Split out of\n * {@link executeNode} so {@link resume} can re-enter traversal from a\n * suspended node without re-running the node body.\n *\n * @param branchLabel - When set (e.g. from a resume signal), restrict\n * traversal to out-edges whose `label` matches — this is how an Approval\n * node's `approve`/`reject` decision selects its downstream branch. When\n * no edge carries the label, traversal falls back to the normal edge set.\n */\n private async traverseNext(\n node: FlowNodeParsed,\n flow: FlowParsed,\n variables: Map<string, unknown>,\n context: AutomationContext,\n steps: StepLogEntry[],\n branchLabel?: string,\n ): Promise<void> {\n // Find next nodes — separate conditional and unconditional edges\n let outEdges = flow.edges.filter(\n e => e.source === node.id && e.type !== 'fault',\n );\n\n // Branch selection (resume): prefer edges tagged with the decision label.\n if (branchLabel) {\n const labeled = outEdges.filter(e => e.label === branchLabel);\n if (labeled.length > 0) outEdges = labeled;\n }\n\n const conditionalEdges: FlowEdgeParsed[] = [];\n const unconditionalEdges: FlowEdgeParsed[] = [];\n for (const edge of outEdges) {\n if (edge.condition) {\n conditionalEdges.push(edge);\n } else {\n unconditionalEdges.push(edge);\n }\n }\n\n // Conditional edges: evaluate sequentially (mutually exclusive)\n for (const edge of conditionalEdges) {\n if (this.evaluateCondition(edge.condition!, variables)) {\n const nextNode = flow.nodes.find(n => n.id === edge.target);\n if (nextNode) {\n await this.executeNode(nextNode, flow, variables, context, steps);\n }\n }\n }\n\n // Unconditional edges: execute in parallel (Promise.all)\n if (unconditionalEdges.length > 0) {\n const parallelTasks = unconditionalEdges\n .map(edge => flow.nodes.find(n => n.id === edge.target))\n .filter((n): n is FlowNodeParsed => n != null)\n .map(nextNode => this.executeNode(nextNode, flow, variables, context, steps));\n\n await Promise.all(parallelTasks);\n }\n }\n\n /**\n * Execute a structured control-flow **region** (ADR-0031) — the nested\n * body of a `loop` container (or, later, a `parallel` branch / `try_catch`\n * region). The region is a self-contained single-entry/single-exit\n * sub-graph carried in the container's `config`; it runs in the **enclosing\n * variable scope** (the caller's `variables` map), so the iterator variable\n * and any body mutations are visible to the surrounding flow — a region is\n * NOT a separate `subflow` invocation.\n *\n * The region executes against a synthetic flow view of its own\n * nodes/edges, so the main DAG traversal (`traverseNext`) is never aware of\n * scope markers — keeping the shared traversal untouched.\n *\n * #1479: the executed body steps are **returned** (tagged with `grouping`)\n * so the calling container node can fold them into the parent run log via\n * `NodeExecutionResult.childSteps`. Tagging only fills fields left undefined,\n * so when regions nest, each step keeps its **innermost** container's\n * `parentNodeId` / `iteration` / `regionKind`. On failure the region throws\n * as before (preserving `try_catch` retry semantics); a failed attempt's\n * partial steps are not surfaced.\n *\n * Durable pause (`suspend`) inside a region is not supported in this\n * iteration — it is converted into a clear error (mirrors the `subflow`\n * nested-pause guard).\n */\n async runRegion(\n region: FlowRegionParsed,\n variables: Map<string, unknown>,\n context: AutomationContext,\n grouping?: { parentNodeId: string; iteration?: number; regionKind?: string },\n ): Promise<StepLogEntry[]> {\n const entryId = findRegionEntry(region);\n const entry = region.nodes.find(n => n.id === entryId);\n if (!entry) {\n throw new Error(`region entry node '${entryId}' not found`);\n }\n // A synthetic flow view — executeNode/traverseNext only read `nodes`/`edges`.\n const subFlow = { nodes: region.nodes, edges: region.edges ?? [] } as unknown as FlowParsed;\n const regionSteps: StepLogEntry[] = [];\n try {\n await this.executeNode(entry, subFlow, variables, context, regionSteps);\n } catch (err) {\n if (isSuspendSignal(err)) {\n throw new Error(\n `durable pause inside a structured region (node '${err.nodeId}') is not supported`,\n );\n }\n throw err;\n }\n // Tag this region's steps with their immediate container. Innermost wins:\n // a step that already carries a `parentNodeId` (set by a nested region)\n // is left untouched.\n if (grouping) {\n for (const step of regionSteps) {\n if (step.parentNodeId === undefined) {\n step.parentNodeId = grouping.parentNodeId;\n if (grouping.iteration !== undefined) step.iteration = grouping.iteration;\n if (grouping.regionKind !== undefined) step.regionKind = grouping.regionKind;\n }\n }\n }\n return regionSteps;\n }\n\n /**\n * Execute a promise with timeout using Promise.race.\n */\n private executeWithTimeout(\n promise: Promise<NodeExecutionResult>,\n timeoutMs: number,\n nodeId: string,\n ): Promise<NodeExecutionResult> {\n return Promise.race([\n promise,\n new Promise<NodeExecutionResult>((_, reject) =>\n setTimeout(() => reject(new Error(`Node '${nodeId}' timed out after ${timeoutMs}ms`)), timeoutMs),\n ),\n ]);\n }\n\n /**\n * Safe expression evaluator.\n * Uses simple operator-based parsing without `new Function`.\n * Supports: comparisons (>, <, >=, <=, ==, !=, ===, !==),\n * boolean literals (true, false), and basic arithmetic.\n */\n evaluateCondition(expression: string | { dialect?: string; source?: string; ast?: unknown }, variables: Map<string, unknown>): boolean {\n // M9.5+ wiring: route Expression envelopes through @objectstack/formula\n // ExpressionEngine. CEL is the default; legacy `{var}` template syntax\n // is preserved as a fallback for back-compat.\n const isEnvelope = typeof expression === 'object' && expression != null && 'dialect' in expression;\n const dialect = isEnvelope ? (expression as { dialect?: string }).dialect : undefined;\n const exprStr = typeof expression === 'string' ? expression : ((expression as { source?: string })?.source ?? '');\n\n if (isEnvelope && dialect && dialect !== 'cel' && dialect !== 'flow' && dialect !== 'template') {\n // Other dialects (cron, js) are not boolean predicates here.\n return false;\n }\n\n // CEL path — bind `vars` scope for `{step.result}` style references via\n // the equivalent `vars.step.result` CEL identifier path.\n if (dialect === 'cel' || (isEnvelope && !dialect)) {\n try {\n const vars: Record<string, unknown> = {};\n for (const [key, value] of variables) {\n // Convert \"step.result\" keys into nested object paths.\n const segs = key.split('.');\n let cursor = vars;\n for (let i = 0; i < segs.length - 1; i++) {\n if (typeof cursor[segs[i]] !== 'object' || cursor[segs[i]] === null) {\n cursor[segs[i]] = {};\n }\n cursor = cursor[segs[i]] as Record<string, unknown>;\n }\n cursor[segs[segs.length - 1]] = value;\n }\n // Expose variables two ways under `extra`: as a `vars` namespace\n // (so `vars.step.result` keeps working) AND spread to top level (so\n // bare identifiers like `status` / `previous.status` resolve — the\n // natural authoring style for record-change start conditions).\n const result = ExpressionEngine.evaluate(\n { dialect: 'cel', source: exprStr },\n { extra: { ...vars, vars }, record: vars },\n );\n // ADR-0032 §Decision 1c — NO silent fallback. A non-`ok` result is a\n // real fault (malformed predicate, or — pre build-validation — a\n // `{…}` template mistakenly written into a CEL condition). Surfacing\n // it as a thrown, attributed error makes execute()'s catch record a\n // loud flow failure, instead of the old `return false` that made a\n // broken condition indistinguishable from \"condition not met\" (#1491).\n if (!result.ok) {\n throw new Error(\n `condition failed to evaluate as CEL: ${result.error?.message ?? 'unknown error'} — ` +\n `source: \\`${exprStr}\\`. Conditions are bare CEL (e.g. \\`record.rating >= 4\\`); ` +\n `do not wrap field references in \\`{…}\\` template braces.`,\n );\n }\n return Boolean(result.value);\n } catch (err) {\n // Re-throw with the source attached (ADR-0032 §1d — errors written\n // for self-correction). Never swallow to `false`.\n const msg = (err as Error)?.message ?? String(err);\n throw new Error(\n msg.includes('source:') ? msg : `condition evaluation error: ${msg} — source: \\`${exprStr}\\``,\n );\n }\n }\n\n // Legacy template path: {varName} → value, then primitive compare.\n let resolved = exprStr;\n for (const [key, value] of variables) {\n resolved = resolved.split(`{${key}}`).join(String(value));\n }\n resolved = resolved.trim();\n\n try {\n // Boolean literals\n if (resolved === 'true') return true;\n if (resolved === 'false') return false;\n\n // Comparison operators (ordered by length to match longer operators first)\n const operators = ['===', '!==', '>=', '<=', '!=', '==', '>', '<'] as const;\n for (const op of operators) {\n const idx = resolved.indexOf(op);\n if (idx !== -1) {\n const left = resolved.slice(0, idx).trim();\n const right = resolved.slice(idx + op.length).trim();\n return this.compareValues(left, op, right);\n }\n }\n\n // Numeric truthy check\n const numVal = Number(resolved);\n if (!isNaN(numVal)) return numVal !== 0;\n\n return false;\n } catch {\n return false;\n }\n }\n\n /**\n * Compare two string-represented values with an operator.\n */\n private compareValues(left: string, op: string, right: string): boolean {\n const lNum = Number(left);\n const rNum = Number(right);\n const bothNumeric = !isNaN(lNum) && !isNaN(rNum) && left !== '' && right !== '';\n\n if (bothNumeric) {\n switch (op) {\n case '>': return lNum > rNum;\n case '<': return lNum < rNum;\n case '>=': return lNum >= rNum;\n case '<=': return lNum <= rNum;\n case '==': case '===': return lNum === rNum;\n case '!=': case '!==': return lNum !== rNum;\n default: return false;\n }\n }\n // String comparison\n switch (op) {\n case '==': case '===': return left === right;\n case '!=': case '!==': return left !== right;\n case '>': return left > right;\n case '<': return left < right;\n case '>=': return left >= right;\n case '<=': return left <= right;\n default: return false;\n }\n }\n\n /**\n * Retry execution with exponential backoff, jitter, and recursive protection.\n * Uses an iterative loop with an internal retry flag to prevent recursive call stacking.\n */\n private async retryExecution(\n flowName: string,\n context: AutomationContext | undefined,\n startTime: number,\n errorHandling: {\n maxRetries?: number;\n retryDelayMs?: number;\n backoffMultiplier?: number;\n maxRetryDelayMs?: number;\n jitter?: boolean;\n },\n ): Promise<AutomationResult> {\n const maxRetries = errorHandling.maxRetries ?? 3;\n const baseDelay = errorHandling.retryDelayMs ?? 1000;\n const multiplier = errorHandling.backoffMultiplier ?? 1;\n const maxDelay = errorHandling.maxRetryDelayMs ?? 30000;\n const useJitter = errorHandling.jitter ?? false;\n\n let lastError = 'Max retries exceeded';\n for (let i = 0; i < maxRetries; i++) {\n // Calculate delay with exponential backoff\n let delay = Math.min(baseDelay * Math.pow(multiplier, i), maxDelay);\n if (useJitter) {\n delay = delay * (0.5 + Math.random() * 0.5);\n }\n await new Promise(r => setTimeout(r, delay));\n\n // Execute directly without recursion into retryExecution again\n const result = await this.executeWithoutRetry(flowName, context);\n if (result.success) return result;\n lastError = result.error ?? 'Unknown error';\n }\n return { success: false, error: lastError, durationMs: Date.now() - startTime };\n }\n\n /**\n * Execute a flow without triggering retry logic (used by retryExecution to prevent recursion).\n */\n private async executeWithoutRetry(\n flowName: string,\n context?: AutomationContext,\n ): Promise<AutomationResult> {\n const startTime = Date.now();\n const flow = this.flows.get(flowName);\n\n if (!flow) {\n return { success: false, error: `Flow '${flowName}' not found` };\n }\n if (this.flowEnabled.get(flowName) === false) {\n return { success: false, error: `Flow '${flowName}' is disabled` };\n }\n\n const variables = new Map<string, unknown>();\n if (flow.variables) {\n for (const v of flow.variables) {\n if (v.isInput && context?.params?.[v.name] !== undefined) {\n variables.set(v.name, context.params[v.name]);\n }\n }\n }\n if (context?.record) {\n variables.set('$record', context.record);\n }\n\n const runId = this.nextRunId();\n const startedAt = new Date().toISOString();\n const steps: StepLogEntry[] = [];\n\n try {\n const startNode = flow.nodes.find(n => n.type === 'start');\n if (!startNode) {\n return { success: false, error: 'Flow has no start node' };\n }\n\n await this.executeNode(startNode, flow, variables, context ?? {}, steps);\n\n const output: Record<string, unknown> = {};\n if (flow.variables) {\n for (const v of flow.variables) {\n if (v.isOutput) {\n output[v.name] = variables.get(v.name);\n }\n }\n }\n\n const durationMs = Date.now() - startTime;\n this.recordLog({\n id: runId,\n flowName,\n flowVersion: flow.version,\n status: 'completed',\n startedAt,\n completedAt: new Date().toISOString(),\n durationMs,\n trigger: {\n type: context?.event ?? 'manual',\n userId: context?.userId,\n object: context?.object,\n },\n steps,\n output,\n });\n\n return { success: true, output, durationMs };\n } catch (err: unknown) {\n const errorMessage = err instanceof Error ? err.message : String(err);\n const durationMs = Date.now() - startTime;\n this.recordLog({\n id: runId,\n flowName,\n flowVersion: flow.version,\n status: 'failed',\n startedAt,\n completedAt: new Date().toISOString(),\n durationMs,\n trigger: {\n type: context?.event ?? 'manual',\n userId: context?.userId,\n object: context?.object,\n },\n steps,\n error: errorMessage,\n });\n return { success: false, error: errorMessage, durationMs };\n }\n }\n}\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\nimport type { Logger } from '@objectstack/spec/contracts';\nimport type { SuspendedRun, SuspendedRunStore } from './engine.js';\n\n/**\n * Durable persistence for suspended flow runs (ADR-0019).\n *\n * The engine keeps an in-memory map of paused runs; that map is lost on a\n * process restart (e.g. a hibernating Cloudflare Worker), so a run that paused\n * at an `approval` / `wait` / `screen` node can never be resumed afterwards.\n * A {@link SuspendedRunStore} backs the in-memory map with durable storage so a\n * cold-booted kernel can rehydrate and continue.\n *\n * Two implementations ship here:\n * - {@link InMemorySuspendedRunStore} — a Map (the default behaviour, for\n * tests / dev). It JSON round-trips on save/load so it faithfully exercises\n * the serialization boundary a DB store imposes.\n * - {@link ObjectStoreSuspendedRunStore} — persists to the `sys_automation_run`\n * object via the ObjectQL engine, for production / serverless hosts.\n */\n\nconst TABLE = 'sys_automation_run';\nconst SYSTEM_CTX = { isSystem: true, roles: [], permissions: [] } as const;\n\n/** Deep clone via JSON so a stored snapshot can't alias live engine state. */\nfunction jsonClone<T>(value: T): T {\n return JSON.parse(JSON.stringify(value)) as T;\n}\n\n/** Parse a JSON column that may already be an object (some drivers auto-parse). */\nfunction parseJson<T>(raw: unknown, fallback: T): T {\n if (raw == null || raw === '') return fallback;\n if (typeof raw === 'string') {\n try {\n return JSON.parse(raw) as T;\n } catch {\n return fallback;\n }\n }\n return raw as T;\n}\n\n/**\n * In-memory {@link SuspendedRunStore}. Snapshots are JSON-cloned on the way in\n * and out, matching the serialize/deserialize boundary of a DB-backed store —\n * so a unit test can share one instance across two engine instances to simulate\n * a process restart (suspend on engine A, resume on engine B).\n */\nexport class InMemorySuspendedRunStore implements SuspendedRunStore {\n private readonly runs = new Map<string, SuspendedRun>();\n\n async save(run: SuspendedRun): Promise<void> {\n this.runs.set(run.runId, jsonClone(run));\n }\n\n async load(runId: string): Promise<SuspendedRun | null> {\n const run = this.runs.get(runId);\n return run ? jsonClone(run) : null;\n }\n\n async delete(runId: string): Promise<void> {\n this.runs.delete(runId);\n }\n\n async list(): Promise<SuspendedRun[]> {\n return [...this.runs.values()].map(jsonClone);\n }\n}\n\n/**\n * Minimal ObjectQL engine surface the {@link ObjectStoreSuspendedRunStore} uses.\n * Matches the find/insert/update/delete shape exposed by the `objectql` service\n * (and mirrors `ApprovalEngine` in plugin-approvals).\n */\nexport interface SuspendedRunStoreEngine {\n find(object: string, options?: any): Promise<any[]>;\n insert(object: string, data: any, options?: any): Promise<any>;\n update(object: string, data: any, options?: any): Promise<any>;\n delete?(object: string, options?: any): Promise<any>;\n}\n\ninterface MinimalLogger {\n warn?: Logger['warn'];\n debug?: Logger['debug'];\n}\n\n/**\n * Durable {@link SuspendedRunStore} backed by the `sys_automation_run` object.\n *\n * Persists the resumable run state (`variables` / `steps` / `context` / `screen`)\n * JSON-serialized, so the engine's `Map`-based variable context round-trips. The\n * row is keyed by `runId` and removed on terminal completion; only live pauses\n * are stored. All access uses a system context — these are infrastructure rows,\n * not tenant data subject to RLS (the tenant is captured in `organization_id`\n * for scoping/observability).\n */\nexport class ObjectStoreSuspendedRunStore implements SuspendedRunStore {\n constructor(\n private readonly engine: SuspendedRunStoreEngine,\n private readonly logger?: MinimalLogger,\n ) {}\n\n async save(run: SuspendedRun): Promise<void> {\n const now = new Date().toISOString();\n const row = this.serialize(run);\n // Upsert: a re-suspend (the run paused again at a downstream node) updates\n // the existing row rather than inserting a duplicate.\n const existing = await this.engine.find(TABLE, {\n where: { id: run.runId }, limit: 1, context: SYSTEM_CTX,\n });\n if (Array.isArray(existing) && existing[0]) {\n await this.engine.update(\n TABLE,\n { ...row, updated_at: now },\n { where: { id: run.runId }, context: SYSTEM_CTX },\n );\n } else {\n await this.engine.insert(\n TABLE,\n { ...row, created_at: now, updated_at: now },\n { context: SYSTEM_CTX },\n );\n }\n }\n\n async load(runId: string): Promise<SuspendedRun | null> {\n const rows = await this.engine.find(TABLE, {\n where: { id: runId }, limit: 1, context: SYSTEM_CTX,\n });\n const row = Array.isArray(rows) ? rows[0] : null;\n return row ? this.deserialize(row) : null;\n }\n\n async delete(runId: string): Promise<void> {\n if (typeof this.engine.delete !== 'function') {\n this.logger?.warn?.(\n `[automation] ObjectStoreSuspendedRunStore: engine has no delete(); suspended run '${runId}' row not removed`,\n );\n return;\n }\n await this.engine.delete(TABLE, { where: { id: runId }, context: SYSTEM_CTX });\n }\n\n async list(): Promise<SuspendedRun[]> {\n const rows = await this.engine.find(TABLE, {\n where: { status: 'paused' }, limit: 1000, context: SYSTEM_CTX,\n });\n return (Array.isArray(rows) ? rows : []).map(r => this.deserialize(r));\n }\n\n /** Flatten a run into a `sys_automation_run` row (state columns JSON-encoded). */\n private serialize(run: SuspendedRun): Record<string, unknown> {\n const ctx = (run.context ?? {}) as Record<string, unknown>;\n const org = ctx.organizationId ?? ctx.tenantId ?? null;\n return {\n id: run.runId,\n organization_id: org,\n flow_name: run.flowName,\n flow_version: run.flowVersion ?? null,\n node_id: run.nodeId,\n status: 'paused',\n correlation: run.correlation ?? null,\n user_id: ctx.userId ?? null,\n variables_json: JSON.stringify(run.variables ?? {}),\n steps_json: JSON.stringify(run.steps ?? []),\n context_json: JSON.stringify(run.context ?? {}),\n screen_json: run.screen ? JSON.stringify(run.screen) : null,\n started_at: run.startedAt,\n start_time: run.startTime ?? null,\n };\n }\n\n /** Rebuild a run from a `sys_automation_run` row. */\n private deserialize(row: any): SuspendedRun {\n const startedAt = row.started_at ?? new Date().toISOString();\n return {\n runId: String(row.id),\n flowName: String(row.flow_name ?? ''),\n flowVersion: row.flow_version ?? undefined,\n nodeId: String(row.node_id ?? ''),\n variables: parseJson<Record<string, unknown>>(row.variables_json, {}),\n steps: parseJson<SuspendedRun['steps']>(row.steps_json, []),\n context: parseJson<SuspendedRun['context']>(row.context_json, {}),\n startedAt,\n startTime: typeof row.start_time === 'number' ? row.start_time : (Date.parse(startedAt) || Date.now()),\n correlation: row.correlation ?? undefined,\n screen: parseJson<SuspendedRun['screen']>(row.screen_json, undefined as any),\n };\n }\n}\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\nimport { ObjectSchema, Field } from '@objectstack/spec/data';\n\n/**\n * sys_automation_run — Durable state of a **suspended** automation flow run.\n *\n * ADR-0019: a flow that reaches a long-lived pause node (an `approval` node,\n * `wait`, `screen`, …) suspends. Without persistence the continuation lives\n * only in the engine's in-memory map, so a process restart (e.g. a hibernating\n * Cloudflare Worker) loses the run and `resume(runId)` fails even though the\n * approval record survives. Persisting the run here makes the pause **durable**:\n * the engine writes a row on suspend and deletes it on terminal completion, so a\n * cold-booted kernel can rehydrate and continue.\n *\n * Lifecycle: one row per *currently* suspended run. The row is removed when the\n * run resumes to completion or fails — only live pauses are stored. `id` is the\n * `runId`; `correlation` ties back to the pausing node's external state (e.g.\n * `sys_approval_request.id`, mirrored by `sys_approval_request.flow_run_id`).\n *\n * The resumable state (`variables` / `steps` / `context` / `screen`) is stored\n * JSON-serialized — the engine works with a `Map`, which round-trips through\n * these `*_json` columns.\n *\n * Writers: the automation engine's durable {@link SuspendedRunStore}.\n * Readers: operability surfaces (a \"pending/suspended runs\" view), the engine on\n * resume after a restart.\n *\n * @namespace sys\n */\nexport const SysAutomationRun = ObjectSchema.create({\n name: 'sys_automation_run',\n label: 'Automation Run',\n pluralLabel: 'Automation Runs',\n icon: 'pause-circle',\n isSystem: true,\n managedBy: 'system',\n description: 'Durable state of a suspended automation flow run (ADR-0019)',\n displayNameField: 'id',\n titleFormat: '{flow_name} · {node_id}',\n compactLayout: ['flow_name', 'node_id', 'status', 'correlation', 'started_at', 'updated_at'],\n\n fields: {\n id: Field.text({ label: 'Run ID', required: true, readonly: true, group: 'System' }),\n\n organization_id: Field.lookup('sys_organization', {\n label: 'Organization',\n required: false,\n group: 'System',\n description: 'Tenant that owns this run (propagated from the trigger context)',\n }),\n\n flow_name: Field.text({\n label: 'Flow',\n required: true,\n maxLength: 255,\n searchable: true,\n group: 'Identity',\n }),\n\n flow_version: Field.number({ label: 'Flow Version', required: false, group: 'Identity' }),\n\n node_id: Field.text({\n label: 'Paused Node',\n required: true,\n maxLength: 255,\n description: 'Node the run is suspended at; resume continues from its out-edges.',\n group: 'State',\n }),\n\n status: Field.select(\n ['paused'],\n {\n label: 'Status',\n required: true,\n defaultValue: 'paused',\n description: 'Only suspended runs are persisted; the row is deleted on terminal completion.',\n group: 'State',\n },\n ),\n\n correlation: Field.text({\n label: 'Correlation',\n required: false,\n maxLength: 255,\n description: 'Correlation key from the pausing node (e.g. approval request id).',\n group: 'State',\n }),\n\n user_id: Field.text({\n label: 'User',\n required: false,\n maxLength: 255,\n description: 'User who triggered the run (from context.userId).',\n group: 'State',\n }),\n\n variables_json: Field.textarea({\n label: 'Variables',\n required: false,\n description: 'JSON snapshot of the flow variable map at suspend time.',\n group: 'State',\n }),\n\n steps_json: Field.textarea({\n label: 'Steps',\n required: false,\n description: 'JSON snapshot of the executed step logs so far.',\n group: 'State',\n }),\n\n context_json: Field.textarea({\n label: 'Context',\n required: false,\n description: 'JSON snapshot of the trigger / automation context.',\n group: 'State',\n }),\n\n screen_json: Field.textarea({\n label: 'Screen',\n required: false,\n description: 'JSON snapshot of the screen spec the run is waiting on (screen-flow runtime).',\n group: 'State',\n }),\n\n started_at: Field.datetime({ label: 'Started At', required: true, group: 'State' }),\n\n start_time: Field.number({\n label: 'Start Time (epoch ms)',\n required: false,\n description: 'Epoch ms when the run started; used to compute duration on resume.',\n group: 'State',\n }),\n\n created_at: Field.datetime({\n label: 'Created At',\n required: true,\n defaultValue: 'NOW()',\n readonly: true,\n group: 'System',\n }),\n\n updated_at: Field.datetime({ label: 'Updated At', required: false, group: 'System' }),\n },\n\n indexes: [\n // \"Which runs are suspended for this flow?\" — operability / resume sweeps.\n { fields: ['flow_name', 'status'] },\n { fields: ['status', 'updated_at'] },\n // Look up a suspended run by the pausing node's correlation key.\n { fields: ['correlation'] },\n ],\n});\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\nimport type { PluginContext } from '@objectstack/core';\nimport { defineActionDescriptor } from '@objectstack/spec/automation';\nimport type { AutomationEngine } from '../engine.js';\n\n/**\n * Logic built-in nodes — decision / assignment.\n *\n * (The `loop` container is registered separately — see `loop-node.ts` — as a\n * structured iteration construct per ADR-0031.)\n *\n * Part of the automation engine's foundational vocabulary, so the core\n * {@link AutomationServicePlugin} seeds them directly (ADR-0018). These are NOT\n * shipped as a separately installable plugin — \"plugins are plugins; the\n * platform's foundational capabilities are built in.\" Third-party node types\n * are still registered via `engine.registerNodeExecutor()`.\n */\nexport function registerLogicNodes(engine: AutomationEngine, ctx: PluginContext): void {\n // decision node — conditional branching\n engine.registerNodeExecutor({\n type: 'decision',\n descriptor: defineActionDescriptor({\n type: 'decision', version: '1.0.0', name: 'Decision',\n description: 'Branch execution based on conditions.',\n icon: 'git-branch', category: 'logic', source: 'builtin',\n }),\n async execute(node, variables, _context) {\n const config = node.config as Record<string, unknown> | undefined;\n const conditions = (config?.conditions ?? []) as Array<{ label: string; expression: string }>;\n\n for (const cond of conditions) {\n if (engine.evaluateCondition(cond.expression, variables)) {\n return { success: true, branchLabel: cond.label };\n }\n }\n return { success: true, branchLabel: 'default' };\n },\n });\n\n // assignment node — set variables\n engine.registerNodeExecutor({\n type: 'assignment',\n descriptor: defineActionDescriptor({\n type: 'assignment', version: '1.0.0', name: 'Assignment',\n description: 'Set flow variables.',\n icon: 'variable', category: 'logic', source: 'builtin',\n }),\n async execute(node, variables, _context) {\n const config = (node.config ?? {}) as Record<string, unknown>;\n for (const [key, value] of Object.entries(config)) {\n variables.set(key, value);\n }\n return { success: true };\n },\n });\n\n ctx.logger.info('[Logic Nodes] 2 built-in node executors registered');\n}\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\nimport type { PluginContext } from '@objectstack/core';\nimport { defineActionDescriptor, LOOP_MAX_ITERATIONS_CEILING } from '@objectstack/spec/automation';\nimport type { FlowRegionParsed } from '@objectstack/spec/automation';\nimport type { AutomationContext } from '@objectstack/spec/contracts';\nimport type { AutomationEngine, StepLogEntry } from '../engine.js';\nimport { interpolate } from './template.js';\n\n/**\n * `loop` built-in node — a **structured iteration container** (ADR-0031).\n *\n * Replaces the previous no-op `loop` stub. The node owns a bounded **body\n * region** (`config.body`, a single-entry/single-exit sub-graph) and drives\n * iteration over a collection: for each item it binds the current value to\n * `config.iteratorVariable` (and the zero-based index to `config.indexVariable`,\n * when given) in the **enclosing variable scope** and runs the body region. A\n * **hard max-iteration guard** (`config.maxIterations`, clamped to\n * {@link LOOP_MAX_ITERATIONS_CEILING}) keeps termination analyzable.\n *\n * The body region runs as a unit via {@link AutomationEngine.runRegion}; the\n * loop node's *ordinary* out-edges in the main graph remain the \"after-loop\"\n * continuation, so the DAG invariant for ordinary edges is preserved.\n *\n * **Back-compat:** a `loop` node with no `config.body` keeps the legacy\n * flat-graph behavior (sets `$loopItems`/`$loopIndex` and falls through) — the\n * container construct is additive.\n */\nexport function registerLoopNode(engine: AutomationEngine, ctx: PluginContext): void {\n engine.registerNodeExecutor({\n type: 'loop',\n descriptor: defineActionDescriptor({\n type: 'loop',\n version: '2.0.0',\n name: 'Loop',\n description: 'Iterate a body region over a collection (bounded, structured container).',\n icon: 'repeat',\n category: 'logic',\n source: 'builtin',\n configSchema: {\n type: 'object',\n properties: {\n collection: { type: 'string', description: 'Template/variable resolving to the array to iterate' },\n iteratorVariable: { type: 'string', description: 'Loop variable holding the current item' },\n indexVariable: { type: 'string', description: 'Optional loop variable holding the current index' },\n maxIterations: { type: 'integer', minimum: 1, maximum: LOOP_MAX_ITERATIONS_CEILING },\n body: {\n type: 'object',\n description: 'Loop body region (single-entry/single-exit sub-graph)',\n properties: { nodes: { type: 'array' }, edges: { type: 'array' } },\n },\n },\n required: ['collection'],\n },\n }),\n async execute(node, variables, context) {\n const cfg = (node.config ?? {}) as Record<string, unknown>;\n const body = cfg.body as FlowRegionParsed | undefined;\n\n // ── Legacy flat-graph loop (no body) — preserve prior stub behavior. ──\n if (body == null) {\n const collectionName = typeof cfg.collection === 'string' ? cfg.collection : undefined;\n if (collectionName) {\n const legacy = variables.get(collectionName);\n if (Array.isArray(legacy)) {\n variables.set('$loopItems', legacy);\n variables.set('$loopIndex', 0);\n }\n }\n return { success: true };\n }\n\n // ── Structured loop container. ──\n const iteratorVariable = typeof cfg.iteratorVariable === 'string' && cfg.iteratorVariable\n ? cfg.iteratorVariable\n : 'item';\n const indexVariable = typeof cfg.indexVariable === 'string' && cfg.indexVariable\n ? cfg.indexVariable\n : undefined;\n\n // Resolve the collection: a `{token}` template, a bare variable name, or\n // (defensively) an already-resolved array.\n const rawCollection = cfg.collection;\n let collection: unknown;\n if (Array.isArray(rawCollection)) {\n collection = rawCollection;\n } else if (typeof rawCollection === 'string') {\n collection = interpolate(rawCollection, variables, context ?? ({} as AutomationContext));\n if ((collection == null) && variables.has(rawCollection)) {\n collection = variables.get(rawCollection);\n }\n }\n\n if (!Array.isArray(collection)) {\n return {\n success: false,\n error: `loop '${node.id}': collection '${String(rawCollection)}' did not resolve to an array`,\n };\n }\n\n // Hard iteration guard.\n const requested = typeof cfg.maxIterations === 'number' ? cfg.maxIterations : LOOP_MAX_ITERATIONS_CEILING;\n const maxIterations = Math.min(requested, LOOP_MAX_ITERATIONS_CEILING);\n if (collection.length > maxIterations) {\n return {\n success: false,\n error:\n `loop '${node.id}': collection length ${collection.length} exceeds maxIterations ${maxIterations}`,\n };\n }\n\n let iterations = 0;\n const childSteps: StepLogEntry[] = [];\n for (let i = 0; i < collection.length; i++) {\n variables.set(iteratorVariable, collection[i]);\n if (indexVariable) variables.set(indexVariable, i);\n // Body runs in the shared scope; iterator var + mutations are visible.\n // #1479: collect each iteration's body steps, tagged with the index.\n const iterSteps = await engine.runRegion(body, variables, context ?? ({} as AutomationContext), {\n parentNodeId: node.id,\n iteration: i,\n regionKind: 'loop-body',\n });\n childSteps.push(...iterSteps);\n iterations++;\n }\n\n return { success: true, output: { iterations }, childSteps };\n },\n });\n\n ctx.logger.info('[Loop Node] 1 built-in node executor registered');\n}\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\n/**\n * Template interpolation helpers shared across node executors.\n *\n * Supported syntax (intentionally minimal — no full expression language):\n *\n * {variable} → variables.get('variable')\n * {variable.path.segment} → walks dotted path on the resolved value\n * {list.0} / {rec.items.2} → numeric segments index into arrays\n * {$User.Id} → reads from context.userId\n * {$User.Email} → reads from context.user?.email\n * {NOW()} → ISO timestamp at evaluation time\n * {TODAY()} → YYYY-MM-DD at evaluation time\n * {TODAY() + 90} → date + N days (days only, integer)\n *\n * Anything that fails to resolve becomes the literal `null` value (for\n * single-token templates) or the empty string (for embedded substitution),\n * matching the behavior of common low-code formula engines.\n *\n * The interpolator walks objects, arrays, and primitives recursively so it\n * can be applied wholesale to a node's `config.fields`/`config.filter` blocks.\n */\n\nimport type { AutomationContext } from '@objectstack/spec/contracts';\n\nexport type VariableMap = Map<string, unknown>;\n\n/**\n * Resolve a dotted path against a base value.\n * Returns `undefined` for any missing intermediate node.\n */\nfunction resolvePath(base: unknown, path: string[]): unknown {\n let cur: unknown = base;\n for (const seg of path) {\n if (cur == null) return undefined;\n if (typeof cur !== 'object') return undefined;\n cur = (cur as Record<string, unknown>)[seg];\n }\n return cur;\n}\n\n/**\n * Resolve a single template token (without braces) to a value.\n * Returns `undefined` if the token cannot be resolved.\n */\nfunction resolveToken(token: string, variables: VariableMap, context: AutomationContext): unknown {\n const trimmed = token.trim();\n if (!trimmed) return undefined;\n\n // Built-in date helpers — `NOW()` / `TODAY()` with optional `+ N` day offset.\n // The offset may be a literal integer or any token resolvable from `variables`.\n const dateFnMatch = /^(NOW|TODAY)\\s*\\(\\s*\\)\\s*(?:([+\\-])\\s*(\\S+))?$/.exec(trimmed);\n if (dateFnMatch) {\n const fn = dateFnMatch[1];\n const sign = dateFnMatch[2] === '-' ? -1 : 1;\n const offsetRaw = dateFnMatch[3];\n let offset = 0;\n if (offsetRaw) {\n const asNum = Number(offsetRaw);\n if (!isNaN(asNum)) {\n offset = asNum;\n } else if (variables.has(offsetRaw)) {\n offset = Number(variables.get(offsetRaw)) || 0;\n }\n }\n const now = new Date();\n if (offset) now.setDate(now.getDate() + sign * offset);\n if (fn === 'NOW') return now.toISOString();\n return now.toISOString().slice(0, 10);\n }\n\n // $User.* shortcuts\n if (trimmed.startsWith('$User.')) {\n const path = trimmed.slice('$User.'.length).split('.');\n if (path[0] === 'Id') return context.userId;\n if (path[0] === 'Email') return resolvePath((context as any).user, ['email', ...path.slice(1)]) ?? undefined;\n return resolvePath((context as any).user, path);\n }\n\n // Direct variable / dotted path lookup (fast path, no arithmetic).\n // Path segments after the head may be identifiers OR array indices (`\\d+`),\n // so `{list.0}` / `{record.target_channels.0}` resolve into arrays (#1872).\n if (/^[A-Za-z_$][\\w$]*(?:\\.(?:[A-Za-z_$][\\w$]*|\\d+))*$/.test(trimmed)) {\n const segments = trimmed.split('.');\n const head = segments[0];\n if (variables.has(head)) {\n return resolvePath(variables.get(head), segments.slice(1));\n }\n if (variables.has(trimmed)) return variables.get(trimmed);\n return undefined;\n }\n\n // Arithmetic / mixed expression: substitute variable references (foo, foo.bar)\n // with their numeric/string literal forms, then evaluate via Function().\n // Restricted to a safe character set (digits, basic operators, parentheses,\n // dots and identifier characters) — never executed on raw user input.\n if (!/^[\\w\\s+\\-*/%().,?:<>=!&|\"'$]+$/.test(trimmed)) return undefined;\n let safe = trimmed;\n safe = safe.replace(/([A-Za-z_$][\\w$]*(?:\\.[A-Za-z_$][\\w$]*)*)/g, (match) => {\n // Don't substitute reserved literals\n if (match === 'true' || match === 'false' || match === 'null' || match === 'undefined') return match;\n const segs = match.split('.');\n const head = segs[0];\n let val: unknown;\n if (variables.has(head)) val = resolvePath(variables.get(head), segs.slice(1));\n else if (variables.has(match)) val = variables.get(match);\n if (val === undefined || val === null) return 'null';\n if (typeof val === 'number' || typeof val === 'boolean') return String(val);\n return JSON.stringify(String(val));\n });\n try {\n // eslint-disable-next-line @typescript-eslint/no-implied-eval, no-new-func\n const fn = new Function(`\"use strict\"; return (${safe});`);\n return fn();\n } catch {\n return undefined;\n }\n}\n\n/**\n * Replace `{...}` tokens in a string with resolved values.\n * - When the entire string is a single token, returns the raw value (preserving type).\n * - Otherwise concatenates string substitutions, with `null`/`undefined` rendered as ''.\n */\nexport function interpolateString(\n input: string,\n variables: VariableMap,\n context: AutomationContext,\n): unknown {\n if (!input.includes('{')) return input;\n const single = /^\\{([^{}]+)\\}$/.exec(input);\n if (single) {\n const value = resolveToken(single[1], variables, context);\n return value;\n }\n return input.replace(/\\{([^{}]+)\\}/g, (_match, expr) => {\n const value = resolveToken(expr, variables, context);\n if (value === undefined || value === null) return '';\n return String(value);\n });\n}\n\n/**\n * Recursively interpolate template tokens in arbitrary JSON-like values.\n */\nexport function interpolate<T = unknown>(\n value: T,\n variables: VariableMap,\n context: AutomationContext,\n): T {\n if (typeof value === 'string') {\n return interpolateString(value, variables, context) as unknown as T;\n }\n if (Array.isArray(value)) {\n return value.map(v => interpolate(v, variables, context)) as unknown as T;\n }\n if (value && typeof value === 'object') {\n const out: Record<string, unknown> = {};\n for (const [k, v] of Object.entries(value as Record<string, unknown>)) {\n out[k] = interpolate(v, variables, context);\n }\n return out as unknown as T;\n }\n return value;\n}\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\nimport type { PluginContext } from '@objectstack/core';\nimport { defineActionDescriptor } from '@objectstack/spec/automation';\nimport type { FlowRegionParsed } from '@objectstack/spec/automation';\nimport type { AutomationContext } from '@objectstack/spec/contracts';\nimport type { AutomationEngine, StepLogEntry } from '../engine.js';\n\n/** One branch of a parallel block — a region plus an optional label. */\ninterface ParallelBranch extends FlowRegionParsed {\n name?: string;\n}\n\n/**\n * `parallel` built-in node — a **structured parallel block** with an\n * **implicit join** (ADR-0031 §Decision 2).\n *\n * The node declares N branch regions in `config.branches[]`; each branch is a\n * self-contained single-entry/single-exit sub-graph (validated at\n * `registerFlow()`). The executor runs every branch concurrently\n * (`Promise.all`) in the **enclosing variable scope** and continues **once when\n * all branches complete** — the join is implicit at block end, engine\n * synchronized. There is no author-visible split/join gateway to mis-wire or\n * deadlock; the node's ordinary out-edges remain the after-block continuation.\n *\n * Concurrency model: JavaScript is single-threaded, so branches interleave only\n * at `await` points and the shared `variables` map is never torn. Branches\n * SHOULD write distinct variables; on a key collision the last writer to settle\n * wins (same semantics as the engine's existing unconditional-edge fan-out).\n *\n * If any branch fails (a node returns `success: false` or throws), the block\n * fails — surfaced as a node failure so the flow's fault edge / error handling\n * applies. Durable pause inside a branch is unsupported (a clear error), mirror-\n * ing the loop container.\n */\nexport function registerParallelNode(engine: AutomationEngine, ctx: PluginContext): void {\n engine.registerNodeExecutor({\n type: 'parallel',\n descriptor: defineActionDescriptor({\n type: 'parallel',\n version: '1.0.0',\n name: 'Parallel',\n description: 'Run N branch regions concurrently and join implicitly when all complete.',\n icon: 'git-fork',\n category: 'logic',\n source: 'builtin',\n configSchema: {\n type: 'object',\n properties: {\n branches: {\n type: 'array',\n minItems: 2,\n description: 'Branch regions executed concurrently; implicit join at block end',\n items: {\n type: 'object',\n properties: {\n name: { type: 'string' },\n nodes: { type: 'array' },\n edges: { type: 'array' },\n },\n },\n },\n },\n required: ['branches'],\n },\n }),\n async execute(node, variables, context) {\n const cfg = (node.config ?? {}) as Record<string, unknown>;\n const branches = cfg.branches as ParallelBranch[] | undefined;\n\n if (!Array.isArray(branches) || branches.length < 2) {\n return {\n success: false,\n error: `parallel '${node.id}': config.branches must declare at least 2 branch regions`,\n };\n }\n\n let branchSteps: StepLogEntry[][];\n try {\n // Implicit join: continue once when ALL branches have completed.\n // #1479: each branch returns its body steps, tagged with the branch index.\n branchSteps = await Promise.all(\n branches.map((branch, i) =>\n engine.runRegion(branch, variables, context ?? ({} as AutomationContext), {\n parentNodeId: node.id,\n iteration: i,\n regionKind: 'parallel-branch',\n }),\n ),\n );\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n return { success: false, error: `parallel '${node.id}': branch failed — ${message}` };\n }\n\n return { success: true, output: { branches: branches.length }, childSteps: branchSteps.flat() };\n },\n });\n\n ctx.logger.info('[Parallel Node] 1 built-in node executor registered');\n}\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\nimport type { PluginContext } from '@objectstack/core';\nimport { defineActionDescriptor } from '@objectstack/spec/automation';\nimport type { FlowRegionParsed } from '@objectstack/spec/automation';\nimport type { AutomationContext } from '@objectstack/spec/contracts';\nimport type { AutomationEngine } from '../engine.js';\n\ninterface RetryPolicy {\n maxRetries?: number;\n retryDelayMs?: number;\n backoffMultiplier?: number;\n maxRetryDelayMs?: number;\n jitter?: boolean;\n}\n\n/**\n * `try_catch` built-in node — **structured try/catch/retry** (ADR-0031 §Decision 3).\n *\n * Runs the protected `try` region; if it throws (a node fails), an optional\n * `retry` policy re-runs the `try` region with exponential backoff. If the\n * region still fails after retries, the optional `catch` region runs with the\n * caught error bound to `errorVariable` (default `$error`). Both regions are\n * self-contained single-entry/single-exit sub-graphs validated at\n * `registerFlow()`, executed in the **enclosing variable scope** via\n * {@link AutomationEngine.runRegion}.\n *\n * Outcome:\n * - `try` (or a retry) succeeds → the node succeeds, downstream continues.\n * - `try` exhausts retries, a `catch` is present and succeeds → the node\n * succeeds (the error was handled).\n * - `try` exhausts retries and there is **no** `catch` (or `catch` itself\n * fails) → the node fails, surfacing to the flow's fault edge / error handling.\n *\n * This is the low-code-native error model — the same `fault` + exponential-\n * backoff retry the engine already implements, surfaced as a construct rather\n * than BPMN boundary events.\n */\nexport function registerTryCatchNode(engine: AutomationEngine, ctx: PluginContext): void {\n engine.registerNodeExecutor({\n type: 'try_catch',\n descriptor: defineActionDescriptor({\n type: 'try_catch',\n version: '1.0.0',\n name: 'Try / Catch',\n description: 'Run a protected region with optional retry and a catch handler (structured error handling).',\n icon: 'shield-alert',\n category: 'logic',\n source: 'builtin',\n supportsRetry: true,\n configSchema: {\n type: 'object',\n properties: {\n try: {\n type: 'object',\n description: 'Protected region (single-entry/single-exit sub-graph)',\n properties: { nodes: { type: 'array' }, edges: { type: 'array' } },\n },\n catch: {\n type: 'object',\n description: 'Handler region run when the try region fails',\n properties: { nodes: { type: 'array' }, edges: { type: 'array' } },\n },\n errorVariable: { type: 'string', description: 'Variable holding the caught error in the catch region' },\n retry: {\n type: 'object',\n properties: {\n maxRetries: { type: 'integer', minimum: 0, maximum: 10 },\n retryDelayMs: { type: 'integer', minimum: 0 },\n backoffMultiplier: { type: 'number', minimum: 1 },\n maxRetryDelayMs: { type: 'integer', minimum: 0 },\n jitter: { type: 'boolean' },\n },\n },\n },\n required: ['try'],\n },\n }),\n async execute(node, variables, context) {\n const cfg = (node.config ?? {}) as Record<string, unknown>;\n const tryRegion = cfg.try as FlowRegionParsed | undefined;\n const catchRegion = cfg.catch as FlowRegionParsed | undefined;\n const errorVariable =\n typeof cfg.errorVariable === 'string' && cfg.errorVariable ? cfg.errorVariable : '$error';\n const retry = (cfg.retry ?? {}) as RetryPolicy;\n\n if (tryRegion == null) {\n return { success: false, error: `try_catch '${node.id}': config.try region is required` };\n }\n\n const ctxOrEmpty = context ?? ({} as AutomationContext);\n const maxRetries = typeof retry.maxRetries === 'number' ? retry.maxRetries : 0;\n const baseDelay = typeof retry.retryDelayMs === 'number' ? retry.retryDelayMs : 0;\n const multiplier = typeof retry.backoffMultiplier === 'number' ? retry.backoffMultiplier : 1;\n const maxDelay = typeof retry.maxRetryDelayMs === 'number' ? retry.maxRetryDelayMs : 30000;\n const useJitter = retry.jitter === true;\n\n // Run the try region, retrying with exponential backoff up to maxRetries.\n let lastError = 'unknown error';\n for (let attempt = 0; attempt <= maxRetries; attempt++) {\n if (attempt > 0) {\n let delay = Math.min(baseDelay * Math.pow(multiplier, attempt - 1), maxDelay);\n if (useJitter) delay = delay * (0.5 + Math.random() * 0.5);\n if (delay > 0) await new Promise(r => setTimeout(r, delay));\n }\n try {\n // #1479: surface the successful try region's steps.\n const trySteps = await engine.runRegion(tryRegion, variables, ctxOrEmpty, {\n parentNodeId: node.id,\n regionKind: 'try',\n });\n return { success: true, output: { attempts: attempt + 1, caught: false }, childSteps: trySteps };\n } catch (err) {\n lastError = err instanceof Error ? err.message : String(err);\n }\n }\n\n // The try region (and any retries) failed. Run the catch handler if present.\n if (catchRegion != null) {\n variables.set(errorVariable, { nodeId: node.id, message: lastError });\n try {\n // #1479: surface the catch handler region's steps.\n const catchSteps = await engine.runRegion(catchRegion, variables, ctxOrEmpty, {\n parentNodeId: node.id,\n regionKind: 'catch',\n });\n return {\n success: true,\n output: { attempts: maxRetries + 1, caught: true, error: lastError },\n childSteps: catchSteps,\n };\n } catch (catchErr) {\n const catchMsg = catchErr instanceof Error ? catchErr.message : String(catchErr);\n return { success: false, error: `try_catch '${node.id}': catch region failed — ${catchMsg}` };\n }\n }\n\n // No catch handler — surface the failure to the flow's fault edge / error handling.\n return { success: false, error: `try_catch '${node.id}': try region failed — ${lastError}` };\n },\n });\n\n ctx.logger.info('[TryCatch Node] 1 built-in node executor registered');\n}\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\nimport type { PluginContext } from '@objectstack/core';\nimport { defineActionDescriptor } from '@objectstack/spec/automation';\nimport type { IDataEngine } from '@objectstack/spec/contracts';\nimport type { AutomationEngine } from '../engine.js';\nimport { interpolate } from './template.js';\n\n/**\n * CRUD built-in nodes — `get_record` / `create_record` / `update_record` /\n * `delete_record`, wired to the runtime data layer (ObjectQL / IDataEngine).\n * Part of the platform baseline, so the core {@link AutomationServicePlugin}\n * seeds them directly (ADR-0018) rather than shipping a separate plugin.\n *\n * Each executor:\n * 1. Interpolates `{var}` / `{var.path}` / `{$User.*}` / `{NOW()}` tokens in\n * `node.config` against the running flow's variable context.\n * 2. Calls the resolved data engine via `ctx.getService('data')`.\n * 3. Writes the result back to the variable context under `outputVariable`\n * (or under `<nodeId>.id` / `<nodeId>.records` by default), so downstream\n * nodes can reference fields like `{leadRecord.company}`.\n *\n * If no data engine is registered, executors degrade to a no-op success so\n * test environments without ObjectQL still complete the flow without errors.\n */\nexport function registerCrudNodes(engine: AutomationEngine, ctx: PluginContext): void {\n const getData = (): IDataEngine | undefined => {\n try {\n return ctx.getService<IDataEngine>('data') ?? ctx.getService<IDataEngine>('objectql');\n } catch {\n return undefined;\n }\n };\n\n // ── get_record ────────────────────────────────────────\n engine.registerNodeExecutor({\n type: 'get_record',\n descriptor: defineActionDescriptor({\n type: 'get_record', version: '1.0.0', name: 'Get Records',\n description: 'Query records from an object.',\n icon: 'search', category: 'data', source: 'builtin',\n }),\n async execute(node, variables, context) {\n const cfg = (node.config ?? {}) as Record<string, unknown>;\n const objectName = String(cfg.objectName ?? cfg.object ?? '');\n if (!objectName) return { success: false, error: 'get_record: objectName required' };\n\n const filter = interpolate(cfg.filter ?? cfg.filters ?? {}, variables, context) as Record<string, unknown>;\n const fields = cfg.fields as string[] | undefined;\n const limit = typeof cfg.limit === 'number' ? cfg.limit : undefined;\n const outputVariable = cfg.outputVariable as string | undefined;\n\n const data = getData();\n if (!data) {\n ctx.logger.warn(`[get_record] no data engine; skipping ${objectName}`);\n return { success: true, output: { records: [], object: objectName } };\n }\n\n try {\n if (limit && limit > 1) {\n const records = await data.find(objectName, { where: filter, fields, limit });\n if (outputVariable) variables.set(outputVariable, records);\n return { success: true, output: { records, object: objectName } };\n }\n const record = await data.findOne(objectName, { where: filter, fields });\n if (outputVariable) variables.set(outputVariable, record);\n return { success: true, output: { record, id: record?.id, object: objectName } };\n } catch (err) {\n return { success: false, error: `get_record(${objectName}) failed: ${(err as Error).message}` };\n }\n },\n });\n\n // ── create_record ─────────────────────────────────────\n engine.registerNodeExecutor({\n type: 'create_record',\n descriptor: defineActionDescriptor({\n type: 'create_record', version: '1.0.0', name: 'Create Record',\n description: 'Insert a new record into an object.',\n icon: 'plus-circle', category: 'data', source: 'builtin',\n }),\n async execute(node, variables, context) {\n const cfg = (node.config ?? {}) as Record<string, unknown>;\n const objectName = String(cfg.objectName ?? cfg.object ?? '');\n if (!objectName) return { success: false, error: 'create_record: objectName required' };\n\n const fields = interpolate(cfg.fields ?? {}, variables, context) as Record<string, unknown>;\n const outputVariable = cfg.outputVariable as string | undefined;\n\n const data = getData();\n if (!data) {\n ctx.logger.warn(`[create_record] no data engine; skipping ${objectName}`);\n const mockId = `mock-${objectName}-${Date.now()}`;\n if (outputVariable) variables.set(outputVariable, { id: mockId });\n return { success: true, output: { id: mockId, object: objectName } };\n }\n\n try {\n const created = await data.insert(objectName, fields);\n const createdRecord = Array.isArray(created) ? created[0] : created;\n const insertedId =\n createdRecord && typeof createdRecord === 'object'\n ? (createdRecord as Record<string, unknown>).id\n : createdRecord;\n if (outputVariable) {\n // #1873 — expose the created RECORD so later nodes can reference\n // `{var.id}` (and other fields), not just the bare id string. When the\n // driver returns a bare id, wrap it as `{ id }` so `{var.id}` still works.\n variables.set(\n outputVariable,\n createdRecord && typeof createdRecord === 'object' ? createdRecord : { id: insertedId },\n );\n }\n return { success: true, output: { id: insertedId, record: createdRecord, object: objectName } };\n } catch (err) {\n return { success: false, error: `create_record(${objectName}) failed: ${(err as Error).message}` };\n }\n },\n });\n\n // ── update_record ─────────────────────────────────────\n engine.registerNodeExecutor({\n type: 'update_record',\n descriptor: defineActionDescriptor({\n type: 'update_record', version: '1.0.0', name: 'Update Records',\n description: 'Update records matching a filter.',\n icon: 'edit', category: 'data', source: 'builtin',\n }),\n async execute(node, variables, context) {\n const cfg = (node.config ?? {}) as Record<string, unknown>;\n const objectName = String(cfg.objectName ?? cfg.object ?? '');\n if (!objectName) return { success: false, error: 'update_record: objectName required' };\n\n const filter = interpolate(cfg.filter ?? cfg.filters ?? {}, variables, context) as Record<string, unknown>;\n const fields = interpolate(cfg.fields ?? {}, variables, context) as Record<string, unknown>;\n\n const data = getData();\n if (!data) {\n ctx.logger.warn(`[update_record] no data engine; skipping ${objectName}`);\n return { success: true };\n }\n\n try {\n const result = await data.update(objectName, fields, { where: filter });\n return { success: true, output: { result, object: objectName } };\n } catch (err) {\n return { success: false, error: `update_record(${objectName}) failed: ${(err as Error).message}` };\n }\n },\n });\n\n // ── delete_record ─────────────────────────────────────\n engine.registerNodeExecutor({\n type: 'delete_record',\n descriptor: defineActionDescriptor({\n type: 'delete_record', version: '1.0.0', name: 'Delete Records',\n description: 'Delete records matching a filter.',\n icon: 'trash', category: 'data', source: 'builtin',\n }),\n async execute(node, variables, context) {\n const cfg = (node.config ?? {}) as Record<string, unknown>;\n const objectName = String(cfg.objectName ?? cfg.object ?? '');\n if (!objectName) return { success: false, error: 'delete_record: objectName required' };\n\n const filter = interpolate(cfg.filter ?? cfg.filters ?? {}, variables, context) as Record<string, unknown>;\n\n const data = getData();\n if (!data) return { success: true };\n\n try {\n const result = await data.delete(objectName, { where: filter });\n return { success: true, output: { result, object: objectName } };\n } catch (err) {\n return { success: false, error: `delete_record(${objectName}) failed: ${(err as Error).message}` };\n }\n },\n });\n\n ctx.logger.info('[CRUD Nodes] 4 built-in node executors registered (data-backed)');\n}\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\nimport type { PluginContext } from '@objectstack/core';\nimport { defineActionDescriptor } from '@objectstack/spec/automation';\nimport type { AutomationEngine } from '../engine.js';\nimport { interpolate } from './template.js';\n\n/**\n * Screen / Script built-in nodes — 'screen' and 'script' executors.\n * Part of the core flow capability, so the {@link AutomationServicePlugin}\n * seeds them directly (ADR-0018) rather than shipping a separate plugin.\n *\n * - 'screen' nodes collect user input. A screen that declares `config.fields`\n * (or sets `config.waitForInput === true`) suspends the run on entry via the\n * engine's durable pause (ADR-0019), surfacing a `ScreenSpec` for the client\n * to render; the run continues via `resume()` with the collected values (set\n * as bare flow variables). A field-less screen — or one with\n * `waitForInput === false` — stays a server pass-through (input vars, if any,\n * are already injected from `context.params`).\n * - 'script' nodes name a callable to run (#1870):\n * - `config.actionType` selecting a built-in side-effect ('email', 'slack',\n * logger-backed), or\n * - `config.function` (or a bare `actionType` that matches no built-in)\n * naming a registered function — resolved via `engine.resolveFunction()`,\n * which the host bridges to `bundle.functions` / `defineStack({ functions })`.\n * A target that resolves to neither fails the step LOUDLY rather than the old\n * silent \"no-op handler\" success, so an unwired callable can't quietly skip.\n */\n\n/**\n * Built-in `script` side-effect action types with a (logger-backed) handler.\n * Anything else is treated as a registered-function name (#1870).\n */\nconst SCRIPT_BUILTIN_ACTION_TYPES = new Set(['email', 'slack']);\n\nexport function registerScreenNodes(engine: AutomationEngine, ctx: PluginContext): void {\n // screen — server-side pass-through (input vars already injected by engine).\n engine.registerNodeExecutor({\n type: 'screen',\n descriptor: defineActionDescriptor({\n type: 'screen', version: '1.0.0', name: 'Screen',\n description: 'Collect user input via a screen (human-input element).',\n icon: 'window', category: 'human', source: 'builtin',\n // Human-input nodes suspend the flow awaiting input.\n supportsPause: true, isAsync: true,\n }),\n async execute(node, variables, context) {\n const cfg = (node.config ?? {}) as Record<string, unknown>;\n // `{var}` tokens in screen config resolve against the live flow\n // variables here (the engine does NOT pre-interpolate node config) — so\n // a step's title/description/field-default/object-form-default can pull\n // from prior nodes (e.g. `{lead_record.company}`, `{account_id}`).\n const interp = (v: unknown): string | undefined => {\n if (v == null) return undefined;\n const r = interpolate(v, variables, context);\n return r == null ? undefined : String(r);\n };\n\n // ── Object-form screen (master-detail wizards) ──────────────────────\n // When the step names an `objectName`, render that object's FULL\n // create/edit form — including any inline master-detail child grids —\n // instead of a flat field list. The client persists the record (and its\n // children, atomically) and resumes the run with the new id bound to\n // `idVariable`, so a later step can reference it (e.g. an Opportunity\n // step prefilling its `account` FK from the Customer step's new id).\n const objectName =\n typeof cfg.objectName === 'string' && cfg.objectName.trim() ? cfg.objectName.trim() : undefined;\n if (objectName) {\n const defaults =\n cfg.defaults && typeof cfg.defaults === 'object'\n ? (interpolate(cfg.defaults, variables, context) as Record<string, unknown>)\n : undefined;\n const idVariable =\n typeof cfg.idVariable === 'string' && cfg.idVariable.trim() ? cfg.idVariable.trim() : undefined;\n return {\n success: true,\n suspend: true,\n screen: {\n nodeId: node.id,\n kind: 'object-form',\n title: interp(cfg.title) ?? node.label ?? objectName,\n description: interp(cfg.description),\n objectName,\n mode: cfg.mode === 'edit' ? 'edit' : 'create',\n recordId: cfg.recordId != null ? interp(cfg.recordId) : undefined,\n defaults,\n idVariable,\n fields: [],\n },\n };\n }\n\n const rawFields = Array.isArray(cfg.fields) ? (cfg.fields as Array<Record<string, unknown>>) : [];\n const hasFields = rawFields.length > 0;\n // Suspend to collect input when the screen declares fields, or opts in\n // explicitly. `waitForInput === false` forces a server pass-through.\n const shouldPause = cfg.waitForInput === true || (hasFields && cfg.waitForInput !== false);\n if (!shouldPause) {\n return { success: true };\n }\n const fields = rawFields.map((f) => ({\n name: String(f.name ?? ''),\n label: f.label != null ? String(f.label) : undefined,\n type: f.type != null ? String(f.type) : undefined,\n required: f.required === true,\n options: Array.isArray(f.options) ? (f.options as Array<{ value: unknown; label: string }>) : undefined,\n defaultValue: f.defaultValue !== undefined ? interpolate(f.defaultValue, variables, context) : undefined,\n placeholder: f.placeholder != null ? String(f.placeholder) : undefined,\n })).filter((f) => f.name.length > 0);\n return {\n success: true,\n suspend: true,\n screen: {\n nodeId: node.id,\n title: interp(cfg.title) ?? node.label ?? 'Input',\n description: interp(cfg.description),\n fields,\n },\n };\n },\n });\n\n // script — dispatch by actionType.\n engine.registerNodeExecutor({\n type: 'script',\n descriptor: defineActionDescriptor({\n type: 'script', version: '1.0.0', name: 'Script',\n description: 'Run a custom script action.',\n icon: 'code', category: 'logic', source: 'builtin',\n }),\n async execute(node, variables, context) {\n const cfg = (node.config ?? {}) as Record<string, unknown>;\n // `function` is canonical; `functionName` is an accepted alias — AI/templates\n // commonly emit it alongside `actionType: 'invoke_function'` (#1870 DX).\n const fnRaw = cfg.function ?? cfg.functionName;\n const fnName = typeof fnRaw === 'string' && fnRaw.trim() ? fnRaw.trim() : undefined;\n const actionType = typeof cfg.actionType === 'string' && cfg.actionType.trim() ? cfg.actionType.trim() : undefined;\n\n // Built-in side-effect actions keep their logger-backed behavior — but\n // only when an explicit `function` isn't set (that always wins).\n if (!fnName && actionType && SCRIPT_BUILTIN_ACTION_TYPES.has(actionType)) {\n ctx.logger.info(\n `[Script:${actionType}] template=${String(cfg.template)} ` +\n `recipients=${JSON.stringify(cfg.recipients)} ` +\n `vars=${JSON.stringify(cfg.variables)}`,\n );\n return {\n success: true,\n output: { actionType, template: cfg.template, recipients: cfg.recipients },\n };\n }\n\n // Inline `config.script` (a JS source body) is a distinct, recognized\n // form — but the built-in runtime has no server-side JS sandbox, so it\n // does not execute it. Warn loudly (not a silent success) and steer the\n // author to the supported path — a registered function — rather than\n // failing the flow. Executing inline scripts is a separate capability,\n // out of #1870's callable-resolution scope.\n const inlineScript = typeof cfg.script === 'string' && cfg.script.trim() ? cfg.script : undefined;\n if (!fnName && inlineScript) {\n ctx.logger.warn(\n `[Script] node '${node.id}': inline \\`config.script\\` is not executed by the built-in runtime ` +\n `(no server-side JS sandbox) — this node is a no-op. To run server logic, move it into a ` +\n `registered function and call it via \\`config.function\\` + \\`defineStack({ functions })\\`.`,\n );\n return { success: true, output: { script: 'not-executed' } };\n }\n\n // `actionType: 'invoke_function'` is a MARKER meaning \"call the named\n // function\" — the name lives in `function`/`functionName`, not in actionType\n // itself. A bare actionType that matched no built-in is still accepted as a\n // function name (shorthand).\n const target = fnName ?? (actionType === 'invoke_function' ? undefined : actionType);\n if (!target) {\n return {\n success: false,\n error:\n actionType === 'invoke_function'\n ? `script node '${node.id}': actionType 'invoke_function' requires \\`config.function\\` (or \\`functionName\\`) naming the function to call.`\n : `script node '${node.id}': declares neither \\`actionType\\` nor \\`function\\` — nothing to run.`,\n };\n }\n\n const handler = engine.resolveFunction(target);\n if (!handler) {\n return {\n success: false,\n error:\n `script node '${node.id}': '${target}' is not a built-in action ` +\n `(${[...SCRIPT_BUILTIN_ACTION_TYPES].join(', ')}) and no function named '${target}' is registered. ` +\n `Register it via \\`defineStack({ functions: { '${target}': fn } })\\`, or fix the name (#1870).`,\n };\n }\n\n // Map declared inputs (`config.inputs` | `config.input`) to the function,\n // interpolating `{var}` references against the live flow variables (so a\n // function can consume a prior node's output, e.g. `{aiResult.id}`).\n const input = interpolate(cfg.inputs ?? cfg.input ?? {}, variables, context) as Record<string, unknown>;\n const outputVariable =\n typeof cfg.outputVariable === 'string' && cfg.outputVariable.trim() ? cfg.outputVariable.trim() : undefined;\n try {\n const result = await handler({ input, variables, automation: context, logger: ctx.logger });\n // Pure-function pattern: the function RETURNS its result; `outputVariable`\n // exposes it as a flow variable so a later declarative node persists it\n // (e.g. `update_record fields: { ai_category: '{aiResult.ai_category}' }`).\n // Data I/O stays on the flow graph — the function itself does no writes.\n if (outputVariable) variables.set(outputVariable, result);\n return { success: true, output: { function: target, result } };\n } catch (err) {\n return {\n success: false,\n error: `script function '${target}' (node '${node.id}') failed: ${(err as Error).message}`,\n };\n }\n },\n });\n\n ctx.logger.info('[Screen/Script Nodes] 2 built-in node executors registered');\n}\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\nimport type { PluginContext } from '@objectstack/core';\nimport { defineActionDescriptor } from '@objectstack/spec/automation';\nimport { randomUUID } from 'node:crypto';\nimport type { AutomationEngine } from '../engine.js';\nimport { interpolate } from './template.js';\n\n/**\n * HTTP built-in node — canonical `http` (ADR-0018 M3) + deprecated aliases.\n *\n * `http` is the single outbound-callout verb the platform offers Flow, Workflow\n * Rules and Approval. It replaces the five divergent names (`http_request` /\n * `http_call` / `webhook` / …) which are kept as **deprecated aliases** of\n * `http` for back-compat (registered via {@link AutomationEngine.registerNodeAlias}).\n *\n * Two execution modes:\n *\n * - **Durable (`config.durable: true`)** — fire-and-forget callout enqueued\n * onto the `service-messaging` HTTP outbox (`sys_http_delivery`), inheriting\n * retry / idempotency / dead-letter. The flow gets back `{ deliveryId }` and\n * does NOT block on the response. This closes the \"`http_request` is a bare\n * fetch with no retry\" reliability gap (ADR-0018 §4). When no messaging HTTP\n * outbox is wired the node degrades to the inline call below.\n *\n * - **Request/response (default)** — a synchronous `fetch()` returning\n * `{ response, status }` to the flow, preserving the historical `http_request`\n * behavior so existing flows that read the response keep working. (The ADR's\n * `isAsync` suspend-and-resume variant is future work.)\n */\n\n/** Structural view of `service-messaging`'s HTTP outbox surface (ADR-0018 M3). */\ninterface MessagingHttpSurface {\n isHttpDeliveryReady?(): boolean;\n enqueueHttp?(input: {\n source: string;\n refId: string;\n dedupKey: string;\n label?: string;\n url: string;\n method?: string;\n headers?: Record<string, string>;\n signingSecret?: string;\n timeoutMs?: number;\n payload: unknown;\n }): Promise<string>;\n}\n\nconst HTTP_TYPE = 'http' as const;\n\nexport function registerHttpNodes(engine: AutomationEngine, ctx: PluginContext): void {\n const getMessaging = (): MessagingHttpSurface | undefined => {\n try {\n return ctx.getService<MessagingHttpSurface>('messaging');\n } catch {\n return undefined;\n }\n };\n\n engine.registerNodeExecutor({\n type: HTTP_TYPE,\n descriptor: defineActionDescriptor({\n type: HTTP_TYPE,\n version: '1.0.0',\n name: 'HTTP',\n description:\n 'Call an external HTTP endpoint. With `durable: true`, the call is enqueued on the '\n + 'messaging outbox with retry / dead-letter; otherwise it runs inline and returns the response.',\n icon: 'globe',\n category: 'io',\n source: 'builtin',\n // Capable of outbox-backed durable delivery (used when durable:true\n // and the messaging HTTP outbox is wired).\n needsOutbox: true,\n supportsRetry: true,\n paradigms: ['flow', 'approval'],\n configSchema: {\n type: 'object',\n required: ['url'],\n properties: {\n url: { type: 'string', description: 'Target URL' },\n method: { type: 'string', description: 'HTTP method (default GET; POST when durable)' },\n headers: { type: 'object', description: 'Request headers' },\n body: { description: 'Request body (JSON-serialised)' },\n durable: {\n type: 'boolean',\n description: 'Fire-and-forget via the durable outbox (retry/dead-letter) instead of inline request/response',\n },\n timeoutMs: { type: 'number', description: 'Per-request timeout (ms)' },\n signingSecret: { type: 'string', description: 'HMAC-SHA256 secret → X-Objectstack-Signature' },\n },\n },\n }),\n async execute(node, variables, context) {\n const raw = (node.config ?? {}) as Record<string, unknown>;\n const cfg = interpolate(raw, variables, context) as Record<string, unknown>;\n\n const url = cfg.url as string | undefined;\n if (!url) return { success: false, error: 'http: url is required' };\n\n const durable = cfg.durable === true;\n const headers = cfg.headers as Record<string, string> | undefined;\n const body = cfg.body;\n const timeoutMs = typeof cfg.timeoutMs === 'number' ? cfg.timeoutMs : undefined;\n const signingSecret = cfg.signingSecret as string | undefined;\n\n // ── Durable mode: enqueue onto the messaging HTTP outbox ──────────\n if (durable) {\n const messaging = getMessaging();\n if (messaging?.isHttpDeliveryReady?.() && messaging.enqueueHttp) {\n try {\n const deliveryId = await messaging.enqueueHttp({\n source: 'flow',\n refId: node.id,\n dedupKey: randomUUID(),\n label: `flow:${node.id}`,\n url,\n method: (cfg.method as string) ?? 'POST',\n headers,\n signingSecret,\n timeoutMs,\n payload: body ?? {},\n });\n return { success: true, output: { deliveryId, enqueued: true } };\n } catch (err) {\n return { success: false, error: `http (durable) failed to enqueue: ${(err as Error).message}` };\n }\n }\n // No outbox available — degrade to a best-effort inline call.\n ctx.logger.warn(\n `[http] node '${node.id}' requested durable delivery but no messaging HTTP outbox is wired; falling back to inline fetch`,\n );\n }\n\n // ── Request/response mode (default; preserves http_request) ───────\n const method = (cfg.method as string) ?? 'GET';\n const controller = new AbortController();\n const timer = timeoutMs ? setTimeout(() => controller.abort(), timeoutMs) : undefined;\n try {\n const response = await fetch(url, {\n method,\n headers,\n body: body !== undefined && body !== null ? JSON.stringify(body) : undefined,\n signal: controller.signal,\n });\n const data = await readBody(response);\n return {\n success: response.ok,\n output: { response: data, status: response.status },\n error: response.ok ? undefined : `HTTP ${response.status}`,\n };\n } catch (err) {\n const e = err as { name?: string; message?: string };\n const msg = e?.name === 'AbortError' ? `timeout after ${timeoutMs}ms` : e?.message ?? String(err);\n return { success: false, error: `http: ${msg}` };\n } finally {\n if (timer) clearTimeout(timer);\n }\n },\n });\n\n // ADR-0018 M3: collapse the divergent outbound verbs onto `http`. Old saved\n // flows / workflow rules / approval actions keep running via these aliases.\n engine.registerNodeAlias('http_request', HTTP_TYPE, { name: 'HTTP Request', needsOutbox: true });\n engine.registerNodeAlias('http_call', HTTP_TYPE, { name: 'HTTP Call', needsOutbox: true });\n engine.registerNodeAlias('webhook', HTTP_TYPE, { name: 'Webhook', needsOutbox: true });\n\n ctx.logger.info('[HTTP] http executor registered (+ deprecated aliases: http_request, http_call, webhook)');\n}\n\n/** Read a response body as JSON, falling back to text (empty body → null). */\nasync function readBody(response: { json(): Promise<unknown>; text(): Promise<string> }): Promise<unknown> {\n try {\n return await response.json();\n } catch {\n try {\n const text = await response.text();\n return text || null;\n } catch {\n return null;\n }\n }\n}\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\nimport type { PluginContext } from '@objectstack/core';\nimport { defineActionDescriptor } from '@objectstack/spec/automation';\nimport type { AutomationEngine, ConnectorActionContext } from '../engine.js';\n\n/**\n * Connector built-in node — `connector_action` (generic integration dispatch).\n *\n * Part of the platform baseline alongside `http_request` (ADR-0018 §Addendum):\n * where `http_request` calls *any raw URL*, `connector_action` invokes *any\n * registered connector's action*. The platform ships the generic dispatch node\n * + an (initially empty) connector registry on the engine; concrete connectors\n * — `connector-rest`, `connector-slack`, `connector-salesforce`, … — populate\n * the registry at runtime via `engine.registerConnector()`.\n *\n * Because the registry starts empty, a flow referencing a connector that no\n * plugin has registered fails the *step* with a clear error rather than failing\n * to register — graceful degradation matching `http_request`'s fail-soft style.\n */\nexport function registerConnectorNodes(engine: AutomationEngine, ctx: PluginContext): void {\n engine.registerNodeExecutor({\n type: 'connector_action',\n descriptor: defineActionDescriptor({\n type: 'connector_action',\n version: '1.0.0',\n name: 'Connector Action',\n description:\n 'Invoke an action on a registered connector (Slack, Salesforce, a REST API, …). '\n + 'The connector itself is contributed by an integration plugin via registerConnector().',\n icon: 'plug',\n category: 'io',\n source: 'builtin',\n supportsRetry: true,\n // Present in both authoring paradigms (ADR-0018 §registry table;\n // workflow_rule retired per ADR-0019).\n paradigms: ['flow', 'approval'],\n // Config contract — drives the Studio property form and flow validation.\n configSchema: {\n type: 'object',\n required: ['connectorId', 'actionId'],\n properties: {\n connectorId: { type: 'string', description: 'Registered connector name' },\n actionId: { type: 'string', description: 'Action key declared by the connector' },\n input: { type: 'object', description: 'Mapped inputs for the action' },\n },\n },\n }),\n async execute(node, variables, context) {\n const cfg = node.connectorConfig;\n if (!cfg?.connectorId || !cfg?.actionId) {\n return {\n success: false,\n error: `connector_action '${node.id}': connectorConfig.connectorId and .actionId are required`,\n };\n }\n\n const handler = engine.resolveConnectorAction(cfg.connectorId, cfg.actionId);\n if (!handler) {\n return {\n success: false,\n error:\n `connector_action '${node.id}': no handler for `\n + `'${cfg.connectorId}.${cfg.actionId}' — is the connector plugin registered?`,\n };\n }\n\n const handlerCtx: ConnectorActionContext = {\n variables,\n automation: context,\n logger: ctx.logger,\n };\n\n try {\n const output = await handler((cfg.input ?? {}) as Record<string, unknown>, handlerCtx);\n return { success: true, output };\n } catch (err) {\n return {\n success: false,\n error: `connector_action(${cfg.connectorId}.${cfg.actionId}) failed: ${(err as Error).message}`,\n };\n }\n },\n });\n\n ctx.logger.info('[Connector] 1 built-in node executor registered (connector_action)');\n}\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\nimport type { PluginContext } from '@objectstack/core';\nimport { defineActionDescriptor } from '@objectstack/spec/automation';\nimport type { AutomationEngine } from '../engine.js';\nimport { interpolate } from './template.js';\n\n/**\n * Structural view of `@objectstack/service-messaging`'s service (ADR-0012),\n * declared locally so service-automation does not take a runtime dependency on\n * it — mirrors the `ConnectorRegistrySurface` pattern. The `notify` node\n * resolves whatever object is registered under the `messaging` service and\n * dispatches through this shape; if no such service is present the node\n * degrades to a no-op success.\n */\nexport interface MessagingServiceSurface {\n emit(input: {\n topic: string;\n audience: string[];\n payload?: Record<string, unknown>;\n severity?: string;\n dedupKey?: string;\n source?: { object: string; id: string };\n actorId?: string;\n channels?: string[];\n }): Promise<{ notificationId: string; delivered: number; failed: number }>;\n}\n\n/** Coerce a config value (string | string[]) into a clean string[]. */\nfunction toStringList(value: unknown): string[] {\n if (Array.isArray(value)) return value.map((v) => String(v)).filter(Boolean);\n if (typeof value === 'string' && value.trim()) return [value.trim()];\n return [];\n}\n\n/**\n * `notify` built-in node (ADR-0012) — outbound notification.\n *\n * Baseline node and the human-notification counterpart to `http_request`\n * (\"raw call\") and `connector_action` (\"call a registered integration\"):\n * `notify` hands a topic + recipients + message to the platform's messaging\n * service, which fans it out across the user's channels (inbox by default).\n *\n * Like the CRUD nodes degrade without a data engine, `notify` degrades to a\n * warning + success when no `messaging` service is registered — the capability\n * simply isn't installed in that stack. Install `MessagingServicePlugin`\n * (`@objectstack/service-messaging`) and the same flow starts delivering, with\n * no flow edit. This is the seam that fixes the \"notify drops on the floor\"\n * gap (#1292) once messaging is present.\n */\nexport function registerNotifyNode(engine: AutomationEngine, ctx: PluginContext): void {\n const getMessaging = (): MessagingServiceSurface | undefined => {\n try {\n return ctx.getService<MessagingServiceSurface>('messaging');\n } catch {\n return undefined;\n }\n };\n\n engine.registerNodeExecutor({\n type: 'notify',\n descriptor: defineActionDescriptor({\n type: 'notify', version: '1.0.0', name: 'Notify',\n description: 'Send an outbound notification to users via the messaging service (inbox / email / push / …).',\n icon: 'bell', category: 'io', source: 'builtin',\n supportsRetry: true,\n // Delivery is outbox-backed inside the messaging service (ADR-0030\n // emit → sys_notification_delivery), so it inherits retry/dead-letter.\n needsOutbox: true,\n paradigms: ['flow', 'approval'],\n }),\n async execute(node, variables, context) {\n const cfg = (node.config ?? {}) as Record<string, unknown>;\n\n const recipients = toStringList(interpolate(cfg.recipients ?? cfg.to ?? [], variables, context));\n const title = String(interpolate(cfg.title ?? cfg.subject ?? '', variables, context) ?? '');\n const body = String(interpolate(cfg.message ?? cfg.body ?? '', variables, context) ?? '');\n const channels = toStringList(cfg.channels);\n const topic = cfg.topic ? String(cfg.topic) : undefined;\n const severity = cfg.severity ? String(cfg.severity) : undefined;\n const actionUrl = cfg.actionUrl\n ? String(interpolate(cfg.actionUrl, variables, context) ?? '')\n : undefined;\n const payload = cfg.payload\n ? (interpolate(cfg.payload, variables, context) as Record<string, unknown>)\n : undefined;\n\n if (!title) return { success: false, error: 'notify: title (or subject) is required' };\n if (recipients.length === 0) {\n return { success: false, error: 'notify: at least one recipient is required' };\n }\n\n const messaging = getMessaging();\n if (!messaging) {\n ctx.logger.warn(\n `[notify] no messaging service registered; notification \"${title}\" not delivered`,\n );\n return {\n success: true,\n output: { delivered: 0, failed: 0, skipped: true },\n };\n }\n\n try {\n // ADR-0030 single ingress: hand the messaging service a topic +\n // audience + payload; it writes the L2 event and materializes\n // per channel. title/body/url ride in the payload (templates in\n // a later phase fall back to these).\n const result = await messaging.emit({\n topic: topic ?? 'notify',\n audience: recipients,\n payload: { ...(payload ?? {}), title, body, url: actionUrl },\n severity,\n channels: channels.length ? channels : undefined,\n });\n return {\n success: true,\n output: {\n notificationId: result.notificationId,\n delivered: result.delivered,\n failed: result.failed,\n },\n };\n } catch (err) {\n return { success: false, error: `notify failed: ${(err as Error).message}` };\n }\n },\n });\n\n ctx.logger.info('[Notify] 1 built-in node executor registered (notify)');\n}\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\nimport type { PluginContext } from '@objectstack/core';\nimport { defineActionDescriptor } from '@objectstack/spec/automation';\nimport type { IJobService } from '@objectstack/spec/contracts';\nimport type { AutomationEngine, SuspendedRunStore } from '../engine.js';\n\n/**\n * `wait` built-in node — a durable pause (ADR-0019 suspend/resume), the timer /\n * signal sibling of the human-input `screen` and `approval` nodes.\n *\n * On entry the node *suspends* the run (the engine snapshots the continuation\n * and returns `{ status: 'paused', runId }`). How it resumes depends on\n * `waitEventConfig.eventType`:\n *\n * - **timer** — schedule a one-shot job (`IJobService`, `{ type: 'once', at }`)\n * that calls `engine.resume(runId)` when the duration elapses. With no job\n * service the node still suspends, but resumption must come from an external\n * `resume(runId)` (logged) — never silently no-ops or fails the flow,\n * matching the platform's degrade-don't-crash convention.\n * - **signal / webhook / manual / condition** — suspend with the signal name as\n * the correlation key; an external producer resumes the run when the event\n * arrives (`resume(runId)`), exactly like a decision-less approval.\n *\n * Reads its own run id from the `$runId` variable the engine injects at start\n * (same mechanism the approval node uses to map external state back to the run).\n */\nexport function registerWaitNode(engine: AutomationEngine, ctx: PluginContext): void {\n const getJobService = (): IJobService | undefined => {\n try {\n return ctx.getService<IJobService>('job');\n } catch {\n return undefined;\n }\n };\n\n engine.registerNodeExecutor({\n type: 'wait',\n descriptor: defineActionDescriptor({\n type: 'wait',\n version: '1.0.0',\n name: 'Wait',\n description: 'Pause the flow until a timer elapses or a named signal arrives.',\n icon: 'timer-reset',\n category: 'logic',\n source: 'builtin',\n // Durable pause — the run suspends and resumes later (timer/signal).\n supportsPause: true,\n isAsync: true,\n }),\n async execute(node, variables, _context) {\n // Prefer the spec-structured `waitEventConfig` block; fall back to a loose\n // `config` for hand-authored flows that put the same keys under config.\n const loose = (node.config ?? {}) as Record<string, unknown>;\n const wec = (node.waitEventConfig ?? {}) as Record<string, unknown>;\n const eventType = String(wec.eventType ?? loose.eventType ?? 'timer');\n const runId = variables.get('$runId');\n\n if (eventType === 'timer') {\n const durationMs =\n parseIsoDuration(wec.timerDuration ?? loose.timerDuration ?? loose.duration) ??\n (typeof wec.timeoutMs === 'number' ? wec.timeoutMs : undefined) ??\n (typeof loose.timeoutMs === 'number' ? (loose.timeoutMs as number) : undefined);\n\n // Persist the wake deadline as node output: the engine writes output\n // to variables (`<nodeId>.waitUntil`) *before* snapshotting the\n // suspended run, so a cold-booted kernel can re-arm the timer from the\n // durable store ({@link rearmSuspendedWaitTimers}).\n const at = durationMs && durationMs > 0 ? new Date(Date.now() + durationMs).toISOString() : undefined;\n const output = at ? { waitUntil: at } : undefined;\n\n const job = getJobService();\n if (job && runId != null && at) {\n const jobName = `flow-wait:${String(runId)}:${node.id}`;\n try {\n await job.schedule(jobName, { type: 'once', at }, async () => {\n try {\n await engine.resume(String(runId));\n } finally {\n // One-shot: drop the job so it never re-fires.\n try {\n await job.cancel?.(jobName);\n } catch {\n /* best-effort */\n }\n }\n });\n return { success: true, suspend: true, correlation: jobName, output };\n } catch (err) {\n ctx.logger.warn(\n `[wait] node '${node.id}': failed to schedule timer resume (${(err as Error)?.message ?? err}); ` +\n `suspending without auto-resume (resume it via resume(runId))`,\n );\n }\n } else if (!job) {\n ctx.logger.warn(\n `[wait] node '${node.id}': no job service registered — suspending without an auto-resume timer ` +\n `(resume it via resume(runId), or install the job service for durable timers)`,\n );\n }\n // Degrade: still suspend; resumption comes from an external resume()\n // (or a later boot's re-arm pass, when the deadline was persisted).\n return { success: true, suspend: true, correlation: `timer:${node.id}`, output };\n }\n\n // signal / webhook / manual / condition — suspend; an external producer\n // resumes the run when the named event arrives.\n const signal = String(wec.signalName ?? loose.signalName ?? loose.signal ?? `wait:${node.id}`);\n return { success: true, suspend: true, correlation: signal };\n },\n });\n\n ctx.logger.info('[Wait Node] 1 built-in node executor registered');\n}\n\n/** Minimal logger surface for {@link rearmSuspendedWaitTimers}. */\ninterface RearmLogger {\n info(msg: string, ...args: unknown[]): void;\n warn(msg: string, ...args: unknown[]): void;\n}\n\n/**\n * Re-arm auto-resume timers for suspended timer-`wait` runs after a cold boot\n * (ADR-0019 follow-up). The one-shot job a `wait` node schedules lives in the\n * job service's process memory unless that service is itself durable — so a\n * restart loses the wake-up while the suspended run survives in\n * `sys_automation_run`. This pass walks the durable store and:\n *\n * - **overdue** deadlines (`<nodeId>.waitUntil` in the past) → `resume()` now;\n * - **future** deadlines → re-schedule the same `flow-wait:<runId>:<nodeId>`\n * one-shot job (no job service → warn and leave it for an external resume);\n * - runs without a persisted `waitUntil` (approval / screen / signal pauses,\n * or pre-deadline-persistence rows) → skipped untouched.\n *\n * Double-fire safe: if the original job *did* survive (durable job store), the\n * second `resume(runId)` finds no suspended run — the engine's resume\n * idempotency absorbs it. Returns how many runs were resumed or re-armed.\n *\n * Called by `AutomationServicePlugin.start()` *after* the flow pull, because\n * `resume()` needs the flow definitions registered.\n */\nexport async function rearmSuspendedWaitTimers(\n engine: Pick<AutomationEngine, 'resume'>,\n store: SuspendedRunStore,\n job: IJobService | undefined,\n logger: RearmLogger,\n): Promise<number> {\n let runs;\n try {\n runs = await store.list();\n } catch (err) {\n logger.warn(`[wait] timer re-arm: failed to list suspended runs: ${(err as Error)?.message ?? err}`);\n return 0;\n }\n\n let rearmed = 0;\n for (const run of runs) {\n const wakeAt = run.variables?.[`${run.nodeId}.waitUntil`];\n if (typeof wakeAt !== 'string' || !wakeAt) continue; // not a timer wait\n const atMs = Date.parse(wakeAt);\n if (Number.isNaN(atMs)) continue;\n\n if (atMs <= Date.now()) {\n // Deadline elapsed while the process was down — resume immediately.\n try {\n await engine.resume(run.runId);\n rearmed++;\n } catch (err) {\n logger.warn(`[wait] timer re-arm: resume of overdue run '${run.runId}' failed: ${(err as Error)?.message ?? err}`);\n }\n continue;\n }\n\n if (!job) {\n logger.warn(\n `[wait] timer re-arm: run '${run.runId}' waits until ${wakeAt} but no job service is registered — ` +\n `resume it externally via resume(runId)`,\n );\n continue;\n }\n\n const jobName = `flow-wait:${run.runId}:${run.nodeId}`;\n try {\n await job.schedule(jobName, { type: 'once', at: wakeAt }, async () => {\n try {\n await engine.resume(run.runId);\n } finally {\n try {\n await job.cancel?.(jobName);\n } catch {\n /* best-effort */\n }\n }\n });\n rearmed++;\n } catch (err) {\n logger.warn(`[wait] timer re-arm: failed to re-schedule run '${run.runId}': ${(err as Error)?.message ?? err}`);\n }\n }\n return rearmed;\n}\n\n/**\n * Parse an ISO-8601 duration (the subset flows use — weeks/days + a time part\n * of hours/minutes/seconds, e.g. `PT1H`, `P3D`, `PT90M`, `P1DT12H`) into\n * milliseconds. A bare positive number is treated as milliseconds. Returns\n * `undefined` for anything unparseable / non-positive.\n */\nexport function parseIsoDuration(input: unknown): number | undefined {\n if (typeof input === 'number' && Number.isFinite(input)) return input > 0 ? input : undefined;\n if (typeof input !== 'string') return undefined;\n const s = input.trim();\n if (!s) return undefined;\n // Plain numeric string ⇒ milliseconds.\n if (/^\\d+(?:\\.\\d+)?$/.test(s)) {\n const n = Number(s);\n return n > 0 ? n : undefined;\n }\n const m = /^P(?:(\\d+)W)?(?:(\\d+)D)?(?:T(?:(\\d+)H)?(?:(\\d+)M)?(?:(\\d+(?:\\.\\d+)?)S)?)?$/.exec(s);\n if (!m) return undefined;\n const [, w, d, h, min, sec] = m;\n if (!w && !d && !h && !min && !sec) return undefined;\n const totalSec =\n Number(w ?? 0) * 7 * 86_400 +\n Number(d ?? 0) * 86_400 +\n Number(h ?? 0) * 3_600 +\n Number(min ?? 0) * 60 +\n Number(sec ?? 0);\n const ms = totalSec * 1000;\n return ms > 0 ? ms : undefined;\n}\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\nimport type { PluginContext } from '@objectstack/core';\nimport { defineActionDescriptor } from '@objectstack/spec/automation';\nimport type { AutomationContext } from '@objectstack/spec/contracts';\nimport type { AutomationEngine } from '../engine.js';\nimport { interpolate } from './template.js';\n\n/** Hard cap on subflow nesting — turns an accidental cycle into a clean error. */\nconst MAX_SUBFLOW_DEPTH = 16;\n\n/**\n * `subflow` built-in node — invoke another flow as a step (reuse / DRY).\n *\n * Resolves `config.input` (a `{token}` mapping) against the parent's variables,\n * runs `config.flowName` via the engine, and writes the child's output back to\n * the parent — under `${nodeId}.output`, and under `config.outputVariable` as a\n * bare variable when given.\n *\n * **Nested durable pause (linked-runs model).** If the child *suspends* (a\n * nested `approval` / `screen` / `wait`), the child's continuation is already\n * persisted by the engine as its own run; this node then suspends the PARENT\n * run at this node with `correlation: 'subflow:<childRunId>'`, so both rows\n * survive a restart and stay linked. The engine's resume boundary completes\n * the chain in both directions:\n *\n * - resuming the CHILD directly (approval service / wait timer hold the child\n * `$runId`) bubbles UP on completion — the engine auto-resumes the parent\n * with the child's output, mapped exactly like the synchronous path;\n * - resuming the PARENT (a UI holds the parent run id from the original\n * `execute()` response) delegates DOWN to the suspended child.\n *\n * The linkage rides on the child's context (`$parentRunId` / `$parentNodeId` /\n * `$parentOutputVariable`), which the engine persists with the child run — no\n * schema change. A depth guard ({@link MAX_SUBFLOW_DEPTH}) turns an accidental\n * recursive cycle into a clean error instead of a stack overflow.\n */\nexport function registerSubflowNode(engine: AutomationEngine, ctx: PluginContext): void {\n engine.registerNodeExecutor({\n type: 'subflow',\n descriptor: defineActionDescriptor({\n type: 'subflow',\n version: '1.0.0',\n name: 'Subflow',\n description: 'Invoke another flow as a reusable step and capture its output.',\n icon: 'workflow',\n category: 'logic',\n source: 'builtin',\n // A child that suspends (approval/screen/wait) suspends this node too —\n // the parent run pauses here and resumes when the child completes.\n supportsPause: true,\n }),\n async execute(node, variables, context) {\n const cfg = (node.config ?? {}) as Record<string, unknown>;\n const flowName =\n typeof cfg.flowName === 'string' ? cfg.flowName : typeof cfg.flow === 'string' ? cfg.flow : undefined;\n if (!flowName) {\n return { success: false, error: `subflow '${node.id}': config.flowName is required` };\n }\n\n // Cycle guard: depth rides on the context so it accumulates across nesting.\n const depth = Number((context as { $subflowDepth?: number } | undefined)?.$subflowDepth ?? 0);\n if (depth >= MAX_SUBFLOW_DEPTH) {\n return {\n success: false,\n error: `subflow '${flowName}': max nesting depth (${MAX_SUBFLOW_DEPTH}) exceeded — recursive subflow?`,\n };\n }\n\n // Map inputs (resolve `{var}` against the parent's variables/context).\n const rawInput = (cfg.input && typeof cfg.input === 'object' ? cfg.input : {}) as Record<string, unknown>;\n const params = interpolate(rawInput, variables, context ?? ({} as AutomationContext)) as Record<string, unknown>;\n\n const outVar = typeof cfg.outputVariable === 'string' && cfg.outputVariable ? cfg.outputVariable : undefined;\n\n // Parent linkage for nested durable pause: should the child suspend, the\n // engine persists these with the child run and uses them to bubble the\n // child's eventual completion back into THIS run (resume at this node).\n // `$runId` is injected by the engine at run start (ADR-0019).\n const parentRunId = variables.get('$runId');\n const childContext = {\n ...(context ?? {}),\n $subflowDepth: depth + 1,\n params,\n ...(parentRunId != null\n ? {\n $parentRunId: String(parentRunId),\n $parentNodeId: node.id,\n ...(outVar ? { $parentOutputVariable: outVar } : {}),\n }\n : {}),\n } as AutomationContext;\n\n const child = await engine.execute(flowName, childContext);\n\n if (child.status === 'paused') {\n // Nested durable pause: the child's continuation is persisted under its\n // own run id; suspend the parent here, linked via the correlation key.\n // A nested screen surfaces on the parent's paused result so a UI runner\n // can render it against the parent run id (the engine delegates the\n // parent's resume down to the child).\n if (!child.runId) {\n return { success: false, error: `subflow '${flowName}' paused without a run id — cannot link the runs` };\n }\n return {\n success: true,\n suspend: true,\n correlation: `subflow:${child.runId}`,\n screen: child.screen,\n };\n }\n if (!child.success) {\n return { success: false, error: `subflow '${flowName}' failed: ${child.error ?? 'unknown error'}` };\n }\n\n // Bare output variable (like the assignment node, the executor may write\n // directly to the parent variable map).\n if (outVar) variables.set(outVar, child.output ?? null);\n\n return { success: true, output: { output: child.output ?? null } };\n },\n });\n\n ctx.logger.info('[Subflow Node] 1 built-in node executor registered');\n}\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\nimport type { PluginContext } from '@objectstack/core';\nimport { defineActionDescriptor } from '@objectstack/spec/automation';\nimport type { AutomationContext } from '@objectstack/spec/contracts';\nimport type { AutomationEngine } from '../engine.js';\nimport { interpolate } from './template.js';\n\n/** Hard cap on map fan-out — turns a runaway collection into a clean error. */\nconst MAX_MAP_ITEMS = 10_000;\n\n/**\n * `map` built-in node — **sequential multi-instance** (ADR-0037 Track A2).\n *\n * Runs a per-item **subflow** for each element of a collection, **one item at a\n * time**, and continues once every item's subflow has completed — collecting\n * each result into `config.outputVariable`. Unlike `loop` (whose body region\n * runs synchronously and cannot pause), each item here is a full child run, so\n * the per-item subflow **may durably pause** (an `approval` / `screen` /\n * `wait`). This is the worked \"batch approval\" shape: *approve each item in\n * turn, then continue.*\n *\n * Mechanism (no token tree — one program counter, ADR-0037):\n * - The node tracks its progress in flow variables (`${nodeId}.$mapState`).\n * - For item *k* it invokes `config.flowName` via `engine.execute`, tagging the\n * child run with `$parentRunId` + `$parentMapNode` so the engine knows to\n * bubble the child's completion **back into this node** (not past it).\n * - If the child completes synchronously, the result is recorded and the loop\n * advances inline. If the child **pauses**, the parent suspends at this node\n * (`correlation: 'map:<childRunId>'`); when the child later completes, the\n * engine **re-enters** this node (it reads `$mapItemOutput` / `$mapItemDone`,\n * records the item, and starts the next).\n *\n * v1 is **sequential and fail-fast**: items run in order; the first item whose\n * subflow fails fails the map. Concurrent fan-out + partial aggregation is a\n * deliberate follow-up (ADR-0037 — needs N:1 aggregation + serialization).\n */\nexport function registerMapNode(engine: AutomationEngine, ctx: PluginContext): void {\n engine.registerNodeExecutor({\n type: 'map',\n descriptor: defineActionDescriptor({\n type: 'map',\n version: '1.0.0',\n name: 'Map',\n description: 'Run a per-item subflow for each element of a collection, one at a time (each item may pause).',\n icon: 'list-check',\n category: 'logic',\n source: 'builtin',\n // Each item's subflow may pause, so the map suspends and resumes per item.\n supportsPause: true,\n isAsync: true,\n }),\n async execute(node, variables, context) {\n const cfg = (node.config ?? {}) as Record<string, unknown>;\n const flowName =\n typeof cfg.flowName === 'string' ? cfg.flowName : typeof cfg.flow === 'string' ? cfg.flow : undefined;\n if (!flowName) {\n return { success: false, error: `map '${node.id}': config.flowName (the per-item subflow) is required` };\n }\n\n const iteratorVariable =\n typeof cfg.iteratorVariable === 'string' && cfg.iteratorVariable ? cfg.iteratorVariable : 'item';\n const indexVariable =\n typeof cfg.indexVariable === 'string' && cfg.indexVariable ? cfg.indexVariable : undefined;\n const outVar =\n typeof cfg.outputVariable === 'string' && cfg.outputVariable ? cfg.outputVariable : undefined;\n\n // Resolve the collection (template / bare var / already-an-array).\n const rawCollection = cfg.collection;\n let collection: unknown;\n if (Array.isArray(rawCollection)) {\n collection = rawCollection;\n } else if (typeof rawCollection === 'string') {\n collection = interpolate(rawCollection, variables, context ?? ({} as AutomationContext));\n if (collection == null && variables.has(rawCollection)) collection = variables.get(rawCollection);\n }\n if (!Array.isArray(collection)) {\n return { success: false, error: `map '${node.id}': collection '${String(rawCollection)}' did not resolve to an array` };\n }\n if (collection.length > MAX_MAP_ITEMS) {\n return { success: false, error: `map '${node.id}': collection length ${collection.length} exceeds the ${MAX_MAP_ITEMS} cap` };\n }\n\n // ── Progress state, carried across re-entries in the variable map. ──\n const stateKey = `${node.id}.$mapState`;\n const state = (variables.get(stateKey) as { started: number; results: unknown[] } | undefined) ?? {\n started: 0,\n results: [],\n };\n\n // Re-entry: the previously-started item's subflow just completed (the\n // engine bubbled its output here). Record it and clear the handoff vars.\n if (variables.get(`${node.id}.$mapItemDone`) === true) {\n state.results.push(variables.get(`${node.id}.$mapItemOutput`) ?? null);\n variables.delete(`${node.id}.$mapItemDone`);\n variables.delete(`${node.id}.$mapItemOutput`);\n }\n\n const parentRunId = variables.get('$runId');\n\n // Drive items in order. Synchronous items advance inline; a pausing item\n // suspends the run and is resumed via re-entry.\n while (state.started < collection.length) {\n const idx = state.started;\n const item = collection[idx];\n variables.set(iteratorVariable, item);\n if (indexVariable) variables.set(indexVariable, idx);\n\n const rawInput = (cfg.input && typeof cfg.input === 'object' ? cfg.input : {}) as Record<string, unknown>;\n const params = interpolate(rawInput, variables, context ?? ({} as AutomationContext)) as Record<string, unknown>;\n\n // When the mapped item IS a record (has an id), expose it as the\n // child's `record` + `object` so a per-item `approval` / `update_record`\n // targets that item — the natural \"approve each row\" shape. Otherwise\n // the item is just data, passed via `params`.\n const itemIsRecord = item != null && typeof item === 'object' && typeof (item as any).id === 'string';\n const itemObject = typeof cfg.itemObject === 'string' ? cfg.itemObject : (context as any)?.object;\n\n const childContext = {\n ...(context ?? {}),\n params,\n ...(itemIsRecord ? { record: item, object: itemObject } : {}),\n ...(parentRunId != null\n ? { $parentRunId: String(parentRunId), $parentMapNode: node.id }\n : {}),\n } as AutomationContext;\n\n const child = await engine.execute(flowName, childContext);\n\n if (child.status === 'paused') {\n if (!child.runId) {\n return { success: false, error: `map '${node.id}': item ${idx} paused without a run id — cannot link the runs` };\n }\n // Mark this item started and suspend; the engine re-enters on bubble.\n state.started = idx + 1;\n variables.set(stateKey, state);\n return { success: true, suspend: true, correlation: `map:${child.runId}` };\n }\n if (!child.success) {\n return { success: false, error: `map '${node.id}': item ${idx} (subflow '${flowName}') failed: ${child.error ?? 'unknown error'}` };\n }\n // Synchronous completion — record and advance.\n state.started = idx + 1;\n state.results.push(child.output ?? null);\n }\n\n // All items done.\n variables.set(stateKey, state);\n if (outVar) variables.set(outVar, state.results);\n return { success: true, output: { results: state.results, count: state.results.length } };\n },\n });\n\n ctx.logger.info('[Map Node] 1 built-in node executor registered');\n}\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\n/**\n * Built-in node executors — the automation engine's foundational vocabulary.\n *\n * Per ADR-0018 and the platform principle \"plugins are plugins; the platform's\n * foundational capabilities are built in,\" these node packs are seeded directly\n * by the core {@link AutomationServicePlugin} rather than shipped as separately\n * installable plugins. Each `register*Nodes(engine, ctx)` publishes its\n * descriptors with `source: 'builtin'`.\n *\n * Scope (built-in baseline):\n * - logic — decision / assignment (engine core)\n * - logic — loop (structured iteration container, ADR-0031)\n * - logic — parallel (structured parallel block, implicit join, ADR-0031)\n * - logic — try_catch (structured try/catch/retry, ADR-0031)\n * - data — get/create/update/delete_record (platform CRUD baseline)\n * - human — screen / script (core flow capability)\n * - io — http_request (foundational outbound I/O)\n * - io — connector_action (generic integration dispatch)\n * - io — notify (outbound notification via messaging service)\n *\n * `connector_action` is the *generic dispatch* counterpart to `http_request`\n * (ADR-0018 §Addendum): the platform ships the node + an (initially empty)\n * connector registry on the engine, and *concrete* connectors populate it at\n * runtime via `engine.registerConnector()`. Third-party node types continue to\n * extend the vocabulary via `engine.registerNodeExecutor()`, keeping the action\n * list open and marketplace-extensible.\n */\n\nimport type { PluginContext } from '@objectstack/core';\nimport type { AutomationEngine } from '../engine.js';\nimport { registerLogicNodes } from './logic-nodes.js';\nimport { registerLoopNode } from './loop-node.js';\nimport { registerParallelNode } from './parallel-node.js';\nimport { registerTryCatchNode } from './try-catch-node.js';\nimport { registerCrudNodes } from './crud-nodes.js';\nimport { registerScreenNodes } from './screen-nodes.js';\nimport { registerHttpNodes } from './http-nodes.js';\nimport { registerConnectorNodes } from './connector-nodes.js';\nimport { registerNotifyNode } from './notify-node.js';\nimport { registerWaitNode } from './wait-node.js';\nimport { registerSubflowNode } from './subflow-node.js';\nimport { registerMapNode } from './map-node.js';\n\nexport { registerLogicNodes } from './logic-nodes.js';\nexport { registerLoopNode } from './loop-node.js';\nexport { registerParallelNode } from './parallel-node.js';\nexport { registerTryCatchNode } from './try-catch-node.js';\nexport { registerCrudNodes } from './crud-nodes.js';\nexport { registerScreenNodes } from './screen-nodes.js';\nexport { registerHttpNodes } from './http-nodes.js';\nexport { registerConnectorNodes } from './connector-nodes.js';\nexport { registerNotifyNode } from './notify-node.js';\nexport { registerWaitNode, parseIsoDuration, rearmSuspendedWaitTimers } from './wait-node.js';\nexport { registerSubflowNode } from './subflow-node.js';\nexport { registerMapNode } from './map-node.js';\n\n/**\n * Seed every built-in node executor into the engine. Called by\n * {@link AutomationServicePlugin.init} so a bare `new AutomationServicePlugin()`\n * yields a fully-functional automation capability with no companion plugins.\n */\nexport function installBuiltinNodes(engine: AutomationEngine, ctx: PluginContext): void {\n registerLogicNodes(engine, ctx);\n registerLoopNode(engine, ctx);\n registerParallelNode(engine, ctx);\n registerTryCatchNode(engine, ctx);\n registerCrudNodes(engine, ctx);\n registerScreenNodes(engine, ctx);\n registerHttpNodes(engine, ctx);\n registerConnectorNodes(engine, ctx);\n registerNotifyNode(engine, ctx);\n registerWaitNode(engine, ctx);\n registerSubflowNode(engine, ctx);\n registerMapNode(engine, ctx);\n\n const types = engine.getRegisteredNodeTypes();\n ctx.logger.info(\n `[Automation] ${types.length} built-in node executors installed: ${types.join(', ')}`,\n );\n}\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\nimport type { Plugin, PluginContext } from '@objectstack/core';\nimport type { IJobService } from '@objectstack/spec/contracts';\nimport { AutomationEngine } from './engine.js';\nimport { installBuiltinNodes, rearmSuspendedWaitTimers } from './builtin/index.js';\nimport { SysAutomationRun } from './sys-automation-run.object.js';\nimport { ObjectStoreSuspendedRunStore, type SuspendedRunStoreEngine } from './suspended-run-store.js';\n\n/**\n * Configuration options for the AutomationServicePlugin.\n */\nexport interface AutomationServicePluginOptions {\n /** Enable debug logging for flow execution */\n debug?: boolean;\n /**\n * Durable suspended-run persistence (ADR-0019):\n * - `'auto'` (default): persist to `sys_automation_run` via the ObjectQL\n * engine when one is available, otherwise stay in-memory.\n * - `'memory'`: never persist — keep suspended runs in memory only (the\n * historical behaviour; suitable for tests / single-process dev).\n *\n * When persistence is on, a run paused at an approval / wait / screen node\n * survives a process restart and can be resumed after a cold boot.\n */\n suspendedRunStore?: 'auto' | 'memory';\n /**\n * Max in-memory execution-log entries retained per process (ring buffer;\n * oldest evicted). The buffer is diagnostic-only and already bounded\n * (launch-readiness.md P1-2); this just makes the window tunable. Defaults\n * to {@link DEFAULT_MAX_EXECUTION_LOG_SIZE} (1000).\n */\n maxLogSize?: number;\n}\n\n/**\n * AutomationServicePlugin — Core engine plugin\n *\n * Responsibilities:\n * 1. init phase: Create engine instance, register as 'automation' service, and\n * seed the platform's built-in node executors (logic / CRUD / screen-script /\n * http_request) via {@link installBuiltinNodes}. Per ADR-0018, foundational\n * capabilities are built into the core, not packaged as optional plugins.\n * 2. start phase: Trigger 'automation:ready' hook so third-party plugins can\n * register additional node types, then pull flow definitions from the\n * ObjectQL schema registry and register them with the engine.\n * 3. destroy phase: Clean up resources\n *\n * The engine's `registerNodeExecutor()` stays open so plugins extend the\n * node/action vocabulary at runtime — the marketplace-extensibility contract.\n *\n * @example\n * ```ts\n * import { LiteKernel } from '@objectstack/core';\n * import { AutomationServicePlugin } from '@objectstack/service-automation';\n *\n * const kernel = new LiteKernel();\n * kernel.use(new AutomationServicePlugin());\n * await kernel.bootstrap();\n *\n * const automation = kernel.getService('automation');\n * ```\n */\nexport class AutomationServicePlugin implements Plugin {\n name = 'com.objectstack.service-automation';\n version = '1.0.0';\n type = 'standard' as const;\n // Soft dependency on metadata: we look it up at start() and tolerate absence.\n // Do NOT declare a hard kernel dependency, so this plugin works in environments\n // where MetadataPlugin is not registered.\n dependencies: string[] = [];\n\n private engine?: AutomationEngine;\n private readonly options: AutomationServicePluginOptions;\n\n constructor(options: AutomationServicePluginOptions = {}) {\n this.options = options;\n }\n\n async init(ctx: PluginContext): Promise<void> {\n this.engine = new AutomationEngine(ctx.logger, undefined, {\n maxLogSize: this.options.maxLogSize,\n });\n\n // Register as global service — other plugins access via ctx.getService('automation')\n ctx.registerService('automation', this.engine);\n\n // Register the sys_automation_run object so suspended-run state migrates\n // like other sys_* tables (ADR-0019). Best-effort: a host without the\n // manifest service still runs in-memory. Skipped when persistence is off.\n if ((this.options.suspendedRunStore ?? 'auto') !== 'memory') {\n try {\n ctx.getService<{ register(m: unknown): void }>('manifest').register({\n id: 'com.objectstack.service-automation',\n name: 'Automation Service',\n version: '1.0.0',\n type: 'plugin',\n scope: 'system',\n defaultDatasource: 'cloud',\n namespace: 'sys',\n objects: [SysAutomationRun],\n });\n } catch (err) {\n ctx.logger.warn(\n `[Automation] manifest service unavailable; sys_automation_run not registered (suspended runs stay in-memory): ${(err as Error).message}`,\n );\n }\n }\n\n // Seed the platform's built-in node executors. A bare\n // `new AutomationServicePlugin()` is thus a self-contained automation\n // capability — no companion node-pack plugins required (ADR-0018).\n installBuiltinNodes(this.engine, ctx);\n\n if (this.options.debug) {\n ctx.hook('automation:beforeExecute', async (flowName: string) => {\n ctx.logger.debug(`[Automation] Before execute: ${flowName}`);\n });\n }\n\n ctx.logger.info('[Automation] Engine initialized');\n }\n\n async start(ctx: PluginContext): Promise<void> {\n if (!this.engine) {\n ctx.logger.warn('[Automation] start() called before init() — engine missing, skipping');\n return;\n }\n\n // Trigger hook to notify engine is ready — other plugins can start registering nodes\n await ctx.trigger('automation:ready', this.engine);\n\n const nodeTypes = this.engine.getRegisteredNodeTypes();\n ctx.logger.info(\n `[Automation] Engine started with ${nodeTypes.length} node types: ${nodeTypes.join(', ') || '(none)'}`,\n );\n\n // Upgrade to durable suspended-run persistence when an ObjectQL engine is\n // present (ADR-0019). The engine was constructed in init() before\n // services were wired, so we attach the DB-backed store here. Without an\n // engine (or with `suspendedRunStore: 'memory'`) the in-memory default\n // stands — suspended runs simply don't survive a restart.\n let durableStore: ObjectStoreSuspendedRunStore | null = null;\n if ((this.options.suspendedRunStore ?? 'auto') !== 'memory') {\n let dataEngine: SuspendedRunStoreEngine | null = null;\n try { dataEngine = ctx.getService<SuspendedRunStoreEngine>('objectql'); }\n catch { try { dataEngine = ctx.getService<SuspendedRunStoreEngine>('data'); } catch { /* none */ } }\n if (dataEngine && typeof dataEngine.find === 'function' && typeof dataEngine.insert === 'function') {\n durableStore = new ObjectStoreSuspendedRunStore(dataEngine, ctx.logger);\n this.engine.setSuspendedRunStore(durableStore);\n ctx.logger.info('[Automation] Suspended-run persistence enabled (sys_automation_run)');\n } else {\n ctx.logger.info('[Automation] No ObjectQL engine — suspended runs kept in-memory only');\n }\n }\n\n // #1870 — bridge `script`-node function calls to the host function\n // registry. ObjectQL holds the name→handler map populated from\n // `bundle.functions` / `defineStack({ functions })` (the same registry\n // hooks/actions resolve through). Wiring it here lets a `script` node\n // invoke an authored function by name; an unresolved name fails the step\n // loudly. Best-effort: without ObjectQL, function-calling script nodes\n // fail with a clear \"no function registered\" error when executed.\n try {\n const fnRegistry = ctx.getService<{\n resolveFunction?: (name: string) => ((c: unknown) => unknown) | undefined;\n }>('objectql');\n if (fnRegistry && typeof fnRegistry.resolveFunction === 'function') {\n this.engine.setFunctionResolver((name) => {\n const fn = fnRegistry.resolveFunction!(name);\n return typeof fn === 'function'\n ? (fnCtx) => (fn as (c: unknown) => unknown)(fnCtx)\n : undefined;\n });\n ctx.logger.debug('[Automation] script-node function registry bridged to objectql.resolveFunction');\n }\n } catch {\n ctx.logger.debug('[Automation] objectql not present — script-node function calls will fail loudly when used');\n }\n\n // Pull flow definitions from the ObjectQL schema registry. AppPlugin.init()\n // calls manifest.register(payload), which routes to ql.registerApp() and\n // stores each inline flow under type 'flow'. By the time start() runs,\n // every init() phase has completed, so the registry is fully populated.\n try {\n const ql = ctx.getService<{\n registry?: { listItems?: (type: string) => unknown[] };\n }>('objectql');\n if (!ql) {\n ctx.logger.debug('[Automation] objectql service not found at start()');\n } else if (!ql.registry) {\n ctx.logger.debug('[Automation] objectql.registry is undefined at start()');\n } else if (typeof ql.registry.listItems !== 'function') {\n ctx.logger.debug('[Automation] objectql.registry.listItems is not a function');\n }\n const flows = ql?.registry?.listItems?.('flow') ?? [];\n ctx.logger.debug(`[Automation] flow pull: registry returned ${flows.length} flow(s)`);\n let registered = 0;\n for (const f of flows) {\n const def = f as { name?: string };\n if (!def?.name) continue;\n try {\n this.engine.registerFlow(def.name, def as never);\n registered++;\n } catch (e) {\n const msg = e instanceof Error ? e.message : String(e);\n ctx.logger.warn(`[Automation] failed to register flow ${def.name}: ${msg}`);\n }\n }\n if (registered > 0) {\n ctx.logger.info(`[Automation] Pulled ${registered} flow(s) from ObjectQL registry`);\n }\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n ctx.logger.warn(`[Automation] flow pull from ObjectQL registry failed: ${msg}`);\n }\n\n // ADR-0019 follow-up: re-arm auto-resume timers for runs that were\n // suspended at a timer-`wait` node when the process went down. Must run\n // *after* the flow pull above — resume() needs the flow definitions\n // registered. Overdue deadlines resume immediately; future ones get\n // their one-shot job re-scheduled. Best-effort: a failure here only\n // means those runs wait for an external resume(runId).\n if (durableStore) {\n let job: IJobService | undefined;\n try { job = ctx.getService<IJobService>('job'); } catch { /* none */ }\n try {\n const rearmed = await rearmSuspendedWaitTimers(this.engine, durableStore, job, ctx.logger);\n if (rearmed > 0) {\n ctx.logger.info(`[Automation] Re-armed ${rearmed} suspended wait timer(s) after restart`);\n }\n } catch (err) {\n ctx.logger.warn(`[Automation] wait-timer re-arm failed: ${(err as Error).message}`);\n }\n }\n }\n\n async destroy(): Promise<void> {\n this.engine = undefined;\n }\n}\n"],"mappings":"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","names":["durationMs","defineActionDescriptor","defineActionDescriptor","defineActionDescriptor","defineActionDescriptor","defineActionDescriptor","defineActionDescriptor","defineActionDescriptor","defineActionDescriptor","defineActionDescriptor","defineActionDescriptor","defineActionDescriptor","defineActionDescriptor","defineActionDescriptor","defineActionDescriptor","defineActionDescriptor","defineActionDescriptor","defineActionDescriptor","defineActionDescriptor","defineActionDescriptor"]}
|