Files
claude-mem/src/hooks/save.ts
T
Alex Newman c27f07023c fix: Remove all session validation to prevent continuation errors after /exit
Root cause: Hooks provide session_id as the source of truth. We were adding
unnecessary validation (checking if sessions exist, checking status, etc.)
which caused 409 conflicts when continuing sessions after /exit.

Changes:
1. worker-service.ts: Removed 409 "Session already exists" check in handleInit
2. SessionStore.ts: Made createSDKSession idempotent using INSERT OR IGNORE
3. new-hook.ts: Simplified to just call createSDKSession - no findActiveSDKSession,
   no reactivateSession logic, no status management
4. save-hook.ts: Removed session validation, use fixed port instead of session.worker_port
5. summary-hook.ts: Removed session validation, use fixed port instead of session.worker_port

Philosophy: Hooks manage lifecycle, we just save data with whatever session_id
they give us. No validation, no status checks, no guessing.

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-21 21:44:58 -04:00

83 lines
2.6 KiB
TypeScript

import { SessionStore } from '../services/sqlite/SessionStore.js';
import { createHookResponse } from './hook-response.js';
import { logger } from '../utils/logger.js';
import { ensureWorkerRunning } from '../shared/worker-utils.js';
export interface PostToolUseInput {
session_id: string;
cwd: string;
tool_name: string;
tool_input: any;
tool_output: any;
[key: string]: any;
}
// Tools to skip (low value or too frequent)
const SKIP_TOOLS = new Set([
'ListMcpResourcesTool'
]);
/**
* Save Hook - PostToolUse
* Sends tool observations to worker via HTTP POST
*/
export async function saveHook(input?: PostToolUseInput): Promise<void> {
if (!input) {
throw new Error('saveHook requires input');
}
const { session_id, tool_name, tool_input, tool_output } = input;
if (SKIP_TOOLS.has(tool_name)) {
console.log(createHookResponse('PostToolUse', true));
return;
}
// Ensure worker is running first (runs cleanup if restarting)
const workerReady = await ensureWorkerRunning();
if (!workerReady) {
throw new Error('Worker service failed to start or become healthy');
}
const db = new SessionStore();
// Get or create session - no validation, just use the session_id from hook
const sessionDbId = db.createSDKSession(session_id, '', ''); // project and prompt not needed for observations
const promptNumber = db.getPromptCounter(sessionDbId);
db.close();
const toolStr = logger.formatTool(tool_name, tool_input);
// Use fixed worker port - no session.worker_port validation needed
const FIXED_PORT = parseInt(process.env.CLAUDE_MEM_WORKER_PORT || '37777', 10);
logger.dataIn('HOOK', `PostToolUse: ${toolStr}`, {
sessionId: sessionDbId,
workerPort: FIXED_PORT
});
const response = await fetch(`http://127.0.0.1:${FIXED_PORT}/sessions/${sessionDbId}/observations`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
tool_name,
tool_input: tool_input !== undefined ? JSON.stringify(tool_input) : '{}',
tool_output: tool_output !== undefined ? JSON.stringify(tool_output) : '{}',
prompt_number: promptNumber
}),
signal: AbortSignal.timeout(2000)
});
if (!response.ok) {
const errorText = await response.text();
logger.failure('HOOK', 'Failed to send observation', {
sessionId: sessionDbId,
status: response.status
}, errorText);
throw new Error(`Failed to send observation to worker: ${response.status} ${errorText}`);
}
logger.debug('HOOK', 'Observation sent successfully', { sessionId: sessionDbId, toolName: tool_name });
console.log(createHookResponse('PostToolUse', true));
}