Supervised logical sessions for locally installed coding-agent harnesses.
A session has a stable AgentHarness.SessionRef and one active turn at a
time. Provider adapters own the external CLI lifecycle and publish normalized
events through the session.
Summary
Functions
Waits for a turn's terminal event without a completion-subscription race.
Requests cancellation.
Returns provider capabilities for a live session.
Lists the currently live, PID-free session handles in deterministic ID order.
Lists stored session snapshots and whether each has a live SessionServer.
Monitors a live session from the calling process.
Cascades deletion of a non-live session aggregate from a Store.
Responds to a structured provider request with exactly-once local ownership.
Starts a supervised logical session for provider.
Accepts one turn locally and starts provider admission asynchronously.
Returns the live session snapshot.
Stops an idle session.
Returns a replay-followed-by-live stream for a turn.
Subscribes the calling process, or :pid, to session or turn events.
Removes an event subscription.
Functions
@spec await( AgentHarness.Turn.t(), keyword() ) :: {:ok, term()} | {:error, term()}
Waits for a turn's terminal event without a completion-subscription race.
The event-wait timeout defaults to :infinity. Do not call this blocking
function from a GenServer callback; subscribe and monitor the session
instead.
@spec cancel(AgentHarness.Turn.t()) :: :ok | {:error, term()}
Requests cancellation.
:ok records the intent and schedules at most one provider command; it is not
provider acknowledgement. The session remains cancelling until the provider
emits a terminal event. A later provider-command failure retires the session
and is observed through events and the session monitor.
@spec capabilities(AgentHarness.SessionRef.t()) :: AgentHarness.Capabilities.t() | {:error, term()}
Returns provider capabilities for a live session.
@spec list_sessions() :: [AgentHarness.SessionRef.t()]
Lists the currently live, PID-free session handles in deterministic ID order.
Lists stored session snapshots and whether each has a live SessionServer.
The Store defaults to the built-in Memory instance. Pass
store: {module, owner} to inspect a custom Store; a PID-free session handle
does not retain that ownership information after its process stops.
@spec monitor(AgentHarness.SessionRef.t() | AgentHarness.Subscription.t()) :: {:ok, reference()} | {:error, :session_not_found}
Monitors a live session from the calling process.
The caller receives the standard {:DOWN, monitor_ref, :process, pid, reason}
message if the session exits. This is the non-blocking lifecycle signal to use
from an orchestrator GenServer.
@spec purge_session( AgentHarness.SessionRef.t() | String.t(), keyword() ) :: :ok | {:error, term()}
Cascades deletion of a non-live session aggregate from a Store.
The Store defaults to the built-in Memory instance. Pass :store again for
a custom Store because a PID-free stopped handle does not retain its owner.
@spec respond(AgentHarness.Request.t(), AgentHarness.Response.t()) :: :ok | {:error, term()}
Responds to a structured provider request with exactly-once local ownership.
The call waits for provider acknowledgement, while the owning SessionServer
remains responsive to status, cancellation, and shutdown commands. A second
in-flight response returns {:error, :response_in_progress}; a definite
provider rejection releases the claim. Uncertain acknowledgement returns
{:error, {:provider_command_uncertain, reason}}, fails the active turn, and
retires the session. The public response-call timeout must remain longer than
the per-session provider-command watchdog.
@spec start_session( atom(), keyword() ) :: {:ok, AgentHarness.SessionRef.t()} | {:error, term()}
Starts a supervised logical session for provider.
Authentication remains the responsibility of the locally installed CLI.
Provider opening and initial Store finalization have separate bounded phases
(:startup_timeout and :startup_finalization_timeout). Independent session
handshakes are not serialized; call from a supervised task when the caller
itself must remain responsive. Before returning success, the caller and
SessionServer complete a two-way readiness acknowledgement so a queued ready
message cannot return a handle to a server that already died.
@spec start_turn(AgentHarness.SessionRef.t(), term(), keyword()) :: {:ok, AgentHarness.Turn.t()} | {:error, term()}
Accepts one turn locally and starts provider admission asynchronously.
The returned turn initially has status :starting. A session rejects a
second concurrent turn. If the local SessionServer call times out, the error
includes the stable turn handle for reconciliation.
@spec status(AgentHarness.SessionRef.t()) :: map() | {:error, term()}
Returns the live session snapshot.
@spec stop_session( AgentHarness.SessionRef.t(), keyword() ) :: :ok | {:error, term()}
Stops an idle session.
Set force: true to interrupt an active turn locally and close the session.
Forced shutdown does not call the provider cancellation command; it kills
in-flight provider tasks and retires the local provider session.
@spec stream( AgentHarness.Turn.t(), keyword() ) :: {:ok, Enumerable.t()} | {:error, term()}
Returns a replay-followed-by-live stream for a turn.
The stream must be consumed by the process that calls this function. It ends
after a terminal turn event included by the replay cursor or delivered live.
The per-event timeout defaults to :infinity; pass a finite :timeout for
bounded callers. A completed turn with no terminal event in the requested
replay returns {:error, :replay_unavailable}.
@spec subscribe( AgentHarness.SessionRef.t() | AgentHarness.Turn.t(), keyword() ) :: {:ok, AgentHarness.Subscription.t()} | {:error, term()}
Subscribes the calling process, or :pid, to session or turn events.
:from may be :latest, :start, or {:after, sequence}.
@spec unsubscribe(AgentHarness.Subscription.t()) :: :ok | {:error, term()}
Removes an event subscription.