openclaw - 💡(How to fix) Fix Allow plugin approvals to carry long-form context (Telegram, Slack, Discord)

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…

Root Cause

The verdict reason is sliced. The command JSON is sliced. The action footer itself is sliced. The recipient cannot determine what they are approving.

Fix Action

Fix / Workaround

Real-world workaround (until merged)

Code Example

🚨 Plugin approval required
Title: [primary] risky_class
Description: The user's most recent active request was to research BMW
models on forums/Reddit and anchor a new price baseline. The 'Ok. Então
... | exec: {"command":"set -euo pipefail\ngit add skills/flight-search/
scripts/db.py skills...
Approve/Deny or /redi...

---

interface PluginApprovalRequest {
  title: string;                    // ≤80, unchanged
  description: string;              // ≤256, unchanged (fallback)
  longDescription?: string;         // NEW, ≤4000, for HTML-capable channels
  severity?: "info" | "warning" | "critical";
  // ... existing fields
}

---

interface PluginApprovalRequest {
  // ...
  deliverPrompt?: boolean;  // default true; when false, gateway creates
                            // the approval record + decision promise but
                            // skips channel delivery. Plugin is responsible
                            // for delivering its own prompt.
}
RAW_BUFFERClick to expand / collapse

Problem

plugin.approval.request hard-caps description at 256 chars (PLUGIN_APPROVAL_DESCRIPTION_MAX_LENGTH in src/infra/plugin-approvals.ts).

For real-world plugin approvals, that limit forces dense, useful context to be truncated mid-sentence or mid-token. Example from a tool-policy-evaluator-style plugin Tier 4 escalation on Telegram:

🚨 Plugin approval required
Title: [primary] risky_class
Description: The user's most recent active request was to research BMW
models on forums/Reddit and anchor a new price baseline. The 'Ok. Então
... | exec: {"command":"set -euo pipefail\ngit add skills/flight-search/
scripts/db.py skills...
Approve/Deny or /redi...

The verdict reason is sliced. The command JSON is sliced. The action footer itself is sliced. The recipient cannot determine what they are approving.

The 256-char cap was reasonable when "approval description" meant a one-line exec preview. For LLM-generated verdicts and structured tool-call payloads, it is the wrong shape.

Why not "just shorten the description"

Plugins that produce structured verdicts (gatekeepers, security plugins, code review plugins) need a fundamentally longer payload — full reasoning + the artifact under review + state context. SMS-class channels have a real short-form constraint, but Telegram (4096 chars), Discord (2000), Slack (4000+), and Matrix have no equivalent limit.

Proposal

Add longDescription?: string to PluginApprovalRequest:

interface PluginApprovalRequest {
  title: string;                    // ≤80, unchanged
  description: string;              // ≤256, unchanged (fallback)
  longDescription?: string;         // NEW, ≤4000, for HTML-capable channels
  severity?: "info" | "warning" | "critical";
  // ... existing fields
}

At render time, channels pick the best they can carry:

  • HTML-capable (Telegram, Discord, Slack, Matrix, Mattermost): use longDescription if present, else fall back to description.
  • Short-form (SMS, IRC): always use description.

Additive, non-breaking. No plugin needs to change unless it wants longer text.

Companion change: deliverPrompt: false

For plugins that want to deliver their own fully-custom approval prompt (rich HTML/Markdown, inline keyboards, channel-specific formatting), add an optional flag:

interface PluginApprovalRequest {
  // ...
  deliverPrompt?: boolean;  // default true; when false, gateway creates
                            // the approval record + decision promise but
                            // skips channel delivery. Plugin is responsible
                            // for delivering its own prompt.
}

This lets plugins go beyond what the standard renderer can do, while still using the gateway's approval lifecycle (/approve <id>, expiry, resolution).

Scope and out-of-scope

In scope: schema addition, renderer logic per channel, tests, docs. Out of scope: changing the default behavior, deprecating description, mandating either field.

Migration

Zero. New fields are optional. Existing plugins continue to work with description only.

Affected files (estimate)

  • src/infra/plugin-approvals.ts — constants + types
  • src/plugin-sdk/approval-renderers.ts — picker (long vs short by channel)
  • src/channels/telegram/approval-handler.runtime.ts — wire long path
  • src/channels/discord/approval-handler.runtime.ts — wire long path
  • src/channels/slack/approval-handler.runtime.ts — wire long path
  • src/channels/matrix/approval-handler.runtime.ts — wire long path
  • docs/concepts/plugin-approvals.md — document new fields
  • CHANGELOG.md — entry
  • Tests for each channel renderer

Estimated ~500 LOC + tests + docs.

Alternative considered: bump the 256 limit

Rejected. Hard cap raise would affect SMS-class channels that genuinely cannot carry longer payloads. The additive longDescription is safer.

Real-world workaround (until merged)

A tool-policy-evaluator-style plugin ships a CLI-shellout companion message in production today. Plugin spawns openclaw message send with the full Markdown body before returning { requireApproval }. Native prompt still fires alongside (since there's no deliverPrompt: false). Two messages per escalation; the rich one is fully readable.

This works but reaches outside the SDK and produces duplicate notifications. Native long-form support would be cleaner.

Discussion welcome

Happy to scope down (e.g., ship just longDescription, defer deliverPrompt) if maintainers prefer two smaller PRs. Also open to alternative shapes — structured payload, per-channel adaptive caps, etc.

I have a draft PR ready to open once the schema direction is agreed on.

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING

openclaw - 💡(How to fix) Fix Allow plugin approvals to carry long-form context (Telegram, Slack, Discord)