f849a69506
All hooks now call ensureWorkerRunning() BEFORE querying the database. This ensures the worker's orphaned session cleanup runs before hooks check for active sessions, preventing 404 errors when hooks try to use sessions that don't exist in worker memory after a restart. Hook order now: 1. ensureWorkerRunning() - starts worker, runs cleanup 2. Query DB - cleanup already marked orphaned sessions as failed 3. Use session - only valid sessions are processed Fixed in: - new-hook: Line 26, before DB queries - save-hook: Line 37, before DB queries - summary-hook: Line 24, before DB queries - cleanup-hook: Line 50, before DB queries This prevents the race condition where hooks would read session status before cleanup ran, then get 404 from worker after cleanup marked sessions failed.
89 lines
2.9 KiB
TypeScript
89 lines
2.9 KiB
TypeScript
import path from 'path';
|
|
import { SessionStore } from '../services/sqlite/SessionStore.js';
|
|
import { createHookResponse } from './hook-response.js';
|
|
import { ensureWorkerRunning, getWorkerPort } from '../shared/worker-utils.js';
|
|
|
|
export interface UserPromptSubmitInput {
|
|
session_id: string;
|
|
cwd: string;
|
|
prompt: string;
|
|
[key: string]: any;
|
|
}
|
|
|
|
/**
|
|
* New Hook - UserPromptSubmit
|
|
* Initializes SDK memory session via HTTP POST to worker service
|
|
*/
|
|
export async function newHook(input?: UserPromptSubmitInput): Promise<void> {
|
|
if (!input) {
|
|
throw new Error('newHook requires input');
|
|
}
|
|
|
|
const { session_id, cwd, prompt } = input;
|
|
const project = path.basename(cwd);
|
|
|
|
// 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();
|
|
|
|
try {
|
|
|
|
// Check for any existing session (active, failed, or completed)
|
|
let existing = db.findActiveSDKSession(session_id);
|
|
let sessionDbId: number;
|
|
let isNewSession = false;
|
|
|
|
if (existing) {
|
|
// Session already active, increment prompt counter
|
|
sessionDbId = existing.id;
|
|
const promptNumber = db.incrementPromptCounter(sessionDbId);
|
|
console.error(`[new-hook] Continuing session ${sessionDbId}, prompt #${promptNumber}`);
|
|
} else {
|
|
// Check for inactive sessions we can reuse
|
|
const inactive = db.findAnySDKSession(session_id);
|
|
|
|
if (inactive) {
|
|
// Reactivate the existing session
|
|
sessionDbId = inactive.id;
|
|
db.reactivateSession(sessionDbId, prompt);
|
|
const promptNumber = db.incrementPromptCounter(sessionDbId);
|
|
isNewSession = true;
|
|
console.error(`[new-hook] Reactivated session ${sessionDbId}, prompt #${promptNumber}`);
|
|
} else {
|
|
// Create new session
|
|
sessionDbId = db.createSDKSession(session_id, project, prompt);
|
|
const promptNumber = db.incrementPromptCounter(sessionDbId);
|
|
isNewSession = true;
|
|
console.error(`[new-hook] Created new session ${sessionDbId}, prompt #${promptNumber}`);
|
|
}
|
|
}
|
|
|
|
// Get fixed port
|
|
const port = getWorkerPort();
|
|
|
|
// Only initialize worker on new sessions
|
|
if (isNewSession) {
|
|
// Initialize session via HTTP
|
|
const response = await fetch(`http://127.0.0.1:${port}/sessions/${sessionDbId}/init`, {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify({ project, userPrompt: prompt }),
|
|
signal: AbortSignal.timeout(5000)
|
|
});
|
|
|
|
if (!response.ok) {
|
|
const errorText = await response.text();
|
|
throw new Error(`Failed to initialize session: ${response.status} ${errorText}`);
|
|
}
|
|
}
|
|
|
|
console.log(createHookResponse('UserPromptSubmit', true));
|
|
} finally {
|
|
db.close();
|
|
}
|
|
}
|