# Headless runs and JSON events

> Run one Code turn from a prompt or standard input, constrain workspace authority and cost, and consume JSON events without confusing them with session SSE.

Source: https://kaitoi.io/docs/cli/headless/
Section: Kaitoi CLI / Editors and automation
Written for: Developers scripting a single Code task.
Prerequisites: Kaitoi CLI installed and the chosen provider configured
Last reviewed: 2026-10-24

---

A positional prompt runs one Code turn and exits. This is useful for bounded
investigation or automation that needs a result and an exit status. For several
turns in an ongoing session, use the [session API](/docs/cli/session-api/).
Examples describe CLI **0.9.0**.

## Supply the prompt

For a project containing `src/main.py`:

```bash
kaitoi code --workspace . --mode plan --trust-workspace-read-only \
  --max-credits 100 --json "Explain @src/main.py"
```

The explicit workspace is trusted for reads. The model can still incur usage.
Replace the path with a real project file and choose a credit bound appropriate
to your account and task.

With no positional prompt, piped UTF-8 stdin supplies the prompt. A literal `-`
also selects stdin. When you supply a prompt argument, stdin is left unread.
`--json` requires a nonempty prompt; it does not start a persistent server.
To use a literal prompt beginning with a reserved subcommand word such as
`crew`, `acp`, or `serve`, place `--` before it.

## Read the event stream


A prompt enters one process. JSON event records describe turn start, text
deltas, tools, permissions, and completion or failure. Each EventRecord has a
sequence, timestamp, and kind. An optional final trace object has a different
shape. Consume complete lines and inspect the process exit status.

The figure shows selected JSON record excerpts for turn.started, assistant.text.delta and turn.completed; intervening records are omitted. A line parser routes event kinds to handlers, while the optional type: trace record takes its own branch. Stderr remains outside that parser.

*Figure: A headless process emits one turn's events and then exits.*

Normal EventRecord lines include `sequence`, `timestamp`, and `kind`, plus
event-specific fields. Kinds include `turn.started`, `assistant.text.delta`,
`tool.called`, `permission.requested`, and `turn.completed`, `turn.failed`, or
`turn.cancelled`. A distinct final object can use `{"type":"trace","data":...}`.
Handle that separately instead of requiring every line to have `kind`.

These terminal JSON events are different from served-session SSE. In
particular, `turn.end` belongs to the served session's per-turn boundary, not
the normal terminal CLI event vocabulary.

## Grant only the intended authority

`--auto-approve-edits` permits file writes and edits; it does not grant deletion,
moves, command execution, or transfers. `--auto-approve` enables routine
approval review with hard exclusions, including credit spending and risky
operations. Existing saved command and MCP approvals can apply independently.
Prepare the exact needed approvals rather than treating either flag as a
universal unattended permission grant.

Plan mode and read-only workspace trust do not make a turn free or side-effect
free: model usage, session logs, and previously approved hooks can still occur.
Some requests can be denied or remain unavailable in an unattended context.

## Bound and finish the task

`--max-credits` is checked before the next model step. The current step can
finish beyond the bound. `--max-steps` limits model steps, not the duration or
number of shell commands. See [limits](/docs/cli/prices/) for those boundaries.

The process exits according to the turn result and stops its session background
processes, MCP connections, and language servers. Read the exit status together
with the terminal event and actual outputs. Stopping or failing does not roll
back completed file edits or prove that remote work was canceled.
