codex-agent-sdk-go

mcp
Guvenlik Denetimi
Uyari
Health Uyari
  • License — License: MIT
  • Description — Repository has a description
  • Active repo — Last push 0 days ago
  • Low visibility — Only 5 GitHub stars
Code Gecti
  • Code scan — Scanned 12 files during light audit, no dangerous patterns found
Permissions Gecti
  • Permissions — No dangerous permissions requested

Bu listing icin henuz AI raporu yok.

SUMMARY

Go SDK for the OpenAI Codex CLI app-server transport with JSON-RPC 2.0, typed events, approvals, MCP config, and structured output.

README.md

Codex Agent SDK for Go

Go 1.25
License: MIT

Go SDK for the OpenAI Codex CLI app-server transport — spawns codex app-server as a child process, speaks JSON-RPC 2.0 over stdio, and exposes a typed Go API for threads, turns, provider-created child activity, streaming events, approvals, background terminals, and MCP configuration.

Status: preview (v0.x). API may change before v1.0.0. Feedback welcome.

Sibling SDK: claude-agent-sdk-go does the same thing for the Claude Code CLI.

Why this SDK?

Codex's app-server exposes a JSON-RPC 2.0 protocol over stdio — bidirectional, stateful, with server-initiated approval requests. Consuming it directly means handling line-framing with a 2 MiB minimum buffer, demultiplexing three request shapes (responses, notifications, server-initiated requests), serializing concurrent turns to preserve event boundaries, and mapping schema-covered notification methods to typed Go events. This SDK handles all of that and exposes a clean, typed API.

Feature matrix

Feature Status on current mainline
codex app-server transport
codex exec --json one-shot ❌ deferred to v2
Thread start / resume / fork / archive / list
thread.Run() (buffered) + thread.RunStreamed() (channel)
Streaming events: turn/item lifecycle, token usage, thread lifecycle/goals, process output, model/account/warning events, hooks, realtime, and unknown-event fallback
ThreadItem variants: agentMessage, userMessage, commandExecution, fileChange, mcpToolCall, webSearch, memoryRead/Write, plan, reasoning, subAgentActivity, systemError
Input variants: text, localImage
Sandbox modes: read-only, workspace-write, danger-full-access
Approval policies: untrusted, on-failure, on-request, granular, never
Approval callback (server-initiated request → caller decides)
MCP server config (stdio + streamable HTTP)
JSON-schema structured output
Typed errors with Is*() helpers
Turn interrupt
Client-wide thread event stream, including provider-created child threads ✅ schema-gated
Background terminal inventory, exact termination request, and stop-all request ✅ schema-gated experimental API
CLI discovery + soft version probe
Goroutine leak detection (goleak)
Hook observer events (HookStarted / HookCompleted) ✅ v0.2.0 — via WithHooks(true)
Programmatic Go hook callbacks (shim bridge, auto-wired) LIMITED v0.3.0 — WithHookCallback(h) manages hooks.json; Codex app-server 0.129–0.144 discovers but does not run ephemeral untrusted hooks. See docs/hooks.md.
Slash-command equivalents (Compact, SetModel, ListMCPServerStatus, Review, etc.) ✅ v0.4.0 — typed methods plus GitDiff / InitAgentsMD helpers. See docs/commands.md.
Native FFI (CGO) ❌ deferred

Prerequisites

  • Go 1.25+
  • Codex CLI installed: npm install -g @openai/codex (or your distro's equivalent)
    • Recommended/tested CLI: 0.144.1 (verified 2026-07-12); older versions run with a soft warning and receive version-compatible hook flags.
    • Optional child-agent and background-terminal controls are enabled only when DiscoverRuntimeFeatures proves their exact methods and response shapes from the installed CLI schemas. A version string alone never enables them.
  • Auth (one of):
    • OPENAI_API_KEY environment variable (pay-per-token)
    • ~/.codex/auth.json (ChatGPT Plus/Pro subscription; run codex login once outside the daemon)

Install

go get github.com/hishamkaram/codex-agent-sdk-go

Quick start

One-shot query

package main

import (
	"context"
	"fmt"
	"log"

	codex "github.com/hishamkaram/codex-agent-sdk-go"
	"github.com/hishamkaram/codex-agent-sdk-go/types"
)

func main() {
	ctx := context.Background()
	opts := types.NewCodexOptions().
		WithSandbox(types.SandboxReadOnly).
		WithApprovalPolicy(types.ApprovalOnRequest)

	events, err := codex.Query(ctx, "Summarize the repo in the current directory", opts)
	if err != nil {
		log.Fatal(err)
	}
	for event := range events {
		switch e := event.(type) {
		case *types.ItemCompleted:
			if msg, ok := e.Item.(*types.AgentMessage); ok {
				fmt.Println(msg.Text)
			}
		case *types.TurnCompleted:
			fmt.Printf("Tokens: in=%d out=%d\n", e.Usage.InputTokens, e.Usage.OutputTokens)
		}
	}
}

Interactive multi-turn client

client, err := codex.NewClient(ctx, opts)
if err != nil { log.Fatal(err) }
if err := client.Connect(ctx); err != nil { log.Fatal(err) }
defer client.Close(context.Background())

thread, err := client.StartThread(ctx, &types.ThreadOptions{Cwd: "/my/project"})
if err != nil { log.Fatal(err) }

events, _ := thread.RunStreamed(ctx, "Make a plan to fix the CI failure", nil)
for event := range events { /* ... */ }

events2, _ := thread.RunStreamed(ctx, "Now implement the plan", nil)
for event := range events2 { /* ... */ }

Provider-created children and background terminals

runtimeOpts := types.NewCodexOptions().WithExperimentalAPI(true)
features, err := codex.DiscoverRuntimeFeatures(ctx, runtimeOpts)
if err != nil { log.Fatal(err) }

runtimeClient, err := codex.NewClient(ctx, runtimeOpts)
if err != nil { log.Fatal(err) }
if err := runtimeClient.Connect(ctx); err != nil { log.Fatal(err) }
defer runtimeClient.Close(context.Background())

eventsCtx, stopEvents := context.WithCancel(ctx)
runtimeEvents, err := runtimeClient.SubscribeThreadEvents(eventsCtx, 256)
if err != nil { log.Fatal(err) }
runtimeDone := make(chan struct{})
go func() {
    defer close(runtimeDone)
    for event := range runtimeEvents {
        // event.ThreadID also identifies provider-created child threads.
        // Treat event.Err as a terminal stream gap.
        fmt.Printf("thread=%s event=%T err=%v\n", event.ThreadID, event.Event, event.Err)
    }
}()
defer func() { stopEvents(); <-runtimeDone }()

runtimeThread, err := runtimeClient.StartThread(ctx, nil)
if err != nil { log.Fatal(err) }

if features.BackgroundTerminalInventory {
    terminals, err := runtimeClient.ListBackgroundTerminals(ctx, runtimeThread.ID())
    if err != nil { log.Fatal(err) }
    if len(terminals) > 0 && features.BackgroundTerminalTerminate {
        if err := runtimeClient.TerminateBackgroundTerminal(
            ctx, runtimeThread.ID(), terminals[0].ProcessID,
        ); err != nil { log.Fatal(err) }
    }
}

Client.InterruptThreadTurn, Client.TerminateBackgroundTerminal, and
Client.CleanBackgroundTerminals expose provider transport primitives. A
successful RPC means the provider accepted the request; it does not by itself
prove terminal state. Wait for the correlated child lifecycle or a fresh
background-terminal inventory before updating user-visible state. In
particular, interrupting a child turn does not guarantee that its spawning
parent has stopped waiting, so do not present it as delegated-agent Stop
without proving both lifecycles against the installed CLI.

Approval callback

opts = opts.WithApprovalCallback(func(ctx context.Context, req types.ApprovalRequest) types.ApprovalDecision {
	if r, ok := req.(*types.CommandExecutionApprovalRequest); ok {
		if isSafeCommand(r.Command) {
			return types.ApprovalAccept{}
		}
	}
	return types.ApprovalDeny{Reason: "not on allowlist"}
})

What it does

  • Spawns codex app-server as a subprocess with stdin/stdout pipes
  • Frames JSON-RPC 2.0 messages with LF termination (jsonrpc field omitted on wire)
  • Demultiplexes responses (id → pending chan), notifications (→ events chan), server-initiated requests (→ approval callback)
  • Serializes all stdin writes via single stdinMu to prevent frame interleave
  • Serializes per-thread Run() calls via turnMu to preserve turn boundaries
  • Maintains a 2 MiB read buffer for large notification payloads
  • Translates raw JSON-RPC notifications into typed Go events with schema drift checks for Codex upgrades
  • Publishes a bounded client-wide event stream so provider-created child threads remain observable
  • Discovers optional runtime controls from the installed app-server's generated schemas
  • Handles CLI discovery (PATH, ~/.codex/bin, brew, npm install paths) and soft version probe
  • Emits structured logs via zap

What it does NOT do (current preview)

  • codex exec --json (fire-and-forget) transport — the app-server path is the only one implemented
  • Codex-as-MCP-server mode (experimental upstream)
  • Dynamic OpenAI pricing table (use static rates in this SDK; fetch at integration time if needed)

Docs

  • docs/getting-started.md — install, first query, multi-turn, streaming, resume, approvals in ~2 pages
  • docs/architecture.md — the four layers, dispatcher goroutine, turn lock, concurrency contract, shutdown ladder
  • docs/wire-protocol.md — JSON-RPC method reference, wire quirks (flat vs nested IDs, per-item delta methods), known-unknown methods
  • docs/approvals.md — approval request/decision taxonomy, sandbox × policy matrix, deadlock warning
  • docs/hooks.md — observer mode + auto-wired programmatic callbacks via WithHookCallback (v0.3.0)
  • docs/commands.md — slash-command-equivalent typed methods (Compact, SetModel, Rollback, StartReview, …) and local-helper parity (GitDiff, InitAgentsMD) (v0.4.0)

Examples

Eight runnable examples under examples/:

Example What it shows
simple_query One-shot Query()
streaming Multi-turn RunStreamed + delta streaming
resume Persistent thread across process restarts
fork Branching a thread
with_approvals Command + file approval callback
with_mcp Registering MCP servers (stdio + HTTP)
with_hooks Observe HookStarted/HookCompleted events from your configured hooks
structured_output JSON-schema-constrained final response

Build all: make examples. Run any: go run ./examples/<name>.

License

MIT — see LICENSE.

Yorumlar (0)

Sonuc bulunamadi