Skip to content

[Feature]: Broadcast inbound_claim hook to all plugins (not just plugin-bound conversations) #48434

@flowolforg

Description

@flowolforg

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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions