CodexWrapper.Session (CodexWrapper v0.4.0)

Copy Markdown View Source

Multi-turn session management.

Wraps repeated Exec / ExecResume calls, automatically threading the session_id so each turn continues the same conversation.

The first turn uses Exec to create a new session. Subsequent turns use ExecResume with the session ID extracted from the JSON output.

Usage

config = CodexWrapper.Config.new(working_dir: "/path/to/project")
session = CodexWrapper.Session.new(config)

{:ok, session, result} = CodexWrapper.Session.send(session, "What files are in this project?")
{:ok, session, result} = CodexWrapper.Session.send(session, "Now add tests for lib/foo.ex")

# Access history
CodexWrapper.Session.turns(session)
#=> [%Result{...}, %Result{...}]

# Resume a previous session
session = CodexWrapper.Session.resume(config, "session-id-abc")

Summary

Functions

Archive this session (codex archive). Reversible with unarchive/1.

Permanently delete this session (codex delete).

Get the last result, if any.

Create a new session with the given config.

Resume an existing session by ID.

Send a message in the session. Returns the updated session and result.

Get the session ID (if established).

Send a message and return a stream of events.

Get the total cost across all turns.

Get the number of completed turns.

Get the conversation history (list of results).

Unarchive this session (codex unarchive).

Types

t()

@type t() :: %CodexWrapper.Session{
  config: CodexWrapper.Config.t(),
  exec_opts: keyword(),
  history: [CodexWrapper.Result.t()],
  session_id: String.t() | nil
}

Functions

archive(session)

@spec archive(t()) :: {:ok, String.t()} | {:error, :no_session | term()}

Archive this session (codex archive). Reversible with unarchive/1.

Returns {:error, :no_session} if no turn has run yet, since the session id only exists once the CLI has created it.

delete(session, opts \\ [])

@spec delete(
  t(),
  keyword()
) :: {:ok, String.t()} | {:error, :no_session | :confirmation_required | term()}

Permanently delete this session (codex delete).

Requires confirm: true, like CodexWrapper.Commands.Archive.delete/3 it delegates to; without it the CLI is not invoked. Returns {:error, :no_session} if no turn has run yet.

history(session)

@spec history(t()) :: [CodexWrapper.Result.t()]

Alias for turns/1.

last_result(session)

@spec last_result(t()) :: CodexWrapper.Result.t() | nil

Get the last result, if any.

new(config, opts \\ [])

@spec new(
  CodexWrapper.Config.t(),
  keyword()
) :: t()

Create a new session with the given config.

Options

Any option accepted by CodexWrapper.exec/2 (exec-level options only):

  • :model - Model name
  • :profile - Named config profile (--profile); first turn only, since codex exec resume does not accept --profile
  • :sandbox - Sandbox mode
  • :approval_policy - Approval policy (:untrusted, :on_request, :never)
  • :full_auto - Enable full-auto (boolean)
  • :dangerously_bypass_approvals_and_sandbox - Bypass all (boolean)
  • :skip_git_repo_check - Skip git check (boolean)

resume(config, session_id, opts \\ [])

@spec resume(CodexWrapper.Config.t(), String.t(), keyword()) :: t()

Resume an existing session by ID.

send(session, prompt, opts \\ [])

@spec send(t(), String.t(), keyword()) ::
  {:ok, t(), CodexWrapper.Result.t()} | {:error, term()}

Send a message in the session. Returns the updated session and result.

The first turn creates a new session via Exec. Subsequent turns use ExecResume with the session ID from the first result.

Internally uses --json to extract the session ID from NDJSON events.

session_id(session)

@spec session_id(t()) :: String.t() | nil

Get the session ID (if established).

stream(session, prompt, opts \\ [])

@spec stream(t(), String.t(), keyword()) :: {t(), Enumerable.t()}

Send a message and return a stream of events.

Returns {session, stream}. The returned session is the same session passed in -- this function does not thread session_id across turns. If you need multi-turn continuity, use send/3 instead, which runs the turn synchronously and updates session_id from the final events.

Use stream/3 when you want to observe events from a single turn (for example, to render intermediate output as the CLI produces it) and do not need to chain into a follow-up turn on the same thread.

total_cost(session)

@spec total_cost(t()) :: float()

Get the total cost across all turns.

Returns 0.0 as the Codex CLI does not currently expose per-turn cost. This is provided for API compatibility with future cost reporting.

turn_count(session)

@spec turn_count(t()) :: non_neg_integer()

Get the number of completed turns.

turns(session)

@spec turns(t()) :: [CodexWrapper.Result.t()]

Get the conversation history (list of results).

unarchive(session)

@spec unarchive(t()) :: {:ok, String.t()} | {:error, :no_session | term()}

Unarchive this session (codex unarchive).

Returns {:error, :no_session} if no turn has run yet.