Note
This document was generated by Codex
A .NET SDK for interacting with OpenAI Codex through the Codex CLI, providing buffered turns, streamed events, resumable threads, structured output, local image input, and Microsoft Agent Framework integration.
- Buffered turns via
Thread.RunAsync - Real-time JSONL event streaming via
Thread.RunStreamedAsync - Resumable Codex threads with
StartThreadandResumeThread - Strongly typed event and item models for agent messages, command execution, file changes, MCP tool calls, web search, reasoning, and to-do lists
- Structured output through JSON Schema with
TurnOptions.OutputSchema - Local image input through
Input.FromParts,TextInput, andLocalImageInput - Codex CLI configuration for models, context windows, automatic compaction thresholds, sandboxing, approval policy, reasoning effort, web search, additional directories, base URL, API key, and environment variables
- Microsoft Agent Framework integration through
CodexSdk.MAF - .NET 10.0 target with nullable reference types and implicit usings enabled
Install via NuGet:
dotnet add package CodexSdk --prereleaseFor Microsoft Agent Framework support:
dotnet add package CodexSdk.MAF --prerelease- .NET 10.0 SDK
- Node.js, when installing the Codex CLI through npm
- Codex CLI:
npm install -g @openai/codex- OpenAI API key available to the CLI. Set
CODEX_API_KEYin the environment, or passCodexOptions.ApiKey.
using OpenAI.CodexSdk;
using CodexClient = OpenAI.CodexSdk.Codex;
var codex = new CodexClient();
var thread = codex.StartThread();
var turn = await thread.RunAsync("List the files in the current directory.");
Console.WriteLine(turn.FinalResponse);
Console.WriteLine($"Thread ID: {thread.Id}");using OpenAI.CodexSdk;
using CodexClient = OpenAI.CodexSdk.Codex;
var codex = new CodexClient();
var thread = codex.StartThread();
await foreach (var evt in thread.RunStreamedAsync("Summarize this repository."))
{
switch (evt)
{
case ItemCompletedEvent { Item: AgentMessageItem msg }:
Console.WriteLine($"[agent] {msg.Text}");
break;
case ItemCompletedEvent { Item: CommandExecutionItem cmd }:
Console.WriteLine($"[cmd] {cmd.Command} exit={cmd.ExitCode}");
Console.WriteLine(cmd.AggregatedOutput);
break;
case TurnCompletedEvent completed:
Console.WriteLine($"[usage] in={completed.Usage.InputTokens} out={completed.Usage.OutputTokens}");
break;
}
}using OpenAI.CodexSdk;
using CodexClient = OpenAI.CodexSdk.Codex;
var codex = new CodexClient();
var thread = codex.StartThread();
var schema = new Dictionary<string, object?>
{
["type"] = "object",
["properties"] = new Dictionary<string, object?>
{
["summary"] = new Dictionary<string, object?> { ["type"] = "string" },
["status"] = new Dictionary<string, object?>
{
["type"] = "string",
["enum"] = new[] { "ok", "action_required" },
},
},
["required"] = new[] { "summary", "status" },
["additionalProperties"] = false,
};
var turn = await thread.RunAsync(
"Summarize the git status of the current repository.",
new TurnOptions { OutputSchema = schema });
Console.WriteLine(turn.FinalResponse);using OpenAI.CodexSdk;
using CodexClient = OpenAI.CodexSdk.Codex;
var codex = new CodexClient();
var thread = codex.StartThread(new ThreadOptions
{
SandboxMode = SandboxMode.ReadOnly,
});
var turn = await thread.RunAsync(Input.FromParts(
[
new TextInput("Describe what you see in these screenshots."),
new LocalImageInput("./ui.png"),
new LocalImageInput("./diagram.jpg"),
]));
Console.WriteLine(turn.FinalResponse);
var savedThreadId = thread.Id ?? throw new InvalidOperationException("Thread ID was not set.");
var resumed = codex.ResumeThread(savedThreadId);
var followUp = await resumed.RunAsync("Continue the analysis from where we left off.");
Console.WriteLine(followUp.FinalResponse);using Microsoft.Agents.AI;
using OpenAI.CodexSdk.MAF;
var agent = new CodexAIAgent();
var session = await agent.CreateSessionAsync();
var response = await agent.RunAsync(
"Explain what this project does in one paragraph.",
session);
Console.WriteLine(response.Text);
await foreach (var update in agent.RunStreamingAsync(
"List the files in the samples directory.",
session))
{
if (!string.IsNullOrWhiteSpace(update.Text))
{
Console.Write(update.Text);
}
}The SDK uses a thin process bridge over codex exec --experimental-json, then maps the JSONL event stream into .NET types.
Codex - Main SDK entry point
- Creates new threads with
StartThread - Resumes existing threads with
ResumeThread - Owns global
CodexOptions - Reuses the same Codex CLI execution bridge across threads
Thread - Conversation and turn API
- Runs buffered prompts with
RunAsync - Streams events with
RunStreamedAsync - Stores the current thread ID after
thread.started - Supports text, local image inputs, and per-turn structured output
CodexExec - Codex CLI subprocess bridge
- Resolves the
codexexecutable fromCodexOptions.CodexPathOverrideorPATH - Starts
codex exec --experimental-json - Sends prompts through stdin
- Yields stdout JSONL lines as an async stream
- Applies CLI arguments, config overrides, and environment variables
All streamed events derive from ThreadEvent:
ThreadStartedEvent- contains the resumable thread IDTurnStartedEvent- emitted when a prompt starts processingTurnCompletedEvent- contains token usageTurnFailedEvent- contains terminal turn errorsItemStartedEvent- emitted when a thread item beginsItemUpdatedEvent- emitted as a thread item changesItemCompletedEvent- emitted when an item reaches a terminal stateThreadErrorEvent- emitted for unrecoverable stream errors
All thread items derive from ThreadItem:
AgentMessageItem- final or intermediate agent textReasoningItem- reasoning summary textCommandExecutionItem- command, status, output, and exit codeFileChangeItem- file patch changes and apply statusMcpToolCallItem- MCP server, tool, arguments, result, and errorsWebSearchItem- web search query informationTodoListItem- agent task list stateErrorItem- non-fatal item-level errors
The CodexSdk.MAF package provides a Microsoft.Agents.AI bridge backed by Codex threads.
- Implements
AIAgent - Supports buffered
RunAsyncand streamingRunStreamingAsync - Converts Codex usage into
UsageDetails - Uses
ChatHistoryProviderwhen configured - Converts image
DataContentinputs to temporary local image arguments for the duration of each turn - Maps assistant responses to
ChatMessageandAgentResponseUpdate
- Stores the Codex
ThreadId - Serializes and deserializes session state
- Enables conversation continuity across processes or requests
- Accepts core
CodexOptions - Accepts per-thread
ThreadOptions - Supports explicit
ThreadId - Uses
IsResumeto resume an existing Codex thread selected by options - Accepts an optional
ChatHistoryProvider
The SDK forwards environment variables to the Codex CLI process.
CODEX_API_KEY- API key used by the CLI. Set fromCodexOptions.ApiKeywhen provided.CODEX_INTERNAL_ORIGINATOR_OVERRIDE- set by the SDK to identify C# SDK-originated CLI calls.
When CodexOptions.Env is provided, the SDK uses that dictionary as the process environment. When it is not provided, the SDK inherits the current process environment.
Global options for the Codex client:
using OpenAI.CodexSdk;
using CodexClient = OpenAI.CodexSdk.Codex;
var codex = new CodexClient(new CodexOptions
{
CodexPathOverride = "/path/to/codex",
BaseUrl = "https://api.openai.com/v1",
ApiKey = "sk-...",
Config = new Dictionary<string, CodexConfigValue>
{
["model_provider"] = "openai",
["approval_policy"] = "on-request",
},
Env = new Dictionary<string, string>
{
["CODEX_API_KEY"] = "sk-...",
},
});Per-thread options forwarded to codex exec:
var thread = codex.StartThread(new ThreadOptions
{
Model = "gpt-5.6-sol",
SandboxMode = SandboxMode.WorkspaceWrite,
WorkingDirectory = "/path/to/project",
SkipGitRepoCheck = true,
ModelReasoningEffort = ModelReasoningEffort.High,
ModelContextWindow = 1_000_000,
ModelAutoCompactTokenLimit = 900_000,
NetworkAccessEnabled = true,
WebSearchMode = WebSearchMode.Live,
ApprovalPolicy = ApprovalMode.OnRequest,
AdditionalDirectories = ["/path/to/shared/context"],
});ModelContextWindow and ModelAutoCompactTokenLimit are forwarded only when their values are greater than zero. Null, zero, and negative values are omitted. The Codex CLI may cap the effective context window based on the selected model's supported maximum.
Per-turn options:
using var cts = new CancellationTokenSource(TimeSpan.FromMinutes(2));
var turn = await thread.RunAsync(
"Return a JSON summary of this repository.",
new TurnOptions
{
OutputSchema = schema,
CancellationToken = cts.Token,
});dotnet restore codexsdk.slnx
dotnet build codexsdk.slnx --configuration Debug
dotnet build codexsdk.slnx --configuration ReleaseTests are not currently present. When a test project is added, run it with:
dotnet test <path-to-test-csproj>dotnet format codexsdk.slnxdotnet run --project samples/samples.csprojdotnet pack codexsdk.slnx -o ./releases -c ReleaseOr use the repository helper script:
./publish.sh 0.0.1-preview.1The samples/ folder contains runnable examples:
BasicStreaming- streamed event handling and buffered turnsStructuredOutput- JSON Schema output withTurnOptions.OutputSchemaImageAndResume- local image input and thread resumptionCodexMafAgent- Microsoft Agent Framework integration, streaming, buffered responses, and session serialization
Run all enabled samples:
dotnet run --project samples/samples.csprojRunStreamedAsyncyields each parsed Codex JSONL event.RunAsyncbuffers completed items and returns aTurn.RunAsyncstores the latestAgentMessageItem.TextasTurn.FinalResponse.RunAsyncthrows anInvalidOperationExceptionwhen aTurnFailedEventis received.
Thread.Idis populated from theThreadStartedEvent.Codex.ResumeThread(id)resumes a thread persisted by the Codex CLI.- MAF sessions serialize the Codex thread ID through
CodexAgentSession.
- Polymorphic event and item models use the
typediscriminator emitted by the CLI. - Enum values are parsed from snake_case JSON names.
- Structured output schemas are written to a temporary JSON file and passed to
codex exec --output-schema.
- Each turn starts a Codex CLI subprocess and closes stdin after sending the prompt.
- Cancellation tokens terminate the underlying process when cancellation is requested.
- The SDK captures stderr and includes it when the CLI exits with a non-zero code.
Install the Codex CLI globally:
npm install -g @openai/codexIf the CLI is installed in a custom location, pass the executable path:
var codex = new Codex(new CodexOptions
{
CodexPathOverride = "/path/to/codex",
});Install Node.js from https://nodejs.org/, then install the Codex CLI:
npm install -g @openai/codexSet your API key:
# macOS/Linux
export CODEX_API_KEY="your-api-key"
# Windows PowerShell
$env:CODEX_API_KEY="your-api-key"
# Windows Command Prompt
set CODEX_API_KEY=your-api-keyOr pass it via options:
var codex = new Codex(new CodexOptions
{
ApiKey = "your-api-key",
});If you intentionally run Codex outside a Git repository, enable:
var thread = codex.StartThread(new ThreadOptions
{
SkipGitRepoCheck = true,
});Adjust the thread policy:
var thread = codex.StartThread(new ThreadOptions
{
SandboxMode = SandboxMode.WorkspaceWrite,
ApprovalPolicy = ApprovalMode.OnRequest,
NetworkAccessEnabled = true,
});Contributions are welcome. Please keep changes focused, update samples when behavior changes, and run:
dotnet format codexsdk.slnx
dotnet build codexsdk.slnx --configuration ReleaseMIT License - see LICENSE for details.