codex

Codex SDK, with some useful tools for building AI applications.

AI
moon add peter-jerry-ye/codex@0.129.0
Download zip
Version
0.129.0
License
Apache-2.0
Last updated
2 months ago
Downloads
5K

Dependencies

README

#MoonBit Codex SDK

This is the Codex SDK for MoonBit, ported from the TypeScript SDK.

The SDK communicates with Codex by spawning it in non-interactive mode using codex exec. The target Codex version is 0.79.0.

Codex must be installed and available on your PATH. If not, install with:

pnpm install -g @openai/codex@0.79.0

#Usage

The simplest way to use Codex is to create a @codex.Codex and start a @codex.Thread. Then create a @codex.Turn from the thread using @codex.Thread::run. By default, the OPENAI_API_KEY environment variable is read.

If you are already paid ChatGPT users, you can run the code below directly

///|
#skip
async test {
let codex = @codex.Codex::new()
let thread = codex.start_thread()
let turn = thread.run("Hello, what model are you using?")
// I’m `GPT-5.2`, running inside the Codex CLI harness in your repo (`/Users/../codex-sdk`).
println(turn.final_response)
println(turn.items.to_json().stringify())
println(turn.usage.to_json().stringify())
}

///|
#skip
async test {
let codex = @codex.Codex::new(
options=@codex.CodexOptions::new(base_url="https://openrouter.ai/api/v1"),
)
let thread = codex.start_thread(
options=@codex.ThreadOptions::new(model="anthropic/claude-sonnet-4.5"),
)
let turn = thread.run("Hello?")
println(turn.final_response)
println(turn.items.to_json().stringify())
println(turn.usage.to_json().stringify())
}

For incremental usage, import the @generator package and use @codex.Thread::run_streamed.

///|
#skip
async test {
let codex = @codex.Codex::new(
options=@codex.CodexOptions::new(base_url="https://openrouter.ai/api/v1"),
)
let thread = codex.start_thread(
options=@codex.ThreadOptions::new(model="anthropic/claude-sonnet-4.5"),
)
@async.with_task_group(tg => {
let streamed_turn = thread.run_streamed("Hello?", tg)
while streamed_turn.events.next() is Some(event) {
println(event.to_json().stringify())
}
}) catch {
e => println(e)
}
}

#Architecture Overview

#Process boundary and transport

The MoonBit SDK is a thin but strongly typed wrapper around codex exec:

  1. @codex.CodexExec::run spawns the CLI with --experimental-json, automatically wiring API endpoint overrides, API keys, sandbox flags, working directory overrides, and thread resumption arguments.
  2. The CLI's JSONL stream is fed through @generator.AsyncGenerator so the SDK can yield events as soon as they arrive. This keeps Codex long-running commands responsive while avoiding blocking MoonBit's async runtime.
  3. Each line is decoded into the rich @codex.Event / @codex.ThreadItem hierarchy (events.mbt and items.mbt), which means MoonBit callers never manipulate raw JSON.

The Codex/Thread/Turn trio mirrors the CLI lifecycle: a Codex holds process-level configuration, a Thread models a Codex conversation, and a Turn captures the completed response plus token usage metrics.

#Thread lifecycle and safety

  • Thread::run_streamed owns the async generator returned by CodexExec::run. The method updates the cached thread id when ThreadStarted surfaces, so a later Thread::run call automatically resumes the same conversation.
  • Thread::run is implemented on top of the streaming primitive. It drains the generator, records AgentMessageItem content as the Turn.final_response, retains the full item history for post-processing (e.g., capturing diffs or tool invocations), and surfaces TurnFailed by raising an error after draining the iterator to prevent resource leaks.
  • Structured cleanup exists everywhere a temporary artifact is created; for example, @codex::create_output_schema_file creates /tmp/codex-output-schema-* directories and ensures they are removed even when errors occur.

#Events, items, and observability

The CLI emits high-level telemetry that is mirrored by the SDK:

  • Event::ThreadStarted, TurnStarted, TurnCompleted, and TurnFailed make it trivial to instrument throughput, retries, and token usage.
  • ThreadItem variants capture everything the agent does: CommandExecutionItem surfaces shell commands with exit codes, FileChangeItem contains per-file diffs, McpToolCallItem shows MCP tool usage, and TodoListItem exposes the agent's internal plan.
  • All enums (sandbox mode, approval mode, command status, etc.) expose ToJson/FromJson so you can persist structured logs or forward them to observability backends without lossy string manipulation.

#Structured output and schema enforcement

TurnOptions.output_schema accepts an arbitrary JSON schema. When provided, Thread::run / run_streamed transparently:

  1. Creates a temporary schema file on disk.
  2. Passes --output-schema /tmp/.../schema.json to the CLI.
  3. Deletes the schema file after the turn completes or fails (even if exceptions arise).

This makes it safe to require JSON output without managing files yourself. The final assistant message still flows through ThreadItem::AgentMessageItem, so you can parse it with @json.parse once the turn completes.

#Configuration layers

  • CodexOptions sets global API concerns (binary override, base URL, API key) once per process.
  • ThreadOptions controls per-thread concerns such as the model, sandbox levels (read-only, workspace-write, danger-full-access), working directory routing, and Git safety checks.
  • TurnOptions tunes per-turn behavior, currently focusing on structured output but intentionally keeping room for future features (e.g., custom completion criteria). Because configuration objects implement ToJson/FromJson, they can be marshalled into other systems (task schedulers, Codex automation) without reimplementing serialization.

#Advanced Usage Patterns

#Instrument streaming events

You can subscribe to the event stream for telemetry, custom retry logic, or UI overlays without waiting for a completed turn:

///|
#skip
async test {
let codex = @codex.Codex::new()
let thread = codex.start_thread()
@async.with_task_group(tg => {
let turn = thread.run_streamed("Summarize today's commits", tg)
while turn.events.next() is Some(event) {
match event {
ItemStarted(item) => println("started: \{item.to_json().stringify()}")
ItemCompleted(AgentMessageItem(text~, ..)) =>
println("assistant: \{text}")
TurnCompleted(usage) =>
println("tokens in/out: \{usage.input_tokens}/\{usage.output_tokens}")
TurnFailed(error) => fail("codex turn failed: \{error.message}")
_ => ()
}
}
})
}

#Enforce structured responses

The temporary-schema mechanism lets you demand JSON (or any schema-valid structure) without extra boilerplate:

///|
#skip
async test {
let codex = @codex.Codex::new()
let thread = codex.start_thread()
let turn = thread.run(
"Plan the next refactor as JSON",
turn_options=@codex.TurnOptions::new(output_schema={
"type": "object",
"properties": {
"summary": { "type": "string" },
"files_to_touch": { "type": "array", "items": { "type": "string" } },
},
"required": ["summary", "files_to_touch"],
"additionalProperties": false, // required to be supplied as valid schema
}),
)
println(turn.final_response)
let plan_json = @json.parse(turn.final_response)
println(plan_json.stringify(indent=2))
}

These primitives compose cleanly with your own orchestration layers, since everything in the SDK is expressed as plain MoonBit structs and async functions.

#
Input

type Input = Array[UserInput]

An input to send to the agent.

#
ApprovalMode

pub(all) enum ApprovalMode {
Never
OnRequest
OnFailure
Untrusted
}

Approval mode for actions that require user consent.

#
Codex

type Codex

Codex is the main class for interacting with the Codex agent.

Use the start_thread() method to start a new thread or resume_thread() to resume a previously started thread.

#
Codex::new

fn Codex::new(options? : CodexOptions) -> Codex

Create a new Codex instance.

Arguments

  • options - Optional configuration for the Codex client

Returns

A new Codex instance

#
Codex::resume_thread

fn Codex::resume_thread(self : Codex, id : String, options? : ThreadOptions) -> Thread

Resumes a conversation with an agent based on the thread id. Threads are persisted in ~/.codex/sessions.

Arguments

  • id - The id of the thread to resume
  • options - Optional configuration for the thread

Returns

A new thread instance

#
Codex::start_thread

fn Codex::start_thread(self : Codex, options? : ThreadOptions) -> Thread

Starts a new conversation with an agent.

Arguments

  • options - Optional configuration for the thread

Returns

A new thread instance

#
CodexOptions

type CodexOptions

Options for configuring the Codex client.

#
CodexOptions::new

fn CodexOptions::new(codex_path_override? : String, base_url? : String, api_key? : String, env? : Map[String, String]) -> CodexOptions

#
CommandExecutionStatus

pub enum CommandExecutionStatus {
InProgress
Completed
Failed
}

#
Event

pub enum Event {
ThreadStarted(String)
TurnStarted
TurnCompleted(Usage)
TurnFailed(ThreadError)
ItemStarted(ThreadItem)
ItemUpdated(ThreadItem)
ItemCompleted(ThreadItem)
ThreadErrorEvent(String)
}

#
FileUpdateChange

pub struct FileUpdateChange {
path : String
kind : PatchChangeKind
}

A set of file changes by the agent.

#
McpToolCallResult

pub struct McpToolCallResult {
content : Array[Json]
structured_content : Json
}

#
McpToolCallStatus

pub enum McpToolCallStatus {
InProgress
Completed
Failed
}

The status of an MCP tool call.

#
ModelReasoningEffort

pub(all) enum ModelReasoningEffort {
Minimal
Low
High
Xhigh
}

#
PatchApplyStatus

pub enum PatchApplyStatus {
Completed
Failed
}

The status of a file change.

#
PatchChangeKind

pub enum PatchChangeKind {
Add
Delete
Update
}

Indicates the type of the file change.

#
SandboxMode

pub(all) enum SandboxMode {
ReadOnly
WorkspaceWrite
DangerFullAccess
}

Sandbox mode that controls the level of access the agent has.
impl Show for SandboxMode

#
StreamedTurn

#alias(RunStreamedResult)
pub struct StreamedTurn {
events :
AsyncGenerator
[Event]
}

The result of the run_streamed method.

#
Thread

type Thread

Represent a thread of conversation with the agent. One thread can have multiple consecutive turns.

#
Thread::id

fn Thread::id(self : Thread) -> String?

Returns the ID of the thread. Populated after the first turn starts.

#
Thread::run

async fn Thread::run(self : Thread, prompt : String, extra_input? : Array[UserInput], turn_options? : TurnOptions) -> Turn

Provides the input to the agent and returns the completed turn.

Arguments

  • input - The user input/prompt to send to the agent
  • turn_options - Options for configuring this turn

Returns

A Turn containing all items, the final response, and usage information

#
Thread::run_streamed

async fn[G] Thread::run_streamed(self : Thread, prompt : String, extra_input? : Array[UserInput], turn_options? : TurnOptions, taskgroup :
TaskGroup
[G]) -> StreamedTurn

Provides the input to the agent and streams events as they are produced during the turn.

Arguments

  • input - The user input/prompt to send to the agent
  • turn_options - Options for configuring this turn
  • taskgroup - The TaskGroup to run the streaming generator in

Returns

A StreamedTurn containing an async iterator of events

#
ThreadError

pub struct ThreadError {
message : String
}

#
ThreadItem

pub enum ThreadItem {
AgentMessageItem(String, String)
ReasoningItem(String, String)
CommandExecutionItem(String, String, String, Int?, CommandExecutionStatus)
FileChangeItem(String, Array[FileUpdateChange], PatchApplyStatus)
McpToolCallItem(String, String, String, McpToolCallStatus, Json?, Result[McpToolCallResult, String]?)
WebSearchItem(String, String)
TodoListItem(String, Array[TodoItem])
ErrorItem(String, String)
}

#
ThreadOptions

type ThreadOptions

Options for configuring a thread.

#
ThreadOptions::new

fn ThreadOptions::new(model? : String, sandbox_mode? : SandboxMode, working_directory? : String, skip_git_repo_check? : Bool, model_reasoning_effort? : ModelReasoningEffort, network_access_enabled? : Bool, web_search_enabled? : Bool, approval_policy? : ApprovalMode, additional_directories? : Array[String]) -> ThreadOptions

#
TodoItem

pub struct TodoItem {
text : String
completed : Bool
}

An item in the agent's to-do list.
impl Show for TodoItem
impl ToJson for TodoItem

#
Turn

#alias(RunResult)
pub struct Turn {
items : Array[ThreadItem]
final_response : String
usage : Usage?
}

Completed turn.

#
TurnOptions

type TurnOptions

Options for configuring a turn.

#
TurnOptions::new

fn TurnOptions::new(output_schema? : Json) -> TurnOptions

Params:
  • output_schema - JSON schema describing the expected agent output

#
Usage

pub struct Usage {
input_tokens : Int
cached_input_tokens : Int
output_tokens : Int
}

Describes the usage of tokens during a turn.
impl ToJson for Usage

#
UserInput

pub(all) enum UserInput {
Text(String)
LocalImage(String)
}

Powered by MoonBit

Site sourceReport issuePackagesBuild queueSkillsStatistics

Ā© 2026 mooncakes.io