@@ -71,6 +71,30 @@ function countScarsApplied(scarsApplied: string | string[] | undefined | null):
7171 return normalizeScarsApplied ( scarsApplied ) . length ;
7272}
7373
74+ /**
75+ * Find transcript file path: explicit param or auto-detect from Claude Code projects dir
76+ */
77+ function findTranscriptPath ( explicitPath ?: string ) : string | null {
78+ if ( explicitPath ) {
79+ if ( fs . existsSync ( explicitPath ) ) {
80+ console . error ( `[session_close] Using explicit transcript path: ${ explicitPath } ` ) ;
81+ return explicitPath ;
82+ }
83+ console . warn ( `[session_close] Explicit transcript path does not exist: ${ explicitPath } ` ) ;
84+ }
85+ const homeDir = os . homedir ( ) ;
86+ const projectsDir = path . join ( homeDir , ".claude" , "projects" ) ;
87+ const cwd = process . cwd ( ) ;
88+ const projectDirName = path . basename ( cwd ) ;
89+ const found = findMostRecentTranscript ( projectsDir , projectDirName , cwd ) ;
90+ if ( found ) {
91+ console . error ( `[session_close] Auto-detected transcript: ${ found } ` ) ;
92+ } else {
93+ console . error ( `[session_close] No transcript file found in ${ projectsDir } ` ) ;
94+ }
95+ return found ;
96+ }
97+
7498/**
7599 * Find the most recently modified transcript file in Claude Code projects directory
76100 * Search by recency, not by filename matching (supports post-compaction)
@@ -1198,15 +1222,43 @@ export async function sessionClose(
11981222 }
11991223
12001224 // Capture transcript if enabled (default true for CLI/DAC)
1225+ // Split into two phases: sync ID extraction (fast) + async upload (fire-and-forget)
12011226 let transcriptStatus : TranscriptStatus | undefined ;
12021227 const shouldCaptureTranscript = params . capture_transcript !== false &&
12031228 ( agentIdentity === "cli" || agentIdentity === "desktop" ) ;
12041229
12051230 if ( shouldCaptureTranscript ) {
1206- const transcriptResult = await captureSessionTranscript ( sessionId , params , existingSession , isRetroactive ) ;
1207- transcriptStatus = transcriptResult . status ;
1208- if ( transcriptResult . claudeSessionId ) {
1209- sessionData . claude_code_session_id = transcriptResult . claudeSessionId ;
1231+ // Phase 1: Find transcript and extract Claude session ID (sync, ~10ms)
1232+ const transcriptFilePath = findTranscriptPath ( params . transcript_path ) ;
1233+ if ( transcriptFilePath ) {
1234+ const transcriptContent = fs . readFileSync ( transcriptFilePath , "utf-8" ) ;
1235+ const claudeSessionId = extractClaudeSessionId ( transcriptContent , transcriptFilePath ) || undefined ;
1236+ if ( claudeSessionId ) {
1237+ sessionData . claude_code_session_id = claudeSessionId ;
1238+ console . error ( `[session_close] Extracted Claude session ID: ${ claudeSessionId } ` ) ;
1239+ }
1240+
1241+ // Phase 2: Upload transcript (fire-and-forget — was blocking ~500-5000ms)
1242+ const transcriptProject = isRetroactive ? "default" : ( existingSession ?. project as string | undefined ) ;
1243+ getEffectTracker ( ) . track ( "transcript" , "session_close" , async ( ) => {
1244+ const saveResult = await saveTranscript ( {
1245+ session_id : sessionId ,
1246+ transcript : transcriptContent ,
1247+ format : "json" ,
1248+ project : transcriptProject ,
1249+ } ) ;
1250+ if ( saveResult . success && saveResult . transcript_path ) {
1251+ console . error ( `[session_close] Transcript saved: ${ saveResult . transcript_path } (${ saveResult . size_kb } KB)` ) ;
1252+ // Process transcript for semantic search (chained fire-and-forget)
1253+ processTranscript ( sessionId , transcriptContent , transcriptProject )
1254+ . then ( result => {
1255+ if ( result . success ) {
1256+ console . error ( `[session_close] Transcript processed: ${ result . chunksCreated } chunks` ) ;
1257+ }
1258+ } )
1259+ . catch ( ( err ) => console . error ( "[session_close] Transcript processing failed:" , err instanceof Error ? err . message : err ) ) ;
1260+ }
1261+ } ) ;
12101262 }
12111263 }
12121264
0 commit comments