Skip to content

Session Channel

A session channel carries session-level state and acts as the coordination scope for one or more chats. The session tracks lifecycle, customizations, per-session configuration, changesets, and the catalog of chats that belong to the session. The per-conversation state — turns, streaming responses, tool calls, pending messages, and input requests — lives on the chat channel.

URI

ahp-session:/<uuid>

The path is a server-unique identifier (typically a UUID) chosen by the client at creation time. The session's provider (e.g. "copilot") is not encoded in the URI scheme — it is carried on SessionSummary.provider. This decoupling lets the same scheme address sessions backed by any agent.

Multiple session channels may be active simultaneously. Clients subscribe to each one whose state they want to track.

State

Subscribers receive a SessionState snapshot containing the session metadata (title, status, provider, activity, working directory, …) inlined directly, the lifecycle phase, the catalog of chats belonging to this session, the optional defaultChat routing hint, active-client state, customizations, changesets, the inputNeeded aggregate, and per-session configuration. Per-conversation state (turns, streaming, tool calls, pending messages, input requests) lives on the chat channel. Refer to the State Model guide for a structural overview.

Lifecycle

1. Client picks a session URI (e.g. ahp-session:/<new-uuid>)
2. Client sends createSession(uri, config) command
3. Client sends subscribe(uri) — MAY be batched with the command
4. Server creates session with lifecycle: 'creating', returns the snapshot
5. Server asynchronously initialises the agent backend
6. On success: server dispatches session/ready
7. On failure: server dispatches session/creationFailed
8. Server broadcasts root/sessionAdded to clients subscribed to ahp-root://

Creation

createSession is a JSON-RPC request. The client picks the URI; the server allocates session state and begins backend initialisation. If the URI is already in use the server returns SessionAlreadyExists (-32003).

Active session

Once a session reaches lifecycle: 'ready', clients may create chats on it with createChat. Each chat is independently subscribable at its own ahp-chat:/<cid> URI; see the Chat Channel specification for the per-chat lifecycle, turn flow, tool calls, and input request handling.

Session-scoped actions dispatched on this channel are limited to:

  • Catalog mutations — session/chatAdded, session/chatRemoved, session/chatUpdated, and session/defaultChatChanged.
  • Session-wide configuration — active-client tracking, customizations, changesets, lifecycle transitions.

All actions dispatched on this channel travel on ActionEnvelopes whose channel is the session URI. Action payloads do NOT carry their own session URI — the channel comes from the envelope.

Chat catalog mutations

Three discrete actions keep SessionState.chats in sync as chats come and go. Sessions with a single chat trivially round-trip a session/chatAdded once at creation; multi-chat sessions exercise all three:

ActionPayloadReducer behavior
session/chatAddedsummary: ChatSummaryUpsert by summary.resource. Appends when no entry has the same URI; otherwise replaces the existing entry. Mirrors root/sessionAdded.
session/chatRemovedchat: URIRemoves the matching entry. No-op when no entry matches. If state.defaultChat referenced the removed URI, the reducer clears it. Mirrors root/sessionRemoved.
session/chatUpdatedchat: URI, changes: Partial<ChatSummary>Merges the non-identity fields of changes onto the matching entry. No-op when no entry matches; clients SHOULD then wait for a session/chatAdded. Identity fields (resource) MUST NOT be carried in changes. Mirrors root/sessionSummaryChanged.

The producer of the chat's own ChatState is responsible for emitting matching session/chatUpdated actions so the catalog and the per-chat channel stay consistent.

Chat aggregation

SessionSummary carries session-wide identity (resource, provider, createdAt, workingDirectory) but several of its mutable fields are aggregates derived from the session's chats. Producers SHOULD apply these rules so clients that only consume the session summary (a session list, for example) still see meaningful state:

FieldDerivation rule
statusTake the activity bits (Idle / InProgress / InputNeeded / Error) from the defaultChat when set, else from the most recently modified chat. Promote InputNeeded if any chat needs input. Promote Error if any chat is in an error state. The orthogonal IsRead / IsArchived flags remain session-scoped and pass through unchanged.
activityMirror the activity string of the chat that contributes the activity bits — usually the default chat, but the chat that raised InputNeeded / Error when a non-default chat wins the promotion.
modifiedAtThe maximum of every chat's modifiedAt.
workingDirectoryThe session-level default. Individual chats MAY override via ChatSummary.workingDirectory; aggregating per-chat overrides up is meaningless and SHOULD NOT be attempted.
changesOptional roll-up. Producers MAY sum per-chat changeset stats or report the most expensive chat's stats — whichever is cheaper to compute.

Sessions with a single chat satisfy all of the above trivially (the chat's values pass through). The rules only matter once a session carries multiple chats.

Aggregated input requests

A chat blocks on user input (an elicitation) or on a tool confirmation deep inside its turn state. Discovering those blocks would normally require subscribing to every chat channel — impractical for a mobile app or a tool-providing client that only watches the session.

SessionState.inputNeeded is a session-level roll-up of every outstanding block across all chats. The host upserts entries with session/inputNeededSet and removes them with session/inputNeededRemoved as the underlying chat-level requests appear and resolve. Whenever the list is non-empty the session's status carries the InputNeeded bit.

Each entry is a SessionInputRequest — a discriminated union over kind:

kindCarriesRespond by dispatching…
chatInputthe mirrored ChatInputRequestchat/inputCompleted (or chat/inputAnswerChanged)
toolConfirmationa ToolCallConfirmationState plus turnIdchat/toolCallConfirmed or chat/toolCallResultConfirmed
toolClientExecutiona ToolCallState in running status plus turnId and the owning clientIdchat/toolCallComplete (optionally chat/toolCallContentChanged)

Every entry carries the owning chat URI plus the identifiers (request.id, or turnId + toolCall.toolCallId) needed to construct the response. A client therefore answers by dispatching the ordinary chat/* action to that chat's channel — it does not need to have subscribed to the chat first. inputNeeded is a read/respond convenience surface, not a separate response protocol: the chat channel remains the source of truth and the host removes the aggregate entry once the chat-level request resolves.

Disposal

jsonc
// Client → Server (request)
{
  "jsonrpc": "2.0",
  "id": 5,
  "method": "disposeSession",
  "params": { "channel": "ahp-session:/<uuid>" },
}

The server tears down the session backend, drops associated subscriptions, and broadcasts root/sessionRemoved to clients subscribed to ahp-root://.

Methods and events on this channel

This section lists wire methods that are interpreted in the context of a session URI (ahp-session:/<uuid>).

Commands (params.channel = "ahp-session:/<uuid>")

MethodKindPurpose
createSessionrequestCreate a session at the chosen URI.
disposeSessionrequestDispose this session and its backend resources (cascades to every chat in the session's catalog).

Notifications (params.channel = "ahp-session:/<uuid>")

MethodKindMeaning
actionserver → client notificationSession action envelope (session/* action payloads — catalog updates, lifecycle, customizations, changesets).
dispatchActionclient → server notificationDispatch client actions on this session (session/titleChanged, session/defaultChatChanged, ...).
unsubscribeclient → server notificationStop receiving messages for this session channel.

auth/required may also target a session URI when auth is required for an operation scoped to that session; see Authentication.

Server Validation of Client Actions

When the server receives a client-dispatched action on this channel, it MUST validate it before applying. Invalid actions MUST be echoed back with a rejectionReason on the ActionEnvelope. The following validation rules apply:

ActionConditionServer Behavior
Any action referencing a non-existent sessionChannel URI not foundServer MUST silently ignore the action (no echo)
session/defaultChatChangeddefaultChat URI does not match an entry in the session's chat catalogServer MUST reject the action

Turn-, tool-call-, input-request-, and pending-message-level validation lives on the Chat Channel.

Actions

Refer to the Session Channel Reference for the full per-action reference. All session-scoped action envelopes carry channel: "ahp-session:/<uuid>".

Catalogue Notifications

Session catalogue events (creation, disposal, summary mutations) are emitted on the Root Channel, not on the session channel itself. This lets clients track the session list without subscribing to every session.

Released under the MIT License.