@open-mercato/core 0.10.1-develop.7816.1.6c959754cf → 0.10.1-develop.7822.1.5d6a1d00a1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/dist/modules/communication_channels/workers/poll-channel.js +44 -16
  3. package/dist/modules/communication_channels/workers/poll-channel.js.map +2 -2
  4. package/dist/modules/sales/commands/documents.js +1 -1
  5. package/dist/modules/sales/commands/documents.js.map +2 -2
  6. package/dist/modules/workflows/api/definitions/[id]/route.js +15 -0
  7. package/dist/modules/workflows/api/definitions/[id]/route.js.map +2 -2
  8. package/dist/modules/workflows/api/definitions/generate/route.js +3 -1
  9. package/dist/modules/workflows/api/definitions/generate/route.js.map +2 -2
  10. package/dist/modules/workflows/api/definitions/route.js +15 -0
  11. package/dist/modules/workflows/api/definitions/route.js.map +2 -2
  12. package/dist/modules/workflows/lib/agent-result-mapping.js +3 -14
  13. package/dist/modules/workflows/lib/agent-result-mapping.js.map +2 -2
  14. package/dist/modules/workflows/lib/definition-evaluation.js +16 -2
  15. package/dist/modules/workflows/lib/definition-evaluation.js.map +2 -2
  16. package/dist/modules/workflows/lib/mapping-target-validation.js +85 -0
  17. package/dist/modules/workflows/lib/mapping-target-validation.js.map +7 -0
  18. package/dist/modules/workflows/lib/parallel-handler.js +10 -8
  19. package/dist/modules/workflows/lib/parallel-handler.js.map +2 -2
  20. package/dist/modules/workflows/lib/safe-mapping-path.js +38 -0
  21. package/dist/modules/workflows/lib/safe-mapping-path.js.map +7 -0
  22. package/dist/modules/workflows/lib/step-handler.js +5 -16
  23. package/dist/modules/workflows/lib/step-handler.js.map +2 -2
  24. package/package.json +8 -8
  25. package/src/modules/communication_channels/workers/poll-channel.ts +34 -5
  26. package/src/modules/sales/commands/documents.ts +3 -1
  27. package/src/modules/workflows/api/definitions/[id]/route.ts +17 -0
  28. package/src/modules/workflows/api/definitions/generate/route.ts +3 -1
  29. package/src/modules/workflows/api/definitions/route.ts +15 -0
  30. package/src/modules/workflows/i18n/de.json +1 -0
  31. package/src/modules/workflows/i18n/en.json +1 -0
  32. package/src/modules/workflows/i18n/es.json +1 -0
  33. package/src/modules/workflows/i18n/ko.json +1 -0
  34. package/src/modules/workflows/i18n/pl.json +1 -0
  35. package/src/modules/workflows/lib/agent-result-mapping.ts +3 -16
  36. package/src/modules/workflows/lib/definition-evaluation.ts +19 -2
  37. package/src/modules/workflows/lib/mapping-target-validation.ts +113 -0
  38. package/src/modules/workflows/lib/parallel-handler.ts +13 -9
  39. package/src/modules/workflows/lib/safe-mapping-path.ts +44 -0
  40. package/src/modules/workflows/lib/step-handler.ts +5 -32
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../src/modules/workflows/lib/step-handler.ts"],
4
- "sourcesContent": ["/**\n * Workflows Module - Step Handler Service\n *\n * Handles individual workflow step execution:\n * - Creating step instances when entering a step\n * - Executing step logic based on step type (START, END, AUTOMATED, USER_TASK)\n * - Completing step instances when exiting\n *\n * Functional API (no classes) following Open Mercato conventions.\n */\n\nimport { EntityManager } from '@mikro-orm/core'\nimport {\n WorkflowInstance,\n WorkflowBranchInstance,\n WorkflowDefinition,\n StepInstance,\n UserTask,\n WorkflowEvent,\n type StepInstanceStatus,\n type WorkflowStepType,\n} from '../data/entities'\nimport { emitWorkflowsEvent } from '../events'\nimport { calculateDueDate, parseDuration } from './duration'\nimport {\n CONDITION_STEP_TYPE,\n conditionReadContext,\n evaluateConditionExpression,\n readWaitForConditionConfig,\n} from './condition-handler'\nimport { mapAgentResultToContext } from './agent-result-mapping'\nimport {\n WORKFLOW_AGENT_OUTCOME_CONTEXT_KEY,\n buildAgentOutcomeContextEntry,\n mapDispositionToAgentOutcome,\n} from './outcome-routing'\nimport { logWorkflowEvent } from './event-logger'\nimport { DRY_RUN_EVENT_TYPES, isDryRunInstance } from './dry-run'\nimport { findDefinitionForInstance } from './find-definition'\nimport { resolveDefinitionInterpolationMode, type WorkflowInterpolationMode } from './interpolation-pipeline'\nimport {\n collectResolvedEntityTypes,\n flattenTaskText,\n resolveTaskAssignment,\n resolveTaskDeadlineDuration,\n resolveTaskDecisions,\n resolveTaskEntityBindings,\n resolveTaskText,\n type TaskInterpolate,\n} from './task-resolution'\nimport { scheduleUserTaskSla } from './task-sla'\nimport type { TaskQuickActionInput } from './task-quick-action'\nimport { createLogger } from '@open-mercato/shared/lib/logger'\nimport { findWorkflowDefinition } from './find-definition'\nimport { validateAgainstPorts } from './port-contract'\nimport type { WorkflowIoContract } from '../data/validators'\n\nconst logger = createLogger('workflows')\n\n// ============================================================================\n// Types and Interfaces\n// ============================================================================\n\nexport interface StepExecutionContext {\n workflowContext: Record<string, any>\n userId?: string\n triggerData?: any\n // Populated by executeStep from the loaded definition; absent means lenient.\n interpolationMode?: WorkflowInterpolationMode\n // Populated by executeStep from the loaded definition so task notifications\n // can name the workflow the way its author did.\n workflowName?: string\n}\n\nexport interface StepExecutionResult {\n status: 'COMPLETED' | 'WAITING' | 'FAILED'\n outputData?: any\n nextSteps?: string[] // For parallel forks (Phase 7)\n waitReason?: 'USER_TASK' | 'SIGNAL' | 'TIMER' | 'FORK' | 'CONDITION'\n error?: string\n}\n\nexport class StepExecutionError extends Error {\n constructor(\n message: string,\n public code: string,\n public details?: any\n ) {\n super(message)\n this.name = 'StepExecutionError'\n }\n}\n\n// ============================================================================\n// Main Step Execution Functions\n// ============================================================================\n\n/**\n * Enter a workflow step - create step instance and mark as ACTIVE\n *\n * @param em - Entity manager\n * @param instance - Workflow instance\n * @param stepId - Step ID to enter\n * @param context - Execution context\n * @returns Created step instance\n */\nexport async function enterStep(\n em: EntityManager,\n instance: WorkflowInstance,\n stepId: string,\n context: StepExecutionContext,\n branch?: WorkflowBranchInstance | null\n): Promise<StepInstance> {\n // Load workflow definition to get step details\n const definition = await findDefinitionForInstance(em, instance)\n\n if (!definition) {\n throw new StepExecutionError(\n `Workflow definition not found: ${instance.definitionId}`,\n 'DEFINITION_NOT_FOUND',\n { definitionId: instance.definitionId }\n )\n }\n\n // Find step in definition\n const stepDef = definition.definition.steps.find((s: any) => s.stepId === stepId)\n if (!stepDef) {\n throw new StepExecutionError(\n `Step not found in workflow definition: ${stepId}`,\n 'STEP_NOT_FOUND',\n { workflowId: definition.workflowId, stepId }\n )\n }\n\n const now = new Date()\n\n // Create step instance\n const stepInstance = em.create(StepInstance, {\n workflowInstanceId: instance.id,\n branchInstanceId: branch ? branch.id : null,\n stepId: stepDef.stepId,\n stepName: stepDef.stepName,\n stepType: stepDef.stepType,\n status: 'ACTIVE',\n inputData: context.triggerData || null,\n enteredAt: now,\n retryCount: 0,\n tenantId: instance.tenantId,\n organizationId: instance.organizationId,\n createdAt: now,\n updatedAt: now,\n })\n\n await em.persist(stepInstance).flush()\n\n // Log STEP_ENTERED event\n await logStepEvent(em, {\n workflowInstanceId: instance.id,\n stepInstanceId: stepInstance.id,\n ...(branch ? { branchInstanceId: branch.id } : {}),\n eventType: 'STEP_ENTERED',\n eventData: {\n stepId: stepDef.stepId,\n stepName: stepDef.stepName,\n stepType: stepDef.stepType,\n },\n userId: context.userId,\n tenantId: instance.tenantId,\n organizationId: instance.organizationId,\n })\n\n return stepInstance\n}\n\n/**\n * Exit a workflow step - mark as completed and record timing\n *\n * @param em - Entity manager\n * @param stepInstance - Step instance to exit\n * @param outputData - Optional output data from step execution\n */\n/**\n * Announces the BUSINESS milestone a completing step carries, if it carries one.\n *\n * Resolved here rather than plumbed through every `exitStep` caller because a\n * milestone must be announced from EVERY completion path \u2014 the inline advance,\n * a signal resume, a task completion, a timer, a condition wake, a parallel\n * branch \u2014 and a per-caller argument would silently miss whichever path was\n * added next. Both reads are by primary key and the executor already loaded both\n * rows, so in practice they resolve from the identity map rather than the\n * database.\n *\n * Best-effort by construction: a step that completed HAS completed, and a failure\n * to announce that fact must never undo it.\n */\nasync function emitStepMilestone(em: EntityManager, stepInstance: StepInstance): Promise<void> {\n try {\n const instance = await em.findOne(WorkflowInstance, { id: stepInstance.workflowInstanceId })\n if (!instance) return\n const definition = await em.findOne(WorkflowDefinition, { id: instance.definitionId })\n const stepDef = definition?.definition?.steps?.find((step: any) => step.stepId === stepInstance.stepId)\n const milestoneKey = typeof stepDef?.milestone === 'string' ? stepDef.milestone : null\n if (!milestoneKey) return\n await emitWorkflowsEvent(\n 'workflows.instance.milestone_reached',\n {\n instanceId: instance.id,\n workflowId: instance.workflowId,\n milestoneKey,\n // For the trace only. A consumer matches on `milestoneKey`; matching on\n // the step is exactly the coupling the milestone model removes.\n stepId: stepInstance.stepId,\n occurredAt: new Date().toISOString(),\n data: stepInstance.outputData ?? null,\n tenantId: instance.tenantId,\n organizationId: instance.organizationId,\n },\n { persistent: true },\n )\n } catch {\n // ignore \u2014 announcing a milestone can never fail a step that completed\n }\n}\n\n/**\n * `step_instances.execution_time_ms` is a Postgres `integer`, so a step that\n * lasted 2^31 ms (~24.86 days) or longer cannot be stored there \u2014 the flush\n * that completes the step would throw after its task already committed,\n * stranding the run. Such a duration persists as `null` (every reader already\n * falls back to `exitedAt - enteredAt`); the exact value still travels on the\n * `STEP_EXITED` event, whose payload is jsonb.\n */\nexport const MAX_PERSISTED_EXECUTION_TIME_MS = 2_147_483_647\n\nexport function toPersistableExecutionTimeMs(executionTimeMs: number | null): number | null {\n if (executionTimeMs === null || !Number.isFinite(executionTimeMs)) return null\n return Math.abs(executionTimeMs) > MAX_PERSISTED_EXECUTION_TIME_MS ? null : executionTimeMs\n}\n\nexport async function exitStep(\n em: EntityManager,\n stepInstance: StepInstance,\n outputData?: any\n): Promise<void> {\n const now = new Date()\n\n // Calculate execution time if we have enteredAt\n let executionTimeMs: number | null = null\n if (stepInstance.enteredAt) {\n executionTimeMs = now.getTime() - stepInstance.enteredAt.getTime()\n }\n\n // Update step instance\n stepInstance.status = 'COMPLETED'\n stepInstance.outputData = outputData || null\n stepInstance.exitedAt = now\n stepInstance.executionTimeMs = toPersistableExecutionTimeMs(executionTimeMs)\n stepInstance.updatedAt = now\n\n await em.flush()\n\n // Log STEP_EXITED event\n await logStepEvent(em, {\n workflowInstanceId: stepInstance.workflowInstanceId,\n stepInstanceId: stepInstance.id,\n eventType: 'STEP_EXITED',\n eventData: {\n stepId: stepInstance.stepId,\n status: 'COMPLETED',\n executionTimeMs,\n hasOutput: !!outputData,\n },\n tenantId: stepInstance.tenantId,\n organizationId: stepInstance.organizationId,\n })\n\n await emitStepMilestone(em, stepInstance)\n}\n\n/**\n * Mark a step instance FAILED and log `STEP_FAILED`.\n *\n * The ONE place a step row becomes FAILED, so a failure the run went on to\n * tolerate leaves the same durable trace a fatal one does. Tolerating a failure\n * is a routing decision, not a reason to forget it happened: without the row and\n * the event there is nothing for the run verdict to be computed from, and\n * rerun-from-step refuses the step it exists for because its latest attempt\n * still reads ACTIVE.\n *\n * Idempotent \u2014 a second call on an already-FAILED row is a no-op, so a handler\n * that reported the failure itself never produces two `STEP_FAILED` rows.\n */\nexport async function markStepInstanceFailed(\n em: EntityManager,\n stepInstance: StepInstance,\n failure: {\n error: string\n details?: unknown\n branchInstanceId?: string | null\n userId?: string\n }\n): Promise<void> {\n if (stepInstance.status === 'FAILED') return\n\n const now = new Date()\n stepInstance.status = 'FAILED'\n stepInstance.errorData = {\n error: failure.error,\n details: failure.details ?? undefined,\n }\n stepInstance.exitedAt = now\n stepInstance.updatedAt = now\n await em.flush()\n\n await logStepEvent(em, {\n workflowInstanceId: stepInstance.workflowInstanceId,\n stepInstanceId: stepInstance.id,\n ...(failure.branchInstanceId ? { branchInstanceId: failure.branchInstanceId } : {}),\n eventType: 'STEP_FAILED',\n eventData: { stepId: stepInstance.stepId, error: failure.error },\n ...(failure.userId ? { userId: failure.userId } : {}),\n tenantId: stepInstance.tenantId,\n organizationId: stepInstance.organizationId,\n })\n}\n\n/**\n * Execute a workflow step based on its type\n *\n * Main entry point for step execution. Handles:\n * - START: Immediate completion\n * - END: Workflow completion\n * - AUTOMATED: Activity execution (MVP: immediate completion)\n * - USER_TASK: Create user task and wait\n * - SUB_WORKFLOW: Invoke child workflow\n *\n * @param em - Entity manager\n * @param instance - Workflow instance\n * @param stepId - Step ID to execute\n * @param context - Execution context\n * @param container - DI container (required for SUB_WORKFLOW steps)\n * @returns Execution result with status and output\n */\nexport async function executeStep(\n em: EntityManager,\n instance: WorkflowInstance,\n stepId: string,\n context: StepExecutionContext,\n container?: any,\n branch?: WorkflowBranchInstance | null\n): Promise<StepExecutionResult> {\n try {\n // Enter the step (create step instance)\n const stepInstance = await enterStep(em, instance, stepId, context, branch)\n\n // Load workflow definition to get step configuration\n const definition = await findDefinitionForInstance(em, instance)\n\n if (!definition) {\n throw new StepExecutionError(\n `Workflow definition not found: ${instance.definitionId}`,\n 'DEFINITION_NOT_FOUND',\n { definitionId: instance.definitionId }\n )\n }\n\n const stepDef = definition.definition.steps.find((s: any) => s.stepId === stepId)\n if (!stepDef) {\n throw new StepExecutionError(\n `Step not found: ${stepId}`,\n 'STEP_NOT_FOUND',\n { stepId }\n )\n }\n\n // Execute based on step type\n const result = await executeStepByType(\n em,\n instance,\n stepInstance,\n stepDef,\n {\n ...context,\n interpolationMode: resolveDefinitionInterpolationMode(definition.definition),\n workflowName: definition.workflowName,\n },\n container,\n branch\n )\n\n // If step completed, exit it\n if (result.status === 'COMPLETED') {\n await exitStep(em, stepInstance, result.outputData)\n } else if (result.status === 'FAILED') {\n // A handler that REPORTS a failure (`handleAutomatedStep` returns\n // `{status:'FAILED'}` for a failed sync activity) never reaches the catch\n // below, so before this the row stayed ACTIVE for ever and no\n // `STEP_FAILED` was logged \u2014 the failure existed only in a value the\n // caller was free to tolerate. The row is now terminal on both paths.\n await markStepInstanceFailed(em, stepInstance, {\n error: result.error ?? 'Step execution failed',\n details: result.outputData,\n branchInstanceId: branch?.id ?? null,\n userId: context.userId,\n })\n }\n\n return result\n } catch (error) {\n // Handle step execution errors\n const errorMessage = error instanceof Error ? error.message : String(error)\n\n // Try to mark step as failed if we have a step instance\n try {\n const failedStepInstance = await em.findOne(StepInstance, {\n workflowInstanceId: instance.id,\n stepId,\n status: 'ACTIVE',\n })\n\n if (failedStepInstance) {\n await markStepInstanceFailed(em, failedStepInstance, {\n error: errorMessage,\n details: error instanceof StepExecutionError ? error.details : undefined,\n branchInstanceId: branch?.id ?? null,\n userId: context.userId,\n })\n }\n } catch (updateError) {\n // Swallow update errors to preserve original error\n logger.error('Failed to update step instance with error', { err: updateError })\n }\n\n return {\n status: 'FAILED',\n error: errorMessage,\n }\n }\n}\n\n// ============================================================================\n// Step Type Handlers\n// ============================================================================\n\n/**\n * Execute step based on its type\n *\n * @param em - Entity manager\n * @param instance - Workflow instance\n * @param stepInstance - Step instance\n * @param stepDef - Step definition from workflow\n * @param context - Execution context\n * @returns Execution result\n */\nasync function executeStepByType(\n em: EntityManager,\n instance: WorkflowInstance,\n stepInstance: StepInstance,\n stepDef: any,\n context: StepExecutionContext,\n container?: any,\n branch?: WorkflowBranchInstance | null\n): Promise<StepExecutionResult> {\n const stepType: WorkflowStepType = stepDef.stepType\n\n switch (stepType) {\n case 'START':\n return handleStartStep(stepDef, context)\n\n case 'END':\n return handleEndStep(stepDef, context)\n\n case 'AUTOMATED':\n return await handleAutomatedStep(em, instance, stepInstance, stepDef, context, container, branch)\n\n case 'USER_TASK':\n return await handleUserTaskStep(em, instance, stepInstance, stepDef, context, branch)\n\n case 'SUB_WORKFLOW':\n if (!container) {\n throw new StepExecutionError(\n 'Container required for SUB_WORKFLOW execution',\n 'CONTAINER_REQUIRED',\n { stepType }\n )\n }\n return await handleSubWorkflowStep(em, container, instance, stepInstance, stepDef, context)\n\n case 'IF_ELSE':\n case 'SWITCH':\n return handleBranchingStep(stepType)\n\n case 'WAIT_FOR_SIGNAL':\n return await handleWaitForSignalStep(em, instance, stepInstance, stepDef, context, branch)\n\n case 'WAIT_FOR_TIMER':\n return await handleWaitForTimerStep(em, instance, stepInstance, stepDef, context, branch)\n\n case 'WAIT_FOR_CONDITION':\n return await handleWaitForConditionStep(em, instance, stepInstance, stepDef, context, branch)\n\n case 'PARALLEL_FORK': {\n // Entering a fork opens branch tokens and parks the root token in the\n // FORKED state; the interleaved loop in the executor drives the branches.\n if (branch) {\n // Nested forks are rejected by definition validation; fail closed.\n throw new StepExecutionError(\n 'Nested PARALLEL_FORK is not supported',\n 'NESTED_FORK_NOT_SUPPORTED',\n { stepType, stepId: stepDef.stepId }\n )\n }\n const definition = await findDefinitionForInstance(em, instance)\n if (!definition) {\n throw new StepExecutionError(\n `Workflow definition not found: ${instance.definitionId}`,\n 'DEFINITION_NOT_FOUND',\n { definitionId: instance.definitionId }\n )\n }\n const { openFork } = await import('./parallel-handler')\n await openFork(em, instance, definition, stepDef)\n return { status: 'WAITING', waitReason: 'FORK', outputData: { stepType: 'PARALLEL_FORK', forkStepId: stepDef.stepId } }\n }\n\n case 'PARALLEL_JOIN':\n // The join is a synchronization point handled by the parallel loop\n // (branches are marked COMPLETED on arrival; the loop fires the join).\n // Executing the step itself is a no-op.\n return { status: 'COMPLETED', outputData: { stepType: 'PARALLEL_JOIN', timestamp: new Date().toISOString() } }\n\n default:\n throw new StepExecutionError(\n `Unknown step type: ${stepType}`,\n 'UNKNOWN_STEP_TYPE',\n { stepType }\n )\n }\n}\n\n/**\n * Handle START step - no-op, immediately complete\n */\nfunction handleStartStep(\n stepDef: any,\n context: StepExecutionContext\n): StepExecutionResult {\n return {\n status: 'COMPLETED',\n outputData: {\n stepType: 'START',\n timestamp: new Date().toISOString(),\n },\n }\n}\n\n/**\n * Handle END step - mark as complete\n */\nfunction handleEndStep(\n stepDef: any,\n context: StepExecutionContext\n): StepExecutionResult {\n return {\n status: 'COMPLETED',\n outputData: {\n stepType: 'END',\n timestamp: new Date().toISOString(),\n finalContext: context.workflowContext,\n },\n }\n}\n\n/**\n * Handle IF_ELSE / SWITCH steps - pure transition sugar.\n *\n * Branching nodes carry NO runtime semantics of their own: they behave exactly\n * like an AUTOMATED step with no activities and complete immediately. Every\n * routing decision stays in `findValidTransitions`, which already evaluates the\n * business_rules condition language and orders candidates by priority, so the\n * unconditioned \"otherwise\" route wins only when no cased route matches.\n */\nfunction handleBranchingStep(stepType: 'IF_ELSE' | 'SWITCH'): StepExecutionResult {\n return {\n status: 'COMPLETED',\n outputData: {\n stepType,\n timestamp: new Date().toISOString(),\n },\n }\n}\n\n/**\n * Handle AUTOMATED step - execute activities\n *\n * Executes activities defined in step configuration.\n * Supports both sync and async activities.\n */\nasync function handleAutomatedStep(\n em: EntityManager,\n instance: WorkflowInstance,\n stepInstance: StepInstance,\n stepDef: any,\n context: StepExecutionContext,\n container?: any,\n branch?: WorkflowBranchInstance | null\n): Promise<StepExecutionResult> {\n // Extract activities from step definition\n const activities = stepDef.activities || []\n\n if (activities.length === 0) {\n // No activities defined - immediate completion (legacy behavior)\n return {\n status: 'COMPLETED',\n outputData: {\n stepType: 'AUTOMATED',\n timestamp: new Date().toISOString(),\n },\n }\n }\n\n // Import activity executor\n const { executeActivities, interpolateVariables } = await import('./activity-executor')\n\n // Persist the resolved INVOKE_AGENT input so the run inspector shows what this\n // step actually received. `inputData` was seeded at step entry from the\n // workflow trigger data (null for a manually-started run), never the agent's\n // real, interpolated `config.input` \u2014 the same value the executor computes via\n // `interpolateActivityConfig` (a thin wrapper over `interpolateVariables`), so\n // re-interpolating the input field here yields exactly what the agent runs on.\n const agentActivity = activities.find(\n (activity: any) => activity.activityType === 'INVOKE_AGENT' && activity?.config?.input !== undefined,\n )\n if (agentActivity) {\n try {\n const resolvedInput = interpolateVariables(\n (agentActivity as any).config.input,\n context.workflowContext,\n instance,\n { mode: context.interpolationMode },\n )\n if (resolvedInput !== undefined) {\n stepInstance.inputData = resolvedInput\n stepInstance.updatedAt = new Date()\n await em.flush()\n }\n } catch {\n // Display-only capture: an interpolation error is surfaced by the actual\n // activity run below, so it must never abort the step here.\n }\n }\n\n try {\n // Execute activities with proper context\n const results = await executeActivities(em, container, activities, {\n workflowInstance: instance,\n workflowContext: context.workflowContext,\n stepContext: { stepId: stepDef.stepId, stepName: stepDef.stepName },\n stepInstanceId: stepInstance.id,\n userId: context.userId,\n interpolationMode: context.interpolationMode,\n })\n\n // Check if there are pending async activities\n const pendingActivities = results.filter(r => r.async && !r.success)\n if (pendingActivities.length > 0) {\n // Workflow should pause and wait for async activities\n const now = new Date()\n if (branch) {\n branch.status = 'WAITING_FOR_ACTIVITIES'\n branch.updatedAt = now\n } else {\n instance.status = 'WAITING_FOR_ACTIVITIES'\n instance.pausedAt = now\n instance.updatedAt = now\n }\n await em.flush()\n\n return {\n status: 'WAITING',\n waitReason: 'SIGNAL', // Reuse SIGNAL wait reason (will be resumed by activity completion)\n outputData: {\n pendingActivities: pendingActivities.map(r => ({\n activityId: r.activityId,\n activityName: r.activityName,\n jobId: r.jobId,\n })),\n },\n }\n }\n\n // Check for failures in sync activities\n const failures = results.filter(r => !r.success && !r.async)\n if (failures.length > 0) {\n const errorMessages = failures.map(f => `${f.activityName || f.activityId}: ${f.error}`).join('; ')\n return {\n status: 'FAILED',\n error: `${failures.length} activity(ies) failed: ${errorMessages}`,\n outputData: {\n failures: failures.map(f => ({\n activityId: f.activityId,\n activityName: f.activityName,\n error: f.error,\n retryCount: f.retryCount,\n })),\n },\n }\n }\n\n // INVOKE_AGENT (agent_orchestrator) integration:\n // A `__park` marker means the agent's proposal was routed to a human \u2014 park\n // the instance on the signal exactly like handleWaitForSignalStep. The step\n // declares `signalConfig.signalName`, so sendSignal (relaxed) resumes it when\n // agent_orchestrator's dispose path fires `agent_orchestrator.proposal.ready`.\n const parkResult = results.find((r) => r.output && (r.output as any).__park)\n if (parkResult) {\n const signalName = (parkResult.output as any).__park.signalName as string\n const proposalId = (parkResult.output as any).proposalId\n const now = new Date()\n await logStepEvent(em, {\n workflowInstanceId: instance.id,\n stepInstanceId: stepInstance.id,\n ...(branch ? { branchInstanceId: branch.id } : {}),\n eventType: 'SIGNAL_AWAITING',\n eventData: { signalName, proposalId, reason: 'INVOKE_AGENT' },\n userId: context.userId,\n tenantId: instance.tenantId,\n organizationId: instance.organizationId,\n })\n if (branch) {\n branch.status = 'PAUSED'\n branch.updatedAt = now\n } else {\n instance.status = 'PAUSED'\n instance.pausedAt = now\n instance.updatedAt = now\n }\n await em.flush()\n return {\n status: 'WAITING',\n waitReason: 'SIGNAL',\n outputData: { signalName, proposalId, awaitingSince: now },\n }\n }\n\n // Inline-resolved agent result (auto_approved / researcher / artifact):\n // surface the disposition into context (top-level, matching the human-path\n // signal merge) so the outgoing transition can branch (effector vs skip)\n // uniformly.\n const inlineAgent = results.find(\n (r) => r.output && typeof (r.output as any).kind === 'string' && 'agentId' in (r.output as any),\n )\n if (inlineAgent && !branch) {\n const out = inlineAgent.output as any\n const agentActivity = activities.find(\n (a: any) => a.activityType === 'INVOKE_AGENT' && a.activityId === inlineAgent.activityId,\n )\n const mappedPatch = mapAgentResultToContext(\n {\n kind: out.kind,\n agentId: out.agentId,\n proposalId: out.proposalId,\n proposalPayload: out.proposalPayload,\n data: out.data,\n artifacts: out.artifacts,\n },\n agentActivity?.config?.outputMapping,\n )\n const contextPatch =\n mappedPatch ??\n (out.kind === 'auto_approved'\n ? {\n disposition: 'auto_approved',\n agentProposalId: out.proposalId,\n proposalPayload: out.proposalPayload,\n }\n : out.kind === 'artifact'\n ? {\n disposition: 'researcher',\n [`${stepInstance.stepId}_agent`]: { artifacts: out.artifacts, summary: out.summary },\n }\n : null)\n // Outcome routing (spec 7.2): record which of the five fixed disposition\n // kinds this step resolved to under an ENGINE-OWNED context key. The\n // executor routes on this marker alone \u2014 never on the author-visible\n // `disposition` key \u2014 which is what makes branching on a disposition\n // wiring rather than context string-matching.\n const resolvedOutcome = mapDispositionToAgentOutcome(out.kind)\n const outcomeMarker = resolvedOutcome\n ? {\n [WORKFLOW_AGENT_OUTCOME_CONTEXT_KEY]: buildAgentOutcomeContextEntry(\n stepInstance.stepId,\n resolvedOutcome,\n out.proposalId,\n ),\n }\n : null\n\n const nextContext = { ...instance.context, ...(contextPatch ?? {}), ...(outcomeMarker ?? {}) }\n if (contextPatch || outcomeMarker) {\n instance.context = nextContext\n instance.updatedAt = new Date()\n await em.flush()\n }\n }\n\n // All activities completed successfully\n const activityOutputs = results.reduce((acc, r) => {\n if (r.output) {\n acc[r.activityId] = r.output\n }\n return acc\n }, {} as Record<string, any>)\n\n return {\n status: 'COMPLETED',\n outputData: {\n stepType: 'AUTOMATED',\n timestamp: new Date().toISOString(),\n activityResults: activityOutputs,\n activityCount: results.length,\n },\n }\n } catch (error: any) {\n return {\n status: 'FAILED',\n error: `Activity execution failed: ${error.message}`,\n }\n }\n}\n\n/**\n * Resolve a user task's deadline from its authored duration.\n *\n * An unparseable duration fails the step with an actionable message instead of\n * quietly resolving to some default the author never asked for.\n */\nfunction resolveUserTaskDeadline(stepId: string, slaDuration: string): Date {\n try {\n return calculateDueDate(slaDuration)\n } catch (error) {\n throw new StepExecutionError(\n `Invalid user task deadline \"${slaDuration}\": ${error instanceof Error ? error.message : String(error)}`,\n 'INVALID_USER_TASK_DEADLINE',\n { stepId, slaDuration }\n )\n }\n}\n\n/**\n * Publish the declared `workflows.task.assigned` domain event for a freshly\n * created task, alongside (never instead of) the internal `USER_TASK_CREATED`\n * audit row. It carries both the individual assignee and the role queue so the\n * notification subscriber can reach role-assigned tasks too.\n *\n * Delivery is at-least-once and consumers must be idempotent; a bus failure\n * must never break workflow execution, so this is strictly best-effort.\n */\nasync function emitTaskAssignedEvent(\n userTask: UserTask,\n instance: WorkflowInstance,\n context: StepExecutionContext,\n quickAction?: TaskQuickActionInput\n): Promise<void> {\n try {\n await emitWorkflowsEvent(\n 'workflows.task.assigned',\n {\n // Everything the notification quick-action rule needs, and nothing more.\n // ABSENT means \"unknown\", and the subscriber falls back to the deep\n // link \u2014 a quick action must never be offered on a guess.\n ...(quickAction ? { quickAction } : {}),\n taskId: userTask.id,\n taskName: userTask.taskName,\n workflowInstanceId: instance.id,\n workflowId: instance.workflowId,\n workflowName: context.workflowName ?? instance.workflowId,\n assignedUserId: userTask.assignedTo ?? null,\n assignedToRoles: userTask.assignedToRoles ?? null,\n dueDate: userTask.dueDate ? userTask.dueDate.toISOString() : null,\n // Additive: the records the task is about. Modules that own those\n // records subscribe and surface the task on their own turf \u2014 the\n // customers module writes a `CustomerTodoLink` from this. Workflows\n // never reaches into another module's tables to do it itself.\n entityBindings: userTask.entityBindings ?? null,\n tenantId: instance.tenantId,\n organizationId: instance.organizationId ?? null,\n },\n { persistent: true }\n )\n } catch (error) {\n logger.error('Failed to emit workflows.task.assigned', {\n component: 'step-handler',\n taskId: userTask.id,\n err: error,\n })\n }\n\n if (userTask.assigneeKind !== 'customer' || !userTask.assignedTo) return\n\n try {\n // Addressed to ONE portal principal (`recipientUserId` is what narrows the\n // SSE audience) and carrying an id and nothing else. The receiver asks the\n // authorized portal API what the task is; the bridge never carries the\n // answer, so a mis-scoped connection learns nothing from it.\n await emitWorkflowsEvent(\n 'workflows.task.portal_assigned',\n {\n taskId: userTask.id,\n recipientUserId: userTask.assignedTo,\n tenantId: instance.tenantId,\n organizationId: instance.organizationId ?? null,\n },\n { persistent: false }\n )\n } catch (error) {\n logger.error('Failed to emit workflows.task.portal_assigned', {\n component: 'step-handler',\n taskId: userTask.id,\n err: error,\n })\n }\n}\n\n/**\n * Handle USER_TASK step - create user task and enter waiting state\n *\n * Creates a UserTask entity and returns WAITING status.\n * The workflow will pause until the task is completed by a user.\n */\nasync function handleUserTaskStep(\n em: EntityManager,\n instance: WorkflowInstance,\n stepInstance: StepInstance,\n stepDef: any,\n context: StepExecutionContext,\n branch?: WorkflowBranchInstance | null\n): Promise<StepExecutionResult> {\n const userTaskConfig = stepDef.userTaskConfig || {}\n\n // Everything an author can write on a task \u2014 its title, its instructions, its\n // decision labels, its dynamic assignee and its entity bindings \u2014 is resolved\n // HERE, once, against the context the run has at creation time. Resolving\n // later would let a definition edit retro-change what a running task says.\n // Lenient on purpose: an unresolved dynamic assignee is what the fallback role\n // queue exists for, not a reason to fail the step.\n //\n // Imported dynamically like every other activity-executor use in this file, so\n // the module graph stays acyclic.\n const { interpolateVariables } = await import('./activity-executor')\n const interpolate: TaskInterpolate = (value) =>\n interpolateVariables(value, context.workflowContext, instance)\n\n const assignment = resolveTaskAssignment(userTaskConfig, interpolate)\n if (assignment.fellBackToRoles) {\n logger.warn('Dynamic task assignee resolved to nothing; falling back to the role queue', {\n component: 'step-handler',\n stepId: stepDef.stepId,\n workflowInstanceId: instance.id,\n assignedToRoles: assignment.assignedToRoles,\n })\n }\n if (assignment.fellBackFromCustomer) {\n // The author addressed this to a portal principal and no id resolved, so it\n // landed in the backoffice namespace instead. The work is still reachable,\n // but by a different audience than intended \u2014 that is worth saying out loud.\n logger.warn('Portal task assignee resolved to nothing; task created for the backoffice instead', {\n component: 'step-handler',\n stepId: stepDef.stepId,\n workflowInstanceId: instance.id,\n })\n }\n\n const { bindings, unresolved } = resolveTaskEntityBindings(userTaskConfig.entityBindings, interpolate)\n if (unresolved.length) {\n logger.warn('Task entity bindings skipped because their ids did not resolve', {\n component: 'step-handler',\n stepId: stepDef.stepId,\n workflowInstanceId: instance.id,\n unresolved,\n })\n }\n\n const taskName = String(interpolate(stepDef.stepName) ?? stepDef.stepName)\n const instructions = resolveTaskText(userTaskConfig.instructions, interpolate)\n const decisions = resolveTaskDecisions(userTaskConfig.decisions, interpolate)\n const deadlineDuration = resolveTaskDeadlineDuration(userTaskConfig)\n\n // Create user task\n const now = new Date()\n\n // Dry run (spec section 8.2): a simulation raises NO real task. The row is\n // what everything else hangs off \u2014 the `workflows.task.assigned` event and\n // therefore the notification subscriber, the SLA reminder/breach queue jobs,\n // and every Work Inbox and task-list query \u2014 so suppressing the row is what\n // suppresses all of them, rather than a filter at each read site that a new\n // reader could forget. Everything above still runs, so the report says\n // exactly who the task WOULD have gone to. The step still WAITS: the author\n // simulates the decision through step-through or the advance action.\n if (isDryRunInstance(instance)) {\n await logStepEvent(em, {\n workflowInstanceId: instance.id,\n stepInstanceId: stepInstance.id,\n ...(branch ? { branchInstanceId: branch.id } : {}),\n eventType: DRY_RUN_EVENT_TYPES.userTaskSimulated,\n eventData: {\n taskName,\n assignedTo: assignment.assignedTo,\n assignedToRoles: assignment.assignedToRoles,\n assigneeKind: assignment.assigneeKind,\n ...(bindings.length ? { entityBindings: bindings } : {}),\n ...(decisions.length ? { decisions } : {}),\n ...(deadlineDuration ? { deadlineDuration } : {}),\n },\n tenantId: instance.tenantId,\n organizationId: instance.organizationId,\n })\n\n if (branch) {\n branch.status = 'PAUSED'\n branch.updatedAt = now\n } else {\n instance.status = 'PAUSED'\n instance.updatedAt = now\n }\n await em.flush()\n\n return {\n status: 'WAITING',\n waitReason: 'USER_TASK',\n outputData: { simulated: true, wouldRaiseTask: taskName },\n }\n }\n\n const userTask = em.create(UserTask, {\n workflowInstanceId: instance.id,\n stepInstanceId: stepInstance.id,\n branchInstanceId: branch ? branch.id : null,\n taskName,\n description: flattenTaskText(instructions) ?? stepDef.description ?? null,\n status: 'PENDING',\n formSchema: userTaskConfig.formSchema || null,\n formData: null,\n assignedTo: assignment.assignedTo,\n // Decided by `resolveTaskAssignment` from the authored `assigneeKind`, and\n // `'customer'` only when an individual assignee actually resolved. Written\n // explicitly rather than left to the column default so the discriminator is\n // a decision at every creation site, not an accident.\n assigneeKind: assignment.assigneeKind,\n assignedToRoles: assignment.assignedToRoles,\n entityBindings: bindings.length ? bindings : null,\n // Denormalized from the SAME resolved bindings, so the two can never drift.\n // Null for a task about nothing, which is what the vacuous-pass rule reads.\n entityTypes: bindings.length ? collectResolvedEntityTypes(bindings) : null,\n priority: userTaskConfig.priority ?? null,\n dueDate: deadlineDuration\n ? resolveUserTaskDeadline(stepDef.stepId, deadlineDuration)\n : null,\n tenantId: instance.tenantId,\n organizationId: instance.organizationId,\n createdAt: now,\n updatedAt: now,\n })\n\n await em.persist(userTask).flush()\n\n // Log USER_TASK_CREATED event\n await logStepEvent(em, {\n workflowInstanceId: instance.id,\n stepInstanceId: stepInstance.id,\n ...(branch ? { branchInstanceId: branch.id } : {}),\n eventType: 'USER_TASK_CREATED',\n eventData: {\n userTaskId: userTask.id,\n taskName: userTask.taskName,\n assignedTo: userTask.assignedTo,\n assignedToRoles: userTask.assignedToRoles,\n ...(bindings.length ? { entityBindings: bindings } : {}),\n ...(userTask.priority ? { priority: userTask.priority } : {}),\n ...(decisions.length ? { decisions } : {}),\n ...(assignment.fellBackToRoles ? { assignmentFallback: 'roles' } : {}),\n },\n tenantId: instance.tenantId,\n organizationId: instance.organizationId,\n })\n\n await emitTaskAssignedEvent(userTask, instance, context, {\n decisions: decisions.map((decision) => ({ id: decision.id })),\n formSchema: userTask.formSchema ?? null,\n editablePrefilled: userTaskConfig.editablePrefilled ?? null,\n })\n\n // Reminders and the deadline breach are scheduled HERE, once, with the\n // deadline already absolute on each job payload \u2014 see `lib/task-sla.ts`. A\n // task with no deadline schedules nothing at all.\n await scheduleUserTaskSla({\n workflowInstanceId: instance.id,\n stepInstanceId: stepInstance.id,\n branchInstanceId: branch ? branch.id : null,\n userTaskId: userTask.id,\n dueDate: userTask.dueDate ?? null,\n reminders: userTaskConfig.reminders ?? null,\n tenantId: instance.tenantId,\n organizationId: instance.organizationId,\n userId: context.userId,\n now,\n })\n\n // Pause execution - waits for user task completion. For a branch, only the\n // branch pauses; sibling branches keep running.\n if (branch) {\n branch.status = 'PAUSED'\n branch.updatedAt = now\n } else {\n instance.status = 'PAUSED'\n instance.updatedAt = now\n }\n await em.flush()\n\n return {\n status: 'WAITING',\n waitReason: 'USER_TASK',\n outputData: {\n userTaskId: userTask.id,\n },\n }\n}\n\n/**\n * Handle SUB_WORKFLOW step - invoke another workflow and wait for completion\n *\n * Creates a child workflow instance with mapped input data,\n * executes it synchronously, and returns mapped output data.\n */\nasync function handleSubWorkflowStep(\n em: EntityManager,\n container: any,\n instance: WorkflowInstance,\n stepInstance: StepInstance,\n stepDef: any,\n context: StepExecutionContext\n): Promise<StepExecutionResult> {\n const { subWorkflowId, inputMapping, outputMapping, version } = stepDef.config || {}\n\n if (!subWorkflowId) {\n return {\n status: 'FAILED',\n error: 'Sub-workflow ID not specified in step configuration'\n }\n }\n\n // Map input data from parent context to child context\n let childContext = mapInputData(instance.context, inputMapping || {})\n\n // Import workflow executor functions\n const { startWorkflow, executeWorkflow } = await import('./workflow-executor')\n\n try {\n // Resolve the child definition to read its declared port contract (if any).\n // Validation is opt-in by contract presence: only children that declare\n // `definition.io` ports are checked, so legacy untyped sub-workflows behave\n // exactly as before.\n const childDefinition = await findWorkflowDefinition(em, {\n workflowId: subWorkflowId,\n version,\n tenantId: instance.tenantId,\n organizationId: instance.organizationId,\n })\n const ioContract = (childDefinition?.definition as { io?: WorkflowIoContract } | undefined)?.io\n\n // Validate/coerce mapped inputs against the child's declared input ports.\n if (ioContract?.inputs?.length) {\n const { coerced, errors } = validateAgainstPorts(childContext, ioContract.inputs)\n if (errors.length > 0) {\n const message = `Sub-workflow input validation failed: ${errors.map((e) => e.message).join('; ')}`\n await logStepEvent(em, {\n workflowInstanceId: instance.id,\n stepInstanceId: stepInstance.id,\n eventType: 'SUB_WORKFLOW_FAILED',\n eventData: { subWorkflowId, reason: 'INPUT_VALIDATION', error: message },\n userId: context.userId,\n tenantId: instance.tenantId,\n organizationId: instance.organizationId,\n })\n return { status: 'FAILED', error: message }\n }\n childContext = coerced\n }\n\n // Start child workflow with parent metadata\n const childInstance = await startWorkflow(em, {\n workflowId: subWorkflowId,\n version,\n initialContext: childContext,\n correlationKey: instance.correlationKey || undefined,\n metadata: {\n ...instance.metadata,\n labels: {\n ...instance.metadata?.labels,\n parentInstanceId: instance.id,\n parentStepId: stepDef.stepId,\n parentStepInstanceId: stepInstance.id,\n },\n },\n tenantId: instance.tenantId,\n organizationId: instance.organizationId,\n })\n\n // Log sub-workflow invocation event\n await logStepEvent(em, {\n workflowInstanceId: instance.id,\n stepInstanceId: stepInstance.id,\n eventType: 'SUB_WORKFLOW_STARTED',\n eventData: {\n childInstanceId: childInstance.id,\n subWorkflowId,\n version,\n inputData: childContext,\n },\n userId: context.userId,\n tenantId: instance.tenantId,\n organizationId: instance.organizationId,\n })\n\n // Execute child workflow synchronously\n const result = await executeWorkflow(em, container, childInstance.id, {\n userId: context.userId,\n })\n\n // Handle child workflow result\n if (result.status === 'COMPLETED') {\n // Map output data from child context to parent context (+ io-port\n // validation). Shared with the async resume path so both apply identical\n // mapping/validation rules.\n const mapped = mapSubWorkflowOutput(result.context, outputMapping || {}, ioContract)\n if (mapped.error) {\n await logStepEvent(em, {\n workflowInstanceId: instance.id,\n stepInstanceId: stepInstance.id,\n eventType: 'SUB_WORKFLOW_FAILED',\n eventData: { childInstanceId: childInstance.id, reason: 'OUTPUT_VALIDATION', error: mapped.error },\n userId: context.userId,\n tenantId: instance.tenantId,\n organizationId: instance.organizationId,\n })\n return { status: 'FAILED', error: mapped.error }\n }\n const outputData = mapped.outputData\n\n await logStepEvent(em, {\n workflowInstanceId: instance.id,\n stepInstanceId: stepInstance.id,\n eventType: 'SUB_WORKFLOW_COMPLETED',\n eventData: {\n childInstanceId: childInstance.id,\n outputData,\n },\n userId: context.userId,\n tenantId: instance.tenantId,\n organizationId: instance.organizationId,\n })\n\n return {\n status: 'COMPLETED',\n outputData,\n }\n } else if (result.status === 'FAILED') {\n await logStepEvent(em, {\n workflowInstanceId: instance.id,\n stepInstanceId: stepInstance.id,\n eventType: 'SUB_WORKFLOW_FAILED',\n eventData: {\n childInstanceId: childInstance.id,\n error: result.errors?.join(', '),\n },\n userId: context.userId,\n tenantId: instance.tenantId,\n organizationId: instance.organizationId,\n })\n\n return {\n status: 'FAILED',\n error: `Sub-workflow failed: ${result.errors?.join(', ')}`,\n }\n } else {\n // The child parked at its first async/agent step (RUNNING / PAUSED /\n // WAITING_FOR_ACTIVITIES). Make the SUB_WORKFLOW step suspendable: park the\n // parent on SUB_WORKFLOW_SIGNAL_NAME. The child's terminal completeWorkflow\n // enqueues a resume job that resumes this parent (mirrors the INVOKE_AGENT\n // __park branch above).\n const { SUB_WORKFLOW_SIGNAL_NAME } = await import('./activity-executor')\n const now = new Date()\n await logStepEvent(em, {\n workflowInstanceId: instance.id,\n stepInstanceId: stepInstance.id,\n eventType: 'SIGNAL_AWAITING',\n eventData: {\n signalName: SUB_WORKFLOW_SIGNAL_NAME,\n childInstanceId: childInstance.id,\n reason: 'SUB_WORKFLOW',\n },\n userId: context.userId,\n tenantId: instance.tenantId,\n organizationId: instance.organizationId,\n })\n instance.status = 'PAUSED'\n instance.pausedAt = now\n instance.updatedAt = now\n await em.flush()\n return {\n status: 'WAITING',\n waitReason: 'SIGNAL',\n outputData: { childInstanceId: childInstance.id },\n }\n }\n } catch (error: any) {\n await logStepEvent(em, {\n workflowInstanceId: instance.id,\n stepInstanceId: stepInstance.id,\n eventType: 'SUB_WORKFLOW_FAILED',\n eventData: {\n subWorkflowId,\n error: error.message,\n },\n userId: context.userId,\n tenantId: instance.tenantId,\n organizationId: instance.organizationId,\n })\n\n return {\n status: 'FAILED',\n error: `Sub-workflow execution failed: ${error.message}`,\n }\n }\n}\n\n/**\n * Handle WAIT_FOR_SIGNAL step - pause workflow until signal received\n *\n * Creates a waiting state and pauses the workflow until an external signal\n * with the matching signal name is received. The signal payload will be merged\n * into the workflow context when received.\n */\nasync function handleWaitForSignalStep(\n em: EntityManager,\n instance: WorkflowInstance,\n stepInstance: StepInstance,\n stepDef: any,\n context: StepExecutionContext,\n branch?: WorkflowBranchInstance | null\n): Promise<StepExecutionResult> {\n const signalConfig = stepDef.signalConfig || {}\n const signalName = signalConfig.signalName || stepDef.stepId\n const timeout = signalConfig.timeout ? parseDuration(signalConfig.timeout) : null\n\n const now = new Date()\n\n // Log signal awaiting event\n await logStepEvent(em, {\n workflowInstanceId: instance.id,\n stepInstanceId: stepInstance.id,\n ...(branch ? { branchInstanceId: branch.id } : {}),\n eventType: 'SIGNAL_AWAITING',\n eventData: {\n signalName,\n timeout,\n description: stepDef.description,\n },\n userId: context.userId,\n tenantId: instance.tenantId,\n organizationId: instance.organizationId,\n })\n\n // Pause execution (branch-scoped when running inside a parallel branch)\n if (branch) {\n branch.status = 'PAUSED'\n branch.updatedAt = now\n } else {\n instance.status = 'PAUSED'\n instance.pausedAt = now\n instance.updatedAt = now\n }\n await em.flush()\n\n // Return WAITING status to halt executor\n return {\n status: 'WAITING',\n waitReason: 'SIGNAL',\n outputData: {\n signalName,\n timeout,\n awaitingSince: now,\n },\n }\n}\n\n/**\n * Handle WAIT_FOR_TIMER step - pause workflow until a timer fires.\n *\n * Reads `duration` (relative, e.g. \"PT5M\") or `until` (ISO 8601 datetime) from\n * `stepDef.config` (preferred \u2014 matches StepsEditor) or `stepDef.timerConfig`.\n * Either value may be a `{{...}}` template: the definition validator accepts\n * templates there, so they are resolved here, once, against the run context\n * with the definition's interpolation mode.\n * Enqueues a delayed timer job on the workflow-activities queue; when the job\n * is processed by the activity worker, it calls `timerHandler.fireTimer` to\n * resume the workflow.\n */\nasync function handleWaitForTimerStep(\n em: EntityManager,\n instance: WorkflowInstance,\n stepInstance: StepInstance,\n stepDef: any,\n context: StepExecutionContext,\n branch?: WorkflowBranchInstance | null\n): Promise<StepExecutionResult> {\n const timerConfig = stepDef.config || stepDef.timerConfig || {}\n\n if (!timerConfig.duration && !timerConfig.until) {\n throw new StepExecutionError(\n 'WAIT_FOR_TIMER requires either \"duration\" (e.g., \"PT5M\") or \"until\" (ISO 8601 datetime)',\n 'TIMER_CONFIG_MISSING',\n { stepId: stepDef.stepId }\n )\n }\n\n const { interpolateVariables } = await import('./activity-executor')\n const resolveTimerValue = (value: unknown): unknown =>\n value ? interpolateVariables(value, context.workflowContext, instance, { mode: context.interpolationMode }) : undefined\n const duration = resolveTimerValue(timerConfig.duration)\n const until = resolveTimerValue(timerConfig.until)\n\n const fireAtMs = timerConfig.until\n ? resolveTimerUntilMs(until, stepDef.stepId)\n : Date.now() + resolveTimerDurationMs(duration, stepDef.stepId)\n\n const delayMs = fireAtMs - Date.now()\n const fireAt = new Date(fireAtMs)\n\n // Immediate-fire path: skip the queue round-trip if the timer is in the past\n if (delayMs <= 0) {\n return {\n status: 'COMPLETED',\n outputData: {\n stepType: 'WAIT_FOR_TIMER',\n timerFiredImmediately: true,\n fireAt,\n duration,\n until,\n },\n }\n }\n\n const now = new Date()\n\n // Enqueue delayed timer job via the shared activity queue.\n // Imported here to avoid a top-level cycle between step-handler and activity-executor.\n const { enqueueTimerJob } = await import('./activity-executor')\n const jobId = await enqueueTimerJob({\n workflowInstanceId: instance.id,\n stepInstanceId: stepInstance.id,\n branchInstanceId: branch ? branch.id : undefined,\n tenantId: instance.tenantId,\n organizationId: instance.organizationId,\n userId: context.userId,\n fireAt: fireAt.toISOString(),\n delayMs,\n })\n\n await logWorkflowEvent(em, {\n workflowInstanceId: instance.id,\n stepInstanceId: stepInstance.id,\n ...(branch ? { branchInstanceId: branch.id } : {}),\n eventType: 'TIMER_AWAITING',\n eventData: {\n fireAt: fireAt.toISOString(),\n duration: duration || null,\n until: until || null,\n jobId,\n },\n userId: context.userId,\n tenantId: instance.tenantId,\n organizationId: instance.organizationId,\n })\n\n if (branch) {\n branch.status = 'PAUSED'\n branch.updatedAt = now\n } else {\n instance.status = 'PAUSED'\n instance.pausedAt = now\n instance.updatedAt = now\n }\n await em.flush()\n\n return {\n status: 'WAITING',\n waitReason: 'TIMER',\n outputData: {\n fireAt,\n duration,\n until,\n jobId,\n },\n }\n}\n\nfunction resolveTimerUntilMs(until: unknown, stepId: string | undefined): number {\n const targetDate =\n until instanceof Date ? until : typeof until === 'string' || typeof until === 'number' ? new Date(until) : null\n if (!targetDate || isNaN(targetDate.getTime())) {\n throw new StepExecutionError(\n `WAIT_FOR_TIMER invalid \"until\" datetime: ${String(until)}`,\n 'TIMER_CONFIG_INVALID',\n { stepId, until }\n )\n }\n return targetDate.getTime()\n}\n\nfunction resolveTimerDurationMs(duration: unknown, stepId: string | undefined): number {\n const invalidDuration = (reason: string) =>\n new StepExecutionError(\n `WAIT_FOR_TIMER invalid \"duration\": ${String(duration)}`,\n 'TIMER_CONFIG_INVALID',\n { stepId, duration, reason }\n )\n if (typeof duration !== 'string') throw invalidDuration('the duration is not a string')\n try {\n return parseDuration(duration)\n } catch (error) {\n throw invalidDuration(error instanceof Error ? error.message : String(error))\n }\n}\n\n/**\n * Handle WAIT_FOR_CONDITION step - pause until a context predicate holds.\n *\n * Evaluates the step's ConditionExpression against the token read context. A\n * predicate that already holds completes the step inline with no queue job at\n * all. Otherwise the token parks and a delayed `kind: 'condition'` poll job is\n * enqueued carrying an ABSOLUTE `deadlineAt`, so neither a slow queue nor a\n * re-enqueue can extend the configured timeout. The event-driven wake\n * (`conditionHandler.wakeConditionWaiters`) is the fast path on top of it.\n */\nasync function handleWaitForConditionStep(\n em: EntityManager,\n instance: WorkflowInstance,\n stepInstance: StepInstance,\n stepDef: any,\n context: StepExecutionContext,\n branch?: WorkflowBranchInstance | null\n): Promise<StepExecutionResult> {\n const config = readWaitForConditionConfig(stepDef)\n\n const data = {\n ...conditionReadContext(instance, branch ?? null),\n ...(context.workflowContext || {}),\n }\n\n const met = await evaluateConditionExpression({\n condition: config.condition,\n data,\n instanceId: instance.id,\n userId: context.userId,\n })\n\n const now = new Date()\n\n if (met) {\n await logWorkflowEvent(em, {\n workflowInstanceId: instance.id,\n stepInstanceId: stepInstance.id,\n ...(branch ? { branchInstanceId: branch.id } : {}),\n eventType: 'CONDITION_MET',\n eventData: { stepId: stepDef.stepId, attempts: 1, waitedMs: 0, wokenBy: 'immediate' },\n userId: context.userId,\n tenantId: instance.tenantId,\n organizationId: instance.organizationId,\n })\n\n return {\n status: 'COMPLETED',\n outputData: {\n stepType: CONDITION_STEP_TYPE,\n conditionMet: true,\n attempts: 1,\n waitedMs: 0,\n wokenBy: 'immediate',\n },\n }\n }\n\n const deadlineAt = new Date(now.getTime() + config.timeoutMs)\n\n const { enqueueConditionCheckJob } = await import('./activity-executor')\n const jobId = await enqueueConditionCheckJob({\n workflowInstanceId: instance.id,\n stepInstanceId: stepInstance.id,\n branchInstanceId: branch ? branch.id : undefined,\n tenantId: instance.tenantId,\n organizationId: instance.organizationId,\n userId: context.userId,\n deadlineAt: deadlineAt.toISOString(),\n attempt: 1,\n delayMs: Math.min(config.pollIntervalMs, config.timeoutMs),\n })\n\n await logWorkflowEvent(em, {\n workflowInstanceId: instance.id,\n stepInstanceId: stepInstance.id,\n ...(branch ? { branchInstanceId: branch.id } : {}),\n eventType: 'CONDITION_AWAITING',\n eventData: {\n stepId: stepDef.stepId,\n condition: config.condition,\n deadlineAt: deadlineAt.toISOString(),\n pollIntervalMs: config.pollIntervalMs,\n onTimeout: config.onTimeout,\n jobId,\n },\n userId: context.userId,\n tenantId: instance.tenantId,\n organizationId: instance.organizationId,\n })\n\n if (branch) {\n branch.status = 'PAUSED'\n branch.updatedAt = now\n } else {\n instance.status = 'PAUSED'\n instance.pausedAt = now\n instance.updatedAt = now\n }\n await em.flush()\n\n return {\n status: 'WAITING',\n waitReason: 'CONDITION',\n outputData: {\n stepType: CONDITION_STEP_TYPE,\n deadlineAt: deadlineAt.toISOString(),\n pollIntervalMs: config.pollIntervalMs,\n onTimeout: config.onTimeout,\n jobId,\n },\n }\n}\n\n// ============================================================================\n// Helper Functions\n// ============================================================================\n\n// parseDuration is imported from ./duration\n\n/**\n * Log step-related event to event sourcing table\n */\nasync function logStepEvent(\n em: EntityManager,\n event: {\n workflowInstanceId: string\n stepInstanceId: string\n branchInstanceId?: string | null\n eventType: string\n eventData: any\n userId?: string\n tenantId: string\n organizationId: string\n }\n): Promise<WorkflowEvent> {\n const workflowEvent = em.create(WorkflowEvent, {\n ...event,\n occurredAt: new Date(),\n })\n\n await em.persist(workflowEvent).flush()\n return workflowEvent\n}\n\n/**\n * Get nested value from object using dot notation\n *\n * @param obj - Source object\n * @param path - Dot-notation path (e.g., \"user.email\")\n * @returns Value at path or undefined\n */\nfunction getNestedValue(obj: any, path: string): any {\n return path.split('.').reduce((current, key) => current?.[key], obj)\n}\n\n/**\n * Set nested value in object using dot notation\n *\n * @param obj - Target object\n * @param path - Dot-notation path (e.g., \"user.email\")\n * @param value - Value to set\n */\nfunction setNestedValue(obj: any, path: string, value: any): void {\n const keys = path.split('.')\n const lastKey = keys.pop()!\n const target = keys.reduce((current, key) => {\n if (!(key in current)) current[key] = {}\n return current[key]\n }, obj)\n target[lastKey] = value\n}\n\n/**\n * Map data from source context using mapping configuration\n *\n * @param sourceContext - Source data object\n * @param mapping - Mapping configuration (targetKey -> sourcePath)\n * @returns Mapped data object\n */\nfunction mapInputData(\n sourceContext: Record<string, any>,\n mapping: Record<string, string>\n): Record<string, any> {\n const result: Record<string, any> = {}\n\n for (const [targetKey, sourcePath] of Object.entries(mapping)) {\n const value = getNestedValue(sourceContext, sourcePath)\n if (value !== undefined) {\n setNestedValue(result, targetKey, value)\n }\n }\n\n // If no mapping provided, pass entire context\n return Object.keys(result).length > 0 ? result : sourceContext\n}\n\n/**\n * Map output data from child context back to parent\n *\n * @param childContext - Child workflow context\n * @param mapping - Mapping configuration (targetKey -> sourcePath)\n * @returns Mapped output data\n */\nfunction mapOutputData(\n childContext: Record<string, any>,\n mapping: Record<string, string>\n): Record<string, any> {\n const result: Record<string, any> = {}\n\n for (const [targetKey, sourcePath] of Object.entries(mapping)) {\n const value = getNestedValue(childContext, sourcePath)\n if (value !== undefined) {\n setNestedValue(result, targetKey, value)\n }\n }\n\n // If no mapping provided, pass entire child context\n return Object.keys(result).length > 0 ? result : childContext\n}\n\n/**\n * Validate a completed sub-workflow's child context against the child's\n * declared output ports, then map it to parent output. Shared by the\n * synchronous (inline) completion branch in `handleSubWorkflowStep` and the\n * async parent resume path so both apply identical mapping/validation rules.\n *\n * Output ports are named in the CHILD's vocabulary, while `outputMapping` is\n * `{ parentKey: childPath }`, so validation and coercion run on the child\n * context before mapping \u2014 validating the mapped result would look ports up\n * under caller-chosen parent keys and reject any renamed mapping. Returns the\n * mapped `outputData` on success or an `error` message on port-validation\n * failure (never throws).\n */\nexport function mapSubWorkflowOutput(\n childContext: Record<string, any>,\n outputMapping: Record<string, string>,\n ioContract?: WorkflowIoContract\n): { outputData: Record<string, any>; error?: undefined } | { outputData?: undefined; error: string } {\n let childOutput = childContext\n\n if (ioContract?.outputs?.length) {\n const { coerced, errors } = validateAgainstPorts(childContext, ioContract.outputs)\n if (errors.length > 0) {\n return { error: `Sub-workflow output validation failed: ${errors.map((e) => e.message).join('; ')}` }\n }\n childOutput = coerced\n }\n\n return { outputData: mapOutputData(childOutput, outputMapping || {}) }\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["/**\n * Workflows Module - Step Handler Service\n *\n * Handles individual workflow step execution:\n * - Creating step instances when entering a step\n * - Executing step logic based on step type (START, END, AUTOMATED, USER_TASK)\n * - Completing step instances when exiting\n *\n * Functional API (no classes) following Open Mercato conventions.\n */\n\nimport { EntityManager } from '@mikro-orm/core'\nimport {\n WorkflowInstance,\n WorkflowBranchInstance,\n WorkflowDefinition,\n StepInstance,\n UserTask,\n WorkflowEvent,\n type StepInstanceStatus,\n type WorkflowStepType,\n} from '../data/entities'\nimport { emitWorkflowsEvent } from '../events'\nimport { calculateDueDate, parseDuration } from './duration'\nimport {\n CONDITION_STEP_TYPE,\n conditionReadContext,\n evaluateConditionExpression,\n readWaitForConditionConfig,\n} from './condition-handler'\nimport { mapAgentResultToContext } from './agent-result-mapping'\nimport {\n WORKFLOW_AGENT_OUTCOME_CONTEXT_KEY,\n buildAgentOutcomeContextEntry,\n mapDispositionToAgentOutcome,\n} from './outcome-routing'\nimport { logWorkflowEvent } from './event-logger'\nimport { DRY_RUN_EVENT_TYPES, isDryRunInstance } from './dry-run'\nimport { findDefinitionForInstance } from './find-definition'\nimport { resolveDefinitionInterpolationMode, type WorkflowInterpolationMode } from './interpolation-pipeline'\nimport {\n collectResolvedEntityTypes,\n flattenTaskText,\n resolveTaskAssignment,\n resolveTaskDeadlineDuration,\n resolveTaskDecisions,\n resolveTaskEntityBindings,\n resolveTaskText,\n type TaskInterpolate,\n} from './task-resolution'\nimport { scheduleUserTaskSla } from './task-sla'\nimport type { TaskQuickActionInput } from './task-quick-action'\nimport { createLogger } from '@open-mercato/shared/lib/logger'\nimport { findWorkflowDefinition } from './find-definition'\nimport { validateAgainstPorts } from './port-contract'\nimport type { WorkflowIoContract } from '../data/validators'\nimport { safeGetNestedValue, safeSetNestedValue } from './safe-mapping-path'\n\nconst logger = createLogger('workflows')\n\n// ============================================================================\n// Types and Interfaces\n// ============================================================================\n\nexport interface StepExecutionContext {\n workflowContext: Record<string, any>\n userId?: string\n triggerData?: any\n // Populated by executeStep from the loaded definition; absent means lenient.\n interpolationMode?: WorkflowInterpolationMode\n // Populated by executeStep from the loaded definition so task notifications\n // can name the workflow the way its author did.\n workflowName?: string\n}\n\nexport interface StepExecutionResult {\n status: 'COMPLETED' | 'WAITING' | 'FAILED'\n outputData?: any\n nextSteps?: string[] // For parallel forks (Phase 7)\n waitReason?: 'USER_TASK' | 'SIGNAL' | 'TIMER' | 'FORK' | 'CONDITION'\n error?: string\n}\n\nexport class StepExecutionError extends Error {\n constructor(\n message: string,\n public code: string,\n public details?: any\n ) {\n super(message)\n this.name = 'StepExecutionError'\n }\n}\n\n// ============================================================================\n// Main Step Execution Functions\n// ============================================================================\n\n/**\n * Enter a workflow step - create step instance and mark as ACTIVE\n *\n * @param em - Entity manager\n * @param instance - Workflow instance\n * @param stepId - Step ID to enter\n * @param context - Execution context\n * @returns Created step instance\n */\nexport async function enterStep(\n em: EntityManager,\n instance: WorkflowInstance,\n stepId: string,\n context: StepExecutionContext,\n branch?: WorkflowBranchInstance | null\n): Promise<StepInstance> {\n // Load workflow definition to get step details\n const definition = await findDefinitionForInstance(em, instance)\n\n if (!definition) {\n throw new StepExecutionError(\n `Workflow definition not found: ${instance.definitionId}`,\n 'DEFINITION_NOT_FOUND',\n { definitionId: instance.definitionId }\n )\n }\n\n // Find step in definition\n const stepDef = definition.definition.steps.find((s: any) => s.stepId === stepId)\n if (!stepDef) {\n throw new StepExecutionError(\n `Step not found in workflow definition: ${stepId}`,\n 'STEP_NOT_FOUND',\n { workflowId: definition.workflowId, stepId }\n )\n }\n\n const now = new Date()\n\n // Create step instance\n const stepInstance = em.create(StepInstance, {\n workflowInstanceId: instance.id,\n branchInstanceId: branch ? branch.id : null,\n stepId: stepDef.stepId,\n stepName: stepDef.stepName,\n stepType: stepDef.stepType,\n status: 'ACTIVE',\n inputData: context.triggerData || null,\n enteredAt: now,\n retryCount: 0,\n tenantId: instance.tenantId,\n organizationId: instance.organizationId,\n createdAt: now,\n updatedAt: now,\n })\n\n await em.persist(stepInstance).flush()\n\n // Log STEP_ENTERED event\n await logStepEvent(em, {\n workflowInstanceId: instance.id,\n stepInstanceId: stepInstance.id,\n ...(branch ? { branchInstanceId: branch.id } : {}),\n eventType: 'STEP_ENTERED',\n eventData: {\n stepId: stepDef.stepId,\n stepName: stepDef.stepName,\n stepType: stepDef.stepType,\n },\n userId: context.userId,\n tenantId: instance.tenantId,\n organizationId: instance.organizationId,\n })\n\n return stepInstance\n}\n\n/**\n * Exit a workflow step - mark as completed and record timing\n *\n * @param em - Entity manager\n * @param stepInstance - Step instance to exit\n * @param outputData - Optional output data from step execution\n */\n/**\n * Announces the BUSINESS milestone a completing step carries, if it carries one.\n *\n * Resolved here rather than plumbed through every `exitStep` caller because a\n * milestone must be announced from EVERY completion path \u2014 the inline advance,\n * a signal resume, a task completion, a timer, a condition wake, a parallel\n * branch \u2014 and a per-caller argument would silently miss whichever path was\n * added next. Both reads are by primary key and the executor already loaded both\n * rows, so in practice they resolve from the identity map rather than the\n * database.\n *\n * Best-effort by construction: a step that completed HAS completed, and a failure\n * to announce that fact must never undo it.\n */\nasync function emitStepMilestone(em: EntityManager, stepInstance: StepInstance): Promise<void> {\n try {\n const instance = await em.findOne(WorkflowInstance, { id: stepInstance.workflowInstanceId })\n if (!instance) return\n const definition = await em.findOne(WorkflowDefinition, { id: instance.definitionId })\n const stepDef = definition?.definition?.steps?.find((step: any) => step.stepId === stepInstance.stepId)\n const milestoneKey = typeof stepDef?.milestone === 'string' ? stepDef.milestone : null\n if (!milestoneKey) return\n await emitWorkflowsEvent(\n 'workflows.instance.milestone_reached',\n {\n instanceId: instance.id,\n workflowId: instance.workflowId,\n milestoneKey,\n // For the trace only. A consumer matches on `milestoneKey`; matching on\n // the step is exactly the coupling the milestone model removes.\n stepId: stepInstance.stepId,\n occurredAt: new Date().toISOString(),\n data: stepInstance.outputData ?? null,\n tenantId: instance.tenantId,\n organizationId: instance.organizationId,\n },\n { persistent: true },\n )\n } catch {\n // ignore \u2014 announcing a milestone can never fail a step that completed\n }\n}\n\n/**\n * `step_instances.execution_time_ms` is a Postgres `integer`, so a step that\n * lasted 2^31 ms (~24.86 days) or longer cannot be stored there \u2014 the flush\n * that completes the step would throw after its task already committed,\n * stranding the run. Such a duration persists as `null` (every reader already\n * falls back to `exitedAt - enteredAt`); the exact value still travels on the\n * `STEP_EXITED` event, whose payload is jsonb.\n */\nexport const MAX_PERSISTED_EXECUTION_TIME_MS = 2_147_483_647\n\nexport function toPersistableExecutionTimeMs(executionTimeMs: number | null): number | null {\n if (executionTimeMs === null || !Number.isFinite(executionTimeMs)) return null\n return Math.abs(executionTimeMs) > MAX_PERSISTED_EXECUTION_TIME_MS ? null : executionTimeMs\n}\n\nexport async function exitStep(\n em: EntityManager,\n stepInstance: StepInstance,\n outputData?: any\n): Promise<void> {\n const now = new Date()\n\n // Calculate execution time if we have enteredAt\n let executionTimeMs: number | null = null\n if (stepInstance.enteredAt) {\n executionTimeMs = now.getTime() - stepInstance.enteredAt.getTime()\n }\n\n // Update step instance\n stepInstance.status = 'COMPLETED'\n stepInstance.outputData = outputData || null\n stepInstance.exitedAt = now\n stepInstance.executionTimeMs = toPersistableExecutionTimeMs(executionTimeMs)\n stepInstance.updatedAt = now\n\n await em.flush()\n\n // Log STEP_EXITED event\n await logStepEvent(em, {\n workflowInstanceId: stepInstance.workflowInstanceId,\n stepInstanceId: stepInstance.id,\n eventType: 'STEP_EXITED',\n eventData: {\n stepId: stepInstance.stepId,\n status: 'COMPLETED',\n executionTimeMs,\n hasOutput: !!outputData,\n },\n tenantId: stepInstance.tenantId,\n organizationId: stepInstance.organizationId,\n })\n\n await emitStepMilestone(em, stepInstance)\n}\n\n/**\n * Mark a step instance FAILED and log `STEP_FAILED`.\n *\n * The ONE place a step row becomes FAILED, so a failure the run went on to\n * tolerate leaves the same durable trace a fatal one does. Tolerating a failure\n * is a routing decision, not a reason to forget it happened: without the row and\n * the event there is nothing for the run verdict to be computed from, and\n * rerun-from-step refuses the step it exists for because its latest attempt\n * still reads ACTIVE.\n *\n * Idempotent \u2014 a second call on an already-FAILED row is a no-op, so a handler\n * that reported the failure itself never produces two `STEP_FAILED` rows.\n */\nexport async function markStepInstanceFailed(\n em: EntityManager,\n stepInstance: StepInstance,\n failure: {\n error: string\n details?: unknown\n branchInstanceId?: string | null\n userId?: string\n }\n): Promise<void> {\n if (stepInstance.status === 'FAILED') return\n\n const now = new Date()\n stepInstance.status = 'FAILED'\n stepInstance.errorData = {\n error: failure.error,\n details: failure.details ?? undefined,\n }\n stepInstance.exitedAt = now\n stepInstance.updatedAt = now\n await em.flush()\n\n await logStepEvent(em, {\n workflowInstanceId: stepInstance.workflowInstanceId,\n stepInstanceId: stepInstance.id,\n ...(failure.branchInstanceId ? { branchInstanceId: failure.branchInstanceId } : {}),\n eventType: 'STEP_FAILED',\n eventData: { stepId: stepInstance.stepId, error: failure.error },\n ...(failure.userId ? { userId: failure.userId } : {}),\n tenantId: stepInstance.tenantId,\n organizationId: stepInstance.organizationId,\n })\n}\n\n/**\n * Execute a workflow step based on its type\n *\n * Main entry point for step execution. Handles:\n * - START: Immediate completion\n * - END: Workflow completion\n * - AUTOMATED: Activity execution (MVP: immediate completion)\n * - USER_TASK: Create user task and wait\n * - SUB_WORKFLOW: Invoke child workflow\n *\n * @param em - Entity manager\n * @param instance - Workflow instance\n * @param stepId - Step ID to execute\n * @param context - Execution context\n * @param container - DI container (required for SUB_WORKFLOW steps)\n * @returns Execution result with status and output\n */\nexport async function executeStep(\n em: EntityManager,\n instance: WorkflowInstance,\n stepId: string,\n context: StepExecutionContext,\n container?: any,\n branch?: WorkflowBranchInstance | null\n): Promise<StepExecutionResult> {\n try {\n // Enter the step (create step instance)\n const stepInstance = await enterStep(em, instance, stepId, context, branch)\n\n // Load workflow definition to get step configuration\n const definition = await findDefinitionForInstance(em, instance)\n\n if (!definition) {\n throw new StepExecutionError(\n `Workflow definition not found: ${instance.definitionId}`,\n 'DEFINITION_NOT_FOUND',\n { definitionId: instance.definitionId }\n )\n }\n\n const stepDef = definition.definition.steps.find((s: any) => s.stepId === stepId)\n if (!stepDef) {\n throw new StepExecutionError(\n `Step not found: ${stepId}`,\n 'STEP_NOT_FOUND',\n { stepId }\n )\n }\n\n // Execute based on step type\n const result = await executeStepByType(\n em,\n instance,\n stepInstance,\n stepDef,\n {\n ...context,\n interpolationMode: resolveDefinitionInterpolationMode(definition.definition),\n workflowName: definition.workflowName,\n },\n container,\n branch\n )\n\n // If step completed, exit it\n if (result.status === 'COMPLETED') {\n await exitStep(em, stepInstance, result.outputData)\n } else if (result.status === 'FAILED') {\n // A handler that REPORTS a failure (`handleAutomatedStep` returns\n // `{status:'FAILED'}` for a failed sync activity) never reaches the catch\n // below, so before this the row stayed ACTIVE for ever and no\n // `STEP_FAILED` was logged \u2014 the failure existed only in a value the\n // caller was free to tolerate. The row is now terminal on both paths.\n await markStepInstanceFailed(em, stepInstance, {\n error: result.error ?? 'Step execution failed',\n details: result.outputData,\n branchInstanceId: branch?.id ?? null,\n userId: context.userId,\n })\n }\n\n return result\n } catch (error) {\n // Handle step execution errors\n const errorMessage = error instanceof Error ? error.message : String(error)\n\n // Try to mark step as failed if we have a step instance\n try {\n const failedStepInstance = await em.findOne(StepInstance, {\n workflowInstanceId: instance.id,\n stepId,\n status: 'ACTIVE',\n })\n\n if (failedStepInstance) {\n await markStepInstanceFailed(em, failedStepInstance, {\n error: errorMessage,\n details: error instanceof StepExecutionError ? error.details : undefined,\n branchInstanceId: branch?.id ?? null,\n userId: context.userId,\n })\n }\n } catch (updateError) {\n // Swallow update errors to preserve original error\n logger.error('Failed to update step instance with error', { err: updateError })\n }\n\n return {\n status: 'FAILED',\n error: errorMessage,\n }\n }\n}\n\n// ============================================================================\n// Step Type Handlers\n// ============================================================================\n\n/**\n * Execute step based on its type\n *\n * @param em - Entity manager\n * @param instance - Workflow instance\n * @param stepInstance - Step instance\n * @param stepDef - Step definition from workflow\n * @param context - Execution context\n * @returns Execution result\n */\nasync function executeStepByType(\n em: EntityManager,\n instance: WorkflowInstance,\n stepInstance: StepInstance,\n stepDef: any,\n context: StepExecutionContext,\n container?: any,\n branch?: WorkflowBranchInstance | null\n): Promise<StepExecutionResult> {\n const stepType: WorkflowStepType = stepDef.stepType\n\n switch (stepType) {\n case 'START':\n return handleStartStep(stepDef, context)\n\n case 'END':\n return handleEndStep(stepDef, context)\n\n case 'AUTOMATED':\n return await handleAutomatedStep(em, instance, stepInstance, stepDef, context, container, branch)\n\n case 'USER_TASK':\n return await handleUserTaskStep(em, instance, stepInstance, stepDef, context, branch)\n\n case 'SUB_WORKFLOW':\n if (!container) {\n throw new StepExecutionError(\n 'Container required for SUB_WORKFLOW execution',\n 'CONTAINER_REQUIRED',\n { stepType }\n )\n }\n return await handleSubWorkflowStep(em, container, instance, stepInstance, stepDef, context)\n\n case 'IF_ELSE':\n case 'SWITCH':\n return handleBranchingStep(stepType)\n\n case 'WAIT_FOR_SIGNAL':\n return await handleWaitForSignalStep(em, instance, stepInstance, stepDef, context, branch)\n\n case 'WAIT_FOR_TIMER':\n return await handleWaitForTimerStep(em, instance, stepInstance, stepDef, context, branch)\n\n case 'WAIT_FOR_CONDITION':\n return await handleWaitForConditionStep(em, instance, stepInstance, stepDef, context, branch)\n\n case 'PARALLEL_FORK': {\n // Entering a fork opens branch tokens and parks the root token in the\n // FORKED state; the interleaved loop in the executor drives the branches.\n if (branch) {\n // Nested forks are rejected by definition validation; fail closed.\n throw new StepExecutionError(\n 'Nested PARALLEL_FORK is not supported',\n 'NESTED_FORK_NOT_SUPPORTED',\n { stepType, stepId: stepDef.stepId }\n )\n }\n const definition = await findDefinitionForInstance(em, instance)\n if (!definition) {\n throw new StepExecutionError(\n `Workflow definition not found: ${instance.definitionId}`,\n 'DEFINITION_NOT_FOUND',\n { definitionId: instance.definitionId }\n )\n }\n const { openFork } = await import('./parallel-handler')\n await openFork(em, instance, definition, stepDef)\n return { status: 'WAITING', waitReason: 'FORK', outputData: { stepType: 'PARALLEL_FORK', forkStepId: stepDef.stepId } }\n }\n\n case 'PARALLEL_JOIN':\n // The join is a synchronization point handled by the parallel loop\n // (branches are marked COMPLETED on arrival; the loop fires the join).\n // Executing the step itself is a no-op.\n return { status: 'COMPLETED', outputData: { stepType: 'PARALLEL_JOIN', timestamp: new Date().toISOString() } }\n\n default:\n throw new StepExecutionError(\n `Unknown step type: ${stepType}`,\n 'UNKNOWN_STEP_TYPE',\n { stepType }\n )\n }\n}\n\n/**\n * Handle START step - no-op, immediately complete\n */\nfunction handleStartStep(\n stepDef: any,\n context: StepExecutionContext\n): StepExecutionResult {\n return {\n status: 'COMPLETED',\n outputData: {\n stepType: 'START',\n timestamp: new Date().toISOString(),\n },\n }\n}\n\n/**\n * Handle END step - mark as complete\n */\nfunction handleEndStep(\n stepDef: any,\n context: StepExecutionContext\n): StepExecutionResult {\n return {\n status: 'COMPLETED',\n outputData: {\n stepType: 'END',\n timestamp: new Date().toISOString(),\n finalContext: context.workflowContext,\n },\n }\n}\n\n/**\n * Handle IF_ELSE / SWITCH steps - pure transition sugar.\n *\n * Branching nodes carry NO runtime semantics of their own: they behave exactly\n * like an AUTOMATED step with no activities and complete immediately. Every\n * routing decision stays in `findValidTransitions`, which already evaluates the\n * business_rules condition language and orders candidates by priority, so the\n * unconditioned \"otherwise\" route wins only when no cased route matches.\n */\nfunction handleBranchingStep(stepType: 'IF_ELSE' | 'SWITCH'): StepExecutionResult {\n return {\n status: 'COMPLETED',\n outputData: {\n stepType,\n timestamp: new Date().toISOString(),\n },\n }\n}\n\n/**\n * Handle AUTOMATED step - execute activities\n *\n * Executes activities defined in step configuration.\n * Supports both sync and async activities.\n */\nasync function handleAutomatedStep(\n em: EntityManager,\n instance: WorkflowInstance,\n stepInstance: StepInstance,\n stepDef: any,\n context: StepExecutionContext,\n container?: any,\n branch?: WorkflowBranchInstance | null\n): Promise<StepExecutionResult> {\n // Extract activities from step definition\n const activities = stepDef.activities || []\n\n if (activities.length === 0) {\n // No activities defined - immediate completion (legacy behavior)\n return {\n status: 'COMPLETED',\n outputData: {\n stepType: 'AUTOMATED',\n timestamp: new Date().toISOString(),\n },\n }\n }\n\n // Import activity executor\n const { executeActivities, interpolateVariables } = await import('./activity-executor')\n\n // Persist the resolved INVOKE_AGENT input so the run inspector shows what this\n // step actually received. `inputData` was seeded at step entry from the\n // workflow trigger data (null for a manually-started run), never the agent's\n // real, interpolated `config.input` \u2014 the same value the executor computes via\n // `interpolateActivityConfig` (a thin wrapper over `interpolateVariables`), so\n // re-interpolating the input field here yields exactly what the agent runs on.\n const agentActivity = activities.find(\n (activity: any) => activity.activityType === 'INVOKE_AGENT' && activity?.config?.input !== undefined,\n )\n if (agentActivity) {\n try {\n const resolvedInput = interpolateVariables(\n (agentActivity as any).config.input,\n context.workflowContext,\n instance,\n { mode: context.interpolationMode },\n )\n if (resolvedInput !== undefined) {\n stepInstance.inputData = resolvedInput\n stepInstance.updatedAt = new Date()\n await em.flush()\n }\n } catch {\n // Display-only capture: an interpolation error is surfaced by the actual\n // activity run below, so it must never abort the step here.\n }\n }\n\n try {\n // Execute activities with proper context\n const results = await executeActivities(em, container, activities, {\n workflowInstance: instance,\n workflowContext: context.workflowContext,\n stepContext: { stepId: stepDef.stepId, stepName: stepDef.stepName },\n stepInstanceId: stepInstance.id,\n userId: context.userId,\n interpolationMode: context.interpolationMode,\n })\n\n // Check if there are pending async activities\n const pendingActivities = results.filter(r => r.async && !r.success)\n if (pendingActivities.length > 0) {\n // Workflow should pause and wait for async activities\n const now = new Date()\n if (branch) {\n branch.status = 'WAITING_FOR_ACTIVITIES'\n branch.updatedAt = now\n } else {\n instance.status = 'WAITING_FOR_ACTIVITIES'\n instance.pausedAt = now\n instance.updatedAt = now\n }\n await em.flush()\n\n return {\n status: 'WAITING',\n waitReason: 'SIGNAL', // Reuse SIGNAL wait reason (will be resumed by activity completion)\n outputData: {\n pendingActivities: pendingActivities.map(r => ({\n activityId: r.activityId,\n activityName: r.activityName,\n jobId: r.jobId,\n })),\n },\n }\n }\n\n // Check for failures in sync activities\n const failures = results.filter(r => !r.success && !r.async)\n if (failures.length > 0) {\n const errorMessages = failures.map(f => `${f.activityName || f.activityId}: ${f.error}`).join('; ')\n return {\n status: 'FAILED',\n error: `${failures.length} activity(ies) failed: ${errorMessages}`,\n outputData: {\n failures: failures.map(f => ({\n activityId: f.activityId,\n activityName: f.activityName,\n error: f.error,\n retryCount: f.retryCount,\n })),\n },\n }\n }\n\n // INVOKE_AGENT (agent_orchestrator) integration:\n // A `__park` marker means the agent's proposal was routed to a human \u2014 park\n // the instance on the signal exactly like handleWaitForSignalStep. The step\n // declares `signalConfig.signalName`, so sendSignal (relaxed) resumes it when\n // agent_orchestrator's dispose path fires `agent_orchestrator.proposal.ready`.\n const parkResult = results.find((r) => r.output && (r.output as any).__park)\n if (parkResult) {\n const signalName = (parkResult.output as any).__park.signalName as string\n const proposalId = (parkResult.output as any).proposalId\n const now = new Date()\n await logStepEvent(em, {\n workflowInstanceId: instance.id,\n stepInstanceId: stepInstance.id,\n ...(branch ? { branchInstanceId: branch.id } : {}),\n eventType: 'SIGNAL_AWAITING',\n eventData: { signalName, proposalId, reason: 'INVOKE_AGENT' },\n userId: context.userId,\n tenantId: instance.tenantId,\n organizationId: instance.organizationId,\n })\n if (branch) {\n branch.status = 'PAUSED'\n branch.updatedAt = now\n } else {\n instance.status = 'PAUSED'\n instance.pausedAt = now\n instance.updatedAt = now\n }\n await em.flush()\n return {\n status: 'WAITING',\n waitReason: 'SIGNAL',\n outputData: { signalName, proposalId, awaitingSince: now },\n }\n }\n\n // Inline-resolved agent result (auto_approved / researcher / artifact):\n // surface the disposition into context (top-level, matching the human-path\n // signal merge) so the outgoing transition can branch (effector vs skip)\n // uniformly.\n const inlineAgent = results.find(\n (r) => r.output && typeof (r.output as any).kind === 'string' && 'agentId' in (r.output as any),\n )\n if (inlineAgent && !branch) {\n const out = inlineAgent.output as any\n const agentActivity = activities.find(\n (a: any) => a.activityType === 'INVOKE_AGENT' && a.activityId === inlineAgent.activityId,\n )\n const mappedPatch = mapAgentResultToContext(\n {\n kind: out.kind,\n agentId: out.agentId,\n proposalId: out.proposalId,\n proposalPayload: out.proposalPayload,\n data: out.data,\n artifacts: out.artifacts,\n },\n agentActivity?.config?.outputMapping,\n )\n const contextPatch =\n mappedPatch ??\n (out.kind === 'auto_approved'\n ? {\n disposition: 'auto_approved',\n agentProposalId: out.proposalId,\n proposalPayload: out.proposalPayload,\n }\n : out.kind === 'artifact'\n ? {\n disposition: 'researcher',\n [`${stepInstance.stepId}_agent`]: { artifacts: out.artifacts, summary: out.summary },\n }\n : null)\n // Outcome routing (spec 7.2): record which of the five fixed disposition\n // kinds this step resolved to under an ENGINE-OWNED context key. The\n // executor routes on this marker alone \u2014 never on the author-visible\n // `disposition` key \u2014 which is what makes branching on a disposition\n // wiring rather than context string-matching.\n const resolvedOutcome = mapDispositionToAgentOutcome(out.kind)\n const outcomeMarker = resolvedOutcome\n ? {\n [WORKFLOW_AGENT_OUTCOME_CONTEXT_KEY]: buildAgentOutcomeContextEntry(\n stepInstance.stepId,\n resolvedOutcome,\n out.proposalId,\n ),\n }\n : null\n\n const nextContext = { ...instance.context, ...(contextPatch ?? {}), ...(outcomeMarker ?? {}) }\n if (contextPatch || outcomeMarker) {\n instance.context = nextContext\n instance.updatedAt = new Date()\n await em.flush()\n }\n }\n\n // All activities completed successfully\n const activityOutputs = results.reduce((acc, r) => {\n if (r.output) {\n acc[r.activityId] = r.output\n }\n return acc\n }, {} as Record<string, any>)\n\n return {\n status: 'COMPLETED',\n outputData: {\n stepType: 'AUTOMATED',\n timestamp: new Date().toISOString(),\n activityResults: activityOutputs,\n activityCount: results.length,\n },\n }\n } catch (error: any) {\n return {\n status: 'FAILED',\n error: `Activity execution failed: ${error.message}`,\n }\n }\n}\n\n/**\n * Resolve a user task's deadline from its authored duration.\n *\n * An unparseable duration fails the step with an actionable message instead of\n * quietly resolving to some default the author never asked for.\n */\nfunction resolveUserTaskDeadline(stepId: string, slaDuration: string): Date {\n try {\n return calculateDueDate(slaDuration)\n } catch (error) {\n throw new StepExecutionError(\n `Invalid user task deadline \"${slaDuration}\": ${error instanceof Error ? error.message : String(error)}`,\n 'INVALID_USER_TASK_DEADLINE',\n { stepId, slaDuration }\n )\n }\n}\n\n/**\n * Publish the declared `workflows.task.assigned` domain event for a freshly\n * created task, alongside (never instead of) the internal `USER_TASK_CREATED`\n * audit row. It carries both the individual assignee and the role queue so the\n * notification subscriber can reach role-assigned tasks too.\n *\n * Delivery is at-least-once and consumers must be idempotent; a bus failure\n * must never break workflow execution, so this is strictly best-effort.\n */\nasync function emitTaskAssignedEvent(\n userTask: UserTask,\n instance: WorkflowInstance,\n context: StepExecutionContext,\n quickAction?: TaskQuickActionInput\n): Promise<void> {\n try {\n await emitWorkflowsEvent(\n 'workflows.task.assigned',\n {\n // Everything the notification quick-action rule needs, and nothing more.\n // ABSENT means \"unknown\", and the subscriber falls back to the deep\n // link \u2014 a quick action must never be offered on a guess.\n ...(quickAction ? { quickAction } : {}),\n taskId: userTask.id,\n taskName: userTask.taskName,\n workflowInstanceId: instance.id,\n workflowId: instance.workflowId,\n workflowName: context.workflowName ?? instance.workflowId,\n assignedUserId: userTask.assignedTo ?? null,\n assignedToRoles: userTask.assignedToRoles ?? null,\n dueDate: userTask.dueDate ? userTask.dueDate.toISOString() : null,\n // Additive: the records the task is about. Modules that own those\n // records subscribe and surface the task on their own turf \u2014 the\n // customers module writes a `CustomerTodoLink` from this. Workflows\n // never reaches into another module's tables to do it itself.\n entityBindings: userTask.entityBindings ?? null,\n tenantId: instance.tenantId,\n organizationId: instance.organizationId ?? null,\n },\n { persistent: true }\n )\n } catch (error) {\n logger.error('Failed to emit workflows.task.assigned', {\n component: 'step-handler',\n taskId: userTask.id,\n err: error,\n })\n }\n\n if (userTask.assigneeKind !== 'customer' || !userTask.assignedTo) return\n\n try {\n // Addressed to ONE portal principal (`recipientUserId` is what narrows the\n // SSE audience) and carrying an id and nothing else. The receiver asks the\n // authorized portal API what the task is; the bridge never carries the\n // answer, so a mis-scoped connection learns nothing from it.\n await emitWorkflowsEvent(\n 'workflows.task.portal_assigned',\n {\n taskId: userTask.id,\n recipientUserId: userTask.assignedTo,\n tenantId: instance.tenantId,\n organizationId: instance.organizationId ?? null,\n },\n { persistent: false }\n )\n } catch (error) {\n logger.error('Failed to emit workflows.task.portal_assigned', {\n component: 'step-handler',\n taskId: userTask.id,\n err: error,\n })\n }\n}\n\n/**\n * Handle USER_TASK step - create user task and enter waiting state\n *\n * Creates a UserTask entity and returns WAITING status.\n * The workflow will pause until the task is completed by a user.\n */\nasync function handleUserTaskStep(\n em: EntityManager,\n instance: WorkflowInstance,\n stepInstance: StepInstance,\n stepDef: any,\n context: StepExecutionContext,\n branch?: WorkflowBranchInstance | null\n): Promise<StepExecutionResult> {\n const userTaskConfig = stepDef.userTaskConfig || {}\n\n // Everything an author can write on a task \u2014 its title, its instructions, its\n // decision labels, its dynamic assignee and its entity bindings \u2014 is resolved\n // HERE, once, against the context the run has at creation time. Resolving\n // later would let a definition edit retro-change what a running task says.\n // Lenient on purpose: an unresolved dynamic assignee is what the fallback role\n // queue exists for, not a reason to fail the step.\n //\n // Imported dynamically like every other activity-executor use in this file, so\n // the module graph stays acyclic.\n const { interpolateVariables } = await import('./activity-executor')\n const interpolate: TaskInterpolate = (value) =>\n interpolateVariables(value, context.workflowContext, instance)\n\n const assignment = resolveTaskAssignment(userTaskConfig, interpolate)\n if (assignment.fellBackToRoles) {\n logger.warn('Dynamic task assignee resolved to nothing; falling back to the role queue', {\n component: 'step-handler',\n stepId: stepDef.stepId,\n workflowInstanceId: instance.id,\n assignedToRoles: assignment.assignedToRoles,\n })\n }\n if (assignment.fellBackFromCustomer) {\n // The author addressed this to a portal principal and no id resolved, so it\n // landed in the backoffice namespace instead. The work is still reachable,\n // but by a different audience than intended \u2014 that is worth saying out loud.\n logger.warn('Portal task assignee resolved to nothing; task created for the backoffice instead', {\n component: 'step-handler',\n stepId: stepDef.stepId,\n workflowInstanceId: instance.id,\n })\n }\n\n const { bindings, unresolved } = resolveTaskEntityBindings(userTaskConfig.entityBindings, interpolate)\n if (unresolved.length) {\n logger.warn('Task entity bindings skipped because their ids did not resolve', {\n component: 'step-handler',\n stepId: stepDef.stepId,\n workflowInstanceId: instance.id,\n unresolved,\n })\n }\n\n const taskName = String(interpolate(stepDef.stepName) ?? stepDef.stepName)\n const instructions = resolveTaskText(userTaskConfig.instructions, interpolate)\n const decisions = resolveTaskDecisions(userTaskConfig.decisions, interpolate)\n const deadlineDuration = resolveTaskDeadlineDuration(userTaskConfig)\n\n // Create user task\n const now = new Date()\n\n // Dry run (spec section 8.2): a simulation raises NO real task. The row is\n // what everything else hangs off \u2014 the `workflows.task.assigned` event and\n // therefore the notification subscriber, the SLA reminder/breach queue jobs,\n // and every Work Inbox and task-list query \u2014 so suppressing the row is what\n // suppresses all of them, rather than a filter at each read site that a new\n // reader could forget. Everything above still runs, so the report says\n // exactly who the task WOULD have gone to. The step still WAITS: the author\n // simulates the decision through step-through or the advance action.\n if (isDryRunInstance(instance)) {\n await logStepEvent(em, {\n workflowInstanceId: instance.id,\n stepInstanceId: stepInstance.id,\n ...(branch ? { branchInstanceId: branch.id } : {}),\n eventType: DRY_RUN_EVENT_TYPES.userTaskSimulated,\n eventData: {\n taskName,\n assignedTo: assignment.assignedTo,\n assignedToRoles: assignment.assignedToRoles,\n assigneeKind: assignment.assigneeKind,\n ...(bindings.length ? { entityBindings: bindings } : {}),\n ...(decisions.length ? { decisions } : {}),\n ...(deadlineDuration ? { deadlineDuration } : {}),\n },\n tenantId: instance.tenantId,\n organizationId: instance.organizationId,\n })\n\n if (branch) {\n branch.status = 'PAUSED'\n branch.updatedAt = now\n } else {\n instance.status = 'PAUSED'\n instance.updatedAt = now\n }\n await em.flush()\n\n return {\n status: 'WAITING',\n waitReason: 'USER_TASK',\n outputData: { simulated: true, wouldRaiseTask: taskName },\n }\n }\n\n const userTask = em.create(UserTask, {\n workflowInstanceId: instance.id,\n stepInstanceId: stepInstance.id,\n branchInstanceId: branch ? branch.id : null,\n taskName,\n description: flattenTaskText(instructions) ?? stepDef.description ?? null,\n status: 'PENDING',\n formSchema: userTaskConfig.formSchema || null,\n formData: null,\n assignedTo: assignment.assignedTo,\n // Decided by `resolveTaskAssignment` from the authored `assigneeKind`, and\n // `'customer'` only when an individual assignee actually resolved. Written\n // explicitly rather than left to the column default so the discriminator is\n // a decision at every creation site, not an accident.\n assigneeKind: assignment.assigneeKind,\n assignedToRoles: assignment.assignedToRoles,\n entityBindings: bindings.length ? bindings : null,\n // Denormalized from the SAME resolved bindings, so the two can never drift.\n // Null for a task about nothing, which is what the vacuous-pass rule reads.\n entityTypes: bindings.length ? collectResolvedEntityTypes(bindings) : null,\n priority: userTaskConfig.priority ?? null,\n dueDate: deadlineDuration\n ? resolveUserTaskDeadline(stepDef.stepId, deadlineDuration)\n : null,\n tenantId: instance.tenantId,\n organizationId: instance.organizationId,\n createdAt: now,\n updatedAt: now,\n })\n\n await em.persist(userTask).flush()\n\n // Log USER_TASK_CREATED event\n await logStepEvent(em, {\n workflowInstanceId: instance.id,\n stepInstanceId: stepInstance.id,\n ...(branch ? { branchInstanceId: branch.id } : {}),\n eventType: 'USER_TASK_CREATED',\n eventData: {\n userTaskId: userTask.id,\n taskName: userTask.taskName,\n assignedTo: userTask.assignedTo,\n assignedToRoles: userTask.assignedToRoles,\n ...(bindings.length ? { entityBindings: bindings } : {}),\n ...(userTask.priority ? { priority: userTask.priority } : {}),\n ...(decisions.length ? { decisions } : {}),\n ...(assignment.fellBackToRoles ? { assignmentFallback: 'roles' } : {}),\n },\n tenantId: instance.tenantId,\n organizationId: instance.organizationId,\n })\n\n await emitTaskAssignedEvent(userTask, instance, context, {\n decisions: decisions.map((decision) => ({ id: decision.id })),\n formSchema: userTask.formSchema ?? null,\n editablePrefilled: userTaskConfig.editablePrefilled ?? null,\n })\n\n // Reminders and the deadline breach are scheduled HERE, once, with the\n // deadline already absolute on each job payload \u2014 see `lib/task-sla.ts`. A\n // task with no deadline schedules nothing at all.\n await scheduleUserTaskSla({\n workflowInstanceId: instance.id,\n stepInstanceId: stepInstance.id,\n branchInstanceId: branch ? branch.id : null,\n userTaskId: userTask.id,\n dueDate: userTask.dueDate ?? null,\n reminders: userTaskConfig.reminders ?? null,\n tenantId: instance.tenantId,\n organizationId: instance.organizationId,\n userId: context.userId,\n now,\n })\n\n // Pause execution - waits for user task completion. For a branch, only the\n // branch pauses; sibling branches keep running.\n if (branch) {\n branch.status = 'PAUSED'\n branch.updatedAt = now\n } else {\n instance.status = 'PAUSED'\n instance.updatedAt = now\n }\n await em.flush()\n\n return {\n status: 'WAITING',\n waitReason: 'USER_TASK',\n outputData: {\n userTaskId: userTask.id,\n },\n }\n}\n\n/**\n * Handle SUB_WORKFLOW step - invoke another workflow and wait for completion\n *\n * Creates a child workflow instance with mapped input data,\n * executes it synchronously, and returns mapped output data.\n */\nasync function handleSubWorkflowStep(\n em: EntityManager,\n container: any,\n instance: WorkflowInstance,\n stepInstance: StepInstance,\n stepDef: any,\n context: StepExecutionContext\n): Promise<StepExecutionResult> {\n const { subWorkflowId, inputMapping, outputMapping, version } = stepDef.config || {}\n\n if (!subWorkflowId) {\n return {\n status: 'FAILED',\n error: 'Sub-workflow ID not specified in step configuration'\n }\n }\n\n // Map input data from parent context to child context\n let childContext = mapInputData(instance.context, inputMapping || {})\n\n // Import workflow executor functions\n const { startWorkflow, executeWorkflow } = await import('./workflow-executor')\n\n try {\n // Resolve the child definition to read its declared port contract (if any).\n // Validation is opt-in by contract presence: only children that declare\n // `definition.io` ports are checked, so legacy untyped sub-workflows behave\n // exactly as before.\n const childDefinition = await findWorkflowDefinition(em, {\n workflowId: subWorkflowId,\n version,\n tenantId: instance.tenantId,\n organizationId: instance.organizationId,\n })\n const ioContract = (childDefinition?.definition as { io?: WorkflowIoContract } | undefined)?.io\n\n // Validate/coerce mapped inputs against the child's declared input ports.\n if (ioContract?.inputs?.length) {\n const { coerced, errors } = validateAgainstPorts(childContext, ioContract.inputs)\n if (errors.length > 0) {\n const message = `Sub-workflow input validation failed: ${errors.map((e) => e.message).join('; ')}`\n await logStepEvent(em, {\n workflowInstanceId: instance.id,\n stepInstanceId: stepInstance.id,\n eventType: 'SUB_WORKFLOW_FAILED',\n eventData: { subWorkflowId, reason: 'INPUT_VALIDATION', error: message },\n userId: context.userId,\n tenantId: instance.tenantId,\n organizationId: instance.organizationId,\n })\n return { status: 'FAILED', error: message }\n }\n childContext = coerced\n }\n\n // Start child workflow with parent metadata\n const childInstance = await startWorkflow(em, {\n workflowId: subWorkflowId,\n version,\n initialContext: childContext,\n correlationKey: instance.correlationKey || undefined,\n metadata: {\n ...instance.metadata,\n labels: {\n ...instance.metadata?.labels,\n parentInstanceId: instance.id,\n parentStepId: stepDef.stepId,\n parentStepInstanceId: stepInstance.id,\n },\n },\n tenantId: instance.tenantId,\n organizationId: instance.organizationId,\n })\n\n // Log sub-workflow invocation event\n await logStepEvent(em, {\n workflowInstanceId: instance.id,\n stepInstanceId: stepInstance.id,\n eventType: 'SUB_WORKFLOW_STARTED',\n eventData: {\n childInstanceId: childInstance.id,\n subWorkflowId,\n version,\n inputData: childContext,\n },\n userId: context.userId,\n tenantId: instance.tenantId,\n organizationId: instance.organizationId,\n })\n\n // Execute child workflow synchronously\n const result = await executeWorkflow(em, container, childInstance.id, {\n userId: context.userId,\n })\n\n // Handle child workflow result\n if (result.status === 'COMPLETED') {\n // Map output data from child context to parent context (+ io-port\n // validation). Shared with the async resume path so both apply identical\n // mapping/validation rules.\n const mapped = mapSubWorkflowOutput(result.context, outputMapping || {}, ioContract)\n if (mapped.error) {\n await logStepEvent(em, {\n workflowInstanceId: instance.id,\n stepInstanceId: stepInstance.id,\n eventType: 'SUB_WORKFLOW_FAILED',\n eventData: { childInstanceId: childInstance.id, reason: 'OUTPUT_VALIDATION', error: mapped.error },\n userId: context.userId,\n tenantId: instance.tenantId,\n organizationId: instance.organizationId,\n })\n return { status: 'FAILED', error: mapped.error }\n }\n const outputData = mapped.outputData\n\n await logStepEvent(em, {\n workflowInstanceId: instance.id,\n stepInstanceId: stepInstance.id,\n eventType: 'SUB_WORKFLOW_COMPLETED',\n eventData: {\n childInstanceId: childInstance.id,\n outputData,\n },\n userId: context.userId,\n tenantId: instance.tenantId,\n organizationId: instance.organizationId,\n })\n\n return {\n status: 'COMPLETED',\n outputData,\n }\n } else if (result.status === 'FAILED') {\n await logStepEvent(em, {\n workflowInstanceId: instance.id,\n stepInstanceId: stepInstance.id,\n eventType: 'SUB_WORKFLOW_FAILED',\n eventData: {\n childInstanceId: childInstance.id,\n error: result.errors?.join(', '),\n },\n userId: context.userId,\n tenantId: instance.tenantId,\n organizationId: instance.organizationId,\n })\n\n return {\n status: 'FAILED',\n error: `Sub-workflow failed: ${result.errors?.join(', ')}`,\n }\n } else {\n // The child parked at its first async/agent step (RUNNING / PAUSED /\n // WAITING_FOR_ACTIVITIES). Make the SUB_WORKFLOW step suspendable: park the\n // parent on SUB_WORKFLOW_SIGNAL_NAME. The child's terminal completeWorkflow\n // enqueues a resume job that resumes this parent (mirrors the INVOKE_AGENT\n // __park branch above).\n const { SUB_WORKFLOW_SIGNAL_NAME } = await import('./activity-executor')\n const now = new Date()\n await logStepEvent(em, {\n workflowInstanceId: instance.id,\n stepInstanceId: stepInstance.id,\n eventType: 'SIGNAL_AWAITING',\n eventData: {\n signalName: SUB_WORKFLOW_SIGNAL_NAME,\n childInstanceId: childInstance.id,\n reason: 'SUB_WORKFLOW',\n },\n userId: context.userId,\n tenantId: instance.tenantId,\n organizationId: instance.organizationId,\n })\n instance.status = 'PAUSED'\n instance.pausedAt = now\n instance.updatedAt = now\n await em.flush()\n return {\n status: 'WAITING',\n waitReason: 'SIGNAL',\n outputData: { childInstanceId: childInstance.id },\n }\n }\n } catch (error: any) {\n await logStepEvent(em, {\n workflowInstanceId: instance.id,\n stepInstanceId: stepInstance.id,\n eventType: 'SUB_WORKFLOW_FAILED',\n eventData: {\n subWorkflowId,\n error: error.message,\n },\n userId: context.userId,\n tenantId: instance.tenantId,\n organizationId: instance.organizationId,\n })\n\n return {\n status: 'FAILED',\n error: `Sub-workflow execution failed: ${error.message}`,\n }\n }\n}\n\n/**\n * Handle WAIT_FOR_SIGNAL step - pause workflow until signal received\n *\n * Creates a waiting state and pauses the workflow until an external signal\n * with the matching signal name is received. The signal payload will be merged\n * into the workflow context when received.\n */\nasync function handleWaitForSignalStep(\n em: EntityManager,\n instance: WorkflowInstance,\n stepInstance: StepInstance,\n stepDef: any,\n context: StepExecutionContext,\n branch?: WorkflowBranchInstance | null\n): Promise<StepExecutionResult> {\n const signalConfig = stepDef.signalConfig || {}\n const signalName = signalConfig.signalName || stepDef.stepId\n const timeout = signalConfig.timeout ? parseDuration(signalConfig.timeout) : null\n\n const now = new Date()\n\n // Log signal awaiting event\n await logStepEvent(em, {\n workflowInstanceId: instance.id,\n stepInstanceId: stepInstance.id,\n ...(branch ? { branchInstanceId: branch.id } : {}),\n eventType: 'SIGNAL_AWAITING',\n eventData: {\n signalName,\n timeout,\n description: stepDef.description,\n },\n userId: context.userId,\n tenantId: instance.tenantId,\n organizationId: instance.organizationId,\n })\n\n // Pause execution (branch-scoped when running inside a parallel branch)\n if (branch) {\n branch.status = 'PAUSED'\n branch.updatedAt = now\n } else {\n instance.status = 'PAUSED'\n instance.pausedAt = now\n instance.updatedAt = now\n }\n await em.flush()\n\n // Return WAITING status to halt executor\n return {\n status: 'WAITING',\n waitReason: 'SIGNAL',\n outputData: {\n signalName,\n timeout,\n awaitingSince: now,\n },\n }\n}\n\n/**\n * Handle WAIT_FOR_TIMER step - pause workflow until a timer fires.\n *\n * Reads `duration` (relative, e.g. \"PT5M\") or `until` (ISO 8601 datetime) from\n * `stepDef.config` (preferred \u2014 matches StepsEditor) or `stepDef.timerConfig`.\n * Either value may be a `{{...}}` template: the definition validator accepts\n * templates there, so they are resolved here, once, against the run context\n * with the definition's interpolation mode.\n * Enqueues a delayed timer job on the workflow-activities queue; when the job\n * is processed by the activity worker, it calls `timerHandler.fireTimer` to\n * resume the workflow.\n */\nasync function handleWaitForTimerStep(\n em: EntityManager,\n instance: WorkflowInstance,\n stepInstance: StepInstance,\n stepDef: any,\n context: StepExecutionContext,\n branch?: WorkflowBranchInstance | null\n): Promise<StepExecutionResult> {\n const timerConfig = stepDef.config || stepDef.timerConfig || {}\n\n if (!timerConfig.duration && !timerConfig.until) {\n throw new StepExecutionError(\n 'WAIT_FOR_TIMER requires either \"duration\" (e.g., \"PT5M\") or \"until\" (ISO 8601 datetime)',\n 'TIMER_CONFIG_MISSING',\n { stepId: stepDef.stepId }\n )\n }\n\n const { interpolateVariables } = await import('./activity-executor')\n const resolveTimerValue = (value: unknown): unknown =>\n value ? interpolateVariables(value, context.workflowContext, instance, { mode: context.interpolationMode }) : undefined\n const duration = resolveTimerValue(timerConfig.duration)\n const until = resolveTimerValue(timerConfig.until)\n\n const fireAtMs = timerConfig.until\n ? resolveTimerUntilMs(until, stepDef.stepId)\n : Date.now() + resolveTimerDurationMs(duration, stepDef.stepId)\n\n const delayMs = fireAtMs - Date.now()\n const fireAt = new Date(fireAtMs)\n\n // Immediate-fire path: skip the queue round-trip if the timer is in the past\n if (delayMs <= 0) {\n return {\n status: 'COMPLETED',\n outputData: {\n stepType: 'WAIT_FOR_TIMER',\n timerFiredImmediately: true,\n fireAt,\n duration,\n until,\n },\n }\n }\n\n const now = new Date()\n\n // Enqueue delayed timer job via the shared activity queue.\n // Imported here to avoid a top-level cycle between step-handler and activity-executor.\n const { enqueueTimerJob } = await import('./activity-executor')\n const jobId = await enqueueTimerJob({\n workflowInstanceId: instance.id,\n stepInstanceId: stepInstance.id,\n branchInstanceId: branch ? branch.id : undefined,\n tenantId: instance.tenantId,\n organizationId: instance.organizationId,\n userId: context.userId,\n fireAt: fireAt.toISOString(),\n delayMs,\n })\n\n await logWorkflowEvent(em, {\n workflowInstanceId: instance.id,\n stepInstanceId: stepInstance.id,\n ...(branch ? { branchInstanceId: branch.id } : {}),\n eventType: 'TIMER_AWAITING',\n eventData: {\n fireAt: fireAt.toISOString(),\n duration: duration || null,\n until: until || null,\n jobId,\n },\n userId: context.userId,\n tenantId: instance.tenantId,\n organizationId: instance.organizationId,\n })\n\n if (branch) {\n branch.status = 'PAUSED'\n branch.updatedAt = now\n } else {\n instance.status = 'PAUSED'\n instance.pausedAt = now\n instance.updatedAt = now\n }\n await em.flush()\n\n return {\n status: 'WAITING',\n waitReason: 'TIMER',\n outputData: {\n fireAt,\n duration,\n until,\n jobId,\n },\n }\n}\n\nfunction resolveTimerUntilMs(until: unknown, stepId: string | undefined): number {\n const targetDate =\n until instanceof Date ? until : typeof until === 'string' || typeof until === 'number' ? new Date(until) : null\n if (!targetDate || isNaN(targetDate.getTime())) {\n throw new StepExecutionError(\n `WAIT_FOR_TIMER invalid \"until\" datetime: ${String(until)}`,\n 'TIMER_CONFIG_INVALID',\n { stepId, until }\n )\n }\n return targetDate.getTime()\n}\n\nfunction resolveTimerDurationMs(duration: unknown, stepId: string | undefined): number {\n const invalidDuration = (reason: string) =>\n new StepExecutionError(\n `WAIT_FOR_TIMER invalid \"duration\": ${String(duration)}`,\n 'TIMER_CONFIG_INVALID',\n { stepId, duration, reason }\n )\n if (typeof duration !== 'string') throw invalidDuration('the duration is not a string')\n try {\n return parseDuration(duration)\n } catch (error) {\n throw invalidDuration(error instanceof Error ? error.message : String(error))\n }\n}\n\n/**\n * Handle WAIT_FOR_CONDITION step - pause until a context predicate holds.\n *\n * Evaluates the step's ConditionExpression against the token read context. A\n * predicate that already holds completes the step inline with no queue job at\n * all. Otherwise the token parks and a delayed `kind: 'condition'` poll job is\n * enqueued carrying an ABSOLUTE `deadlineAt`, so neither a slow queue nor a\n * re-enqueue can extend the configured timeout. The event-driven wake\n * (`conditionHandler.wakeConditionWaiters`) is the fast path on top of it.\n */\nasync function handleWaitForConditionStep(\n em: EntityManager,\n instance: WorkflowInstance,\n stepInstance: StepInstance,\n stepDef: any,\n context: StepExecutionContext,\n branch?: WorkflowBranchInstance | null\n): Promise<StepExecutionResult> {\n const config = readWaitForConditionConfig(stepDef)\n\n const data = {\n ...conditionReadContext(instance, branch ?? null),\n ...(context.workflowContext || {}),\n }\n\n const met = await evaluateConditionExpression({\n condition: config.condition,\n data,\n instanceId: instance.id,\n userId: context.userId,\n })\n\n const now = new Date()\n\n if (met) {\n await logWorkflowEvent(em, {\n workflowInstanceId: instance.id,\n stepInstanceId: stepInstance.id,\n ...(branch ? { branchInstanceId: branch.id } : {}),\n eventType: 'CONDITION_MET',\n eventData: { stepId: stepDef.stepId, attempts: 1, waitedMs: 0, wokenBy: 'immediate' },\n userId: context.userId,\n tenantId: instance.tenantId,\n organizationId: instance.organizationId,\n })\n\n return {\n status: 'COMPLETED',\n outputData: {\n stepType: CONDITION_STEP_TYPE,\n conditionMet: true,\n attempts: 1,\n waitedMs: 0,\n wokenBy: 'immediate',\n },\n }\n }\n\n const deadlineAt = new Date(now.getTime() + config.timeoutMs)\n\n const { enqueueConditionCheckJob } = await import('./activity-executor')\n const jobId = await enqueueConditionCheckJob({\n workflowInstanceId: instance.id,\n stepInstanceId: stepInstance.id,\n branchInstanceId: branch ? branch.id : undefined,\n tenantId: instance.tenantId,\n organizationId: instance.organizationId,\n userId: context.userId,\n deadlineAt: deadlineAt.toISOString(),\n attempt: 1,\n delayMs: Math.min(config.pollIntervalMs, config.timeoutMs),\n })\n\n await logWorkflowEvent(em, {\n workflowInstanceId: instance.id,\n stepInstanceId: stepInstance.id,\n ...(branch ? { branchInstanceId: branch.id } : {}),\n eventType: 'CONDITION_AWAITING',\n eventData: {\n stepId: stepDef.stepId,\n condition: config.condition,\n deadlineAt: deadlineAt.toISOString(),\n pollIntervalMs: config.pollIntervalMs,\n onTimeout: config.onTimeout,\n jobId,\n },\n userId: context.userId,\n tenantId: instance.tenantId,\n organizationId: instance.organizationId,\n })\n\n if (branch) {\n branch.status = 'PAUSED'\n branch.updatedAt = now\n } else {\n instance.status = 'PAUSED'\n instance.pausedAt = now\n instance.updatedAt = now\n }\n await em.flush()\n\n return {\n status: 'WAITING',\n waitReason: 'CONDITION',\n outputData: {\n stepType: CONDITION_STEP_TYPE,\n deadlineAt: deadlineAt.toISOString(),\n pollIntervalMs: config.pollIntervalMs,\n onTimeout: config.onTimeout,\n jobId,\n },\n }\n}\n\n// ============================================================================\n// Helper Functions\n// ============================================================================\n\n// parseDuration is imported from ./duration\n\n/**\n * Log step-related event to event sourcing table\n */\nasync function logStepEvent(\n em: EntityManager,\n event: {\n workflowInstanceId: string\n stepInstanceId: string\n branchInstanceId?: string | null\n eventType: string\n eventData: any\n userId?: string\n tenantId: string\n organizationId: string\n }\n): Promise<WorkflowEvent> {\n const workflowEvent = em.create(WorkflowEvent, {\n ...event,\n occurredAt: new Date(),\n })\n\n await em.persist(workflowEvent).flush()\n return workflowEvent\n}\n\n/**\n * Map data from source context using mapping configuration\n *\n * @param sourceContext - Source data object\n * @param mapping - Mapping configuration (targetKey -> sourcePath)\n * @returns Mapped data object\n */\nfunction mapInputData(\n sourceContext: Record<string, any>,\n mapping: Record<string, string>\n): Record<string, any> {\n const result: Record<string, any> = {}\n\n for (const [targetKey, sourcePath] of Object.entries(mapping)) {\n const value = safeGetNestedValue(sourceContext, sourcePath)\n if (value !== undefined) {\n safeSetNestedValue(result, targetKey, value)\n }\n }\n\n // If no mapping provided, pass entire context\n return Object.keys(result).length > 0 ? result : sourceContext\n}\n\n/**\n * Map output data from child context back to parent\n *\n * @param childContext - Child workflow context\n * @param mapping - Mapping configuration (targetKey -> sourcePath)\n * @returns Mapped output data\n */\nfunction mapOutputData(\n childContext: Record<string, any>,\n mapping: Record<string, string>\n): Record<string, any> {\n const result: Record<string, any> = {}\n\n for (const [targetKey, sourcePath] of Object.entries(mapping)) {\n const value = safeGetNestedValue(childContext, sourcePath)\n if (value !== undefined) {\n safeSetNestedValue(result, targetKey, value)\n }\n }\n\n // If no mapping provided, pass entire child context\n return Object.keys(result).length > 0 ? result : childContext\n}\n\n/**\n * Validate a completed sub-workflow's child context against the child's\n * declared output ports, then map it to parent output. Shared by the\n * synchronous (inline) completion branch in `handleSubWorkflowStep` and the\n * async parent resume path so both apply identical mapping/validation rules.\n *\n * Output ports are named in the CHILD's vocabulary, while `outputMapping` is\n * `{ parentKey: childPath }`, so validation and coercion run on the child\n * context before mapping \u2014 validating the mapped result would look ports up\n * under caller-chosen parent keys and reject any renamed mapping. Returns the\n * mapped `outputData` on success or an `error` message on port-validation\n * failure (never throws).\n */\nexport function mapSubWorkflowOutput(\n childContext: Record<string, any>,\n outputMapping: Record<string, string>,\n ioContract?: WorkflowIoContract\n): { outputData: Record<string, any>; error?: undefined } | { outputData?: undefined; error: string } {\n let childOutput = childContext\n\n if (ioContract?.outputs?.length) {\n const { coerced, errors } = validateAgainstPorts(childContext, ioContract.outputs)\n if (errors.length > 0) {\n return { error: `Sub-workflow output validation failed: ${errors.map((e) => e.message).join('; ')}` }\n }\n childOutput = coerced\n }\n\n return { outputData: mapOutputData(childOutput, outputMapping || {}) }\n}\n"],
5
+ "mappings": "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",
6
6
  "names": ["agentActivity"]
7
7
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@open-mercato/core",
3
- "version": "0.10.1-develop.7816.1.6c959754cf",
3
+ "version": "0.10.1-develop.7822.1.5d6a1d00a1",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -263,19 +263,19 @@
263
263
  "sharp": "^0.35.3",
264
264
  "svix": "^1.99.1",
265
265
  "ts-pattern": "^5.0.0",
266
- "zod": "^4.4.3"
266
+ "zod": "^4.6.6"
267
267
  },
268
268
  "peerDependencies": {
269
- "@open-mercato/ai-assistant": "0.10.1-develop.7816.1.6c959754cf",
270
- "@open-mercato/shared": "0.10.1-develop.7816.1.6c959754cf",
271
- "@open-mercato/ui": "0.10.1-develop.7816.1.6c959754cf",
269
+ "@open-mercato/ai-assistant": "0.10.1-develop.7822.1.5d6a1d00a1",
270
+ "@open-mercato/shared": "0.10.1-develop.7822.1.5d6a1d00a1",
271
+ "@open-mercato/ui": "0.10.1-develop.7822.1.5d6a1d00a1",
272
272
  "react": "^19.0.0",
273
273
  "react-dom": "^19.0.0"
274
274
  },
275
275
  "devDependencies": {
276
- "@open-mercato/ai-assistant": "0.10.1-develop.7816.1.6c959754cf",
277
- "@open-mercato/shared": "0.10.1-develop.7816.1.6c959754cf",
278
- "@open-mercato/ui": "0.10.1-develop.7816.1.6c959754cf",
276
+ "@open-mercato/ai-assistant": "0.10.1-develop.7822.1.5d6a1d00a1",
277
+ "@open-mercato/shared": "0.10.1-develop.7822.1.5d6a1d00a1",
278
+ "@open-mercato/ui": "0.10.1-develop.7822.1.5d6a1d00a1",
279
279
  "@testing-library/dom": "^10.4.1",
280
280
  "@testing-library/jest-dom": "^7.0.1",
281
281
  "@testing-library/react": "^16.3.3",
@@ -17,6 +17,7 @@ import { emitCommunicationChannelsEvent } from '../events'
17
17
  import type { ChannelAdapterRegistry } from '../lib/registry'
18
18
  import type { NormalizedInboundMessage } from '../lib/adapter'
19
19
  import { createLogger } from '@open-mercato/shared/lib/logger'
20
+ import { getTelemetryRuntime } from '@open-mercato/shared/lib/telemetry/runtime'
20
21
 
21
22
  const logger = createLogger('communication_channels').child({ component: 'poll-channel' })
22
23
 
@@ -82,7 +83,7 @@ type CredentialsServiceLike = {
82
83
  * `(channel_id, external_message_id)`).
83
84
  * 6. Update `channel.lastPolledAt = NOW()` on success.
84
85
  * 7. On error: classify, set `channel.status` accordingly, set `last_error`, emit
85
- * `channel.requires_reauth` on 401, retry transient failures up to MAX.
86
+ * `channel.requires_reauth` after failed 401 recovery, retry transient failures up to MAX.
86
87
  *
87
88
  * The hub doesn't drain a remote mailbox indefinitely — `fetchHistory` returns a
88
89
  * single page (provider decides the size). If the provider has more, the next tick
@@ -158,6 +159,10 @@ export default async function handle(
158
159
  credentials = {}
159
160
  }
160
161
  }
162
+ const refreshDeps = {
163
+ credentialsService,
164
+ logger: (...args: unknown[]) => logger.warn('credential refresh diagnostic', { channelId: channel.id, details: args }),
165
+ }
161
166
  const refreshed = await refreshCredentialsIfNeeded(
162
167
  {
163
168
  adapter,
@@ -165,7 +170,7 @@ export default async function handle(
165
170
  credentials,
166
171
  scope: credentialsScope,
167
172
  },
168
- { credentialsService },
173
+ refreshDeps,
169
174
  )
170
175
  credentials = refreshed.credentials
171
176
 
@@ -178,10 +183,11 @@ export default async function handle(
178
183
  let normalized: NormalizedInboundMessage[] = []
179
184
  let nextCursor: string | undefined
180
185
  let hasMore = false
181
- try {
182
- const result = await adapter.fetchHistory({
186
+ const fetchHistory = adapter.fetchHistory.bind(adapter)
187
+ const fetchPage = (withCredentials: Record<string, unknown>) =>
188
+ fetchHistory({
183
189
  conversationId: channel.externalIdentifier ?? channel.id,
184
- credentials,
190
+ credentials: withCredentials,
185
191
  cursor: channel.lastPolledAt ? channel.lastPolledAt.toISOString() : undefined,
186
192
  channelState: (channel.channelState as Record<string, unknown> | null) ?? undefined,
187
193
  scope: {
@@ -194,6 +200,29 @@ export default async function handle(
194
200
  // since the persisted cursor.
195
201
  contactFilter: { addresses: [], sinceDays: 7 },
196
202
  })
203
+ try {
204
+ let result: Awaited<ReturnType<typeof fetchPage>>
205
+ try {
206
+ result = await fetchPage(credentials)
207
+ } catch (err) {
208
+ if (!isReauthError(classifyOutboundError(err))) throw err
209
+ try {
210
+ getTelemetryRuntime()?.reportError(err, {
211
+ module: 'communication_channels',
212
+ code: 'communication_channels.poll_authentication_failed',
213
+ attributes: { channelId: channel.id, providerKey: channel.providerKey },
214
+ })
215
+ } catch {
216
+ logger.warn('poll authentication failure could not be reported', { channelId: channel.id })
217
+ }
218
+ const forced = await refreshCredentialsIfNeeded(
219
+ { adapter, channelId: channel.id, credentials, scope: credentialsScope, force: true },
220
+ refreshDeps,
221
+ )
222
+ if (!forced.refreshed) throw err
223
+ credentials = forced.credentials
224
+ result = await fetchPage(credentials)
225
+ }
197
226
  normalized = Array.isArray(result?.messages) ? result.messages : []
198
227
  nextCursor = result?.nextCursor
199
228
  hasMore = result?.hasMore === true
@@ -3253,7 +3253,9 @@ function convertLineCalculationToEntityInput(
3253
3253
  unitPriceNet:
3254
3254
  toNumericString(
3255
3255
  line.unitPriceNet ??
3256
- lineResult.netAmount / Math.max(line.quantity || 1, 1),
3256
+ (line.unitPriceGross !== null && line.unitPriceGross !== undefined
3257
+ ? deriveLineNetFromGross(null, line.unitPriceGross, line.taxRate)
3258
+ : lineResult.netAmount / Math.max(line.quantity || 1, 1)),
3257
3259
  ) ?? "0",
3258
3260
  unitPriceGross:
3259
3261
  toNumericString(
@@ -28,6 +28,12 @@ import {
28
28
  } from '../../openapi'
29
29
  import { invalidateTriggerCache } from '../../../lib/event-trigger-service'
30
30
  import { normalizeDefinitionValidationIssues } from '../../../lib/definition-error-body'
31
+ import { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'
32
+ import {
33
+ mappingTargetIssuesForApi,
34
+ UNSAFE_MAPPING_TARGET_MESSAGE,
35
+ UNSAFE_MAPPING_TARGET_MESSAGE_KEY,
36
+ } from '../../../lib/mapping-target-validation'
31
37
  import { getCodeWorkflow, getAllCodeWorkflows } from '../../../lib/code-registry'
32
38
  import { codeWorkflowUuid } from '../../../lib/find-definition'
33
39
  import {
@@ -200,6 +206,17 @@ export async function PUT(
200
206
  }
201
207
 
202
208
  const input: UpdateWorkflowDefinitionApiInput = validation.data
209
+ const { translate } = await resolveTranslations()
210
+ const mappingMessage = translate(UNSAFE_MAPPING_TARGET_MESSAGE_KEY, UNSAFE_MAPPING_TARGET_MESSAGE)
211
+ const mappingIssues = input.definition
212
+ ? mappingTargetIssuesForApi(input.definition, ['definition'], mappingMessage)
213
+ : []
214
+ if (mappingIssues.length > 0) {
215
+ return NextResponse.json(
216
+ { error: 'Validation failed', details: mappingIssues },
217
+ { status: 400 }
218
+ )
219
+ }
203
220
  const guardResult = await validateCrudMutationGuard(container, {
204
221
  tenantId: tenantId ?? '',
205
222
  organizationId: organizationId ?? null,
@@ -66,6 +66,7 @@ import {
66
66
  computeDefinitionContextLedger,
67
67
  warmContextLedgerResolvers,
68
68
  } from '../../../lib/definition-context-schema'
69
+ import { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'
69
70
  import { evaluateWorkflowDefinition } from '../../../lib/definition-evaluation'
70
71
  import { listWorkflowFunctions } from '../../../lib/workflow-function-registry'
71
72
  import { listWorkflowSafeCommands } from '../../../lib/workflow-safe-commands'
@@ -292,7 +293,8 @@ export async function POST(request: NextRequest) {
292
293
  }
293
294
 
294
295
  const ledger = await tryComputeLedger(container, interpreted.definition)
295
- const evaluation = evaluateWorkflowDefinition(interpreted.definition, { ledger })
296
+ const { translate } = await resolveTranslations()
297
+ const evaluation = evaluateWorkflowDefinition(interpreted.definition, { ledger, translate })
296
298
 
297
299
  // Nothing above wrote a row, and nothing below does either — this route
298
300
  // never resolves an EntityManager, so a future write would be an obvious