---
title: Agent Terminal Protocol
description: ATP is how Termio hosts any agent CLI — one JSON manifest declaring how to launch it, how it reports status, and how to resume its exact conversation. The agent’s own TUI stays in a real terminal.
---

Editor protocols re-render your agent inside an editor pane. ATP takes the
opposite bet: the agent’s own TUI already is the interface, so it stays in a real
terminal, and the protocol standardizes only the thin layer around it —
**launch**, **live status**, and **exact resume**. Every agent Termio ships is
defined by this same manifest; there is no privileged internal API.

## The manifest

One JSON file per agent. This is Termio’s bundled Grok manifest, verbatim:

```json
{
  "id": "grok",
  "order": 95,
  "name": "Grok",
  "wire": "grok",
  "command": "grok",
  "permissionBypassFlag": "--yolo",
  "resume": {
    "create": "--session-id {id}",
    "resume": "--resume {id}",
    "storeRoot": "~/.grok/sessions",
    "storeMatch": "dir:{id}"
  },
  "icon": { "vector": "grok" },
  "install": "https://x.ai/cli",
  "titleStatus": {
    "attention": ["Action Required"]
  },
  "hooks": {
    "type": "json",
    "file": "~/.grok/hooks/termio.json",
    "dialect": "grok",
    "conversation": "sessionId",
    "events": [
      { "on": "SessionStart", "state": "idle" },
      { "on": "UserPromptSubmit", "state": "working" },
      { "on": "PreToolUse", "state": "working" },
      { "on": "PostToolUse", "state": "working" },
      { "on": "Stop", "state": "done" }
    ]
  }
}
```

| Field | Meaning |
| --- | --- |
| `id` | Stable identifier. Sessions persist it, so it never changes once shipped. |
| `name` | Display name in the picker and sidebar. |
| `command` | The CLI to launch, resolved on your login shell’s `PATH`. |
| `permissionBypassFlag` | The vendor’s skip-permissions flag, wired to a one-click toggle. Optional. |
| `icon` | `vector` (a built-in brand mark), `path` (your PNG/SVG file), or `symbol` (an SF Symbol), with an optional `tint`. |
| `install` | The vendor’s install page, offered when the command isn’t found. Optional. |
| `order` | Position in the agent picker. Optional; omitted manifests sort last. |
| `skills` | The agent’s user-level skills directory (`{"dir": "~/.claude/skills"}`), which is where Termio writes its session-control skill. Optional; omit it for an agent with no skills ecosystem. |
| `configHome` | The environment variable the agent documents for moving its config tree, and the path that variable replaces (`{"env": "CLAUDE_CONFIG_DIR", "path": "~/.claude"}`). Set it and Termio installs where the agent actually reads, rather than at the literal default. Optional. |

## Status

A session is always in one of four states — `working`, `attention` (blocked on
you), `done`, or `idle` — shown in the sidebar, the menu bar, and on your
iPhone. The manifest declares how the agent reports them, through three
channels:

| Channel | Role |
| --- | --- |
| `hooks` | The precise channel: Termio installs the agent’s own hook configuration (JSON, TOML, or a plugin, per `dialect`) so the agent itself reports each `on → state` event. When hooks are declared they are the single source of truth. |
| `titleStatus` | Regex rules over the agent’s live terminal title (OSC 0/2) — the in-band signal some agents broadcast. Coexists with hooks and corrects a missed event the instant the title flips. |
| `status` | Screen-classification regexes for agents with no hook system at all. Ignored when hooks are declared. |

`hooks.conversation` names where the agent’s own session id appears in its hook
payloads (a stdin JSON field for shell hooks, an event key path for plugins).
With it, each status report also carries conversation *identity* — so when the
agent rotates to a new conversation mid-session (`/new`, `/clear`), Termio
re-binds the tab to the live one the moment the next event fires.

## Resume

Reopening a session relaunches the agent into the same conversation. Resume is
**exact-or-nothing**: Termio either resumes the precise conversation a tab is
bound to, or launches fresh — it never guesses. Two families, inferred from
which fields are present:

**Pinned id** — the CLI accepts a session id at launch. `create` starts a fresh
conversation under a Termio-minted id and `resume` continues it; `storeRoot` +
`storeMatch` describe the agent’s on-disk session store so Termio can tell the
two apart (creating a duplicate id errors, as does resuming a missing one).

**Discovered id** — the agent mints the id itself. `discover` describes the
mechanism, never an agent: where session records live, how a record is read, and
key paths to the id and working directory. A record is either `jsonl` (the first
line of a log that is itself the transcript) or `json` (a standalone metadata
file). Termio recovers the id, binds it to the tab, and resumes exactly from
then on.

```json
"resume": {
  "resume": "resume {id}",
  "discover": {
    "root": "~/.codex/sessions",
    "format": "jsonl",
    "id": "payload.id",
    "cwd": "payload.cwd"
  }
}
```

## Add your own agent

Drop a manifest at `~/.termio/config/agents/<id>.json` and restart Termio — it
appears in the picker alongside the built-ins, with the same status dots and
resume behavior. A minimal manifest is just an id, a name, and a command; add
status and resume as the CLI supports them.

<Callout type="tip">
  The [Custom agents](/docs/custom-agents) guide walks through this end to end —
  icons, screen-scrape status rules, and overriding a built-in.
</Callout>
