fix: 为 sessionStorage existingSessionFiles Map 添加容量上限 (#1227)

* 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] <deepseek-ai@claude-code-best.win>

* fix: 为 cacheWarningStateBySource Map 设置上限防止内存泄漏

Map 以 querySource 为 key 存储每个来源的缓存命中率历史状态,
但 querySource 类型为 `any`,长时间会话中可能产生大量唯一值,
Map 持续增长永不清理。

新增 MAX_SOURCE_ENTRIES = 50 上限,新增条目时若达到上限则
逐出最早插入的条目(Map 按插入顺序迭代)。

同时也新增 _resetCacheWarningStateForTest() 用于测试隔离。

Co-Authored-By: deepseek-v4-pro[1m] <deepseek-ai@claude-code-best.win>

* 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] <deepseek-ai@claude-code-best.win>

---------

Co-authored-by: deepseek-v4-pro[1m] <deepseek-ai@claude-code-best.win>
This commit is contained in:
Cepvor
2026-05-16 08:09:40 +08:00
committed by GitHub
parent 0face46fbe
commit 835dd2d804

View File

@@ -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<string, string>()
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) {