@mastra/core
Version:
233 lines • 11.1 kB
TypeScript
import { EventProcessor } from '../../../events/processor.js';
import type { Event } from '../../../events/types.js';
import type { Mastra } from '../../../mastra/index.js';
import type { StepExecutionStrategy } from '../../../worker/types.js';
import type { RestartExecutionParams, StepFlowEntry, StepResult, TimeTravelExecutionParams, WorkflowRunState } from '../../../workflows/types.js';
import type { Workflow } from '../../../workflows/workflow.js';
export type ProcessorArgs = {
activeStepsPath: Record<string, number[]>;
workflow: Workflow;
workflowId: string;
runId: string;
executionPath: number[];
stepResults: Record<string, StepResult<any, any, any, any>>;
resumeSteps: string[];
prevResult: StepResult<any, any, any, any>;
requestContext: Record<string, any>;
timeTravel?: TimeTravelExecutionParams;
restart?: RestartExecutionParams;
resumeData?: any;
parentWorkflow?: ParentWorkflow;
parentContext?: {
workflowId: string;
input: any;
};
retryCount?: number;
perStep?: boolean;
format?: 'legacy' | 'vnext';
state?: Record<string, any>;
outputOptions?: {
includeState?: boolean;
includeResumeLabels?: boolean;
};
forEachIndex?: number;
nestedRunId?: string;
};
export type ParentWorkflow = {
workflowId: string;
runId: string;
executionPath: number[];
resume: boolean;
stepResults: Record<string, StepResult<any, any, any, any>>;
parentWorkflow?: ParentWorkflow;
timeTravel?: TimeTravelExecutionParams;
restart?: RestartExecutionParams;
stepId: string;
stepGraph: StepFlowEntry[];
activeStepsPath: Record<string, number[]>;
resumeSteps: string[];
resumeData: any;
input: any;
parentContext?: {
workflowId: string;
input: any;
};
};
export declare class WorkflowEventProcessor extends EventProcessor {
#private;
private stepExecutor;
private stepExecutionStrategy?;
private abortControllers;
private parentChildRelationships;
private runFormats;
private deliveryAttempts;
private static readonly MAX_DELIVERY_ATTEMPTS;
private static readonly TERMINAL_SENTINEL;
private static readonly DELIVERY_ATTEMPTS_MAX_ENTRIES;
private readonly topicCleanupDelayMs;
private static readonly DEFAULT_TOPIC_CLEANUP_DELAY_MS;
private readonly pendingTopicCleanups;
private static readonly ACTIVE_RUN_STATUSES;
constructor({ mastra, stepExecutionStrategy, topicCleanupDelayMs, }: {
mastra: Mastra;
stepExecutionStrategy?: StepExecutionStrategy;
topicCleanupDelayMs?: number;
});
/**
* Schedule deletion of a finished run's `workflow.events.v2.<runId>` topic.
*
* Per-run watch topics are written by every step of a run; on transports
* that retain messages (e.g. Redis Streams) they would otherwise live
* forever once the run ends. Deletion is delayed so subscribers still
* draining the terminal `workflow-finish` event aren't cut off, and
* fire-and-forget because topic cleanup must never affect run completion.
*
* Best-effort by design: if the process exits before the timer fires, the
* transport-level idle TTL (e.g. `streamIdleTtlMs`) is the backstop.
*
* A finished run can be re-executed under the same runId (`timeTravel`,
* `restart`), so deletion is double-guarded: a restart processed by this
* process cancels the pending timer directly, and when the timer fires we
* re-check the run's persisted status — a restart may have been picked up
* by a different worker process — and skip deletion while the run is
* active again.
*/
private scheduleRunTopicCleanup;
private cancelRunTopicCleanup;
private clearRunTopicUnlessActive;
/**
* Get or create an AbortController for a workflow run
*/
private getOrCreateAbortController;
/**
* Cancel a workflow run and all its nested child workflows
*/
private cancelRunAndChildren;
/**
* Clean up abort controller and relationships when a workflow completes.
* Also cleans up any orphaned child entries that reference this run as parent.
*/
private cleanupRun;
/**
* Resolves the tracing context for a run, walking up the parent chain so a
* nested workflow run (e.g. `agentic-execution` inside `agentic-loop`)
* inherits its parent's parent span. `EventedRun.start` records the context
* on Mastra keyed by runId; nested runs are only registered against their
* parent.
*/
private resolveRunTracingContext;
/**
* Snapshot of the run's current span as the {traceId, spanId, parentSpanId} shape that
* `UpdateWorkflowStateOptions.tracingContext` expects, so a suspend's persisted snapshot
* can stitch the resumed AGENT_RUN/WORKFLOW_RUN span back to the original trace. Mirrors
* `default.ts`'s `persistTracingContext`; the evented engine holds the live span on
* Mastra (since it can't ride pubsub events), so we resolve it via runId here.
*/
private resolveSuspendTracingContext;
/**
* Applies the workflow's `pruneSnapshot` option to an already-persisted snapshot.
*
* The evented engine persists suspensions via merge operations
* (`updateWorkflowResults` + `updateWorkflowState`) rather than writing a full
* snapshot object, so the prune hook can't intercept the write itself. Instead,
* after the merge completes, we load the merged snapshot, prune it, and
* re-persist the full row. No-op when the workflow has no `pruneSnapshot` option.
*/
private pruneAndRepersistSnapshot;
__registerMastra(mastra: Mastra): void;
private errorWorkflow;
protected processWorkflowCancel({ workflowId, runId, prevResult, ...args }: ProcessorArgs): Promise<void>;
protected processWorkflowStart({ workflow, parentWorkflow, workflowId, runId, resumeSteps, prevResult, resumeData, timeTravel, restart, executionPath, stepResults, requestContext, perStep, format, state, outputOptions, forEachIndex, }: ProcessorArgs & {
initialState?: Record<string, any>;
}): Promise<void>;
protected endWorkflow(args: ProcessorArgs, status?: 'success' | 'failed' | 'canceled' | 'paused'): Promise<void>;
protected processWorkflowEnd(args: ProcessorArgs): Promise<void>;
protected processWorkflowSuspend(args: ProcessorArgs): Promise<void>;
protected processWorkflowFail(args: ProcessorArgs): Promise<void>;
protected processWorkflowStepRun(args: ProcessorArgs): Promise<void>;
/**
* Shared leaf-step runner. Executes a single leaf entry - a plain `step`, a
* declarative `agent` / `tool` / `mapping` entry, or a `loop` / `foreach`
* body - and emits its lifecycle events (`workflow.step.end`, retries,
* suspend, cancel). The per-kind interpretation happens in the step
* executor's dispatch; here entries are only inspected via the `step-entry`
* accessors.
*/
protected runLeafStep(args: ProcessorArgs & {
step: Extract<StepFlowEntry, {
type: 'step' | 'agent' | 'tool' | 'mapping' | 'loop' | 'foreach';
}>;
}): Promise<void>;
/**
* Aggregate the results of all branches of a `parallel` / `conditional` entry once
* every branch has reached a terminal state (`success` / `skipped`) or `suspended`.
*
* This runs once per branch completion. It only acts when every branch is accounted
* for; otherwise it returns and lets a later branch finish the aggregation. Because
* `stepResults` is the snapshot returned by the caller's `updateWorkflowResults`
* call — which grows monotonically per branch — only the branch whose write landed
* last observes the full set, so exactly one branch emits (no double emit).
*
* - if any branch is still suspended → re-emit `workflow.suspend` with the full set
* of suspended paths and persist the workflow state. This both fixes the race where
* each branch would overwrite `suspendedPaths` on its own, and lets the workflow
* stay suspended while only some branches have been resumed.
* - otherwise → emit `workflow.step.end` for the parallel/conditional entry with the
* merged branch outputs (the existing behaviour).
*/
protected aggregateBranchResults({ workflow, workflowId, runId, branchEntry, branchExecutionPath, latestBranchResult, resumeSteps, timeTravel, restart, parentWorkflow, stepResults, activeStepsPath, requestContext, state, outputOptions, }: {
workflow: Workflow;
workflowId: string;
runId: string;
branchEntry: Extract<StepFlowEntry, {
type: 'parallel' | 'conditional';
}>;
branchExecutionPath: number[];
/**
* The in-flight result of the branch that just finished (i.e. the one at
* `branchExecutionPath`). Used for that branch's output so non-JSON values (e.g.
* `Date`) survive — the copy in `stepResults` has been round-tripped through storage
* serialization. Other branches' outputs unavoidably come from `stepResults`.
*/
latestBranchResult?: StepResult<any, any, any, any>;
resumeSteps: string[];
timeTravel?: TimeTravelExecutionParams;
restart?: RestartExecutionParams;
parentWorkflow?: ParentWorkflow;
stepResults: Record<string, any>;
activeStepsPath: Record<string, number[]>;
requestContext: Record<string, any>;
state: Record<string, any>;
outputOptions?: {
includeState?: boolean;
includeResumeLabels?: boolean;
};
}): Promise<void>;
protected processWorkflowStepEnd({ workflow, workflowId, runId, executionPath, resumeSteps, timeTravel, restart, prevResult, parentWorkflow, stepResults, activeStepsPath, parentContext, requestContext, perStep, state, outputOptions, forEachIndex, nestedRunId, }: ProcessorArgs): Promise<void>;
loadData({ workflowId, runId, }: {
workflowId: string;
runId: string;
}): Promise<WorkflowRunState | null | undefined>;
/**
* Result of handling a single workflow event.
*
* - `ok: true` — event was processed; the transport should ack.
* - `ok: false, retry: true` — transient failure, the transport should
* nack/redeliver (or, for HTTP push, return 5xx so the broker retries).
* - `ok: false, retry: false` — terminal/poison failure, the transport
* should drop the event (or return 4xx for HTTP push).
*/
handle(event: Event): Promise<{
ok: true;
} | {
ok: false;
retry: boolean;
}>;
/**
* @deprecated prefer {@link WorkflowEventProcessor.handle}, which returns a
* structured result instead of relying on an ack callback. Kept as a thin
* wrapper so existing pull-mode call sites continue to work.
*/
process(event: Event, ack?: () => Promise<void>): Promise<void>;
}
//# sourceMappingURL=index.d.ts.map