import type { DiagnosticTraceContext } from "../infra/diagnostic-trace-context.js"; import type { PluginConversationBinding } from "./conversation-binding.types.js"; export type PluginHookMessageContext = { channelId: string; accountId?: string; conversationId?: string; /** * Canonical session key for this conversation — the same value the agent * runtime sees as `params.sessionKey` for the run that produced the * outbound payload, and the same value `agent_end`/`llm_input`/`llm_output` * fire with. Plugins correlating per-turn state across `agent_end` and * `message_sending` rely on this equality. * * For inbound message hooks (`inbound_claim` etc.), this is the canonical * session for the inbound conversation as resolved by `resolveSessionKey` * / `deriveInboundMessageHookContext`. * * For outbound delivery hooks (`message_sending` and `message_sent`), * this mirrors `OutboundSessionContext.key` from the dispatch path when * delivery has a session attached. When the outbound path has no * resolvable session (e.g. internal smoke runs without * `OutboundSessionContext`), this field is omitted; plugins must treat * it as optional. */ sessionKey?: string; /** * Per-turn run identifier (UUID), unique to one end-to-end agent turn: * stable across all LLM-call iterations, retry attempts (compaction, * empty-response, planning-only, etc.), and multi-payload reply chunks * within that turn; distinct for each new inbound user message and for * each cron/heartbeat/followup-triggered run. * * Generated once in `agent-runner-execution.ts`/`followup-runner.ts` via * `crypto.randomUUID()`. Currently populated for inbound message hooks * (`inbound_claim`, `message_received`) and for agent-runtime hooks that * already receive the run id (e.g. `agent_end`, `llm_input`, `llm_output`). * It is **not yet** plumbed through the outbound delivery path, so * plugins observing `message_sending` / `message_sent` should not rely * on `runId` to correlate against `agent_end`; use `sessionKey` for * outbound→inbound correlation today (with the caveat that it cannot * disambiguate concurrent turns in the same session). */ runId?: string; messageId?: string; senderId?: string; trace?: DiagnosticTraceContext; traceId?: string; spanId?: string; parentSpanId?: string; callDepth?: number; }; export type PluginHookInboundClaimContext = PluginHookMessageContext & { parentConversationId?: string; senderId?: string; messageId?: string; pluginBinding?: PluginConversationBinding; }; export type PluginHookInboundClaimEvent = { content: string; body?: string; bodyForAgent?: string; transcript?: string; timestamp?: number; channel: string; accountId?: string; conversationId?: string; parentConversationId?: string; senderId?: string; senderName?: string; senderUsername?: string; threadId?: string | number; messageId?: string; sessionKey?: string; runId?: string; trace?: DiagnosticTraceContext; traceId?: string; spanId?: string; parentSpanId?: string; isGroup: boolean; commandAuthorized?: boolean; wasMentioned?: boolean; metadata?: Record; }; export type PluginHookMessageReceivedEvent = { from: string; content: string; timestamp?: number; threadId?: string | number; messageId?: string; senderId?: string; sessionKey?: string; runId?: string; trace?: DiagnosticTraceContext; traceId?: string; spanId?: string; parentSpanId?: string; metadata?: Record; }; export type PluginHookMessageSendingEvent = { to: string; content: string; replyToId?: string | number; threadId?: string | number; metadata?: Record; }; export type PluginHookMessageSendingResult = { content?: string; cancel?: boolean; cancelReason?: string; metadata?: Record; }; export type PluginHookMessageSentEvent = { to: string; content: string; success: boolean; messageId?: string; sessionKey?: string; runId?: string; trace?: DiagnosticTraceContext; traceId?: string; spanId?: string; parentSpanId?: string; error?: string; };