fix(tools): prevent AbortSignal listener leak in exec and process tools

Each tool call added an abort listener to the shared agent signal
without cleanup, exceeding the default 10-listener limit after 11+
exec calls. Fix by using { once: true } and removing the listener
on child process close (exec) to prevent accumulation.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
yushen 2026-02-06 19:58:31 +08:00
parent a3f5a21561
commit c24fafadeb
2 changed files with 14 additions and 11 deletions

View file

@ -164,9 +164,22 @@ export function createExecTool(
// Don't reject, let close event handle
});
// Signal handling: don't kill if already backgrounded
const onAbort = signal ? () => {
if (yielded) return; // Already backgrounded, ignore abort
if (timeout) clearTimeout(timeout);
if (yieldTimer) clearTimeout(yieldTimer);
child.kill("SIGTERM");
} : undefined;
if (signal && onAbort) {
signal.addEventListener("abort", onAbort, { once: true });
}
child.on("close", (code) => {
if (timeout) clearTimeout(timeout);
if (yieldTimer) clearTimeout(yieldTimer);
if (signal && onAbort) signal.removeEventListener("abort", onAbort);
// If already backgrounded, don't resolve again
if (yielded) return;
@ -202,16 +215,6 @@ export function createExecTool(
},
});
});
// Signal handling: don't kill if already backgrounded
if (signal) {
signal.addEventListener("abort", () => {
if (yielded) return; // Already backgrounded, ignore abort
if (timeout) clearTimeout(timeout);
if (yieldTimer) clearTimeout(yieldTimer);
child.kill("SIGTERM");
});
}
});
},
};

View file

@ -112,7 +112,7 @@ export function createProcessTool(defaultCwd?: string): AgentTool<typeof Process
if (signal) {
signal.addEventListener("abort", () => {
child.kill("SIGTERM");
});
}, { once: true });
}
resolve({ success: true });