Run agents inside a process you can replay.

Adaptive process orchestration: BPMN, CMMN and DMN run as written, an AI agent is one step inside them, and every step is recorded, so any run replays against the process definition that authorised it.

case 4821 · history1 row edited after the fact
  1. 1case.startedrefund.bpmn v3 · build pinnedpinned
  2. 2condition.evaluatedamount <= 500 · trueamount <= 500 · falsere-deriveddiverged
  3. 3command.dispatchedAssess claim · agent:runre-derived
  4. 4agent.turngpt-4.1 · 2 tool calls · response keptfed in
  5. 5command.resultAssess claim · recommend: approvefed in
  6. 6human.gateApprove refund · claims-leadfed in
  7. 7state.transitionApprove refund · completedre-derived
  8. 8case.completedPay outre-derived

$ kirto-flow-replay -model refund.bpmn -history case-4821.json

{"chainLen": 4, "match": true, "versionSkew": false, …}exit 0

{"chainLen": 4, "divergences": [{"index": 0, "field": "attrs", …}], "match": false, …}exit 1

An illustrative case. Replay feeds the recorded agent turns and results back in, derives the rest again from the pinned model, and compares. A record edited after the fact does not replay.
Replay

What a replay checks

A case's record holds two kinds of row: what the world handed the case, and what the engine derived from it. Replay feeds the first back in and derives the second again.

  1. The inputs are fed back in. Signals, messages, timer fires and task results, in the order they arrived. A model's answer and a tool's result cannot be recomputed, so the recorded ones are used.
  2. The rest is derived again. A fresh engine on the pinned model turns the inputs into conditions evaluated, decisions made, commands dispatched, transitions and completion. It carries no state from the original run and does no I/O.
  3. The two are compared by index. Each derived row is checked against the stored row at the same position, on type, element id and attributes. The sequence number, timestamp and pins are written by the host and are not compared. Every mismatch is reported with both values.

A clean replay shows the engine reached the stored chain from the inputs alone. The same replay runs in the browser modeller, on the gateway's replay endpoint, and in CI, where the exit code is the verdict.

Replay a case
$ kirto-flow-replay -model refund.bpmn -history case-4821.json
{
  "chainLen": 5,
  "divergences": [
    {
      "field": "attrs",
      "index": 0,
      "recorded": "{\"expression\":\"amount \u003c= 50\",\"result\":true}",
      "replayed": "{\"expression\":\"amount \u003c= 500\",\"result\":true}"
    },
    {
      "field": "elementID",
      "index": 1,
      "recorded": "refuse",
      "replayed": "pay"
    }
  ],
  "match": false,
  "recordedWith": "0.1.0-dev",
  "replayedWith": "0.1.0-dev",
  "versionSkew": false
}
$ echo $?
1
exit 0
Every derived row matches the stored one.
exit 1
A row diverged, or the replaying build differs from the recording build at MAJOR.MINOR.
exit 2
The replay could not run: a missing flag, an unreadable file, a history with no case.started.

Output of kirto-flow-replay on a two-task refund case recorded by Flow's engine, after two stored rows were edited: the gateway's condition and the task it dispatched.

Process

The process governs the agent

Three OMG standards run as written, and an AI agent is one step inside them.

The path

BPMN 2.0

Processes whose order is known: gateways, events, subprocesses, compensation, multi-instance. An agent is a service task.

The judgement calls

CMMN 1.1

Cases whose next step is not known in advance. Discretionary items, planning tables and sentries decide what may happen next, including what an agent may call.

The rules

DMN

Decision tables and FEEL that a domain expert reads and checks. A business rule task evaluates them inside the engine, and the case records the rules that matched.

Process
BPMN 2.0
Case
CMMN 1.1
Decisions
DMN, with FEEL compiled to CEL
DMN TCK
3,494 of 3,494 cases pass, across 147 models
Model providers
anthropic, openai, gemini
Hosts
The broker (kirto-flow), and DBOS on Postgres
Agent loop

A reason/act loop that can be replayed

agent:run is a capability like any other. A BPMN service task or a CMMN task names an agent spec, and the spec names the provider, the model, the prompt, the tools and the limits. A case pins the spec version it started with.

Each model call and each tool call is recorded before the next one starts. After a crash the loop resumes from the record, and a call that completed is not made again. A call in flight when the worker died is dispatched again, with the same idempotency key.

An agent's tools are the process's own connectors, resolved through the same registry and described by the same schema. A connector written once is a service task and a tool.

The static agent
  • Every turn is a record. An agent.turn row keeps the model's response and the OpenTelemetry GenAI attributes. A replay feeds it back in.
  • The tools are an allowlist. A namespace or one exact tool. A call outside it is refused before the connector runs, and the model is told.
  • A person can gate a call. Calls in an approval namespace wait for a decision in a durable suspension, which survives a restart.
  • The limits are hard. A turn cap is required. A wall-time deadline and a token budget are optional, and each ends the loop with a code an error boundary catches.
  • The case can decide the tools. In case mode the tool list is what the case grants now, CMMN discretionary items or BPMN ad-hoc activities, re-read every turn.
Architecture

Pure engines, a host underneath

01

The standards are the model

A model is BPMN, CMMN or DMN XML. A case resolves a concrete model version and agent spec version when it starts, so publishing a new version does not move a running case.

02

The engines do no I/O

Each engine is a pure reducer: Advance(state, event) returns the commands to carry out and the next state. FEEL and DMN evaluate inside it. Every task, timer, message and model call leaves it as a command. The same state and event give the same commands, a determinism test holds the engines to it, and replay rests on it.

03

The host is replaceable

The broker runs the engines over a Raft log, and kirto-flow puts broker, engines, connectors and gateway in one binary that needs only a data directory. The DBOS host runs cases on Postgres. The model and the connector code are the same on both.

Modeller

Model in the browser. Deploy to production.

The engine in the browser

The modeller loads Flow's engines compiled to WebAssembly. Start a process or a case, step it event by event, and see which elements fire, which conditions evaluate and what state results, before anything is deployed. FEEL, DMN, validation and replay run there too.

Panels from the connectors

Each connector describes its operations with a JSON Schema derived from its input struct. The modeller renders a task's properties panel from it, so the form and the code read one definition.

Publish, then activate

A model is published as a new version through the gateway's API, then activated. A new case that asks for the latest version gets the active one. Running cases keep the one they started on, and their records replay against it.

What Flow does not do

  • Replay checks derivation, not inputs. It shows the stored chain follows from the stored inputs over the pinned model. An input edited together with every row derived from it replays clean. The record is not signed.
  • Replay holds within a minor version. A build that differs from the recording build at MAJOR.MINOR exits 1, because a reducer change can change what the engine derives.
  • Case mode needs the DBOS host. The broker's agent engine has no per-turn grant event yet and refuses a case-mode agent spec.
  • Control acts between turns. A revoked grant, and on DBOS the deadline, take effect at the next turn. A model call in flight finishes.
  • Budgets are turns, time and tokens. There is no spend cap in currency.
  • Three model providers. Anthropic, OpenAI and Gemini, each through its own SDK. A spec's base URL points an adapter at another endpoint.