Skip to content

[Feature] Channel-aware device scope upgrade approval — send approval card back to triggering channel #92666

Description

@xxtyyq

Summary

OpenClaw device pairing scope upgrade requests currently require SSH / control-plane approval via openclaw devices approve. This breaks the flow when the scope upgrade was triggered by a CLI command issued from a chat session (webchat / QQ / 飞书 / Discord / etc.).

We should attach the triggering chat session context (channel + sender) to the pending request, and have the Gateway auto-send an approval card back to that channel, so the user can click ✅ in-channel without SSH.

Motivation

Today, when a chat session asks the agent to do something that needs broader operator scope (e.g., openclaw cron run needs operator.admin), the flow is:

  1. CLI triggers scope upgrade → ~/.openclaw/devices/pending.json gets a new entry
  2. User gets NO notification
  3. User has to SSH to the host and run openclaw devices approve <requestId>
  4. User has to go back to the chat session and ask the agent to retry

This is a poor UX. We want:

  1. CLI attaches the triggering chat session context to the pending request
  2. Gateway auto-sends an approval card back to that channel
  3. User clicks ✅ in-channel → scope upgrade completes, CLI auto-retries

Existing precedent

OpenClaw 6.6.6 already implements this for DM pairing:

openclaw pairing approve <channel> <code> --notify

When approved, OpenClaw automatically sends a confirmation back to the same channel via notify.js (which already exists in the device-pair stock plugin). The same notification mechanism can be reused for device scope upgrade approval.

This proves the channel-bound approval path is already viable internally — we just need to extend it from DM pairing to device scope upgrade.

Proposed implementation

Schema change (pending.json)

{
  "requestId": "2889c4f2-...",
  "deviceId": "...",
  "scopes": ["operator.admin"],
  ...
  "origin": {
    "channel": "webchat",
    "accountId": "default",
    "sessionKey": "agent:starry:webchat:...",
    "senderId": "owner"
  }
}

Behavior change (gateway)

When pending.json gains a new entry with origin set:

  1. Gateway calls the corresponding channel's outbound renderer
  2. Renderer produces a channel-specific approval card:
    • webchat: inline message + [Approve] [Reject] button (Control UI WebSocket)
    • 飞书: Interactive message card + button callbacks
    • QQ: Markdown message + emoji 投票( / in 60s)
    • Discord: Button component v2
    • Slack: Block kit button
  3. Button callbacks invoke device.pair.approve <requestId> / device.pair.reject <requestId>
  4. After approve, CLI auto-retries the original command

Fallback

If channel doesn't support inline approval (or outbound fails), send a markdown card + link to http://gateway.local/devices/approve/<requestId> for web UI approval. If both fail, fall back to email + SSH-required warning.

Security considerations

  1. Owner verification required: Even if a user clicks ✅ in-channel, the approval action itself must verify commands.ownerAllowFrom. Channel-level click is convenience, NOT privilege escalation.

  2. pending.json remains single source of truth: Channel approval is UI only, NOT a new source of truth. The durable record is still ~/.openclaw/devices/pending.json + paired.json.

  3. Scope check at device.pair.approve is unchanged: The standard operator.pairing / operator.admin check still applies. We're just moving the UI from terminal to chat.

  4. Multi-device race condition: If multiple devices trigger scope upgrades simultaneously, each gets its own requestId. The approval UI must scope buttons per requestId to avoid confusion.

  5. No silent auto-approve: The silent: true flag must NOT enable channel auto-approve for scope upgrades — only metadata upgrades (display name, OS version hints) are eligible for silent approval, per existing docs.

Affected components

Component Impact
gateway/pairing pending.json schema gains origin field
gateway/devices New device.pair.notify outbound RPC
channels/* Each channel implements approval card renderer
control-ui webchat inline button adapter
device-pair stock plugin Wire approval into existing notify.js

Test cases

T1 · webchat triggers → webchat receives approval card

  1. In webchat, ask agent to run openclaw cron run X (needs admin)
  2. Same webchat session shows approval card
  3. Click ✅ → cron run auto-succeeds

T2 · 飞书 triggers → 飞书 receives interactive card

  1. In 飞书 DM, ask agent to run openclaw devices remove X
  2. 飞书 shows interactive card with buttons
  3. Click ✅ → device removed

T3 · Non-owner tries to approve

  1. Owner A triggers scope upgrade in shared channel
  2. Non-owner B clicks ✅
  3. Approval is rejected because B is not in commands.ownerAllowFrom

T4 · Channel doesn't support inline approval

  1. Trigger in a channel without inline buttons
  2. Receive markdown card + web UI link
  3. Click link → web UI approves

T5 · Channel outbound fails

  1. Trigger in 飞书, but feishu token is expired
  2. Markdown card delivery fails
  3. Fallback: email + SSH-required warning to owner

Related code locations

/app/dist/device-pair/index.js
/app/dist/device-pair/pair-command-approve.js
/app/dist/device-pair/notify.js              ← already has notify infrastructure!
/app/dist/device-pair/notify-state.js
/app/docs/cli/devices.md
/app/docs/gateway/pairing.md
/app/docs/channels/pairing.md                ← DM pairing precedent

Note: device-pair stock plugin already has notify.js — the channel-bound notification path is architecturally present, we just need to wire device-pair approval into it, not just device-pairing notifications.

Reproduction steps

  1. Set up OpenClaw 2026.6.6 with QQ Bot + 飞书 channels
  2. From a chat session, ask the agent to do something that requires operator.admin (e.g., openclaw cron run)
  3. Observe: scope upgrade request is silently dropped in ~/.openclaw/devices/pending.json with no user-facing notification
  4. Workaround: SSH to host, run openclaw devices list, then openclaw devices approve <requestId>

Filed by: OpenClaw self-hosted user on 群晖 NAS (single-user deployment). Motivation: improve UX for scope upgrades triggered by chat-driven agent workflows.

Metadata

Metadata

Assignees

No one assigned

    Labels

    P2Normal backlog priority with limited blast radius.clawsweeper:needs-maintainer-reviewClawSweeper marked this issue as needing maintainer review before automation.clawsweeper:needs-product-decisionClawSweeper marked this issue as needing a product or behavior decision.clawsweeper:needs-security-reviewClawSweeper marked this issue as needing security-sensitive review.clawsweeper:no-new-fix-prClawSweeper does not recommend queueing a new automated fix PR for this issue.clawsweeper:source-reproClawSweeper found a high-confidence source-level issue reproduction.impact:auth-providerAuth, provider routing, model choice, or SecretRef resolution may break.impact:message-lossChannel message delivery can be lost, duplicated, or misrouted.impact:securitySecurity boundary, credential, authz, sandbox, or sensitive-data risk.issue-rating: 🦞 diamond lobsterVery strong issue quality with high-confidence source-level or clear reproduction.

    Type

    No type

    Fields

    Priority

    None yet

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions