Skip to content

Commit 9fb631f

Browse files
authored
fix: better shutdown logic (#543)
Make cancellation a no-op for unknown sessions, close each session query during disposal, and run the same cleanup path on SIGINT and SIGTERM in CLI mode. Follow up to #530
1 parent 5c81e99 commit 9fb631f

3 files changed

Lines changed: 14 additions & 7 deletions

File tree

src/acp-agent.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -968,7 +968,7 @@ export class ClaudeAcpAgent implements Agent {
968968
async cancel(params: CancelNotification): Promise<void> {
969969
const session = this.sessions[params.sessionId];
970970
if (!session) {
971-
throw new Error("Session not found");
971+
return;
972972
}
973973
session.cancelled = true;
974974
for (const [, pending] of session.pendingMessages) {
@@ -988,6 +988,7 @@ export class ClaudeAcpAgent implements Agent {
988988
await this.cancel({ sessionId });
989989
session.settingsManager.dispose();
990990
session.abortController.abort();
991+
session.query.close();
991992
delete this.sessions[sessionId];
992993
}
993994

src/index.ts

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -26,15 +26,20 @@ if (process.argv.includes("--cli")) {
2626

2727
const { connection, agent } = runAcp();
2828

29-
// Exit cleanly when the ACP connection closes (e.g. stdin EOF, transport
30-
// error). Without this, `process.stdin.resume()` keeps the event loop
31-
// alive indefinitely, causing orphan process accumulation in oneshot mode.
32-
connection.closed.then(async () => {
29+
async function shutdown() {
3330
await agent.dispose().catch((err) => {
3431
console.error("Error during cleanup:", err);
3532
});
3633
process.exit(0);
37-
});
34+
}
35+
36+
// Exit cleanly when the ACP connection closes (e.g. stdin EOF, transport
37+
// error). Without this, `process.stdin.resume()` keeps the event loop
38+
// alive indefinitely, causing orphan process accumulation in oneshot mode.
39+
connection.closed.then(shutdown);
40+
41+
process.on("SIGTERM", shutdown);
42+
process.on("SIGINT", shutdown);
3843

3944
// Keep process alive while connection is open
4045
process.stdin.resume();

src/tests/acp-agent.test.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1538,7 +1538,7 @@ describe("session/close", () => {
15381538

15391539
function injectSession(agent: ClaudeAcpAgent, sessionId: string) {
15401540
function* empty() {}
1541-
const gen = Object.assign(empty(), { interrupt: vi.fn() });
1541+
const gen = Object.assign(empty(), { interrupt: vi.fn(), close: vi.fn() });
15421542
agent.sessions[sessionId] = {
15431543
query: gen as any,
15441544
input: new Pushable(),
@@ -1633,6 +1633,7 @@ describe("getOrCreateSession param change detection", () => {
16331633
function* empty() {}
16341634
const gen = Object.assign(empty(), {
16351635
interrupt: vi.fn(),
1636+
close: vi.fn(),
16361637
supportedCommands: vi.fn().mockResolvedValue([]),
16371638
});
16381639
agent.sessions[sessionId] = {

0 commit comments

Comments
 (0)