Follow a run's events
A run's events are its journal as it's written: the run starting, each step starting and ending, waits, and the run's end. Follow them to show progress, or to react when the run ends without polling.
Ask your coding agent: "I want my app to show a run's progress as it happens." For reference, the code looks like this. It starts a run in the background, then prints each event until the run ends:
import { createClient } from '@kindgi/sdk/client';
const kindgi = createClient();
const run = await kindgi.runs.start({ flow: 'acme.reserve-order', input: { orderId: 'A-400' }, options: { wait: false },});
for await (const event of kindgi.runs.follow(run.id)) { console.log(event.sequence, event.kind, event.nodeId ?? '');}// The loop ends after run.completed, run.failed or run.cancelled.0 run.started2 run.step-started reserve3 run.step-retry-scheduled reserve4 run.step-retry-scheduled reserve5 run.step-completed reserve7 run.completedfrom kindgi.client import Kindgi
kindgi = Kindgi()
run = kindgi.runs.start( flow="acme.reserve-order", input={"orderId": "A-400"}, options={"wait": False})
for event in kindgi.runs.follow(run.id): print(event.sequence, event.kind, event.node_id or "")# The loop ends after run.completed, run.failed or run.cancelled.0 run.started2 run.step-started reserve3 run.step-retry-scheduled reserve4 run.step-retry-scheduled reserve5 run.step-completed reserve7 run.completedpackage com.acme.app;
import com.kindgi.client.EventStream;import com.kindgi.client.Kindgi;import com.kindgi.client.models.Run;import com.kindgi.client.models.RunEvent;import com.kindgi.client.models.StartRunBody;import java.util.Map;import java.util.Objects;
public final class FollowRun { public static void main(String[] args) { Kindgi kindgi = Kindgi.create();
Run run = kindgi.runs().start(StartRunBody.WithFlow.builder() .flow("acme.reserve-order") .input(Map.of("orderId", "A-400")) .options(StartRunBody.WithFlow.Options.builder().wait_(false).build()) .build());
try (EventStream<RunEvent> events = kindgi.runs().follow(run.id())) { for (RunEvent event : events) { System.out.println(event.sequence() + " " + event.kind() + " " + Objects.toString(event.nodeId(), "")); } } // The loop ends after run.completed, run.failed or run.cancelled. }}0 run.started2 run.step-started reserve3 run.step-retry-scheduled reserve4 run.step-retry-scheduled reserve5 run.step-completed reserve7 run.completedpackage acme.app
import com.kindgi.client.Kindgiimport com.kindgi.client.models.StartRunBodyimport scala.jdk.CollectionConverters._import scala.util.Using
object FollowRun { def main(args: Array[String]): Unit = { val kindgi = Kindgi.create()
val run = kindgi.runs().start(StartRunBody.WithFlow.builder() .flow("acme.reserve-order") .input(Map("orderId" -> "A-400").asJava) .options(StartRunBody.WithFlow.Options.builder().wait_(false).build()) .build())
Using.resource(kindgi.runs().follow(run.id)) { events => events.forEachRemaining { event => println(s"${event.sequence} ${event.kind} ${Option(event.nodeId).getOrElse("")}") } } // The loop ends after run.completed, run.failed or run.cancelled. }}0 run.started2 run.step-started reserve3 run.step-retry-scheduled reserve4 run.step-retry-scheduled reserve5 run.step-completed reserve7 run.completedThe stream starts from the run's first event, whenever you open it, so you
don't miss what happened before. It ends after run.completed, run.failed or
run.cancelled; the last event's payload.output is the run's output.
The events
Section titled “The events”Each event has the run's id, a kind, a sequence (its place in the
journal), a timestamp, the step's nodeId for step events, and a payload:
kind |
When | payload |
|---|---|---|
run.started |
The run started. | input |
run.step-started |
A step started. | input |
run.step-completed |
A step ended. | output |
run.step-failed |
A step failed. | message |
run.step-retry-scheduled |
A step failed and will be retried. | attempt, nextDelayMs, previousError |
run.iteration-started, run.iteration-completed |
A pass of a loop. | iteration, … |
run.wait-suspended, run.wait-resumed |
The run stopped to wait, and went on (approvals). | |
run.completed |
The run completed. | output |
run.failed |
The run failed. | message |
run.cancelled |
The run was cancelled. |
Edge decisions are in the journal, not in the stream.
Reconnecting
Section titled “Reconnecting”The server ends a stream after 5 minutes; a run that waits longer needs a new
one. Each event's id is <run id>:<sequence>, and a new stream opened with
the last id you saw (the Last-Event-Id header) starts after it.
runs.follow does this for you, in TypeScript, Python, Java and Scala (the
Java client's runs().follow): it reconnects after a dropped connection or
the server's limit, and ends only with the run. runs.followProgress
(Python: runs.follow_progress; Java: runs().followProgress) follows the
progress stream (no payloads) the same way.
- In Python,
runs.streamis the plain call: it ends when the server closes the stream, andlast_event_id=f"{run.id}:{event.sequence}"opens it again after that event. - In Java and Scala,
runs().stream(runId)is the plain call too, andRunsStreamParams.builder().lastEventId(…)opens it again after an event. - In TypeScript,
runs.streamstill follows the run to its end, asfollowdoes, but it's deprecated: in a later release, announced in advance, it becomes the plain call, as in Python.
From the CLI
Section titled “From the CLI”pnpm exec kindgi runs stream <run-id>uv run kindgi runs stream <run-id>./kindgiw runs stream <run-id>./kindgiw runs stream <run-id>It prints one event per line, as JSON:
{"eventId":"28795fe7-a357-4d67-8740-1bda88f3b85c:0","runId":"28795fe7-a357-4d67-8740-1bda88f3b85c",…,"kind":"run.started","sequence":0,"payload":{"input":{"orderId":"A-300"}}}{"eventId":"28795fe7-a357-4d67-8740-1bda88f3b85c:2",…,"kind":"run.step-started","sequence":2,"nodeId":"reserve","payload":{"input":{"orderId":"A-300"}}}…{"eventId":"28795fe7-a357-4d67-8740-1bda88f3b85c:7",…,"kind":"run.completed","sequence":7,"payload":{"output":{"attempt":3,"orderId":"A-300","reserved":true}}}To show a run's progress in a browser, without giving the page your API token, see Follow a run from the browser.