openclaw - ✅(Solved) Fix Plugin hooks (llm_input, llm_output, agent_end) don't fire for non-default agents [1 pull requests, 6 comments, 5 participants]

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…
GitHub stats
openclaw/openclaw#50025Fetched 2026-04-08 01:00:11
View on GitHub
Comments
6
Participants
5
Timeline
13
Reactions
0
Timeline (top)
commented ×6cross-referenced ×4referenced ×2subscribed ×1

Plugin hooks (llm_input, llm_output, agent_end) fire correctly for the default/main agent but are silently skipped for dynamically created agents. This means plugins like @opik/opik-openclaw only capture traces for the main agent — all other agents are invisible to the observability layer.

Error Message

Gateway debug logs show:

Root Cause

Plugin hooks (llm_input, llm_output, agent_end) fire correctly for the default/main agent but are silently skipped for dynamically created agents. This means plugins like @opik/opik-openclaw only capture traces for the main agent — all other agents are invisible to the observability layer.

Fix Action

Fixed

PR fix notes

PR #50493: fix: make initializeGlobalHookRunner idempotent (#50025)

Description (problem / solution / changelog)

Summary

  • Fixes #50025 — plugin hooks (llm_input, llm_output, agent_end) now fire for all agents, not just the default agent
  • initializeGlobalHookRunner was unconditionally replacing the process-wide singleton hook runner every time activatePluginRegistry was called. When a non-default agent triggered ensureRuntimePluginsLoaded, a cache key mismatch caused a fresh (empty) registry to be built, clobbering the gateway's working hook runner
  • Added a guard clause: if a hook runner already exists with registered typed hooks, skip re-initialization

Changes

  • src/plugins/hook-runner-global.ts — guard clause + debug log in initializeGlobalHookRunner
  • src/plugins/hook-runner-global.test.ts — 3 new tests covering idempotency, upgrade-from-hookless, and reset-clears-guard

Test plan

  • All 5 hook-runner-global tests pass (2 existing + 3 new)
  • Full plugin test suite passes (605 tests)
  • Embedded runner test suite passes
  • Build succeeds with no errors

Changed files

  • src/plugins/hook-runner-global.test.ts (modified, +50/-0)
  • src/plugins/hook-runner-global.ts (modified, +8/-0)

Code Example

openclaw agent --agent main --message "test" --json

---

openclaw agent --agent <dynamic-agent-id> --message "test" --json

---

[hooks] running llm_input (1 handlers)
[hooks] running agent_end (1 handlers)
[hooks] running llm_output (1 handlers)

---

embedded run start: runId=... sessionId=... provider=anthropic model=claude-opus-4-5
embedded run agent end: runId=... isError=false
embedded run done: runId=... durationMs=3529 aborted=false
RAW_BUFFERClick to expand / collapse

Summary

Plugin hooks (llm_input, llm_output, agent_end) fire correctly for the default/main agent but are silently skipped for dynamically created agents. This means plugins like @opik/opik-openclaw only capture traces for the main agent — all other agents are invisible to the observability layer.

Environment

  • OpenClaw: 2026.3.13 (also reproduced on 2026.3.8)
  • Plugin: @opik/[email protected]
  • Node.js: 22.22.1
  • Platform: Docker (node:22-bookworm)

Setup

Multi-tenant deployment with dynamically created per-user agents. Plugin installed via openclaw plugins install @opik/opik-openclaw, config verified via openclaw opik status. Plugin is in plugins.allow and plugins.installs with full provenance.

Steps to reproduce

  1. Install and configure the opik-openclaw plugin
  2. Start the gateway
  3. Confirm plugin loads: opik: exporting traces to project "..." appears in logs
  4. Send a message to the main agent:
    openclaw agent --agent main --message "test" --json
  5. Send a message to a non-default agent:
    openclaw agent --agent <dynamic-agent-id> --message "test" --json
  6. Check traces — only the main agent trace appears

Observed behavior

Gateway debug logs show:

Main agent — hooks fire, trace captured:

[hooks] running llm_input (1 handlers)
[hooks] running agent_end (1 handlers)
[hooks] running llm_output (1 handlers)

Non-default agent — no hook logs at all, despite a successful embedded run:

embedded run start: runId=... sessionId=... provider=anthropic model=claude-opus-4-5
embedded run agent end: runId=... isError=false
embedded run done: runId=... durationMs=3529 aborted=false

The [hooks] running llm_input log (from the gateway's hook runner) is completely absent for non-default agents. The plugin handler code is never reached.

Expected behavior

Plugin hooks should fire for all agents, not just the default agent. The plugin API docs and architecture indicate hooks are gateway-wide.

Impact

This makes it impossible to use observability plugins (Opik, or any future plugin relying on llm_input/llm_output/agent_end) in multi-agent deployments — which is a core OpenClaw use case.

extent analysis

Fix Plan

To fix the issue of plugin hooks not firing for dynamically created agents, we need to modify the plugin registration and hook execution logic. Here are the steps:

  • Update the plugin registration to include a flag indicating that it should be executed for all agents, not just the default one.
  • Modify the hook execution logic to check for this flag and execute the plugin hooks for all agents if it is set.

Example code changes:

// In plugin registration code
const plugin = {
  // ...
  executeForAllAgents: true, // Add this flag
};

// In hook execution logic
if (plugin.executeForAllAgents) {
  // Execute plugin hooks for all agents
  executePluginHooks(plugin, agent);
} else {
  // Execute plugin hooks only for default agent
  if (agent.id === 'main') {
    executePluginHooks(plugin, agent);
  }
}

Verification

To verify that the fix worked, follow these steps:

  • Install and configure the opik-openclaw plugin
  • Start the gateway
  • Send a message to a non-default agent using the openclaw agent command
  • Check the gateway debug logs for the presence of [hooks] running llm_input logs for the non-default agent
  • Verify that the plugin handler code is reached and executed correctly for the non-default agent

Extra Tips

  • Make sure to update the plugin API documentation to reflect the new flag and its behavior.
  • Test the fix thoroughly to ensure that it works correctly for all agents and does not introduce any regressions.
  • Consider adding additional logging or debugging statements to help diagnose any issues that may arise.

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…

FAQ

Expected behavior

Plugin hooks should fire for all agents, not just the default agent. The plugin API docs and architecture indicate hooks are gateway-wide.

Still need to ship something?

×6

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

Back to top recommendations

TRENDING