Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Gateway

The gateway is a Unix socket in the home of the alate. The daemon listens on it. Each terminal that attaches is a client, and so is the Telegram bot and the colony bridge.

The gateway is the only door. Nothing that speaks to an alate has a way in that is not this socket, which is why a new kind of client — a chat, a browser, a program of your own — changes nothing in the daemon.

The protocol

One JSON object for each line, in both directions. You can read it with nc, and you can write another client for it.

Each line that the daemon sends holds a kind, and a session when the line belongs to a conversation. A line with no session is the daemon speaking for itself: the greeting, a heartbeat, a session list, a permission question.

A client sends {"kind":"attach"} first. The daemon then opens a session for it and answers with hello. A program that only wants to know whether an alate is awake connects and closes without sending anything, and no conversation is made for it.

A client can also say what it is: {"kind":"attach","channel":"telegram: 42"}. The name is what /sessions shows for that conversation. It is cut to 32 characters, and line ends are removed, because it is printed in a list. The field can be absent, and a client that does not send it is listed as attached.

What a client sends

KindFieldsEffect
attachchannel (optional)Say that this is a client, and open a session for it.
attachattachments (optional)Set this to true when the client can receive file attachments from the agent.
prompttextSay this to the agent, as if it were typed.
cancelStop the run in flight.
answerid, decisionAnswer a confirm. allow, allow_always or deny.
attachment_resultid, error (optional)Confirm an attachment, or report why the gateway could not send it.
watchidLook at a different session, and replay it. With <session>:<message>, replay the newest branch under that message.
sessionsAsk what sessions there are.
treeAsk for the sessions and their branches.
forkidStart a branch at <session>:<message>, in a new session. The connection then watches the new session.
renameid, textGive the name text to the branch that holds <session>:<message>.
newOpen another session on this connection.

A request needs no session on it. A connection has one session that it watches, and each request is about that one. watch is what changes it.

sessions does not name every session there has ever been: the answer holds the open ones and the 20 most recent stored ones, because a client prints the answer in a list. watch still finds an older session by its id, or by the start of one.

What the daemon sends

KindFieldsMeaning
helloinstance, model, context_window, thinkingThe first frame. What this alate is.
session_openedinfoA session started. Sent to everybody.
session_closedidA session ended, and sends nothing more.
sessionslive, storedThe answer to sessions, to the connection that asked. live holds every session that is open; stored holds the 20 most recent on disk.
history_startidA replay starts. What is drawn for this session is old.
history_endidThe replay is complete. What comes now is live.
treesessionsThe answer to tree, to the connection that asked. Each item has id, live and view: the turns of the session and how they branch.
prefilltextA prompt for the input box of this client. A fork at a prompt sends it.
turn_startedA turn started.
texttextText from the model.
thinkingtextReasoning from the model.
tool_stream_startblock, nameA tool call opened, and its arguments still arrive.
tool_stream_deltablock, bytesMore of those arguments arrived.
tool_callid, name, argumentsA tool call, complete and committed.
tool_progressid, chunkPartial output of a tool.
tool_resultid, name, text, is_error, detailsA tool completed.
attachmentid, name, data, captionA Base64 file for an attachment-capable client. This goes only to that client.
turn_endedusage, stop, errorA turn is complete.
run_endedstop, turns, errorThe run stopped.
noticetextSomething a plugin wants seen.
messagefrom, textA message from another conversation, delivered into this one. from names the session that spoke.
prompttextA prompt went to the agent. Echoed to everybody in that session.
heartbeatat, noteThe alate woke on its own.
confirmid, tool, summary, riskA tool waits for permission. The first answer decides.

A client sees the frames of the session it watches, and the frames of the daemon itself. Two terminals on two sessions thus do not draw each other’s replies.

Watching a different session

To change what it watches, a client sends {"kind":"watch","id":"..."}. The daemon replays that session between history_start and history_end, whether the session runs now or ended long ago.

There is no store of recent frames. What a client missed is in the transcript, which is what watch reads — so what it gets back cannot disagree with what happened.

Seven kinds are not replayed: confirm, hello, sessions, tree, prefill, history_start and history_end. A question that was answered an hour ago must not open a window over the new client, and the other six are addressed to one connection and not to a conversation.

Branches

A session is a tree of messages, as in aphid. The address of a message is <session>:<message>.

fork opens a new session that continues the branch at that message. At a prompt, the branch starts before the prompt, and the daemon sends the prompt back in a prefill frame. At an answer that ends its turn, the branch starts after the answer. The new session writes to the same file as the session it came from. Its id is the address it started at. If the source session runs now, the daemon refuses the fork.

watch with an address shows a branch, but it does not continue it. To continue a branch, fork it.

The log

Each line is also written to alate.log in the home. Read the hours when nobody watched with jq:

$ jq -r 'select(.kind == "heartbeat") | .at + "  " + .note' alate.log
$ jq -r 'select(.session == "20260811T090000-0000") | .text // empty' alate.log

This file is the frames, and not the log of the program. For the log of the program, refer to Logs.

The socket

The socket permits only its owner to read and write it. Anything that can connect can make the agent run commands, so the permissions of the file are the whole of the access control.

The gateway needs a Unix socket, so aphid alate does not work on Windows.

A socket file that no daemon is behind is removed and made again. Two daemons cannot serve one alate: the second one stops and says so.

gateway.socket in alate.json moves the file. It is gateway.sock in the home when absent.

The clients

ClientWhat it is
CLIaphid alate attach. A terminal on the alate.
Windowaphid alate gui. A window on the alate, on the desktop.
TelegramA bot. Each chat is a conversation.
ColonyNot written yet.