Skip to content

fix(imessage): stage remote media before plugin dispatch#91803

Merged
omarshahine merged 1 commit into
openclaw:mainfrom
TurboTheTurtle:codex/91744-imessage-staged-media-path
Jun 28, 2026
Merged

fix(imessage): stage remote media before plugin dispatch#91803
omarshahine merged 1 commit into
openclaw:mainfrom
TurboTheTurtle:codex/91744-imessage-staged-media-path

Conversation

@TurboTheTurtle

@TurboTheTurtle TurboTheTurtle commented Jun 10, 2026

Copy link
Copy Markdown
Contributor

Closes #91744.

What Problem This Solves

Remote iMessage attachments can arrive with MediaRemoteHost and raw macOS Messages paths such as /Users/.../Library/Messages/Attachments/.... Plugin-bound Codex inbound claims could build metadata before OpenClaw staged those paths, so Codex could receive an unreadable remote Mac path instead of a local/cache image path.

Summary

  • Stage remote inbound iMessage media before plugin-bound Codex inbound-claim metadata is built.
  • Use cache-mode staging for plugin-bound Codex claims so Codex receives a local/cache localImage path instead of a raw remote Mac Messages attachment path.
  • Keep the normal reply path single-stage; ordinary non-plugin remote iMessage media still stages later in get-reply.
  • For message_received hooks where staging has not run, omit the live mediaPath/mediaUrl fields and include mediaStagingPending, mediaRemoteHost, and originalMedia* metadata so consumers can detect the pending remote-media contract.

Evidence

Final head: 4a0c2e3fc8c76cfc32f656662650daf54737d0d1.

Scenario executed: near-real remote iMessage MediaRemoteHost runtime path using the real stageRemoteInboundMediaIfNeeded, message hook mapper, and Codex turn-input builder. The harness used an isolated OpenClaw state dir and an SCP-compatible local shim to simulate successful remote attachment transfer without exposing live Messages data.

Proof harness: node --import tsx --input-type=module <inline proof harness>.

{
  "prHeadSha": "4a0c2e3fc8c76cfc32f656662650daf54737d0d1",
  "scenario": "near-real remote iMessage MediaRemoteHost staging before plugin-bound Codex claim",
  "redactedOriginalRemoteMedia": {
    "provider": "imessage",
    "accountId": "default",
    "mediaRemoteHost": "openclaw-mac.example",
    "mediaPath": "/Users/redacted/Library/Messages/Attachments/AA/BB/photo.jpg",
    "mediaType": "image/jpeg"
  },
  "stageResult": {
    "staged": true,
    "mediaStagedFlag": true,
    "cacheFileExists": true
  },
  "pluginClaimMediaPath": "/var/folders/.../state/media/remote-cache/agent-main-imessage-direct-chat--2a3a4048/photo.jpg",
  "codexInput": [
    { "type": "text", "text": "what is this?", "text_elements": [] },
    { "type": "localImage", "path": "/var/folders/.../state/media/remote-cache/agent-main-imessage-direct-chat--2a3a4048/photo.jpg" },
    { "type": "localImage", "path": "/var/folders/.../state/media/remote-cache/agent-main-imessage-direct-chat--2a3a4048/photo.jpg" }
  ],
  "checks": {
    "stagedBeforeCodexClaim": true,
    "codexReceivesLocalImage": true,
    "stagedPathIsRemoteCache": true,
    "rawMessagesPathVisible": false
  }
}

Observed result: the plugin-bound claim metadata is rewritten from the raw remote Mac attachment path to the staged remote-cache path before Codex input is built, and Codex receives localImage entries for that staged cache path. The raw /Users/.../Messages/Attachments/... path does not reach Codex.

What was not tested: a live remote Mac/iMessage conversation; the proof uses a near-real remote-host scenario with a local SCP shim to avoid requiring live private Messages credentials.

Validation

  • Focused remote iMessage dispatch regressions passed, including plugin-bound staging and the message_received pending-stage metadata contract.
  • Hook mapper, SCP remote-cache staging, Codex conversation turn-input, and dispatch regressions passed in the rebased branch.
  • Formatting, lint, and whitespace checks passed.

AI-assisted: yes.

@TurboTheTurtle

Copy link
Copy Markdown
Contributor Author

@clawsweeper re-review

@clawsweeper

clawsweeper Bot commented Jun 10, 2026

Copy link
Copy Markdown
Contributor

🦞🧹
ClawSweeper re-review requested.

I asked ClawSweeper to review this item again.
Action: item re-review queued (workflow sweep.yml, event repository_dispatch).
Result: the existing ClawSweeper review comment will be edited in place when the review finishes.

@openclaw-barnacle openclaw-barnacle Bot added size: M triage: mock-only-proof Candidate: PR proof only shows tests, mocks, snapshots, lint, typecheck, or CI. labels Jun 10, 2026
@clawsweeper

clawsweeper Bot commented Jun 10, 2026

Copy link
Copy Markdown
Contributor

Codex review: needs maintainer review before merge. Reviewed June 23, 2026, 2:36 PM ET / 18:36 UTC.

Summary
The PR stages remote iMessage media into the local cache before plugin-bound Codex inbound-claim metadata is built, adds pending/original media metadata for unstaged message_received hooks, and covers the dispatch, staging, hook-mapper, and Codex turn-input paths with tests.

PR surface: Source +137, Tests +387. Total +524 across 8 files.

Reproducibility: yes. The linked issue plus current-main source show plugin-bound Codex claim metadata is built from raw remote iMessage media before dispatch-side staging, and the PR body supplies final-head terminal proof that the staged cache path reaches Codex after the fix.

Review metrics: 1 noteworthy metric.

  • Plugin hook media contract: 1 metadata contract changed. Remote unstaged message_received events now use pending/original metadata instead of live raw media fields, which is compatibility-sensitive for hook consumers.

Stored data model
Persistent data-model change detected: persistent cache schema: extensions/codex/src/conversation-turn-input.test.ts, persistent cache schema: src/auto-reply/reply.stage-sandbox-media.scp-remote-path.test.ts, persistent cache schema: src/auto-reply/reply/stage-remote-inbound-media.ts, persistent cache schema: src/auto-reply/reply/stage-sandbox-media.ts, serialized state: src/auto-reply/reply.stage-sandbox-media.scp-remote-path.test.ts, vector/embedding metadata: extensions/codex/src/conversation-turn-input.test.ts, and 2 more. Confirm migration or upgrade compatibility proof before merge.

Root-cause cluster
Relationship: fixed_by_candidate
Canonical: #91744
Summary: The current PR is the open candidate fix for the canonical remote iMessage attachment path issue; the earlier related issue and PR fixed the pre-understanding staging order but not the plugin-bound Codex metadata path.

Members:

Proposal only: this assessment does not dispatch repair, suppress jobs, mutate sibling items, close, or merge anything.

Merge readiness
Overall: 🐚 platinum hermit
Proof: 🐚 platinum hermit
Patch quality: 🐚 platinum hermit
Result: ready for maintainer review.

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

Rank-up moves:

  • [P2] Have a maintainer explicitly accept the pending/original message_received media contract before merge.

Risk before merge

  • [P1] The PR intentionally changes the plugin-visible message_received contract for unstaged remote iMessage attachments: consumers no longer receive raw remote paths as live media fields and must read mediaStagingPending/originalMedia* metadata instead.
  • [P1] The plugin-bound claim path now performs remote SCP staging before the Codex plugin gets the turn, so a slow or unavailable remote host can delay that dispatch path even though staging failures are handled as empty staged results.

Maintainer options:

  1. Accept the pending-media hook contract (recommended)
    Land after maintainer review confirms remote message_received events should expose pending/original metadata instead of raw remote paths as live media fields.
  2. Preserve existing hook semantics
    If existing plugins must keep raw mediaPath/mediaPaths on message_received, revise the contract before merge so the new pending/original fields are additive and clearly documented.
  3. Pause for live remote-host proof
    If maintainers require proof beyond the final-head near-real SCP harness, pause until a redacted Linux-gateway plus remote-Mac iMessage run demonstrates the same staged Codex input.

Next step before merge

  • No automated repair is needed; the remaining action is maintainer review of the compatibility-sensitive hook metadata change and dispatch-time SCP staging tradeoff.

Security
Cleared: No concrete security or supply-chain issue was found; the PR reuses existing SCP normalization and attachment-root allowlist staging without adding dependencies, workflows, secrets, or new execution sources.

Review details

Best possible solution:

Land this dispatch-layer staging fix once maintainers accept the explicit pending-media hook contract; keep Codex consuming staged localImage paths and leave normal get-reply staging single-owner for non-plugin turns.

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

Yes. The linked issue plus current-main source show plugin-bound Codex claim metadata is built from raw remote iMessage media before dispatch-side staging, and the PR body supplies final-head terminal proof that the staged cache path reaches Codex after the fix.

Is this the best way to solve the issue?

Yes. Staging the dispatch hook copy before plugin-bound Codex claims is the right layer because Codex expects readable localImage paths; teaching Codex about remote macOS Messages paths would be channel-specific and less maintainable.

AGENTS.md: found and applied where relevant.

Codex review notes: model internal, reasoning high; reviewed against a86ca4f4ba97.

Label changes

Label justifications:

  • P1: The PR fixes a real iMessage-to-Codex media workflow where remote attachments reach Codex as unreadable Mac filesystem paths.
  • merge-risk: 🚨 compatibility: The diff changes plugin-visible message_received media metadata semantics for unstaged remote iMessage attachments.
  • merge-risk: 🚨 availability: The diff adds remote SCP staging before plugin-bound Codex claim dispatch, so remote-host stalls can affect that inbound path.
  • rating: 🐚 platinum hermit: Overall readiness is 🐚 platinum hermit; proof is 🐚 platinum hermit and patch quality is 🐚 platinum hermit.
  • status: 👀 ready for maintainer look: ClawSweeper has no concrete contributor-facing blocker left for this PR. Sufficient (terminal): The PR body contains final-head redacted terminal proof from a near-real remote-host staging harness showing the raw iMessage path rewritten to a remote-cache localImage before Codex input is built.
  • proof: sufficient: Contributor real behavior proof is sufficient. The PR body contains final-head redacted terminal proof from a near-real remote-host staging harness showing the raw iMessage path rewritten to a remote-cache localImage before Codex input is built.
Evidence reviewed

PR surface:

Source +137, Tests +387. Total +524 across 8 files.

View PR surface stats
Area Files Added Removed Net
Source 5 191 54 +137
Tests 3 390 3 +387
Docs 0 0 0 0
Config 0 0 0 0
Generated 0 0 0 0
Other 0 0 0 0
Total 8 581 57 +524

What I checked:

  • Live PR state: The PR is open, mergeable, clean, and currently changes only the iMessage/Codex media staging path plus focused tests after the unrelated script/CI files were removed. (4a0c2e3fc8c7)
  • Patch stages hook metadata before plugin claims: At PR head, dispatch builds hook state from a copy of ctx, stages remote media in cache mode before runInboundClaimForPluginOutcome, and rebuilds inbound-claim event/context from the staged hook copy. (src/auto-reply/reply/dispatch-from-config.ts:1560, 4a0c2e3fc8c7)
  • Message-received hook contract is explicit: At PR head, unstaged remote iMessage message_received events omit live mediaPath/mediaUrl/mediaType fields while carrying mediaStagingPending, mediaRemoteHost, and originalMedia* metadata. (src/auto-reply/reply/dispatch-from-config.ts:1601, 4a0c2e3fc8c7)
  • Cache-mode staging uses existing safe remote staging path: stageSandboxMedia now accepts remoteMediaMode: "cache" and uses the existing remote attachment allowlist/SCP path while forcing the remote-cache destination instead of a sandbox-relative path. (src/auto-reply/reply/stage-sandbox-media.ts:38, 4a0c2e3fc8c7)
  • Codex consumes local image paths as filesystem reads: Upstream Codex defines localImage/local_image as a local path that is read from disk and converted to model-visible image content; raw remote Mac paths therefore fail on a Linux gateway. (../codex/codex-rs/protocol/src/user_input.rs:33, b539e3e72bbc)
  • Codex app-server protocol accepts localImage: The app-server README documents turn/start input variants including localImage with an absolute path, matching the OpenClaw Codex plugin's localImage payload shape. (../codex/codex-rs/app-server/README.md:714, b539e3e72bbc)

Likely related people:

  • TurboTheTurtle: Authored the merged pre-understanding remote iMessage staging fix for the earlier related issue and authored this candidate fix for the remaining plugin-bound Codex metadata path. (role: prior related fix contributor and current candidate-fix author; confidence: high; commits: ae52be9f3279, 4a0c2e3fc8c7; files: src/auto-reply/reply/get-reply.ts, src/auto-reply/reply/get-reply.message-hooks.test.ts, src/auto-reply/reply/dispatch-from-config.ts)
  • steipete: GitHub history shows the Codex app-server protocol bridge commit introduced the localImage turn-input surface, and recent history shows substantial related staging/security work around media paths. (role: Codex bridge and media staging area contributor; confidence: high; commits: 69566e43cb8b, 538605ff44d2, 41c5990d1313; files: extensions/codex/src/conversation-turn-input.ts, extensions/codex/src/app-server/protocol.ts, src/auto-reply/reply/stage-sandbox-media.ts)
  • WarrenJones: History shows the plugin message_received media metadata surface was added in a focused hooks commit, which is the compatibility-sensitive contract this PR adjusts for remote pending media. (role: hook media metadata contributor; confidence: medium; commits: 65e2120f8c0d; files: src/hooks/message-hook-mappers.ts, src/hooks/message-hook-mappers.test.ts)
  • SebTardif: History shows a follow-up commit aligned internal message_received media metadata with the plugin hook mapper, so they are relevant to the internal-hook side of the contract. (role: internal hook media metadata contributor; confidence: medium; commits: 9a3e7d4f51ef; files: src/hooks/message-hook-mappers.ts, src/hooks/message-hook-mappers.test.ts)
  • jalehman: Recent GitHub history and shallow blame both point to broad recent auto-reply/session routing changes in dispatch-from-config, making this a useful routing contact for the dispatch path. (role: recent auto-reply session surface contributor; confidence: medium; commits: 127e174c9e4d, 475252453b4b; files: src/auto-reply/reply/dispatch-from-config.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.

@openclaw-barnacle openclaw-barnacle Bot added proof: supplied External PR includes structured after-fix real behavior proof. and removed triage: mock-only-proof Candidate: PR proof only shows tests, mocks, snapshots, lint, typecheck, or CI. labels Jun 10, 2026
@clawsweeper clawsweeper Bot added proof: sufficient ClawSweeper judged the real behavior proof convincing. rating: 🐚 platinum hermit Good normal PR readiness with ordinary maintainer review expected. status: 👀 ready for maintainer look ClawSweeper has no concrete contributor-facing blocker left for this PR. P1 High-priority user-facing bug, regression, or broken workflow. merge-risk: 🚨 availability 🚨 May cause crashes, hangs, restart loops, stalls, or process outages. labels Jun 10, 2026
@omarshahine
omarshahine force-pushed the codex/91744-imessage-staged-media-path branch from 3363914 to 3c32fcf Compare June 15, 2026 22:13
@openclaw-barnacle openclaw-barnacle Bot removed the proof: sufficient ClawSweeper judged the real behavior proof convincing. label Jun 15, 2026

Copy link
Copy Markdown
Contributor

@clawsweeper re-review

@clawsweeper

clawsweeper Bot commented Jun 15, 2026

Copy link
Copy Markdown
Contributor

🦞🧹
ClawSweeper re-review requested.

I asked ClawSweeper to review this item again.
Action: item re-review queued (workflow sweep.yml, event repository_dispatch).
Result: the existing ClawSweeper review comment will be edited in place when the review finishes.

Re-review progress:

@TurboTheTurtle

Copy link
Copy Markdown
Contributor Author

@clawsweeper re-review

@clawsweeper clawsweeper Bot removed rating: 🐚 platinum hermit Good normal PR readiness with ordinary maintainer review expected. status: 👀 ready for maintainer look ClawSweeper has no concrete contributor-facing blocker left for this PR. labels Jun 16, 2026
@omarshahine

Copy link
Copy Markdown
Contributor

@clawsweeper re-review

@clawsweeper

clawsweeper Bot commented Jun 16, 2026

Copy link
Copy Markdown
Contributor

🦞🧹
ClawSweeper re-review requested.

I asked ClawSweeper to review this item again.
Action: item re-review queued (workflow sweep.yml, event repository_dispatch).
Result: the existing ClawSweeper review comment will be edited in place when the review finishes.

Re-review progress:

@clawsweeper clawsweeper Bot added proof: sufficient ClawSweeper judged the real behavior proof convincing. rating: 🧂 unranked krab Not merge-ready due to missing proof or serious correctness/safety concerns. status: ⏳ waiting on author ClawSweeper has contributor-facing work open and is waiting for author action. labels Jun 16, 2026
@TurboTheTurtle
TurboTheTurtle force-pushed the codex/91744-imessage-staged-media-path branch from adf424b to d1c4813 Compare June 17, 2026 01:15
@openclaw-barnacle openclaw-barnacle Bot added agents Agent runtime and tooling and removed proof: sufficient ClawSweeper judged the real behavior proof convincing. labels Jun 17, 2026
@TurboTheTurtle
TurboTheTurtle force-pushed the codex/91744-imessage-staged-media-path branch from 1205640 to 950a6f4 Compare June 17, 2026 01:27
@openclaw-barnacle openclaw-barnacle Bot added the agents Agent runtime and tooling label Jun 22, 2026
@TurboTheTurtle
TurboTheTurtle force-pushed the codex/91744-imessage-staged-media-path branch 4 times, most recently from 6446e7b to 9fedd5b Compare June 22, 2026 06:04
@clawsweeper clawsweeper Bot removed proof: sufficient ClawSweeper judged the real behavior proof convincing. rating: 🐚 platinum hermit Good normal PR readiness with ordinary maintainer review expected. status: 👀 ready for maintainer look ClawSweeper has no concrete contributor-facing blocker left for this PR. labels Jun 22, 2026
@omarshahine
omarshahine force-pushed the codex/91744-imessage-staged-media-path branch from 9fedd5b to cdd1976 Compare June 23, 2026 03:17
@omarshahine

Copy link
Copy Markdown
Contributor

@clawsweeper re-review

@clawsweeper

clawsweeper Bot commented Jun 23, 2026

Copy link
Copy Markdown
Contributor

🦞🧹
ClawSweeper re-review requested.

I asked ClawSweeper to review this item again.
Action: item re-review queued (workflow sweep.yml, event repository_dispatch).
Result: the existing ClawSweeper review comment will be edited in place when the review finishes.

@omarshahine

Copy link
Copy Markdown
Contributor

@TurboTheTurtle the staged-path proof looks good and CI is green, thanks for pushing this through. Two things before it can land:

1. Drop the unrelated files. This PR should be only the iMessage staging fix. These three are unrelated scope bundled in to get the branch green and should move to their own PR(s):

  • scripts/github/resolve-openclaw-ref.sh (bash 3.2 compat for mapfile / local -n)
  • src/agents/embedded-agent-runner/run.fast-mode-auto.test.ts
  • test/scripts/bench-gateway-restart.test.ts

They are fine changes on their own, but bundling CI fixes into a behavior PR makes the diff harder to reason about and to revert cleanly. Please split them out and rebase this branch down to the staging change plus its own tests.

2. Confirm the message_received media contract. In dispatch-from-config.ts, buildMessageReceivedHookContext() deletes MediaPath / MediaPaths / MediaUrl / MediaUrls / MediaType / MediaTypes from the message_received event whenever MediaRemoteHost is set and staging has not run. The effect is that a message_received consumer for a remote iMessage attachment now sees no media at all, so it cannot even tell an image was attached.

Is total omission the intended contract? #91744 suggested keeping the raw Mac path as original/debug metadata rather than dropping it. I would prefer the hook event still carry something (for example the original path under an originalMedia* / debug field, or a "media pending stage" marker) so message_received consumers are not blind to attachments. If full omission is deliberate, please add an inline comment at that branch stating the contract and why a message_received consumer should not receive remote media.

@TurboTheTurtle
TurboTheTurtle force-pushed the codex/91744-imessage-staged-media-path branch from cdd1976 to 4a0c2e3 Compare June 23, 2026 06:09
@openclaw-barnacle openclaw-barnacle Bot removed scripts Repository scripts agents Agent runtime and tooling labels Jun 23, 2026
@clawsweeper clawsweeper Bot added proof: sufficient ClawSweeper judged the real behavior proof convincing. rating: 🐚 platinum hermit Good normal PR readiness with ordinary maintainer review expected. status: 👀 ready for maintainer look ClawSweeper has no concrete contributor-facing blocker left for this PR. and removed merge-risk: 🚨 automation 🚨 May affect CI, automerge, proof capture, label sync, or maintainer automation. labels Jun 23, 2026
@omarshahine
omarshahine force-pushed the codex/91744-imessage-staged-media-path branch from 4a0c2e3 to 3302341 Compare June 28, 2026 02:34
@omarshahine
omarshahine merged commit 830467b into openclaw:main Jun 28, 2026
92 checks passed
@omarshahine

Copy link
Copy Markdown
Contributor

Merged via squash.

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

Labels

extensions: codex merge-risk: 🚨 availability 🚨 May cause crashes, hangs, restart loops, stalls, or process outages. merge-risk: 🚨 compatibility 🚨 May break existing users, config, migrations, defaults, or upgrade paths. P1 High-priority user-facing bug, regression, or broken workflow. proof: sufficient ClawSweeper judged the real behavior proof convincing. proof: supplied External PR includes structured after-fix real behavior proof. rating: 🐚 platinum hermit Good normal PR readiness with ordinary maintainer review expected. size: L 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.

[Bug]: Remote iMessage attachment staging succeeds but Codex-visible path remains raw Mac path

2 participants