diff --git a/src/agents/core/session/types.ts b/src/agents/core/session/types.ts index 2d5b62418..a4dbb0497 100644 --- a/src/agents/core/session/types.ts +++ b/src/agents/core/session/types.ts @@ -17,7 +17,7 @@ export interface BaseNormalizedMessage { /** * Correlation status */ -export type CorrelationStatus = 'pending' | 'matched' | 'failed'; +export type CorrelationStatus = 'pending' | 'matched' | 'failed' | 'file_not_found'; /** * Correlation result diff --git a/src/cli/commands/hook.ts b/src/cli/commands/hook.ts index 5e91b9fed..e0825c8ba 100644 --- a/src/cli/commands/hook.ts +++ b/src/cli/commands/hook.ts @@ -807,9 +807,22 @@ async function createSessionRecord(event: SessionStartEvent, sessionId: string, existing.status = 'active'; if (gitBranch) existing.gitBranch = gitBranch; if (remoteRepository) existing.repository = remoteRepository; + + // Check if the transcript file actually exists on disk before marking correlation as matched + const { existsSync } = await import('node:fs'); + const transcriptFileExists = event.transcript_path ? existsSync(event.transcript_path) : false; + const correlationStatus = transcriptFileExists ? 'matched' as const : 'file_not_found' as const; + + if (!transcriptFileExists && event.transcript_path) { + logger.warn( + `[hook:SessionStart] Transcript path reported but file does not exist: ${event.transcript_path}. ` + + `Correlation status set to 'file_not_found' to prevent metrics processing failures.` + ); + } + existing.correlation = { ...existing.correlation, - status: 'matched', + status: correlationStatus, ...(event.session_id && { agentSessionId: event.session_id }), ...(event.transcript_path && { agentSessionFile: event.transcript_path }), }; @@ -837,12 +850,25 @@ async function createSessionRecord(event: SessionStartEvent, sessionId: string, const { appendTranscriptMarker, appendAuditEvent, isExternalOrigin } = await import( '../../agents/core/session/session-origin-audit.js' ); + const { existsSync } = await import('node:fs'); const origin = getConfigValue(SESSION_ORIGIN_ENV_KEY, config) === SESSION_ORIGIN.EXTERNAL_RESUME ? SESSION_ORIGIN.EXTERNAL_RESUME : undefined; - // Create session record with correlation already matched + // Check if the transcript file actually exists on disk before marking correlation as matched + // This prevents metrics processing from failing on non-existent files (e.g., interactive PTY sessions) + const transcriptFileExists = event.transcript_path ? existsSync(event.transcript_path) : false; + const correlationStatus = transcriptFileExists ? 'matched' as const : 'file_not_found' as const; + + if (!transcriptFileExists && event.transcript_path) { + logger.warn( + `[hook:SessionStart] Transcript path reported but file does not exist: ${event.transcript_path}. ` + + `Correlation status set to 'file_not_found' to prevent metrics processing failures.` + ); + } + + // Create session record with correlation status based on file existence const session = { sessionId, agentName, @@ -856,7 +882,7 @@ async function createSessionRecord(event: SessionStartEvent, sessionId: string, activeDurationMs: 0, // Initialize active duration tracking ...(origin && { origin }), correlation: { - status: 'matched' as const, + status: correlationStatus, agentSessionId: event.session_id, agentSessionFile: event.transcript_path, retryCount: 0 diff --git a/src/providers/plugins/sso/session/SessionSyncer.ts b/src/providers/plugins/sso/session/SessionSyncer.ts index cce531fce..c1aed6380 100644 --- a/src/providers/plugins/sso/session/SessionSyncer.ts +++ b/src/providers/plugins/sso/session/SessionSyncer.ts @@ -112,6 +112,19 @@ export class SessionSyncer { } if (!sessionMetadata.correlation || sessionMetadata.correlation.status !== 'matched') { + // Handle 'file_not_found' status specially - this is expected for some interactive sessions + if (sessionMetadata.correlation?.status === 'file_not_found') { + logger.info( + `[SessionSyncer] Skipping session ${sessionId} - transcript file not found (status: file_not_found). ` + + `This is expected for some interactive PTY sessions where the transcript is not persisted.` + ); + return { + success: true, + message: 'Session skipped - transcript file not found (expected for some interactive sessions)', + processorResults: {}, + failedProcessors: [] + }; + } return { success: false, message: `Session not correlated (status: ${sessionMetadata.correlation?.status || 'unknown'})`,