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>
71 lines
2.7 KiB
TypeScript
71 lines
2.7 KiB
TypeScript
/**
|
|
* Summarize Handler - Stop
|
|
*
|
|
* Extracted from summary-hook.ts - sends summary request to worker.
|
|
* Transcript parsing stays in the hook because only the hook has access to
|
|
* the transcript file path.
|
|
*/
|
|
|
|
import type { EventHandler, NormalizedHookInput, HookResult } from '../types.js';
|
|
import { ensureWorkerRunning, getWorkerPort, fetchWithTimeout } from '../../shared/worker-utils.js';
|
|
import { logger } from '../../utils/logger.js';
|
|
import { extractLastMessage } from '../../shared/transcript-parser.js';
|
|
import { HOOK_EXIT_CODES, HOOK_TIMEOUTS, getTimeout } from '../../shared/hook-constants.js';
|
|
|
|
const SUMMARIZE_TIMEOUT_MS = getTimeout(HOOK_TIMEOUTS.DEFAULT);
|
|
|
|
export const summarizeHandler: 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 summary gracefully
|
|
return { continue: true, suppressOutput: true, exitCode: HOOK_EXIT_CODES.SUCCESS };
|
|
}
|
|
|
|
const { sessionId, transcriptPath } = input;
|
|
|
|
const port = getWorkerPort();
|
|
|
|
// Validate required fields before processing
|
|
if (!transcriptPath) {
|
|
// No transcript available - skip summary gracefully (not an error)
|
|
logger.debug('HOOK', `No transcriptPath in Stop hook input for session ${sessionId} - skipping summary`);
|
|
return { continue: true, suppressOutput: true, exitCode: HOOK_EXIT_CODES.SUCCESS };
|
|
}
|
|
|
|
// Extract last assistant message from transcript (the work Claude did)
|
|
// Note: "user" messages in transcripts are mostly tool_results, not actual user input.
|
|
// The user's original request is already stored in user_prompts table.
|
|
const lastAssistantMessage = extractLastMessage(transcriptPath, 'assistant', true);
|
|
|
|
logger.dataIn('HOOK', 'Stop: Requesting summary', {
|
|
workerPort: port,
|
|
hasLastAssistantMessage: !!lastAssistantMessage
|
|
});
|
|
|
|
// Send to worker - worker handles privacy check and database operations
|
|
const response = await fetchWithTimeout(
|
|
`http://127.0.0.1:${port}/api/sessions/summarize`,
|
|
{
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify({
|
|
contentSessionId: sessionId,
|
|
last_assistant_message: lastAssistantMessage
|
|
}),
|
|
},
|
|
SUMMARIZE_TIMEOUT_MS
|
|
);
|
|
|
|
if (!response.ok) {
|
|
// Return standard response even on failure (matches original behavior)
|
|
return { continue: true, suppressOutput: true };
|
|
}
|
|
|
|
logger.debug('HOOK', 'Summary request sent successfully');
|
|
|
|
return { continue: true, suppressOutput: true };
|
|
}
|
|
};
|