fix: persist session completion to database in completeByDbId (#1532)

completeByDbId only cleaned up in-memory state, leaving sdk_sessions rows
with status='active' and completed_at=NULL indefinitely. Ghost sessions
accumulated and exhausted the agent pool, causing 60s timeout errors.

- Add SessionStore.markSessionCompleted() to set status/completed_at/completed_at_epoch
- Call it at the start of completeByDbId before in-memory cleanup
- Inject SessionStore into SessionCompletionHandler via constructor
- Add 4 tests covering status, timestamps, isolation, and non-existent IDs

Closes #1532

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Ousama Ben Younes
2026-04-01 06:02:14 +00:00
parent 3651a34e96
commit 12501412b9
4 changed files with 84 additions and 2 deletions
@@ -11,12 +11,14 @@
import { SessionManager } from '../SessionManager.js';
import { SessionEventBroadcaster } from '../events/SessionEventBroadcaster.js';
import { SessionStore } from '../../sqlite/SessionStore.js';
import { logger } from '../../../utils/logger.js';
export class SessionCompletionHandler {
constructor(
private sessionManager: SessionManager,
private eventBroadcaster: SessionEventBroadcaster
private eventBroadcaster: SessionEventBroadcaster,
private sessionStore: SessionStore
) {}
/**
@@ -24,6 +26,9 @@ export class SessionCompletionHandler {
* Used by DELETE /api/sessions/:id and POST /api/sessions/:id/complete
*/
async completeByDbId(sessionDbId: number): Promise<void> {
// Persist completion to database before in-memory cleanup (fix for #1532)
this.sessionStore.markSessionCompleted(sessionDbId);
// Delete from session manager (aborts SDK agent)
await this.sessionManager.deleteSession(sessionDbId);