GenAgent.Backend implementation backed by CodexWrapper.
CodexWrapper 0.5.1 streams NDJSON while closing CLI stdin. This
backend forwards translated events as they arrive, so
handle_stream_event/2 can observe progress during a turn.
Session continuation
Codex reports its persistent thread identifier as thread_id in the
first thread.started event of a turn, not in the terminal
turn.completed event. The EventTranslator captures it and
injects it into the :result event as session_id. This backend's
update_session/2 then records it on the session struct, and the
next turn is dispatched via ExecResume with that id.
Options accepted by start_session/1
Config-level (forwarded to CodexWrapper.Config.new/1):
:binary,:working_dir(aliased as:cwd),:env,:timeout,:verbose
Exec-level (forwarded to CodexWrapper.Exec):
:model,:sandbox,:approval_policy,:full_auto,:dangerously_bypass_approvals_and_sandbox,:skip_git_repo_check,:ephemeral,:config_overrides,:enabled_features,:disabled_features,:images
Options that cannot be preserved on exec resume (:cd,
:add_dirs, :search, :output_schema) are rejected by
start_session/1. Use :working_dir / :cwd for a directory that
persists across turns. Session options are translated into supported
resume arguments; :sandbox and :approval_policy use config
overrides because resume does not accept their exec flags.
Backend-only:
:exec_fn-- a 2-arity function(prompt, session) -> {:ok, Enumerable.t()} | {:error, term()}that replaces the defaultExec/ExecResumedispatch. Intended for tests.
Codex has no equivalent of Claude's --system-prompt; if you need
system-level instructions, pass them via AGENTS.md in the working
directory or through Codex's configuration layer.
Summary
Types
@type t() :: %GenAgent.Backends.Codex{ config: CodexWrapper.Config.t(), exec_fn: (String.t(), t() -> {:ok, Enumerable.t()} | {:error, term()}), exec_opts: keyword(), thread_id: String.t() | nil }