Control Socket Protocol Reference
The control socket is an opt-in local Unix/named-pipe socket that enables external operator consoles (CLIs, bots, GUIs) to interact with a running agents-fleet session. Enable it with --control-socket, AGENTS_FLEET_CONTROL_SOCKET=1 or "controlSocket": true in ~/.fleet/config.json. Opting in to the Telegram hub (--hub, AGENTS_FLEET_TELEGRAM_HUB_ATTACH=1 or "telegramHub": {"attach": true}) implies it.
The wire format is newline-delimited JSON (NDJSON): each message is a single JSON object followed by \n. The current wire-protocol version is 1 (PROTOCOL_VERSION). All message types are additive — legacy decoders tolerate unknown type values and ignore them.
Source: src/controlplane/daemon/protocol.ts
Client → Server Messages
Messages sent by a connected front-end (REPL, bot, other) to the daemon.
hello
First frame a client must send after connecting. Carries authentication and client identity.
| Field | Type | Required | Description |
|---|---|---|---|
type | 'hello' | ✓ | Message discriminator. |
token | string | ✓ | Authentication token. |
clientKind | 'repl' | 'bot' | 'other' | ✓ | Front-end type. |
clientName | string | Human-readable client name. | |
protocolVersion | number | Wire version the client speaks (default: 1). |
prompt
A user-typed prompt from any client; broadcast to all connected clients and routed to the coordinator session.
| Field | Type | Required | Description |
|---|---|---|---|
type | 'prompt' | ✓ | Message discriminator. |
text | string | ✓ | Prompt text. |
requestId | string | Client submission id (1–256 chars). Echoed back on the resulting userMessage so the sender can suppress its own echo. |
confirmResponse
A client's boolean answer to a pending confirmRequest.
| Field | Type | Required | Description |
|---|---|---|---|
type | 'confirmResponse' | ✓ | Message discriminator. |
id | string | ✓ | ID of the pending confirm request. |
approve | boolean | ✓ | Whether the action is approved. |
askResponse
A client's textual answer to a pending ask (freeform or choice). For a choice ask, text is the selected option's value; for a freeform ask, it is the literal text the user entered.
| Field | Type | Required | Description |
|---|---|---|---|
type | 'askResponse' | ✓ | Message discriminator. |
id | string | ✓ | ID of the pending ask request. |
text | string | ✓ | The answer text or selected value. |
ping
Liveness probe; the server replies with pong.
| Field | Type | Required | Description |
|---|---|---|---|
type | 'ping' | ✓ | Message discriminator. |
nativeRoot
Native controls require resolved native mode. Fresh sessions default to legacy; opt in with --native-root. --no-native-root explicitly selects legacy. Saved sessions preserve their mode and opposite-mode resume is refused. Neither the socket nor an integration-target flag changes that mode or grants adoption. After the normal authenticated hello, send a strict frame containing only type, id and request. The native frame limit is 16 KiB. Client-supplied principal/role/owner fields do not grant authority.
{"type":"nativeRoot","id":"read-1","request":{"action":"status","rootId":"ROOT_ID"}}Replace ROOT_ID with a root returned by {"action":"list"}. Native requests are separate from prompt and bypass the model queue. Operator evidence/proof preparation is asynchronous; responses may arrive later, correlated by id. That ID is not the durable operationId.
| Action | Request fields in addition to action |
|---|---|
list | None |
status | rootId |
pause, resume, stop, abort | rootId, operationId, expectedRevision; optional taskId |
release | rootId, operationId, expectedRevision, holdHash |
reclassify | rootId, operationId, expectedRevision |
approve, deny | rootId, operationId, expectedRevision, taskId, nonempty rationale; optional supersedes |
manual-start | rootId, operationId, expectedRevision, taskId |
reconcile | rootId, operationId, expectedRevision, proofId; optional taskId |
abandon | rootId, operationId, expectedRevision; optional taskId |
For a new mutation use a unique operation ID and the observed revision. Retry an uncertain result with the original operation ID, revision and full body, not a fresh revision or a new request intended to recross the effect. JSON uses numeric expectedRevision, not the CLI's --revision string.
Approval is an evidence-pinned human decision, not a task status update. supersedes must name the prior decision when replacing it; consumed acceptance cannot be rewritten. manual-start releases an exact unattempted reservation. The current proofId resolver accepts native worker-settlement records only; arbitrary files, claims and Git SHAs are not repair proof. abandon maps to stop intent and does not claim external cessation. reclassify opens a fresh empty policy root, leaving old work and uncertainty unchanged. Its response is a policy-admission result carrying rootId, previousRootId, previousRootUnchanged, a projected receipt and sessionRebound. sessionRebound is true only when the open persisted durably and the session's default root binding now resolves to the new rootId; the previous root keeps its grant and stays addressable by explicit rootId.
See native controls for CLI quoting, acceptance prerequisites and task-scoped control limits. Sources: src/commands/nativeRootCommand.ts, src/orchestration/control/protocol.ts, src/orchestration/control/NativeOperatorControls.ts, src/orchestration/control/nativeRootTransport.ts.
Server → Client Messages
Messages broadcast from the daemon to all connected clients.
nativeRootResult
A native request returns to its requesting connection:
{ type: "nativeRootResult", id: <transport id or null>, result: <native result> }Read results have kind roots or status; ordinary mutations return kind receipt, a projected durable receipt and acknowledgmentOnly:true. Reclassification returns kind policy-admission with previousRootId, rootId, receipt and previousRootUnchanged:true. Failures return kind error with a safe code. Inspect the receipt status: persistence is not completed execution, acceptance, drain or cancellation. Follow root status for actual phase, evidence, decisions, effects, holds and pending receipts. These handlers still depend on the Node event loop and external services; they do not promise recovery from every RPC or UI stall.
welcome
Sent to a client immediately after a successful hello.
| Field | Type | Required | Description |
|---|---|---|---|
type | 'welcome' | ✓ | Message discriminator. |
sessionId | string | Current session ID. | |
model | string | Active model name. | |
protocolVersion | number | Wire version the server speaks. | |
sessionName | string | Human-readable session name, when the fleet has one. | |
cwd | string | Working directory of the fleet process (fleet control sockets). | |
capabilities | string[] | Additive server capabilities. Fleet control sockets advertise replayEnd, confirmResolvedReason and requestIdEcho. |
userMessage
Echo of a prompt one client sent, fanned out so all clients see it.
| Field | Type | Required | Description |
|---|---|---|---|
type | 'userMessage' | ✓ | Message discriminator. |
source | string | ✓ | Originating client name/kind. |
text | string | ✓ | The prompt text. |
requestId | string | Echo of the originating prompt.requestId, when one was sent. |
reply
An assistant reply chunk/turn broadcast to all clients.
| Field | Type | Required | Description |
|---|---|---|---|
type | 'reply' | ✓ | Message discriminator. |
text | string | ✓ | Reply content. |
ts | number | Epoch-ms timestamp. |
status
Periodic status snapshot with optional dashboard-level worker/task state.
| Field | Type | Required | Description |
|---|---|---|---|
type | 'status' | ✓ | Message discriminator. |
processing | boolean | ✓ | Whether the coordinator is currently processing. |
model | string | Active model name. | |
provider | string | Provider name (copilot, claude). | |
runningFleets | number | Count of running fleet instances. | |
workers | StatusWorker[] | Condensed list of workers ({ name, status }). | |
activeWorkerCount | number | Workers currently running. | |
totalWorkerCount | number | Total workers tracked. | |
tasksDone | number | Completed tasks. | |
tasksTotal | number | Total tasks. | |
tasksActive | number | Tasks in progress. |
confirmRequest
A request for human confirmation, broadcast to all clients.
| Field | Type | Required | Description |
|---|---|---|---|
type | 'confirmRequest' | ✓ | Message discriminator. |
id | string | ✓ | Unique request ID. |
summary | string | ✓ | Human-readable action summary. |
ts | number | Epoch-ms creation time (kept verbatim when replayed). |
askRequest
A request for a human answer (freeform text or choice selection). Distinct from confirmRequest (which is boolean approve/deny).
| Field | Type | Required | Description |
|---|---|---|---|
type | 'askRequest' | ✓ | Message discriminator. |
id | string | ✓ | Unique request ID. |
question | string | ✓ | The question text. |
kind | string | ✓ | Ask kind (open string). |
choices | AskChoiceOption[] | Selectable options ({ label, value, description? }). Omitted for freeform asks. | |
allowFreeform | boolean | Whether the user can type a freeform answer alongside choices. | |
ts | number | Epoch-ms creation time (kept verbatim when replayed). |
confirmResolved
Notification that a pending confirm or ask was resolved — by any client, by another surface of the fleet (e.g. the terminal answered first), by timeout or by shutdown. Absent kind/reason ⇔ a legacy confirm that was answered.
| Field | Type | Required | Description |
|---|---|---|---|
type | 'confirmResolved' | ✓ | Message discriminator. |
id | string | ✓ | The resolved request ID. |
approved | boolean | ✓ | Resolution outcome (true for an answered ask). |
kind | 'confirm' | 'ask' | Which request type was resolved. | |
reason | 'answered' | 'superseded' | 'expired' | 'cancelled' | superseded = answered elsewhere first (the fleet observes the request's abort signal); expired = TTL; cancelled = channel disposed. |
replayEnd
Marks the end of the replay burst a fleet control socket sends right after welcome (advertised via welcome.capabilities: ['replayEnd']). Frames received between welcome and replayEnd are history, not live events.
| Field | Type | Required | Description |
|---|---|---|---|
type | 'replayEnd' | ✓ | Message discriminator. |
Replay order on attach (fleet control sockets)
After a successful hello, a late-attaching client receives, in order:
welcome;- the cached set-once snapshots (
commandCatalog,fleetIntel,fleetEvolution,fleetStore,fleetExperiment,hydrate), when present; - every unresolved
confirmRequest/askRequest, in creation order, with their originalts; - the last
fleetState, when one was broadcast; replayEnd.
Live frames follow. A client that must not repost history (the Telegram hub) treats everything before replayEnd as replay.
error
A protocol or runtime error surfaced to the client.
| Field | Type | Required | Description |
|---|---|---|---|
type | 'error' | ✓ | Message discriminator. |
message | string | ✓ | Error description. |
pong
Reply to a client ping.
| Field | Type | Required | Description |
|---|---|---|---|
type | 'pong' | ✓ | Message discriminator. |
transcript
Per-worker transcript line streamed to attached clients so a fleet-view UI can relay worker-level output.
| Field | Type | Required | Description |
|---|---|---|---|
type | 'transcript' | ✓ | Message discriminator. |
role | 'assistant' | 'tool' | 'system' | 'user' | ✓ | Source role. |
text | string | ✓ | Transcript line content. |
agent | string | Worker name/ID (omitted for coordinator-level lines). | |
ts | number | Epoch-ms timestamp. |
Fleet State & Dashboard Messages
fleetState
Rich live fleet-state snapshot for operator-console dashboards. Broadcast periodically when the control socket is enabled.
| Field | Type | Required | Description |
|---|---|---|---|
type | 'fleetState' | ✓ | Message discriminator. |
ts | number | ✓ | Snapshot wall-clock time (epoch-ms). |
coordinator | FleetCoordinatorState | ✓ | Coordinator state (see below). |
workers | FleetWorkerSummary[] | ✓ | Per-worker summaries (see below). |
workerActive | number | ✓ | Workers currently active. |
workerTotal | number | ✓ | Total workers tracked. |
tasks | FleetTaskSummary | ✓ | Task counts + sampled items. |
session | FleetSessionInfo | Session telemetry (model, provider, sessionName, uptime). | |
tokens | FleetTokenState | Token / context-window data. | |
workerStatusCounts | FleetWorkerStatusCounts | Rollup: { running, idle, completed, failed }. | |
workerMax | number | Configured concurrency cap. | |
loops | FleetLoopSummary[] | Active goal-driven loops. |
FleetCoordinatorState
| Field | Type | Required | Description |
|---|---|---|---|
sessionId | string | Session identifier. | |
isProcessing | boolean | ✓ | Whether the coordinator is actively processing. |
planMode | boolean | ✓ | Whether plan mode is engaged. |
dispatchPaused | boolean | ✓ | Whether task dispatch is paused. |
staleMs | number | Elapsed ms since last SDK activity (only while processing + watchdog active). |
FleetWorkerSummary
Per-worker summary with optional analytics enrichment (Tier 2 fields).
| Field | Type | Required | Description |
|---|---|---|---|
id | string | ✓ | Worker agent ID. |
name | string | ✓ | Worker name. |
status | string | ✓ | Coarse status. |
toolUseCount | number | ✓ | Total tool invocations. |
tokenCount | number | ✓ | Total tokens consumed. |
agentType | string | Agent type (e.g. 'coder', 'explorer'). | |
taskId | string | Assigned task ID. | |
startedAt | number | Epoch-ms start time. | |
lastActivityAt | number | Epoch-ms of last activity. | |
worktreePath | string | Absolute path to the worker's git worktree. | |
topTools | string[] | Most-used tool names (analytics enrichment). | |
recentActivity | string | Last tool/activity description. | |
inputTokens | number | Per-worker input token count. | |
outputTokens | number | Per-worker output token count. | |
completedAt | number | Epoch-ms completion time. | |
durationMs | number | Elapsed ms from start to completion (or now). |
FleetTokenState
| Field | Type | Required | Description |
|---|---|---|---|
contextTokens | number | Current context-window usage. | |
contextLimit | number | Context-window limit. | |
contextPercent | number | Context usage as a percentage. | |
messagesLength | number | Number of messages in context. | |
inputTokens | number | Session-total input tokens. | |
outputTokens | number | Session-total output tokens. | |
totalTokens | number | Session-total tokens. | |
cacheReadTokens | number | Session-total cache-read tokens. | |
cacheWriteTokens | number | Session-total cache-write tokens. |
FleetTaskSummary
| Field | Type | Required | Description |
|---|---|---|---|
done | number | ✓ | Completed tasks. |
running | number | ✓ | In-progress tasks. |
pending | number | ✓ | Pending tasks. |
blocked | number | ✓ | Blocked tasks. |
total | number | ✓ | Total tasks. |
items | FleetTaskItem[] | Sampled task items ({ id, subject, status, owner? }). |
Analytics Message
fleetAnalytics
Periodic analytics snapshot broadcast alongside fleetState.
| Field | Type | Required | Description |
|---|---|---|---|
type | 'fleetAnalytics' | ✓ | Message discriminator. |
ts | number | ✓ | Snapshot wall-clock time (epoch-ms). |
toolUsage | ToolUsageEntry[] | ✓ | Aggregated tool usage across all workers. |
workerTimeline | WorkerTimelineEntry[] | ✓ | Worker timeline for Gantt-style rendering. |
ToolUsageEntry
| Field | Type | Required | Description |
|---|---|---|---|
toolName | string | ✓ | Tool name. |
count | number | ✓ | Total invocations. |
lastUsedAt | number | Epoch-ms of last use. |
WorkerTimelineEntry
| Field | Type | Required | Description |
|---|---|---|---|
id | string | ✓ | Worker ID. |
name | string | ✓ | Worker name. |
agentType | string | ✓ | Agent type. |
startedAt | number | ✓ | Epoch-ms start time. |
status | string | ✓ | Worker status. |
toolUseCount | number | ✓ | Tool invocations. |
tokenCount | number | ✓ | Tokens consumed. |
completedAt | number | Epoch-ms completion time. |
File Change Notifications
fileChange
Streamed to attached clients when a file-mutating tool completes so an operator console can render a live file-change feed. Bridged from file.changed store events in src/cli/controlSocket.ts.
| Field | Type | Required | Description |
|---|---|---|---|
type | 'fileChange' | ✓ | Message discriminator. |
ts | number | ✓ | Epoch-ms snapshot timestamp. |
changes | FileChangeEntry[] | ✓ | Array of file changes. |
FileChangeEntry
| Field | Type | Required | Description |
|---|---|---|---|
path | string | ✓ | Path relative to fleet cwd. |
absolutePath | string | ✓ | Full resolved absolute path. |
agent | string | ✓ | Worker ID/name that made the change. |
tool | string | ✓ | Tool that produced the change (e.g. 'edit_file', 'create_file'). |
changeType | 'created' | 'edited' | 'deleted' | ✓ | Nature of the change. |
ts | number | ✓ | Epoch-ms timestamp. |
Tool Call Notifications
toolCall
Streamed to attached clients when a coordinator or worker tool call starts or completes. Enables live tool-call feeds and analytics in operator consoles. The result field is truncated to 2000 characters.
| Field | Type | Required | Description |
|---|---|---|---|
type | 'toolCall' | ✓ | Message discriminator. |
agent | string | ✓ | Agent ID (e.g. 'coordinator' or a worker AgentId). |
tool | string | ✓ | Tool name. |
phase | 'start' | 'complete' | ✓ | Whether this is a start or completion event. |
ts | number | ✓ | Epoch-ms timestamp. |
agentName | string | Human-readable agent name (workers may have descriptive names). | |
detail | string | Human-readable detail/description (typically from tool_start). | |
input | Record<string, unknown> | Tool input parameters (start phase only; best-effort). | |
result | string | Tool result or error message (complete phase only; truncated to 2000 chars). | |
isError | boolean | true when the tool reported an error. | |
durationMs | number | Duration in milliseconds (complete phase only). |
Hub Activity Feed
hubEvent
Inter-agent activity event for live fleet communication feeds: worker→coordinator reports, coordinator→worker messages, broadcasts, and agent/task lifecycle transitions.
| Field | Type | Required | Description |
|---|---|---|---|
type | 'hubEvent' | ✓ | Message discriminator. |
from | string | ✓ | Source agent/endpoint ID (e.g. an AgentId or 'coordinator'). |
kind | string | ✓ | Event kind (open string): 'report', 'message', 'broadcast', 'spawned', 'completed', 'failed', 'task.created', 'task.updated'. Consumers must handle unknown values. |
ts | number | ✓ | Epoch-ms timestamp. |
to | string | Recipient endpoint ID (omitted for broadcasts and lifecycle events). | |
text | string | Human-readable summary (message content, task subject, error). |
Session Bootstrap Messages
hydrate
History-backfill batch sent once when an operator console attaches, so it can populate conversation and per-worker transcripts that occurred before connection.
| Field | Type | Required | Description |
|---|---|---|---|
type | 'hydrate' | ✓ | Message discriminator. |
conversation | Array<{ role, text, ts }> | ✓ | Recent conversation history. |
transcripts | Array<{ agent, role, text, ts }> | ✓ | Per-worker transcript lines. |
hub | HubEventMessage[] | ✓ | Hub activity feed history. |
truncated | boolean | ✓ | true when conversation was capped to a recent window. |
commandCatalog
Full CLI command catalog pushed once on attach so the operator console can discover and render every slash command (built-ins and workflow commands).
| Field | Type | Required | Description |
|---|---|---|---|
type | 'commandCatalog' | ✓ | Message discriminator. |
commands | CommandCatalogEntry[] | ✓ | Array of { name, description, kind, category }. |
Intelligence & Evolution Messages
fleetIntel
Fleet-intelligence snapshot for the operator-console Intel dashboard. Pushed once at boot, cached and replayed to each client on attach, and re-broadcast after intel-affecting commands (/fleet-intel, /learn).
| Field | Type | Required | Description |
|---|---|---|---|
type | 'fleetIntel' | ✓ | Message discriminator. |
ts | number | ✓ | Epoch-ms timestamp. |
summary | FleetIntelSummary | ✓ | Aggregate stats: { sessionCount, totalAgentRuns, failedRuns, successRate, totalTokens, avgSessionDurationMs, topErrors }. |
suggestions | FleetIntelSuggestion[] | ✓ | Pending suggestions: { id, suggestionType, title, confidence, createdAt }. |
skills | FleetIntelSkill[] | ✓ | Skill usage: { name, uses, lastUsedAt, crew? }. |
fleetEvolution
Role/skill evolution (self-improvement) snapshot for the /learn pipeline dashboard. Same caching and replay semantics as fleetIntel.
| Field | Type | Required | Description |
|---|---|---|---|
type | 'fleetEvolution' | ✓ | Message discriminator. |
ts | number | ✓ | Epoch-ms timestamp. |
shadows | FleetEvolutionShadow[] | ✓ | Proposed-but-not-applied skill patches (A/B candidates). |
timeline | FleetEvolutionRecord[] | ✓ | Applied evolution history. |
skills | FleetEvolutionSkill[] | ✓ | Per-skill telemetry (success rate, evolution count). |
fleetStore
Read-only intelligence-store explorer snapshot for browsing the fleet's accumulated intelligence database (~/.fleet/intel/fleet-intel.db).
| Field | Type | Required | Description |
|---|---|---|---|
type | 'fleetStore' | ✓ | Message discriminator. |
ts | number | ✓ | Epoch-ms timestamp. |
counts | FleetStoreCount[] | ✓ | Entity row counts: { name, count }. |
sessions | FleetStoreSession[] | ✓ | Top sessions by token spend. |
steering | FleetStoreSteering[] | ✓ | Recent steering insights. |
fleetExperiment
Experiment-framework snapshot for the operator-console Experiments dashboard.
| Field | Type | Required | Description |
|---|---|---|---|
type | 'fleetExperiment' | ✓ | Message discriminator. |
ts | number | ✓ | Epoch-ms timestamp. |
experiments | FleetExperimentEntry[] | ✓ | Experiments with optional latestRun aggregates (per-variant mean, pass rate, lift, winner). |
Discovery descriptor
A fleet with its control socket enabled registers a descriptor (<id>.json) in ~/.fleet/sockets (override AGENTS_FLEET_REGISTRY_DIR): schemaVersion, id (<pid>-<suffix>), pid, name, cwd, socketPath, tokenPath, fleetHome, startedAt, protocolVersion, and — only when the fleet opted in to the Telegram hub — hubAttach: true. Absent hubAttach ⇒ the hub ignores the fleet. The hub validates every field (the socket path and token path must be the ones derived from fleetHome) and asks the operator to approve the fleet before it reads the token. Source: src/channels/daemon/fleetRegistry.ts.
Wire Framing
All messages are encoded as newline-delimited JSON. The encodeMessage() function serializes a message to JSON.stringify(msg) + '\n'. The decoder buffers incoming bytes and splits on \n, with a 1 MiB per-frame safety cap to prevent unbounded memory growth from malformed input.
{"type":"status","processing":true,"model":"gpt-4o"}\n
{"type":"reply","text":"Hello from the coordinator"}\n