fix: worker startup crash and missing observation columns
Two bugs fixed: 1. SessionCompletionHandler called dbManager.getSessionStore() during WorkerService construction, before DB initialization. Changed to accept DatabaseManager and defer the call to runtime. 2. migration009 (generated_by_model, relevance_count columns) only ran via the deprecated MigrationRunner path, never through SessionStore's migration chain. Added addObservationModelColumns() to SessionStore constructor. Checks column existence directly since schema_versions may have been marked applied without the ALTER TABLE succeeding. Also removed duplicate transcriptWatcher declaration and shutdown block (merge artifact). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
+370
-241
File diff suppressed because one or more lines are too long
@@ -26,7 +26,6 @@ function resolveCreateSessionArgs(
|
|||||||
platformSource: platformSource ? normalizePlatformSource(platformSource) : undefined
|
platformSource: platformSource ? normalizePlatformSource(platformSource) : undefined
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
>>>>>>> pr-1472
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Session data store for SDK sessions, observations, and summaries
|
* Session data store for SDK sessions, observations, and summaries
|
||||||
@@ -65,6 +64,7 @@ export class SessionStore {
|
|||||||
this.addObservationContentHashColumn();
|
this.addObservationContentHashColumn();
|
||||||
this.addSessionCustomTitleColumn();
|
this.addSessionCustomTitleColumn();
|
||||||
this.addSessionPlatformSourceColumn();
|
this.addSessionPlatformSourceColumn();
|
||||||
|
this.addObservationModelColumns();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -920,6 +920,30 @@ export class SessionStore {
|
|||||||
this.db.prepare('INSERT OR IGNORE INTO schema_versions (version, applied_at) VALUES (?, ?)').run(24, new Date().toISOString());
|
this.db.prepare('INSERT OR IGNORE INTO schema_versions (version, applied_at) VALUES (?, ?)').run(24, new Date().toISOString());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add generated_by_model and relevance_count columns to observations (migration 26)
|
||||||
|
*
|
||||||
|
* Note: Cannot trust schema_versions alone — the old MigrationRunner may have
|
||||||
|
* recorded version 26 without the ALTER TABLE actually succeeding. Always
|
||||||
|
* check column existence directly.
|
||||||
|
*/
|
||||||
|
private addObservationModelColumns(): void {
|
||||||
|
const columns = this.db.query('PRAGMA table_info(observations)').all() as TableColumnInfo[];
|
||||||
|
const hasGeneratedByModel = columns.some(col => col.name === 'generated_by_model');
|
||||||
|
const hasRelevanceCount = columns.some(col => col.name === 'relevance_count');
|
||||||
|
|
||||||
|
if (hasGeneratedByModel && hasRelevanceCount) return;
|
||||||
|
|
||||||
|
if (!hasGeneratedByModel) {
|
||||||
|
this.db.run('ALTER TABLE observations ADD COLUMN generated_by_model TEXT');
|
||||||
|
}
|
||||||
|
if (!hasRelevanceCount) {
|
||||||
|
this.db.run('ALTER TABLE observations ADD COLUMN relevance_count INTEGER DEFAULT 0');
|
||||||
|
}
|
||||||
|
|
||||||
|
this.db.prepare('INSERT OR IGNORE INTO schema_versions (version, applied_at) VALUES (?, ?)').run(26, new Date().toISOString());
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Update the memory session ID for a session
|
* Update the memory session ID for a session
|
||||||
* Called by SDKAgent when it captures the session ID from the first SDK message
|
* Called by SDKAgent when it captures the session ID from the first SDK message
|
||||||
|
|||||||
@@ -199,9 +199,6 @@ export class WorkerService {
|
|||||||
// Stale session reaper interval (Issue #1168)
|
// Stale session reaper interval (Issue #1168)
|
||||||
private staleSessionReaperInterval: ReturnType<typeof setInterval> | null = null;
|
private staleSessionReaperInterval: ReturnType<typeof setInterval> | null = null;
|
||||||
|
|
||||||
// Transcript watcher for external CLI sessions (e.g. Codex, Gemini)
|
|
||||||
private transcriptWatcher: TranscriptWatcher | null = null;
|
|
||||||
|
|
||||||
// AI interaction tracking for health endpoint
|
// AI interaction tracking for health endpoint
|
||||||
private lastAiInteraction: {
|
private lastAiInteraction: {
|
||||||
timestamp: number;
|
timestamp: number;
|
||||||
@@ -992,13 +989,6 @@ export class WorkerService {
|
|||||||
this.staleSessionReaperInterval = null;
|
this.staleSessionReaperInterval = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Stop transcript watcher
|
|
||||||
if (this.transcriptWatcher) {
|
|
||||||
this.transcriptWatcher.stop();
|
|
||||||
this.transcriptWatcher = null;
|
|
||||||
logger.info('SYSTEM', 'Transcript watcher stopped');
|
|
||||||
}
|
|
||||||
|
|
||||||
await performGracefulShutdown({
|
await performGracefulShutdown({
|
||||||
server: this.server.getHttpServer(),
|
server: this.server.getHttpServer(),
|
||||||
sessionManager: this.sessionManager,
|
sessionManager: this.sessionManager,
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ export class SessionRoutes extends BaseRouteHandler {
|
|||||||
this.completionHandler = new SessionCompletionHandler(
|
this.completionHandler = new SessionCompletionHandler(
|
||||||
sessionManager,
|
sessionManager,
|
||||||
eventBroadcaster,
|
eventBroadcaster,
|
||||||
dbManager.getSessionStore()
|
dbManager
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -11,14 +11,14 @@
|
|||||||
|
|
||||||
import { SessionManager } from '../SessionManager.js';
|
import { SessionManager } from '../SessionManager.js';
|
||||||
import { SessionEventBroadcaster } from '../events/SessionEventBroadcaster.js';
|
import { SessionEventBroadcaster } from '../events/SessionEventBroadcaster.js';
|
||||||
import { SessionStore } from '../../sqlite/SessionStore.js';
|
import { DatabaseManager } from '../DatabaseManager.js';
|
||||||
import { logger } from '../../../utils/logger.js';
|
import { logger } from '../../../utils/logger.js';
|
||||||
|
|
||||||
export class SessionCompletionHandler {
|
export class SessionCompletionHandler {
|
||||||
constructor(
|
constructor(
|
||||||
private sessionManager: SessionManager,
|
private sessionManager: SessionManager,
|
||||||
private eventBroadcaster: SessionEventBroadcaster,
|
private eventBroadcaster: SessionEventBroadcaster,
|
||||||
private sessionStore: SessionStore
|
private dbManager: DatabaseManager
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -27,7 +27,7 @@ export class SessionCompletionHandler {
|
|||||||
*/
|
*/
|
||||||
async completeByDbId(sessionDbId: number): Promise<void> {
|
async completeByDbId(sessionDbId: number): Promise<void> {
|
||||||
// Persist completion to database before in-memory cleanup (fix for #1532)
|
// Persist completion to database before in-memory cleanup (fix for #1532)
|
||||||
this.sessionStore.markSessionCompleted(sessionDbId);
|
this.dbManager.getSessionStore().markSessionCompleted(sessionDbId);
|
||||||
|
|
||||||
// Delete from session manager (aborts SDK agent via SIGTERM)
|
// Delete from session manager (aborts SDK agent via SIGTERM)
|
||||||
await this.sessionManager.deleteSession(sessionDbId);
|
await this.sessionManager.deleteSession(sessionDbId);
|
||||||
|
|||||||
Reference in New Issue
Block a user