Hook Event Types, Triggers, and Producer Context

Lists every internal event key, its trigger and wait behavior, and the context each producer supplies. Useful for developers wiring hook subscriptions in the Hooks guide.

Read this when

  • You are choosing which event key a handler should subscribe to
  • You need the trigger and wait behavior of a specific event
  • You need the context fields a producer supplies to your handler

Every internal event key, what sets it off and how waiting works, plus the context that each producer hands over. This page belongs to the Hooks guide.

Event types

You can subscribe to one exact key from the table below, or to a bare family (command, session, agent, gateway, message). A family subscription delivers every action inside that family. Avoid registering one handler against both command and command:new, otherwise a new command will call it twice. session:compact is neither a family nor a wildcard, so subscribe to the two exact compaction keys instead.

EventTrigger and wait behavior
command:newAuthorized new-session command handling, or a Gateway session operation that emits new-command hooks; awaited.
command:resetAuthorized reset-command handling or Gateway session reset; awaited.
command:stopAuthorized channel-user and Gateway client abort handling after parent cancellation; awaited, with no hook reply delivery.
session:auto-resetExisting session replaced due to daily/idle policy; dispatched independently of the successor turn.
session:compact:beforeBefore compaction work; awaited.
session:compact:afterAfter successful compaction; awaited.
session:patchAn authorized Gateway patch is applied, or a supported model-selection path persists a change; asynchronous notification.
agent:bootstrapWorkspace bootstrap resolution before context injection; awaited.
gateway:startupScheduled after hook loading and sidecar/channel startup work; does not delay initial Gateway bind.
gateway:shutdownShutdown begins, before channel/plugin teardown; bounded wait.
gateway:pre-restartShutdown has a finite expected-restart delay; bounded wait.
message:receivedAccepted inbound dispatch with a session key; asynchronous observation.
message:transcribedPre-agent preprocessing has nonempty audio transcript text and a session key; asynchronous observation.
message:preprocessedMedia/link preprocessing completed or was skipped, with a session key; asynchronous observation.
message:sentA delivery owner reports a send outcome with a session key; asynchronous observation. Inspect context.success.

For gateway:shutdown and gateway:pre-restart hooks, the first wait is bounded, which lets independent teardown move ahead. A timeout does not cancel the handler. Before shared state is closed, the Gateway waits for the real hook completion, so a handler that never settles can keep in-process shutdown from finishing.

Not every inbound transport update, nor every attempted low-level send, yields an internal message event. Suppressed or duplicate inbound dispatches, and paths lacking a session key, may skip them. Treat these as observation points: they are neither a full transport audit nor a means of blocking message processing. Fast native-command paths may bypass preprocessing events. When preprocessed fires, it means that phase was reached, not that every attachment or link was understood successfully. In the same way, compaction may be skipped or fail after its before event, and retries can emit before more than once.

Subscriptions to unknown keys such as command:nwe are still registered, yet the loader issues a warning and hooks info lists them. Core never emits them. A custom key fires only when custom code emits it explicitly; declaring it in metadata does not create a trigger.

Stop handling for authorized channel users (fast and bare stop words included) is observed by command:stop, along with chat.abort/sessions.abort client requests, Talk voice cancellation, and /acp cancel. It does not fire on interrupt, restart, watchdog, operator revocation, or supersession, and it does not serve as a natural agent-finalization gate. That contract is covered by before_agent_finalize in Plugin hooks.

Event context highlights

The fields listed here describe what each producer sends. Optional values may be missing, and fields from one event should not be assumed to exist on another.

command:new and command:reset: agentId, sessionEntry, previousSessionEntry, commandSource, senderId, workspaceDir, storePath, and cfg on the chat command path. Entries and routing metadata vary with the caller. Gateway reset supplies commandSource: "gateway:sessions.reset"; Gateway agent reset supplies gateway:agent, and session creation may supply webchat. Gateway callers leave out senderId. Session creation emits new-command hooks only when it is requested with emitCommandHooks for an existing parent. For the session being replaced, prefer previousSessionEntry: chat and Gateway paths emit at different points during reset, so it is not a universal pre-reset or successful-reset receipt. A sessionFile value may be a transcript identifier instead of a readable file path, so do not assume it is JSONL on disk.

command:stop: optional sessionEntry, sessionId, commandSource, and senderId. The full new/reset context is not included.

session:auto-reset: cfg, agentId, workspaceDir, storePath, sessionEntry that names the sessionId which ended, plus optional sessionFile, reason (either daily or idle), and, when supplied, transcriptArchived, nextSessionId, and nextSessionKey.

agent:bootstrap: workspaceDir, a mutable bootstrapFiles, and, if provided, cfg, sessionKey, sessionId, agentId. Every bootstrap record carries name, path, missing, and optionally content. Replacing or extending the array is allowed for a handler, yet final path deduplication, session/privacy filtering, and context budgets still apply.

session:patch: a cloned post-operation sessionEntry, request-shaped patch, and cfg. Target/expectation fields and submitted settings make up the patch, not a computed changed-fields diff. Even when a submitted value was already present, successful Gateway patches can emit. Emission also happens on supported model-selection paths, which include /model, the model picker, and model changes made through session_status; a read-only status query does not emit. This is not a notification for every session-store write.

Compaction: sessionId, missingSessionKey, messageCount, and optional tokenCount are present in both phases. messageCountOriginal and optional tokenCountOriginal are added to Before. After adds compactedCount and optional summaryLength, tokensBefore, tokensAfter, and firstKeptEntryId. Unavailable token counts must not be inferred as zero.

gateway:startup: cfg, deps, and workspaceDir. Shutdown and pre-restart: reason and restartExpectedMs (null when shutdown is expected with no restart). By default the shutdown wait is 5 seconds; pre-restart grants a separate 10-second budget. These bound the caller's wait, not the handler's work: a timeout does not cancel promises. Channels have not yet been torn down, but neither queued agent work nor message delivery is guaranteed to finish before shutdown. Typed session_end drain behavior belongs to Plugin hooks.

Message context

message:received holds from, content, channelId, plus the optional timestamp, accountId, conversationId, messageId, media, originalMedia, mediaStagingPending, and metadata. When picking content, the order is: a nonblank command body first, then the raw body, then the generic body. BodyForAgent is never chosen by it; instead of the mapper stripping every bit of enrichment, the fallback body is defined by the surface.

A received metadata may carry to, provider, surface, threadId, senderId, senderName, senderUsername, senderE164, guildId, channelName, and topicName. The legacy attachment aliases are mediaPath, mediaUrl, mediaType, mediaPaths, mediaUrls, and mediaTypes; remote-staging metadata may additionally contain mediaRemoteHost, mediaStagingPending, along with the matching originalMediaPath, originalMediaUrl, originalMediaType, originalMediaPaths, originalMediaUrls, and originalMediaTypes. The structured media arrays are the preferred choice.

message:transcribed and message:preprocessed carry channelId, cfg, and the optional fields from, to, body, bodyForAgent, timestamp, conversationId, messageId, senderId, senderName, senderUsername, provider, surface, along with the structured media fields. When transcription applies, a required transcript text is added; when preprocessing applies, optional transcript, isGroup, and groupId are added. The enriched body prepared for the agent lives in bodyForAgent. mediaPath and mediaType stay as deprecated aliases for the first attachment. Neither of these contexts guarantees accountId or the metadata object belonging to the received event.

Any single structured media fact may hold path, url, contentType, kind, transcribed, messageId, and workspaceDir. Source order is retained across facts. If mediaStagingPending is true, then media is not provided and originalMedia instead describes the original attachments, so remote paths must not be handled as local files.

message:sent holds to, content, success, channelId, plus the optional error, accountId, conversationId, messageId, isGroup, and groupId. A failure is reported by success: false on a path that already emitted an outcome; if no event arrives, that proves neither success nor failure. For outbound delivery, one outcome may be reported per logical payload instead of per text chunk, and a partial failure may carry a message ID for a part that was already sent. Settlement of a durable outbound queue can postpone when the observation appears; it does not make the hook itself durable. Never resend blindly after a failure, since a delivered part could be duplicated. A send result does not prove the recipient read the message.

1,351 words · updated Oct 26, 2026