For maintainers. Using T3 Code? See docs/user.
T3 Code is a server runtime that owns agent sessions, workspaces, and version control, plus clients (web, desktop, mobile) that talk to it over one authenticated Effect RPC WebSocket. The server is the execution boundary: every provider process, terminal, git operation, and filesystem read happens there, never in the client.
┌────────────────────────────────────────────────┐
│ Clients: apps/web, apps/desktop, apps/mobile │
│ shared runtime: packages/client-runtime │
│ connection supervisor, RPC session, Atom state│
└──────────────────┬─────────────────────────────┘
│ Effect RPC over WebSocket (/ws)
│ contract: packages/contracts
┌──────────────────▼─────────────────────────────┐
│ apps/server │
│ orchestration engine (event-sourced) │
│ provider driver registry (6 built-in drivers) │
│ checkpointing, VCS, terminals, filesystem │
└──────────────────┬─────────────────────────────┘
│ per-driver transport
┌──────────────────▼─────────────────────────────┐
│ Agent CLIs: Codex, Claude, Cursor, Grok, │
│ OpenCode, Antigravity │
└────────────────────────────────────────────────┘
The client/server contract is an Effect RPC group, not a hand-rolled push protocol. rpc.ts
declares WS_METHODS and assembles WsRpcGroup; each member is either unary or a server stream
(stream: true). Streaming members such as orchestration.subscribeShell,
orchestration.subscribeThread, subscribeServerConfig, and terminal.attach replace what used to
be a broadcast push bus: a client subscribes to what it needs and the server pushes only on that
subscription.
ws.ts serves the group. websocketRpcRouteLayer mounts GET /ws, authenticates the upgrade
through EnvironmentAuth.authenticateWebSocketUpgrade, then hands the socket to
RpcServer.toHttpEffectWebsocket. Authorization is per method: RPC_REQUIRED_SCOPE maps each method
to a scope, and authorizeEffect/authorizeStream enforce it. Holding a valid socket is not
authorization to call everything on it. See environment-auth.md.
On the client, session.ts opens the socket and builds the typed client.
RpcSessionFactory is the service; a session exposes client, initialConfig, ready, probe,
and closed. It performs one attempt and does not retry. Retry, backoff, and offline policy belong
to the connection supervisor.
packages/client-runtime holds every non-visual client concern: connection lifecycle,
authentication, RPC, cached environment data, and domain state as Atom factories. Web and mobile
compose it the same way (apps/web/src/connection/runtime.ts and
apps/mobile/src/connection/runtime.ts mirror each other, differing only in platform-specific
background-activity layers) and differ beyond that only in the platform layer they supply and the
UI they build on top. React components never construct transports, retry loops,
or RPC clients. See connection-runtime.md.
The server does not mutate app state directly. Clients dispatch typed commands; the engine turns them into persisted events; projections derive the read model.
OrchestrationEngine.ts serializes this. dispatch offers a CommandEnvelope onto
commandQueue and awaits its result; a single worker fiber takes envelopes one at a time, so command
processing is totally ordered. For each envelope processEnvelope:
- checks the durable command receipt, making retries idempotent;
- runs
decideOrchestrationCommand(decider.ts) to produce events from command plus current state, pure and side-effect free; - inside one SQL transaction, appends events to the event store, applies them to the in-memory read
model via
projector.ts, projects them into persisted tables, and writes the accepted receipt; - after commit, swaps in the new read model, cleans up attachments, and publishes committed events to subscribers. Attachment cleanup failures are logged and do not reject committed commands.
Because persistence and projection share a transaction, the read model cannot durably disagree with the event log. On dispatch failure the engine rereads persisted events past the starting sequence and reconciles.
Command and event names live in orchestration.ts. Some commands are client
dispatchable (thread.create, thread.turn.start, thread.approval.respond); others are internal
and produced only by server-side reactors (thread.message.assistant.delta,
thread.turn.diff.complete).
A turn is complete when its session leaves running status, projected by
settledTurnStateForSessionStatus in projector.ts. Checkpoint work settling later
does not define turn end.
Thread settlement is server-owned. Each server's own settings control PR and inactivity
settlement. Those keys are user preferences, so clients write them to every shared-settings sync
target (SHARED_SERVER_SETTING_KEYS in packages/client-runtime/src/state/sharedSettings.ts) and
warn when another target drifts. A target must have an active connection and advertise the
threadAutoSettlement capability, which signals that the server can hold every shared key.
ThreadSettlementReactor checks threads at startup, when those settings change, and
once per minute, including when no client is connected. It dispatches the guarded internal
thread.auto-settle command, which uses the existing settlement event lifecycle. Automatic
settlement excludes live background work and requires a comparable PR timestamp for immediate PR
settlement. The command carries the latest activity timestamp and rejects any later event for its
thread after the reactor's snapshot.
Clients render the persisted settlement state and do not derive settlement from PR or inactivity
state. A committed thread.settled event also lets ProviderCommandReactor stop an idle provider
session.
Follow-up work runs asynchronously in queue-backed workers built on DrainableWorker:
ProviderRuntimeIngestion normalizes provider runtime streams into orchestration commands,
ProviderCommandReactor dispatches provider calls in response to intent events,
CheckpointReactor captures and reverts workspace checkpoints, and
ThreadSettlementReactor evaluates server-owned automatic settlement rules.
DrainableWorker pairs a transactional queue with a transactional count of outstanding items.
enqueue atomically offers and increments; processing always decrements. drain retries until the
count reaches zero, so a test can await "queue empty and current item finished" instead of sleeping.
Each of these four services exposes drain for exactly this.
Runtime receipts are a test-only mechanism. RuntimeReceiptBusLive in
RuntimeReceiptBus.ts publishes nothing; only the test layer is PubSub-backed. Do not
build production behavior on receipts.
Six drivers ship built in, registered in builtInDrivers.ts as BUILT_IN_DRIVERS:
Codex, Claude, Cursor, Grok, OpenCode, and Antigravity. A driver declares its kind and config schema and creates a
scoped adapter; ProviderInstanceRegistry owns live instances and ProviderAdapterRegistry resolves
an instance to its adapter, so ProviderService routes session and turn operations without knowing
which agent is behind them. See providers.md.
Each turn is bracketed by workspace checkpoints so diffs and reverts are exact. CheckpointStore
captures state as hidden Git refs through the VCS driver's checkpoint operations;
CheckpointDiffQuery answers turn and full-thread diff requests; CheckpointReactor coordinates
baseline capture, completed-turn capture, diff projection, and reverting both the workspace and the
provider conversation. The storage contract is VcsCheckpointOps in
VcsDriver.ts, implemented for Git in the same directory.
serverRuntimeStartup.ts runs a fixed lifecycle: start keybindings, settings, and
reactors; publish welcome; signal command readiness (logged as Accepting commands); wait for the
HTTP listener via markHttpListening; publish ready; fork the heartbeat; then either print headless
output or open the browser. Command readiness precedes the listener, so a socket that opens can
already dispatch.