diff --git a/packages/workflow-executor/src/adapters/run-to-available-step-mapper.ts b/packages/workflow-executor/src/adapters/run-to-available-step-mapper.ts index ba90c1a671..36d0d0e4ae 100644 --- a/packages/workflow-executor/src/adapters/run-to-available-step-mapper.ts +++ b/packages/workflow-executor/src/adapters/run-to-available-step-mapper.ts @@ -14,6 +14,7 @@ import type { import { z } from 'zod'; import { deserializeRecordId } from './record-id-serializer'; +import { ServerWorkflowTriggerType } from './server-types'; import toStepDefinition from './step-definition-mapper'; import { DomainValidationError, @@ -157,6 +158,7 @@ export default function toAvailableStepExecution( stepId: pending.stepName, stepIndex: pending.stepIndex, collectionId: run.collectionId, + triggerType: run.triggerType ?? ServerWorkflowTriggerType.manual, baseRecordRef: { collectionName: run.collectionName, recordId: deserializeRecordId(run.selectedRecordId), diff --git a/packages/workflow-executor/src/adapters/server-types.ts b/packages/workflow-executor/src/adapters/server-types.ts index f0d56a802a..883984099e 100644 --- a/packages/workflow-executor/src/adapters/server-types.ts +++ b/packages/workflow-executor/src/adapters/server-types.ts @@ -179,6 +179,11 @@ export interface ServerStepHistory { /** Mirror of the server's `WorkflowRunState` enum (workflow-run-model.ts). */ export type ServerWorkflowRunState = 'started' | 'pending' | 'loading' | 'aborted' | 'finished'; +export enum ServerWorkflowTriggerType { + manual = 'manual', + webhook = 'webhook', +} + export interface ServerHydratedWorkflowRun { id: number; workflowId: string; @@ -187,6 +192,7 @@ export interface ServerHydratedWorkflowRun { selectedRecordId: string; bpmnVersion: string; runState: ServerWorkflowRunState; + triggerType?: ServerWorkflowTriggerType; workflowHistory: ServerStepHistory[]; /** Server types declare `Date`; Express serializes to ISO 8601 string on the wire. */ createdAt: string; diff --git a/packages/workflow-executor/src/index.ts b/packages/workflow-executor/src/index.ts index 7fa349a083..77298f8948 100644 --- a/packages/workflow-executor/src/index.ts +++ b/packages/workflow-executor/src/index.ts @@ -58,6 +58,8 @@ export type { ExecutionContext, } from './types/execution-context'; +export { TriggerType } from './types/validated/execution'; + export type { AgentPort, ExecuteActionQuery, diff --git a/packages/workflow-executor/src/runner.ts b/packages/workflow-executor/src/runner.ts index ae07bb9e04..1b4a6a867d 100644 --- a/packages/workflow-executor/src/runner.ts +++ b/packages/workflow-executor/src/runner.ts @@ -302,6 +302,13 @@ export default class Runner { let chainedCount = 0; // additional steps chained after the initial one const maxDepth = this.config.maxChainDepth ?? DEFAULT_MAX_CHAIN_DEPTH; + this.logger('Debug', 'Run started executing', { + runId: step.runId, + stepId: step.stepId, + stepIndex: step.stepIndex, + triggerType: step.triggerType, + }); + // Sequential by design: each step's outcome drives the next dispatch; steps within one run // cannot overlap. The no-await-in-loop rule doesn't apply here. /* eslint-disable no-await-in-loop, no-constant-condition */ diff --git a/packages/workflow-executor/src/types/validated/execution.ts b/packages/workflow-executor/src/types/validated/execution.ts index cca43350bb..fa4ca7c265 100644 --- a/packages/workflow-executor/src/types/validated/execution.ts +++ b/packages/workflow-executor/src/types/validated/execution.ts @@ -31,12 +31,19 @@ export const StepSchema = z .strict(); export type Step = z.infer; +export enum TriggerType { + Manual = 'manual', + Webhook = 'webhook', +} +export const TriggerTypeSchema = z.nativeEnum(TriggerType); + export const AvailableStepExecutionSchema = z .object({ runId: z.string().min(1), stepId: z.string().min(1), stepIndex: z.number().int().nonnegative(), collectionId: z.string().min(1), + triggerType: TriggerTypeSchema, baseRecordRef: RecordRefSchema, stepDefinition: StepDefinitionSchema, previousSteps: z.array(StepSchema), diff --git a/packages/workflow-executor/test/adapters/run-to-available-step-mapper.test.ts b/packages/workflow-executor/test/adapters/run-to-available-step-mapper.test.ts index 4b8cd06ff9..4397c00e91 100644 --- a/packages/workflow-executor/test/adapters/run-to-available-step-mapper.test.ts +++ b/packages/workflow-executor/test/adapters/run-to-available-step-mapper.test.ts @@ -13,8 +13,10 @@ import { ServerStepExecutionTypeEnum, ServerStepTypeEnum, ServerTaskTypeEnum, + ServerWorkflowTriggerType, } from '../../src/adapters/server-types'; import { DomainValidationError, InvalidStepDefinitionError } from '../../src/errors'; +import { TriggerType } from '../../src/types/validated/execution'; import { StepType } from '../../src/types/validated/step-definition'; const logger = jest.fn(); @@ -108,6 +110,7 @@ describe('toAvailableStepExecution', () => { stepId: 'step-a', stepIndex: 0, collectionId: '11', + triggerType: TriggerType.Manual, baseRecordRef: { collectionName: 'customers', recordId: ['123'], @@ -124,6 +127,23 @@ describe('toAvailableStepExecution', () => { }); }); + it('should forward the run triggerType', () => { + const run = makeRun({ triggerType: ServerWorkflowTriggerType.webhook }); + + const result = toAvailableStepExecution(run); + + expect(result?.triggerType).toBe(TriggerType.Webhook); + }); + + it('should default triggerType to manual when the orchestrator omits it', () => { + const run = makeRun(); + delete run.triggerType; + + const result = toAvailableStepExecution(run); + + expect(result?.triggerType).toBe(TriggerType.Manual); + }); + it('should stringify the numeric run id', () => { const run = makeRun({ id: 999 }); diff --git a/packages/workflow-executor/test/integration/workflow-execution.test.ts b/packages/workflow-executor/test/integration/workflow-execution.test.ts index f4c38b460e..3302c68403 100644 --- a/packages/workflow-executor/test/integration/workflow-execution.test.ts +++ b/packages/workflow-executor/test/integration/workflow-execution.test.ts @@ -13,6 +13,7 @@ import ExecutorHttpServer from '../../src/http/executor-http-server'; import Runner from '../../src/runner'; import SchemaCache from '../../src/schema-cache'; import InMemoryStore from '../../src/stores/in-memory-store'; +import { TriggerType } from '../../src/types/validated/execution'; import { StepExecutionMode, StepType } from '../../src/types/validated/step-definition'; // --------------------------------------------------------------------------- @@ -227,6 +228,7 @@ function buildPendingStep( stepId: 'step-1', stepIndex: 0, collectionId: 'col-1', + triggerType: TriggerType.Manual, baseRecordRef: BASE_RECORD_REF, previousSteps: [], user: STEP_USER, @@ -248,6 +250,7 @@ describe('workflow execution (integration)', () => { stepId: 'step-1', stepIndex: 0, collectionId: 'col-1', + triggerType: TriggerType.Manual, baseRecordRef: { collectionName: 'customers', recordId: [42], stepIndex: 0 }, stepDefinition: { type: StepType.ReadRecord, prompt: 'Read the customer email' }, previousSteps: [], diff --git a/packages/workflow-executor/test/runner.test.ts b/packages/workflow-executor/test/runner.test.ts index 29f24abce5..04efbecdb3 100644 --- a/packages/workflow-executor/test/runner.test.ts +++ b/packages/workflow-executor/test/runner.test.ts @@ -27,6 +27,7 @@ import TriggerRecordActionStepExecutor from '../src/executors/trigger-record-act import UpdateRecordStepExecutor from '../src/executors/update-record-step-executor'; import Runner from '../src/runner'; import SchemaCache from '../src/schema-cache'; +import { TriggerType } from '../src/types/validated/execution'; import { StepExecutionMode, StepType } from '../src/types/validated/step-definition'; // --------------------------------------------------------------------------- @@ -161,6 +162,7 @@ function makePendingStep( stepId: 'step-1', stepIndex: 0, collectionId: 'col-1', + triggerType: TriggerType.Manual, baseRecordRef: { collectionName: 'customers', recordId: ['1'], stepIndex: 0 }, stepDefinition: makeStepDefinition(stepType), previousSteps: [],