CodingAgent.Session (CodingAgent v0.1.0)

Copy Markdown View Source

A GenServer that owns one agent's conversation and drives its agentic loop: send the context to the model, execute any tool calls it asks for, feed the results back, and repeat until the model produces a final answer (or :max_turns is hit).

Each session is a process, so many independent coding-agent conversations can run concurrently under CodingAgent.SessionSupervisor.

The read/write/edit tools never touch the real filesystem -- they operate on an in-memory map of path => content that lives for the duration of the session. send_message/3 returns that map alongside the reply so the caller decides what to do with it (write it to disk, diff it, send it elsewhere, discard it, ...).

Summary

Functions

Returns a specification to start this module under a supervisor.

Returns the full conversation context so far.

Returns the session's current in-memory workspace (path => content).

Sends a user message and runs the agent loop to completion.

Starts a session.

Starts a session under CodingAgent.SessionSupervisor, registered under id in CodingAgent.SessionRegistry so it can be reached as {:via, Registry, {CodingAgent.SessionRegistry, id}}.

Sends a user message and runs the agent loop to completion, just like send_message/3, but streams text as the model produces it.

Functions

child_spec(init_arg)

Returns a specification to start this module under a supervisor.

See Supervisor.

context(session)

@spec context(GenServer.server()) :: ReqLLM.Context.t()

Returns the full conversation context so far.

files(session)

@spec files(GenServer.server()) :: %{required(String.t()) => String.t()}

Returns the session's current in-memory workspace (path => content).

send_message(session, text, timeout \\ 120_000)

@spec send_message(GenServer.server(), String.t(), timeout()) ::
  {:ok, String.t(), %{required(String.t()) => String.t()}} | {:error, term()}

Sends a user message and runs the agent loop to completion.

Returns {:ok, reply_text, files}, where files is the session's in-memory workspace (a path => content map) after every tool call from this turn has been applied.

start_link(opts)

@spec start_link(keyword()) :: GenServer.on_start()

Starts a session.

Options:

  • :model - a req_llm model id, e.g. "openrouter:anthropic/claude-sonnet-4.5" (defaults to CodingAgent.OpenRouter.default_model/0)
  • :system_prompt - extra system prompt text; the skill catalog (if any) is appended automatically
  • :skills_dirs - list of directories to scan for SKILL.md files
  • :tools - list of ReqLLM.Tool structs (defaults to CodingAgent.Tools.defaults/1 built from the discovered skills)
  • :max_turns - safety cap on tool-call round-trips (default 10)
  • :files - initial in-memory workspace as a path => content map, seeded before the read/write/edit tools see it (default %{})
  • :name - optional GenServer name, e.g. {:via, Registry, ...}
  • any other option is forwarded to ReqLLM.generate_text/3 (e.g. :temperature, :max_tokens)

start_session(id, opts \\ [])

@spec start_session(
  term(),
  keyword()
) :: {:ok, pid()} | {:error, term()}

Starts a session under CodingAgent.SessionSupervisor, registered under id in CodingAgent.SessionRegistry so it can be reached as {:via, Registry, {CodingAgent.SessionRegistry, id}}.

stream_message(session, text, on_chunk, timeout \\ 120_000)

@spec stream_message(GenServer.server(), String.t(), (String.t() -> any()), timeout()) ::
  {:ok, String.t(), %{required(String.t()) => String.t()}} | {:error, term()}

Sends a user message and runs the agent loop to completion, just like send_message/3, but streams text as the model produces it.

on_chunk is called with each text chunk (a String.t()) as it arrives, across every turn of the loop -- including turns that precede a tool call. It runs inside the session's own process, so it blocks the session for the duration of the call; this mirrors send_message/3, which already processes a whole turn synchronously.

Returns the same {:ok, reply_text, files} / {:error, reason} shape as send_message/3 once the whole turn (including any tool round-trips) has finished.

via(id)

@spec via(term()) :: GenServer.server()