Files
claude-mem/src/hooks/summary-hook.ts
T
Erik M Jacobs 3ea180c1ef refactor(worker): Remove file-based locking and improve Windows stability
This commit simplifies worker startup coordination and addresses Windows-specific issues:

**Lock Removal**:
- Removed entire file-based locking system (~100 lines)
- Replaced with health-check-first approach
- Port binding provides natural mutual exclusion - multiple spawns fail cleanly

**Windows Stability**:
- Removed all AbortSignal.timeout() calls to reduce Bun libuv assertion errors
- Added 500ms shutdown delays on Windows to prevent zombie ports
- Worker service has its own timeouts, so client-side timeouts are redundant

**Package.json Updates**:
- Updated worker scripts to use worker-service.cjs directly
- Removed references to deleted worker-cli.js and worker-wrapper.cjs

**Key Changes**:
- src/services/worker-service.ts: Lock removal, shutdown delays, simplified start logic
- src/hooks/*.ts: Removed AbortSignal.timeout from all HTTP requests
- src/shared/worker-utils.ts: Removed AbortSignal.timeout from health checks
- package.json: Updated worker:* scripts

Resolves startup hangs, reduces assertion errors, and prevents zombie port issues.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-29 16:44:40 -05:00

89 lines
2.8 KiB
TypeScript

/**
* Summary Hook - Stop
*
* Pure HTTP client - sends data to worker, worker handles all database operations
* including privacy checks. This allows the hook to run under any runtime
* (Node.js or Bun) since it has no native module dependencies.
*
* Transcript parsing stays in the hook because only the hook has access to
* the transcript file path.
*/
import { stdin } from 'process';
import { STANDARD_HOOK_RESPONSE } from './hook-response.js';
import { logger } from '../utils/logger.js';
import { ensureWorkerRunning, getWorkerPort } from '../shared/worker-utils.js';
import { HOOK_TIMEOUTS } from '../shared/hook-constants.js';
import { extractLastMessage } from '../shared/transcript-parser.js';
export interface StopInput {
session_id: string;
cwd: string;
transcript_path: string;
}
/**
* Summary Hook Main Logic - Fire-and-forget HTTP client
*/
async function summaryHook(input?: StopInput): Promise<void> {
// Ensure worker is running before any other logic
await ensureWorkerRunning();
if (!input) {
throw new Error('summaryHook requires input');
}
const { session_id } = input;
const port = getWorkerPort();
// Validate required fields before processing
if (!input.transcript_path) {
throw new Error(`Missing transcript_path in Stop hook input for session ${session_id}`);
}
// Extract last user AND assistant messages from transcript
const lastUserMessage = extractLastMessage(input.transcript_path, 'user');
const lastAssistantMessage = extractLastMessage(input.transcript_path, 'assistant', true);
logger.dataIn('HOOK', 'Stop: Requesting summary', {
workerPort: port,
hasLastUserMessage: !!lastUserMessage,
hasLastAssistantMessage: !!lastAssistantMessage
});
// Send to worker - worker handles privacy check and database operations
const response = await fetch(`http://127.0.0.1:${port}/api/sessions/summarize`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
contentSessionId: session_id,
last_user_message: lastUserMessage,
last_assistant_message: lastAssistantMessage
})
// Note: Removed signal to avoid Windows Bun cleanup issue (libuv assertion)
});
if (!response.ok) {
console.log(STANDARD_HOOK_RESPONSE);
throw new Error(`Summary generation failed: ${response.status}`);
}
logger.debug('HOOK', 'Summary request sent successfully');
console.log(STANDARD_HOOK_RESPONSE);
}
// Entry Point
let input = '';
stdin.on('data', (chunk) => input += chunk);
stdin.on('end', async () => {
let parsed: StopInput | undefined;
try {
parsed = input ? JSON.parse(input) : undefined;
} catch (error) {
throw new Error(`Failed to parse hook input: ${error instanceof Error ? error.message : String(error)}`);
}
await summaryHook(parsed);
});