Cogitator
API Reference

Workflows API

Public exports of @cogitator-ai/workflows — builder, executor, nodes, checkpoints, human approvals, timers, sagas, map-reduce, subworkflows, triggers, run management and observability — with their signatures.

Workflow state types must satisfy WorkflowState (Record<string, unknown>): use a type alias or interface MyState extends WorkflowState.

Building and Running

import {
  WorkflowBuilder,
  WorkflowExecutor,
  agentNode,
  functionNode,
} from '@cogitator-ai/workflows';
import type { WorkflowState } from '@cogitator-ai/types';

interface ReportState extends WorkflowState {
  topic?: string;
  draft?: string;
  words?: number;
}

const workflow = new WorkflowBuilder<ReportState>('report')
  .initialState({ topic: 'WebAssembly' })
  .addNode(
    'write',
    agentNode<ReportState>(writer, {
      inputMapper: (state) => `Write a short report about ${state.topic}`,
      stateMapper: (result) => ({ draft: result.output }),
    })
  )
  .addNode(
    'count',
    functionNode<ReportState>('count', async (state) => state.draft?.split(/\s+/).length ?? 0, {
      stateMapper: (words) => ({ words: words as number }),
    }),
    { after: ['write'] }
  )
  .build();

const result = await new WorkflowExecutor(cog).execute(workflow);
console.log(result.state.words, result.error);

WorkflowBuilder<S>

MethodSignaturePurpose
constructornew WorkflowBuilder<S>(name: string)
initialStateinitialState(state: S): thisDefault state, merged with the execution input
addNodeaddNode(name: string, node: NodeFn<S> | WorkflowNode<S>, options?: { after?: string[]; config?: NodeConfig<S> }): thisAdd a node function or a factory-made node
addConditionaladdConditional(name: string, condition: (state: S) => string | string[], options?: { after? }): thisRoute to the node(s) condition names
addLoopaddLoop(name: string, options: { condition: (state: S) => boolean; back: string; exit: string; after? }): thisLoop back while condition holds
addParalleladdParallel(name: string, targets: string[], options?: { after? }): thisFan out to several nodes
entryPointentryPoint(nodeName: string): thisSet the entry point explicitly
buildbuild(): Workflow<S>Validate and compile

NodeFn<S> is (ctx: NodeContext<S>) => Promise<NodeResult<S>> with ctx = { state, input?, nodeId, workflowId, step, reportProgress? } and NodeResult = { state?: Partial<S>; output?; next? }. NodeConfig<S> is { name?, timeout?, retries?, retryDelay?, compensation?: CompensationConfig<S> }; compensation ({ compensate, compensateCondition?, compensateOrder?, compensateTimeout? }) is rolled back by the executor when a later node of the run fails (Sagas). build() throws when there is no single root (independent roots need a root addParallel or .entryPoint()) and when a node after a loop is neither its back nor its exit node. See Builder.

WorkflowExecutor

MethodSignaturePurpose
constructornew WorkflowExecutor(cogitator: Cogitator, checkpointStore?: CheckpointStore)Default store: InMemoryCheckpointStore
executeexecute<S>(workflow: Workflow<S>, input?: Partial<S>, options?: ExecutorExecuteOptions): Promise<WorkflowResult<S>>Run to completion; a failure lands in result.error
resumeresume<S>(workflow: Workflow<S>, checkpointId: string, options?: ExecutorExecuteOptions): Promise<WorkflowResult<S>>Continue from a checkpoint, skipping completed nodes
streamstream<S>(workflow, input?, options?: Omit<ExecutorExecuteOptions, 'onNodeStart' | 'onNodeComplete' | 'onNodeError' | 'onNodeProgress'>): AsyncIterable<StreamingWorkflowEvent>Events: workflow_started, node_started, node_progress, node_completed, node_error, workflow_completed

ExecutorExecuteOptions extends WorkflowExecuteOptions:

OptionPurpose
maxConcurrency, maxIterationsParallel nodes, loop safety limit
checkpoint, checkpointStrategySave checkpoints ('per-iteration' or 'per-node')
onNodeStart, onNodeComplete, onNodeError, onNodeProgressCallbacks
workflowId, skipNodes, nodeResultsIdentity and resumption
signalCancel the run
tracer, metricsCollectorObservability; default to the global tracer / collector when those are enabled
defaultRetry, defaultCircuitBreakerSaga defaults for every node
deadLetterQueue, idempotencyStoreSaga infrastructure
approvalStore, approvalNotifier, timerStoreStores for human and timer nodes
depthSubworkflow nesting depth
onApprovalRequired(request)A human node opened an approval request (each chain step and escalation included)
onTimerScheduled(entry)A timer node stored a persisted timer in the timerStore (persist: true configs only)
onDeadLetter(entry)A node that finally failed was written to the deadLetterQueue
onCompensationStart(nodeId), onCompensationComplete(nodeId)A node's config.compensation starts / finished rolling back (successfully or not) after a later node failed

These five callbacks are the run observers of WorkflowExecuteOptionsV2, also accepted by the manager's execute(); one that throws is logged and does not break the run.

WorkflowResult<S>: { workflowId, workflowName, state, nodeResults: Map<string, { output, duration }>, duration, checkpointId?, error? }. NodeExecutionError (nodeName) and NodeTimeoutError (nodeName, timeoutMs) are the errors nodes fail with. WorkflowScheduler is the executor's internal DAG scheduler (dependency graph, ready nodes, concurrency), exported for custom executors. See Execution.

Nodes

ExportSignaturePurpose
agentNodeagentNode<S>(agent: Agent, options?: { inputMapper?; stateMapper?: (result: RunResult) => Partial<S>; runOptions? }): WorkflowNode<S>Run an agent
toolNodetoolNode<S, TArgs>(tool: Tool<TArgs>, options: { argsMapper: (state: S, input?) => TArgs; stateMapper?; signal? }): WorkflowNode<S>Run a tool
functionNodefunctionNode<S, O>(name: string, fn: (state: S, input?) => Promise<O>, options?: { stateMapper?: (output) => Partial<S> }): WorkflowNode<S>Run a function
customNodecustomNode<S>(name: string, fn: (ctx: NodeContext<S>) => Promise<NodeResult<S>>): WorkflowNode<S>Full control over state, output and next
humanWorkflowNodehumanWorkflowNode<S>(config: HumanNodeConfig<S>, options?: { approvalStore?; approvalNotifier?; stateMapper? }): WorkflowNode<S>Wait for a person (see Human)
timerWorkflowNodetimerWorkflowNode<S>(config: AnyTimerNodeConfig<S>, options?: { timerStore?; stateMapper? }): WorkflowNode<S>Wait for a delay, date or cron time
mapWorkflowNodemapWorkflowNode<S, T>(config: MapNodeConfig<S, T>, options?: { stateMapper? }): WorkflowNode<S>Process items in parallel
mapReduceWorkflowNodemapReduceWorkflowNode<S, T, R>(config: MapReduceNodeConfig<S, T, R>, options?: { stateMapper? }): WorkflowNode<S>Map, then reduce
subworkflowWorkflowNodesubworkflowWorkflowNode<PS, CS>(config: SubworkflowConfig<PS, CS>, options?: { checkpointStore? }): WorkflowNode<PS>Run a nested workflow
parallelSubworkflowsNodeparallelSubworkflowsNode<S, CS>(config: ParallelSubworkflowsConfig<S, CS>, options?: { checkpointStore? }): WorkflowNode<S>Run nested workflows in parallel

The *WorkflowNode adapters turn the configs built by the helpers below (approvalNode, delayNode, mapNode, subworkflowNode, …) into nodes for addNode. See Nodes.

Checkpoints

ExportSignaturePurpose
InMemoryCheckpointStorenew InMemoryCheckpointStore()Process memory
FileCheckpointStorenew FileCheckpointStore(directory?: string)JSON files
RedisCheckpointStorenew RedisCheckpointStore(options: { client: CheckpointRedisClient; keyPrefix? })Redis (any client with the listed methods)
PostgresCheckpointStorenew PostgresCheckpointStore(options: { client: CheckpointPgClient; table? })Postgres ({ query(text, values?) }, e.g. a pg pool)
createCheckpointIdcreateCheckpointId(): stringNew checkpoint id

CheckpointStore: save(checkpoint), load(id), list(workflowName), delete(id).

Human in the Loop

ExportSignaturePurpose
approvalNodeapprovalNode<S>(name, { title, description?, assignee?, timeout?, timeoutAction?: 'approve' | 'reject' | 'fail', priority? }): NamedHumanNodeConfig<S>Approve / reject
choiceNodechoiceNode<S>(name, { title, choices: ApprovalChoice[], … })Pick one of several choices
inputNodeinputNode<S>(name, { title, … })Free-form input
ratingNoderatingNode<S>(name, { title, … })Numeric rating
chainNodechainNode<S>(name, { title, chain: ApprovalChainStep[], … })Several approvers in turn
managementChainmanagementChain<S>(name, { title, manager, director?, vp?, timeoutPerStep? })Manager → director → VP chain
humanNodehumanNode<S>(name, approval: HumanNodeConfig<S>['approval']): NamedHumanNodeConfig<S>Any approval config
executeHumanNodeexecuteHumanNode<S>(state, config, context: HumanNodeContext): Promise<HumanNodeResult<S>>Run an approval outside a workflow
InMemoryApprovalStorenew InMemoryApprovalStore(options?: { ttl?; cleanupInterval? })Process memory
FileApprovalStorenew FileApprovalStore(options: { directory; pollInterval? })JSON files
RedisApprovalStorenew RedisApprovalStore(options: { client: ApprovalStoreRedisClient; keyPrefix?; pollInterval?; onPollError? })Redis, shared between processes
PostgresApprovalStorenew PostgresApprovalStore(options: { client: ApprovalStorePgClient; tablePrefix?; pollInterval?; onPollError? })Postgres, shared between processes
withDelegationwithDelegation(store: ApprovalStore, options?: { onDelegation? }): ApprovalStoreLet assignees delegate requests
ApprovalAlreadyAnsweredErrorclass extends Error { requestId; existing? }submitResponse on an answered request: the first answer wins
submitOrExistingsubmitOrExisting(store, response): Promise<ApprovalResponse>Submit, or return the answer that got there first
WITHDRAWN, withdrawnResponse, isUnansweredconstant, (requestId) => ApprovalResponse, (response) => booleanRequests deleted or expired before an answer resolve waiters with a withdrawn response
ConsoleNotifier, WebhookNotifier, CompositeNotifier, slackNotifier, filteredNotifier, priorityRouter, nullNotifierApprovalNotifiersTell people a request is waiting

ApprovalStore: createRequest, getRequest, getPendingRequests(workflowId?), getPendingForAssignee(assignee), submitResponse, getResponse, deleteRequest, onResponse(requestId, callback). HumanNodeResult: { approved, decision, response, state, timedOut?, escalated?, withdrawn? }; a humanWorkflowNode outputs { approved, decision, timedOut, escalated, withdrawn }, where escalated means the escalateTo assignee answered.

Timers and Cron

ExportSignaturePurpose
delayNodedelayNode(name, delay: number, options?): FixedDelayConfigFixed delay (ms)
dynamicDelayNodedynamicDelayNode<S>(name, getDelay: (state: S) => number, options?)Delay computed from state
cronWaitNodecronWaitNode(name, expression: string, options?): CronWaitConfigWait for the next cron time
untilNodeuntilNode<S>(name, getDate: (state: S) => Date | number, options?)Wait until a date
InMemoryTimerStore, FileTimerStorenew InMemoryTimerStore(), new FileTimerStore(directory)Local timer stores
RedisTimerStore, PostgresTimerStorenew RedisTimerStore({ client, keyPrefix?, claimTtl? }), new PostgresTimerStore({ client, table?, claimTtl? })Durable stores that claim due timers, so one worker fires each
TimerManagernew TimerManager(store: TimerStore, config?: TimerManagerConfig) — start, stop, schedule, cancel, registerHandler, setDefaultHandler, getStats, processNowPolls the store and fires handlers; renews claims while a handler runs and releases timers it did not handle
RecurringTimerSchedulernew RecurringTimerScheduler(manager) — scheduleRecurring, cancelRecurringRecurring timers
createInMemoryTimerStore, createFileTimerStore, createTimerManager, createRecurringScheduler, createTimerNodeHelpersfactories
parseCronExpression, validateCronExpression, isValidCronExpression, getNextCronOccurrence, getPreviousCronOccurrence, getNextCronOccurrences, msUntilNextCronOccurrence, cronMatchesDate, describeCronExpression, createCronIterator, CRON_PRESETS, CRON_FIELDS, getSupportedTimezones, isValidTimezonecron helpers
Duration, parseDuration, formatDurationparseDuration('5m') → msDuration helpers
calculateTimerDelay, executeTimerNode, AbortErrorfunctions, classTimer node internals

TimerManagerConfig: { pollInterval?, batchSize?, processOverdueOnStart?, cleanupMaxAge?, cleanupInterval?, enableCleanup?, maxRetries?, onError?, onTimerFired?, onTimerMissed?, onDeadLetter?, onClaimLost? }. See Scheduling.

Sagas

ExportSignaturePurpose
executeWithRetryexecuteWithRetry<T>(fn: (attempt) => Promise<T>, options?: RetryOptions<T>): Promise<RetryResult<T>>Retry with backoff
withRetry, RetryablewithRetry(fn, options?) → wrapped function; @Retryable(options?) decoratorRetry wrappers
estimateRetryDurationestimateRetryDuration(config?: Partial<RetryConfig>): numberWorst-case retry time
CircuitBreaker, createCircuitBreakernew CircuitBreaker(config?: Partial<CircuitBreakerConfig>) — canExecute(nodeId), recordSuccess, recordFailure, getState, getStats, forceState, reset, onEventPer-node circuit breaker
CircuitBreakerOpenError, WithCircuitBreakererror class, decorator
CompensationManager, createCompensationManagerregisterCompensation(nodeId, fn, options?), registerFromConfig(nodeId, config), markCompleted, compensate(state, failedNodeId, error, hooks?: CompensationHooks): Promise<CompensationReport>Undo completed steps after a failure
CompensationBuilder, compensationBuildercompensationBuilder<S>().addStep(nodeId, fn).addConditionalStep(…).build()Build a compensation manager
InMemoryDLQ, FileDLQ, BaseDLQ, createInMemoryDLQ, createFileDLQ, createDLQEntryadd, get, list(filters?), retry, remove, count, clearDead-letter queue
InMemoryIdempotencyStore, FileIdempotencyStore, BaseIdempotencyStore, createInMemoryIdempotencyStore, createFileIdempotencyStorecheck, store, get, delete, clearIdempotency records
idempotent, Idempotent, generateIdempotencyKey, generateCustomKeyidempotent(store, key, fn), decorator, key helpersRun once per key

CompensationHooks: { onStepStart?(nodeId), onStepComplete?(result: CompensationResult) }, called around each step that runs (steps skipped by their condition report neither). The executor runs the same logic for nodes with config.compensation. See Sagas.

Map-Reduce and Subworkflows

ExportSignaturePurpose
mapNode, reduceNode, mapReduceNodemapNode<S, T>(name, config): MapNodeConfig<S, T>, …Configs for mapWorkflowNode / mapReduceWorkflowNode
executeMap, executeReduce, executeMapReduceexecuteMapReduce<S, T, R>(state, config): Promise<MapReduceResult<T, R>>, …Run without a workflow
parallelMap, sequentialMap, batchedMap(state, items: (state) => unknown[], mapper, …)Map helpers
collect, sum, count, first, last, groupBy, partition, flatMap, statsreducer configsReady-made reducers
subworkflowNode, simpleSubworkflow, nestedSubworkflow, conditionalSubworkflowsubworkflowNode<PS, CS>(name, config): SubworkflowConfig<PS, CS>, …Configs for subworkflowWorkflowNode
parallelSubworkflows, fanOutFanIn, scatterGatherparallelSubworkflows<S>(name, config): ParallelSubworkflowsConfig<S>, …Configs for parallelSubworkflowsNode
executeSubworkflow, executeParallelSubworkflows, raceSubworkflows, fallbackSubworkflows(parentState, config, context: SubworkflowContext)Run without a node
MaxDepthExceededErrorerror classNesting deeper than allowed

See Patterns.

Run Management and Triggers

ExportSignaturePurpose
DefaultWorkflowManager, createWorkflowManagernew DefaultWorkflowManager(config: { cogitator; runStore?; checkpointStore?; maxConcurrency?; defaultTimeout?; tracer?; metrics?; onRunStateChange? })Queue, schedule and track runs
manager methodsstart, stop, registerWorkflow, schedule(workflow, options?: ScheduleOptions): Promise<string>, registerCronJob(workflow, expression, options?: { id?; timezone?; jobOptions? }): string, unregisterCronJob(jobId), setCronJobEnabled(jobId, enabled), getCronJobs(): CronJob[], execute(workflow, input?, options?), cancel(runId, reason?), pause(runId), resume(runId, options?): Promise<void>, retry, replay(workflow, runId, fromNode), getStatus, listRuns, getStats, getActiveCount, onRunStateChange, cleanup, dispose
InMemoryRunStore, FileRunStorenew InMemoryRunStore(), new FileRunStore({ directory, … })Run records
RedisRunStore, PostgresRunStorenew RedisRunStore({ client, keyPrefix? }), new PostgresRunStore({ client, table? })Durable run records
JobScheduler, PriorityQueue, createJobSchedulernew JobScheduler(config: SchedulerConfig)The manager's queue and cron scheduling
DefaultTriggerManager, createTriggerManagernew DefaultTriggerManager(config?: { store?; eventEmitter?; onTriggerFire? }) — register, unregister, enable, disable, list, fire, onTrigger, handleWebhook(request), emitEvent(type, payload), registerWorkflow(workflow, triggers), start, stopCron, webhook and event triggers
cronTrigger, webhookTrigger, eventTriggercronTrigger(expression, options?), webhookTrigger(path, method?, options?), eventTrigger(eventType, options?)Trigger configs
CronTriggerExecutor, WebhookTriggerExecutor, createCronTrigger, createWebhookTrigger, validateCronTriggerConfig, validateWebhookTriggerConfig, WebhookAuthError, WebhookRateLimitErrorexecutors and validation
InMemoryTriggerStore, SimpleTriggerEventEmitterclassesDefaults of the trigger manager
TokenBucket, RateLimiter, SlidingWindowRateLimiter, createRateLimiter, createSlidingWindowLimiterrate limitersWebhook rate limiting

schedule() with cron queues one run at the next occurrence; registerCronJob queues a run (with jobOptions, triggerId: 'cron:<jobId>') on every occurrence while the manager is started. pause() needs a checkpointStore and aborts the run; resume() continues it from its last checkpoint with the options it was started with (unless you pass new ones) and resolves once it is running. Scheduled runs get the manager's checkpoint store, tracer, metrics and their timeout ?? defaultTimeout; ScheduleOptions.maxRetries retries a failed scheduled run (counted in metadata.retryAttempt). A run's workflowId defaults to its run id. See Scheduling.

Observability

ExportSignaturePurpose
WorkflowTracer, createTracer, getGlobalTracer, setGlobalTracernew WorkflowTracer(config?: Partial<TracingConfig>)Spans per workflow and node
WorkflowMetricsCollector, createMetricsCollector, getGlobalMetrics, setGlobalMetricsnew WorkflowMetricsCollector(config?: Partial<MetricsConfig>) — isEnabled()Run and node metrics
ConsoleSpanExporter, OTLPSpanExporter, ZipkinSpanExporter, CompositeSpanExporter, NoopSpanExporter, createSpanExportercreateSpanExporter(config: ExporterConfig)Span exporters

Pass a tracer and metrics collector to execute() as tracer / metricsCollector. A run that passes none uses the global ones (setGlobalTracer / setGlobalMetrics) when they are enabled — tracer.isSampled() / metrics.isEnabled(); the built-in globals are disabled.

Types

Re-exported from @cogitator-ai/types: Workflow, WorkflowState, WorkflowNode, WorkflowResult, WorkflowEvent, StreamingWorkflowEvent, WorkflowExecuteOptions, WorkflowCheckpoint, CheckpointStore, NodeContext, NodeResult, NodeConfig, NodeFn, the edge and builder option types, the saga types (RetryConfig, CircuitBreakerConfig, CompensationConfig, DeadLetterEntry, IdempotencyStore, …), the human types (ApprovalRequest, ApprovalResponse, ApprovalStore, ApprovalNotifier, HumanNodeConfig, …), the timer types (TimerEntry, TimerStore), the manager types (WorkflowRun, RunStore, WorkflowManager, ScheduleOptions, …), the trigger types (CronTriggerConfig, WebhookTriggerConfig, EventTriggerConfig, WorkflowTrigger, TriggerManager, …) and the tracing types (TracingConfig, MetricsConfig, WorkflowSpan, …).

On this page