codex

package module
v0.1.2 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Sep 6, 2026 License: MIT Imports: 12 Imported by: 0

README

Codex SDK for Go

CI Go Reference Go Report Card Release

Unofficial Go port of the official TypeScript SDK (@openai/codex-sdk) for the OpenAI Codex agent.

⚠️ Not affiliated with or endorsed by OpenAI.

Embed the Codex coding agent in Go services, CLIs and CI jobs. The SDK spawns the codex CLI with exec --experimental-json, writes your prompt to stdin, and streams structured JSONL events back — the same mechanism the TypeScript SDK uses, with idiomatic Go on top: context for cancellation, channels for streaming, typed events and errors, and zero third-party dependencies.

Features

  • 🧵 Threads & turns — StartThread, ResumeThread, multi-turn conversations persisted by codex
  • ⚡ Streaming — RunStreamed delivers every thread.*, turn.* and item.* event over a channel as it happens
  • 🧱 Typed items — agent messages, reasoning, command executions, file changes, MCP tool calls, web searches, todo lists
  • 🎯 Structured output — pass a JSON schema per turn, get JSON back
  • 🖼️ Images — attach local images alongside text
  • 🔒 Sandbox & approvals — read-only / workspace-write / full-access, approval policy, network access, extra directories
  • ⚙️ Config overrides — structured maps flattened to --config key=value TOML, plus raw overrides
  • 🛑 Cancellation — cancel the context and the codex process is killed
  • 🧪 Testable — unit tests run against a fake codex binary; no API key required
  • 📦 Zero dependencies — stdlib only

See docs/PARITY.md for a field-by-field map to the TypeScript SDK.

Installation

go get github.com/schlunsen/codex-sdk-go

Requires Go 1.24+ and the Codex CLI:

npm install -g @openai/codex   # or: brew install codex
codex login                    # or: export CODEX_API_KEY=...

Quickstart

package main

import (
	"context"
	"fmt"
	"log"

	codex "github.com/schlunsen/codex-sdk-go"
)

func main() {
	client, err := codex.New(nil)
	if err != nil {
		log.Fatal(err)
	}

	thread := client.StartThread(nil)
	turn, err := thread.Run(context.Background(), "Diagnose the test failure and propose a fix", nil)
	if err != nil {
		log.Fatal(err)
	}

	fmt.Println(turn.FinalResponse)
	fmt.Println(len(turn.Items), "items")
}

Call Run again on the same Thread to continue the conversation:

next, err := thread.Run(ctx, "Implement the fix", nil)
Streaming responses

Run buffers events until the turn finishes. To react to intermediate progress — tool calls, file changes, reasoning — use RunStreamed:

stream, err := thread.RunStreamed(ctx, "Diagnose the test failure and propose a fix", nil)
if err != nil {
	log.Fatal(err)
}

for ev := range stream.Events() {
	switch e := ev.(type) {
	case *types.ItemCompletedEvent:
		switch item := e.Item.(type) {
		case *types.CommandExecutionItem:
			fmt.Printf("$ %s (exit %d)\n", item.Command, *item.ExitCode)
		case *types.AgentMessageItem:
			fmt.Println(item.Text)
		}
	case *types.TurnCompletedEvent:
		fmt.Printf("tokens: %d in / %d out\n", e.Usage.InputTokens, e.Usage.OutputTokens)
	}
}
if err := stream.Err(); err != nil { // nil on success; *types.ExecError, *types.ParseError, or ctx error
	log.Fatal(err)
}

To stop a turn early without cancelling your context, call stream.Close(). While the turn is in progress it kills the codex process, closes Events(), and returns codex.ErrClosed (distinct from the context errors, so an errgroup or retry loop can tell a deliberate close from an upstream abort). Once turn.completed or turn.failed has been delivered, Close no longer kills anything — codex is left to exit and persist the session — and it returns the same error Err() would, so defer stream.Close() releases event delivery while allowing session persistence to finish. A clean process exit without turn.completed or turn.failed returns codex.ErrIncompleteTurn.

Structured output
schema := map[string]any{
	"type": "object",
	"properties": map[string]any{
		"summary": map[string]any{"type": "string"},
		"status":  map[string]any{"type": "string", "enum": []string{"ok", "action_required"}},
	},
	"required":             []string{"summary", "status"},
	"additionalProperties": false,
}

turn, err := thread.Run(ctx, "Summarize repository status",
	types.NewTurnOptions().WithOutputSchema(schema))
// turn.FinalResponse is JSON conforming to the schema
Attaching images

Text entries are concatenated into the prompt; image entries are passed via --image.

turn, err := thread.RunInputs(ctx, []types.UserInput{
	types.TextInput("Describe these screenshots"),
	types.LocalImageInput("./ui.png"),
	types.LocalImageInput("./diagram.jpg"),
}, nil)
Resuming a thread

Threads are persisted by codex in ~/.codex/sessions. If you lose the in-memory Thread, reconstruct it by id:

thread := client.ResumeThread(savedThreadID, nil)
turn, err := thread.Run(ctx, "Implement the fix", nil)

thread.ID() is populated after the first turn starts.

Thread options
thread := client.StartThread(types.NewThreadOptions().
	WithModel("gpt-5-codex").
	WithSandboxMode(types.SandboxWorkspaceWrite).
	WithApprovalPolicy(types.ApprovalNever).
	WithWorkingDirectory("/path/to/project").
	WithAdditionalDirectories("/path/to/shared").
	WithSkipGitRepoCheck(true).
	WithModelReasoningEffort(types.ReasoningHigh).
	WithNetworkAccess(true).
	WithWebSearchMode(types.WebSearchLive))

Codex refuses to run outside a git repository unless SkipGitRepoCheck is set.

Controlling the CLI environment

By default the codex process inherits the parent environment. Provide Env to fully control it (useful in sandboxed hosts). The SDK still injects CODEX_API_KEY when APIKey is set, and identifies itself via CODEX_INTERNAL_ORIGINATOR_OVERRIDE=codex_sdk_go.

client, err := codex.New(types.NewCodexOptions().
	WithAPIKey(os.Getenv("OPENAI_API_KEY")).
	WithBaseURL("https://my-proxy.example/v1").   // -> --config openai_base_url=...
	WithEnv(map[string]string{"PATH": "/usr/local/bin"}).
	WithCodexPath("/opt/codex/bin/codex"))
Passing --config overrides

Config takes a nested map, flattens it to dotted paths and serializes values as TOML literals:

client, err := codex.New(types.NewCodexOptions().
	WithConfig(types.ConfigObject{
		"show_raw_agent_reasoning": true,
		"sandbox_workspace_write":  map[string]any{"network_access": true},
	}))
// --config show_raw_agent_reasoning=true
// --config sandbox_workspace_write.network_access=true

For keys that can't be expressed as dotted paths, pass raw TOML with ConfigOverrides; they are forwarded unchanged after Config and before SDK-managed / thread-specific overrides:

types.NewCodexOptions().
	WithConfig(types.ConfigObject{"default_permissions": "audit"}).
	WithConfigOverrides(`permissions.audit.filesystem={":root"="read","/path/.env"="deny"}`)
Cancellation

Every Run* method takes a context.Context. Cancelling it stops the codex process and closes the event channel; stream.Err() / Run return context.Canceled or context.DeadlineExceeded. On Unix the SDK first sends SIGINT to codex's process group — codex aborts the turn and kills the shell command it is running — and escalates to SIGKILL of the group if codex has not exited within a second. On Windows the process is killed outright.

ctx, cancel := context.WithTimeout(context.Background(), 5*time.Minute)
defer cancel()
turn, err := thread.Run(ctx, prompt, nil)
Error handling
Error When
*types.CLINotFoundError codex binary not found (types.IsCLINotFoundError)
*types.TurnFailedError agent emitted turn.failed
*types.ExecError codex exited non-zero; carries ExitCode, Signal, Stderr
*types.ThreadStreamError stream emitted a fatal error event but exited 0
*types.ParseError a JSONL line couldn't be decoded
*types.ConfigError a config override couldn't be serialized

All support errors.As, and Is* helpers are provided.

Events and items

Events implement types.ThreadEvent; items implement types.ThreadItem. Unknown types from newer CLI versions decode into *types.UnknownEvent / *types.UnknownItem with the raw JSON preserved, so upgrades never break your consumer.

Event Payload
thread.started ThreadID
turn.started —
turn.completed Usage
turn.failed Error.Message
item.started / item.updated / item.completed Item types.ThreadItem
error Message
Item Key fields
agent_message Text
reasoning Text
command_execution Command, AggregatedOutput, ExitCode, Status
file_change Changes[]{Path, Kind}, Status
mcp_tool_call Server, Tool, Arguments, Result, Error, Status
web_search Query
todo_list Items[]{Text, Completed}
error Message

CLI discovery

The binary is resolved in order: CodexOptions.CodexPathOverride → $CODEX_PATH → codex on PATH → common install locations (~/.npm-global/bin, /opt/homebrew/bin, /usr/local/bin, ~/.local/bin, ~/.bun/bin, ~/.cargo/bin, …).

Examples

Example Shows
examples/simple_run One-shot turn
examples/streaming Live event rendering, Ctrl-C cancellation
examples/resume_thread Multi-turn + resume by id
examples/structured_output JSON schema output
examples/with_images Text + image input
go run ./examples/streaming "Run the tests and summarize what fails"

Development

make test-race   # unit tests against a fake codex binary (no network)
make test-live   # smoke test against a real codex CLI (needs codex login or CODEX_API_KEY)
make examples    # compile examples
make lint        # go vet + golangci-lint
make coverage    # HTML coverage report

See CONTRIBUTING.md. Sibling project: claude-agent-sdk-go.

License

MIT — see LICENSE.

Documentation

Overview

Package codex is an unofficial Go SDK for the OpenAI Codex agent.

It mirrors the official TypeScript SDK (@openai/codex-sdk): it spawns the `codex` CLI with `exec --experimental-json`, writes the prompt to stdin and streams structured JSONL events back over stdout.

Basic usage:

client, err := codex.New(nil)
if err != nil {
    log.Fatal(err)
}
thread := client.StartThread(nil)
turn, err := thread.Run(ctx, "Diagnose the test failure and propose a fix", nil)
if err != nil {
    log.Fatal(err)
}
fmt.Println(turn.FinalResponse)

Call Run repeatedly on the same Thread to continue the conversation, or use RunStreamed to react to events as they are produced.

Index

Constants

This section is empty.

Variables

View Source
var ErrClosed = errors.New("codex: streamed turn closed")

ErrClosed is returned by StreamedTurn.Err and StreamedTurn.Close when Close stopped a turn that was still in progress. It is distinct from the context errors so callers can tell a deliberate close from an upstream cancellation.

View Source
var ErrIncompleteTurn = errors.New("codex: stream ended without a terminal turn event")

ErrIncompleteTurn means the process exited successfully without reporting turn.completed or turn.failed.

View Source
var Version = strings.TrimSpace(versionFile)

Version is the SDK version, read from the VERSION file at build time.

Functions

This section is empty.

Types

type Codex

type Codex struct {
	// contains filtered or unexported fields
}

Codex is the entry point for interacting with the Codex agent. Create one with New, then use StartThread or ResumeThread.

func New

func New(options *types.CodexOptions) (*Codex, error)

New creates a Codex client. Passing nil uses default options. The codex executable is located at construction time; a *types.CLINotFoundError is returned if it cannot be found.

func (*Codex) ExecutablePath

func (c *Codex) ExecutablePath() string

ExecutablePath returns the resolved path of the codex binary.

func (*Codex) ResumeThread

func (c *Codex) ResumeThread(id string, options *types.ThreadOptions) *Thread

ResumeThread resumes a previously started thread by id. Threads are persisted by codex in ~/.codex/sessions.

func (*Codex) StartThread

func (c *Codex) StartThread(options *types.ThreadOptions) *Thread

StartThread starts a new conversation with the agent. Passing nil uses default thread options. The thread id is populated once the first turn starts.

type StreamedTurn

type StreamedTurn struct {
	// contains filtered or unexported fields
}

StreamedTurn is the result of Thread.RunStreamed. Read events from Events until it is closed, then call Err to learn how the turn ended.

func (*StreamedTurn) Close added in v0.1.1

func (s *StreamedTurn) Close() error

Close releases the turn and waits for cleanup. If the turn is still in progress the codex process (and, on Unix, its whole process group) is killed and Close returns ErrClosed. If turn.completed or turn.failed has already been delivered, codex is left to exit on its own (so the session is persisted) and Close returns the turn's terminal error, exactly like Err. Close is safe to call more than once and from a goroutine other than the one reading Events.

func (*StreamedTurn) Err

func (s *StreamedTurn) Err() error

Err blocks until the stream has finished and returns the terminal error: nil on success, *types.ExecError if codex exited non-zero, *types.ParseError on malformed output, ErrIncompleteTurn on missing terminal events, the context error on cancellation, or ErrClosed if Close stopped the turn.

func (*StreamedTurn) Events

func (s *StreamedTurn) Events() <-chan types.ThreadEvent

Events yields thread events as they are produced. It is closed when the turn ends, the process exits, the context is cancelled, or Close is called.

type Thread

type Thread struct {
	// contains filtered or unexported fields
}

Thread represents a conversation with the agent. One thread can have multiple consecutive turns. A Thread is safe to reuse sequentially; do not run turns on the same Thread concurrently.

func (*Thread) ID

func (t *Thread) ID() string

ID returns the thread id. It is empty until the first turn has started.

func (*Thread) Run

func (t *Thread) Run(ctx context.Context, prompt string, turnOptions *types.TurnOptions) (*Turn, error)

Run sends a text prompt to the agent and returns the completed turn.

func (*Thread) RunInputs

func (t *Thread) RunInputs(ctx context.Context, inputs []types.UserInput, turnOptions *types.TurnOptions) (*Turn, error)

RunInputs sends structured input (text and local images) to the agent and returns the completed turn.

func (*Thread) RunStreamed

func (t *Thread) RunStreamed(ctx context.Context, prompt string, turnOptions *types.TurnOptions) (*StreamedTurn, error)

RunStreamed sends a text prompt to the agent and streams events as they are produced.

func (*Thread) RunStreamedInputs

func (t *Thread) RunStreamedInputs(ctx context.Context, inputs []types.UserInput, turnOptions *types.TurnOptions) (*StreamedTurn, error)

RunStreamedInputs sends structured input to the agent and streams events as they are produced.

type Turn

type Turn struct {
	// Items are all items that completed during the turn, in order.
	Items []types.ThreadItem
	// FinalResponse is the text of the last agent_message item (JSON when
	// structured output was requested).
	FinalResponse string
	// Usage is the token usage reported for the turn; nil if not reported.
	Usage *types.Usage
}

Turn is a completed turn of a thread.

Directories

Path Synopsis
examples
resume_thread command
Example: multi-turn conversation and resuming a persisted thread by id.
Example: multi-turn conversation and resuming a persisted thread by id.
simple_run command
Example: one-shot turn with the Codex agent.
Example: one-shot turn with the Codex agent.
streaming command
Example: stream events as the agent works.
Example: stream events as the agent works.
structured_output command
Example: request JSON output that conforms to a schema.
Example: request JSON output that conforms to a schema.
with_images command
Example: send text together with local images.
Example: send text together with local images.
internal
config
Package config serializes structured Codex config overrides into the `--config key=value` TOML-literal form the codex CLI expects.
Package config serializes structured Codex config overrides into the `--config key=value` TOML-literal form the codex CLI expects.
log
Package log provides a minimal verbosity-gated logger for the SDK.
Package log provides a minimal verbosity-gated logger for the SDK.
transport
Package transport spawns the codex CLI and streams its JSONL output.
Package transport spawns the codex CLI and streams its JSONL output.
Package types contains the public type definitions for the Codex SDK: thread events, thread items, configuration options and error types.
Package types contains the public type definitions for the Codex SDK: thread events, thread items, configuration options and error types.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL