# Agent output format

Agent mode (`--agent`, or `--format agent`) is a compact, stable text contract
for coding agents. It exists alongside JSON, not instead of it: agent mode is
for short action acknowledgements that a human can scan and an agent can
pattern-match without a JSON parser, while `--json` remains the complete
machine-readable API. See [output modes](../guide/concepts.md#output-modes) for
the high-level picture and the [CLI reference](cli.md) for per-command flags.

This page documents the agent format as a contract. Tooling may rely on the
guarantees described here.

## Selecting the mode

```sh
kata list --agent                 # agent text
kata list --json                  # full JSON envelope
kata list --format agent          # equivalent to --agent
kata list --format human          # default terminal output
```

- `--json` and `--agent` are aliases for `--format json` and `--format agent`.
- Passing several output-mode flags that resolve to the **same** mode is valid
  (`--format agent --agent`). Flags that resolve to **different** modes are a
  usage error before the command runs.
- `--quiet` is orthogonal: a suppression flag, not a mode. In agent mode it may
  suppress the `OK ...` success header where a command already has quiet
  behavior, but it never suppresses an `ERR ...` line.
- `--format` is reserved globally for output mode. `kata import` selects its
  source format with `--source-format <kata|beads>`.

## Stability contract

Agent output is plain UTF-8 with no ANSI styling. Its shape is stable:

- In non-quiet agent mode the first token is always `OK` for success or `ERR`
  for failure. Success goes to stdout; failure goes to stderr with a nonzero
  exit code.
- The second token is the command or record kind: `create`, `comment`, `list`,
  `show`, `event`, and so on. The literal `kata` appears only on a top-level
  parse error before a subcommand runs.
- Remaining first-line fields are stable positional fields where this document
  specifies them, and `key=value` otherwise. **Field order is part of the
  contract.**
- New fields may be appended to a line or block. Existing field names, first
  tokens, and their meanings do not change without an agent-format version bump.
- List-valued fields are comma-separated with no spaces (`labels=bug,safari`).
- Nullable free-form fields are **omitted when absent**. Agent output never
  emits sentinel values such as `owner=null` or `owner=unowned`, because a user
  could legitimately choose those exact strings as an owner name.
- Values containing whitespace, quotes, or control characters are double-quoted
  with Go/JSON-style escaping; simple tokens stay unquoted.

### Version

The current agent format version is `1`. A breaking change bumps this integer.
Discover it without parsing JSON:

```text
$ kata version --agent
OK version version=0.0.0 agent_format=1
```

The same value appears as `agent_format` in `kata version --json`; the complete
JSON schema is documented in the [CLI reference](cli.md#daemon-and-diagnostics).
Ordinary command output does not repeat the version, to keep transcripts small.

## Errors

```text
ERR comment validation: comment body is required
Hint: pass --body, --body-file, or --body-stdin
```

- The first line is `ERR <command> <kind>: <message>`.
- `<kind>` reuses the CLI error taxonomy: `usage`, `validation`, `not_found`,
  `conflict`, `confirm`, `daemon_unavailable`, `internal`.
- Optional follow-up lines use fixed field names such as `Hint:`, `Code:`, and
  `Exit-Code:`.
- A top-level parse error, before any subcommand runs, uses `kata` as the
  command token: `ERR kata usage: unknown command "cretae"`.
- In `--format json` errors remain the JSON error envelope; in `--format human`
  they remain the existing human text.

## Success shapes

### Mutations

A mutation line names what happened and which issue changed, with `changed=` and
`reused=` flags where they apply, followed by block lines:

```text
OK create abc4
Issue: abc4 "Fix login race"
Status: open
```

```text
OK close abc4
Issue: abc4 "Fix login race"
Status: closed
Reason: done
Evidence: commit:3a401a8
```

```text
OK delete abc4
Issue: abc4 "Fix login race"
Status: deleted
Undo: kata restore abc4 --agent
```

An `Undo:` line is emitted only when the follow-up is factual and mechanically
correct. Generic `Next:` guidance is intentionally not part of the format.

### Reads

A read emits an `OK <command> count=<n>` header followed by one row per record:

```text
OK list count=3
- issue=abc4 status=open priority=2 owner=agent-a labels=bug,safari title="Fix login race"
- issue=def7 status=open labels=architecture title="Control channel"
- issue=j9k2 status=closed title="Old task"
```

Empty reads emit the header with `count=0` and no rows:

```text
OK search count=0 query="login race"
```

`kata update --check --agent` appends package ownership fields after the
existing result fields, in this order:

```text
OK update update_available=true current=v0.14.1 latest=v0.14.2 distribution=homebrew upgrade_hint="brew upgrade kata" package_release_may_lag=true
```

The field order is `update_available`, `current`, `latest`, `distribution`,
optional `upgrade_hint`, then `package_release_may_lag`. Ordinary archives emit
`distribution="" package_release_may_lag=false` and omit `upgrade_hint`.
Install-capable forms on package-managed builds use the existing usage error
shape and exit code 2:

```text
ERR update usage: Homebrew manages this installation; run 'brew upgrade kata' instead
```

The check reads Kata's GitHub release feed, so a reported release can appear
before the owning package manager publishes it.

#### Federation quarantine discovery

`kata federation quarantine list --agent` emits one row per active quarantine:

```text
OK federation-quarantine-list count=1
- project=spoke-project project_id=7 quarantine_id=3 direction=push first_event=41 last_event=44 event_count=4 created_at=2026-07-10T12:00:00Z error="hub rejected batch"
```

The row field order is `project`, `project_id`, `quarantine_id`, `direction`,
`first_event`, `last_event`, `event_count`, `created_at`, and `error`. An empty
list emits only `OK federation-quarantine-list count=0`.

`kata federation quarantine show <id> --agent` emits the owning project and
batch summary followed by one row per quarantined event UID:

```text
OK federation-quarantine-show quarantine_id=3
- project=spoke-project project_id=7 direction=push first_event=41 last_event=44 event_count=4 created_at=2026-07-10T12:00:00Z error="hub rejected batch"
- event_uid=01HZNQ7VFPK1XGD8R5MABCD4EA
- event_uid=01HZNQ7VFPK1XGD8R5MABCD4EB
```

The event UID rows preserve the order recorded for the quarantined batch.
Operators can inspect these read-only commands before invoking the separately
confirmed `quarantine retry` or `quarantine skip` mutation.

`kata search` appends a `mode=` field to its header (`lexical`, `hybrid`, or
`semantic`) reporting the strategy actually run. When the embedding endpoint is
configured but could not serve a query, the search falls back to lexical and a
`degraded=<reason>` field follows `mode=`; it is omitted when the query was not
degraded. Result rows carry `matched=` listing the contributing sources (FTS
column names), and a row gains `semantic` in that list when the vector leg
matched it:

```text
OK search count=1 query="auth redirect duplicates" mode=hybrid
- issue=abc4 score=0.0312 status=open matched=title,semantic title="Login callback double-submits on Safari"
```

`count`, `query`, and the existing row fields keep their names, positions, and
meanings, so these additions are purely additive and `agent_format` stays `1`.
A daemon without `[search.embeddings]` always reports `mode=lexical` and never
sets `degraded=`, so its output is unchanged apart from the appended `mode=`.

#### Single-result `next`

`kata next --agent` is an intentional exception to the usual read shape of a
`count=<n>` header followed by *n* rows. It emits exactly one compact record
when a ready issue is selected:

```text
OK next issue=abc4 priority=1 owner=agent-a title="Fix callback race"
```

The field order is `issue`, optional `priority`, optional `owner`, then
`title`. Absent priority and owner values are omitted rather than rendered as
sentinels. A cross-project selection uses a qualified value such as
`issue=example-project#abc4`.

An empty ready set is also a successful single record:

```text
OK next found=false
```

With `--full`, the success header identifies the selected ref and is followed
by the same detail fields and sections as `kata show --agent`:

```text
OK next abc4
Issue: abc4 "Fix callback race"
Status: open
Owner: agent-a
Labels: bug
Priority: 1
```

The show sections for labels, body, metadata, links, comments, and leases are
included when present. Their existing ordering and omission rules apply.

### Events

Non-tail reads use a header plus rows; tail mode is stream-safe and emits exactly
one line per event:

```text
OK events count=2 next_after_id=44
- id=42 type=issue.created issue=abc4 actor=agent-a
- id=43 type=issue.commented issue=abc4 actor=codex
```

```text
OK event id=42 type=issue.created issue=abc4 actor=agent-a
```

`kata events --tail --json` is newline-delimited JSON; `kata events --tail
--agent` is one agent line per event and never emits multi-line blocks.

## Multiline text

Bodies and comments are preserved in fenced `text` blocks:

````text
OK show abc4
Issue: abc4 "Fix login race"
Status: open
Body:
```text
Safari can double-submit the callback.
```
````

- No silent truncation is allowed. If truncation is ever introduced, the
  metadata fields immediately precede the field they describe and are emitted as
  a pair, `<Field>-Truncated: true` and `<Field>-Bytes: <full-byte-count>`,
  never one without the other.
- A fenced block uses at least three backticks and the `text` info string. If
  the content itself contains a triple-backtick sequence, a longer fence is
  chosen so the block stays valid Markdown.

## Guarantees

- `--json` is never weakened or removed; it stays the complete structured API.
- Agent mode does not change daemon API responses; it is purely a CLI rendering
  of the same data.
- Agent mode is not the default.
- Interactive commands (`kata tui`) and daemon startup (`kata daemon start`)
  reject agent mode with a usage error rather than launching.
