Files
openclaw/src/plugins/hook-message.types.ts
Edward Abrams 05db911775 fix(outbound): thread session keys into outbound hooks (#73706)
Thread the canonical outbound session key into plugin message_sending and message_sent hook contexts, and align native command redirect routed delivery with the agent runtime session key. This lets plugins correlate agent_end with outbound delivery hooks without seeing missing or divergent session keys.

Verification:
- gh pr checks 73706 --repo openclaw/openclaw --watch=false
- Real behavior proof: https://github.com/openclaw/openclaw/actions/runs/26526635074/job/78131933497

Thanks @zeroaltitude.

Co-authored-by: Edward Abrams <zeroaltitude@gmail.com>
2026-05-28 00:43:27 +01:00

132 lines
4.1 KiB
TypeScript

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<string, unknown>;
};
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<string, unknown>;
};
export type PluginHookMessageSendingEvent = {
to: string;
content: string;
replyToId?: string | number;
threadId?: string | number;
metadata?: Record<string, unknown>;
};
export type PluginHookMessageSendingResult = {
content?: string;
cancel?: boolean;
cancelReason?: string;
metadata?: Record<string, unknown>;
};
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;
};