For maintainers. Using T3 Code? See docs/user.
A provider is the agent runtime that does the actual work. T3 Code supports several, and the orchestration layer does not know which one is behind a thread.
builtInDrivers.ts exports BUILT_IN_DRIVERS with five entries:
| Driver kind | Driver source |
|---|---|
codex |
Drivers/CodexDriver.ts |
claudeAgent |
Drivers/ClaudeDriver.ts |
cursor |
Drivers/CursorDriver.ts |
grok |
Drivers/GrokDriver.ts |
opencode |
Drivers/OpenCodeDriver.ts |
Each driver declares its driverKind, a configSchema, and a create function that builds an
adapter in a child scope. Adapter implementations live beside them in
apps/server/src/provider/Layers/ (CodexAdapter.ts, ClaudeAdapter.ts, and so on) and conform to
ProviderAdapter.ts. Read the driver plus its adapter to see how a specific agent's
transport, config, and event shapes are mapped.
Two registries separate configuration from live processes:
ProviderInstanceRegistrykeys configured instances byProviderInstanceId. Creating one looks up the driver bydriverKind, decodesentry.configwith that driver's schema, opens a child scope, and callsdriver.create.ProviderAdapterRegistryresolves an instance ID to its live adapter viagetByInstance.
ProviderService sits on top. It combines the adapter registry with the provider session
directory to route session and turn operations for a thread, so callers name a thread, not an agent.
Adding a driver means writing the driver plus adapter and adding it to BUILT_IN_DRIVERS. No
orchestration, contract, or client change is required for the common case.
Clients never call a provider directly. They dispatch orchestration commands over the RPC method
orchestration.dispatchCommand, defined with the rest of the orchestration surface in
orchestration.ts. The client-dispatchable provider-facing commands are
thread.turn.start, thread.turn.interrupt, thread.approval.respond,
thread.user-input.respond, thread.checkpoint.revert, and thread.session.stop, plus the mode
setters thread.runtime-mode.set and thread.interaction-mode.set.
The engine persists an event for the command, and a server-side reactor performs the provider call.
Provider output comes back as internal commands such as thread.message.assistant.delta and
thread.session.set, which clients observe through orchestration.subscribeThread. See
overview.md for the command/event loop.
Provider work flows through three queue-backed workers. All three are built with
makeDrainableWorker from DrainableWorker.ts and expose drain for deterministic test
synchronization.
ProviderRuntimeIngestionconsumes provider runtime streams and emits orchestration commands.ProviderCommandReactorreacts to orchestration intent events and dispatches provider calls.CheckpointReactorcaptures workspace checkpoints on turn start and completion, and performs reverts.
A thread in buffered assistant delivery mode accumulates assistant text instead of streaming each
delta. The buffer is not held until turn completion. In ProviderRuntimeIngestion,
MAX_BUFFERED_ASSISTANT_CHARS is 24,000: the append that would exceed it invalidates the buffer and
spills the whole accumulated text as one delta. The buffer also flushes at interaction boundaries,
when a request opens (approval) or user input is requested, via
flushBufferedAssistantMessagesForTurn.