Runtime Architecture
September 8, 2026 · View on GitHub
Documentation index · CodexKit
Goals
CodexKit is an embedded agent runtime for iOS and macOS apps that can:
- authenticate with ChatGPT
- restore auth state securely
- create and resume agent threads
- stream agent output into app UI
- register host-owned tools
- require explicit approval for sensitive tool execution
The SDK is tool-agnostic. Host apps decide which tools exist.
Package Structure
CodexKit
Owns the core runtime:
AgentRuntimeAgentRuntime.ConfigurationAgentEventAgentThread,AgentThreadConfiguration,AgentTurn,AgentMessage- ChatGPT auth/session primitives
- backend transport protocols and
CodexResponsesBackend - tool types and approval types
CodexKitUI
Owns optional SwiftUI-friendly helpers:
ApprovalInboxDeviceCodePromptCoordinatorAgentRuntimeStore
This target is optional and does not add any concrete tools.
DemoApp
Owns example-app-only pieces outside the package:
- checked-in Xcode app project
- demo runtime factory
- demo view model and SwiftUI screen
Test-only mock auth and backend fixtures live under Tests/ support code rather than in a package product.
Runtime Boundary
AgentRuntime
AgentRuntime is the primary public entry point.
It owns:
- thread creation and resume
- per-thread model and reasoning defaults
- message send
- event streaming, including provider progress and account-limit updates
- model discovery through capable backends
- active-turn steering and interruption
- tool invocation routing
- approval pauses and resume
- persisted runtime state
It is initialized from AgentRuntime.Configuration, which contains:
authProvidersecureStorebackendapprovalPresenterstateStore- optional
toolsandmaximumParallelToolCalls(defaults to four)
The old dependency-bag setup is intentionally replaced by this single configuration object.
Backend configuration supplies default execution settings, while AgentThreadConfiguration lets a thread carry its own model and reasoning effort. Future turns resolve those values from the thread first, then fall back to backend defaults.
Internal runtime plumbing
These concepts still exist internally, but are no longer meant to be first-class app-facing setup types:
- tool registry
- approval coordinator
- turn-session continuation plumbing
Apps interact with them indirectly through AgentRuntime.
Public API Surface
Core models
public struct ChatGPTSession
public struct AgentThread
public struct AgentTurn
public struct AgentMessage
public struct AgentTurnSummary
public struct AgentRuntimeError
public enum AgentEvent
Host extension points
public protocol AgentBackend
public protocol ApprovalPresenting
public protocol RuntimeStateStoring
public protocol ToolExecuting
Authentication and session persistence intentionally use the concrete
ChatGPTAuthProvider and KeychainSessionSecureStore types. Custom backends
return the sendable AgentTurnStream value from beginTurn(...).
Runtime and transport types
public actor AgentRuntime
public struct AgentRuntime.Configuration
public struct AgentRuntime.ToolRegistration
public struct AgentTurnStream
public struct AgentRuntimeObservationPublisher
public actor ChatGPTSessionManager
public actor CodexResponsesBackend
public struct CodexResponsesBackendConfiguration
public struct ChatGPTOAuthConfiguration
public final class ChatGPTOAuthProvider
public final class ChatGPTDeviceCodeAuthProvider
public final class KeychainSessionSecureStore
public actor InMemoryRuntimeStateStore
public actor FileRuntimeStateStore
Tool and approval types
public struct ToolDefinition
public struct ToolInvocation
public struct ToolResultEnvelope
public struct ToolExecutionContext
public struct AnyToolExecutor
public struct ApprovalRequest
public struct ApprovalResolution
public enum ApprovalDecision
Optional UI helpers
public final class ApprovalInbox
public final class DeviceCodePromptCoordinator
public final class AgentRuntimeStore
Event Model
The runtime intentionally keeps a smaller event vocabulary than upstream Codex.
Thread lifecycle:
threadStartedthreadStatusChanged
Turn lifecycle:
turnStartedturnCompletedturnInterruptedturnFailed
Streaming:
assistantMessageDeltamessageCommitted(including optional message phase)progress(message lifecycle, reasoning summaries, and web-search activity)rateLimitsUpdated(latest account allowance snapshots, separate from turn token usage)
Tooling:
toolCallStartedtoolCallFinished
Approvals:
approvalRequestedapprovalResolved
Tool Model
The SDK defines how tools work, not which tools exist.
Each tool provides:
- stable name
- description
- JSON input schema
- approval policy
- optional approval copy
supportsParallelExecution(defaults to false)- executor
Registration happens either:
- up front in
AgentRuntime.Configuration.tools - later with
AgentRuntime.registerToolorAgentRuntime.replaceTool
Execution flow:
- backend emits a tool call request
- runtime finds the registered tool
- runtime requests approval when required
- runtime executes the host-provided tool
- runtime returns a normalized
ToolResultEnvelope - backend continues the active turn
Consecutive independent calls from the same batch may overlap when their tool definitions opt in. Serial tools and tools requiring approval form barriers; skill tool-policy constraints preserve serial execution. Results retain provider order even when lifecycle events finish out of order.
Turn control and discovery
Applications can supply AgentSessionProviding for host-managed credentials. AgentExecution handles add explicit ownership of persistent and ephemeral work, and observation publishers offer bounded async sequences alongside Combine. See SDK integration for these interfaces and typed HTTP retry metadata.
One persistent turn may run on a thread at a time. Manual compaction and activation share the same operation reservation: conflicting calls report thread_busy. Compaction also rejects a changed context before installing its marker or saving a result. Deactivation during an operation is deferred until it finishes; runtime restoration reports runtime_busy while an operation or write is pending. Hosts capture its ID from turnStarted or activeTurnID(in:), then use steer(_:images:in:expectedTurnID:) to queue input for the next model request, or interrupt(in:expectedTurnID:) to cancel it. Interruption records an interrupted turn, returns the thread to idle, clears pending waits, and ends the stream with CancellationError. Ephemeral turns remain independent.
listModels(policy:) delegates to AgentBackendModelDiscovering when supported and otherwise returns bundled metadata. The built-in Responses backend caches account catalogs in memory, supports ETag refresh, and exposes stale/bundled fallback provenance. rateLimits() returns the latest observed account limits without issuing a quota request. Typed identifiers include CodexModel.gpt6Astra; strings remain open to future server-provided identifiers.
The built-in Responses backend requires response.completed before successful completion. Premature stream endings enter the existing safe-retry path only before any visible output or tool effects. The runtime separately requires a valid turnCompleted from custom backends.
HTTP events, backend events, and public runtime events use bounded asynchronous queues. Producers await capacity, while a small reserved tail preserves terminal lifecycle events. Runtime tool/time budgets and backend model-pass/response budgets bound continued work. See messaging limits for defaults and ownership semantics, and the performance verification for measured parser results and capacity coverage.
See Runtime progress, tools, and turn control for examples and compatibility details.
Recommended iOS Integration Path
For a normal production iOS app, the recommended live stack is:
ChatGPTAuthProviderconfigured for device-code authenticationKeychainSessionSecureStoreCodexResponsesBackendSQLiteRuntimeStateStorefromCodexKitSQLiteApprovalInboxandDeviceCodePromptCoordinatorfromCodexKitUIAgentRuntimeStorewhen the app wants a ready-made SwiftUI-friendly state model
Browser OAuth remains available through ChatGPTOAuthProvider, but it is now the advanced path rather than the primary one.
Demo App
The demo app validates the intended setup:
- live ChatGPT sign-in
- persisted auth/session state
- thread creation and resume
- streamed output
- app-defined tool registration
- approval-gated tool execution
- account model refresh and reported usage allowances
- live progress and message phases
- parallel sample lookups, adding input to a running chat turn, and stopping it
Follow the demo walkthrough to exercise these paths.
The demo target should be treated as example integration code, not as required plumbing for host apps.
SQLite is the default scalable persistence choice; Realm is also available for apps using that adapter. FileRuntimeStateStore remains useful for small snapshots and simple integrations, but rewrites the full snapshot for incremental mutations.
Plain and structured streams share one internal prepared-turn context and lifecycle consumer. Structured validation is an additional event handler. Each turn snapshots its tool registrations before opening the backend request; subsequent registerTool/replaceTool calls affect future turns. The snapshot binds approval policy, parallel-execution policy, and executor together.