On this page
AgentRoomImplementation

How AgentRoom is put together

The boundaries that hold AgentRoom together. What the backend owns, what a client is allowed to ask for, how workspace access is bounded, and why a thread outlives the process that created it.

AgentRoom is a backend on a Mac with two Apple clients talking to it. Almost every design decision in it comes from one rule: the backend owns the truth, and a client asks for things rather than doing them.

This doc covers where that line sits. If you have not read what AgentRoom is, start there.

What the backend owns

The backend is the source of truth for registered workspaces, sessions, turns, messages, metrics, recent events, and audit. Clients render that state. They do not keep a competing copy of it and they do not reconstruct it from anywhere else.

One consequence shows up immediately in the client design: the list of agents a client can offer you comes from the backend, not from a list compiled into the app. Add support for an agent on the backend and it becomes selectable in the apps without shipping the apps again.

What a client can and cannot do

Clients create sessions and send turns. They do not launch agent processes, run shell commands, or read provider credentials. That boundary is the reason the API looks the way it does.

Git is the clearest example. A client cannot send a Git command string. It calls fixed operations: status, branches, staging, discard, commit, fetch, fast-forward pull, push, and branch creation. There is no route that takes arbitrary text and hands it to Git, because such a route is a shell by another name.

File access works the same way. Reads are limited to registered folders, bounded in size, symlink-checked so a link cannot walk out of the folder, and filtered so files with secret-shaped names do not come back. Writes go through a fixed set of routes with optimistic locking, so two clients editing the same file produce a conflict rather than a silent overwrite.

The mutation routes are deliberately narrow:

OperationBound
File write and deleteOptimistic lock, size cap, registered folder only
Create folderOne level only, refuses a name already taken, no lock because it replaces nothing
RenameSame parent only, never overwrites
Move and copyStays inside one workspace, refuses a folder landing inside its own subtree
Delete folderNever the workspace root, refuses protected entries and symlinks, capped by entry count and total size

A copy is bounded by the folder caps rather than the write cap, because its bytes never cross the API. The backend does the copy locally and revalidates each source file as it goes rather than trusting a path list built moments earlier.

Threads outlive the process

A session, its turns, and its messages are written to disk on every change and read back before the API starts accepting requests. A thread survives a backend restart, a crash, and an app update.

The mechanism is worth understanding because it explains a behaviour you will eventually see. The coding agent keeps its own memory of the conversation, and AgentRoom keeps its own record. The join between the two is an identifier AgentRoom recorded when the thread was created. On startup that identifier is handed back to the agent, and the next turn continues the same native conversation.

If the agent cannot resume from that identifier, AgentRoom says so in a system message on the thread. It does not quietly start a fresh conversation under the old thread's name, and it does not try to rebuild the history by reading the agent's own transcript files. A thread that cannot be resumed tells you.

Deleting a thread is the only thing that removes its record.

Configuration has two paths, and the split is a trust boundary

Some settings come from the environment or the Mac app's Keychain storage. Others live in a settings file that the Mac app and paired clients both edit.

Which setting goes where is not a matter of convenience. Values that would amount to remote code execution are absent from the settings file by construction rather than filtered out at the edge. The path to an agent's executable is the clearest case: "run this binary" is not a preference, so it is not a remotely editable setting, and a request naming it fails the same way a typo does.

The precedence rule is short. An environment value wins over the file and locks that key, so an operator can pin any setting beyond a client's reach by setting it in the environment. Changes take effect on restart, and only the Mac can restart the backend. Nothing is reconfigured underneath a turn that is already running.

Trust and safety covers what this means in practice, including the parts that are still sharp.

Extending it

The adapter between AgentRoom and a coding agent is one interface, and beside it sits a registry that answers every question about an agent that is not the agent's own protocol: where standing instructions get delivered, who reports a turn's diff, whether workspace skills load, and how a lost child process is restored.

That registry is also the answer to which agents exist. The type the API validates against is derived from it, so registering an agent makes it valid everywhere at once rather than in the six places you would otherwise have to remember.

Next: the coding agents AgentRoom drives.