Files
claude-mem/src/cli/handlers/session-complete.ts
T
Rod Boev 418e38ee46 fix: hook resilience and worker lifecycle improvements (#957, #923, #984, #987, #1042)
Reduce timeouts to eliminate 10-30s startup delay when worker is dead
(common on WSL2 after hibernate). Add stale PID detection, graceful
error handling across all handlers, and error classification that
distinguishes worker unavailability from handler bugs.

- HEALTH_CHECK 30s→3s, new POST_SPAWN_WAIT (5s), PORT_IN_USE_WAIT (3s)
- isProcessAlive() with EPERM handling, cleanStalePidFile()
- getPluginVersion() try-catch for shutdown race (#1042)
- isWorkerUnavailableError: transport+5xx+429→exit 0, 4xx→exit 2
- No-op handler for unknown event types (#984)
- Wrap all handler fetch calls in try-catch for graceful degradation
- CLAUDE_MEM_HEALTH_TIMEOUT_MS env var override with validation
2026-02-10 15:34:35 -05:00

67 lines
2.2 KiB
TypeScript

/**
* Session Complete Handler - Stop (Phase 2)
*
* Completes the session after summarize has been queued.
* This removes the session from the active sessions map, allowing
* the orphan reaper to clean up any remaining subprocess.
*
* Fixes Issue #842: Orphan reaper starts but never reaps because
* sessions stay in the active sessions map forever.
*/
import type { EventHandler, NormalizedHookInput, HookResult } from '../types.js';
import { ensureWorkerRunning, getWorkerPort } from '../../shared/worker-utils.js';
import { logger } from '../../utils/logger.js';
export const sessionCompleteHandler: EventHandler = {
async execute(input: NormalizedHookInput): Promise<HookResult> {
// Ensure worker is running
const workerReady = await ensureWorkerRunning();
if (!workerReady) {
// Worker not available — skip session completion gracefully
return { continue: true, suppressOutput: true };
}
const { sessionId } = input;
const port = getWorkerPort();
if (!sessionId) {
logger.warn('HOOK', 'session-complete: Missing sessionId, skipping');
return { continue: true, suppressOutput: true };
}
logger.info('HOOK', '→ session-complete: Removing session from active map', {
workerPort: port,
contentSessionId: sessionId
});
try {
// Call the session complete endpoint by contentSessionId
const response = await fetch(`http://127.0.0.1:${port}/api/sessions/complete`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
contentSessionId: sessionId
})
});
if (!response.ok) {
const text = await response.text();
logger.warn('HOOK', 'session-complete: Failed to complete session', {
status: response.status,
body: text
});
} else {
logger.info('HOOK', 'Session completed successfully', { contentSessionId: sessionId });
}
} catch (error) {
// Log but don't fail - session may already be gone
logger.warn('HOOK', 'session-complete: Error completing session', {
error: (error as Error).message
});
}
return { continue: true, suppressOutput: true };
}
};