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.
Package versions use the format x.y.z and may include a -preview suffix for prerelease builds.
- x follows the target .NET version.
- y is incremented when the library introduces breaking changes or updates its Microsoft Agent Framework dependency.
- z is incremented for library patch releases.
- 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
CodexOptions.CodexPathOverrideorcodexbefore each invocation usingPATH, WindowsPATHEXT, and a Unix login shell after aPATHmiss - 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- defaults tocodex_sdk_cs; an existing value is preserved.
Each invocation copies CodexOptions.Env when provided, or inherits the current process environment otherwise. Command lookup and the CLI process use this environment. A supplied dictionary fully replaces the host environment and remains unchanged.
On Unix, when lookup finds Codex through the login shell specified by this environment's SHELL, the CLI receives that shell's complete PATH. Relative entries are resolved against the shell's working directory. Other variables continue to come from the copied environment; SDK-provided API keys are applied after lookup.
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 ReleaseRun the CLI startup component tests from the repository root on macOS or Linux with Node.js installed:
npm ci --prefix tests/fixtures/codex-cli --ignore-scripts --no-audit --no-fund
dotnet test --project tests/CodexSdk.Tests --filter-class '*CodexCliStartupTests'These tests use the pinned npm Codex package and real processes to query --version, including a child test process with a restricted inherited PATH. They require no Codex credentials. See the fixture instructions.
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/codexBefore each invocation, the SDK resolves CodexPathOverride, or codex when it is omitted. Command names are searched using the invocation's environment snapshot; Windows uses PATHEXT, and Unix checks execute permission. After a Unix PATH miss, the SDK can read the PATH from a supported login shell named by that environment's SHELL, with a three-second timeout. A successful login-shell lookup also supplies the CLI process's PATH. Lookup honors cancellation. A lookup failure reports the requested command or path and any login-shell diagnostic.
If the CLI is installed in a custom location, pass the executable path:
var codex = new Codex(new CodexOptions
{
CodexPathOverride = "/path/to/codex",
});When binding CodexOptions from JSON through a host integration (for example, an agw Extra block), provide codexOptions as a nested object:
{ "codexOptions": { "codexPathOverride": "/opt/homebrew/bin/codex" } }The npm build of the Codex CLI is a Node.js script (#!/usr/bin/env node), so its process PATH must contain Node.js. When the SDK discovers Codex through a login shell, it passes that shell's PATH to the CLI. The shell configuration must make both Codex and Node.js available, including installations in separate directories.
An explicit CodexPathOverride selects the executable directly. Its interpreter dependencies use the supplied or inherited environment, so that environment's PATH must include Node.js.
Set 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.