What - the sessions bounded context: the daemon's thread registry and the supervision of the session
workers that run the threads. A thread is a project dir plus a conversation id, and it runs in one long-lived
infer headless --serve worker.
Why - infer daemon owns every external connection. Clients never spawn infer headless themselves,
several clients can follow one thread, and a worker keeps its project's cwd, config and crash isolation.
How - Registry launches a worker per thread and relays frames without translating them. Client frames
go to the worker's stdin, and worker stdout lines go to the thread's clients, one frame each.
What the registry decides
Following a thread - new_session (a fresh conversation id) and resume_conversation (the given id)
make a client follow that thread, and the worker answers with MESSAGES_SNAPSHOT. A client follows one
thread at a time, because bare AG-UI events carry no thread id. The thread options on these frames
(model, mode, system_prompt, custom_instructions, sandbox_directories, max_turns) apply when
the worker launches.
Forwarded frames - user_message, interrupt, user_question_response and computer_use_control go
to the client's thread unchanged. A user_message relaunches a thread whose worker exited.
Browser commands - a browser_command line on a worker's stdout routes through the BrowserRelay
(RouteBrowser, which the daemon wires to the browser context's extension relay) and the browser_result carrying the command's id goes
back to that worker's stdin, so the worker's Browser tools resolve without binding a port. Commands serialize
across threads, because one browser serves them, and the thread's clients never see the frames.
Panel requests - list_*, select_model, set_mode and tool_request run on the client's thread
when it is in the frame's project_dir, else on any live worker there, else on a fresh idle one. The reply
(conversations, models, tool_result, ...) goes back to the requester only.
Approvals - every CUSTOM approval_request reaches the thread's clients. The first approval_response
for a tool_call_id goes to the worker, and the other clients get CUSTOM approval_resolved.
Crashes and idling - a worker that exits mid-turn ends its open run with a synthesized RUN_ERROR. A
worker nobody follows is stopped after the idle timeout, and daemon shutdown stops every worker.
How it plugs in
domain/ holds the ports: Client (a connection frames are delivered to), Worker, LaunchWorker and
BrowserRelay.
Registry.Handle and Registry.Detach are what a driving adapter calls, per frame and on disconnect. This
context imports no protocol: cmd/daemon puts the registry behind the AG-UI binding's handler, and each
connection is a Client.
The Telegram channel becomes a driving adapter too, with one Client per chat speaking the same ipc
frames.
infrastructure.LaunchWorker is the worker adapter. It runs this binary as
headless --serve --require-approval in the project dir, with the thread options as flags and INFER_
env overrides. It stops the worker with an interrupt frame and stdin EOF, which works on every platform, so
the worker's gateway, MCP servers and containers shut down. The worker logs JSON to stderr instead of keeping
a log file of its own, and the daemon collects its stderr into the daemon log with the thread's
project_dir, conversation_id and worker_pid.
cmd/daemon wires the registry behind the binding when browser_use is enabled with the extension
backend.
type Registry struct {
// contains filtered or unexported fields
}
Registry is the daemon's thread registry. It launches one worker per thread,
relays client frames to worker stdin and worker lines to the thread's
clients, lets the first answer to an approval win, ends a run a crashed
worker left open with RUN_ERROR, routes worker browser_command frames to the
connected browser extension, and stops idle workers.
Handle routes one client frame. new_session and resume_conversation make
the client follow that thread, panel requests run on a worker in their
project dir, and approval_response answers the approval it names once.
RouteBrowser wires the relay worker browser_command frames take to the
extension connection. Call it once before any worker launches, which is what
lets pump read the field without the registry lock.