feat(plugins): invoke message_received and message_sent hooks#10109
feat(plugins): invoke message_received and message_sent hooks#10109nezovskii wants to merge 1 commit intoopenclaw:mainfrom
Conversation
Add actual invocations for message hooks that were defined but never called: - message_received: invoked in process-message.ts when an inbound message is received, includes from, content, timestamp, and metadata (chatType, conversationId, messageId, channel) - message_sent: invoked in deliver.ts after successful message delivery, includes to, content, and success status This enables plugins to intercept messages for logging, analytics, or external integrations (e.g., syncing to external databases). Closes openclaw#9524
| // Invoke message_sent hook for plugin integrations | ||
| const hookRunner = getGlobalHookRunner(); | ||
| if (hookRunner?.hasHooks("message_sent") && results.length > 0) { | ||
| const combinedText = payloads.map((p) => p.text ?? "").join("\n"); | ||
| hookRunner.runMessageSent( | ||
| { | ||
| to, | ||
| content: combinedText, | ||
| success: true, | ||
| }, | ||
| { | ||
| channel, | ||
| accountId, | ||
| agentId: params.mirror?.agentId, | ||
| sessionKey: params.mirror?.sessionKey, | ||
| }, | ||
| ); | ||
| } |
There was a problem hiding this comment.
Incorrect success reporting
message_sent is emitted with success: true whenever results.length > 0, even if some payloads failed (e.g., bestEffort: true with partial failures) or if the function delivered media-only payloads with empty text. This will mislead plugins that rely on success for auditing/analytics.
Consider deriving success from the actual delivery outcome (e.g., track whether any payload threw in the loop, or emit one hook event per payload/result). If you want a single aggregated event, include counts (attempted/succeeded/failed) and set success accordingly.
Prompt To Fix With AI
This is a comment left during a code review.
Path: src/infra/outbound/deliver.ts
Line: 376:393
Comment:
**Incorrect success reporting**
`message_sent` is emitted with `success: true` whenever `results.length > 0`, even if some payloads failed (e.g., `bestEffort: true` with partial failures) or if the function delivered media-only payloads with empty `text`. This will mislead plugins that rely on `success` for auditing/analytics.
Consider deriving `success` from the actual delivery outcome (e.g., track whether any payload threw in the loop, or emit one hook event per payload/result). If you want a single aggregated event, include counts (attempted/succeeded/failed) and set `success` accordingly.
How can I resolve this? If you propose a fix, please make it concise.| agentId: params.mirror?.agentId, | ||
| sessionKey: params.mirror?.sessionKey, | ||
| }, | ||
| ); |
There was a problem hiding this comment.
Missing required hook context
runMessageSent(..., ctx) is passed { channel, accountId, agentId, sessionKey }, but PluginHookMessageContext requires channelId: string (see src/plugins/types.ts). Passing channel here is both the wrong key and (likely) the wrong value type, so hook handlers will see ctx.channelId as undefined at runtime.
Update the context object to include the required channelId field (and ensure it matches whatever your hook system expects as a channel identifier).
Prompt To Fix With AI
This is a comment left during a code review.
Path: src/infra/outbound/deliver.ts
Line: 389:392
Comment:
**Missing required hook context**
`runMessageSent(..., ctx)` is passed `{ channel, accountId, agentId, sessionKey }`, but `PluginHookMessageContext` requires `channelId: string` (see `src/plugins/types.ts`). Passing `channel` here is both the wrong key and (likely) the wrong value type, so hook handlers will see `ctx.channelId` as `undefined` at runtime.
Update the context object to include the required `channelId` field (and ensure it matches whatever your hook system expects as a channel identifier).
How can I resolve this? If you propose a fix, please make it concise.| { | ||
| channel: params.msg.channel, | ||
| agentId: params.route.agentId, | ||
| sessionKey: params.route.sessionKey, | ||
| conversationId, | ||
| }, | ||
| ); |
There was a problem hiding this comment.
Missing required hook context
runMessageReceived(..., ctx) is passed { channel, agentId, sessionKey, conversationId }, but PluginHookMessageContext requires channelId: string (see src/plugins/types.ts). As-is, hook handlers will get ctx.channelId as undefined.
Use the correct required field name (channelId) and ensure the value matches the intended channel identifier.
Prompt To Fix With AI
This is a comment left during a code review.
Path: src/web/auto-reply/monitor/process-message.ts
Line: 167:173
Comment:
**Missing required hook context**
`runMessageReceived(..., ctx)` is passed `{ channel, agentId, sessionKey, conversationId }`, but `PluginHookMessageContext` requires `channelId: string` (see `src/plugins/types.ts`). As-is, hook handlers will get `ctx.channelId` as `undefined`.
Use the correct required field name (`channelId`) and ensure the value matches the intended channel identifier.
How can I resolve this? If you propose a fix, please make it concise.bfc1ccb to
f92900f
Compare
Add actual invocations for message hooks that were defined but never called:
message_received: invoked in process-message.ts when an inbound message is received, includes from, content, timestamp, and metadata (chatType, conversationId, messageId, channel)
message_sent: invoked in deliver.ts after successful message delivery, includes to, content, and success status
This enables plugins to intercept messages for logging, analytics, or external integrations (e.g., syncing to external databases).
Closes #9524
Greptile Overview
Greptile Summary
This PR wires up the previously-defined plugin message hooks by invoking
message_receivedwhen a WhatsApp web inbound message is processed andmessage_sentafter outbound delivery completes.The hook calls are placed in
src/web/auto-reply/monitor/process-message.ts(inbound) andsrc/infra/outbound/deliver.ts(outbound) using the global hook runner (src/plugins/hook-runner-global.ts).Key issue to address before merge: both hook invocations pass an invalid context shape (using
channelinstead of the requiredchannelId), so hook handlers will not receive the expectedchannelId. Additionally,message_sentcurrently reportssuccess: truebased only onresults.length > 0, which can be incorrect in partial-failure/best-effort scenarios and can mislead plugin integrations.Confidence Score: 3/5
PluginHookMessageContextshape (missing requiredchannelId), which will break consumers at runtime. Themessage_sent.successflag is also computed in a way that can be observably wrong underbestEffortpartial failures.(2/5) Greptile learns from your feedback when you react with thumbs up/down!
Context used:
dashboard- CLAUDE.md (source)dashboard- AGENTS.md (source)