Control surface, not agent brain
For each environment, T3 Code authoritatively coordinates repository and provider execution without replacing a provider's model loop, native context, or authentication.
What this chapter resolves
- Draw the ownership boundary between client, T3 server, provider adapter, and provider CLI.
- Explain why multi-harness support is an adapter problem rather than a shared agent implementation.
- Identify the server services that make T3 Code more than a terminal wrapper.
The product’s cleanest architectural sentence is: each T3 environment is a server-authoritative control plane around replaceable coding-agent runtimes.
Clients are thin with respect to trusted workspace execution—not small programs.
They express intent and render projected state, while Electron and mobile still own
substantial native behavior. For the selected environment, the server owns
repository-facing orchestration. A provider runtime owns its model/tool loop. The
adapter and ProviderService divide live protocol behavior from persisted session
binding and recovery state.
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 (5 built-in drivers) │
│ checkpointing, VCS, terminals, filesystem │
└──────────────────┬─────────────────────────────┘
│ per-driver transport
┌──────────────────▼─────────────────────────────┐
│ Agent CLIs: Codex, Claude, Cursor, Grok, │
│ OpenCode │
└────────────────────────────────────────────────┘Six ownership layers
| Owner | Owns | Does not own |
|---|---|---|
| T3 domain | durable project/thread history, lifecycle intent, messages, activities, proposed plans, checkpoint metadata, query projections | live process objects, provider-native memory, every transient UI signal |
| ProviderService + session directory | instance routing and persisted binding, cwd, model/runtime payload, continuation cursor, status | native cursor meaning or provider reasoning |
| Provider adapter | live process/session object, native protocol, cursor interpretation, approval/input mapping, canonical event translation | cross-provider product policy or durable projections |
| Provider runtime | model/tool loop, native conversation/context, compaction, provider account authentication | T3’s multi-environment UI, checkpoint model, or product lifecycle |
| Client shell | navigation, presentation, caches, device settings; mobile drafts and durable outbox; Electron OS integrations | authoritative environment thread history or remote workspace execution |
| Selected environment | workspace files, provider processes, Git, PTYs, server SQLite/files/settings/secrets | other environments supervised by the same client |
A normalized, deliberately bounded seam
export interface ProviderAdapterShape<TError> {
/**
* Provider kind implemented by this adapter.
*/
readonly provider: ProviderDriverKind;
readonly capabilities: ProviderAdapterCapabilities;
/**
* Start a provider-backed session.
*/
readonly startSession: (
input: ProviderSessionStartInput,
) => Effect.Effect<ProviderSession, TError>;
/**
* Send a turn to an active provider session.
*/
readonly sendTurn: (
input: ProviderSendTurnInput,
) => Effect.Effect<ProviderTurnStartResult, TError>;
/**
* Interrupt an active turn.
*/
readonly interruptTurn: (threadId: ThreadId, turnId?: TurnId) => Effect.Effect<void, TError>; /**
* Stop all sessions owned by this adapter.
*/
readonly stopAll: () => Effect.Effect<void, TError>;
/**
* Canonical runtime event stream emitted by this adapter.
*/
readonly streamEvents: Stream.Stream<ProviderRuntimeEvent>;The contract is not a promise of feature equality. Its lifecycle, interaction, history, rollback, feedback, and event-stream surface is deliberately bounded, but not tiny. At this revision, the separate common capability object only describes session model switching; other semantic differences are expressed by adapter behavior, errors, and provider presentation. Uniform orchestration is achieved by normalizing at the boundary, not by pretending the providers are identical.
Why the server is the real product kernel
const AuthLayerLive = EnvironmentAuth.layer.pipe(
Layer.provideMerge(PersistenceLayerLive),
Layer.provide(ServerSecretStore.layer),
);
const CloudManagedEndpointRuntimeLive = Layer.mergeAll(
RelayClientLive,
CloudManagedEndpointRuntime.layer.pipe(
Layer.provide(ServerSecretStore.layer),
Layer.provide(RelayClientLive),
),
);
const ProviderRuntimeLayerLive = ProviderSessionReaperLive.pipe(
Layer.provideMerge(ProviderLayerLive),
Layer.provideMerge(OrchestrationLayerLive),
);
const RuntimeCoreDependenciesLive = ReactorLayerLive.pipe(
// Core Services
Layer.provideMerge(ServerSettingsLayerLive),
Layer.provideMerge(CheckpointingLayerLive),
Layer.provideMerge(SourceControlProviderRegistryLayerLive),
Layer.provideMerge(GitLayerLive),
Layer.provideMerge(VcsLayerLive),
Layer.provideMerge(ProviderRuntimeLayerLive),
Layer.provideMerge(Layer.mergeAll(TerminalLayerLive, PreviewLayerLive)),
Layer.provideMerge(PersistenceLayerLive),
Layer.provideMerge(Keybindings.layer),
Layer.provideMerge(ProviderRegistryLive),
// The instance registry is the new routing keystone — text generation,
// adapter lookup, and runtime ingestion all resolve `ProviderInstanceId`
// through this layer. Built-in drivers come from `BUILT_IN_DRIVERS`;
// `providerInstances` hydration merges `settings.providers.<kind>`
// with explicit `providerInstances` entries on boot.
Layer.provideMerge(ProviderInstanceRegistryHydrationLive),
// Shared native/canonical NDJSON writers used by both the per-instance
// drivers (native stream, written from inside each `<X>Adapter`) and
// `ProviderService` (canonical stream, written after event normalization).
// Provided once at the runtime level so every consumer sees the same
// logger instances.
Layer.provideMerge(ProviderEventLoggers.layer),
// `OpenCodeDriver.create()` yields `OpenCodeRuntime`; previously the old
// `ProviderRegistryLive` pulled `OpenCodeRuntimeLive` in for itself, but
// the rewritten registry reads snapshots off the instance registry and
// no longer transitively provides it. Exposing it at the runtime level
// keeps a single Live for all opencode consumers.
Layer.provideMerge(OpenCodeRuntime.OpenCodeRuntimeLive),
Layer.provideMerge(WorkspaceLayerLive),
Layer.provideMerge(ProjectFaviconResolverLayerLive),
Layer.provideMerge(RepositoryIdentityResolver.layer),
Layer.provideMerge(ServerEnvironment.layer),
Layer.provideMerge(AuthLayerLive),
Layer.provideMerge(ServerSecretStore.layer),
Layer.provideMerge(
Layer.mergeAll(
CloudCliTokenManager.layer.pipe(
Layer.provide(ServerSecretStore.layer),
Layer.provide(ExternalLauncher.layer),
),
CloudManagedEndpointRuntimeLive,
),
),
);Keep workspace authority server-side
A phone can supervise the same environment as desktop because remote repository, provider, Git, and PTY operations remain behind that environment's server.
Keep provider complexity at the edge
The orchestration core speaks canonical commands and events; Codex/Claude/ACP/OpenCode quirks remain inside adapters.
The server becomes a large trust boundary
Remote authentication and per-method scopes matter because one socket fronts shells, files, Git, and autonomous coding agents.
Capabilities can diverge
A common product model still needs explicit answers for provider-only features, resume semantics, plan events, usage, and approvals.