Skip to content

About

Codex SDK for C#

Resources

Stars

1 star

Watchers

0 watching

Forks

Repository files navigation

OpenAI Codex SDK for C#

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.

Status & Version

NuGet Version (with prereleases) NuGet Version (with prereleases) CI/CD

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.

Features

  • Buffered turns via Thread.RunAsync
  • Real-time JSONL event streaming via Thread.RunStreamedAsync
  • Resumable Codex threads with StartThread and ResumeThread
  • 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, and LocalImageInput
  • 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

Installation

Core SDK

Install via NuGet:

dotnet add package CodexSdk --prerelease

Microsoft Agent Framework Integration (Optional)

For Microsoft Agent Framework support:

dotnet add package CodexSdk.MAF --prerelease

Prerequisites

  • .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_KEY in the environment, or pass CodexOptions.ApiKey.

Quick Start

Buffered Turn

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}");

Streaming Events

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;
    }
}

Structured Output

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);

Local Image Input and Resume

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);

Microsoft Agent Framework Integration

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);
    }
}

Architecture

The SDK uses a thin process bridge over codex exec --experimental-json, then maps the JSONL event stream into .NET types.

Core Components

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.CodexPathOverride or codex before each invocation using PATH, Windows PATHEXT, and a Unix login shell after a PATH miss
  • 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

Event Types

All streamed events derive from ThreadEvent:

  • ThreadStartedEvent - contains the resumable thread ID
  • TurnStartedEvent - emitted when a prompt starts processing
  • TurnCompletedEvent - contains token usage
  • TurnFailedEvent - contains terminal turn errors
  • ItemStartedEvent - emitted when a thread item begins
  • ItemUpdatedEvent - emitted as a thread item changes
  • ItemCompletedEvent - emitted when an item reaches a terminal state
  • ThreadErrorEvent - emitted for unrecoverable stream errors

Item Types

All thread items derive from ThreadItem:

  • AgentMessageItem - final or intermediate agent text
  • ReasoningItem - reasoning summary text
  • CommandExecutionItem - command, status, output, and exit code
  • FileChangeItem - file patch changes and apply status
  • McpToolCallItem - MCP server, tool, arguments, result, and errors
  • WebSearchItem - web search query information
  • TodoListItem - agent task list state
  • ErrorItem - non-fatal item-level errors

Microsoft Agent Framework Integration

The CodexSdk.MAF package provides a Microsoft.Agents.AI bridge backed by Codex threads.

CodexAIAgent

  • Implements AIAgent
  • Supports buffered RunAsync and streaming RunStreamingAsync
  • Converts Codex usage into UsageDetails
  • Uses ChatHistoryProvider when configured
  • Converts image DataContent inputs to temporary local image arguments for the duration of each turn
  • Maps assistant responses to ChatMessage and AgentResponseUpdate

CodexAgentSession

  • Stores the Codex ThreadId
  • Serializes and deserializes session state
  • Enables conversation continuity across processes or requests

CodexAIAgentOptions

  • Accepts core CodexOptions
  • Accepts per-thread ThreadOptions
  • Supports explicit ThreadId
  • Uses IsResume to resume an existing Codex thread selected by options
  • Accepts an optional ChatHistoryProvider

Configuration

Environment Variables

The SDK forwards environment variables to the Codex CLI process.

  • CODEX_API_KEY - API key used by the CLI. Set from CodexOptions.ApiKey when provided.
  • CODEX_INTERNAL_ORIGINATOR_OVERRIDE - defaults to codex_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.

CodexOptions

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-...",
    },
});

ThreadOptions

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.

TurnOptions

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,
    });

Development

Building

dotnet restore codexsdk.slnx
dotnet build codexsdk.slnx --configuration Debug
dotnet build codexsdk.slnx --configuration Release

Testing

Run 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.

Formatting

dotnet format codexsdk.slnx

Running Samples

dotnet run --project samples/samples.csproj

Packaging NuGet Packages

dotnet pack codexsdk.slnx -o ./releases -c Release

Or use the repository helper script:

./publish.sh 0.0.1-preview.1

Examples

The samples/ folder contains runnable examples:

  • BasicStreaming - streamed event handling and buffered turns
  • StructuredOutput - JSON Schema output with TurnOptions.OutputSchema
  • ImageAndResume - local image input and thread resumption
  • CodexMafAgent - Microsoft Agent Framework integration, streaming, buffered responses, and session serialization

Run all enabled samples:

dotnet run --project samples/samples.csproj

Key Behaviors

Message Streaming and Termination

  • RunStreamedAsync yields each parsed Codex JSONL event.
  • RunAsync buffers completed items and returns a Turn.
  • RunAsync stores the latest AgentMessageItem.Text as Turn.FinalResponse.
  • RunAsync throws an InvalidOperationException when a TurnFailedEvent is received.

Thread Resumption

  • Thread.Id is populated from the ThreadStartedEvent.
  • Codex.ResumeThread(id) resumes a thread persisted by the Codex CLI.
  • MAF sessions serialize the Codex thread ID through CodexAgentSession.

JSON Serialization

  • Polymorphic event and item models use the type discriminator 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.

Resource Management

  • 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.

Troubleshooting

Codex CLI discovery

Install the Codex CLI globally:

npm install -g @openai/codex

Before 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" } }

"env: node: No such file or directory"

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.

Authentication Errors

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-key

Or pass it via options:

var codex = new Codex(new CodexOptions
{
    ApiKey = "your-api-key",
});

Git Repository Check

If you intentionally run Codex outside a Git repository, enable:

var thread = codex.StartThread(new ThreadOptions
{
    SkipGitRepoCheck = true,
});

Sandbox and Approval Issues

Adjust the thread policy:

var thread = codex.StartThread(new ThreadOptions
{
    SandboxMode = SandboxMode.WorkspaceWrite,
    ApprovalPolicy = ApprovalMode.OnRequest,
    NetworkAccessEnabled = true,
});

Contributing

Contributions are welcome. Please keep changes focused, update samples when behavior changes, and run:

dotnet format codexsdk.slnx
dotnet build codexsdk.slnx --configuration Release

License

MIT License - see LICENSE for details.

Links

About

Codex SDK for C#

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages