Skip to main content
Runtime modes control how T3 Code agents interact with your system, balancing autonomy with safety through approval policies and sandbox restrictions.

Overview

T3 Code provides two primary runtime modes accessible via the chat toolbar:

Full Access

Default mode with maximum autonomy. Sessions start with approvalPolicy: never and sandboxMode: danger-full-access.

Supervised

Safety-first mode with user approval required. Sessions start with approvalPolicy: on-request and sandboxMode: workspace-write, prompting for command/file approvals.

Runtime Mode Types

The RuntimeMode type defines the high-level security posture:
packages/contracts/src/orchestration.ts
export const RuntimeMode = Schema.Literals([
  "approval-required",
  "full-access"
]);

export const DEFAULT_RUNTIME_MODE: RuntimeMode = "full-access";
Runtime modes are set at session creation and affect the entire conversation lifecycle. They map to specific Codex approval policies and sandbox modes.

Approval Policies

Approval policies determine when the agent must request user permission:
export const ProviderApprovalPolicy = Schema.Literals([
  "untrusted",    // Approve everything (not yet implemented)
  "on-failure",   // Approve after failed attempts
  "on-request",   // Approve when agent requests permission
  "never",        // Never require approval
]);

Approval Policy Mapping

T3 Code maps runtime modes to Codex approval policies:
apps/server/src/codexAppServerManager.ts
function mapCodexRuntimeMode(runtimeMode: RuntimeMode): {
  readonly approvalPolicy: "on-request" | "never";
  readonly sandbox: "workspace-write" | "danger-full-access";
} {
  if (runtimeMode === "approval-required") {
    return {
      approvalPolicy: "on-request",
      sandbox: "workspace-write",
    };
  }

  return {
    approvalPolicy: "never",
    sandbox: "danger-full-access",
  };
}

Sandbox Modes

Sandbox modes restrict file system and system access:
export const ProviderSandboxMode = Schema.Literals([
  "read-only",            // Read files only, no modifications
  "workspace-write",      // Write within workspace, read elsewhere
  "danger-full-access",   // Unrestricted file system access
]);
Full Access Mode grants unrestricted file system access and never prompts for approval. Only use in trusted environments with code you’re comfortable executing.

Approval Request Types

When approval is required, the agent requests permission for specific operations:
export const ProviderRequestKind = Schema.Literals([
  "command",      // Shell command execution
  "file-read",    // Reading file contents
  "file-change",  // Writing or modifying files
]);

Approval Request Flow

1

Agent Requests Approval

The agent sends an approval request via JSON-RPC (e.g., item/commandExecution/requestApproval)
2

Server Generates Request ID

The server creates a unique ApprovalRequestId and tracks the pending request
3

Client Displays Prompt

The web UI shows the approval dialog with request details
4

User Responds

User chooses: accept, acceptForSession, decline, or cancel
5

Server Forwards Decision

The server sends the decision back to the provider via JSON-RPC response

Approval Decisions

Users can respond to approval requests with four decision types:
export const ProviderApprovalDecision = Schema.Literals([
  "accept",              // Approve this one request
  "acceptForSession",    // Approve this and similar requests for session
  "decline",             // Deny this request
  "cancel",              // Cancel the entire operation
]);
await providerService.respondToRequest({
  threadId,
  requestId,
  decision: "accept",
});

User Input Requests

Beyond approval requests, agents can request structured user input:
export const ProviderUserInputAnswers = Schema.Record(
  Schema.String,
  Schema.Unknown
);

// Example: Plan mode questions
const answers = {
  "question-1": "Option A",
  "question-2": ["Choice 1", "Choice 2"],
};

await providerService.respondToUserInput({
  threadId,
  requestId,
  answers,
});
User input requests are separate from approval requests and are primarily used in Plan Mode for collaborative planning conversations.

Runtime Mode Changes

Runtime modes can be changed during a thread’s lifetime:
// Dispatch command to change runtime mode
await orchestration.dispatchCommand({
  type: "thread.runtime-mode.set",
  commandId: CommandId.makeUnsafe(randomUUID()),
  threadId,
  runtimeMode: "approval-required",
  createdAt: new IsoDateTime(),
});
Session Restart Required: Changing runtime mode requires stopping and restarting the provider session with new settings. Existing sessions retain their original runtime mode.

Session Status and Runtime Mode

The orchestration session tracks the active runtime mode:
export const OrchestrationSession = Schema.Struct({
  threadId: ThreadId,
  status: OrchestrationSessionStatus,
  providerName: Schema.NullOr(TrimmedNonEmptyString),
  runtimeMode: RuntimeMode.pipe(
    Schema.withDecodingDefault(() => DEFAULT_RUNTIME_MODE)
  ),
  activeTurnId: Schema.NullOr(TurnId),
  lastError: Schema.NullOr(TrimmedNonEmptyString),
  updatedAt: IsoDateTime,
});

Pending Approval Tracking

The server tracks pending approvals in memory:
apps/server/src/codexAppServerManager.ts
interface PendingApprovalRequest {
  requestId: ApprovalRequestId;
  jsonRpcId: string | number;
  method:
    | "item/commandExecution/requestApproval"
    | "item/fileChange/requestApproval"
    | "item/fileRead/requestApproval";
  requestKind: ProviderRequestKind;
  threadId: ThreadId;
  turnId?: TurnId;
  itemId?: ProviderItemId;
}

interface CodexSessionContext {
  // ...
  pendingApprovals: Map<ApprovalRequestId, PendingApprovalRequest>;
  pendingUserInputs: Map<ApprovalRequestId, PendingUserInputRequest>;
  // ...
}

Service Tier

Independent of runtime mode, you can specify compute service tier:
export const ProviderServiceTier = Schema.Literals(["fast", "flex"]);

// Start session with specific tier
const session = await providerService.startSession(threadId, {
  threadId,
  runtimeMode: "full-access",
  serviceTier: "fast",
});

Interaction Modes

Interaction modes control conversation behavior:
export const ProviderInteractionMode = Schema.Literals([
  "default",  // Standard execution mode
  "plan",     // Collaborative planning mode
]);

export const DEFAULT_PROVIDER_INTERACTION_MODE = "default";
Standard execution mode where the agent directly implements requests.
  • Minimal questions, bias toward action
  • Direct execution of user requests
  • request_user_input tool unavailable
Collaborative planning mode for refining specifications before implementation.
  • Three-phase conversation: environment grounding, intent chat, implementation chat
  • request_user_input tool available for structured questions
  • Non-mutating exploration allowed (read files, run tests)
  • Mutating actions blocked until plan finalized
  • Outputs <proposed_plan> blocks when ready
Interaction modes are orthogonal to runtime modes. You can use Plan Mode with either full-access or approval-required runtime mode.

Best Practices

Start Supervised

Use approval-required for unfamiliar codebases or destructive operations

Plan Before Execute

Use Plan Mode for complex changes to validate approach before implementation

Session-Scoped Approvals

Use acceptForSession for repetitive safe operations like test runs

Review Declined Actions

Check activity logs to understand why declined operations were attempted

Security Considerations

Full Access Mode Risks:
  • Unrestricted file system access
  • Arbitrary command execution
  • No approval prompts or safety nets
  • Suitable only for trusted environments
Supervised Mode Limitations:
  • workspace-write restricts writes to workspace directory
  • Read access still available outside workspace
  • Agent cannot execute commands without approval
  • May require more user interaction

Next Steps

Sessions

Learn about session lifecycle and management

Providers

Understand provider adapter architecture

Architecture

Explore the overall system design