From 9f6b68118e7fc350c1bdeaf91bf76e65567bbf31 Mon Sep 17 00:00:00 2001 From: brooksbUWO Date: Mon, 4 May 2026 13:27:07 -0500 Subject: [PATCH] fix(hooks): do not clear sessionFirstMessageProcessed on session.idle SessionStart hooks fire on every user prompt instead of only at session start. The root cause is clearSessionHookState(), called on every session.idle event, which clears sessionFirstMessageProcessed. This resets the isFirstMessage guard, making it always return true, so SessionStart hooks execute on every prompt. sessionFirstMessageProcessed is session-level state (tracks whether the first message has been processed) and should only be cleared in clearAllSessionHookState() on session deletion/disposal, not on idle. sessionErrorState and sessionInterruptState remain cleared on idle since they are per-response transient state. --- src/hooks/claude-code-hooks/session-hook-state.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/hooks/claude-code-hooks/session-hook-state.ts b/src/hooks/claude-code-hooks/session-hook-state.ts index a6b4024bd..cef303d30 100644 --- a/src/hooks/claude-code-hooks/session-hook-state.ts +++ b/src/hooks/claude-code-hooks/session-hook-state.ts @@ -7,7 +7,12 @@ export const sessionInterruptState = new Map() export function clearSessionHookState(sessionID: string): void { sessionErrorState.delete(sessionID) sessionInterruptState.delete(sessionID) - sessionFirstMessageProcessed.delete(sessionID) + // sessionFirstMessageProcessed must NOT be cleared on idle. + // It tracks whether the first message of a session has been processed, + // so that SessionStart hooks fire only once per session. Clearing it + // on idle (which fires after every model response) makes isFirstMessage + // always return true, causing SessionStart hooks to fire on every + // prompt instead of only the first one. } export function clearAllSessionHookState(): void {