class

Anthropic::SessionRunner

Inherits Reference < Object

Runs local tools for a managed-agents session until it ends.

A synchronous port of the TypeScript SDK's SessionToolRunner: it streams session events, executes matching local tools for agent.tool_use / agent.custom_tool_use events, sends the results back, and returns when the session terminates or goes idle.

  • Reconnect: stream disconnects (and clean EOFs) reconnect with jittered exponential backoff (500ms start, 10s cap, reset on every received event). Permanent 4xx failures (any 4xx except 408/409/429) abort the run instead of reconnecting. max_reconnects bounds the loop (nil reconnects forever, like TypeScript).
  • Reconcile: after each (re)connect the runner re-reads recent history so tool calls emitted while disconnected still dispatch (already-answered calls and already-seen tool uses are skipped). A failed history read is non-fatal — the live stream carries on. A terminal event in history ends the run immediately, and a trailing end_turn idle re-arms the idle countdown.
  • Confirmation gating: calls the server gated (evaluated_permission == "ask", or an unrecognized permission) are held until a matching user.tool_confirmation verdict arrives. Only an explicit allow runs the tool; anything else (including server-side deny, which overrides any recorded verdict) resolves the call without executing or posting, exactly like TypeScript.
  • Idle stop: after a session.status_idle event with an end_turn stop reason, the run ends once max_idle elapses with no further activity (any other event disarms the countdown; held confirmations defer it). Defaults to 60 seconds like TypeScript; nil disables it. Note the countdown is only evaluated when events arrive or the stream errors, so detection latency is bounded by event cadence and the client's read timeout — and the event that reveals an expired countdown is swallowed (never routed to on_event).

Tool calls naming an unregistered tool are skipped without posting a result: the name belongs to another client servicing the session, and claiming it would corrupt the conversation.

Divergences from TypeScript: the runner is synchronous (no async iterator of outcomes — use on_call for per-call observability, and on_event for live-stream events, which does not refire for reconciled history), sends rely on the client's retry policy rather than a dedicated send-retry loop, and a failed send aborts the run instead of reconnecting (reconnecting would re-execute the tool). Skipped unregistered calls are marked answered locally (TypeScript leaves them pending for their owner; here that would re-surface them on every reconnect), and tools have no execution timeout (TypeScript aborts tools after 120s) — a hung tool blocks run.

runner = Anthropic::SessionRunner.new(
  client: client,
  session_id: "sess_123",
  tools: [my_tool],
  on_call: ->(call : Anthropic::SessionRunner::ToolCall) {
    puts "#{call.name}: executed=#{call.executed?}"
    nil
  }
)
terminal = runner.run

Constants

DEFAULT_MAX_IDLE = 60.seconds

Default idle stop after an end_turn idle.

Log = ::Log.for("anthropic-cr.session_runner")
RECONCILE_LIMIT = 1000

History window re-read after each (re)connect.

STREAM_BACKOFF_CAP = 10.seconds
STREAM_BACKOFF_START = 500.milliseconds

Reconnect backoff: 500ms start, doubling to a 10s cap (jittered).

TERMINAL_EVENT_TYPES = ["session.status_terminated", "session.deleted"]

Session event types that end a run.

Constructors

new(client : Client, session_id : String, tools : Array(Tool) = [] of Tool, max_events : Int32 | Nil = nil, betas : Array(String) = [] of String, workspace_id : String | Nil = nil, on_event : Proc(JSON::Any, Nil) | Nil = nil, on_call : Proc(ToolCall, Nil) | Nil = nil, max_idle : Time::Span | Nil = DEFAULT_MAX_IDLE, max_reconnects : Int32 | Nil = nil)
Source

Instance methods

run

Stream session events and execute local tools until the session reaches a terminal event. Returns the terminal event, or nil when the run stops another way (max_events / idle / reconnect limit interplay aside, the reconnect limit raises).

Source

Nested types