fix: 内存优化 — FileReadTool 100KB 上限、lookups 缓存、microcompact 替换清理

- FileReadTool maxResultSizeChars 从 Infinity 改为 100KB,大文件持久化到磁盘
- Messages.tsx 新增 computeMessageStructureKey 缓存,流式 delta 时跳过 8 个 Map/Set 重建
- microcompact 返回 clearedToolUseIds,query.ts 消费后清理 replacements Map 释放原始字符串
- 更新内存分析报告 Round 5 和 file-operations 文档

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
claude-code-best
2026-05-02 11:21:22 +08:00
parent 3eba5ade1a
commit f724300079
8 changed files with 205 additions and 32 deletions

View File

@@ -1397,6 +1397,54 @@ export function buildMessageLookups(
}
}
/**
* Compute a lightweight structural fingerprint for buildMessageLookups caching.
* Only captures information that affects lookup results (types, IDs, counts),
* not content. Returns an empty string when the arrays are structurally empty.
*
* O(n) but allocates only a string — much cheaper than the 8 Maps/Sets that
* buildMessageLookups creates on every call.
*/
export function computeMessageStructureKey(
normalizedMessages: NormalizedMessage[],
messages: Message[],
): string {
const parts: string[] = [
String(normalizedMessages.length),
'|',
String(messages.length),
]
for (const msg of messages) {
parts.push(msg.type[0])
if (msg.type === 'assistant') {
const aMsg = msg as AssistantMessage
const content = aMsg.message?.content
if (Array.isArray(content)) {
for (const block of content) {
if (typeof block !== 'string' && block.type === 'tool_use') {
parts.push('t', (block as ToolUseBlock).id)
}
}
}
} else if (msg.type === 'user') {
const content = (msg as UserMessage).message?.content
if (Array.isArray(content)) {
for (const block of content) {
if (typeof block !== 'string' && block.type === 'tool_result') {
parts.push('r', (block as ToolResultBlockParam).tool_use_id)
}
}
}
}
}
for (const msg of normalizedMessages) {
if (msg.type === 'progress') {
parts.push('p', (msg as ProgressMessage).parentToolUseID as string)
}
}
return parts.join(',')
}
/** Empty lookups for static rendering contexts that don't need real lookups. */
export const EMPTY_LOOKUPS: MessageLookups = {
siblingToolUseIDs: new Map(),