ClaudeWrapper.Session (ClaudeWrapper v0.10.0)

Copy Markdown View Source

Multi-turn session management.

Wraps repeated Query.execute/2 calls, automatically threading the session_id returned by the CLI so each turn continues the same conversation.

Usage

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

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

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

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

Summary

Functions

Branch the conversation into a new, independent session.

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

Types

t()

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

Functions

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

@spec fork(t(), String.t() | ClaudeWrapper.Prompt.t(), keyword()) ::
  {:ok, t(), ClaudeWrapper.Result.t()} | {:error, ClaudeWrapper.Error.t()}

Branch the conversation into a new, independent session.

Resumes the session's current session_id with --fork-session and sends prompt. The CLI clones the prior context into a new session; the returned Session is bound to that new forked id, while the original struct passed in is left completely untouched -- you can keep using it to continue the original thread.

prompt accepts a string or a ClaudeWrapper.Prompt.t/0, exactly like send/3.

Returns {:error, %ClaudeWrapper.Error{kind: :no_session}} if the session has no established session_id yet (nothing to fork from -- send at least one turn first).

Example

{:ok, session, _} = ClaudeWrapper.Session.send(session, "Draft a plan")
{:ok, branch, _} = ClaudeWrapper.Session.fork(session, "Now try a riskier variant")
# `session` still points at the original thread; `branch` is the fork.

last_result(session)

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

Get the last result, if any.

new(config, opts \\ [])

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

Create a new session with the given config.

Options

Any option accepted by ClaudeWrapper.query/2 (query-level options only):

  • :model - Model name
  • :system_prompt - System prompt
  • :max_turns - Max turns per send
  • :permission_mode - Permission mode
  • :max_budget_usd - Budget limit
  • :effort - Effort level

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

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

Resume an existing session by ID.

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

@spec send(t(), String.t() | ClaudeWrapper.Prompt.t(), keyword()) ::
  {:ok, t(), ClaudeWrapper.Result.t()} | {:error, ClaudeWrapper.Error.t()}

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

The first turn creates a new session. Subsequent turns use --resume with the session ID from the first result.

prompt is either a plain string or a ClaudeWrapper.Prompt.t/0. A Prompt is rendered (which expands attached files and git diffs) before the turn runs; a render failure short-circuits to {:error, %ClaudeWrapper.Error{}} without launching the CLI.

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 result.

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 session.

total_cost(session)

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

Get the total cost across all turns.

turn_count(session)

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

Get the number of completed turns.

turns(session)

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

Get the conversation history (list of results).