Skip to content

fix: share diagnostic event listeners across module bundles#11168

Closed
jg-noncelogic wants to merge 2 commits intoopenclaw:mainfrom
jg-noncelogic:fix/diagnostic-events-global-listeners
Closed

fix: share diagnostic event listeners across module bundles#11168
jg-noncelogic wants to merge 2 commits intoopenclaw:mainfrom
jg-noncelogic:fix/diagnostic-events-global-listeners

Conversation

@jg-noncelogic
Copy link
Copy Markdown
Contributor

@jg-noncelogic jg-noncelogic commented Feb 7, 2026

Summary

Fixes #5190

When the plugin-sdk is bundled separately from the main gateway code, each bundle gets its own listeners Set in diagnostic-events.ts. This causes events emitted by the gateway to never reach plugin subscribers using onDiagnosticEvent().

Root cause: The bundler inlines the diagnostic-events module into both dist/plugin-sdk/index.js and the main gateway bundles, creating separate module instances with independent state.

Fix: Use globalThis to share the listeners registry across all module instances, ensuring plugins receive diagnostic events properly.

Test plan

  • Existing tests pass (pnpm test src/infra/diagnostic-events.test.ts)
  • Verified plugins (diagnostics-otel, audit-neon) receive events after fix
  • Verified audit records appear in database after fix

🤖 Generated with Claude Code

Greptile Summary

This PR fixes a bug where diagnostic event listeners in plugins (e.g., diagnostics-otel, audit-neon) never received events emitted by the gateway. The root cause was that the bundler inlined diagnostic-events.ts into both the plugin-sdk bundle and the main gateway bundle, creating separate module instances with independent listeners Sets and seq counters.

The fix replaces the module-scoped let seq and const listeners with a shared globalThis-backed state object keyed by "__openclaw_diagnostic_events__". A getGlobalState() helper lazily initializes the shared state on first access and returns the existing state on subsequent calls from any bundle.

  • Single file change in src/infra/diagnostic-events.ts — moves seq and listeners into a DiagnosticGlobalState object stored on globalThis
  • All existing functions (emitDiagnosticEvent, onDiagnosticEvent, resetDiagnosticEventsForTest) are updated to use the shared state reference
  • The pattern is consistent with existing globalThis usage in the codebase (e.g., src/web/test-helpers.ts)

Confidence Score: 5/5

  • This PR is safe to merge — it's a focused, minimal fix with no behavioral changes beyond sharing state across bundles as intended.
  • The change is small and well-scoped (single file, ~30 lines). The globalThis pattern is a standard solution for cross-bundle state sharing in Node.js. The init guard correctly prevents overwriting existing state. All consumers (emit, subscribe, reset) operate through the same shared object reference. The approach is consistent with existing patterns in the codebase. No new APIs or behavioral changes are introduced beyond fixing the stated bug.
  • No files require special attention.

Last reviewed commit: 4df7815

When the plugin-sdk is bundled separately from the main gateway code,
each bundle gets its own `listeners` Set. This causes events emitted
by the gateway to never reach plugin subscribers.

Fix by using globalThis to share the listeners registry across all
module instances, ensuring plugins receive diagnostic events properly.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@jg-noncelogic jg-noncelogic force-pushed the fix/diagnostic-events-global-listeners branch from 697b441 to 90b94d0 Compare February 17, 2026 18:25
@jg-noncelogic jg-noncelogic reopened this Feb 17, 2026
Copy link
Copy Markdown
Contributor

@greptile-apps greptile-apps Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1 file reviewed, 1 comment

Edit Code Review Agent Settings | Greptile

Comment thread src/infra/diagnostic-events.ts
Make the narrowing intent explicit so future TS changes or refactors
cannot introduce a type error.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Copy link
Copy Markdown
Contributor

@greptile-apps greptile-apps Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1 file reviewed, 1 comment

Edit Code Review Agent Settings | Greptile

Comment thread src/infra/diagnostic-events.ts
@jg-noncelogic
Copy link
Copy Markdown
Contributor Author

Closing — upstream has since implemented globalThis-based sharing for diagnostic event listeners (via getDiagnosticEventsState() with __openclawDiagnosticEventsState), which also includes a dispatchDepth recursion guard. This PR is superseded. Thanks for the review feedback!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

1 participant