ReqManagedAgents.Session (ReqManagedAgents v0.6.2)

Copy Markdown View Source

The one agent loop, provider- and transport-agnostic.

A Session drives any ReqManagedAgents.Provider to completion: invoke a turn → normalize → run the return-of-control tools locally via the :handler → resume → repeat until a terminal. The loop host runs the agent loop — a managed provider server-side, or ReqManagedAgents.Providers.Local in-process. The provider's mode/0 (:streaming push or :request_response pull) only changes how a turn's events are acquired; the loop, the result shape, and the raw-event passthrough are identical across providers.

Pick a provider — ReqManagedAgents.Providers.ClaudeManagedAgents (streaming), ReqManagedAgents.Providers.BedrockAgentCore (request/response), or ReqManagedAgents.Providers.Local (in-process) — and:

# synchronous run-to-completion
{:ok, %ReqManagedAgents.SessionResult{terminal: t, stop_reason: r, events: raw}} =
  ReqManagedAgents.Session.run(provider, handler: MyTools, prompt: "Hi", ...)

# live, long-lived (stays alive after a terminal; `:notify` gets {:managed_agents_session, %ReqManagedAgents.SessionResult{}})
{:ok, pid} = ReqManagedAgents.Session.start_link(provider, handler: MyTools, notify: self(), ...)
ReqManagedAgents.Session.message(pid, "follow-up")

Required opts: :handler (a ReqManagedAgents.Handler module or a 3-arity fn). Optional: :context, :prompt, :outcome (a %ReqManagedAgents.Outcome{} or a map with the same keys %{description:, rubric:, max_iterations:} — kicks off a user.define_outcome graded session instead of a user.message; mutually exclusive with :prompt, outcome wins; {:error, :outcome_unsupported} on providers without native support), :timeout, :max_turns, :notify, :name, :telemetry_metadata, :turn_guard (a 1-arity fun invoked after each turn's usage accumulation with %{usage: %ReqManagedAgents.Usage{}, turns: n, session_id: id}, returning :cont or {:halt, reason}; on halt the run stops with {:error, {:halted, reason}} and a :terminated result is notified — usage/turns accumulate within the current request, the same scope as :max_turns; the guard runs before the max_turns check and wins when both would trip on the same turn; it fires on terminal-tool re-prompt turns, whose turns counter keeps incrementing normally; guards must not raise — a raising guard crashes the session and surfaces as {:error, {exception, stack}} to run/2 rather than producing a {:halt, …}), :require_terminal_tool + :terminal_tool + :max_reprompts (default 2): an :end_turn that never called terminal_tool is re-driven with a re-prompt; exhausted re-prompts finish with stop_reason: :no_terminal_tool. Re-prompt turns count against :max_turns. For long AgentCore runs set :timeout (the end-to-end run budget, default 600_000 ms) at or above the server-side budget — a run/2 timeout returns {:error, :timeout} and tears down the in-flight invoke client-side (the poll task and its HTTP stream are shut down). The server may still run the already-received invocation to its own limit: the server-side timeoutSeconds remains the authoritative server budget. Transport liveness is guarded per turn by :idle_timeout and total cost by the :timeout_seconds/:max_iterations/:max_tokens per-invocation overrides (Bedrock AgentCore only). Provider-specific opts (e.g. :agent_id/:environment_id, :harness_arn/:runtime_session_id, :session_id to resume) are forwarded to the provider's open/2.

Summary

Functions

Return a child_spec for a live session.

Send a follow-up user message into a running live session.

Post a pre-built raw user event (e.g. Event.tool_confirmation/2, a mid-session Event.define_outcome/3) into a running live session. The event is pushed verbatim — no turn accounting or accumulator reset. Streaming providers only: {:error, :unsupported} on :request_response providers (their input is consumed by poll_turn/2, there is no out-of-band channel).

Start a long-lived session. Unlike run/2, it stays alive after a terminal for follow-ups.

Functions

child_spec(init_arg)

Return a child_spec for a live session.

message(pid, text)

@spec message(pid(), String.t()) :: :ok

Send a follow-up user message into a running live session.

run(provider, opts)

@spec run(
  module(),
  keyword()
) :: {:ok, ReqManagedAgents.SessionResult.t()} | {:error, term()}

send_event(pid, event)

@spec send_event(pid(), map()) :: :ok | {:error, term()}

Post a pre-built raw user event (e.g. Event.tool_confirmation/2, a mid-session Event.define_outcome/3) into a running live session. The event is pushed verbatim — no turn accounting or accumulator reset. Streaming providers only: {:error, :unsupported} on :request_response providers (their input is consumed by poll_turn/2, there is no out-of-band channel).

start_link(provider, opts)

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

Start a long-lived session. Unlike run/2, it stays alive after a terminal for follow-ups.