Skip to content

Read a run's journal

View as Markdown

This guide reads a run's journal: every step it took, with its input and output, the branches it followed, and the agent turns inside it.

The journal is the ordered record of a run, written before the run moves on. Runs, the journal and durability explains what it holds and why.

  • A run's id, on a runtime such as kindgi dev, with Kindgi 0.1's CLI. List runs finds one.
  • For the app's half: Node 22.12 or later, Python 3.11 or later, or Java 17 or later, with Kindgi 0.1's client, and KINDGI_API_URL and KINDGI_API_TOKEN set.

The journal shows what the run did, step by step:

Terminal window
pnpm exec kindgi runs journal <run-id>

<run-id> is the run's id. For a run of acme.review-order with order A-200, it prints:

{
"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
}
Excerpt

Read it top to bottom: the input, the order looked up, the branch taken, the hold, and the result. e2 fired and e3 didn't, so confirm never ran.

To read only what's new since you last looked, add --since=<sequence>, where <sequence> is the last entry you read. It returns the entries after that one.

To page it, add --limit=<n>, then --cursor=<nextCursor> from the page before.

kind
run.started, run.completed, run.failed, run.cancelled The run's start, with its input, and its 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.

An agent step runs the agent's turn as a run of its own. When an agent step answered something you didn't expect, the turn's journal shows why.

The turn's id is in the step's output, as runId. The turn's run has the flow's run as its parentRunId, so List runs finds it too. Read its journal:

Terminal window
pnpm exec kindgi runs journal <the turn's run id>

<the turn's run id> is the turn's runId. Its journal has the model calls and the tool calls. Each model call has the messages sent, the answer, and its tokens and cost.

Your app reads a journal with runs.journal. Ask your coding agent: "I want my app to show every step a run took." For reference, this function prints a run's journal, one entry a line:

app/print-journal.ts
import { createClient } from '@kindgi/sdk/client';
import type { RunId } from '@kindgi/sdk/types';
const kindgi = createClient(); // KINDGI_API_URL, KINDGI_API_TOKEN
type JournalEntry = { sequence: number; kind: string; nodeId?: string; payload?: unknown };
/** Prints a run's journal, one entry a line. */
export async function printJournal(runId: RunId) {
const { data } = await kindgi.runs.journal(runId);
for (const entry of data as JournalEntry[]) {
console.log(entry.sequence, entry.kind, entry.nodeId ?? '');
}
}

For a run of acme.escalate-order that waited for an approval, it prints:

0 run.started
1 edge.evaluated
2 step.started order
3 step.completed order
4 edge.evaluated
5 step.started hold
6 wait.suspended hold
7 wait.resumed hold
8 step.started hold
9 step.completed hold
10 edge.evaluated
11 edge.evaluated
12 step.started email
13 step.completed email
14 edge.evaluated
15 run.completed

runs.journal returns the whole journal in one answer. To read a long one in pages, pass limit (1 to 100): each page has hasMore (has_more in Python), and nextCursor (next_cursor) while more remain, which you pass as cursor for the next page. Paging needs a runtime of 0.1.8 or later.