fix(abort): wire /abort + sessions.reset/delete to killSessionRun — actually terminate CLI subprocesses#2462
Merged
alexey-pelykh merged 1 commit intomainfrom Apr 22, 2026
Conversation
…ctually terminate CLI subprocesses (#2343) Replace three dead `const aborted = false` / `const ended = true` placeholders left behind by the Pi-embedded gut (`b27cecc795`, #76/#77) with real calls to `killSessionRun` / `waitForSessionRunEnd` from `src/agents/session-run-registry.ts`: - `abort.ts::stopSubagentsForRequester` inner loop — `killSessionRun(childKey)` now flows into the `killed` aggregate, so the loop actually counts subagent runs whose subprocesses were signaled. - `abort.ts::tryFastAbortFromMessage` — `killSessionRun(resolvedTargetKey)` returned in the `aborted` field (previously always `false`). User-facing `/abort` now terminates the running CLI subprocess for non-ACP sessions; ACP cancellation path remains separate. - `sessions.ts::ensureSessionRuntimeCleanup` — replaces the `const ended = true` placeholder with `killSessionRun(canonicalKey)` + `waitForSessionRunEnd` (15s timeout). Returns `UNAVAILABLE` on timeout, restoring the pre-gut contract that `sessions.reset` / `sessions.delete` surface the error when a subprocess refuses to end. Also removes the `if (!params.sessionId) return undefined;` early-exit in `ensureSessionRuntimeCleanup`: `sessionId` is used only to extend the queue-key set; termination operates on `canonicalKey`, which is always present. Callers with no `sessionId` now correctly attempt termination (`waitForSessionRunEnd` short-circuits to `true` when no run is registered, so the wait has no cost when there's nothing to wait for). Polish: extracted `15_000` to `SESSION_RUN_TERMINATION_TIMEOUT_MS`, grouped with the sibling `ACP_RUNTIME_CLEANUP_TIMEOUT_MS` constant. Issue body lists three regression sites; only two apply. The third (`commands-session.ts::applyAbortTarget`) does not exist in the current tree — `git grep applyAbortTarget` returns zero hits. The `/session abort` pathway was consolidated into `tryFastAbortFromMessage` at some earlier point, so no duplicate wiring is needed. AC3 marked N/A. Verification: - `pnpm check` → exit 0 - `pnpm test` (full parallel suite) → 800 files / 7010 passed / 3 skipped — no regression - Rescan: `git grep "const aborted = false"` → zero hits across the whole tree (combined sweep of #2344 subagent kill/steer + #2343 abort/subagent-cascade/fastAbort) - Rescan: `git grep "abortEmbeddedPiRun"` → only test-mock contexts; production has no remaining references (test-mock cleanup tracked under #2089 sweep) - Adversarial validation (fresh-context subclaude): CLEAN on 7 AC + 9 adversarial checks — confirms waitForSessionRunEnd is LIVE (50ms poll tick, max 15s wall-clock), `ensureSessionRuntimeCleanup` callers always pass a valid `canonicalKey`, and no test asserts `aborted === false` on the fast-abort path Closes #2343 Refs: #2089, #2344, #2460, #2461 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
The user-facing
/abortcommand and the gatewaysessions.reset/sessions.deletemethods lost their subprocess-termination semantics in the pi-embedded gut (b27cecc795, #76/#77). Both call sites were rewiring points for ChannelBridge; replacements (killSessionRun+waitForSessionRunEnd) exist insrc/agents/session-run-registry.tsbut were never wired to them — they left behind threeconst aborted = false;/const ended = true;dead placeholders.This PR wires the three call sites. Same family as #2460 (gateway capacity accounting) and #2461 (subagent kill/steer), completing the trio of
session-run-registryintegrations that were outstanding after the gut.Changes
src/auto-reply/reply/abort.tsstopSubagentsForRequesterinner loop (line ~248)const aborted = false;→const aborted = killSessionRun(childKey);(flows intokilledaggregate)src/auto-reply/reply/abort.tstryFastAbortFromMessage(line ~329)const aborted = false;→const aborted = killSessionRun(resolvedTargetKey);(now returned in theabortedfield of the public result)src/gateway/server-methods/sessions.tsensureSessionRuntimeCleanup(line ~178)const ended = true;placeholder withkillSessionRun(canonicalKey)+await waitForSessionRunEnd(canonicalKey, SESSION_RUN_TERMINATION_TIMEOUT_MS). ReturnUNAVAILABLEon timeout. Also removes obsoleteif (!params.sessionId) return undefined;early-exit (termination operates oncanonicalKey, notsessionId).Plus one import per file and a new
SESSION_RUN_TERMINATION_TIMEOUT_MS = 15_000constant grouped with the siblingACP_RUNTIME_CLEANUP_TIMEOUT_MS.Note on stale AC3
Issue #2343 lists three regression sites. The third (
commands-session.ts::applyAbortTarget) does not exist in the current tree —git grep applyAbortTargetreturns zero hits. The/session abortpathway was consolidated intoabort.ts::tryFastAbortFromMessageat some earlier point, so no duplicate wiring is needed. AC3 marked N/A.Behavior change (intended, per AC)
sessions.reset/sessions.delete:const ended = true;placeholder).undefined(success). On timeout →UNAVAILABLEerror with the pre-gut message"Session ${params.key} is still active; try again in a moment."./abortcommand (non-ACP sessions):abortedLastRun = trueand cleared queues, but the CLI subprocess kept running until natural completion.abortController.abort()orprocess.kill(pid, "SIGTERM")viakillSessionRun; theabortedfield in the return value now reflects reality.Key-format alignment
killSessionRunkeys on canonicalsessionKey. All three call sites already operate in canonical space:abort.tssubagent loop:childKeyfromparseAgentSessionKey(canonical)abort.tsfast-abort:resolvedTargetKey = key ?? targetKeyfromresolveSessionEntryForKey(canonical)sessions.ts:params.target.canonicalKey(canonical by construction viaresolveGatewaySessionStoreTarget)No conversion risk. Inherits the registration-vs-kill key discipline established in PRs #2460 / #2461.
Verification
pnpm check(format + tsgo + lint + project-specific lints) → exit 0pnpm test(full parallel suite) → 800 files / 7010 passed / 3 skipped — no regressiongit grep "const aborted = false"→ zero hits (combined sweep of fix(subagents): wire kill and steer actions to session-run-registry killSessionRun #2344 + this PR)git grep "abortEmbeddedPiRun"→ only test-mock contexts; production has no remaining referenceswaitForSessionRunEndis LIVE (50ms poll tick, max 15s wall-clock, non-blocking async)ensureSessionRuntimeCleanupcallers always pass a validcanonicalKeyabortedfield (callers read.handledand.stoppedSubagentsonly)killSessionRunnever throws (internal try/catch aroundprocess.kill)@ts-expect-error/@ts-ignore/as anyintroducedTest plan
pnpm checkexit 0pnpm testfull suite green (7010 / 800 files)Context
getActiveSessionRunCount) + fix(subagents): wire kill and steer to killSessionRun — actually terminate CLI subprocesses #2461 (subagents viakillSessionRun)b27cecc795)src/agents/session-run-registry.tsCloses #2343
Refs: #2089, #2344, #2460, #2461
🤖 Generated with Claude Code