diff --git a/src/main/providers/local-pty-provider-shutdown.test.ts b/src/main/providers/local-pty-provider-shutdown.test.ts index a3b4021411b..2fd971c0a4f 100644 --- a/src/main/providers/local-pty-provider-shutdown.test.ts +++ b/src/main/providers/local-pty-provider-shutdown.test.ts @@ -523,6 +523,8 @@ describe('LocalPtyProvider', () => { expect.any(Function), expect.objectContaining({ ownsRoot: expect.any(Function) }) ) + // Root group SIGKILL must wait for the descendant SIGTERM grace, or Claude skips SessionEnd. + expect(killWithDescendantSweepMock.mock.calls[0][2].awaitEscalation()).toBe(true) }) }) diff --git a/src/main/providers/local-pty-termination.ts b/src/main/providers/local-pty-termination.ts index 426a7aad346..4eace9ae35a 100644 --- a/src/main/providers/local-pty-termination.ts +++ b/src/main/providers/local-pty-termination.ts @@ -1,6 +1,7 @@ import type * as pty from 'node-pty' import { PhysicalExitTracker } from '../../shared/physical-exit-tracker' import { killWithDescendantSweep } from '../pty-descendant-termination' +import { terminateShutdownDescendants } from '../daemon/terminal-descendant-shutdown' import { forceKillPosixPtyProcessGroups } from '../pty/posix-pty-process-groups' import { terminatePtyJob } from '../windows/windows-pty-job' import { @@ -179,7 +180,10 @@ async function shutdownTrackedPty( // Typed agents also detach tool process groups; immediate close must snapshot before root exit. await killWithDescendantSweep(proc.pid, signalRoot, { ownsRoot: () => ptyProcesses.get(id) === proc, - terminateOwnedTree: () => terminatePtyJob(proc) + terminateOwnedTree: () => terminatePtyJob(proc), + terminateDescendants: terminateShutdownDescendants, + // Group SIGKILL must wait out the SIGTERM grace so agents can run exit hooks (Claude SessionEnd). + awaitEscalation: () => operation.immediate }) } else { signalRoot()