Read a run's journal
The journal is the ordered record of a run: every step that started, completed or failed, with its input and output, and every edge the run evaluated. It's written before the run moves on, so it's also how the run continues after a wait.
kindgi runs journal <run-id>For a run of acme.review-order with order A-200:
{ "data": [ { "sequence": 0, "kind": "run.started", "payload": { "input": { "orderId": "A-200" } }, "timestamp": "2026-10-03T20:45:08.120Z" }, { "sequence": 1, "kind": "edge.evaluated", "payload": { "edgeId": "e1", "decision": true }, … }, { "sequence": 2, "kind": "step.started", "nodeId": "order", "payload": { "input": { "orderId": "A-200" } }, … }, { "sequence": 3, "kind": "step.completed", "nodeId": "order", "payload": { "output": { "items": [{ "sku": "desk", "quantity": 1 }, { "sku": "lamp", "quantity": 2 }], "total": 1250, "orderId": "A-200", "customer": "grace@example.com" } }, … }, { "sequence": 4, "kind": "edge.evaluated", "payload": { "edgeId": "e2", "decision": true }, … }, { "sequence": 5, "kind": "edge.evaluated", "payload": { "edgeId": "e3", "decision": false }, … }, { "sequence": 6, "kind": "step.started", "nodeId": "hold", "payload": { "input": { "reason": "Over 1000 USD", "orderId": "A-200" } }, … }, { "sequence": 7, "kind": "step.completed", "nodeId": "hold", "payload": { "output": { "status": "held", "orderId": "A-200" } }, … }, { "sequence": 8, "kind": "edge.evaluated", "payload": { "edgeId": "e4", "decision": true }, … }, { "sequence": 9, "kind": "run.completed", "payload": { "output": { "status": "held", "orderId": "A-200" } }, … } ], "hasMore": false}Read it top to bottom: the input, the order looked up, the branch taken
(e2 fired, e3 didn't, so confirm never ran), the hold, the result.
--since=<sequence> returns only the entries after that one, to read what's
new since you last looked.
The entries
Section titled “The entries”kind |
|
|---|---|
run.started, run.completed, run.failed, run.cancelled |
The run's start (with its input) and end (with its output, or why it failed). |
step.started, step.completed, step.failed |
A step, with the input it got after mapping, its output, or its error. |
edge.evaluated |
An edge and its decision. |
step.retry-scheduled |
A failed attempt that will be retried. |
iteration.started, iteration.completed |
A pass of a loop; the body's steps carry a loopContext. |
fanout.dispatched, fanout.branch-completed, fanout.converged, … |
The branches of a fanout step. |
wait.suspended, wait.resumed |
The run stopped to wait, and went on. |
value.recorded |
A decision a step made once and keeps when it runs again after a wait: which approval an approval gate waits on, or a clock read. |
Agent turns
Section titled “Agent turns”An agent step runs the agent's turn as a run of its own. Its id is in the
step's output (runId), and the turn's run has the flow's run as its
parentRunId:
kindgi runs journal <the turn's run id>The turn's journal has the model calls (the messages sent, the answer, the tokens and the cost of each) and the tool calls. That's where to look when an agent step answered something you didn't expect. List runs shows how to find the turns of a run.
From your app
Section titled “From your app”type JournalEntry = { sequence: number; kind: string; nodeId?: string; payload?: unknown };
const { data } = await kindgi.runs.journal(runId);for (const entry of data as JournalEntry[]) { console.log(entry.sequence, entry.kind, entry.nodeId ?? '');}for entry in kindgi.runs.journal(run_id).data: print(entry.sequence, entry.kind, entry.node_id or "")For a run of acme.escalate-order that waited for an approval:
0 run.started1 edge.evaluated2 step.started order3 step.completed order4 edge.evaluated5 step.started hold6 wait.suspended hold7 wait.resumed hold8 step.started hold9 step.completed hold10 edge.evaluated11 edge.evaluated12 step.started email13 step.completed email14 edge.evaluated15 run.completed