Skip to content

fix(hooks): pass media metadata to internal message_received hook#88740

Merged
steipete merged 1 commit into
openclaw:mainfrom
SebTardif:fix/internal-hooks-media-metadata
May 31, 2026
Merged

fix(hooks): pass media metadata to internal message_received hook#88740
steipete merged 1 commit into
openclaw:mainfrom
SebTardif:fix/internal-hooks-media-metadata

Conversation

@SebTardif

Copy link
Copy Markdown
Contributor

Problem

Commit 65e2120 (PR #87297) added six missing media metadata fields
(mediaPath, mediaUrl, mediaType, mediaPaths, mediaUrls,
mediaTypes) to the plugin toPluginMessageReceivedEvent mapper,
but the parallel internal toInternalMessageReceivedContext mapper
was not updated.

Internal hooks registered via registerInternalHook("message:received", ...) receive a MessageReceivedHookContext whose metadata object is
built by toInternalMessageReceivedContext. Because the media fields
were omitted, internal hook consumers lose all media metadata on
received messages, even though the canonical context carries the data.

The plugin path and the inbound-claim path both surface these fields
correctly; only the internal hooks path is affected.

Fix

Add the same six media fields to the metadata object in
toInternalMessageReceivedContext, matching the plugin mapper that was
fixed in 65e2120.

Production diff is +6 lines in src/hooks/message-hook-mappers.ts, +8
lines of test assertions in the corresponding test file.

Real behavior proof

Behavior addressed: Internal hooks message:received context now
includes all six media metadata fields (mediaPath, mediaUrl,
mediaType, mediaPaths, mediaUrls, mediaTypes) in its metadata
object, matching the plugin path fixed in PR #87297.

Real environment tested: macOS 15.5, Node.js v22.16.0, OpenClaw built
from source (commit 01ef169 + this patch) at /tmp/fix-internal-hooks-media.

Exact steps or command run after this patch:

Step 1: Install deps and run targeted tests

$ cd /tmp/fix-internal-hooks-media
$ CI=true pnpm install --frozen-lockfile
$ node scripts/run-vitest.mjs src/hooks/message-hook-mappers.test.ts

Step 2: Type-check the core project

$ pnpm tsgo:core

Step 3: Build OpenClaw from patched source

$ pnpm build

Step 4: Verify the fix is in compiled production code

$ grep -n 'toInternalMessageReceivedContext' dist/message-hook-mappers-*.js
$ sed -n '229,258p' dist/message-hook-mappers-*.js

Step 5: Start the patched gateway to confirm module loads cleanly

$ timeout 10 node openclaw.mjs gateway

Evidence after fix: terminal output copied below.

Vitest results (13/13 pass, including new media assertions):

$ node scripts/run-vitest.mjs src/hooks/message-hook-mappers.test.ts
[test] starting test/vitest/vitest.hooks.config.ts

 RUN  v4.1.7 /private/tmp/fix-internal-hooks-media

 ✓ |hooks| src/hooks/message-hook-mappers.test.ts (13 tests) 43ms

 Test Files  1 passed (1)
      Tests  13 passed (13)
   Start at  11:29:28
   Duration  828ms (transform 564ms, setup 192ms, import 519ms, tests 43ms, environment 0ms)

[test] passed 1 Vitest shard in 3.52s

Type check passes:

$ pnpm tsgo:core
$ node scripts/run-tsgo.mjs -p tsconfig.core.json --incremental --tsBuildInfoFile .artifacts/tsgo-cache/core.tsbuildinfo

Compiled production code shows all six media fields in toInternalMessageReceivedContext:

$ sed -n '229,258p' dist/message-hook-mappers-D_6ymTKa.js
function toInternalMessageReceivedContext(canonical) {
return {
from: canonical.from,
content: canonical.content,
timestamp: canonical.timestamp,
channelId: canonical.channelId,
accountId: canonical.accountId,
conversationId: canonical.conversationId,
messageId: canonical.messageId,
metadata: {
to: canonical.to,
provider: canonical.provider,
surface: canonical.surface,
threadId: canonical.threadId,
senderId: canonical.senderId,
senderName: canonical.senderName,
senderUsername: canonical.senderUsername,
senderE164: canonical.senderE164,
mediaPath: canonical.mediaPath,
mediaUrl: canonical.mediaUrl,
mediaType: canonical.mediaType,
mediaPaths: canonical.mediaPaths,
mediaUrls: canonical.mediaUrls,
mediaTypes: canonical.mediaTypes,
guildId: canonical.guildId,
channelName: canonical.channelName,
topicName: canonical.topicName
}
};
}

Gateway starts and shuts down cleanly with patched module:

$ timeout 10 node openclaw.mjs gateway
2026-05-31T11:31:41.964-07:00 [gateway] loading configuration…
2026-05-31T11:31:41.995-07:00 [gateway] resolving authentication…
2026-05-31T11:31:42.002-07:00 [gateway] starting...
2026-05-31T11:31:42.539-07:00 [gateway] starting HTTP server...
2026-05-31T11:31:42.565-07:00 [health-monitor] started (interval: 300s, startup-grace: 60s, channel-connect-grace: 120s)
2026-05-31T11:31:51.464-07:00 [gateway] signal SIGTERM received
2026-05-31T11:31:51.464-07:00 [gateway] received SIGTERM; shutting down
2026-05-31T11:31:51.465-07:00 [gateway] signal SIGTERM received
2026-05-31T11:31:51.465-07:00 [gateway] received SIGTERM during shutdown; ignoring

Observed result after fix: The compiled toInternalMessageReceivedContext
function now includes all six media metadata fields (mediaPath,
mediaUrl, mediaType, mediaPaths, mediaUrls, mediaTypes) in its
metadata return object, matching toPluginMessageReceivedEvent. The
gateway loads the patched module cleanly. Tests confirm the fields
propagate through with correct values for both single-attachment and
multi-attachment scenarios.

What was not tested: Live message delivery through a channel provider
(WhatsApp, Telegram) with actual media attachments, because that requires
a configured provider account. The fix is a pure data-forwarding change
with no conditional logic.

Forward canonical inbound media metadata to internal message_received
hooks so internal hook consumers can inspect the same mediaPath,
mediaUrl, mediaType, mediaPaths, mediaUrls, and mediaTypes fields
already available to plugin hooks (fixed in 65e2120) and
inbound_claim.

The toInternalMessageReceivedContext mapper was not updated when commit
65e2120 added these fields to the plugin mapper, causing internal
hooks to silently lose media metadata on received messages.

Signed-off-by: Sebastien Tardif <[email protected]>
@openclaw-barnacle openclaw-barnacle Bot added size: XS proof: supplied External PR includes structured after-fix real behavior proof. labels May 31, 2026
@clawsweeper

clawsweeper Bot commented May 31, 2026

Copy link
Copy Markdown
Contributor

Codex review: needs maintainer review before merge. Reviewed May 31, 2026, 2:40 PM ET / 18:40 UTC.

Summary
The PR adds six canonical media metadata fields to toInternalMessageReceivedContext and extends the mapper test assertions for internal message:received payloads.

PR surface: Source +6, Tests +9. Total +15 across 2 files.

Reproducibility: yes. Source inspection shows canonical inbound contexts carry media fields and the plugin/inbound-claim mappers preserve them, while current main's internal received mapper omits them before dispatching internal hooks.

Review metrics: none identified.

Merge readiness
Overall: 🦞 diamond lobster
Proof: 🦞 diamond lobster
Patch quality: 🦞 diamond lobster
Result: ready for maintainer review.

Overall follows the weaker of proof and patch quality, so missing proof can cap an otherwise strong patch.

Next step before merge

  • No repair lane is needed; the PR is already the focused fix and should proceed through normal maintainer review and gates.

Security
Cleared: No security or supply-chain concern found; the diff only forwards already-canonical media metadata to trusted internal hook metadata and adds test assertions.

Review details

Best possible solution:

Land this narrow mapper/test fix after the normal mergeability and CI gates run against current main.

Do we have a high-confidence way to reproduce the issue?

Yes. Source inspection shows canonical inbound contexts carry media fields and the plugin/inbound-claim mappers preserve them, while current main's internal received mapper omits them before dispatching internal hooks.

Is this the best way to solve the issue?

Yes. The mapper is the single bridge into the internal message:received dispatch path, so copying the same metadata keys there is the narrowest maintainable fix; changing hook runtime or adding a new API would be unnecessary.

AGENTS.md: found and applied where relevant.

Codex review notes: model gpt-5.5, reasoning high; reviewed against e76df691fe43.

Label changes

Label changes:

  • add P2: This is a normal-priority hook metadata bug with limited blast radius to internal message:received consumers handling inbound media.
  • add proof: sufficient: Contributor real behavior proof is sufficient. The PR body includes after-fix terminal output for the targeted mapper test, core typecheck, build, compiled production mapper inspection, and gateway startup.
  • add rating: 🦞 diamond lobster: Overall readiness is 🦞 diamond lobster; proof is 🦞 diamond lobster and patch quality is 🦞 diamond lobster.
  • add status: 👀 ready for maintainer look: ClawSweeper has no concrete contributor-facing blocker left for this PR. Sufficient (terminal): The PR body includes after-fix terminal output for the targeted mapper test, core typecheck, build, compiled production mapper inspection, and gateway startup.

Label justifications:

  • P2: This is a normal-priority hook metadata bug with limited blast radius to internal message:received consumers handling inbound media.
  • rating: 🦞 diamond lobster: Overall readiness is 🦞 diamond lobster; proof is 🦞 diamond lobster and patch quality is 🦞 diamond lobster.
  • status: 👀 ready for maintainer look: ClawSweeper has no concrete contributor-facing blocker left for this PR. Sufficient (terminal): The PR body includes after-fix terminal output for the targeted mapper test, core typecheck, build, compiled production mapper inspection, and gateway startup.
  • proof: sufficient: Contributor real behavior proof is sufficient. The PR body includes after-fix terminal output for the targeted mapper test, core typecheck, build, compiled production mapper inspection, and gateway startup.
Evidence reviewed

PR surface:

Source +6, Tests +9. Total +15 across 2 files.

View PR surface stats
Area Files Added Removed Net
Source 1 6 0 +6
Tests 1 9 0 +9
Docs 0 0 0 0
Config 0 0 0 0
Generated 0 0 0 0
Other 0 0 0 0
Total 2 15 0 +15

What I checked:

Likely related people:

  • steipete: Peter Steinberger authored the earlier message_received metadata emission work and is also the current blame owner for the extracted mapper file in this checkout. (role: introduced behavior and recent area contributor; confidence: high; commits: 9b12275fe113, 1a65425a6eab; files: src/auto-reply/reply/dispatch-from-config.ts, src/hooks/message-hook-mappers.ts)
  • WarrenJones: WarrenJones authored the merged related plugin-hook media metadata fix that this PR mirrors for internal hooks. (role: recent related contributor; confidence: high; commits: 65e2120f8c0d; files: src/hooks/message-hook-mappers.ts, src/hooks/message-hook-mappers.test.ts)
  • davidrudduck: David Rudduck authored a prior fix adding guild/channel metadata to both plugin and internal message_received metadata paths, which is the same mapper invariant being extended here. (role: adjacent metadata contributor; confidence: medium; commits: 24a60799be5a; files: src/auto-reply/reply/dispatch-from-config.ts)
  • Eric Lytle: Eric Lytle added adjacent internal message hook event contexts and tests for transcribed/preprocessed message hooks. (role: internal hooks area contributor; confidence: medium; commits: e0b8b80067cf; files: src/hooks/internal-hooks.ts, src/hooks/message-hooks.test.ts)
What the crustacean ranks mean
  • 🦀 challenger crab: rare, exceptional readiness with strong proof, clean implementation, and convincing validation.
  • 🦞 diamond lobster: very strong readiness with only minor maintainer review expected.
  • 🐚 platinum hermit: good normal PR, likely mergeable with ordinary maintainer review.
  • 🦐 gold shrimp: useful signal, but proof or patch confidence is still limited.
  • 🦪 silver shellfish: thin signal; proof, validation, or implementation needs work.
  • 🧂 unranked krab: not merge-ready because proof is missing/unusable or there are serious correctness or safety concerns.
  • 🌊 off-meta tidepool: rating does not apply to this item.

Shiny media proof means a screenshot, video, or linked artifact directly shows the changed behavior. Runtime, network, CSP, and security claims still need visible diagnostics.

How this review workflow works
  • ClawSweeper keeps one durable marker-backed review comment per issue or PR.
  • Re-runs edit this comment so the latest verdict, findings, and automation markers stay together instead of adding duplicate bot comments.
  • A fresh review can be triggered by eligible @clawsweeper re-review comments, exact-item GitHub events, scheduled/background review runs, or manual workflow dispatch.
  • PR/issue authors and users with repository write access can comment @clawsweeper re-review or @clawsweeper re-run on an open PR or issue to request a fresh review only.
  • Maintainers can also comment @clawsweeper review to request a fresh review only.
  • Fresh-review commands do not start repair, autofix, rebase, CI repair, or automerge.
  • Maintainer-only repair and merge flows require explicit commands such as @clawsweeper autofix, @clawsweeper automerge, @clawsweeper fix ci, or @clawsweeper address review.
  • Maintainers can comment @clawsweeper explain to ask for more context, or @clawsweeper stop to stop active automation.

@clawsweeper clawsweeper Bot added proof: sufficient ClawSweeper judged the real behavior proof convincing. rating: 🦞 diamond lobster Very strong PR readiness with only minor maintainer review expected. status: 👀 ready for maintainer look ClawSweeper has no concrete contributor-facing blocker left for this PR. P2 Normal backlog priority with limited blast radius. labels May 31, 2026
@steipete steipete self-assigned this May 31, 2026
@steipete

Copy link
Copy Markdown
Contributor

Land-ready proof for PR #88740.

Reviewed changed surface and sibling mapper paths: src/hooks/message-hook-mappers.ts, src/hooks/message-hook-mappers.test.ts, src/hooks/internal-hooks.ts, and src/auto-reply/reply/dispatch-from-config.ts.

Local proof run on head 002a324:

  • node scripts/run-vitest.mjs src/hooks/message-hook-mappers.test.ts - pass, 13 tests
  • .agents/skills/autoreview/scripts/autoreview --mode branch --base origin/main - clean, no accepted/actionable findings

GitHub proof checked before merge:

  • CI run 26720975225 green on relevant required jobs
  • Real behavior proof job 26721126308 green
  • CodeQL and OpenGrep checks green or neutral/skipped as expected for this PR shape

Best-fix verdict: best. This is the narrow bridge into internal message:received hook dispatch, and the PR mirrors the already-canonical media metadata fields from the plugin received hook mapper without changing runtime control flow.

@steipete
steipete merged commit 9a3e7d4 into openclaw:main May 31, 2026
194 of 200 checks passed
github-actions Bot pushed a commit to Desicool/openclaw that referenced this pull request Jun 1, 2026
Forward canonical inbound media metadata to internal message:received hook consumers, matching the plugin received hook mapper and inbound-claim metadata path.

This fixes internal hook handlers losing mediaPath, mediaUrl, mediaType, mediaPaths, mediaUrls, and mediaTypes for received messages with attachments.

Verification:
- node scripts/run-vitest.mjs src/hooks/message-hook-mappers.test.ts
- .agents/skills/autoreview/scripts/autoreview --mode branch --base origin/main

Refs: openclaw#88740
Thanks @SebTardif.
Co-authored-by: Sebastien Tardif <[email protected]>
SYU8384 pushed a commit to SYU8384/openclaw that referenced this pull request Jun 3, 2026
Forward canonical inbound media metadata to internal message:received hook consumers, matching the plugin received hook mapper and inbound-claim metadata path.

This fixes internal hook handlers losing mediaPath, mediaUrl, mediaType, mediaPaths, mediaUrls, and mediaTypes for received messages with attachments.

Verification:
- node scripts/run-vitest.mjs src/hooks/message-hook-mappers.test.ts
- .agents/skills/autoreview/scripts/autoreview --mode branch --base origin/main

Refs: openclaw#88740
Thanks @SebTardif.
Co-authored-by: Sebastien Tardif <[email protected]>
sablehead pushed a commit to sablehead/openclaw that referenced this pull request Jun 10, 2026
Forward canonical inbound media metadata to internal message:received hook consumers, matching the plugin received hook mapper and inbound-claim metadata path.

This fixes internal hook handlers losing mediaPath, mediaUrl, mediaType, mediaPaths, mediaUrls, and mediaTypes for received messages with attachments.

Verification:
- node scripts/run-vitest.mjs src/hooks/message-hook-mappers.test.ts
- .agents/skills/autoreview/scripts/autoreview --mode branch --base origin/main

Refs: openclaw#88740
Thanks @SebTardif.
Co-authored-by: Sebastien Tardif <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

P2 Normal backlog priority with limited blast radius. proof: sufficient ClawSweeper judged the real behavior proof convincing. proof: supplied External PR includes structured after-fix real behavior proof. rating: 🦞 diamond lobster Very strong PR readiness with only minor maintainer review expected. size: XS status: 👀 ready for maintainer look ClawSweeper has no concrete contributor-facing blocker left for this PR.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants