Documentation
¶
Overview ¶
Package codex implements a minimal client for the Codex CLI's app-server, which speaks JSON-RPC 2.0 over newline-delimited JSON on stdin/stdout.
This is deliberately not ACP: the Codex app-server has its own protocol (initialize → thread/start → turn/start → notifications → turn/completed), so it cannot reuse the coder/acp-go-sdk plumbing in internal/acp. Only the methods pi-go needs for a one-shot subagent turn are implemented; the rest are noted as TODOs below.
Index ¶
- Constants
- Variables
- type Client
- type ClientInfo
- type ClientOpts
- type ErrorParams
- type Event
- type FileChange
- type InitializeCaps
- type InitializeParams
- type InitializeResponse
- type Item
- type ItemParams
- type JSONRPCNotification
- type JSONRPCRequest
- type JSONRPCResponse
- type RPCError
- type ReviewStartParams
- type ReviewStartResponse
- type ReviewTarget
- type RunResult
- type Session
- type SessionOpts
- type Thread
- type ThreadStartParams
- type ThreadStartResponse
- type Turn
- type TurnCompletedParams
- type TurnInterruptParams
- type TurnStartParams
- type TurnStartResponse
- type TurnStartedParams
- type UserInput
Constants ¶
const ( MethodInitialize = "initialize" MethodInitialized = "initialized" MethodThreadStart = "thread/start" MethodTurnStart = "turn/start" MethodReviewStart = "review/start" MethodTurnInterrupt = "turn/interrupt" )
JSON-RPC methods sent by the client.
const ( NotifyThreadStarted = "thread/started" NotifyTurnStarted = "turn/started" NotifyItemStarted = "item/started" NotifyItemCompleted = "item/completed" NotifyTurnCompleted = "turn/completed" NotifyError = "error" )
Notification methods sent by the app-server.
const ( SandboxReadOnly = "read-only" SandboxWorkspaceWrite = "workspace-write" )
Sandbox modes accepted by thread/start.
const ( TurnInProgress = "inProgress" TurnCompleted = "completed" TurnInterrupted = "interrupted" TurnFailed = "failed" )
Turn statuses reported on the turn/completed notification.
const ( ItemAgentMessage = "agentMessage" ItemReasoning = "reasoning" ItemCommandExecution = "commandExecution" ItemFileChange = "fileChange" ItemMCPToolCall = "mcpToolCall" ItemDynamicToolCall = "dynamicToolCall" ItemWebSearch = "webSearch" ItemExitedReviewMode = "exitedReviewMode" )
Item types of the ThreadItem discriminated union.
const ( StatusSuccess = "success" StatusError = "error" )
Result statuses, mirroring internal/acp so the subagent dispatcher can treat codex and ACP results the same way.
const ( EventTypeMessage = "message" // agent text output EventTypeProgress = "progress" // reasoning / thinking EventTypeTool = "tool" // command execution, file changes, tool calls EventTypeStderr = "stderr" // one line of app-server stderr EventTypeError = "error" // server-reported error )
Event types emitted by a Session.
const ApprovalNever = "never"
ApprovalNever runs the thread autonomously: the app-server never asks the client to approve a command or edit. Subagents are unattended, so this is the only policy pi-go uses.
const BinaryName = "codex"
BinaryName is the expected name of the Codex CLI binary.
const EnvCodexCmd = "PI_CODEX_CMD"
EnvCodexCmd overrides the codex command. Format: "binary arg1 arg2 ..." or a bare "binary", in which case DefaultArgs are appended.
const PhaseFinalAnswer = "final_answer"
PhaseFinalAnswer marks the agentMessage item that carries the turn's answer, as opposed to intermediate analysis chatter.
const ReviewDeliveryInline = "inline"
ReviewDeliveryInline streams review findings as ordinary thread items rather than delivering them as a separate artifact.
const ReviewTargetUncommitted = "uncommittedChanges"
ReviewTargetUncommitted reviews the working tree's uncommitted changes.
Variables ¶
var DefaultArgs = []string{"app-server"}
DefaultArgs puts the codex CLI into app-server (JSON-RPC over stdio) mode.
var DefaultBinaryPaths = []string{
"codex",
".local/bin/codex",
"/usr/local/bin/codex",
"/usr/bin/codex",
"/opt/homebrew/bin/codex",
}
DefaultBinaryPaths lists common installation locations for the codex CLI.
Functions ¶
This section is empty.
Types ¶
type Client ¶
type Client struct {
// contains filtered or unexported fields
}
Client wraps a `codex app-server` subprocess speaking JSON-RPC 2.0 over newline-delimited JSON on stdin/stdout.
A single reader goroutine owns stdout: it routes responses to the pending request that carries the same ID and forwards notifications to the channel returned by notifications(). A second goroutine drains stderr, and a third reaps the process once both are done.
type ClientInfo ¶
type ClientInfo struct {
Title string `json:"title"`
Name string `json:"name"`
Version string `json:"version"`
}
ClientInfo describes the client to the app-server.
type ClientOpts ¶
type ClientOpts struct {
CWD string // working directory of the subprocess
Env []string // full environment for the subprocess (not merged with os.Environ)
Command []string // command override; when set, used verbatim (tests)
}
ClientOpts configures the app-server subprocess.
type ErrorParams ¶
type ErrorParams struct {
Error RPCError `json:"error"`
}
ErrorParams is the payload of the error notification.
type Event ¶
type Event struct {
Type string `json:"type"`
Content string `json:"content,omitempty"`
Error string `json:"error,omitempty"`
SessionID string `json:"session_id,omitempty"` // codex thread ID
}
Event is one streamed update from a codex turn.
type FileChange ¶
type FileChange struct {
Path string `json:"path"`
}
FileChange is one path touched by a fileChange item.
type InitializeCaps ¶
type InitializeCaps struct {
ExperimentalAPI bool `json:"experimentalApi"`
RequestAttestation bool `json:"requestAttestation"`
OptOutNotificationMethods []string `json:"optOutNotificationMethods"`
}
InitializeCaps declares which optional protocol features the client wants.
type InitializeParams ¶
type InitializeParams struct {
ClientInfo ClientInfo `json:"clientInfo"`
Capabilities InitializeCaps `json:"capabilities"`
}
InitializeParams identifies pi-go to the app-server and opts out of the high-frequency delta notifications: pi-go renders whole items, so streaming per-token deltas would only add churn on the notification channel.
type InitializeResponse ¶
type InitializeResponse struct {
UserAgent string `json:"userAgent"`
}
InitializeResponse is the app-server's reply to initialize.
type Item ¶
type Item struct {
Type string `json:"type"`
ID string `json:"id"`
Text string `json:"text,omitempty"` // agentMessage
Phase string `json:"phase,omitempty"` // agentMessage: "final_answer" | "analysis"
Command string `json:"command,omitempty"` // commandExecution
Status string `json:"status,omitempty"` // commandExecution, fileChange, mcpToolCall
ExitCode *int `json:"exitCode,omitempty"` // commandExecution
Tool string `json:"tool,omitempty"` // mcpToolCall, dynamicToolCall
Server string `json:"server,omitempty"` // mcpToolCall
Query string `json:"query,omitempty"` // webSearch
Review string `json:"review,omitempty"` // exitedReviewMode
Summary []string `json:"summary,omitempty"` // reasoning
Changes []FileChange `json:"changes,omitempty"` // fileChange
}
Item is a partially-typed ThreadItem. Only the fields pi-go renders are decoded; the discriminator is Type.
type ItemParams ¶
type ItemParams struct {
ThreadID string `json:"threadId"`
TurnID string `json:"turnId"`
Item Item `json:"item"`
}
ItemParams is the payload of the item/started and item/completed notifications.
type JSONRPCNotification ¶
type JSONRPCNotification struct {
JSONRPC string `json:"jsonrpc"`
Method string `json:"method"`
Params json.RawMessage `json:"params"`
}
JSONRPCNotification is a JSON-RPC 2.0 notification: a method call with no ID and therefore no response.
type JSONRPCRequest ¶
type JSONRPCRequest struct {
JSONRPC string `json:"jsonrpc"`
ID int `json:"id"`
Method string `json:"method"`
Params any `json:"params"`
}
JSONRPCRequest is a client→server JSON-RPC 2.0 request.
type JSONRPCResponse ¶
type JSONRPCResponse struct {
JSONRPC string `json:"jsonrpc"`
ID int `json:"id"`
Result json.RawMessage `json:"result,omitempty"`
Error *RPCError `json:"error,omitempty"`
}
JSONRPCResponse is a server→client response to a request, matched by ID.
type ReviewStartParams ¶
type ReviewStartParams struct {
ThreadID string `json:"threadId"`
Delivery string `json:"delivery"`
Target ReviewTarget `json:"target"`
}
ReviewStartParams starts a code review turn instead of a chat turn.
type ReviewStartResponse ¶
type ReviewStartResponse struct {
Turn Turn `json:"turn"`
}
ReviewStartResponse carries the turn created by review/start.
type ReviewTarget ¶
type ReviewTarget struct {
Type string `json:"type"`
}
ReviewTarget selects what the review covers.
type RunResult ¶
type RunResult struct {
Status string `json:"status"`
Result string `json:"result,omitempty"`
Error string `json:"error,omitempty"`
SessionID string `json:"session_id,omitempty"` // codex thread ID
Stderr string `json:"stderr,omitempty"` // captured app-server stderr
StopReason string `json:"stop_reason,omitempty"` // turn status: completed/interrupted/failed
}
RunResult is the terminal outcome of a codex turn.
type Session ¶
type Session struct {
// contains filtered or unexported fields
}
Session runs one turn (or review) against a `codex app-server` subprocess and streams the result back as Events.
All events are emitted by a single goroutine (loop), which also owns the terminal transition, so the events channel is closed exactly once and never written to afterwards.
func NewSession ¶
func NewSession(ctx context.Context, opts SessionOpts) (*Session, error)
NewSession spawns the app-server, opens a thread and starts the turn.
The notification handler is running before turn/start is sent: codex emits turn/started (and can emit the first items) as soon as the request lands, so a handler started afterwards would miss them.
func (*Session) Cancel ¶
Cancel interrupts the running turn and terminates the app-server.
The interrupt is sent first so codex gets a chance to unwind a running command, but Cancel does not wait for it to be acknowledged: the caller may hold a lock, and the kill that follows is the real backstop. The loop observes the client closing and finishes the session.
func (*Session) Events ¶
Events returns the streaming event channel. It is closed when the turn ends.
type SessionOpts ¶
type SessionOpts struct {
CWD string // working directory for the thread
Prompt string // task prompt (ignored when Review is set; review has no prompt)
Sandbox string // SandboxReadOnly or SandboxWorkspaceWrite
Env []string // full environment for the app-server subprocess
Review bool // run review/start instead of turn/start
Command []string // command override; used verbatim (tests)
}
SessionOpts configures a single codex turn.
type ThreadStartParams ¶
type ThreadStartParams struct {
CWD string `json:"cwd"`
Model *string `json:"model"`
ApprovalPolicy string `json:"approvalPolicy"`
Sandbox string `json:"sandbox"`
ServiceName string `json:"serviceName"`
Ephemeral bool `json:"ephemeral"`
}
ThreadStartParams opens a new thread rooted at CWD.
type ThreadStartResponse ¶
type ThreadStartResponse struct {
Thread Thread `json:"thread"`
}
ThreadStartResponse carries the ID of the newly created thread.
type Turn ¶
type Turn struct {
ID string `json:"id"`
Status string `json:"status"`
Error string `json:"error"`
}
Turn is a single agent turn within a thread.
type TurnCompletedParams ¶
TurnCompletedParams is the payload of the turn/completed notification. The thread ID matters: codex spawns child threads for collab/subagent work and their completions must not be mistaken for the outer turn finishing.
type TurnInterruptParams ¶
TurnInterruptParams cancels an in-flight turn.
type TurnStartParams ¶
type TurnStartParams struct {
ThreadID string `json:"threadId"`
Input []UserInput `json:"input"`
Model *string `json:"model"`
Effort *string `json:"effort"`
}
TurnStartParams sends user input to a thread and starts a turn.
type TurnStartResponse ¶
type TurnStartResponse struct {
Turn Turn `json:"turn"`
}
TurnStartResponse carries the turn created by turn/start.
type TurnStartedParams ¶
TurnStartedParams is the payload of the turn/started notification.