fix(agent): let shutdown supersede a prior budget abort

requestAbort's abortSent branch only upgraded incoming signal reasons, so a
shutdown landing after a soft-budget hard-abort was discarded and abortKind()
stayed budget. finalizeSubagentLifecycle then followed the budget-resumable
path (idle + adopt) even though AgentLifecycleManager.dispose() had run,
leaking the subagent session into SDK/process reuse.

- Upgrade a prior budget abort to shutdown so the run takes the shutdown
  release path; genuine kills (signal/timeout/terminate) stay terminal and
  shutdown is never downgraded to signal.
- Cover the shutdown-races-budget-hard-abort case.

Fixes #8216
This commit is contained in:
roboomp
2026-08-11 06:51:52 +00:00
parent 546016596e
commit fb626e4ef0
2 changed files with 62 additions and 2 deletions
+15 -1
View File
@@ -1095,7 +1095,21 @@ function createSubagentRunMonitor(args: RunMonitorArgs): SubagentRunMonitor {
budgetLimitExceeded = true;
}
if (abortSent) {
if (reason === "signal" && abortReason !== "signal" && abortReason !== "timeout") {
// Shutdown is a superseding external abort: a process teardown that
// races a self-inflicted budget hard-abort must still follow the
// shutdown release path (dispose + unregister) instead of the
// budget-resumable path, which would leave the subagent adopted and
// alive past AgentLifecycleManager.dispose(). Genuine kills
// (signal/timeout/terminate) already dispose terminally, and shutdown
// is never downgraded back to signal.
if (reason === "shutdown" && abortReason === "budget") {
abortReason = "shutdown";
} else if (
reason === "signal" &&
abortReason !== "signal" &&
abortReason !== "timeout" &&
abortReason !== "shutdown"
) {
abortReason = "signal";
}
return;