@@ -24,6 +24,12 @@ interface CacheWarningState {
2424// 模块级状态,每个 querySource 独立跟踪
2525const cacheWarningStateBySource = new Map < string , CacheWarningState > ( )
2626
27+ // Limit the number of tracked sources to prevent unbounded Map growth.
28+ // querySource strings are effectively unbounded (typed as `any`), so a
29+ // long-running session that spawns many subagents could leak memory.
30+ // Evict the oldest entry (by insertion order) when the limit is exceeded.
31+ const MAX_SOURCE_ENTRIES = 50
32+
2733const DEFAULT_CACHE_THRESHOLD = 80
2834
2935/**
@@ -81,6 +87,13 @@ export function shouldShowCacheWarning(
8187 let state = cacheWarningStateBySource . get ( querySource )
8288 if ( ! state ) {
8389 state = { lastHitRate : null , lastTimestamp : null }
90+ // Evict oldest entry when at capacity so the Map stays bounded
91+ if ( cacheWarningStateBySource . size >= MAX_SOURCE_ENTRIES ) {
92+ const oldestKey = cacheWarningStateBySource . keys ( ) . next ( ) . value
93+ if ( oldestKey !== undefined ) {
94+ cacheWarningStateBySource . delete ( oldestKey )
95+ }
96+ }
8497 cacheWarningStateBySource . set ( querySource , state )
8598 }
8699
@@ -132,3 +145,10 @@ export function createCacheWarningMessage(info: CacheHitRateInfo): Message {
132145 isMeta : false ,
133146 } as Message
134147}
148+
149+ /**
150+ * Reset the per-source tracking state — only used in tests.
151+ */
152+ export function _resetCacheWarningStateForTest ( ) : void {
153+ cacheWarningStateBySource . clear ( )
154+ }
0 commit comments