42adfe29c8
The summarize (Stop) and observation (PostToolUse) handlers throw blocking errors (exit code 2) when optional input fields like transcriptPath, toolName, or cwd are missing. This causes visible hook errors on every session stop and after some tool uses. Replace throws with graceful returns matching the existing pattern used for worker-unavailable checks. Fixes #1097 Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
82 lines
3.3 KiB
TypeScript
82 lines
3.3 KiB
TypeScript
/**
|
|
* Observation Handler - PostToolUse
|
|
*
|
|
* Extracted from save-hook.ts - sends tool usage to worker for storage.
|
|
*/
|
|
|
|
import type { EventHandler, NormalizedHookInput, HookResult } from '../types.js';
|
|
import { ensureWorkerRunning, getWorkerPort } from '../../shared/worker-utils.js';
|
|
import { logger } from '../../utils/logger.js';
|
|
import { HOOK_EXIT_CODES } from '../../shared/hook-constants.js';
|
|
import { isProjectExcluded } from '../../utils/project-filter.js';
|
|
import { SettingsDefaultsManager } from '../../shared/SettingsDefaultsManager.js';
|
|
import { USER_SETTINGS_PATH } from '../../shared/paths.js';
|
|
|
|
export const observationHandler: EventHandler = {
|
|
async execute(input: NormalizedHookInput): Promise<HookResult> {
|
|
// Ensure worker is running before any other logic
|
|
const workerReady = await ensureWorkerRunning();
|
|
if (!workerReady) {
|
|
// Worker not available - skip observation gracefully
|
|
return { continue: true, suppressOutput: true, exitCode: HOOK_EXIT_CODES.SUCCESS };
|
|
}
|
|
|
|
const { sessionId, cwd, toolName, toolInput, toolResponse } = input;
|
|
|
|
if (!toolName) {
|
|
// No tool name provided - skip observation gracefully
|
|
return { continue: true, suppressOutput: true, exitCode: HOOK_EXIT_CODES.SUCCESS };
|
|
}
|
|
|
|
const port = getWorkerPort();
|
|
|
|
const toolStr = logger.formatTool(toolName, toolInput);
|
|
|
|
logger.dataIn('HOOK', `PostToolUse: ${toolStr}`, {
|
|
workerPort: port
|
|
});
|
|
|
|
// Validate required fields before sending to worker
|
|
if (!cwd) {
|
|
throw new Error(`Missing cwd in PostToolUse hook input for session ${sessionId}, tool ${toolName}`);
|
|
}
|
|
|
|
// Check if project is excluded from tracking
|
|
const settings = SettingsDefaultsManager.loadFromFile(USER_SETTINGS_PATH);
|
|
if (isProjectExcluded(cwd, settings.CLAUDE_MEM_EXCLUDED_PROJECTS)) {
|
|
logger.debug('HOOK', 'Project excluded from tracking, skipping observation', { cwd, toolName });
|
|
return { continue: true, suppressOutput: true };
|
|
}
|
|
|
|
// Send to worker - worker handles privacy check and database operations
|
|
try {
|
|
const response = await fetch(`http://127.0.0.1:${port}/api/sessions/observations`, {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify({
|
|
contentSessionId: sessionId,
|
|
tool_name: toolName,
|
|
tool_input: toolInput,
|
|
tool_response: toolResponse,
|
|
cwd
|
|
})
|
|
// Note: Removed signal to avoid Windows Bun cleanup issue (libuv assertion)
|
|
});
|
|
|
|
if (!response.ok) {
|
|
// Log but don't throw — observation storage failure should not block tool use
|
|
logger.warn('HOOK', 'Observation storage failed, skipping', { status: response.status, toolName });
|
|
return { continue: true, suppressOutput: true, exitCode: HOOK_EXIT_CODES.SUCCESS };
|
|
}
|
|
|
|
logger.debug('HOOK', 'Observation sent successfully', { toolName });
|
|
} catch (error) {
|
|
// Worker unreachable — skip observation gracefully
|
|
logger.warn('HOOK', 'Observation fetch error, skipping', { error: error instanceof Error ? error.message : String(error) });
|
|
return { continue: true, suppressOutput: true, exitCode: HOOK_EXIT_CODES.SUCCESS };
|
|
}
|
|
|
|
return { continue: true, suppressOutput: true };
|
|
}
|
|
};
|