MAESTRO: fix(db): prevent FK constraint failures on worker restart
Cherry-picked source changes from PR #889 by @Et9797. Fixes #846. Key changes: - Add ensureMemorySessionIdRegistered() guard in SessionStore.ts - Add ON UPDATE CASCADE migration (schema v21) for observations and session_summaries FK constraints - Change message queue from claim-and-delete to claim-confirm pattern (PendingMessageStore.ts) - Add spawn deduplication and unrecoverable error detection in SessionRoutes.ts and worker-service.ts - Add forceInit flag to SDKAgent for stale session recovery Build artifacts skipped (pre-existing dompurify dep issue). Path fixes (HealthMonitor.ts, worker-utils.ts) already merged via PR #634. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -14,12 +14,8 @@ import { logger } from '../../../utils/logger.js';
|
||||
* - Prompt #2+: session_id exists -> INSERT ignored, fetch existing ID
|
||||
* - Result: Same database ID returned for all prompts in conversation
|
||||
*
|
||||
* WHY THIS MATTERS:
|
||||
* - NO "does session exist?" checks needed anywhere
|
||||
* - NO risk of creating duplicate sessions
|
||||
* - ALL hooks automatically connected via session_id
|
||||
* - SAVE hook observations go to correct session (same session_id)
|
||||
* - SDKAgent continuation prompt has correct context (same session_id)
|
||||
* Pure get-or-create: never modifies memory_session_id.
|
||||
* Multi-terminal isolation is handled by ON UPDATE CASCADE at the schema level.
|
||||
*/
|
||||
export function createSDKSession(
|
||||
db: Database,
|
||||
@@ -30,25 +26,33 @@ export function createSDKSession(
|
||||
const now = new Date();
|
||||
const nowEpoch = now.getTime();
|
||||
|
||||
// INSERT OR IGNORE to create session, then backfill project if it was created empty
|
||||
// Check for existing session
|
||||
const existing = db.prepare(`
|
||||
SELECT id FROM sdk_sessions WHERE content_session_id = ?
|
||||
`).get(contentSessionId) as { id: number } | undefined;
|
||||
|
||||
if (existing) {
|
||||
// Backfill project if session was created by another hook with empty project
|
||||
if (project) {
|
||||
db.prepare(`
|
||||
UPDATE sdk_sessions SET project = ?
|
||||
WHERE content_session_id = ? AND (project IS NULL OR project = '')
|
||||
`).run(project, contentSessionId);
|
||||
}
|
||||
return existing.id;
|
||||
}
|
||||
|
||||
// New session - insert fresh row
|
||||
// NOTE: memory_session_id starts as NULL. It is captured by SDKAgent from the first SDK
|
||||
// response and stored via updateMemorySessionId(). CRITICAL: memory_session_id must NEVER
|
||||
// equal contentSessionId - that would inject memory messages into the user's transcript!
|
||||
// response and stored via ensureMemorySessionIdRegistered(). CRITICAL: memory_session_id
|
||||
// must NEVER equal contentSessionId - that would inject memory messages into the user's transcript!
|
||||
db.prepare(`
|
||||
INSERT OR IGNORE INTO sdk_sessions
|
||||
INSERT INTO sdk_sessions
|
||||
(content_session_id, memory_session_id, project, user_prompt, started_at, started_at_epoch, status)
|
||||
VALUES (?, NULL, ?, ?, ?, ?, 'active')
|
||||
`).run(contentSessionId, project, userPrompt, now.toISOString(), nowEpoch);
|
||||
|
||||
// Backfill project if session was created by another hook with empty project
|
||||
if (project) {
|
||||
db.prepare(`
|
||||
UPDATE sdk_sessions SET project = ?
|
||||
WHERE content_session_id = ? AND (project IS NULL OR project = '')
|
||||
`).run(project, contentSessionId);
|
||||
}
|
||||
|
||||
// Return existing or new ID
|
||||
// Return new ID
|
||||
const row = db.prepare('SELECT id FROM sdk_sessions WHERE content_session_id = ?')
|
||||
.get(contentSessionId) as { id: number };
|
||||
return row.id;
|
||||
@@ -57,11 +61,12 @@ export function createSDKSession(
|
||||
/**
|
||||
* Update the memory session ID for a session
|
||||
* Called by SDKAgent when it captures the session ID from the first SDK message
|
||||
* Also used to RESET to null on stale resume failures (worker-service.ts)
|
||||
*/
|
||||
export function updateMemorySessionId(
|
||||
db: Database,
|
||||
sessionDbId: number,
|
||||
memorySessionId: string
|
||||
memorySessionId: string | null
|
||||
): void {
|
||||
db.prepare(`
|
||||
UPDATE sdk_sessions
|
||||
|
||||
Reference in New Issue
Block a user