Skip to content

feat(plugins): invoke message_received and message_sent hooks#10109

Open
nezovskii wants to merge 1 commit intoopenclaw:mainfrom
ActVox:feat/message-hooks-pr
Open

feat(plugins): invoke message_received and message_sent hooks#10109
nezovskii wants to merge 1 commit intoopenclaw:mainfrom
ActVox:feat/message-hooks-pr

Conversation

@nezovskii
Copy link

@nezovskii nezovskii commented Feb 6, 2026

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_received when a WhatsApp web inbound message is processed and message_sent after outbound delivery completes.

The hook calls are placed in src/web/auto-reply/monitor/process-message.ts (inbound) and src/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 channel instead of the required channelId), so hook handlers will not receive the expected channelId. Additionally, message_sent currently reports success: true based only on results.length > 0, which can be incorrect in partial-failure/best-effort scenarios and can mislead plugin integrations.

Confidence Score: 3/5

  • This PR is close to mergeable but needs fixes to hook context wiring and success semantics.
  • The changes are small and localized, but the hook context currently does not match the declared PluginHookMessageContext shape (missing required channelId), which will break consumers at runtime. The message_sent.success flag is also computed in a way that can be observably wrong under bestEffort partial failures.
  • src/infra/outbound/deliver.ts, src/web/auto-reply/monitor/process-message.ts

(2/5) Greptile learns from your feedback when you react with thumbs up/down!

Context used:

  • Context from dashboard - CLAUDE.md (source)
  • Context from dashboard - AGENTS.md (source)

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
@openclaw-barnacle openclaw-barnacle bot added the channel: whatsapp-web Channel integration: whatsapp-web label Feb 6, 2026
Copy link
Contributor

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

2 files reviewed, 3 comments

Edit Code Review Agent Settings | Greptile

Comment on lines +376 to +393
// 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,
},
);
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment on lines +389 to +392
agentId: params.mirror?.agentId,
sessionKey: params.mirror?.sessionKey,
},
);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment on lines +167 to +173
{
channel: params.msg.channel,
agentId: params.route.agentId,
sessionKey: params.route.sessionKey,
conversationId,
},
);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

channel: whatsapp-web Channel integration: whatsapp-web

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Feature Request: Expose message lifecycle hooks in Plugin API

1 participant

Comments