ObanCodex.Agent.Instance (oban_codex v0.3.2)

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, 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; 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).

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
  • :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)