feat(hooks): add message:received hook for pre-turn automation#7545
feat(hooks): add message:received hook for pre-turn automation#7545wangtian24 wants to merge 2 commits intoopenclaw:mainfrom
Conversation
Adds a new hook event type 'message:received' that fires before a message is processed by the agent. This enables: - Running custom logic on incoming messages (e.g., memory curation) - Injecting context into the message body (injectedContext field) - Optionally skipping message processing (skipProcessing field) The hook context includes: - message, messageId, senderId, senderName - channel, chatId, isGroup - sessionKey, agentId - Mutable injectedContext and skipProcessing fields Currently wired up for the WhatsApp web channel; other channels can be added incrementally. Implements openclaw#7541
| // Trigger message:received hook before agent processing | ||
| const hookEvent = createInternalHookEvent("message", "received", route.sessionKey, { | ||
| message: msg.body, | ||
| messageId: msg.id, |
There was a problem hiding this comment.
[P1] Hook context injection mutates msg.body but not msg.rawBody/combinedBody
In this handler you prepend injectedContext directly into msg.body (on-message.ts:193-198), but processMessage() builds its combinedBody from buildInboundLine() and separately passes RawBody: params.msg.body / Body: combinedBody (process-message.ts:143-149, 272-276). This means the injected context will affect command detection / agent input (RawBody) but not the logged/assembled combinedBody (Body), which can cause confusing behavior (agent sees extra prefix while logs/history/context show the original content). Consider injecting at the point where combinedBody is constructed, or ensure both the inbound Body and RawBody are updated consistently when injection is enabled.
Prompt To Fix With AI
This is a comment left during a code review.
Path: src/web/auto-reply/monitor/on-message.ts
Line: 168:171
Comment:
[P1] Hook context injection mutates `msg.body` but not `msg.rawBody`/`combinedBody`
In this handler you prepend `injectedContext` directly into `msg.body` (`on-message.ts:193-198`), but `processMessage()` builds its `combinedBody` from `buildInboundLine()` and separately passes `RawBody: params.msg.body` / `Body: combinedBody` (`process-message.ts:143-149`, `272-276`). This means the injected context will affect command detection / agent input (`RawBody`) but *not* the logged/assembled `combinedBody` (`Body`), which can cause confusing behavior (agent sees extra prefix while logs/history/context show the original content). Consider injecting at the point where `combinedBody` is constructed, or ensure both the inbound `Body` and `RawBody` are updated consistently when injection is enabled.
How can I resolve this? If you propose a fix, please make it concise.| // Trigger message:received hook before agent processing | ||
| const hookEvent = createInternalHookEvent("message", "received", route.sessionKey, { | ||
| message: msg.body, | ||
| messageId: msg.id, | ||
| senderId: msg.senderJid?.trim() || msg.senderE164, |
There was a problem hiding this comment.
[P2] MessageReceivedHookContext duplication of sessionKey/agentId is easy to drift
createInternalHookEvent() already sets event.sessionKey, but MessageReceivedHookContext also requires sessionKey and agentId, and this handler populates both (on-message.ts:168-182). Since hook handlers will read from both event.sessionKey and event.context.sessionKey, it’s easy for a future channel to accidentally mismatch them. Consider removing sessionKey from the context (or deriving it from event.sessionKey in helpers) to keep a single source of truth.
Prompt To Fix With AI
This is a comment left during a code review.
Path: src/web/auto-reply/monitor/on-message.ts
Line: 168:172
Comment:
[P2] `MessageReceivedHookContext` duplication of `sessionKey`/`agentId` is easy to drift
`createInternalHookEvent()` already sets `event.sessionKey`, but `MessageReceivedHookContext` also requires `sessionKey` and `agentId`, and this handler populates both (`on-message.ts:168-182`). Since hook handlers will read from both `event.sessionKey` and `event.context.sessionKey`, it’s easy for a future channel to accidentally mismatch them. Consider removing `sessionKey` from the context (or deriving it from `event.sessionKey` in helpers) to keep a single source of truth.
How can I resolve this? If you propose a fix, please make it concise.
Additional Comments (1)
This lockfile change switches several snapshots from Prompt To Fix With AIThis is a comment left during a code review.
Path: pnpm-lock.yaml
Line: 6320:6327
Comment:
[P1] pnpm-lock changes introduce a second `[email protected]` snapshot variant
This lockfile change switches several snapshots from `axios: 1.13.4([email protected])` to plain `axios: 1.13.4` and adds a new `[email protected]` snapshot alongside the existing `[email protected]([email protected])` (`pnpm-lock.yaml:6323`, `8239+`). If this wasn’t intentional, it can bloat the lockfile and lead to subtle differences in the resolved `follow-redirects` variant (with/without `debug`). It may be worth re-generating the lockfile in a consistent environment or confirming this is expected for the repo’s pnpm version/settings.
How can I resolve this? If you propose a fix, please make it concise. |
Support: This is a real need / 支持:这是真实需求Thanks for being the first to tackle this. I want to add a data point: I independently built an external events-framework (1500 lines Python, event inbox + router + rate-limiting + dedup + quiet hours + Telegram) as a workaround for the lack of native message hooks. 感谢你第一个尝试解决这个问题。我想补充一个数据点:我独立构建了一个外部 events-framework(1500 行 Python,事件收件箱 + 路由器 + 限频 + 去重 + 静默时段 + Telegram)来绕过缺少原生消息钩子的问题。 The fact that multiple people (#7545, #9387, #9859) have independently tried to add 多人(#7545、#9387、#9859)独立尝试添加 I'd love to see maintainers weigh in on the preferred approach so we can consolidate effort. 希望维护者能对首选方案表态,让我们集中力量。 |
bfc1ccb to
f92900f
Compare
Summary
Adds a new hook event type
message:receivedthat fires before a message is processed by the agent. This enables:injectedContextskipProcessingChanges
src/hooks/internal-hooks.ts: AddedMessageReceivedHookContexttype andisMessageReceivedEvent()guardsrc/web/auto-reply/monitor/on-message.ts: Wired up hook for WhatsApp channelsrc/hooks/internal-hooks.test.ts: Added 10 new tests (28 total, all passing)Hook Context
Usage
Future Work
Closes #7541
Greptile Overview
Greptile Summary
This PR introduces a new internal hook event
message:received(typemessage, actionreceived) with a dedicated context shape and a runtime type guard (src/hooks/internal-hooks.ts). The WhatsApp web auto-reply handler now triggers this hook before callingprocessMessage(), allowing hooks to request skipping processing or to inject a context prefix into the inbound message (src/web/auto-reply/monitor/on-message.ts). Tests were extended to cover handler registration/triggering and the newisMessageReceivedEventguard (src/hooks/internal-hooks.test.ts).Key items to double-check before merging:
msg.bodybutprocessMessage()still computes and logscombinedBodyseparately, so the agent input (RawBody) and the logged/contextualBodycan diverge.Confidence Score: 3/5
msg.bodyin a way that can diverge from howprocessMessage()constructs/logscombinedBody, and the pnpm-lock change introduces an additional axios resolution variant that should be verified as intentional.(2/5) Greptile learns from your feedback when you react with thumbs up/down!