Skip to main content

Protocol Reference

Ante uses a typed message-passing protocol between the client and daemon. Messages are exchanged over bounded async channels (in-process) or as JSON Lines over stdin/stdout (external clients).

Wire format

External clients communicate with the daemon using JSON Lines (JSONL) — one JSON object per line over stdin/stdout.

  • Client → Daemon: Send OpMsg objects as JSON lines to stdin
  • Daemon → Client: Receive EventMsg objects as JSON lines from stdout

OpMsg envelope

Every operation is wrapped in an OpMsg:

{
"op": { "StartSession": { "model": "claude-sonnet-4-6", "provider": "anthropic", "streaming": true } },
"id": "op_01ARZ3NDEKTSV4RRFFQ69G5FAV"
}

EventMsg envelope

Every event is wrapped in an EventMsg:

{
"timestamp": "2025-06-01T12:00:00Z",
"id": "evt_01ARZ3NDEKTSV4RRFFQ69G5FAV",
"event": { "AgentMessage": "Here is the result..." },
"parent": "op_01ARZ3NDEKTSV4RRFFQ69G5FAV"
}

The parent field links an event back to the operation that triggered it. It is null when not applicable.

Message IDs

Every message has a typed Id consisting of a prefix (up to 4 bytes) and a ULID. The string format is {prefix}_{ulid}.

PrefixUsage
op_Operations (client → daemon)
evt_Events (daemon → client)
ses_Session identifiers
step_Step identifiers

Example: op_01J5A3B7C9D0E1F2G3H4J5K6M7

Operations (Client → Daemon)

StartSession

Initialize a new session with model, provider, and configuration.

{
"op": {
"StartSession": {
"model": "claude-sonnet-4-6",
"provider": "anthropic",
"policy": null,
"streaming": true,
"system_prompt": null,
"append_system_prompt": null,
"allowed_tools": null,
"disallowed_tools": null,
"cwd": null,
"thinking": null
}
},
"id": "op_..."
}

SessionConfig fields:

FieldTypeDescription
modelstringModel name (e.g. "claude-sonnet-4-6")
providerstringProvider name (e.g. "anthropic", "openai", "gemini", "xai", "openrouter", "local")
policyPermissionMode?Tool approval policy. null uses default
streamingboolEnable streaming deltas (MessageDelta, ThinkingDelta)
system_promptstring?Override the default system prompt entirely
append_system_promptstring?Append content to the default system prompt
allowed_toolsstring[]?Whitelist — only these tools are available
disallowed_toolsstring[]?Blacklist — these tools are removed
cwdstring?Working directory. Defaults to daemon's process directory
thinkingThinking?Thinking level override: "Disabled", "Enabled", "Deep", or "Max"

UpdateSession

Update the active session without restarting it (e.g. switch models mid-session). Today SessionUpdate carries only a new ModelSpec; other session options must be set at StartSession time.

{
"op": {
"UpdateSession": {
"model": { "name": "gpt-5.4", "temperature": 0.2 }
}
},
"id": "op_..."
}

SessionUpdate fields:

FieldTypeDescription
modelModelSpecNew model specification to use

ResumeSession

Resume a previously persisted session by its ID. The daemon restores the conversation history, re-discovers extensions, and replays recent events so the client can rebuild its view.

{ "op": { "ResumeSession": { "session_id": "ses_01ARZ..." } }, "id": "op_..." }
FieldTypeDescription
session_idIdThe session identifier to resume

On success the daemon emits SessionEnd for the current session (if any), then SessionStart and ExtensionRefreshed for the resumed session, followed by up to 200 replayed historical events. On failure it emits an Error event.

Steer

Provide additional guidance to the agent during an active turn without starting a new one.

{ "op": { "Steer": "focus on the auth module first" }, "id": "op_..." }

UserInput

Submit user text input to the agent.

{ "op": { "UserInput": "explain what this project does" }, "id": "op_..." }

ApprovalResponse

Respond to a tool approval request (sent after receiving a TurnPause event with Approval reason).

{
"op": {
"ApprovalResponse": {
"turn_id": "step_01ARZ...",
"responses": [
["tool_use_abc123", "Accept"],
["tool_use_def456", "Skip"]
]
}
},
"id": "op_..."
}

ReviewDecision values:

DecisionDescription
AcceptAllow this tool call
SkipSkip this tool call
AcceptForSessionAllow this tool for the rest of the session
AbortAbort the current task

SlashCommand

Invoke a skill by name.

{ "op": { "SlashCommand": { "name": "commit", "args": "-m 'fix bug'" } }, "id": "op_..." }

Interrupt

Abort whatever is currently running.

{ "op": "Interrupt", "id": "op_..." }

Shutdown

Request a graceful shutdown.

{ "op": "Shutdown", "id": "op_..." }

RegisterLocalProvider

Register a running local inference server (e.g. an offline llama-server started by the client) as the local provider for this daemon. The model is optional — if provided, it pins the local provider to a specific ModelSpec; otherwise the local provider serves whatever model the server is hosting.

{
"op": {
"RegisterLocalProvider": {
"port": 8080,
"model": null
}
},
"id": "op_..."
}
FieldTypeDescription
portu16Port the local llama-server is listening on
modelModelSpec?Optional model spec to pin the provider to

RestoreLocalProvider

Restore the previously registered local provider configuration after a session-level provider switch. Useful when the daemon needs to revert to a previously registered local server without re-supplying its port and model.

{ "op": "RestoreLocalProvider", "id": "op_..." }

Events (Daemon → Client)

Session events

SessionStart

Emitted when a session is initialized. Contains metadata about the active model, provider, session ID, and working directory. Skills and subagents are delivered separately via ExtensionRefreshed.

{
"event": {
"SessionStart": {
"model": { "name": "claude-sonnet-4-6", "max_tokens": 8192 },
"provider": "anthropic",
"session_id": "ses_01ARZ...",
"cwd": "/home/user/project"
}
}
}

SessionInitialized fields:

FieldTypeDescription
modelModelSpecActive model specification
providerstringActive provider name (e.g. "anthropic")
session_idIdUnique session identifier
cwdstringWorking directory path

SessionUpdated

Emitted when the active session is updated in place (e.g. model changed via UpdateSession).

{
"event": {
"SessionUpdated": {
"model": { "name": "gpt-5.4" },
"provider": "openai",
"session_id": "ses_01ARZ...",
"cwd": "/home/user/project"
}
}
}

SessionEnd

Emitted when the session terminates.

{ "event": "SessionEnd" }

Turn lifecycle events

TurnStart

Emitted when a new turn begins processing.

{ "event": { "TurnStart": { "turn_id": "step_01ARZ..." } } }

TurnPause

Emitted when a turn is paused waiting for user input (e.g. tool approval).

{
"event": {
"TurnPause": {
"turn_id": "step_01ARZ...",
"reason": {
"Approval": {
"tools": [
{ "id": "tool_use_abc123", "name": "Bash", "input": { "command": "ls -la" } }
],
"message": "Allow running shell command?"
}
}
}
}
}

TurnPauseReason variants:

VariantFieldsDescription
Approvaltools: ToolUse[], message: stringWaiting for tool approval

TurnEnd

Emitted when a turn completes.

{ "event": { "TurnEnd": { "turn_id": "step_01ARZ...", "status": "Completed" } } }

TurnEndStatus variants:

VariantFieldsDescription
CompletedTurn finished successfully
Interruptedreason?: stringTurn was interrupted
Errormessage: stringTurn ended with an error

Message streaming events

AgentMessage

Complete agent text response (non-streaming).

{ "event": { "AgentMessage": "The project is a web server that..." } }

Thinking

Complete chain-of-thought block (non-streaming).

{ "event": { "Thinking": "Let me analyze the codebase structure..." } }

MessageDelta

Streaming chunk of the agent's message. Concatenate all deltas to build the full message.

{ "event": { "MessageDelta": "The project" } }

ThinkingDelta

Streaming chunk of the agent's thinking. Concatenate all deltas to build the full thinking block.

{ "event": { "ThinkingDelta": "Let me" } }

Tool events

ToolStart

Emitted when a tool invocation begins.

{
"event": {
"ToolStart": {
"id": "tool_use_abc123",
"name": "Read",
"input": { "file_path": "/src/main.rs" }
}
}
}

ToolUpdate

Progress update during tool execution.

{
"event": {
"ToolUpdate": {
"tool_use_id": "tool_use_abc123",
"seq": 0,
"message": "Reading file..."
}
}
}
FieldTypeDescription
tool_use_idstringTool call identifier (matches ToolStart.id)
sequ64Monotonically increasing sequence number
messagestringProgress message

ToolEnd

Emitted when a tool execution completes.

{
"event": {
"ToolEnd": {
"tool_use_id": "tool_use_abc123",
"status": "Completed",
"result_json": { "content": "fn main() { ... }" },
"is_error": false
}
}
}

ToolEndStatus variants:

VariantDescription
CompletedTool ran successfully
CancelledTool execution was cancelled
DeniedTool was denied by the user
FailedTool execution failed

Compaction events

CompactStart

Emitted when dialog compaction begins.

{ "event": "CompactStart" }

CompactEnd

Emitted when dialog compaction completes.

{ "event": "CompactEnd" }

Extension events

ExtensionRefreshed

Emitted when skills, subagents, or MCP servers are refreshed. Sent at least twice during a normal session: once right after SessionStart with the initial skills/subagents (and mcp_servers: []), and again after MCP warm-up completes in the background with the discovered server and tool list.

{
"event": {
"ExtensionRefreshed": {
"session_id": "ses_01ARZ...",
"skills": [
{ "name": "commit", "description": "Create a git commit", "scope": "user", "argument_hint": "-m 'message'" }
],
"subagents": [
{ "name": "explore", "description": "Explore the codebase", "scope": "project" }
],
"mcp_servers": [
{
"name": "filesystem",
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-filesystem", "/tmp"],
"tools": [
{
"name": "read_text_file",
"qualified_name": "mcp__filesystem__read_text_file",
"description": "Read the contents of a text file",
"parameters": [
{ "name": "path", "param_type": "string", "required": true, "description": "Path to the file" }
]
}
]
}
]
}
}
}

Each entry in mcp_servers reflects what was actually launched and discovered: the tools list is empty for servers that failed to connect, and qualified_name is the mcp__<server>__<tool> form the agent uses to call the tool.

Informational events

UsageUpdate

Token usage statistics for the session.

{ "event": { "UsageUpdate": { "usage": { "input_tokens": 1500, "output_tokens": 300 } } } }

Info

General informational message.

{ "event": { "Info": "Compacting conversation history..." } }

InfoBlockStart

Opens a grouped Info entry with a header. Subsequent InfoBlockAppend events sharing the same id render as tree-indented child lines under it — used for multi-step background notifications (e.g. MCP warm-up) that should visually cluster.

{
"event": {
"InfoBlockStart": {
"id": "mcp-warmup-ses_01ARZ...",
"header": "Warming up 4 MCP servers in the background — tools will become available as they connect."
}
}
}

InfoBlockAppend

Appends a child detail line to the InfoBlockStart with the same id. Drops silently if the matching block isn't present.

{
"event": {
"InfoBlockAppend": {
"id": "mcp-warmup-ses_01ARZ...",
"detail": "MCP ready: 4/4 servers connected, 60 tools registered."
}
}
}

Error

Error message.

{ "event": { "Error": "Authentication failed: invalid API key" } }

Goodbye

Final message before the daemon disconnects. After receiving this, no more events will be sent.

{ "event": "Goodbye" }

UserInput

User input recorded for session replay. This variant is written to the persisted event log but is not emitted during live sessions — you will only see it in events replayed by ResumeSession.

{ "event": { "UserInput": "explain what this project does" } }

Offline mode and the protocol

Local-model management (engine installation, model discovery, llama-server lifecycle) is client-side. It is driven through the in-process OfflineSubsystem API, not through the daemon protocol. The only daemon-facing surface for offline mode is RegisterLocalProvider / RestoreLocalProvider — the client starts a llama-server itself, then registers its port (and an optional ModelSpec) with the daemon.

See the Offline Mode page for the user-facing flows and the --offline-model CLI flag.

Transport

In-process channels

When the client and daemon run in the same process (TUI or headless mode), they communicate via bounded Tokio mpsc channels:

ChannelDirectionBuffer size
Op channelClient → Daemon256 messages
Event channelDaemon → Client4096 messages

Stdio transport (JSONL)

For external clients using ante serve (or ante serve --stdio), StdioTransport bridges JSON Lines over stdin/stdout to the internal channel pair:

  • stdin → Parse each line as OpMsg → forward to daemon
  • daemon events → Serialize as JSON → write to stdout (one line per event)
  • EOF on stdin → Automatically sends Op::Shutdown
  • Evt::Goodbye received → Transport exits
  • JSON parse errors → An Evt::Error is sent back on stdout

WebSocket transport

For networked or browser-based clients using ante serve --ws <ADDR>, WsTransport exchanges the same JSONL protocol over WebSocket frames:

  • Each WebSocket connection gets its own daemon instance
  • Messages are the same OpMsg and EventMsg JSON objects, sent as text frames
  • Client disconnect → Daemon instance shuts down, server accepts next connection
  • Op::Shutdown received → Connection and server both exit
  • The server loops accepting new connections until a Shutdown is received

Complete flow example

A full session lifecycle from start to shutdown:

Client                                  Daemon
│ │
│─── OpMsg { StartSession(...) } ──────▶│
│◀── EventMsg { SessionStart(...) } ────│
│ │
│─── OpMsg { UserInput("fix bug") } ───▶│
│◀── EventMsg { TurnStart { turn_id } } │
│◀── EventMsg { ThinkingDelta("...") } │
│◀── EventMsg { ThinkingDelta("...") } │
│◀── EventMsg { Thinking("...") } │
│◀── EventMsg { MessageDelta("...") } │
│◀── EventMsg { ToolStart(ToolUse) } │
│◀── EventMsg { TurnPause(Approval) } │
│ │
│─── OpMsg { ApprovalResponse(...) } ──▶│
│◀── EventMsg { ToolUpdate(...) } │
│◀── EventMsg { ToolEnd(...) } │
│◀── EventMsg { MessageDelta("...") } │
│◀── EventMsg { AgentMessage("...") } │
│◀── EventMsg { UsageUpdate(...) } │
│◀── EventMsg { TurnEnd(Completed) } │
│ │
│─── OpMsg { Shutdown } ───────────────▶│
│◀── EventMsg { SessionEnd } │
│◀── EventMsg { Goodbye } │
│ │