codexappserver

package
v0.1.0-beta.3 Latest Latest
Warning

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

Go to latest
Published: Sep 7, 2026 License: Apache-2.0 Imports: 15 Imported by: 0

Documentation

Overview

Package codexappserver speaks the Codex app-server JSON-RPC protocol to a private stdio child process.

Codex refuses to run a non-managed lifecycle hook until the exact hook definition has been reviewed and trusted, recording that decision as a content hash under `hooks.state` in the user's `config.toml`. A hook that has never been trusted is parsed, listed, and silently skipped, so an installer that only writes `hooks.json` reports success for a binding that can never fire.

The hash is derived from a normalized hook identity, not from the bytes on disk — Codex clamps a SessionEnd timeout before hashing, for one — so reproducing it locally would be wrong in ways that only surface as silence. This package asks Codex for the value instead: `hooks/list` returns each hook's `key`, `currentHash`, and `trustStatus`, and `config/batchWrite` persists trust through Codex's own writer under an optimistic-concurrency version. Nothing here parses or rewrites `config.toml`.

The app-server CLI surface is marked experimental. Every call here is best-effort by contract: callers degrade to reporting an untrusted binding rather than failing setup (ADR-051).

Index

Constants

View Source
const (
	TrustManaged   = "managed"
	TrustTrusted   = "trusted"
	TrustUntrusted = "untrusted"
	TrustModified  = "modified"
)

Trust states reported by hooks/list.

Variables

View Source
var ErrCodexNotFound = errors.New("no Codex executable found")

ErrCodexNotFound reports that no Codex executable could be located. It is an ordinary condition on a machine that has never installed Codex, not a fault.

Functions

func Home

func Home(override string) (string, error)

Home resolves the Codex home directory the same way Codex does. An explicit override wins so tests never touch the contributor's real Codex state.

func Locate

func Locate() (string, error)

Locate finds a Codex executable. Codex ships both as a standalone CLI on PATH and bundled inside the desktop application, and only the bundle is present on a machine that installed Codex through ChatGPT.app.

Types

type Client

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

Client owns one `codex app-server` child process. It is not safe for concurrent use; calls are serialized by the caller or by mu.

func Dial

func Dial(ctx context.Context, options Options) (*Client, error)

Dial starts a private app-server and completes the initialize handshake.

This deliberately spawns its own stdio child rather than attaching to a shared daemon: the Codex desktop application runs its app-server as a private stdio child with no socket to join, and the shared daemon requires the standalone Codex installer. A private child observes no live runtime state; it can inspect persisted thread metadata and read or write configuration.

func (*Client) Close

func (c *Client) Close() error

Close shuts the child process down. It is safe to call more than once.

func (*Client) ListHooks

func (c *Client) ListHooks(ctx context.Context, cwds []string) ([]Hook, error)

ListHooks returns every hook Codex resolves for the given working directories. An empty cwds list asks Codex for its own default.

func (*Client) ListThreads

func (c *Client) ListThreads(ctx context.Context, cwd string, limit int) ([]Thread, error)

ListThreads returns at most limit unarchived threads whose recorded cwd exactly matches cwd, newest update first. Callers ask for two because that is sufficient to distinguish a unique candidate from an ambiguous checkout.

func (*Client) ThreadReads

func (c *Client) ThreadReads(ctx context.Context, id string) (cwd string, reads []ThreadRead, err error)

ThreadReads returns the working directory of a stored Codex task and the file reads its own classifier attributed to completed commands.

This reads a task without resuming or taking ownership of it: `thread/read` is documented as reading a stored task by id, and a separate app-server process observing a task the desktop application is still running reports it as `notLoaded`. Overgent never issues `thread/start`, `thread/resume`, `turn/start`, or any approval, so it observes the member's session without participating in it (ADR-051, ADR-052).

The decoded shape deliberately has no field for `command` or `aggregatedOutput`. Those cross the wire from Codex and are dropped during decoding rather than held and discarded later, so a raw command string or captured output never reaches an Overgent structure at all.

func (*Client) Trust

func (c *Client) Trust(ctx context.Context, edits []TrustEdit, expectedVersion string) error

Trust records the given hooks as trusted in the user's config.toml through Codex's own configuration writer.

Every edit is a narrow upsert of a single `hooks.state."<key>".trusted_hash` value. Overgent never serializes the surrounding document, so a concurrent write by the Codex desktop application cannot be clobbered by this call, and expectedVersion turns a lost update into a returned error rather than silent damage.

func (*Client) UserConfigVersion

func (c *Client) UserConfigVersion(ctx context.Context) (string, error)

UserConfigVersion returns the version of the user config layer, used as the optimistic-concurrency token for a write. An empty result means the layer was not reported and the write must proceed without a compare-and-swap.

type Hook

type Hook struct {
	Key         string `json:"key"`
	EventName   string `json:"eventName"`
	HandlerType string `json:"handlerType"`
	Command     string `json:"command"`
	Source      string `json:"source"`
	SourcePath  string `json:"sourcePath"`
	IsManaged   bool   `json:"isManaged"`
	Enabled     bool   `json:"enabled"`
	CurrentHash string `json:"currentHash"`
	TrustStatus string `json:"trustStatus"`
}

Hook is one entry from hooks/list. Only the fields Overgent acts on are decoded; the protocol carries more and may add more.

func (Hook) Trusted

func (h Hook) Trusted() bool

Trusted reports whether Codex will run this hook as configured. A managed hook is trusted by policy and must never be rewritten by Overgent.

type Options

type Options struct {
	Executable string
	CodexHome  string
	// ClientVersion is reported to Codex during initialize.
	ClientVersion string
}

Options configure a Dial. Executable and CodexHome are resolved when empty.

type Thread

type Thread struct {
	ID        string `json:"id"`
	CWD       string `json:"cwd"`
	UpdatedAt int64  `json:"updatedAt"`
}

Thread is the identity and recency metadata Overgent needs from `thread/list`. Conversation previews, paths to rollout files, and other content-bearing fields are intentionally not decoded.

type ThreadRead

type ThreadRead struct {
	// ItemID is the app-server item this read came from. Callers deduplicate on
	// it so re-reading a thread republishes nothing.
	ItemID string
	// Path is absolute, as Codex reports it. It is meaningless until the caller
	// has checked it against a registered repository root.
	Path string
}

ThreadRead is one file read that Codex's own classifier attributed to a command it ran. It is vendor-inferred evidence, not an observation of the filesystem: OpenAI describes command actions as a best-effort understanding of what a command will do, and a compound command that genuinely reads files can still classify as `unknown` (ADR-052).

type TrustEdit

type TrustEdit struct {
	Key  string
	Hash string
}

TrustEdit names one hook whose current hash should be recorded as trusted.

Jump to

Keyboard shortcuts

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