> ## Documentation Index
> Fetch the complete documentation index at: https://mintlify.com/pingdotgg/t3code/llms.txt
> Use this file to discover all available pages before exploring further.

# Providers

> Understanding the provider adapter architecture and extensibility model

Providers are the bridge between T3 Code's orchestration layer and external agent systems. The provider architecture uses a clean adapter pattern to enable multi-provider support while keeping provider-specific logic isolated.

## Provider Architecture

The provider layer follows a hierarchical service architecture:

```
┌─────────────────────────────────────────┐
│        ProviderService                  │
│  (Cross-provider facade)                │
└──────────────┬──────────────────────────┘
               │
┌──────────────▼──────────────────────────┐
│    ProviderAdapterRegistry              │
│  (Resolves provider → adapter)          │
└──────────────┬──────────────────────────┘
               │
         ┌─────┴─────┐
         │           │
┌────────▼─────┐ ┌───▼──────────┐
│ CodexAdapter │ │ Future       │
│              │ │ Adapters     │
└──────────────┘ └──────────────┘
```

<Note>
  T3 Code is currently **Codex-first**, with support for additional providers (like Claude Code) reserved in the contracts and architecture.
</Note>

## Provider Service

The `ProviderService` acts as the unified facade for all provider operations:

```typescript apps/server/src/provider/Services/ProviderService.ts theme={null}
export interface ProviderServiceShape {
  // Session lifecycle
  readonly startSession: (
    threadId: ThreadId,
    input: ProviderSessionStartInput,
  ) => Effect.Effect<ProviderSession, ProviderServiceError>;
  
  readonly stopSession: (
    input: ProviderStopSessionInput,
  ) => Effect.Effect<void, ProviderServiceError>;
  
  // Turn execution
  readonly sendTurn: (
    input: ProviderSendTurnInput,
  ) => Effect.Effect<ProviderTurnStartResult, ProviderServiceError>;
  
  readonly interruptTurn: (
    input: ProviderInterruptTurnInput,
  ) => Effect.Effect<void, ProviderServiceError>;
  
  // Interactive requests
  readonly respondToRequest: (
    input: ProviderRespondToRequestInput,
  ) => Effect.Effect<void, ProviderServiceError>;
  
  readonly respondToUserInput: (
    input: ProviderRespondToUserInputInput,
  ) => Effect.Effect<void, ProviderServiceError>;
  
  // Conversation management
  readonly rollbackConversation: (input: {
    readonly threadId: ThreadId;
    readonly numTurns: number;
  }) => Effect.Effect<void, ProviderServiceError>;
  
  // Metadata
  readonly listSessions: () => Effect.Effect<ReadonlyArray<ProviderSession>>;
  readonly getCapabilities: (
    provider: ProviderKind,
  ) => Effect.Effect<ProviderAdapterCapabilities, ProviderServiceError>;
  
  // Event stream
  readonly streamEvents: Stream.Stream<ProviderRuntimeEvent>;
}
```

<Note>
  `ProviderService` resolves provider adapters through `ProviderAdapterRegistry`, routes session-scoped calls via `ProviderSessionDirectory`, and exposes one unified provider event stream to callers.
</Note>

## Provider Adapter Contract

Each provider implements the `ProviderAdapterShape` interface:

```typescript apps/server/src/provider/Services/ProviderAdapter.ts theme={null}
export interface ProviderAdapterShape<TError> {
  readonly provider: ProviderKind;
  readonly capabilities: ProviderAdapterCapabilities;
  
  // Session operations
  readonly startSession: (
    input: ProviderSessionStartInput,
  ) => Effect.Effect<ProviderSession, TError>;
  
  readonly stopSession: (
    threadId: ThreadId
  ) => Effect.Effect<void, TError>;
  
  readonly stopAll: () => Effect.Effect<void, TError>;
  
  // Turn operations
  readonly sendTurn: (
    input: ProviderSendTurnInput,
  ) => Effect.Effect<ProviderTurnStartResult, TError>;
  
  readonly interruptTurn: (
    threadId: ThreadId,
    turnId?: TurnId,
  ) => Effect.Effect<void, TError>;
  
  // Approval handling
  readonly respondToRequest: (
    threadId: ThreadId,
    requestId: ApprovalRequestId,
    decision: ProviderApprovalDecision,
  ) => Effect.Effect<void, TError>;
  
  readonly respondToUserInput: (
    threadId: ThreadId,
    requestId: ApprovalRequestId,
    answers: ProviderUserInputAnswers,
  ) => Effect.Effect<void, TError>;
  
  // Thread state
  readonly readThread: (
    threadId: ThreadId,
  ) => Effect.Effect<ProviderThreadSnapshot, TError>;
  
  readonly rollbackThread: (
    threadId: ThreadId,
    numTurns: number,
  ) => Effect.Effect<ProviderThreadSnapshot, TError>;
  
  // Session directory
  readonly listSessions: () => Effect.Effect<ReadonlyArray<ProviderSession>>;
  readonly hasSession: (threadId: ThreadId) => Effect.Effect<boolean>;
  
  // Event stream
  readonly streamEvents: Stream.Stream<ProviderRuntimeEvent>;
}
```

## Provider Capabilities

Adapters declare their capabilities through static metadata:

```typescript theme={null}
export type ProviderSessionModelSwitchMode =
  | "in-session"      // Can change model without restarting
  | "restart-session" // Must restart session to change model
  | "unsupported";    // Model switching not supported

export interface ProviderAdapterCapabilities {
  readonly sessionModelSwitch: ProviderSessionModelSwitchMode;
}
```

<Note>
  Capabilities enable T3 Code to adapt its behavior based on provider limitations. For example, the UI can prompt for session restart when changing models if the provider requires it.
</Note>

## Codex Adapter

The `CodexAdapter` implements provider support for Codex:

<Steps>
  <Step title="Process Management">
    Spawns and manages `codex app-server` child processes via `CodexAppServerManager`
  </Step>

  <Step title="JSON-RPC Communication">
    Handles JSON-RPC protocol over stdio for session and turn operations
  </Step>

  <Step title="Event Transformation">
    Transforms Codex notifications into standardized `ProviderRuntimeEvent` format
  </Step>

  <Step title="Session Directory Integration">
    Registers sessions with `ProviderSessionDirectory` for tracking and routing
  </Step>
</Steps>

### Codex-Specific Features

<Accordion title="Account Detection">
  Detects Codex account type and plan to determine model availability:

  ```typescript theme={null}
  interface CodexAccountSnapshot {
    readonly type: "apiKey" | "chatgpt" | "unknown";
    readonly planType: CodexPlanType | null;
    readonly sparkEnabled: boolean;
  }

  // Spark model restrictions based on plan
  const CODEX_SPARK_DISABLED_PLAN_TYPES = new Set<CodexPlanType>([
    "free", "go", "plus"
  ]);
  ```
</Accordion>

<Accordion title="Model Normalization">
  Normalizes model slugs and applies account-based restrictions:

  ```typescript theme={null}
  export function resolveCodexModelForAccount(
    model: string | undefined,
    account: CodexAccountSnapshot,
  ): string | undefined {
    if (model !== CODEX_SPARK_MODEL || account.sparkEnabled) {
      return model;
    }
    return CODEX_DEFAULT_MODEL; // Fallback for restricted accounts
  }
  ```
</Accordion>

<Accordion title="Collaboration Modes">
  Supports Codex-specific collaboration modes (default and plan):

  ```typescript theme={null}
  function buildCodexCollaborationMode(input: {
    readonly interactionMode?: "default" | "plan";
    readonly model?: string;
    readonly effort?: string;
  }): {
    mode: "default" | "plan";
    settings: {
      model: string;
      reasoning_effort: string;
      developer_instructions: string;
    };
  };
  ```
</Accordion>

<Accordion title="Resume Fallback Logic">
  Implements intelligent thread resume with automatic fallback:

  ```typescript theme={null}
  const RECOVERABLE_THREAD_RESUME_ERROR_SNIPPETS = [
    "not found",
    "missing thread",
    "no such thread",
    "unknown thread",
    "does not exist",
  ];
  ```
</Accordion>

## Provider Events

Providers emit events through a unified event stream:

```typescript packages/contracts/src/provider.ts theme={null}
export const ProviderEvent = Schema.Struct({
  id: EventId,
  kind: ProviderEventKind,           // "session" | "notification" | "request" | "error"
  provider: ProviderKind,             // "codex"
  threadId: ThreadId,
  createdAt: IsoDateTime,
  method: TrimmedNonEmptyStringSchema,
  message: Schema.optional(TrimmedNonEmptyStringSchema),
  turnId: Schema.optional(TurnId),
  itemId: Schema.optional(ProviderItemId),
  requestId: Schema.optional(ApprovalRequestId),
  requestKind: Schema.optional(ProviderRequestKind),
  textDelta: Schema.optional(Schema.String),
  payload: Schema.optional(Schema.Unknown),
});
```

### Event Kinds

<CardGroup cols={2}>
  <Card title="session" icon="plug">
    Lifecycle events: connecting, ready, closed
  </Card>

  <Card title="notification" icon="bell">
    Provider activity: tool calls, message deltas, turn completion
  </Card>

  <Card title="request" icon="hand">
    Approval requests: command, file-read, file-change
  </Card>

  <Card title="error" icon="triangle-exclamation">
    Error events: session errors, protocol errors
  </Card>
</CardGroup>

## Provider Session Directory

The `ProviderSessionDirectory` tracks active sessions across all providers:

```typescript theme={null}
interface ProviderSessionDirectory {
  // Register new session
  register(
    session: ProviderSession
  ): Effect.Effect<void, ProviderSessionDirectoryError>;
  
  // Update session state
  update(
    threadId: ThreadId,
    updates: Partial<ProviderSession>,
  ): Effect.Effect<void, ProviderSessionDirectoryError>;
  
  // Get session by thread ID
  get(
    threadId: ThreadId
  ): Effect.Effect<ProviderSession | undefined>;
  
  // Remove session
  remove(threadId: ThreadId): Effect.Effect<void>;
  
  // List all sessions
  list(): Effect.Effect<ReadonlyArray<ProviderSession>>;
  
  // Event stream for session changes
  streamEvents: Stream.Stream<ProviderSessionDirectoryEvent>;
}
```

<Note>
  The session directory maintains an in-memory registry of active sessions, enabling quick lookups and routing without database queries.
</Note>

## Provider Adapter Registry

The `ProviderAdapterRegistry` resolves provider kinds to concrete adapters:

```typescript theme={null}
interface ProviderAdapterRegistry {
  // Register an adapter
  register(
    adapter: ProviderAdapterShape<unknown>
  ): Effect.Effect<void>;
  
  // Get adapter by provider kind
  get(
    provider: ProviderKind
  ): Effect.Effect<ProviderAdapterShape<unknown>, ProviderServiceError>;
  
  // List all registered providers
  listProviders(): Effect.Effect<ReadonlyArray<ProviderKind>>;
}
```

## Adding New Providers

To add a new provider adapter:

<Steps>
  <Step title="Update Contracts">
    Add the provider to `ProviderKind` literal in `packages/contracts/src/orchestration.ts`:

    ```typescript theme={null}
    export const ProviderKind = Schema.Literal("codex", "claude-code");
    ```
  </Step>

  <Step title="Implement Adapter">
    Create a new adapter implementing `ProviderAdapterShape<TError>`:

    ```typescript theme={null}
    export class ClaudeCodeAdapter implements ProviderAdapterShape<ClaudeCodeError> {
      readonly provider = "claude-code";
      readonly capabilities = { sessionModelSwitch: "in-session" };
      
      // Implement all required methods...
    }
    ```
  </Step>

  <Step title="Register Adapter">
    Register the adapter with `ProviderAdapterRegistry` during server startup
  </Step>

  <Step title="Update UI">
    Add provider selection and configuration UI in the web app
  </Step>
</Steps>

## Provider Errors

Providers use typed errors for robust error handling:

```typescript apps/server/src/provider/Errors.ts theme={null}
export type ProviderServiceError =
  | ProviderValidationError
  | ProviderSessionError
  | CodexError
  | CheckpointError;

export class ProviderSessionError extends Data.TaggedError("ProviderSessionError")<{
  readonly reason: string;
}> {}

export class CodexError extends Data.TaggedError("CodexError")<{
  readonly reason: string;
  readonly cause?: unknown;
}> {}
```

<Note>
  Using Effect's typed errors enables exhaustive error handling and better debugging at the call site.
</Note>

## Event Projection

Provider events are projected into orchestration domain events:

```typescript theme={null}
ProviderRuntimeEvent
  ↓
[Server-side projection]
  ↓
OrchestrationEvent
  ↓
[WebSocket push]
  ↓
Client receives via "orchestration.domainEvent" channel
```

This projection:

1. Decouples provider protocols from client expectations
2. Enables consistent event replay and debugging
3. Supports multiple concurrent provider sessions
4. Facilitates event sourcing and audit trails

## Best Practices

<CardGroup cols={2}>
  <Card title="Isolate Provider Logic" icon="box">
    Keep provider-specific code in adapters, not in `ProviderService`
  </Card>

  <Card title="Use Typed Errors" icon="shield">
    Define specific error types for each provider to enable precise error handling
  </Card>

  <Card title="Emit Rich Events" icon="signal">
    Include sufficient context in events for debugging and replay
  </Card>

  <Card title="Handle Graceful Degradation" icon="heart-pulse">
    Implement fallback behavior for unsupported capabilities
  </Card>
</CardGroup>

## Provider Health Monitoring

The `ProviderHealth` service monitors adapter availability:

```typescript theme={null}
interface ProviderHealth {
  // Check if provider is healthy
  check(
    provider: ProviderKind
  ): Effect.Effect<ProviderHealthStatus>;
  
  // Get health status for all providers
  checkAll(): Effect.Effect<ReadonlyArray<ProviderHealthStatus>>;
}

interface ProviderHealthStatus {
  provider: ProviderKind;
  healthy: boolean;
  message?: string;
  lastChecked: IsoDateTime;
}
```

## Next Steps

<CardGroup cols={2}>
  <Card title="Sessions" icon="plug" href="/concepts/sessions">
    Learn about session lifecycle and management
  </Card>

  <Card title="Runtime Modes" icon="shield" href="/concepts/runtime-modes">
    Understand approval policies and security controls
  </Card>

  <Card title="Architecture" icon="diagram-project" href="/concepts/architecture">
    Explore the overall system design
  </Card>

  <Card title="API Reference" icon="book" href="/api-reference">
    Browse the complete API documentation
  </Card>
</CardGroup>
