Skip to main content

Protocol Reference

Ante uses a typed message-passing protocol between the client and daemon. In-process clients use a bounded operation channel and an unbounded event channel; external clients exchange the same messages over JSON Lines or WebSocket frames.

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-5-5", "provider": "anthropic" } },
"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, replacing any session already running. A field supplied by the client is pinned; an omitted or null field resolves from the host's settings at this session boundary, or from Ante's built-in default when the setting is absent.

{
"op": {
"StartSession": {
"model": "claude-sonnet-5-5",
"provider": "anthropic",
"credential": null,
"permission_mode": null,
"system_prompt": null,
"append_system_prompt": null,
"tools": null,
"include_tools": null,
"exclude_tools": null,
"cwd": null,
"effort": null,
"enable_auto_memory": null,
"short_prompt": null,
"no_skills": null,
"include_skills": null,
"exclude_skills": null,
"save_session": null,
"unattended": null,
"title": null
}
},
"id": "op_..."
}

SessionRequest fields:

FieldTypeDescription
modelstring?Model name (e.g. "claude-sonnet-5-5"); null uses the host default
providerstring?Provider name (e.g. "anthropic", "openai", "gemini", "xai", "openrouter", "local"); null uses the host default
credentialstring?Pin a credential reference, such as "oauth:anthropic/work" on anthropic-subscription, from the first request. The start is refused if the reference is invalid, unavailable, or not accepted by the provider. Omitted or null uses configured credential precedence. Available since v0.3.1
permission_modePermissionMode?Tool approval mode: "strict", "auto", or "yolo"; null uses the host default
system_promptstring?Override the default system prompt entirely
append_system_promptstring?Append content to the default system prompt
toolsstring[]?Exactly these tools, replacing the default tool set as the base. Also gates dynamically registered (MCP) tools
include_toolsstring[]?Tools added on top of the base set (tools, or the default set)
exclude_toolsstring[]?Tools removed from the session; wins over tools and include_tools
cwdstring?Working directory. Defaults to daemon's process directory
effortEffort?Effort override: "min", "low", "medium", "high", "xhigh", or "max". null resolves from the catalog
enable_auto_memorybool?Whether the agent records and recalls auto-memory. null leaves the daemon default (on for interactive sessions)
short_promptbool?Use shorter built-in tool descriptions; the system instructions stay the same. null leaves the settings default
no_skillsbool?When true, skip all skill discovery so no skills are advertised or invocable. null leaves the session default
include_skillsstring[]?Exact skill names added to the session's base set. A supplied list replaces the host's include_skills setting and does not enable discovery when no_skills is true
exclude_skillsstring[]?Exact skill names removed from the session. A supplied list replaces the host's exclude_skills setting and wins over include_skills
save_sessionbool?Whether to write a transcript and resumable snapshot. null leaves the host's session-saving default
unattendedbool?Declare that no client can answer approval prompts. When true, a call that would require approval is denied instead of pausing. Omitted or null means false
titlestring?A display name for the session, shown in place of the first message in resume pickers. Trimmed; empty after trimming means no title. null starts the session untitled

SessionRequest replaced the former SessionOverrides name in v0.preview.92. The JSON field names stayed the same, but absence now has one explicit meaning for StartSession: resolve the host's default now, never “leave the previous session unchanged.” A client implementing /clear should retain and resend its own request, applying any model or permission-mode changes it made; there is no separate restart operation.

Use ante auth list --json to discover stored credential references and the providers that accept them. Credential pins apply to the current session only and are not saved with the conversation; send the reference again when resuming. A rejected pin does not fall back to another credential.

UpdateSession​

Update the active session without restarting it (e.g. switch providers, models, or permission mode mid-session). Each field is optional; an absent field is left unchanged. A permission-mode change — what Shift+Tab cycling sends from the TUI — takes effect immediately, applying to the turn already in flight as well as later ones; a provider or model change applies on the next turn. A title change applies at once and is echoed back on the SessionUpdated event. Other session options must be set at StartSession time.

A provider change continues the conversation on the named catalog provider, keeping the session ID, messages, title, and permission state. model then names a model on that provider; when it is absent, the provider's default model is used. Naming the current provider is not a switch. The whole update is refused — answered with an Error instead of SessionUpdated — if the provider ID is unknown, or if a turn is in flight when a different provider is requested; the session is never re-routed to another provider. Interrupt the running turn first to switch away from a failing or rate-limited provider. Available since v0.2.5.

{
"op": {
"UpdateSession": {
"provider": "openai",
"model": { "id": "gpt-6.1-sol", "temperature": 0.2, "effort": "high" },
"permission_mode": "auto",
"title": "auth refactor"
}
},
"id": "op_..."
}

SessionUpdate fields:

FieldTypeDescription
providerstring?Catalog provider ID to switch to (e.g. "openai"), keeping the conversation. Refused with Error for an unknown ID or while a turn is in flight
modelModelSpec?New model specification to use. Carries the whole request, effort included: a set effort overrides the catalog default; unset fields resolve from the catalog. Resolved on the session's provider — the one provider names when both are set
permission_modePermissionMode?New permission mode ("strict", "auto", or "yolo")
titlestring?Rename the session. The text is trimmed; an empty or whitespace-only string clears the title
credentialstring?Pin a credential reference such as "oauth:openai/work" on the resulting provider for this session. Refused if the provider does not accept it, it has no value, or a turn is running. Absent keeps the current provider's choice; a switch to another provider starts from its configured order. Available since v0.3.0

ResumeSession​

Resume a previously persisted session by its ID. The daemon restores the saved dialog and pinned model, provider, and effort, resolves anything the snapshot did not pin from the host's current defaults, re-discovers extensions, and replays the complete persisted conversation so the client can rebuild its view.

Saved system prompts and prompt mode are restored as sent, including replacements, appended instructions, and short_prompt. Current launch prompt flags apply only to fresh sessions. A change in local date, OS, or shell is reported to the model once on its next turn; an unchanged environment adds no notice.

Permission mode always resolves from the host's current settings. Older snapshots still load, but their saved permission mode is ignored and omitted on the next save.

{ "op": { "ResumeSession": { "session_id": "ses_01ARZ...", "unattended": false } }, "id": "op_..." }
FieldTypeDescription
session_idIdThe session identifier to resume
unattendedboolWhether no client can answer approval prompts for this resumed incarnation. Defaults to false when absent; when true, would-be prompts are denied at admission
toolsstring[]?Base tool set for this resume, replacing the default set and also gating MCP tools. Omitted or null uses current settings. Available since v0.3.1
include_toolsstring[]?Tools added to the base set for this resume. Omitted or null adds none. Available since v0.3.1
exclude_toolsstring[]?Tools removed for this resume; wins over tools and include_tools. Omitted or null excludes none. Available since v0.3.1
credentialstring?Credential reference to pin before the first resumed request, under the same validation rules as StartSession.credential. Omitted or null uses configured precedence. Available since v0.3.1

For example, resume a conversation saved on anthropic-subscription with the work account and a restricted tool set:

{
"op": {
"ResumeSession": {
"session_id": "ses_01ARZ3NDEKTSV4RRFFQ69G5FAV",
"credential": "oauth:anthropic/work",
"tools": ["Read", "Glob", "Grep"]
}
},
"id": "op_01ARZ3NDEKTSV4RRFFQ69G5FAV"
}

The base tool set resolves from the current tools setting unless this request supplies tools; the saved conversation does not restore an old tool set. Additions and exclusions apply to this resume only.

On success the daemon emits SessionEnd for the current session (if any), then SessionStart and ExtensionRefreshed for the resumed session, followed by the complete persisted event log in order, excluding session lifecycle events and malformed lines. There is no 200-event cap. On failure it emits an Error event.

RewindSession​

Restore the active session's conversation and visible history to just before input to. The ID is the TurnStart.turn_id for that input (for user input, the submitting operation's ID). The chosen input and everything after it are removed; no new turn starts and no file edits or other external effects are undone.

{ "op": { "RewindSession": { "to": "op_input_1" } }, "id": "op_rewind_1" }

The session must be saved, with no running or queued turn, active goal, or running background job. The input must still be reachable: saved input boundaries survive resume and are copied into forks, but a successful history-replacing compaction or provider switch invalidates earlier boundaries. Failed compaction leaves them intact. A refused request emits Error without changing the conversation. Success emits SessionRewound.

ForkSession​

Save a new session from the active conversation. Set at to a reachable input ID to copy the conversation just before it, using the same cut as RewindSession; omit at (or pass null) to copy the whole conversation.

{ "op": { "ForkSession": {} }, "id": "op_fork_1" }

The source must be saved with no running or queued turn and no active goal. It stays unchanged. The new session is saved but not started: SessionForked returns its ID, and the client sends ResumeSession to switch to it. A refused request emits Error and creates no session. Forking copies conversation state, not files, a Git worktree, or running processes.

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_..." }

ShellInput​

Run a shell command on the daemon side without starting a turn — the !command path in the TUI. The daemon answers with a ShellOutput event and queues the output into the agent's context for the next turn.

{ "op": { "ShellInput": "git status" }, "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_id": "tool_use_abc123", "decision": "Accept" },
{
"tool_use_id": "tool_use_def456",
"decision": "Deny",
"message": "use the read-only endpoint"
}
]
}
},
"id": "op_..."
}

ToolDecision fields:

FieldTypeDescription
tool_use_idstringTool call being decided (matches ToolStart.id)
decisionReviewDecisionOne of the values below
messagestring?Feedback returned with a denial. Omit for every other decision

ReviewDecision values:

DecisionDescription
AcceptAllow this tool call
DenyDeny this tool call. The agent receives a failed tool result — Tool call denied by user: <message> when a message is present, otherwise Tool call denied by user and was not executed.
AcceptForSessionGrant this call for the rest of the session (scoped — see Session grants)
AcceptAlwaysGrant for the session and persist an allow rule to settings.json ("always allow")

QuestionResponse​

Resolve a pending question pause (TurnPauseReason::Question). Sent after the user answers, skips, or requests a discussion on structured questions asked by AskUser.

{
"op": {
"QuestionResponse": {
"turn_id": "step_01ARZ...",
"tool_use_id": "toolu_1",
"reply": {
"Answered": [
{ "selected": ["JWT (Recommended)"], "note": null }
]
}
}
},
"id": "op_..."
}
FieldTypeDescription
turn_idIdThe paused turn's identifier
tool_use_idstringTool call identifier matching the pause
replyQuestionReplyAnswered(QuestionAnswer[]), Dismissed, or Discuss { message? }

The Answered array corresponds to TurnPause.reason.Question.questions in question order. Each QuestionAnswer contains:

FieldTypeDescription
selectedstring[]Exact labels of selected options, not indices; empty for a free-text-only answer
notestring?Additional text, or the answer itself when selected is empty; omit when no text was entered

An empty selection with no note leaves that question unanswered. To skip the whole prompt, send "reply": "Dismissed". To discuss it, send "reply": { "Discuss": { "message": "Explain the options first" } }; Discuss.message is optional.

SlashCommand​

Invoke a skill by name.

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

Compact​

Manually trigger conversation compaction on the active session (what /compact sends). Optional instructions steer what the replacement summary emphasizes or preserves; they are ignored when the reducer can reclaim enough context without writing a summary. The daemon emits CompactStart and CompactEnd around the operation.

{ "op": { "Compact": {} }, "id": "op_..." }
{ "op": { "Compact": { "instructions": "focus on the API changes" } }, "id": "op_..." }

Compact is a struct variant in v0.preview.90; clients must send the empty object form when they have no instructions rather than the older bare string form.

ContextReport​

Request a per-category breakdown of the active session's context-window occupancy (what /context sends). Answered with a ContextReport event.

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

Goal​

Set, clear, or report a goal-driven execution loop on the active session. A set goal keeps the session working — re-running turns and judging the condition after each one — until it is met, judged unreachable, or cleared.

{ "op": { "Goal": { "Set": "all tests pass" } }, "id": "op_..." }
{ "op": { "Goal": "Clear" }, "id": "op_..." }
{ "op": { "Goal": "Status" }, "id": "op_..." }

AmbientPhrase / AmbientSuggestion​

Request an ad-hoc ambient prediction off the conversation's critical path on a cheap model: AmbientPhrase predicts a spinner "thinking phrase" for the in-progress draft, and AmbientSuggestion predicts a next-prompt suggestion from the last exchange. Both answer with an Ambient event; the monotonic req_id lets the client discard stale results.

{ "op": { "AmbientPhrase": { "draft": "refactor the parser to...", "req_id": 3 } }, "id": "op_..." }
{ "op": { "AmbientSuggestion": { "recent_user": "fix the bug", "recent_agent": "Fixed — the cause was...", "req_id": 7 } }, "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 endpoint (for example, Ante's managed llama serve router or an externally run llama-server) 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 endpoint is hosting.

{
"op": {
"RegisterLocalProvider": {
"port": 8080,
"model": null
}
},
"id": "op_..."
}
FieldTypeDescription
portu16Port the local inference server or router 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. It announces the session identity, mutable settings, and the skills and subagents equipped for the session. ExtensionRefreshed immediately repeats those capability lists alongside MCP state for clients that replace all extension state from one event.

{
"event": {
"SessionStart": {
"model": { "id": "claude-sonnet-5-5", "max_tokens": 8192 },
"provider": {
"id": "anthropic",
"display_name": "Anthropic",
"base_url": "https://api.anthropic.com/v1"
},
"session_id": "ses_01ARZ...",
"cwd": "/home/user/project",
"permission_mode": "strict",
"skills": [],
"subagents": []
}
}
}

SessionInfo fields:

FieldTypeDescription
modelModelSpecActive model specification
providerProviderSpecActive provider (see below)
session_idIdUnique session identifier
cwdstringWorking directory path
permission_modePermissionModeActive permission mode ("strict", "auto", or "yolo")
skillsSkillMetadata[]Skills the user can invoke in this session. Defaults to an empty list when absent for backward compatibility
subagentsSubagentMetadata[]Subagents this session can delegate to. Defaults to an empty list when absent for backward compatibility
titlestring?The session's title when one is set — chosen by the user or the client, never derived from the conversation. Omitted from the payload entirely when unset
forked_fromId?Source session ID for a fork. Omitted for sessions that were not forked; distinct from subagent parentage

The embedded ModelSpec uses the model fields documented in the Catalog Reference. Since v0.preview.92, its optional supported_efforts field carries a custom model's accepted effort ladder; it replaces the old output-only effort_options field.

ProviderSpec fields:

FieldTypeDescription
idstringProvider key, e.g. "anthropic" (accepts name as a legacy alias)
display_namestringFriendly name for display
base_urlstringEndpoint actually in use for this session — environment overrides can move it away from the catalog default
credentialstring?The session's explicit credential choice, as a reference such as "oauth:antix/work", never a token. Omitted when the provider uses its configured order. Available since v0.3.0

The payload type was named SessionInitialized before v0.preview.94 and is now SessionInfo. This is a Rust type name only — the event names (SessionStart, SessionUpdated) and the JSON are unchanged, so no client needs to adapt.

ProviderSpec deliberately carries only what a client cannot look up for itself. The provider's model list is not on the wire; it is identical for every session and is published by ante catalog. Unknown fields are ignored, so payloads from older daemons that still send preferred_models decode fine.

SessionRewound​

Confirms that the active conversation and its persisted history now stand just before to. This acknowledgment is never included in resume replay. A client should remove the selected input and later entries from its displayed conversation after receiving it.

{ "event": { "SessionRewound": { "to": "op_input_1" } }, "parent": "op_rewind_1" }

SessionForked​

Confirms that session_id was saved from forked_from, before input at when present or from the whole conversation when omitted. It does not announce a running session; resume the returned ID to start using it.

{ "event": { "SessionForked": { "forked_from": "ses_parent", "session_id": "ses_child" } }, "parent": "op_fork_1" }

SessionUpdated​

Emitted when the active session is updated in place (e.g. model changed via UpdateSession). Carries the same fields as SessionStart.

{
"event": {
"SessionUpdated": {
"model": { "id": "gpt-6.1-sol" },
"provider": {
"id": "openai",
"display_name": "OpenAI",
"base_url": "https://api.openai.com/v1"
},
"session_id": "ses_01ARZ...",
"cwd": "/home/user/project",
"permission_mode": "strict",
"skills": [],
"subagents": []
}
}
}

SessionEnd​

Emitted when the session span closes. Mirrors TurnEnd: it carries the session's identity, why it ended, and its final usage accounting.

{
"event": {
"SessionEnd": {
"session_id": "ses_01ARZ...",
"reason": "Shutdown",
"usage": { "input_tokens": 15000, "output_tokens": 3000 }
}
}
}

SessionEndReason variants:

VariantDescription
ReplacedThe session was replaced by a new or resumed session
ShutdownThe daemon is shutting down

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
Questiontool_use_id: string, questions: QuestionSpec[]Waiting for user response to structured questions from AskUser

QuestionSpec fields:

FieldTypeDescription
headerstringShort label for the question
questionstringFull question text
multi_selectboolWhether multiple options may be selected; defaults to false. The wire field is multi_select, while the tool input uses multiSelect
optionsQuestionOption[]Offered choices; the client adds its own free-text "Other" input

Each QuestionOption has label and description strings, plus an optional preview string for a code snippet, mockup, or config excerpt. Other tools in the batch may keep running during a question pause. Keep consuming events and clear any pending question on TurnResume or TurnEnd.

TurnResume​

Emitted when the turn resumes after a TurnPause (e.g. the approval was answered or a steer arrived), so clients never have to infer resumption from the next tool event.

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

TurnEnd​

Emitted when a turn completes. steps is the number of turn-loop steps attempted before the turn ended.

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

TurnEndStatus variants:

VariantFieldsDescription
Completed—Turn finished successfully
Interruptedreason?: stringTurn was interrupted
Errorkind?: string, headline: string, details: string[]Turn ended with an error. kind is a stable machine-readable LLM error kind when available — notably "oauth" means the provider sign-in is missing, expired, or revoked. headline is a one-line summary; details are expanded cause lines

Message streaming events​

AgentMessage​

Complete agent text response, assembled from the stream. Clients displaying deltas should avoid rendering this text a second time.

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

Thinking​

Complete reasoning block, assembled from the stream. Clients displaying reasoning deltas should avoid rendering this text a second time.

{ "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",
"tool_name": "Read",
"status": "Completed",
"result_json": { "content": "fn main() { ... }" }
}
}
}

ToolEndStatus variants:

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

tool_name is populated even when execution never starts, so clients can render denied, cancelled, and malformed calls without reconstructing the name from a preceding event. Older events may deserialize it as an empty string. There is no separate is_error field: derive it from status (Completed is the only non-error status). Every accepted tool call gets a terminal ToolEnd, and concurrently running calls emit it independently as each one settles.

TaskEnd​

Emitted when tracked work handed off by a tool call finishes, such as a Bash command that outlived its inline wait window. Available since v0.2.3. The earlier ToolEnd completes the tool call and returns the background handle; it does not mean the process has exited.

{ "event": { "TaskEnd": { "tool_use_id": "tool_use_abc123", "exit_code": 0 } } }

tool_use_id identifies the original call. exit_code is the process exit code (128 + signal for signal termination), or null if the exit could not be observed. Clients can use it to settle the original running-job display. The event is persisted and replayed when the session resumes.

Compaction events​

CompactStart​

Emitted when dialog compaction begins.

{ "event": "CompactStart" }

CompactEnd​

Emitted when dialog compaction completes. A non-null summary is the text committed as the replacement for compacted history and carried forward as context; clients should invalidate earlier rewind and fork targets. null means no history replacement was committed, so those targets remain valid. Do not invalidate targets on CompactStart alone.

{ "event": { "CompactEnd": { "summary": "The user asked to..." } } }

Extension events​

ExtensionRefreshed​

Emitted when skills, subagents, or MCP servers are refreshed. It is sent once immediately after SessionStart with the same skills/subagents and mcp_servers: []. When MCP servers are configured, a second event follows after warm-up with the discovered servers and tools; with no MCP configuration, the initial event is the only one.

{
"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 describes a configured server and its discovered tools. Local servers have command and args; remote servers have url, an empty command, and empty args. The optional url field was added in v0.2.12 and is omitted for local servers. The tools list is empty for servers that failed to connect or exposed no tools, 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. context is omitted before the first response, when the model context limit is unknown, and on usage forwarded from a sub-agent. A client should add every usage value to its totals but retain the last root-session context snapshot when context is absent; delegated work must not replace the root indicator. A present context carries raw measurements only — used_tokens (cache-inclusive window occupancy) and limit_tokens (the raw model context limit); any percentage is derived by the client. usage may also include cache_read_tokens and cache_creation_tokens when the provider reports them.

{
"event": {
"UsageUpdate": {
"usage": { "input_tokens": 1500, "output_tokens": 300 },
"context": { "used_tokens": 1800, "limit_tokens": 200000 }
}
}
}

ContextReport​

Answer to the ContextReport operation: a per-category breakdown of the session's context-window occupancy at the time of the request.

{
"event": {
"ContextReport": {
"system_prompt_tokens": 3200,
"system_tools_tokens": 4100,
"mcp_tools_tokens": 900,
"memory_tokens": 600,
"skills_tokens": 450,
"messages_tokens": 12000,
"used_tokens": 21250,
"limit_tokens": 200000,
"compact_buffer_tokens": 20000
}
}
}
FieldTypeDescription
system_prompt_tokensu32System prompt, excluding the skills and memory sections counted separately
system_tools_tokensu32Built-in tool schemas
mcp_tools_tokensu32MCP tool schemas
memory_tokensu32Project/user instruction files and the auto-memory prompt
skills_tokensu32The available-skills listing
messages_tokensu32Conversation messages — everything not attributed to a category above
used_tokensu32Total context-window occupancy
limit_tokensu32?Model context limit; null when unverified
compact_buffer_tokensu32Tokens reserved at the top of the window; auto-compaction triggers once occupancy grows into this reserve

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. When loading is true the renderer animates a ./../... suffix on the header until the first InfoBlockAppend arrives.

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

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" } }

ShellOutput​

Answer to a ShellInput operation: the command that ran and what it produced.

{
"event": {
"ShellOutput": {
"command": "git status",
"stdout": "On branch main...",
"stderr": "",
"exit_code": 0
}
}
}

Ambient​

An ephemeral ambient hint produced off the main conversation on a cheap model — a predicted "thinking phrase" for the current draft, or a suggested next prompt shown as input ghost text. Requested with the AmbientPhrase / AmbientSuggestion operations; req_id lets clients drop superseded results. Never persisted to the event log.

{ "event": { "Ambient": { "kind": "PromptSuggestion", "req_id": 7, "text": "run the tests" } } }

Offline mode and the protocol​

Local-model management (engine installation, model discovery, and local-router 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 the managed llama serve router (or connects to an external server), 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 Tokio mpsc channels:

ChannelDirectionCapacity
Op channelClient → Daemon256 messages
Event channelDaemon → ClientUnbounded; Ante warns when the pending backlog reaches 4096 and reports when it drains

Events are never dropped or blocked behind a slow in-process client. A client that stops reading accumulates a memory backlog, so consumers should keep draining the event receiver even while handling other work.

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

Unix socket transport​

For clients that dial a host someone else is running, ante serve --sock [PATH] listens on a Unix domain socket (default run/serve.sock in the Ante home) and exchanges the same JSONL protocol over the stream:

  • Each client gets its own connection and at most one active session on the shared host
  • Op::Shutdown ends only that connection; the host keeps accepting new ones
  • The host process exits on SIGINT or SIGTERM
  • Ownership is an exclusive lock on a .lock file beside the socket, so a second host on the same path refuses to start rather than displacing the live one; a stale socket left by a crashed host holds no lock and is replaced

WebSocket transport​

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

  • Each WebSocket client gets its own connection and session on the shared host
  • Messages are the same OpMsg and EventMsg JSON objects, sent as text frames
  • Client disconnect → That connection and session shut down; other clients and the host continue running
  • Op::Shutdown received → Only that connection exits
  • The host accepts clients concurrently until it receives SIGINT or SIGTERM
  • Input remains readable while an event write is stalled; each write has a 30-second deadline, and reply-queue overflow disconnects only the offending peer

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 } │
│ │