Skip to content

Commit f5c3ee5

Browse files
fix: 修复长时间运行会话的内存泄漏问题
/clear 时释放 STATE 中保存的大块数据(API 请求/分类器请求/模型统计), 全屏模式增加 500 条消息上限防止无限增长,修复 progress 消息去重逻辑 避免交错消息导致重复累积(观察到 13k+ 条目/1GB+ 堆)。 Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
1 parent c2ac9a7 commit f5c3ee5

2 files changed

Lines changed: 43 additions & 15 deletions

File tree

src/commands/clear/conversation.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,10 @@ import {
1010
getOriginalCwd,
1111
getSessionId,
1212
regenerateSessionId,
13+
resetCostState,
14+
setLastAPIRequest,
15+
setLastAPIRequestMessages,
16+
setLastClassifierRequests,
1317
} from '../../bootstrap/state.js'
1418
import type { SDKStatusMessage } from '../../entrypoints/sdk/coreTypes.js'
1519
import {
@@ -144,6 +148,14 @@ export async function clearConversation({
144148
// tracking) is retained so those agents keep functioning.
145149
clearSessionCaches(preservedAgentIds)
146150

151+
// Clear large STATE-held data that outlives the message array.
152+
// lastAPIRequestMessages can hold the full post-compaction conversation
153+
// (hundreds of KB–MB) for /share; resetCostState clears modelUsage.
154+
setLastAPIRequest(null)
155+
setLastAPIRequestMessages(null)
156+
setLastClassifierRequests(null)
157+
resetCostState()
158+
147159
setCwd(getOriginalCwd())
148160
readFileState.clear()
149161
discoveredSkillNames?.clear()

src/screens/REPL.tsx

Lines changed: 31 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -3051,12 +3051,22 @@ export function REPL({
30513051
// are O(n) per render, so drop everything before the previous
30523052
// boundary to keep n bounded across multi-day sessions.
30533053
if (isFullscreenEnvEnabled()) {
3054-
setMessages(old => [
3055-
...getMessagesAfterCompactBoundary(old, {
3054+
setMessages(old => {
3055+
const postBoundary = getMessagesAfterCompactBoundary(old, {
30563056
includeSnipped: true,
3057-
}),
3058-
newMessage,
3059-
]);
3057+
})
3058+
// Hard cap: keep at most 500 messages in fullscreen scrollback
3059+
// to prevent unbounded memory growth in multi-day sessions.
3060+
// normalizeMessages/applyGrouping are O(n), and Ink fiber
3061+
// trees cost ~250KB RSS per message. Without this cap,
3062+
// scrollback after several compactions can reach thousands
3063+
// of messages (observed: 13k+, 1GB+ heap).
3064+
const MAX_FULLSCREEN_SCROLLBACK = 500
3065+
const kept = postBoundary.length > MAX_FULLSCREEN_SCROLLBACK
3066+
? postBoundary.slice(-MAX_FULLSCREEN_SCROLLBACK)
3067+
: postBoundary
3068+
return [...kept, newMessage]
3069+
});
30603070
} else {
30613071
setMessages(() => [newMessage]);
30623072
}
@@ -3082,17 +3092,23 @@ export function REPL({
30823092
// history). Replacing those leaves the AgentTool UI stuck at
30833093
// "Initializing…" because it renders the full progress trail.
30843094
setMessages(oldMessages => {
3085-
const last = oldMessages.at(-1);
3086-
const lastData = last?.data as Record<string, unknown> | undefined;
30873095
const newData = newMessage.data as Record<string, unknown>;
3088-
if (
3089-
last?.type === 'progress' &&
3090-
last.parentToolUseID === newMessage.parentToolUseID &&
3091-
lastData?.type === newData.type
3092-
) {
3093-
const copy = oldMessages.slice();
3094-
copy[copy.length - 1] = newMessage;
3095-
return copy;
3096+
// Scan backwards to find the last ephemeral progress with matching
3097+
// parentToolUseID and type. Previously only checked the last message,
3098+
// so interleaved non-ephemeral messages caused duplicate progress
3099+
// entries to accumulate (observed 13k+ entries in sleep-heavy sessions).
3100+
for (let i = oldMessages.length - 1; i >= 0; i--) {
3101+
const m = oldMessages[i]!
3102+
if (m.type !== 'progress') break
3103+
const mData = m.data as Record<string, unknown> | undefined
3104+
if (
3105+
m.parentToolUseID === newMessage.parentToolUseID &&
3106+
mData?.type === newData.type
3107+
) {
3108+
const copy = oldMessages.slice();
3109+
copy[i] = newMessage;
3110+
return copy;
3111+
}
30963112
}
30973113
return [...oldMessages, newMessage];
30983114
});

0 commit comments

Comments
 (0)