Protocol
Acolyte defines a versioned transport contract for requests, ordered event streams, task state, errors, authentication, and final responses.
Goal
Define the stable request/response contract between client and server so transport can evolve without changing lifecycle behavior.
Contract shape
- request: one task payload (
message,history,sessionId, runtime options). - stream: ordered event sequence for progress and tool activity.
- final reply: single terminal payload with assistant output and usage metadata.
Event model
Events are append-only and ordered per request.
status— lifecycle/status updates for UI progressreasoning— optional model reasoning texttool-call— tool invocation start (id, name, args)tool-output— incremental tool output for the call idtool-result— tool completion (success/error, structured error detail)text-delta— assistant text stream chunkstext-end— end of a block of assistant text; the next block opens a new paragraphusage— token usage for the current generation steptasklist— inline task list with group ID, title, and itemserror— terminal stream error
Invariants
- every request completes with either a
chat.doneorchat.errorRPC message. tool-output/tool-resultreference a priortool-callid.- unknown event fields are ignored by clients (forward compatibility).
- error detail payloads are structured and stable.
Versioning
- the protocol is versioned and negotiated by capability handshake.
- additive changes are preferred; breaking changes require version bump.
Transport stance
- transport is an implementation detail.
- HTTP+SSE and WebSocket RPC are both supported.
- new transports must preserve this contract and ordering guarantees.
RPC baseline (WebSocket)
RPC uses JSON envelopes with transport request id (rpc_*), type, and optional payload.
Domain task ids are separate (task_*).
Authentication:
- HTTP endpoints use
Authorization: Bearer <apiKey>. - WebSocket RPC uses Bearer auth via
sec-websocket-protocol(bearer.<apiKey>).
Client methods:
status.getchat.start(request payload)chat.abort(request id)task.status(task id)
Server responses:
status.resultchat.accepted(includestaskId)chat.queuedchat.startedchat.eventchat.donechat.error(may includeerrorId)chat.abort.resulttask.status.resulterror
Queue semantics:
- only one chat request runs per connection at a time.
- additional
chat.startrequests are accepted and reported aschat.queuedwith a 1-based position. - queue positions are re-emitted on queue changes (abort/dequeue) so clients can keep ordering accurate.
chat.aborttargets request id, while task lifecycle/state uses task id.