Skip to main content

workflow

Declares a workflow: its name, what triggers it, how it retries and runs under load, and the handler that does the work. It returns the definition unchanged - it exists for type inference over your event data, so a type parameter types ctx.event.data.
string
required
Unique workflow name.
(ctx: StepContext<TData>) => Promise<unknown>
required
The function that does the work; receives the run context.
Trigger[]
What starts the workflow: event triggers ({ event, if? }, where event may end in a trailing ”*” wildcard and if is a CEL filter) and cron triggers ({ cron }, with an optional “TZ=Area/City” prefix). Omit for an implicit event trigger matching the workflow name.
RetryConfig
Per-workflow retry policy each step inherits ({ maxAttempts, backoff?, initialDelayMs?, maxDelayMs? }).
ConcurrencyConfig
Cap on runs executing at once in a scope ({ limit, key? }); over-limit runs wait and retry as slots free.
RateConfig
Smooth cap on run starts ({ limit, perMs, key? }); overflow is delayed into the future, never dropped.
RateConfig
Shedding cap on run starts ({ limit, perMs, key? }); overflow is dropped and the event response reports dropped: true.
DebounceConfig
Collapse a burst to its last event ({ periodMs, key? }); each new event slides the deadline and replaces the payload.
BatchConfig
Fold many events into one run ({ maxSize, timeoutMs, key? }), flushing on whichever comes first; the run reads them as ctx.events.
PriorityConfig
Dequeue this workflow’s runs ahead of others ({ shiftMs }) by treating them as enqueued that many ms earlier.
SingletonConfig
At most one non-terminal run per key ({ key?, mode? }); mode “cancel” (the default) cancels the running run, “skip” drops the new trigger.
IdempotencyConfig
One run per derived key within a window ({ key?, periodMs? }, periodMs defaulting to 24h); a duplicate is dropped and the response reports deduped: true.
CapConfig
Hard per-run AI spend ceiling ({ maxCost?, maxTokens? }); a run halts before the step.ai call that would cross it and fails with a BudgetError.
TokenThrottleConfig
Token-denominated rate limit on the workflow’s AI spend ({ tokens, perMs, key? }); each step’s actual tokens are debited and new run starts for a drained key are spread into the future, holding no worker.
StepManifestEntry[]
Optional advisory manifest of the workflow’s steps in declaration order, so a UI can render a progress bar (“step 12 of 18”) and mark bookkeeping steps hidden. Advisory only: it never constrains execution and is diffed against the actually-executed steps.
ToolManifestEntry[]
Optional advisory manifest of the workflow’s tools, so a UI can list a tool that has never run. Advisory only: it never constrains what an agent actually calls.
AgentManifestEntry[]
Optional advisory manifest of the workflow’s agent loops - what bounds each one, never what it says. Advisory only: it never constrains execution. See Agent manifest below.
(ctx: StepContext<TData>) => Promise<unknown>
Runs durably after the run has exhausted its retries and failed; receives the original event plus ctx.error. It cannot un-fail the run.

Step manifest (optional)

By default a step exists only once it has executed - Duraton discovers steps at runtime, so the run view has no plan to draw a progress bar from until each step has run. Declaring an optional steps manifest gives the UI the plan up front: the declared step names, their order, an optional description, and a hidden flag for bookkeeping steps. Reach for it only when a workflow has several steps to preview, describe, or hide. A single-step workflow, or one with nothing to describe or hide, needs no manifest - omit it and the run view still renders each step as it executes. The manifest’s name must match the id you pass to ctx.step.run; that is the only reason a step id appears twice.
string
required
The step id. A declared step is matched to an executed step by this name.
string
Optional human-readable label for the step.
boolean
Excludes the step from the customer-facing progress view; it stays present for admin/debug views.
The manifest is advisory metadata for rendering, never a constraint on execution:
  • It never gates a run, never fails a run for drift, and a workflow with no manifest behaves exactly as before.
  • The run view diffs declared vs executed steps by name: a declared step that has not run yet renders as pending (this is what powers “step 12 of 18”); a step that runs but was not declared still renders (discovery wins); a declared step that a conditional path skips simply stays pending / not-reached.
  • Steps execute at runtime in whatever order the handler runs them, including in parallel - the manifest order is presentation order only.
  • hidden steps are excluded from the customer-facing progress but returned in the read model so an admin/debug view can show them.
The manifest is returned by GET /workflows and the list_workflows MCP tool alongside each workflow’s triggers and flow control.

Tool manifest (optional)

The engine has no declarative knowledge of an agent’s tools until a run calls one - steps above solves this for steps, and tools is the same fix for tools. Declaring an optional tools manifest lets a UI list a tool - its name, description, and parameter schema - before it has ever run, which is what a Builder-style tool picker needs. Build it with @duraton/agent-kit’s toolManifest(), from the same tools/mcpServers arrays the handler’s agent() call uses - one array, two readers, so the manifest can never silently disagree with what the model actually sees:
string
required
The tool name the model calls.
string
Optional human-readable label.
Record<string, unknown>
The tool’s JSON Schema input, so a UI can render a parameter form.
Record<string, unknown>
The tool’s JSON Schema output, if declared.
Record<string, unknown>
MCP behaviour hints (readOnlyHint, destructiveHint, idempotentHint, openWorldHint). They inform a default; they never gate.
boolean
Whether this tool parks a run on a human before it executes.
string
The integration this tool reaches, e.g. “google-calendar” - a free-text label, not a Credential id. A code-declared tool is the same code for every tenant, so it names an integration, never a specific tenant’s credential.
string
The agent() call this tool belongs to, for a workflow with more than one agent. Undefined for the common one-agent-per-workflow case.
"declared" | "mcpServer"
required
A hand-declared tool, or an attached MCP server.
The manifest is advisory in the same way steps is - it never gates a run, never fails a run for drift, and a workflow with no manifest behaves exactly as before. AC#11’s both directions hold: a tool the agent offers that is absent from the manifest still runs, and an entry the agent no longer offers never blocks a run. An MCP server’s tools are never enumerated here. toolManifest() projects an attached mcpServers entry as a group placeholder - only its name and source: "mcpServer" - because discovery is a durable step at run time, not registration time. Contacting the server up front to fill the manifest would reintroduce the exact replay drift durable discovery exists to prevent (a server that adds or drops a tool mid-run must not change what a replay sees), and would make registration depend on a third party being reachable. A picker reading the manifest sees the server as an unexpanded group whose tools become known once a run has actually discovered them. Every flow-control field is optional and off by default; each one’s semantics, keys, and overflow behaviour are in the flow-control reference. onFailure is covered in retries, triggers in triggers.

RetryConfig

The shape of the retry field - the per-workflow retry policy each step inherits.
number
required
Total attempts before the run fails, counting the first one.
"fixed" | "linear" | "exponential"
default:"\"fixed\""
How the delay between attempts grows: fixed is constant; linear is initialDelayMs * attempt; exponential is initialDelayMs * 2^(attempt-1).
number
default:"1000"
The base delay before the second attempt, and the unit the backoff shape multiplies.
number
default:"30000"
An upper bound the computed delay is capped at, so exponential backoff cannot grow without limit.

Agent manifest (optional)

Everything that shapes an agent - its model, its ceilings, its guardrails, the conditions that stop it - lives in your code, so nothing about it is visible until a run has happened. Declaring an agents manifest reflects that configuration onto the workflow definition, so a console can list the agent, its model and what bounds it before it has ever run. Build it with the kit’s agentManifest() from the same options object the handler passes to agent(), so the two cannot drift:
The entry’s id is the agent() step id, which is also the agentId its tools carry in the tool manifest - so the tools belonging to an agent are recoverable without listing their declarations twice.
The manifest deliberately has no prompt and no instructions field. Prompt text never enters the durable record, and a definition row an operator can read is that same audience.
Like the step and tool manifests it is one-way and advisory. Duraton stores it, re-emits it on the workflow read, and never reads it back to drive execution: an agent that no longer matches its entry still runs, and a workflow that omits it behaves exactly as before.