Skip to content

fix(channels): send pairing approvals from the approved account - #137268

Merged
steipete merged 5 commits into
openclaw:mainfrom
MarMarLabs:fix/pairing-approval-account
Sep 6, 2026
Merged

steipete merged 5 commits into
openclaw:mainfrom
MarMarLabs:fix/pairing-approval-account

Conversation

@MarMarLabs

@MarMarLabs MarMarLabs commented Sep 3, 2026 •

Copy link
Copy Markdown
Contributor

What Problem This Solves

Pairing approvals on a named account could send the notification through the default account. The affected plugins are iMessage, IRC, LINE, Signal, Synology Chat, Zalo, and Zalo Personal. Synology Chat and Zalo could also report successful notification after an unsuccessful send.

Why This Change Was Made

The core approval callback already carries the selected account. Preserve it through each plugin's existing account resolver and sender. iMessage, Synology Chat, and Zalo reuse their existing outbound delivery functions, removing duplicate notification paths. This also preserves Zalo's account proxy and reports Synology/Zalo delivery failures through the existing notification-failure outcome.

The delivered notification text is unchanged. Pairing decisions and stored approvals are unchanged. Production is +42/−46, net −4 lines; tests are +467/−1.

User Impact

Approval notifications come from the approved account. An unsuccessful notification can be reported as failed while the completed approval remains valid.

Evidence

  • Initial hosted comparison: all seven selected-account cases failed against the original source for the intended credential/transport mismatch; omitted-account controls passed. Real core notifier → Synology plugin → loopback HTTP demonstrated the wrong default endpoint and silent failures before the repair, then the selected endpoint and explicit failed-send outcomes afterward.
  • Focused completion: corrected Zalo baseline produced three intended failures and one passing default control. The remaining candidate suites and Gateway notification-failure control passed. Across both runs, all eight focused suites and the Gateway control passed 204 tests. All sixteen candidate file hashes match the reviewed source.
  • The first candidate run caught an incorrect full-text expectation in the new Zalo proxy test. The test was corrected to the message already sent by the original notifier; the completion run retained exact text, token, target, and proxy assertions.
  • iMessage text correction: the separate final review found that owner reuse activated an old generic declaration and changed the iMessage welcome text. Restored the shared message used by the original notifier and replaced broad string matching with the exact RPC text for both named/default accounts. Both cases reproduced the incorrect text on the published intermediate head; the corrected full iMessage suite passed all 15 tests, with all sixteen candidate hashes verified. The other channels’ evidence is reused only for unchanged files.
  • Fresh autoreview, a separate isolated exact-head review, and independent source/proof verification accepted the correction. Required CI passed on the exact corrected head. Proof uses synthetic data and real loopback HTTP; it does not claim authenticated external service acceptance.

Corrected head: 2a54928c184. Fresh reviews, required CI and native landing checks passed. Merged as b75423c2fcc; all sixteen landed files match the reviewed source.

Original contribution by MarMar Labs (@marmar9615-cloud).

The pairing approval contract carries the account a request was selected
on, but the LINE, Signal, IRC, and Synology Chat notifiers dropped it and
resolved the default account instead. On a multi-account setup the
approval went out from the wrong account with the wrong credentials while
the gateway still reported the notification as sent.

Each of the four notifiers now passes the approved account id to its
account resolver. Omitting the account id still resolves the default
account exactly as before.
@clawsweeper

clawsweeper Bot commented Sep 3, 2026 •

Copy link
Copy Markdown
Contributor

🦞👀
ClawSweeper picked this up.

Pull request received. I will update this pull request when review starts.

ClawSweeper review complete

ClawSweeper finished reviewing this revision. The review result is being finalized.

View the workflow run.

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Sep 3, 2026 •

Copy link
Copy Markdown
Contributor

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-09-05T11:19:40.728896Z eaf609f New commits
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@clawsweeper clawsweeper Bot added P2 Normal backlog priority with limited blast radius. merge-risk: 🚨 message-delivery 🚨 May drop, duplicate, misroute, suppress, or wrongly target messages. 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. labels Sep 3, 2026
@clawsweeper

clawsweeper Bot commented Sep 3, 2026 •

Copy link
Copy Markdown
Contributor

Codex review: needs maintainer review before merge. Reviewed September 6, 2026, 6:20 AM ET / 10:20 UTC.

ClawSweeper review

What this changes

Seven channel plugins preserve the approved account when sending pairing confirmations, reuse existing delivery functions, and report unsuccessful notifications.

Merge readiness

✅ Ready for maintainer review

This repair remains necessary on current main. The previous iMessage text finding and Synology/Zalo delivery findings are addressed; no remaining actionable patch defect was found.

Priority: P2
Reviewed head: 2a54928c1847bca05be0cc57afba4ac0ce0c40e2

Review scores

Measure Result What it means
Overall readiness 🐚 platinum hermit (4/6) A focused owner-boundary repair with production-path evidence, useful regression coverage, and the previous findings addressed.
Proof confidence 🐚 platinum hermit (4/6) ✨ media proof bonus Sufficient (linked_artifact): Captured evidence reports the real core notifier and Synology HTTP client reaching the selected loopback endpoint and surfacing injected send failures, supplemented by all seven account-routing controls and the corrected iMessage text checks. Linked logs were unreachable here; authenticated external-service acceptance is not claimed.
Patch quality 🐚 platinum hermit (4/6) No actionable review findings were identified.

Verification

Check Result Evidence
Real behavior Verified Sufficient (linked_artifact): Captured evidence reports the real core notifier and Synology HTTP client reaching the selected loopback endpoint and surfacing injected send failures, supplemented by all seven account-routing controls and the corrected iMessage text checks. Linked logs were unreachable here; authenticated external-service acceptance is not claimed.
Evidence reviewed 11 items Current main still needs the repair: The fetched main revision still drops accountId in the seven affected notification paths. IRC resolves its sender without accountId; iMessage and Zalo retain separate notification helpers that omit the selected account.
Latest-release check: The latest supplied release also contains the IRC notifier without accountId, establishing that the central account-selection defect is present in shipped source.
Existing owner and callback contract: The Gateway supplies the approved account to notifyPairingApproved, and createTextPairingAdapter preserves that context. Notification exceptions become notification='failed' after approval; the existing Gateway regression test explicitly protects completed approval when notification fails. This is OpenClaw channel routing and does not depend on a Codex runtime or protocol contract.
Findings None None.
Security None None.

How this fits together

Pairing lets an operator approve a sender’s access through the CLI or Gateway. After approval, the channel plugin sends an optional confirmation using the selected account and reports notification success or failure separately.

flowchart TD
  A[Operator approves sender] --> B[Pairing approval owner]
  B --> C[Access approval recorded]
  B --> D[Selected account and recipient]
  D --> E[Channel notification adapter]
  E --> F[Existing account sender]
  F --> G[Confirmation delivered]
  F --> H[Notification failure reported]
Loading

Before merge

None.

Agent review details

Security

None.

PR surface

Source -4, Tests +466. Total +462 across 16 files.

View PR surface stats
Area Files Added Removed Net
Source 9 42 46 -4
Tests 7 467 1 +466
Docs 0 0 0 0
Config 0 0 0 0
Generated 0 0 0 0
Other 0 0 0 0
Total 16 509 47 +462

Review metrics

Metric Value Why it matters
Production versus regression coverage Production net -4 LOC; tests net +466 LOC Reusing existing delivery owners absorbs the repair while regression coverage protects account selection and notification outcomes.

Technical review

Best possible solution:

Keep account selection in each plugin’s existing resolver and sender, preserving confirmation text and reporting delivery failure without undoing access approval.

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

Yes: current source clearly drops the selected account before sending, so approving a named account with distinct transport credentials exercises the defect. This worker inspected source and supplied before/after results but did not execute a reproduction.

Is this the best way to solve the issue?

Yes: forwarding the existing account context and reusing account-aware delivery owners repairs the plugin boundary; changing the core callback or adding another notification API would duplicate behavior already supported.

AGENTS.md: found and applied where relevant.

Codex review notes: model internal, reasoning medium; reviewed against b5412c2a40b2.

Labels

Label justifications:

  • P2: This fixes account-specific confirmation routing and misleading notification outcomes with a limited channel-plugin blast radius.
  • 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 (linked_artifact): Captured evidence reports the real core notifier and Synology HTTP client reaching the selected loopback endpoint and surfacing injected send failures, supplemented by all seven account-routing controls and the corrected iMessage text checks. Linked logs were unreachable here; authenticated external-service acceptance is not claimed.
  • proof: sufficient: Contributor real behavior proof is sufficient. Captured evidence reports the real core notifier and Synology HTTP client reaching the selected loopback endpoint and surfacing injected send failures, supplemented by all seven account-routing controls and the corrected iMessage text checks. Linked logs were unreachable here; authenticated external-service acceptance is not claimed.

Evidence

What I checked:

  • Current main still needs the repair: The fetched main revision still drops accountId in the seven affected notification paths. IRC resolves its sender without accountId; iMessage and Zalo retain separate notification helpers that omit the selected account. (extensions/irc/src/channel.ts:332, b5412c2a40b2)
  • Latest-release check: The latest supplied release also contains the IRC notifier without accountId, establishing that the central account-selection defect is present in shipped source. (extensions/irc/src/channel.ts:332, 3928bad9badf)
  • Existing owner and callback contract: The Gateway supplies the approved account to notifyPairingApproved, and createTextPairingAdapter preserves that context. Notification exceptions become notification='failed' after approval; the existing Gateway regression test explicitly protects completed approval when notification fails. This is OpenClaw channel routing and does not depend on a Codex runtime or protocol contract. (src/gateway/server-methods/channel-pairing.ts:315, 2a54928c1847)
  • Previous iMessage finding resolved: The adapter now uses PAIRING_APPROVED_MESSAGE, matching the removed notifier’s delivered text. Both named-account and omitted-account test cases assert the exact RPC text, including the invitation to start chatting. (extensions/imessage/src/channel.runtime.test.ts:467, 2a54928c1847)
  • Synology notification failure uses the existing sender: The notifier delegates to sendSynologyChatText, which resolves ctx.accountId, requires incomingUrl, and throws when the HTTP client returns false. This addresses the supplied prior review concern without creating another send path. (extensions/synology-chat/src/channel.ts:283, 2a54928c1847)
  • Zalo proxy and unsuccessful-result handling: sendZaloDelivery forwards cfg and accountId to the existing send owner and rejects unsuccessful results. The sender resolves both token and proxy from that account; the added regression cases cover the selected proxy, default control, and unsuccessful notification. (extensions/zalo/src/channel.ts:99, 2a54928c1847)

Likely related people:

  • Peter Steinberger: Suggested for follow-up; no historical authorship or introduction is verified. (role: unverified routing candidate; confidence: low)
  • Ayaan Gazali: Suggested for follow-up; no historical authorship or introduction is verified. (role: unverified routing candidate; confidence: low)

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 (5 earlier review cycles)
  • reviewed 2026-09-03T11:32:38.694Z sha 83d1db6 :: needs real behavior proof before merge. :: none
  • reviewed 2026-09-03T12:49:42.274Z sha fe05d28 :: blocked before merge. :: none
  • reviewed 2026-09-06T07:20:58.865Z sha eaf609f :: needs maintainer review before merge. :: none
  • reviewed 2026-09-06T08:14:17.402Z sha a2014d6 :: blocked before merge. :: [P3] [P3] Preserve the existing iMessage approval message
  • reviewed 2026-09-06T08:29:23.491Z sha a2014d6 :: blocked before merge. :: [P3] [P3] Preserve the existing iMessage approval message

…Personal, and iMessage

The Zalo, Zalo Personal (zalouser), and iMessage pairing notifiers dropped
the approved account id the same way the first four did, so on a
multi-account setup the approval went out from the default account.

Each notifier now passes the approved account id to its account resolver
or sender, and the Zalo and iMessage runtime facades type their params
with an optional accountId so the id survives the module boundary.
Omitting the account id still resolves the default account exactly as
before.
@openclaw-barnacle openclaw-barnacle Bot added channel: imessage Channel integration: imessage channel: zalo Channel integration: zalo channel: zalouser Channel integration: zalouser labels Sep 3, 2026

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: eaf609fcdf

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread extensions/synology-chat/src/channel.ts Outdated
notify: async ({ cfg, id, message }) => {
const account = resolveAccount(cfg);
notify: async ({ cfg, id, message, accountId }) => {
const account = resolveAccount(cfg, accountId);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P1 Badge Reject failed Synology approval sends

When the selected Synology account has no incomingUrl, this notifier returns normally; when the webhook POST fails, sendMessage likewise resolves false rather than throwing (extensions/synology-chat/src/client.ts:138-172). channels.pairing.approve treats either outcome as notification: "sent", so the newly routed named-account path can silently deliver nothing while reporting success. Throw for a missing URL and for a false send result.

AGENTS.md reference: AGENTS.md:L46-L46

Useful? React with 👍 / 👎.

Comment thread extensions/zalo/src/channel.runtime.ts Outdated
}) {
const { resolveZaloAccount } = await import("./accounts.js");
const account = resolveZaloAccount({ cfg: params.cfg });
const account = resolveZaloAccount({ cfg: params.cfg, accountId: params.accountId });

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P1 Badge Reject failed Zalo approval sends

When the selected Zalo account depends on its per-account proxy, resolving the account here but passing only its token discards that proxy; sendMessageZalo applies the account proxy only when given cfg and accountId or an explicit proxy (extensions/zalo/src/send.ts:96-112). It also converts network and API errors into { ok: false }, which this notifier ignores, so the gateway reports notification: "sent" despite delivering nothing. Forward the selected account context and throw when the result is not successful.

AGENTS.md reference: AGENTS.md:L46-L46

Useful? React with 👍 / 👎.

@steipete steipete self-assigned this Sep 6, 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 rating: 🦪 silver shellfish Thin PR readiness signal; proof, validation, or implementation needs work. merge-risk: 🚨 message-delivery 🚨 May drop, duplicate, misroute, suppress, or wrongly target messages. status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask. clawsweeper-recovery-stuck labels Sep 6, 2026
Preserve the approved account across all seven affected plugins. Reuse
existing delivery owners for iMessage, Synology Chat, and Zalo so failed
notifications remain visible and Zalo retains its account proxy.

Keep the existing notification text and completed approval unchanged.
Production decreases by five lines. Secretless hosted proof covers all
seven original account mismatches, real notifier HTTP effects, 204 focused
checks, and exact candidate file hashes.

Co-authored-by: MarMar Labs <[email protected]>
Use the shared approval message already sent by the removed notifier.
Assert the exact RPC text for both selected and default accounts.

Hosted proof reproduced both generic-text regressions on a2014d6,
then passed all 15 iMessage owner tests and verified all 16 candidate
file hashes after this correction.

Co-authored-by: MarMar Labs <[email protected]>
@steipete
steipete merged commit b75423c into openclaw:main Sep 6, 2026
106 of 108 checks passed
@steipete

steipete commented Sep 6, 2026

Copy link
Copy Markdown
Contributor

Merged via squash.

github-actions Bot pushed a commit to Desicool/openclaw that referenced this pull request Sep 7, 2026
…claw#137268)

* fix(channels): send pairing approvals from the approved account

The pairing approval contract carries the account a request was selected
on, but the LINE, Signal, IRC, and Synology Chat notifiers dropped it and
resolved the default account instead. On a multi-account setup the
approval went out from the wrong account with the wrong credentials while
the gateway still reported the notification as sent.

Each of the four notifiers now passes the approved account id to its
account resolver. Omitting the account id still resolves the default
account exactly as before.

* fix(channels): extend account-scoped pairing approvals to Zalo, Zalo Personal, and iMessage

The Zalo, Zalo Personal (zalouser), and iMessage pairing notifiers dropped
the approved account id the same way the first four did, so on a
multi-account setup the approval went out from the default account.

Each notifier now passes the approved account id to its account resolver
or sender, and the Zalo and iMessage runtime facades type their params
with an optional accountId so the id survives the module boundary.
Omitting the account id still resolves the default account exactly as
before.

* fix(channels): reuse account-aware pairing notification senders

Preserve the approved account across all seven affected plugins. Reuse
existing delivery owners for iMessage, Synology Chat, and Zalo so failed
notifications remain visible and Zalo retains its account proxy.

Keep the existing notification text and completed approval unchanged.
Production decreases by five lines. Secretless hosted proof covers all
seven original account mismatches, real notifier HTTP effects, 204 focused
checks, and exact candidate file hashes.

Co-authored-by: MarMar Labs <[email protected]>

* fix(imessage): preserve pairing approval welcome text

Use the shared approval message already sent by the removed notifier.
Assert the exact RPC text for both selected and default accounts.

Hosted proof reproduced both generic-text regressions on a2014d6,
then passed all 15 iMessage owner tests and verified all 16 candidate
file hashes after this correction.

Co-authored-by: MarMar Labs <[email protected]>

---------

Co-authored-by: Peter Steinberger <[email protected]>
Co-authored-by: MarMar Labs <[email protected]>
@eleqtrizit eleqtrizit assigned eleqtrizit and unassigned steipete Sep 13, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

channel: imessage Channel integration: imessage channel: irc channel: line Channel integration: line channel: signal Channel integration: signal channel: synology-chat channel: zalo Channel integration: zalo channel: zalouser Channel integration: zalouser P2 Normal backlog priority with limited blast radius. 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.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants