@@ -11,6 +11,7 @@ import { uuidv7 } from 'uuidv7';
1111import { loadSettings } from './setup.js' ;
1212import { appendToLog , deepEqual } from './utils.js' ;
1313import { parseSessionFile } from './parser.js' ;
14+ import { TraceRegistry } from './traceRegistry.js' ;
1415
1516// ─────────────────────────────────────────────────────────────────────────────
1617// Types
@@ -77,6 +78,12 @@ interface SessionState {
7778 subagentByAgentId : Map < string , SubagentTracker > ;
7879}
7980
81+ interface TraceResolution {
82+ traceId : string ;
83+ sessionCallId ?: string ;
84+ source : 'new' | 'registry-session' | 'registry-transcript' ;
85+ }
86+
8087// ─────────────────────────────────────────────────────────────────────────────
8188// GlobalDaemon
8289// ─────────────────────────────────────────────────────────────────────────────
@@ -91,6 +98,7 @@ export class GlobalDaemon {
9198 private sessions = new Map < string , SessionState > ( ) ;
9299 private sessionQueues = new Map < string , Promise < void > > ( ) ;
93100 private weaveClient : WeaveClient | null = null ;
101+ private traceRegistry = new TraceRegistry ( ) ;
94102
95103 constructor (
96104 private readonly socketPath : string ,
@@ -100,6 +108,9 @@ export class GlobalDaemon {
100108 ) { }
101109
102110 async start ( ) : Promise < void > {
111+ const loadedRegistryEntries = this . traceRegistry . load ( ) ;
112+ this . log ( 'DEBUG' , `Loaded trace registry: ${ loadedRegistryEntries } entries` ) ;
113+
103114 // Initialize Weave client if a project is configured
104115 if ( this . weaveProject ) {
105116 try {
@@ -248,21 +259,39 @@ export class GlobalDaemon {
248259 return ;
249260 }
250261
262+ const source = ( payload [ 'source' ] as string | undefined ) ?? 'unknown' ;
263+ const model = ( payload [ 'model' ] as string | undefined ) ?? 'unknown' ;
264+ const cwd = ( payload [ 'cwd' ] as string | undefined ) ?? '' ;
265+ const traceResolution = await this . resolveTraceForSession ( sessionId , transcriptPath , source ) ;
266+ const existingTurnCount = parseSessionFile ( transcriptPath ) ?. turns . length ?? 0 ;
267+
251268 this . sessions . set ( sessionId , {
252269 sessionId,
253270 transcriptPath,
254- cwd : ( payload [ 'cwd' ] as string | undefined ) ?? '' ,
255- traceId : uuidv7 ( ) ,
256- turnNumber : 0 ,
271+ cwd,
272+ traceId : traceResolution . traceId ,
273+ sessionCallId : traceResolution . sessionCallId ,
274+ turnNumber : existingTurnCount ,
257275 totalToolCalls : 0 ,
258276 turnToolCalls : 0 ,
259277 toolCounts : { } ,
260278 pendingToolCalls : new Map ( ) ,
261279 subagentTrackers : new Map ( ) ,
262280 subagentByAgentId : new Map ( ) ,
263281 } ) ;
282+ this . upsertTraceRegistry (
283+ sessionId ,
284+ traceResolution . traceId ,
285+ transcriptPath ,
286+ source ,
287+ traceResolution . sessionCallId ,
288+ ) ;
264289
265290 this . log ( 'INFO' , `Session created: ${ sessionId } ` ) ;
291+ this . log (
292+ 'DEBUG' ,
293+ `SessionStart details: session=${ sessionId } source=${ source } model=${ model } cwd=${ cwd || '(empty)' } transcript_path=${ transcriptPath } transcript_file=${ path . basename ( transcriptPath ) } trace_id=${ this . sessions . get ( sessionId ) ?. traceId } trace_resolution=${ traceResolution . source } existing_turns=${ existingTurnCount } active_sessions=${ this . sessions . size } ` ,
294+ ) ;
266295 }
267296
268297 private async handleUserPromptSubmit ( sessionId : string , payload : HookPayload ) : Promise < void > {
@@ -273,6 +302,10 @@ export class GlobalDaemon {
273302 }
274303
275304 const prompt = ( payload [ 'prompt' ] as string | undefined ) ?? '' ;
305+ this . log (
306+ 'DEBUG' ,
307+ `UserPromptSubmit: session=${ sessionId } trace_id=${ session . traceId } existing_session_call=${ session . sessionCallId ?? 'none' } current_turn_call=${ session . currentTurnCallId ?? 'none' } turn_number=${ session . turnNumber } prompt=${ GlobalDaemon . promptSnippet ( prompt , 120 ) } ` ,
308+ ) ;
276309
277310 // Create the top-level session call on the first turn
278311 if ( ! session . sessionCallId && this . weaveClient ) {
@@ -294,6 +327,13 @@ export class GlobalDaemon {
294327 } ,
295328 } ) ;
296329 this . log ( 'INFO' , `Created session call: ${ callId } ` ) ;
330+ this . upsertTraceRegistry (
331+ session . sessionId ,
332+ session . traceId ,
333+ session . transcriptPath ,
334+ 'session_call_created' ,
335+ session . sessionCallId ,
336+ ) ;
297337 }
298338
299339 // Create a turn call for every prompt
@@ -575,6 +615,11 @@ export class GlobalDaemon {
575615 const currentTurn = parsedSession ?. turns [ parsedSession . turns . length - 1 ] ;
576616 const usage = currentTurn ?. totalUsage ( ) ;
577617 const model = currentTurn ?. primaryModel ( ) ;
618+ const transcriptTurns = parsedSession ?. turns . length ?? 0 ;
619+ this . log (
620+ 'DEBUG' ,
621+ `Stop: session=${ sessionId } trace_id=${ session . traceId } turn_call=${ session . currentTurnCallId } transcript_path=${ session . transcriptPath } transcript_turns=${ transcriptTurns } parsed_model=${ model ?? 'unknown' } last_assistant_message_present=${ Boolean ( payload [ 'last_assistant_message' ] ) } ` ,
622+ ) ;
578623
579624 // Weave expects summary.usage keyed by model name: { "model-name": { input_tokens, output_tokens } }
580625 const usageSummary = usage && model ? { [ model ] : usage } : { } ;
@@ -586,6 +631,7 @@ export class GlobalDaemon {
586631 output : { assistant_message : ( payload [ 'last_assistant_message' ] as string | undefined ) ?? '' } ,
587632 summary : { usage : usageSummary , tool_count : session . turnToolCalls } ,
588633 } ) ;
634+ session . currentTurnCallId = undefined ;
589635
590636 this . log ( 'INFO' , `Finished turn ${ session . turnNumber } (${ session . turnToolCalls } tools)` ) ;
591637 }
@@ -594,6 +640,18 @@ export class GlobalDaemon {
594640 const session = this . sessions . get ( sessionId ) ;
595641 if ( ! session ) return ;
596642
643+ this . log (
644+ 'DEBUG' ,
645+ `SessionEnd: session=${ sessionId } trace_id=${ session . traceId } reason=${ ( payload [ 'reason' ] as string | undefined ) ?? 'unknown' } transcript_path=${ session . transcriptPath } turns=${ session . turnNumber } total_tools=${ session . totalToolCalls } pending_tools=${ session . pendingToolCalls . size } open_subagents=${ session . subagentByAgentId . size } ` ,
646+ ) ;
647+ this . upsertTraceRegistry (
648+ session . sessionId ,
649+ session . traceId ,
650+ session . transcriptPath ,
651+ ( payload [ 'reason' ] as string | undefined ) ?? 'session_end' ,
652+ session . sessionCallId ,
653+ ) ;
654+
597655 if ( this . weaveClient ) {
598656 const now = new Date ( ) . toISOString ( ) ;
599657
@@ -744,6 +802,48 @@ export class GlobalDaemon {
744802 this . sessionQueues . set ( sessionId , next ) ;
745803 }
746804
805+ private upsertTraceRegistry (
806+ sessionId : string ,
807+ traceId : string ,
808+ transcriptPath : string ,
809+ source : string ,
810+ sessionCallId ?: string ,
811+ ) : void {
812+ try {
813+ this . traceRegistry . upsert ( sessionId , traceId , transcriptPath , source , sessionCallId ) ;
814+ } catch ( err ) {
815+ this . log ( 'ERROR' , `Failed to update trace registry: ${ err } ` ) ;
816+ }
817+ }
818+
819+ private async resolveTraceForSession (
820+ sessionId : string ,
821+ transcriptPath : string ,
822+ sessionSource : string ,
823+ ) : Promise < TraceResolution > {
824+ if ( sessionSource === 'resume' ) {
825+ const bySession = this . traceRegistry . getBySessionId ( sessionId ) ;
826+ if ( bySession ) {
827+ return {
828+ traceId : bySession . traceId ,
829+ sessionCallId : bySession . sessionCallId ,
830+ source : 'registry-session' ,
831+ } ;
832+ }
833+
834+ const byTranscript = this . traceRegistry . getByTranscriptPath ( transcriptPath ) ;
835+ if ( byTranscript ) {
836+ return {
837+ traceId : byTranscript . traceId ,
838+ sessionCallId : byTranscript . sessionCallId ,
839+ source : 'registry-transcript' ,
840+ } ;
841+ }
842+ }
843+
844+ return { traceId : uuidv7 ( ) , source : 'new' } ;
845+ }
846+
747847 private log ( level : 'DEBUG' | 'INFO' | 'ERROR' , msg : string ) : void {
748848 if ( level === 'DEBUG' && ! this . debugEnabled ) return ;
749849 appendToLog ( this . logFile , level , msg ) ;
0 commit comments