Kilin
Concepts

Watching a run

Monitor a running workflow as a person through the Viewer, or as an agent through JSON.

Workflow authoring, execution, monitoring, and approval are available as commands with --json forms, so the thing driving Kilin can equally be a person at a terminal or an outer agent runtime supervising work it delegated. Non-interactive setup uses an explicit provider selection such as kilin skills link --providers agents. The Viewer remains a human inspection surface, while an outer agent can launch and supervise its attached CLI process through --json.

┌─────────────────────────────────────────────────────────────────┐
│  OUTER AGENT RUNTIME                                            │
│  a person at a terminal  ·  or Codex / Claude Code / a script   │
└─────────────────────────────────────────────────────────────────┘
        │                                             ▲
        │  create   kilin workflow init               │  monitor   runs list · runs show
        │  update   edit WORKFLOW.yaml                │  track     runs wait --json
        │  start    kilin run · kilin trigger         │  inspect   kilin ui
        │  decide   runs approve · runs reject        │
        ▼                                             │
┌─────────────────────────────────────────────────────────────────┐
│  KILIN                                                          │
│  validate → compile → immutable revision → execute              │
│  SQLite history  ·  captured logs  ·  JSONL event stream        │
└─────────────────────────────────────────────────────────────────┘
        │                                             ▲
        │  spawns provider subprocesses               │  results · logs · decisions
        ▼                                             │
┌─────────────────────────────────────────────────────────────────┐
│  INNER WORKFLOW — one run                                       │
│                                                                 │
│    analyze  ──▶  implement  ──▶  [approval]  ──▶  verify        │
│    read_only     workspace_write   barrier        read_only     │
│    Claude Code   Codex                            Codex         │
└─────────────────────────────────────────────────────────────────┘

The outer runtime stays outside the run. It cannot reach into a running node, and no edit to WORKFLOW.yaml can change what this run executes, because Kilin records an immutable revision before the first node starts.

Both views below read the same local SQLite history. Neither is a reduced version of the other.

As a person

Open the Viewer for the workflow you are running:

kilin ui change-review --cwd /absolute/path/to/project

The Viewer binds numeric 127.0.0.1 on an operating-system-selected port and shows the compiled graph, node states as they change, the newest 50 runs for that exact workflow identity and working directory, lineage, approval metadata, failures, bounded captured output, and Decision Packets.

On load the Viewer opens the most relevant stored run — one waiting for approval first, then a running run, then the newest finished run — and selects the node that explains the run status. With no stored runs it keeps the current definition view. The selected run, node, stream, and rendered or raw view persist in the URL hash, so a reload restores your place.

The top bar carries a Refresh control that requests the current state at once and restarts the poll backoff. When a captured stream fails to load, the output panel offers a Retry control that requests that stream again, and Refresh requests it again too. When the graph is taller than the strip that holds it, an Expand control appears beside the workflow status and raises the height cap, so a workflow with many parallel branches needs less scrolling; the choice survives polling until you collapse it again.

Beside Refresh, a notification control appears where your browser supports notifications. Granting permission lets the Viewer tell you when a run starts waiting for approval while its tab is hidden — it raises a notification for that run, and clicking it brings you back there. A hidden tab keeps polling at a reduced fifteen-second cadence rather than stopping, so an approval that arrives while you are elsewhere is still picked up; switching back polls at once and resumes the normal cadence.

Selecting a node scrolls the graph to it, so a node below or beside the visible area of a large workflow comes into view without scrolling the strip yourself.

A loop node keeps the word loop on its card and adds its iteration progress, loop · 2/3, once an iteration starts; until then it keeps its bound, loop · up to 3. Selecting it expands the card into a container that draws the body pipeline at the latest started iteration — the body nodes with their statuses, the revise feedback edge, and the pass exit — and selecting a body node opens that execution's evidence. Selecting an execution from an earlier iteration draws that iteration instead. A workflow whose only node is a loop opens expanded. The Loop iterations panel lists every recorded iteration and names the body before the first one runs.

When a run stops at an approval, the Viewer shows guarded Approve and Reject buttons alongside the equivalent CLI commands. The optional note is a multi-line field, so Enter starts a new line and only Approve or Reject submits. A running run also offers a Cancel run button beside the equivalent kilin runs cancel command. Those two are its whole state-mutation surface, a closed set: it cannot edit workflows, start provider runs, or schedule work. Pass --no-open to print the launch URL instead of opening a browser, and treat that URL as a credential.

Without a browser, the same history is available as text:

kilin runs list
kilin runs show <run-id>

As an agent

The bundled run skill validates the visible package, starts kilin ui <id> --cwd <directory> --no-open --json as a managed attached process when the requester's browser can reach the same loopback, and returns the viewer.started URL directly. The Viewer remains alive after run success or failure until it is stopped or the outer-agent session ends. If the agent runs on another machine or cannot retain the process, it returns the manual command with the requester's local project path instead of claiming the remote 127.0.0.1 URL is accessible. That local Viewer sees the same history only when it can access the same Kilin data.

kilin run --json streams one JSON object per line. Every event carries outputVersion: 1 and a type:

run.started · node.started · node.finished · approval.requested
approval.resolved · run.finished · error

Rather than polling, block until the run next needs attention or reaches a terminal state:

kilin runs wait <run-id> --json

A controller records its decision from a second local process while the run stays attached in the first, so nothing has to scrape a terminal:

kilin runs approve <run-id> <approval-node-id> --actor agent
kilin runs cancel <run-id>

Stopping the attached kilin run process itself also stops the run. SIGINT, SIGTERM, and SIGHUP all route through the same cancellation path, so a supervisor, container stop, CI cancellation, or a closed terminal terminates the provider process tree instead of orphaning it, and the command exits 130. If a run is killed outright and cannot clean up, the next command to work in that directory — including another kilin run — ends the processes it left behind before it starts.

Failures carry stable error codes — NODE_TIMEOUT, LOOP_LIMIT_REACHED, APPROVAL_REJECTED, and the rest — so a supervising agent can branch on the code rather than parse prose. runs list, runs show, and workflow validate also accept --json.

What monitoring never exposes

Declared run parameters are deliberately excluded from lifecycle events, runs list, runs show, and the Viewer. Loop iterations are grouped without exposing parameter, feedback, decision-choice, or result values.

Captured stdout, stderr, and results remain private local files under the data directory rather than event payloads. See trust boundaries for what that history can contain, and the command reference for the full flag surface.

On this page