fix: 完善 StreamingToolExecutor.discard() 释放内部状态,修复 NO_FLICKER 模式内存泄漏

discard() 原先仅设置 flag,不释放 tools 数组、siblingAbortController 和 turnSpan。
NO_FLICKER 模式 API 重试时旧工具结果堆积无法被 GC 回收。

修复内容:
- 中止 siblingAbortController 以取消运行中的工具子进程
- 清空 tools 数组释放 TrackedTool 引用(block、assistantMessage、results、pendingProgress)
- 清理 progressAvailableResolve 和 turnSpan
- 添加 7 个测试覆盖 discard 后的各种状态验证

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
claude-code-best
2026-04-28 23:06:31 +08:00
parent c4eab890e7
commit 0542ffea2f
3 changed files with 141 additions and 6 deletions

View File

@@ -64,9 +64,24 @@ export class StreamingToolExecutor {
* Discards all pending and in-progress tools. Called when streaming fallback
* occurs and results from the failed attempt should be abandoned.
* Queued tools won't start, and in-progress tools will receive synthetic errors.
*
* Releases all internal references (tools array, abort controller, context)
* so that the discarded executor and its buffered results can be garbage-collected.
* Without this, repeated API retries in NO_FLICKER mode accumulate leaked
* TrackedTool objects (each holding assistantMessage, results, pendingProgress).
*/
discard(): void {
this.discarded = true
// Abort running tool subprocesses (Bash spawns, etc.) so they don't
// continue producing results after the executor is replaced.
this.siblingAbortController.abort('streaming_fallback')
// Release references to allow GC of tool blocks, messages, and promises.
this.tools.length = 0
this.progressAvailableResolve = undefined
if (this.turnSpan) {
endToolBatchSpan(this.turnSpan)
this.turnSpan = null
}
}
/**