Skip to content

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.

FieldTypeRequiredDescription
type'hello'✓Message discriminator.
tokenstring✓Authentication token.
clientKind'repl' | 'bot' | 'other'✓Front-end type.
clientNamestringHuman-readable client name.
protocolVersionnumberWire 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.

FieldTypeRequiredDescription
type'prompt'✓Message discriminator.
textstring✓Prompt text.
requestIdstringClient 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.

FieldTypeRequiredDescription
type'confirmResponse'✓Message discriminator.
idstring✓ID of the pending confirm request.
approveboolean✓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.

FieldTypeRequiredDescription
type'askResponse'✓Message discriminator.
idstring✓ID of the pending ask request.
textstring✓The answer text or selected value.

ping ​

Liveness probe; the server replies with pong.

FieldTypeRequiredDescription
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.

json
{"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.

ActionRequest fields in addition to action
listNone
statusrootId
pause, resume, stop, abortrootId, operationId, expectedRevision; optional taskId
releaserootId, operationId, expectedRevision, holdHash
reclassifyrootId, operationId, expectedRevision
approve, denyrootId, operationId, expectedRevision, taskId, nonempty rationale; optional supersedes
manual-startrootId, operationId, expectedRevision, taskId
reconcilerootId, operationId, expectedRevision, proofId; optional taskId
abandonrootId, 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:

text
{ 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.

FieldTypeRequiredDescription
type'welcome'✓Message discriminator.
sessionIdstringCurrent session ID.
modelstringActive model name.
protocolVersionnumberWire version the server speaks.
sessionNamestringHuman-readable session name, when the fleet has one.
cwdstringWorking directory of the fleet process (fleet control sockets).
capabilitiesstring[]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.

FieldTypeRequiredDescription
type'userMessage'✓Message discriminator.
sourcestring✓Originating client name/kind.
textstring✓The prompt text.
requestIdstringEcho of the originating prompt.requestId, when one was sent.

reply ​

An assistant reply chunk/turn broadcast to all clients.

FieldTypeRequiredDescription
type'reply'✓Message discriminator.
textstring✓Reply content.
tsnumberEpoch-ms timestamp.

status ​

Periodic status snapshot with optional dashboard-level worker/task state.

FieldTypeRequiredDescription
type'status'✓Message discriminator.
processingboolean✓Whether the coordinator is currently processing.
modelstringActive model name.
providerstringProvider name (copilot, claude).
runningFleetsnumberCount of running fleet instances.
workersStatusWorker[]Condensed list of workers ({ name, status }).
activeWorkerCountnumberWorkers currently running.
totalWorkerCountnumberTotal workers tracked.
tasksDonenumberCompleted tasks.
tasksTotalnumberTotal tasks.
tasksActivenumberTasks in progress.

confirmRequest ​

A request for human confirmation, broadcast to all clients.

FieldTypeRequiredDescription
type'confirmRequest'✓Message discriminator.
idstring✓Unique request ID.
summarystring✓Human-readable action summary.
tsnumberEpoch-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).

FieldTypeRequiredDescription
type'askRequest'✓Message discriminator.
idstring✓Unique request ID.
questionstring✓The question text.
kindstring✓Ask kind (open string).
choicesAskChoiceOption[]Selectable options ({ label, value, description? }). Omitted for freeform asks.
allowFreeformbooleanWhether the user can type a freeform answer alongside choices.
tsnumberEpoch-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.

FieldTypeRequiredDescription
type'confirmResolved'✓Message discriminator.
idstring✓The resolved request ID.
approvedboolean✓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.

FieldTypeRequiredDescription
type'replayEnd'✓Message discriminator.

Replay order on attach (fleet control sockets) ​

After a successful hello, a late-attaching client receives, in order:

  1. welcome;
  2. the cached set-once snapshots (commandCatalog, fleetIntel, fleetEvolution, fleetStore, fleetExperiment, hydrate), when present;
  3. every unresolved confirmRequest / askRequest, in creation order, with their original ts;
  4. the last fleetState, when one was broadcast;
  5. 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.

FieldTypeRequiredDescription
type'error'✓Message discriminator.
messagestring✓Error description.

pong ​

Reply to a client ping.

FieldTypeRequiredDescription
type'pong'✓Message discriminator.

transcript ​

Per-worker transcript line streamed to attached clients so a fleet-view UI can relay worker-level output.

FieldTypeRequiredDescription
type'transcript'✓Message discriminator.
role'assistant' | 'tool' | 'system' | 'user'✓Source role.
textstring✓Transcript line content.
agentstringWorker name/ID (omitted for coordinator-level lines).
tsnumberEpoch-ms timestamp.

Fleet State & Dashboard Messages ​

fleetState ​

Rich live fleet-state snapshot for operator-console dashboards. Broadcast periodically when the control socket is enabled.

FieldTypeRequiredDescription
type'fleetState'✓Message discriminator.
tsnumber✓Snapshot wall-clock time (epoch-ms).
coordinatorFleetCoordinatorState✓Coordinator state (see below).
workersFleetWorkerSummary[]✓Per-worker summaries (see below).
workerActivenumber✓Workers currently active.
workerTotalnumber✓Total workers tracked.
tasksFleetTaskSummary✓Task counts + sampled items.
sessionFleetSessionInfoSession telemetry (model, provider, sessionName, uptime).
tokensFleetTokenStateToken / context-window data.
workerStatusCountsFleetWorkerStatusCountsRollup: { running, idle, completed, failed }.
workerMaxnumberConfigured concurrency cap.
loopsFleetLoopSummary[]Active goal-driven loops.

FleetCoordinatorState ​

FieldTypeRequiredDescription
sessionIdstringSession identifier.
isProcessingboolean✓Whether the coordinator is actively processing.
planModeboolean✓Whether plan mode is engaged.
dispatchPausedboolean✓Whether task dispatch is paused.
staleMsnumberElapsed ms since last SDK activity (only while processing + watchdog active).

FleetWorkerSummary ​

Per-worker summary with optional analytics enrichment (Tier 2 fields).

FieldTypeRequiredDescription
idstring✓Worker agent ID.
namestring✓Worker name.
statusstring✓Coarse status.
toolUseCountnumber✓Total tool invocations.
tokenCountnumber✓Total tokens consumed.
agentTypestringAgent type (e.g. 'coder', 'explorer').
taskIdstringAssigned task ID.
startedAtnumberEpoch-ms start time.
lastActivityAtnumberEpoch-ms of last activity.
worktreePathstringAbsolute path to the worker's git worktree.
topToolsstring[]Most-used tool names (analytics enrichment).
recentActivitystringLast tool/activity description.
inputTokensnumberPer-worker input token count.
outputTokensnumberPer-worker output token count.
completedAtnumberEpoch-ms completion time.
durationMsnumberElapsed ms from start to completion (or now).

FleetTokenState ​

FieldTypeRequiredDescription
contextTokensnumberCurrent context-window usage.
contextLimitnumberContext-window limit.
contextPercentnumberContext usage as a percentage.
messagesLengthnumberNumber of messages in context.
inputTokensnumberSession-total input tokens.
outputTokensnumberSession-total output tokens.
totalTokensnumberSession-total tokens.
cacheReadTokensnumberSession-total cache-read tokens.
cacheWriteTokensnumberSession-total cache-write tokens.

FleetTaskSummary ​

FieldTypeRequiredDescription
donenumber✓Completed tasks.
runningnumber✓In-progress tasks.
pendingnumber✓Pending tasks.
blockednumber✓Blocked tasks.
totalnumber✓Total tasks.
itemsFleetTaskItem[]Sampled task items ({ id, subject, status, owner? }).

Analytics Message ​

fleetAnalytics ​

Periodic analytics snapshot broadcast alongside fleetState.

FieldTypeRequiredDescription
type'fleetAnalytics'✓Message discriminator.
tsnumber✓Snapshot wall-clock time (epoch-ms).
toolUsageToolUsageEntry[]✓Aggregated tool usage across all workers.
workerTimelineWorkerTimelineEntry[]✓Worker timeline for Gantt-style rendering.

ToolUsageEntry ​

FieldTypeRequiredDescription
toolNamestring✓Tool name.
countnumber✓Total invocations.
lastUsedAtnumberEpoch-ms of last use.

WorkerTimelineEntry ​

FieldTypeRequiredDescription
idstring✓Worker ID.
namestring✓Worker name.
agentTypestring✓Agent type.
startedAtnumber✓Epoch-ms start time.
statusstring✓Worker status.
toolUseCountnumber✓Tool invocations.
tokenCountnumber✓Tokens consumed.
completedAtnumberEpoch-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.

FieldTypeRequiredDescription
type'fileChange'✓Message discriminator.
tsnumber✓Epoch-ms snapshot timestamp.
changesFileChangeEntry[]✓Array of file changes.

FileChangeEntry ​

FieldTypeRequiredDescription
pathstring✓Path relative to fleet cwd.
absolutePathstring✓Full resolved absolute path.
agentstring✓Worker ID/name that made the change.
toolstring✓Tool that produced the change (e.g. 'edit_file', 'create_file').
changeType'created' | 'edited' | 'deleted'✓Nature of the change.
tsnumber✓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.

FieldTypeRequiredDescription
type'toolCall'✓Message discriminator.
agentstring✓Agent ID (e.g. 'coordinator' or a worker AgentId).
toolstring✓Tool name.
phase'start' | 'complete'✓Whether this is a start or completion event.
tsnumber✓Epoch-ms timestamp.
agentNamestringHuman-readable agent name (workers may have descriptive names).
detailstringHuman-readable detail/description (typically from tool_start).
inputRecord<string, unknown>Tool input parameters (start phase only; best-effort).
resultstringTool result or error message (complete phase only; truncated to 2000 chars).
isErrorbooleantrue when the tool reported an error.
durationMsnumberDuration 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.

FieldTypeRequiredDescription
type'hubEvent'✓Message discriminator.
fromstring✓Source agent/endpoint ID (e.g. an AgentId or 'coordinator').
kindstring✓Event kind (open string): 'report', 'message', 'broadcast', 'spawned', 'completed', 'failed', 'task.created', 'task.updated'. Consumers must handle unknown values.
tsnumber✓Epoch-ms timestamp.
tostringRecipient endpoint ID (omitted for broadcasts and lifecycle events).
textstringHuman-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.

FieldTypeRequiredDescription
type'hydrate'✓Message discriminator.
conversationArray<{ role, text, ts }>✓Recent conversation history.
transcriptsArray<{ agent, role, text, ts }>✓Per-worker transcript lines.
hubHubEventMessage[]✓Hub activity feed history.
truncatedboolean✓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).

FieldTypeRequiredDescription
type'commandCatalog'✓Message discriminator.
commandsCommandCatalogEntry[]✓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).

FieldTypeRequiredDescription
type'fleetIntel'✓Message discriminator.
tsnumber✓Epoch-ms timestamp.
summaryFleetIntelSummary✓Aggregate stats: { sessionCount, totalAgentRuns, failedRuns, successRate, totalTokens, avgSessionDurationMs, topErrors }.
suggestionsFleetIntelSuggestion[]✓Pending suggestions: { id, suggestionType, title, confidence, createdAt }.
skillsFleetIntelSkill[]✓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.

FieldTypeRequiredDescription
type'fleetEvolution'✓Message discriminator.
tsnumber✓Epoch-ms timestamp.
shadowsFleetEvolutionShadow[]✓Proposed-but-not-applied skill patches (A/B candidates).
timelineFleetEvolutionRecord[]✓Applied evolution history.
skillsFleetEvolutionSkill[]✓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).

FieldTypeRequiredDescription
type'fleetStore'✓Message discriminator.
tsnumber✓Epoch-ms timestamp.
countsFleetStoreCount[]✓Entity row counts: { name, count }.
sessionsFleetStoreSession[]✓Top sessions by token spend.
steeringFleetStoreSteering[]✓Recent steering insights.

fleetExperiment ​

Experiment-framework snapshot for the operator-console Experiments dashboard.

FieldTypeRequiredDescription
type'fleetExperiment'✓Message discriminator.
tsnumber✓Epoch-ms timestamp.
experimentsFleetExperimentEntry[]✓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