-
-
Notifications
You must be signed in to change notification settings - Fork 69.2k
[Feature]: Broadcast inbound_claim hook to all plugins (not just plugin-bound conversations) #48434
Description
Summary
Add a broadcast runInboundClaim() call in dispatch-from-config.ts so plugins can register global inbound_claim handlers without owning a conversation.
Problem to solve
The inbound_claim plugin hook is fully implemented (types, runner, event mappers, tests) but only fires for plugin-bound conversations via runInboundClaimForPluginOutcome(). Plugins that register api.on("inbound_claim", ...) without owning a specific conversation are never invoked.
This limits inbound_claim to exclusive 1:1 conversation takeover (plugin-binding). Use-cases that need passive, global message filtering — such as listen-only group modes, spam filters, or rate limiters — cannot use this hook today.
Proposed solution
Add a broadcast runInboundClaim() call in dispatch-from-config.ts, after the plugin-bound logic and before agent dispatch:
const claimResult = await hookRunner?.runInboundClaim(inboundClaimEvent, inboundClaimContext);
if (claimResult?.handled) {
markIdle("plugin_inbound_claim");
recordProcessed("completed", { reason: "plugin-inbound-claim-handled" });
return { queuedFinal: false, counts: dispatcher.getQueuedCounts() };
}This is ~5 lines. All building blocks already exist — the broadcast call is the only missing piece.
Alternatives considered
Implementing message filtering at the channel adapter level (e.g. Discord/Telegram bot middleware). This is weaker because it bypasses OpenClaw's plugin lifecycle, doesn't integrate with the hook system, and requires per-channel custom code instead of a single reusable plugin.
Impact
- Affected: Plugin developers building passive/global message handlers (spam filters, rate limiters, listen-only modes)
- Severity: Medium — blocks an entire category of plugin use-cases
- Frequency: Always — any plugin needing global inbound filtering hits this
- Consequence: Developers must fork core or use workarounds outside the plugin system
Evidence/examples
Working plugin: extensions/listen-only/ (~490 lines) that uses the broadcast to implement a listen-only mode for group chats:
- Group messages without @mention → silently absorbed, logged to L0, batched
- Group messages with @mention → passed through, agent responds with batched context
- Periodic L1 memory extraction from collected messages
The plugin is self-contained and only needs the broadcast patch in core.
Additional information
Happy to submit a PR if this direction makes sense. The change is backward-compatible — no existing behavior is affected since the broadcast only fires when no plugin-bound claim has already handled the message.