From 835dd2d804ef50016dd0a1e7671812be2cecb710 Mon Sep 17 00:00:00 2001 From: Cepvor <154242055+Evsdrg@users.noreply.github.com> Date: Sat, 16 May 2026 08:09:40 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E4=B8=BA=20sessionStorage=20existingSes?= =?UTF-8?q?sionFiles=20Map=20=E6=B7=BB=E5=8A=A0=E5=AE=B9=E9=87=8F=E4=B8=8A?= =?UTF-8?q?=E9=99=90=20(#1227)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix: 修复子代理 token 消耗在主 spinner 中始终显示为 0 Spinner.tsx 的 token 聚合循环仅统计 in_process_teammate 类型任务, 漏掉了 local_agent(后台代理/verification agent)类型。当后台代理 运行时,主界面 spinner 一直显示 "↓ 0 tokens",因为 background agent 的 token 消耗未被纳入 teammateTokens 聚合。 同时在 inProcessRunner.ts 中,进程内队友完成时计算并设置 result (含 totalTokens/totalToolUseCount/content/usage),使详情弹窗可以 正确展示累计 token 消耗,不再仅依赖 progress.tokenCount 间歇更新。 Co-Authored-By: deepseek-v4-pro[1m] * fix: 为 cacheWarningStateBySource Map 设置上限防止内存泄漏 Map 以 querySource 为 key 存储每个来源的缓存命中率历史状态, 但 querySource 类型为 `any`,长时间会话中可能产生大量唯一值, Map 持续增长永不清理。 新增 MAX_SOURCE_ENTRIES = 50 上限,新增条目时若达到上限则 逐出最早插入的条目(Map 按插入顺序迭代)。 同时也新增 _resetCacheWarningStateForTest() 用于测试隔离。 Co-Authored-By: deepseek-v4-pro[1m] * fix: 为 sessionStorage existingSessionFiles Map 添加容量上限 existingSessionFiles Map 缓存 sessionId → 文件路径映射以避免重复 stat 调用,但在 coordinator/swarm 模式下,每个子代理产生独立 sessionId,长时间运行的 daemon 会话可能累积数千条目。 新增 MAX_CACHED_SESSION_FILES = 200 上限,新增条目时若达到上限则 逐出最早插入的条目。同时在 _resetFlushState() 中清除此缓存以保证 测试隔离。 Co-Authored-By: deepseek-v4-pro[1m] --------- Co-authored-by: deepseek-v4-pro[1m] --- src/utils/sessionStorage.ts | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/utils/sessionStorage.ts b/src/utils/sessionStorage.ts index e38dd96b0..7d0683d8e 100644 --- a/src/utils/sessionStorage.ts +++ b/src/utils/sessionStorage.ts @@ -529,6 +529,10 @@ export function setRemoteIngressUrlForTesting(url: string): void { const REMOTE_FLUSH_INTERVAL_MS = 10 +// Limit the number of cached session-file lookups to prevent unbounded Map growth +// in long-running daemon / swarm sessions that spawn many sub-agents. +const MAX_CACHED_SESSION_FILES = 200 + class Project { // Minimal cache for current session only (not all sessions) currentSessionTag: string | undefined @@ -577,6 +581,7 @@ class Project { this.flushTimer = null this.activeDrain = null this.writeQueues = new Map() + this.existingSessionFiles = new Map() } private incrementPendingWrites(): void { @@ -1288,6 +1293,9 @@ class Project { * Returns the session file path if it exists, null otherwise. * Used for writing to sessions other than the current one. * Caches positive results so we only stat once per session. + * + * The cache is bounded at MAX_CACHED_SESSION_FILES to prevent unbounded + * growth in long-running daemon / swarm sessions that spawn many agents. */ private existingSessionFiles = new Map() private async getExistingSessionFile( @@ -1299,6 +1307,13 @@ class Project { const targetFile = getTranscriptPathForSession(sessionId) try { await stat(targetFile) + // Evict oldest entry when at capacity so the Map stays bounded + if (this.existingSessionFiles.size >= MAX_CACHED_SESSION_FILES) { + const oldestKey = this.existingSessionFiles.keys().next().value + if (oldestKey !== undefined) { + this.existingSessionFiles.delete(oldestKey) + } + } this.existingSessionFiles.set(sessionId, targetFile) return targetFile } catch (e) {