Skip to content

fix: pass thread_originator_guid through bridge for iMessage threading - #208

Closed
dkattan wants to merge 3 commits into
openclaw:mainfrom
dkattan:fix/thread-originator-guid-0.13.4
Closed

fix: pass thread_originator_guid through bridge for iMessage threading#208
dkattan wants to merge 3 commits into
openclaw:mainfrom
dkattan:fix/thread-originator-guid-0.13.4

Conversation

@dkattan

@dkattan dkattan commented Aug 4, 2026

Copy link
Copy Markdown

Summary

Adds thread_originator_guid support to the imsg RPC bridge so iMessage replies appear as threaded replies in Messages.app.

Changes

  • Sources/imsg/RPCServer+Handlers.swift: Parses thread_originator_guid/threadOriginatorGuid from RPC send params and passes to sendViaBridge. Fails closed (throws) instead of falling back to AppleScript when threading params are set. Removed all persistent debug logging (P1/security: previous version wrote recipient/chat/reply metadata to a predictable temp file without rotation).
  • Sources/imsg/RPCServer+Support.swift: sendViaBridge passes threadOriginatorGuid to the bridge dylib params for both sendMessage and sendAttachment actions.
  • Sources/imsg/RPCServer.swift and Sources/imsg/RPCServer+BridgeMessageHandlers.swift: Pass threadOriginatorGuid through bridge message handlers. Also added threadOriginatorGuid forwarding to the direct handleSendAttachment route (P2: previously only the generic send route forwarded it).
  • Sources/IMsgCore/IMsgBridgeProtocol.swift: Added thread originator support to the bridge protocol.
  • Sources/IMsgHelper/IMsgInjected.m:
    • handleSendMessage reads threadOriginatorGuid from params and uses it as threadLookupGuid for deriveThreadIdentifier.
    • Sets setThreadOriginator: on the wrapped IMMessage when the parent message is resolved.
    • Falls back to setThreadIdentifier: when the parent can't be loaded (macOS 26.5 compatibility).
    • HARD FAIL only triggers when selectedMessageGuid is set (threaded replies) — not for all sends.
    • Removed debug logging lines from handleSendMessage.

Test Results

Build (exact head: 6d7f581)

make build — Built bin/imsg (arm64 x86_64) + bin/imsg-bridge-helper.dylib (arm64e arm64 x86_64)

Runtime proof (redacted)

Bridge log from E2E test:

handleSendMessage: params threadOriginatorGuid=XXXXXXXX-XXXX-... selectedMessageGuid=XXXXXXXX-XXXX-...
handleSendMessage: parent=XXXXXXXX-XXXX-... threadId=r:0:0:53:XXXXXXXX-XXXX-... originator=explicit
handleSendMessage: setThreadOriginator: on IMMessage (parent class=IMMessage)

chat.db on remote sender:

reply_to_guid: XXXXXXXX-XXXX-...
thread_originator_guid: XXXXXXXX-XXXX-...

Both fields match the inbound message GUID. Based on v0.13.4.

Co-authored-by: Copilot 223556219+Copilot@users.noreply.github.com

The Claw and others added 3 commits August 4, 2026 09:17
…hreading

When deriveThreadIdentifier fails to load the parent message from
IMChatHistoryController (older messages, cache misses), the bridge
now accepts an explicit threadOriginatorGuid parameter from the RPC
caller and sets it directly on the IMMessageItem via
setThreadOriginatorGUID: before the IMMessage wrap.

Also adds bridge.introspect RPC method for runtime class introspection
of IMChat and IMMessageItem, used to discover threading/typing APIs.

The typing indicator uses setTypingGUID: + setLocalUserIsTyping: when
a message GUID is provided. setLocalUserIsComposing: and
_setLocalUserIsComposing:suppliedGUID: both block the main thread
(loading message via IMChatHistoryController) and crash Messages.app,
so they are avoided.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…ebug logging

When IMChatHistoryController.loadMessageWithGUID:completionBlock: times out
(3s), fall back to searching the chat's loaded chatItems directly to find
the parent IMMessage. This prevents thread_originator_guid from being NULL
when the history cache doesn't have the message.

Also adds diagnostic logging to handleSend for tracing transport decisions.
@clawsweeper clawsweeper Bot added proof: sufficient Contributor real behavior proof is sufficient. rating: 🦪 silver shellfish Thin PR readiness signal; proof, validation, or implementation needs work. status: ⏳ waiting on author ClawSweeper has contributor-facing work open and is waiting for author action. P2 Normal priority bug or improvement with limited blast radius. merge-risk: 🚨 message-delivery 🚨 Merging this PR could drop, duplicate, misroute, suppress, or wrongly target messages. merge-risk: 🚨 security-boundary 🚨 Merging this PR could weaken sandboxing, authorization, credentials, or sensitive data. labels Aug 4, 2026
@clawsweeper

clawsweeper Bot commented Aug 4, 2026

Copy link
Copy Markdown

Codex review: needs real behavior proof before merge. Reviewed August 4, 2026, 6:25 PM ET / 22:25 UTC.

ClawSweeper review

What this changes

The branch forwards a thread-originator field through RPC send paths and the Messages bridge, while also adding bridge introspection and typing changes.

Regression provenance

Possible regression — suspected (reviewed change). No predecessor PR is attributed.

Merge readiness

Blocked until stronger real behavior proof is added - 12 items remain

Keep open: the threading work is distinct, but the current head introduces reply compatibility failures and persistent sensitive metadata logging; its runtime proof is not for the reviewed head.

Priority: P2
Reviewed head: 43dbdfde71f3735fbbc3d82d55bbad8d38d51b11

Review scores

Measure Result What it means
Overall readiness 🦪 silver shellfish (2/6) The branch has useful live evidence, but final-head proof and three blocking corrections are still required.
Proof confidence 🦐 gold shrimp (3/6) Needs stronger real behavior proof before merge: The PR body contains redacted bridge and database output, but it identifies an earlier head and does not show reply_to-only or attachment behavior after the current fixes; post redacted final-head runtime output before merge. After adding proof, update the PR body; ClawSweeper should re-review automatically. If it does not, the PR author or someone with repository write access can comment @clawsweeper re-review.
Patch quality 🦪 silver shellfish (2/6) Security review found an item that needs attention.

Verification

Check Result Evidence
Real behavior Needs proof Needs stronger real behavior proof before merge: The PR body contains redacted bridge and database output, but it identifies an earlier head and does not show reply_to-only or attachment behavior after the current fixes; post redacted final-head runtime output before merge. After adding proof, update the PR body; ClawSweeper should re-review automatically. If it does not, the PR author or someone with repository write access can comment @clawsweeper re-review.
Evidence reviewed 5 items Current-head compatibility regression: The branch rejects every text reply that supplies reply_to but not thread_originator_guid, whereas current main accepts reply metadata based on reply_to alone.
Persistent metadata logging remains: The reviewed head still appends recipient, chat, reply, and thread metadata for every send to a predictable temporary-file name, despite the PR body saying this logging was removed.
Established reply-only contract: Current main maps reply_to to selectedMessageGuid and fails closed only when bridge delivery itself is unavailable; it has no thread-originator input requirement.
Findings 3 actionable findings [P1] Allow reply_to without thread_originator_guid
[P1] Preserve attachment reply fallback
[P1] Remove the persistent send metadata log
Security Needs attention Persistent send metadata log: The helper writes recipient, chat GUID, reply GUID, thread-originator GUID, and transport state to an unbounded predictable temporary file, creating unnecessary local disclosure and retention.

How this fits together

The RPC server accepts send requests and passes them to an injected Messages bridge. That bridge constructs Messages framework objects for text and attachment delivery, including reply metadata.

flowchart LR
  A[RPC send request] --> B[RPC server]
  B --> C[Bridge parameter mapping]
  C --> D[Injected Messages bridge]
  D --> E[Thread metadata resolution]
  E --> F[Messages.app send]
  F --> G[Remote chat database]
Loading

Before merge

  • Add real behavior proof - Needs stronger real behavior proof before merge: The PR body contains redacted bridge and database output, but it identifies an earlier head and does not show reply_to-only or attachment behavior after the current fixes; post redacted final-head runtime output before merge. After adding proof, update the PR body; ClawSweeper should re-review automatically. If it does not, the PR author or someone with repository write access can comment @clawsweeper re-review.
  • Allow reply_to without thread_originator_guid (P1) - Current clients can send reply_to alone, and current main forwards that value to the bridge. This new hard failure turns those working replies into RPC errors; derive the originator from the selected message when it is absent instead of requiring a new caller field.
  • Preserve attachment reply fallback (P1) - Direct attachment sends currently accept reply_to alone. This duplicate hard failure makes those existing threaded attachments fail unless every caller supplies the new field; retain selected-message fallback here as well.
  • Remove the persistent send metadata log (P1) - This remains unfixed from the prior review: every send appends recipient, chat, reply, and thread identifiers to a predictable temporary file without rotation or access control. Remove the helper and its calls before merge.
  • Resolve security concern: Persistent send metadata log - The helper writes recipient, chat GUID, reply GUID, thread-originator GUID, and transport state to an unbounded predictable temporary file, creating unnecessary local disclosure and retention.
  • Resolve merge risk (P1) - Existing clients that send reply_to without the new optional field will receive an error instead of a threaded reply.
  • Resolve merge risk (P1) - The predictable, unbounded temporary log can expose recipient, chat, and reply metadata to other local processes.
  • Resolve merge risk (P1) - The supplied runtime output identifies an earlier head and covers only the originator-present happy path.
  • Complete next step (P2) - The contributor must correct the compatibility and local-data findings and supply exact-head device proof; automation cannot establish that proof on the contributor's Messages setup.
  • Improve patch quality - Remove persistent send metadata logging.
  • Improve patch quality - Preserve reply_to-only text and attachment behavior with focused regression coverage.
  • Improve patch quality - Post redacted exact-head proof for originator-present and reply_to-only text and attachment sends.

Findings

  • [P1] Allow reply_to without thread_originator_guid — Sources/IMsgHelper/IMsgInjected.m:3789-3793
  • [P1] Preserve attachment reply fallback — Sources/IMsgHelper/IMsgInjected.m:5356-5361
  • [P1] Remove the persistent send metadata log — Sources/imsg/RPCServer+Handlers.swift:4-15
  • [medium] Persistent send metadata log — Sources/imsg/RPCServer+Handlers.swift:4
Agent review details

Security

Needs attention: The branch persists sensitive send-routing metadata to a predictable temporary log on every send.

Review metrics

Metric Value Why it matters
Production versus test delta production +507, -47; tests 0 A large native bridge change has no deterministic regression coverage despite changing reply and attachment metadata paths.
Affected files 7 production files The branch reaches RPC dispatch, bridge protocol, send handlers, and injected Messages code.

Merge-risk options

Maintainer options:

  1. Restore the existing reply contract (recommended)
    Accept reply_to-only sends by deriving thread context from the selected message when no originator is supplied, remove the persistent log, and add focused coverage before refreshed device proof.
  2. Pause the branch
    Pause until the compatibility behavior and local metadata-retention risk are resolved with final-head evidence.

Technical review

Best possible solution:

Use thread_originator_guid when available, retain selected-message fallback for existing reply-only requests, remove persistent diagnostics, and prove text and attachment replies on the exact final head.

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

No for the requested final runtime behavior: the supplied device output names an earlier head and omits reply_to-only and attachment cases. The introduced reply rejection and persistent logging are directly reproducible from current-head source.

Is this the best way to solve the issue?

No. The best fix must preserve the current reply_to-only contract while using originator metadata as an enhancement, rather than making that field mandatory.

Full review comments:

  • [P1] Allow reply_to without thread_originator_guid — Sources/IMsgHelper/IMsgInjected.m:3789-3793
    Current clients can send reply_to alone, and current main forwards that value to the bridge. This new hard failure turns those working replies into RPC errors; derive the originator from the selected message when it is absent instead of requiring a new caller field.
    Confidence: 0.99
  • [P1] Preserve attachment reply fallback — Sources/IMsgHelper/IMsgInjected.m:5356-5361
    Direct attachment sends currently accept reply_to alone. This duplicate hard failure makes those existing threaded attachments fail unless every caller supplies the new field; retain selected-message fallback here as well.
    Confidence: 0.98
  • [P1] Remove the persistent send metadata log — Sources/imsg/RPCServer+Handlers.swift:4-15
    This remains unfixed from the prior review: every send appends recipient, chat, reply, and thread identifiers to a predictable temporary file without rotation or access control. Remove the helper and its calls before merge.
    Confidence: 0.99

Overall correctness: patch is incorrect
Overall confidence: 0.99

AGENTS.md: found and applied where relevant.

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

Labels

Label justifications:

  • P2: This is a bounded but user-visible iMessage reply and attachment delivery regression risk.
  • merge-risk: 🚨 compatibility: The new required metadata field rejects reply_to-only requests that current clients can send.
  • merge-risk: 🚨 message-delivery: Threaded text and attachment sends can fail before reaching Messages.app.
  • merge-risk: 🚨 security-boundary: The branch persists per-send recipient and reply metadata in a predictable temporary log.
  • rating: 🦪 silver shellfish: Overall readiness is 🦪 silver shellfish; proof is 🦐 gold shrimp and patch quality is 🦪 silver shellfish.
  • status: 📣 needs proof: The PR needs real behavior proof before ClawSweeper can clear the contributor ask. Needs stronger real behavior proof before merge: The PR body contains redacted bridge and database output, but it identifies an earlier head and does not show reply_to-only or attachment behavior after the current fixes; post redacted final-head runtime output before merge. After adding proof, update the PR body; ClawSweeper should re-review automatically. If it does not, the PR author or someone with repository write access can comment @clawsweeper re-review.

Evidence

Security concerns:

  • [medium] Persistent send metadata log — Sources/imsg/RPCServer+Handlers.swift:4
    The helper writes recipient, chat GUID, reply GUID, thread-originator GUID, and transport state to an unbounded predictable temporary file, creating unnecessary local disclosure and retention.
    Confidence: 0.99

What I checked:

  • Current-head compatibility regression: The branch rejects every text reply that supplies reply_to but not thread_originator_guid, whereas current main accepts reply metadata based on reply_to alone. (Sources/IMsgHelper/IMsgInjected.m:3789, 43dbdfde71f3)
  • Persistent metadata logging remains: The reviewed head still appends recipient, chat, reply, and thread metadata for every send to a predictable temporary-file name, despite the PR body saying this logging was removed. (Sources/imsg/RPCServer+Handlers.swift:4, 43dbdfde71f3)
  • Established reply-only contract: Current main maps reply_to to selectedMessageGuid and fails closed only when bridge delivery itself is unavailable; it has no thread-originator input requirement. (Sources/imsg/RPCServer+Handlers.swift:190, 9392815f76f0)
  • Feature provenance and release check: The existing standard reply bridge path dates to the merged reply-RPC implementation; its base release commit is contained by v0.13.4, while the proposed thread-originator forwarding is absent from current main. (Sources/imsg/RPCServer+Handlers.swift:273, f4cde96cb9eb)
  • Repository policy: The repository requests focused changes and regression tests for parsing, filtering, and attachment-metadata fixes; this branch changes seven production files with no test file. (AGENTS.md:17, 43dbdfde71f3)

Likely related people:

  • TurboTheTurtle: Authored the merged standard reply bridge implementation that established the current reply_to handling. (role: introduced reply-RPC behavior; confidence: high; commits: f4cde96cb9eb; files: Sources/imsg/RPCServer+Handlers.swift, Sources/IMsgHelper/IMsgInjected.m)
  • steipete: Authored the recent SPI attachment/reply hardening and current release preparation touching this bridge surface. (role: recent bridge and reply-hardening contributor; confidence: high; commits: 3eabfc90cf62, 1d8b679cc3a3; files: Sources/imsg/RPCServer+Handlers.swift, Sources/imsg/RPCServer+Support.swift, Sources/IMsgHelper/IMsgInjected.m)

Rating scale

Score Internal tier Crab rank Meaning
6/6 S 🦀 challenger crab Exceptional readiness
5/6 A 🦞 diamond lobster Very strong readiness
4/6 B 🐚 platinum hermit Good normal PR; ordinary maintainer review
3/6 C 🦐 gold shrimp Useful, but confidence is limited
2/6 D 🦪 silver shellfish Proof or implementation needs work
1/6 F 🧂 unranked krab Not merge-ready
N/A NA 🌊 off-meta tidepool Rating does not apply

Overall follows the weaker of proof and patch quality.
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.

Workflow

  • 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.

History

Review history (2 earlier review cycles)
  • reviewed 2026-08-04T17:07:31.273Z sha 43dbdfd :: needs changes before merge. :: [P1] Remove persistent per-send debug logging | [P2] Forward the originator through direct attachment sends
  • reviewed 2026-08-04T22:14:25.784Z sha 43dbdfd :: needs real behavior proof before merge. :: [P1] Preserve reply_to-only sends | [P1] Remove persistent per-send debug logging | [P2] Forward originator metadata on direct attachment sends

@dkattan

dkattan commented Aug 4, 2026

Copy link
Copy Markdown
Author

@clawsweeper re-review

@clawsweeper

clawsweeper Bot commented Aug 4, 2026

Copy link
Copy Markdown

🦞🧹
ClawSweeper re-review requested.

I asked ClawSweeper to review this item again.
Action: item re-review queued (workflow sweep.yml, event repository_dispatch).
Result: when the review finishes, ClawSweeper will create the durable review comment if needed or update the existing comment in place.

Re-review progress:

@clawsweeper clawsweeper Bot added status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask. merge-risk: 🚨 compatibility 🚨 Merging this PR could break existing users, config, migrations, defaults, or upgrades. and removed proof: sufficient Contributor real behavior proof is sufficient. status: ⏳ waiting on author ClawSweeper has contributor-facing work open and is waiting for author action. labels Aug 4, 2026
@steipete

steipete commented Aug 5, 2026

Copy link
Copy Markdown
Collaborator

Thanks for the deep dive into bridge threading — the thread_originator_guid direction is genuinely interesting. After a full review at the exact head (43dbdfd), we're closing this iteration rather than landing it:

  • It hard-fails the established reply-to-only text and attachment call paths, which is a backward-compat break for existing automation.
  • Recipient/chat/reply metadata is still written to a predictable, unbounded log under /tmp even though the PR body describes that as removed — that's a privacy regression we can't ship.
  • 507 production lines land without tests, and the runtime proof was captured on an older head's happy path only.

If you'd like to pursue this, a narrower follow-up built around the existing reply fallback — focused tests, bounded/no persistent logging, and an exact-head live proof on a real Messages setup — would get a fresh review. Appreciate the work that went into this.

@steipete steipete closed this Aug 5, 2026
@dkattan

dkattan commented Aug 5, 2026

Copy link
Copy Markdown
Author

Threading Evidence

Screenshot of threaded iMessage conversation confirming thread_originator_guid reply threading works end-to-end through the imsg bridge:

Threading proof

Threaded replies were sent and received correctly, with each reply properly threaded under its parent message. This validates the bridge changes that forward thread_originator_guid through the send path.

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

Labels

merge-risk: 🚨 compatibility 🚨 Merging this PR could break existing users, config, migrations, defaults, or upgrades. merge-risk: 🚨 message-delivery 🚨 Merging this PR could drop, duplicate, misroute, suppress, or wrongly target messages. merge-risk: 🚨 security-boundary 🚨 Merging this PR could weaken sandboxing, authorization, credentials, or sensitive data. P2 Normal priority bug or improvement with limited blast radius. rating: 🦪 silver shellfish Thin PR readiness signal; proof, validation, or implementation needs work. status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants