Session Channel
A session channel carries session-level state and acts as the coordination scope for one or more chats. The session tracks lifecycle, model/agent defaults, 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 summary, lifecycle phase, the catalog of chats belonging to this session, the optional defaultChat routing hint, model and active-client state, customizations, changesets, 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, andsession/defaultChatChanged. - Session-wide configuration — model and agent defaults, 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:
| Action | Payload | Reducer behavior |
|---|---|---|
session/chatAdded | summary: ChatSummary | Upsert by summary.resource. Appends when no entry has the same URI; otherwise replaces the existing entry. Mirrors root/sessionAdded. |
session/chatRemoved | chat: URI | Removes the matching entry. No-op when no entry matches. If state.defaultChat referenced the removed URI, the reducer clears it. Mirrors root/sessionRemoved. |
session/chatUpdated | chat: 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:
| Field | Derivation rule |
|---|---|
status | Take 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. |
activity | Mirror 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. |
modifiedAt | The maximum of every chat's modifiedAt. |
model / agent | The session-level selection. Per-chat overrides are surfaced on individual ChatSummary entries; do not aggregate them up. |
workingDirectory | The session-level default. Individual chats MAY override via ChatSummary.workingDirectory; aggregating per-chat overrides up is meaningless and SHOULD NOT be attempted. |
changes | Optional 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.
Disposal
// 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>")
| Method | Kind | Purpose |
|---|---|---|
createSession | request | Create a session at the chosen URI. |
disposeSession | request | Dispose this session and its backend resources (cascades to every chat in the session's catalog). |
Notifications (params.channel = "ahp-session:/<uuid>")
| Method | Kind | Meaning |
|---|---|---|
action | server → client notification | Session action envelope (session/* action payloads — catalog updates, lifecycle, model/agent, customizations, changesets). |
dispatchAction | client → server notification | Dispatch client actions on this session (session/modelChanged, session/defaultChatChanged, ...). |
unsubscribe | client → server notification | Stop 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:
| Action | Condition | Server Behavior |
|---|---|---|
| Any action referencing a non-existent session | Channel URI not found | Server MUST silently ignore the action (no echo) |
session/modelChanged | A turn is currently active in any chat in this session | Server MUST defer the model change until every active turn completes, then apply it for next turns |
session/agentChanged | A turn is currently active in any chat in this session | Server MUST defer the agent change until every active turn completes, then apply it for next turns |
session/defaultChatChanged | defaultChat URI does not match an entry in the session's chat catalog | Server 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.