ObanCodex.Agent.Instance (oban_codex v0.6.0)

Copy Markdown View Source

One agent as one :gen_statem process, its asynchronous turns run as Oban jobs. Part of the experimental agent layer (see ObanCodex.Agent).

The process never blocks on Codex: a prompt enqueues an ObanCodex.Worker job and the machine parks in :running until the worker's callbacks route the outcome back as a {:job_finished, payload} cast (via ObanCodex.Agent.job_finished/3). States:

  • :idle -- ready for a prompt
  • :running -- an Oban job is in flight; further prompts are :postponed and a :state_timeout watchdog guards against a turn that never reports back. A retryable failed attempt (ObanCodex.Agent.job_retrying/3) keeps the machine here and re-arms the watchdog, so :job_timeout should exceed one attempt's backoff plus its execution
  • :waiting_for_user -- the last turn asked a question (structured-output directive "ask_user"); the next prompt is treated as the answer and resumes the Codex session
  • :awaiting_permission -- the last turn requested approval (directive "request_permission"); approve resumes the session with the action (under :approved_args, plus any per-approval :args, see below), reject records the denial and returns to :idle (or :paused with a safe-boundary latch), and prompts are :postponed until the gate clears. An approved turn that fails or hits the watchdog RE-GATES (same description, fresh id, {:approval_incomplete, reason} in history): the work was approved but not completed, and a re-approval resumes it
  • :paused -- lockdown via :emergency_pause, or a correlated pause_after_turn or quiesce latch applied at a safe terminal boundary; every call is refused until an explicit resume

Every state change atomically synchronizes the registry value -- the state atom, paired with the pending action or question in the gated states -- so ObanCodex.Agent.status/1 reads both without messaging the process. Each change also emits [:oban_codex, :agent, :transition] telemetry with %{agent_id, from, to} metadata (state atoms). Turn transitions also carry the wrapper-owned generation, turn and arc identities, plus the optional application correlation_id. Lifecycle telemetry carries the host's optional config_revision. Deferred-pause transitions add :cause, :pause_reason, and :pause_action; gate decisions also add :gate_outcome and, for permission gates, :action_id.

Codex thread ids are retained in bounded, host-named conversation arcs. Prompts that omit an arc use "default", preserving the original one-agent, one-conversation behavior. Turn count and accumulated cost ride in the data (see ObanCodex.Agent.info/1).

Config

start_agent/2 takes a keyword list or map:

  • :args -- default Codex args merged under every turn's prompt (build with ObanCodex.Args.defaults/1; string keys); default %{}
  • :approved_args -- Codex args merged over :args for approve continuations ONLY, so conversational approval actually unlocks something -- e.g. %{"sandbox" => "workspace_write"} with %{"approval_policy" => "never"}. Normal turns never carry these. Default %{}.
  • :worker -- the Oban worker module for turns; default ObanCodex.Agent.Job
  • :oban -- the Oban instance name to insert into; default Oban
  • :job_timeout -- the :running watchdog in milliseconds; default 60000
  • :max_history -- cap on retained history entries (newest win); default 500, so an always-on agent's event log cannot grow without bound
  • :session_arcs -- optional %{arc_id => session_id} seed map for restoring provider conversations after this process restarts; default %{}
  • :max_session_arcs -- maximum retained provider handles; least-recently used inactive arcs are evicted as new ones arrive; default 32
  • :config_revision -- optional non-empty opaque string supplied by the host. It is exposed in Agent info, job metadata, and lifecycle telemetry so the host can attribute work to the effective configuration that produced it; default nil
  • :enqueue_fun -- a 2-arity (args, meta) -> {:ok, term} | {:error, term} override of the enqueue itself, for tests (no Oban, no DB)

Summary

Functions

child_spec(arg)

start_link(agent_id, config)