fix: dispose SettingsManager on session close to prevent resource leaks (#454)

## Summary

- `SettingsManager.dispose()` was never called when a session was closed
via `unstable_closeSession` or when the Claude Agent process died
unexpectedly
- Each leaked session left up to 4 active `fs.watch()` file descriptors
and a pending debounce timer
- Added `session.settingsManager.dispose()` in both cleanup paths and
updated test mocks to assert the call

## Test plan

- [x] `npm run build` compiles clean
- [x] `npm run lint` passes
- [x] Existing `session/close` tests updated to verify `dispose()` is
called
- [x] All 4 session/close tests pass
This commit is contained in:
tbounsiar
2026-03-24 22:19:02 +01:00
committed by GitHub
parent 83496544b2
commit 5fd7873c7c
2 changed files with 6 additions and 3 deletions

View File

@@ -811,6 +811,7 @@ export class ClaudeAcpAgent implements Agent {
message.includes("Failed to write to process stdin")
) {
this.logger.error(`Session ${params.sessionId}: Claude Agent process died: ${message}`);
session.settingsManager.dispose();
session.input.end();
delete this.sessions[params.sessionId];
throw RequestError.internalError(
@@ -858,6 +859,7 @@ export class ClaudeAcpAgent implements Agent {
}
await this.cancel({ sessionId: params.sessionId });
session.settingsManager.dispose();
session.abortController.abort();
delete this.sessions[params.sessionId];

View File

@@ -1336,7 +1336,7 @@ describe("stop reason propagation", () => {
currentModelId: "default",
availableModels: [],
},
settingsManager: {} as any,
settingsManager: { dispose: vi.fn() } as any,
accumulatedUsage: {
inputTokens: 0,
outputTokens: 0,
@@ -1470,7 +1470,7 @@ describe("stop reason propagation", () => {
currentModelId: "default",
availableModels: [],
},
settingsManager: {} as any,
settingsManager: { dispose: vi.fn() } as any,
accumulatedUsage: {
inputTokens: 0,
outputTokens: 0,
@@ -1543,7 +1543,7 @@ describe("session/close", () => {
currentModelId: "default",
availableModels: [],
},
settingsManager: {} as any,
settingsManager: { dispose: vi.fn() } as any,
accumulatedUsage: {
inputTokens: 0,
outputTokens: 0,
@@ -1570,6 +1570,7 @@ describe("session/close", () => {
expect(result).toEqual({});
expect(agent.sessions["session-1"]).toBeUndefined();
expect(session.query.interrupt).toHaveBeenCalled();
expect(session.settingsManager.dispose).toHaveBeenCalled();
});
it("should abort the session's abort controller", async () => {