perf(hooks): add subscription-based event filtering to reduce hot-path dispatch overhead#2162
Open
coleleavitt wants to merge 2 commits intocode-yeongyu:devfrom
Open
perf(hooks): add subscription-based event filtering to reduce hot-path dispatch overhead#2162coleleavitt wants to merge 2 commits intocode-yeongyu:devfrom
coleleavitt wants to merge 2 commits intocode-yeongyu:devfrom
Conversation
…h dispatch overhead Replace sequential all-hooks dispatch with subscription-based filtering. Each hook declares which event types it cares about via HOOK_SUBSCRIPTIONS. Critical hooks (claudeCodeHooks, stopContinuationGuard, writeExistingFileGuard) remain awaited; 18 other hooks fire-and-forget with error logging. During LLM streaming (~100 message.part.delta events/sec), 19 of 21 hooks are now skipped entirely, reducing async dispatch from ~2100/sec to ~200/sec.
There was a problem hiding this comment.
2 issues found across 1 file
Confidence score: 2/5
- Stateful hooks no longer receive
session.deletedevents insrc/plugin/event.ts, which is a concrete regression that can leak resources during cleanup - Opencode compatibility is broken because
session.compactedis filtered out by the new subscription map, so compaction handlers stop running - Given both issues are high severity and user-facing, the merge risk is high despite limited file scope
- Pay close attention to
src/plugin/event.ts- event filtering removessession.deletedandsession.compacteddelivery to hooks.
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="src/plugin/event.ts">
<violation number="1" location="src/plugin/event.ts:140">
P1: Custom agent: **Opencode Compatibility**
Opencode compatibility break: `session.compacted` is filtered out by the new subscription map, so hooks that implement compaction handling no longer receive that OpenCode event.</violation>
<violation number="2" location="src/plugin/event.ts:159">
P1: Stateful hooks are filtered from receiving session.deleted events they need for cleanup, causing resource leaks</violation>
</file>
Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.
…cription map - Add session.compacted to SESSION_LIFECYCLE (it's a lifecycle event) - Add session.deleted to hooks that do cleanup: contextWindowMonitor, compactionTodoPreserver, writeExistingFileGuard, todoContinuationEnforcer, directoryReadmeInjector, thinkMode - Add session.compacted to hooks that reset state on compaction: compactionTodoPreserver, atlasHook, directoryAgentsInjector, directoryReadmeInjector, rulesInjector - Fix sync throw escaping fire-and-forget catch by wrapping invoke in Promise.resolve().then() instead of Promise.resolve(invoke())
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
subscriptionsreceive all events as beforeProblem
During LLM streaming, the processor emits ~10 PartDelta events/second. Each event dispatches to ALL 21 hooks sequentially, even though most hooks don't handle delta events. This creates ~2,100 unnecessary async dispatches/second, generating excessive garbage that triggers GC pressure and contributes to TUI freeze.
Solution
Hooks can now declare which event types they care about via a
subscriptionsarray:The dispatcher builds a subscription map at initialization and routes events only to subscribed hooks. Hooks without
subscriptionsare treated as "subscribe to all" for backward compatibility.Changes
src/plugin/event.ts: AddedSubscriptiontype,subscriptionsfield toHook, subscription map building increate(), filtered dispatch indispatch()Verification
bun x tsc --noEmitpasses cleanSummary by cubic
Adds subscription-based event filtering so only subscribed hooks run. This reduces hot-path dispatch during LLM streaming and lowers GC pressure, and fixes lifecycle subscriptions and fire-and-forget error handling.
Refactors
Bug Fixes
Written for commit cae1645. Summary will update on new commits.