fix(claude-code-session-state): clear session-agent map on delete and sync cleanup

External review on PR #4074 noted that adding setSessionAgent for child
sessions left sessionAgentMap holding entries after the session was
deleted or after the sync call_omo_agent executor cleaned up other
owned state. The map only grows; entries never get reused but they do
accumulate across long-running plugin instances.

Close both gaps:
- BackgroundManager.handleEvent for session.deleted now calls
  clearSessionAgent for the deleted session id on both the early-return
  no-task branch and the cascade tail. This pairs with the existing
  clearDelegatedChildSessionBootstrap and SessionCategoryRegistry.remove
  so all owned session state is dropped together.
- sync-executor finally for createdSessionForExecution now calls
  clearSessionAgent alongside the existing subagentSessions,
  syncSubagentSessions, and deleteSessionTools cleanup so sessions this
  executor created cannot leak their agent mapping.

Adds focused tests:
- BackgroundManager.handleEvent - session.deleted cascade > should
  clear session agent state for deleted sessions to prevent map leak
- executeSync > registers child-session bootstrap and tracked prompt
  state before sync prompt dispatch (extended assertion for cleanup)
This commit is contained in:
YeonGyu-Kim
2026-05-17 01:27:00 +09:00
parent 25d8054192
commit 2b8782de85
4 changed files with 27 additions and 1 deletions
@@ -5063,6 +5063,27 @@ describe("BackgroundManager.handleEvent - session.deleted cascade", () => {
manager.shutdown()
})
test("should clear session agent state for deleted sessions to prevent map leak", async () => {
//#given
const { setSessionAgent } = await import("../claude-code-session-state")
resetClaudeCodeSessionState()
const manager = createBackgroundManager()
const sessionID = "session-deleted-agent-leak"
setSessionAgent(sessionID, "sisyphus-junior")
expect(getSessionAgent(sessionID)).toBe("sisyphus-junior")
//#when
manager.handleEvent({
type: "session.deleted",
properties: { info: { id: sessionID } },
})
//#then
expect(getSessionAgent(sessionID)).toBeUndefined()
manager.shutdown()
})
})
describe("BackgroundManager.handleEvent - session.error", () => {
+2
View File
@@ -1660,6 +1660,7 @@ The fallback retry session is now created and can be inspected directly.
if (tasksToCancel.size === 0) {
this.clearTaskHistoryWhenParentTasksGone(sessionID)
clearSessionAgent(sessionID)
return
}
@@ -1698,6 +1699,7 @@ The fallback retry session is now created and can be inspected directly.
this.rootDescendantCounts.delete(sessionID)
clearDelegatedChildSessionBootstrap(sessionID)
clearSessionAgent(sessionID)
SessionCategoryRegistry.remove(sessionID)
}