Skip to content

Plugin typed hooks (api.on) don't fire across module bundle boundaries #23895

@buildzerobob

Description

@buildzerobob

Summary

Plugin hooks registered via api.on("before_reset", handler) never fire when the /new or /reset command is processed. The handler registers successfully (visible in openclaw plugins list as "loaded"), but hookRunner.hasHooks("before_reset") returns false at the call site.

Root Cause

The build system (Rolldown) creates separate module bundles that each have their own globalHookRunner singleton:

  • pi-embedded-CAmQsy9D.js imports getGlobalHookRunner from deliver-Vheuo8GO.js
  • subagent-registry-Bdm_X-N1.js imports getGlobalHookRunner from deliver-Day3efJ9.js

These are different files (43KB vs 45KB) with separate module-level globalHookRunner variables. When a plugin calls api.on("before_reset", handler), the typed hook is registered in one module's registry.typedHooks. But the /new command handler calls getGlobalHookRunner() from a different module's scope, where the hook runner was either never initialized or initialized with a different registry.

The internal hooks system (registerInternalHook / triggerInternalHook) works correctly because it shares state across module boundaries.

Reproduction

  1. Create a workspace plugin that uses api.on("before_reset", handler) with a console.log in the handler
  2. Run openclaw plugins list — plugin shows as "loaded"
  3. Run /new in any channel
  4. Check logs — the handler never fires, no console output

Expected Behavior

api.on("before_reset") handlers should fire when /new or /reset is processed, regardless of which module bundle processes the command.

Workaround

Use workspace hooks (internal hooks system) instead of plugin typed hooks for command:new / command:reset events. This works because triggerInternalHook shares state across module boundaries.

Note: before_compaction and after_compaction have no internal hook equivalent, so plugins cannot reliably hook into compaction events.

Environment

  • OpenClaw version: 2026.2.21 (npm)
  • Node: v25.5.0
  • OS: macOS (arm64)
  • Build files examined: pi-embedded-CAmQsy9D.js, subagent-registry-Bdm_X-N1.js, deliver-Vheuo8GO.js, deliver-Day3efJ9.js

Metadata

Metadata

Assignees

No one assigned

    Labels

    staleMarked as stale due to inactivity

    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