From 0b5a4549a651e9520ff4eabaa725cd6b87533cf8 Mon Sep 17 00:00:00 2001 From: jnjpng Date: Tue, 3 Feb 2026 17:47:22 -0800 Subject: [PATCH] feat: add debug logging for silently caught errors (#809) Co-authored-by: Letta --- src/cli/App.tsx | 4 ++-- src/cli/helpers/accumulator.ts | 9 ++++++-- src/hooks/loader.ts | 38 +++++++++++++++++++++++++++------- src/hooks/writer.ts | 11 ++++++++-- src/tools/manager.ts | 25 +++++++++++++++------- 5 files changed, 66 insertions(+), 21 deletions(-) diff --git a/src/cli/App.tsx b/src/cli/App.tsx index 8ea4dcb..7a857f0 100644 --- a/src/cli/App.tsx +++ b/src/cli/App.tsx @@ -386,8 +386,8 @@ function sendDesktopNotification( // Send terminal bell for native notification process.stdout.write("\x07"); // Run Notification hooks (fire-and-forget, don't block) - runNotificationHooks(message, level).catch(() => { - // Silently ignore hook errors + runNotificationHooks(message, level).catch((error) => { + debugLog("hooks", "Notification hook error", error); }); } diff --git a/src/cli/helpers/accumulator.ts b/src/cli/helpers/accumulator.ts index 7652d58..1673be2 100644 --- a/src/cli/helpers/accumulator.ts +++ b/src/cli/helpers/accumulator.ts @@ -7,6 +7,7 @@ import type { LettaStreamingResponse } from "@letta-ai/letta-client/resources/agents/messages"; import { INTERRUPTED_BY_USER } from "../../constants"; import { runPostToolUseHooks, runPreToolUseHooks } from "../../hooks"; +import { debugLog } from "../../utils/debug"; import { extractCompactionSummary } from "./backfill"; import { findLastSafeSplitPoint } from "./markdownSplit"; import { isShellTool } from "./toolNameMapping"; @@ -652,7 +653,9 @@ export function onChunk(b: Buffers, chunk: LettaStreamingResponse) { toolCallId, undefined, b.agentId, - ).catch(() => {}); + ).catch((error) => { + debugLog("hooks", "PreToolUse hook error (accumulator)", error); + }); } } @@ -748,7 +751,9 @@ export function onChunk(b: Buffers, chunk: LettaStreamingResponse) { b.agentId, precedingReasoning, precedingAssistantMessage, - ).catch(() => {}); + ).catch((error) => { + debugLog("hooks", "PostToolUse hook error (accumulator)", error); + }); b.serverToolCalls.delete(toolCallId); } diff --git a/src/hooks/loader.ts b/src/hooks/loader.ts index 1424a1c..cbbd58b 100644 --- a/src/hooks/loader.ts +++ b/src/hooks/loader.ts @@ -2,6 +2,7 @@ // Loads and matches hooks from settings-manager import { settingsManager } from "../settings-manager"; +import { debugLog } from "../utils/debug"; import { type HookCommand, type HookEvent, @@ -27,8 +28,9 @@ export function clearHooksCache(): void { export function loadGlobalHooks(): HooksConfig { try { return settingsManager.getSettings().hooks || {}; - } catch { + } catch (error) { // Settings not initialized yet + debugLog("hooks", "loadGlobalHooks: Settings not initialized yet", error); return {}; } } @@ -48,8 +50,9 @@ export async function loadProjectHooks( await settingsManager.loadProjectSettings(workingDirectory); } return settingsManager.getProjectSettings(workingDirectory)?.hooks || {}; - } catch { + } catch (error) { // Settings not available + debugLog("hooks", "loadProjectHooks: Settings not available", error); return {}; } } @@ -71,8 +74,9 @@ export async function loadProjectLocalHooks( return ( settingsManager.getLocalProjectSettings(workingDirectory)?.hooks || {} ); - } catch { + } catch (error) { // Settings not available + debugLog("hooks", "loadProjectLocalHooks: Settings not available", error); return {}; } } @@ -162,8 +166,13 @@ export function matchesTool(pattern: string, toolName: string): boolean { try { const regex = new RegExp(`^(?:${pattern})$`); return regex.test(toolName); - } catch { + } catch (error) { // Invalid regex, fall back to exact match + debugLog( + "hooks", + `matchesTool: Invalid regex pattern "${pattern}", falling back to exact match`, + error, + ); return pattern === toolName; } } @@ -270,8 +279,13 @@ export function areHooksDisabled( if (projectDisabled === true) { return true; } - } catch { + } catch (error) { // Project settings not loaded, skip + debugLog( + "hooks", + "areHooksDisabled: Project settings not loaded, skipping", + error, + ); } // Check project-local settings @@ -282,12 +296,22 @@ export function areHooksDisabled( if (localDisabled === true) { return true; } - } catch { + } catch (error) { // Local project settings not loaded, skip + debugLog( + "hooks", + "areHooksDisabled: Local project settings not loaded, skipping", + error, + ); } return false; - } catch { + } catch (error) { + debugLog( + "hooks", + "areHooksDisabled: Failed to check hooks disabled status", + error, + ); return false; } } diff --git a/src/hooks/writer.ts b/src/hooks/writer.ts index 023e7dc..7d97451 100644 --- a/src/hooks/writer.ts +++ b/src/hooks/writer.ts @@ -2,6 +2,7 @@ // Functions to write hooks to settings files via settings-manager import { settingsManager } from "../settings-manager"; +import { debugLog } from "../utils/debug"; import { type HookEvent, type HookMatcher, @@ -37,8 +38,9 @@ export function loadHooksFromLocation( settingsManager.getLocalProjectSettings(workingDirectory)?.hooks || {} ); } - } catch { + } catch (error) { // Settings not loaded yet, return empty + debugLog("hooks", "loadHooksFromLocation: Settings not loaded yet", error); return {}; } } @@ -374,7 +376,12 @@ export function countHooksForEvent( export function isUserHooksDisabled(): boolean { try { return settingsManager.getSettings().hooks?.disabled === true; - } catch { + } catch (error) { + debugLog( + "hooks", + "isUserHooksDisabled: Failed to check user hooks disabled status", + error, + ); return false; } } diff --git a/src/tools/manager.ts b/src/tools/manager.ts index 8b1f414..3455384 100644 --- a/src/tools/manager.ts +++ b/src/tools/manager.ts @@ -8,6 +8,7 @@ import { runPreToolUseHooks, } from "../hooks"; import { telemetry } from "../telemetry"; +import { debugLog } from "../utils/debug"; import { TOOL_DEFINITIONS, type ToolName } from "./toolDefinitions"; export const TOOL_NAMES = Object.keys(TOOL_DEFINITIONS) as ToolName[]; @@ -976,8 +977,8 @@ export async function executeTool( undefined, // precedingAssistantMessage - not available in tool manager context ); postToolUseFeedback = postHookResult.feedback; - } catch { - // Silently ignore hook errors - don't affect tool execution + } catch (error) { + debugLog("hooks", "PostToolUse hook error (success path)", error); } // Run PostToolUseFailure hooks when tool returns error status @@ -1000,8 +1001,12 @@ export async function executeTool( undefined, // precedingAssistantMessage - not available in tool manager context ); postToolUseFailureFeedback = failureHookResult.feedback; - } catch { - // Silently ignore hook execution errors + } catch (error) { + debugLog( + "hooks", + "PostToolUseFailure hook error (tool returned error)", + error, + ); } } @@ -1079,8 +1084,8 @@ export async function executeTool( undefined, // precedingAssistantMessage - not available in tool manager context ); postToolUseFeedback = postHookResult.feedback; - } catch { - // Silently ignore hook errors + } catch (error) { + debugLog("hooks", "PostToolUse hook error (error path)", error); } // Run PostToolUseFailure hooks - exit 2 injects stderr @@ -1098,8 +1103,12 @@ export async function executeTool( undefined, // precedingAssistantMessage - not available in tool manager context ); postToolUseFailureFeedback = failureHookResult.feedback; - } catch { - // Silently ignore hook execution errors + } catch (error) { + debugLog( + "hooks", + "PostToolUseFailure hook error (exception path)", + error, + ); } // Combine feedback from both hook types