Skip to content

Actions

Actions are the sole mutation mechanism for subscribable state. They form a discriminated union keyed by type. Every action is wrapped in an ActionEnvelope for sequencing and origin tracking.

Action Envelope

typescript
ActionEnvelope {
  channel: URI                                          // channel the action targets
  action: Action
  serverSeq: number                                     // monotonic, assigned by server
  origin: { clientId: string, clientSeq: number } | undefined  // undefined = server-originated
  rejectionReason?: string                              // present when the server rejected the action
}
  • channel — the channel URI this action targets. Routing is by envelope, not by fields on the inner action. See Channels & Subscriptions.
  • serverSeq — Monotonically increasing sequence number assigned by the server, used for ordering and replay.
  • origin — Identifies who produced this action. undefined means the server itself (e.g. from an agent backend). Otherwise identifies the client that dispatched it.
  • rejectionReason — When present, indicates the server rejected the action. The client should revert its optimistic prediction. Contains a human-readable explanation (e.g. "no active turn to cancel", "tool call not pending confirmation").

Individual action payloads do not carry their own session: URI or terminal: URI field — the target channel comes from the envelope.

Root Actions

These mutate root state and travel on the Root Channel. One root action — root/configChanged — is client-dispatchable; the rest are server-originated.

TypeClient-dispatchable?When
root/agentsChangedNoAvailable agent backends or their models changed
root/activeSessionsChangedNoCount of active sessions changed
root/terminalsChangedNoLightweight terminal catalogue changed (full replacement)
root/configChangedYesHost-level configuration values changed

Automation Catalogue Actions

Automation catalogue actions travel on ahp-automations://.

TypeClient-dispatchable?When
automation/createRequestedYesA client asks the host to persist a complete definition at a client-chosen resource.
automation/updateRequestedYesA client asks the host to apply a definition patch in action order.
automation/setNoThe host adds or replaces one complete automation state.
automation/removedYesA client permanently removes an automation while its dispose operation is available, or the host removes an entry that is no longer visible.

Create and update requests leave catalogue state unchanged until the host publishes an authoritative automation/set. The host applies accepted patches to its current definition in action order and revalidates client-dispatched removals. Rejected actions carry ActionEnvelope.rejectionReason; a rejected removal causes the originating client to restore its optimistic state.

Session & Chat Actions

Actions travel on the channel named by their prefix: session/* actions on the Session Channel (ahp-session:/<uuid>), and chat/* actions on a Chat Channel (ahp-chat:/<cid>). A session is a catalog of chats; its per-conversation activity — turns, streaming, tool calls, pending messages, and input requests — lives on the chat channels, while lifecycle, metadata, tool-registry, and customization actions live on the session channel. Some actions are server-only (produced by the agent backend), others are client-dispatchable.

When a client dispatches an action, the server applies it to the state and also reacts to it as a side effect (e.g. chat/turnStarted triggers agent processing, chat/turnCancelled aborts it). This avoids a separate command→action translation layer for the common interactive cases.

Lifecycle (session channel)

TypeClient-dispatchable?When
session/readyNoSession backend initialised successfully
session/creationFailedNoSession backend failed to initialise

Turn Lifecycle (chat channel)

TypeClient-dispatchable?When
chat/turnStartedYesUser sent a message; server starts processing
chat/deltaNoStreaming text chunk appended to a response part by partId
chat/responsePartNoNew response part created (markdown, reasoning, content ref, tool call)
chat/reasoningNoReasoning/thinking text appended to a reasoning part by partId
chat/usageNoToken usage report for the active turn
chat/turnCompleteNoTurn finished (assistant idle)
chat/turnCancelledYesTurn was aborted; server stops processing
chat/errorNoError during turn processing; appends an error response part and ends the turn
chat/turnResumeYesResume the latest resumable errored turn without adding another message
chat/truncatedYesTurn history truncated (with optional turnId cutoff)

Tool Calls (chat channel)

Tool calls follow a discriminated-union state machine — see State Model — Tool Call Lifecycle for the full diagram.

TypeClient-dispatchable?When
chat/toolCallStartNoTool call created; LM begins streaming parameters
chat/toolCallDeltaNoInvocation message updated, optionally with partial parameters
chat/toolCallReadyNoParameters complete (or running tool needs re-confirmation)
chat/toolCallConfirmedYesClient approves or denies a pending tool call
chat/toolCallCompleteYes¹Tool execution finished
chat/toolCallResultConfirmedYesClient approves or denies a pending result
chat/toolCallContentChangedYes¹Streaming intermediate content while a tool is running
chat/toolCallAuthRequiredNoRunning MCP-contributed tool call pauses pending authentication
chat/toolCallAuthResolvedNoAuthentication resolved; tool call resumes to running

¹ Client-dispatchable for client-provided tools only (where the tool call's contributor.clientId matches the dispatching client). For server-side tools, only the server produces these actions.

Activity & Metadata

TypeClient-dispatchable?When
session/titleChangedYesSession title updated (auto-generated or client rename)
session/activityChangedNoServer updated the session's current activity description
chat/activityChangedNoServer updated a chat's current activity description
session/changesetsChangedNoThe catalog of changesets the host advertises for this session changed (full replacement)
session/isReadChangedYesClient marked session as read or unread
session/isArchivedChangedYesClient archived or unarchived session
session/configChangedYesMutable session config values changed
session/metaChangedNoThe session's _meta side-channel was replaced

Server & Active-Client Tools (session channel)

TypeClient-dispatchable?When
session/serverToolsChangedNoServer-provided tool list changed (full replacement)
session/activeClientSetYesA client joins or refreshes as an active client (keyed by clientId), with its tools and customizations
session/activeClientRemovedYesA client leaves the active set (by clientId)

See Customizations & Client Tools for the full flow.

Pending Messages (chat channel)

TypeClient-dispatchable?When
chat/pendingMessageSetYesA steering or queued message was set (upsert)
chat/pendingMessageRemovedYesA pending message was cancelled (by client) or consumed (by server)
chat/queuedMessagesReorderedYesQueued messages were reordered

The pendingMessageSet and pendingMessageRemoved actions carry a kind discriminant ('steering' or 'queued'). See the State Model — Pending Messages for semantics.

Input Requests (chat channel)

TypeClient-dispatchable?When
chat/inputRequestedNoServer requested structured input from the user (upsert)
chat/inputAnswerChangedYesClient updated a single draft / submitted / skipped answer
chat/inputCompletedYesClient accepted, declined, or cancelled an input request

See Elicitation for the request lifecycle.

Customizations

TypeClient-dispatchable?When
session/customizationsChangedNoServer replaced the session's top-level customization list (full replacement)
session/customizationToggledYesClient replaced a customization's explicit enablement decisions by id
session/customizationUpdatedNoServer upserted a top-level container (plugin or directory) by id (full-entry replacement, including children)
session/customizationRemovedNoServer removed a customization by id (containers cascade to children)

See the Customizations guide for the full flow.

Terminal Actions

Terminal actions travel on the relevant Terminal Channel.

TypeClient-dispatchable?When
terminal/dataNopty output flowing to clients (appended to tail content part)
terminal/inputYesKeyboard input forwarded to the pty (side-effect-only)
terminal/resizedYesTerminal dimensions changed
terminal/claimedYesClaim transferred (client ↔ session)
terminal/titleChangedYesTitle updated
terminal/cwdChangedNoWorking directory changed
terminal/exitedNoProcess exited (exit code set)
terminal/clearedYesScrollback / content reset
terminal/commandDetectionAvailableNoShell integration loaded; command boundaries now reported
terminal/commandExecutedNoA command has been submitted to the shell and is now executing
terminal/commandFinishedNoA command has finished executing (exit code, duration)

See the Terminals guide for usage flows.

Annotations Actions

Annotations actions travel on a session's annotations channel (ahp-session:/<uuid>/annotations). Every annotations action is client-dispatchable — clients create, re-anchor, resolve, and delete annotations and their entries by dispatching these directly (assigning the Annotation.id / AnnotationEntry.id themselves and applying them optimistically), and the agent host MAY also originate them.

TypeClient-dispatchable?When
annotations/setYesUpsert an annotation — create one with its mandatory first entry, or re-anchor / resolve an existing one
annotations/updatedYesPartially update an annotation's own properties (resolve / re-open, re-anchor) without resending its entries
annotations/removedYesRemove an entire annotation (and every entry it contains)
annotations/entrySetYesUpsert a single entry within an annotation (add or edit)
annotations/entryRemovedYesRemove a single entry; dispatch annotations/removed instead to drop the last remaining entry

See the Annotations Channel reference for the full state shape.

Client-Dispatched Actions

Clients interact with the server by dispatching actions as fire-and-forget notifications:

jsonc
// Client → Server
{
  "jsonrpc": "2.0",
  "method": "dispatchAction",
  "params": {
    "channel": "ahp-chat:/<cid>",
    "clientSeq": 1,
    "action": { "type": "chat/turnStarted", "turnId": "t1", ... }
  }
}

The client applies the action optimistically to its local state before sending. When the server echoes it back in an ActionEnvelope, the client reconciles (see Write-Ahead Reconciliation).

ActionServer-side effect
chat/turnStartedBegins agent processing for the new turn
chat/toolCallConfirmedApproves or denies a pending tool call; unblocks or cancels tool execution
chat/turnCancelledAborts the in-progress turn
session/titleChangedUpdates the session title (rename)
chat/pendingMessageSetStores a steering or queued message (upsert); if queued and idle, auto-starts a turn
chat/pendingMessageRemovedCancels a pending message before it is consumed
chat/queuedMessagesReorderedReorders queued messages; unknown IDs ignored, unmentioned messages kept at end
session/customizationToggledReplaces a customization's explicit enablement decisions by id
session/isReadChangedMarks the session as read or unread
session/isArchivedChangedArchives or unarchives the session

Reducers

State is mutated by pure reducer functions — one per state-bearing channel type:

typescript
rootReducer(state: RootState, action: RootAction): RootState
sessionReducer(state: SessionState, action: SessionAction): SessionState
chatReducer(state: ChatState, action: ChatAction): ChatState
terminalReducer(state: TerminalState, action: TerminalAction): TerminalState

The reducer for a given action envelope is selected by the URI scheme of envelope.channel. Reducers are pure — no side effects, no I/O. The same reducer code runs on both server and client, which is what makes write-ahead possible. Server-side effects (e.g. forwarding a message to the agent SDK) are handled by a separate dispatch layer, not in the reducer.

The reducer switch on action type is exhaustive — the compiler errors if a case is missing. This guarantees that every action type is handled.

Next Steps

Released under the MIT License.