/**
* Session Init Handler - UserPromptSubmit
*
* Extracted from new-hook.ts - initializes session and starts SDK agent.
*/
import type { EventHandler, NormalizedHookInput, HookResult } from '../types.js';
import { ensureWorkerRunning, getWorkerPort } from '../../common/engine-utils.js';
import { getProjectName } from '../../utils/project-name.js';
import { logger } from '../../utils/logger.js';
export const sessionInitHandler: EventHandler = {
async execute(input: NormalizedHookInput): Promise<HookResult> {
// Ensure worker is running before any other logic
await ensureWorkerRunning();
const { sessionId, cwd, prompt } = input;
if (!prompt) {
throw new Error('sessionInitHandler requires prompt');
}
const project = getProjectName(cwd);
const port = getWorkerPort();
logger.debug('HOOK', 'session-init: Calling /api/sessions/init', { contentSessionId: sessionId, project });
// Initialize session via HTTP - handles DB operations and privacy checks
const initResponse = await fetch(`http://127.0.0.1:${port}/api/sessions/init`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
contentSessionId: sessionId,
project,
prompt
})
// Note: Removed signal to avoid Windows Bun cleanup issue (libuv assertion)
});
if (!initResponse.ok) {
throw new Error(`Session initialization failed: ${initResponse.status}`);
}
const initResult = await initResponse.json() as {
sessionDbId: number;
promptNumber: number;
skipped?: boolean;
reason?: string;
};
const sessionDbId = initResult.sessionDbId;
const promptNumber = initResult.promptNumber;
logger.debug('HOOK', 'session-init: Received from /api/sessions/init', { sessionDbId, promptNumber, skipped: initResult.skipped });
// Debug-level alignment log for detailed tracing
logger.debug('HOOK', `[ALIGNMENT] Hook Entry | contentSessionId=${sessionId} | prompt#=${promptNumber} | sessionDbId=${sessionDbId}`);
// Check if prompt was entirely private (worker performs privacy check)
if (initResult.skipped && initResult.reason === 'private') {
logger.info('HOOK', `INIT_COMPLETE | sessionDbId=${sessionDbId} | promptNumber=${promptNumber} | skipped=true | reason=private`, {
sessionId: sessionDbId
});
return { continue: true, suppressOutput: true };
}
// Initialize SDK agent for Claude Code
if (sessionDbId) {
// Strip leading slash from commands for memory agent
// /review 101 -> review 101 (more semantic for observations)
const cleanedPrompt = prompt.startsWith('/') ? prompt.substring(1) : prompt;
logger.debug('HOOK', 'session-init: Calling /sessions/{sessionDbId}/init', { sessionDbId, promptNumber });
// Initialize SDK agent session via HTTP (starts the agent!)
const response = await fetch(`http://127.0.0.1:${port}/sessions/${sessionDbId}/init`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ userPrompt: cleanedPrompt, promptNumber })
// Note: Removed signal to avoid Windows Bun cleanup issue (libuv assertion)
});
if (!response.ok) {
throw new Error(`SDK agent start failed: ${response.status}`);
}
}
logger.info('HOOK', `INIT_COMPLETE | sessionDbId=${sessionDbId} | promptNumber=${promptNumber} | project=${project}`, {
sessionId: sessionDbId
});
return { continue: true, suppressOutput: true };
}
};