@@ -163,6 +163,222 @@ describe('session loader (projects/*.jsonl)', () => {
163163 )
164164 } )
165165
166+ test ( 'loads toolUseResult data from user messages with tool results' , ( ) => {
167+ const sessionId = '66666666-6666-6666-6666-666666666666'
168+ const path = getSessionLogFilePath ( { cwd : projectDir , sessionId } )
169+ mkdirSync (
170+ join (
171+ configDir ,
172+ 'projects' ,
173+ sanitizeProjectNameForSessionStore ( projectDir ) ,
174+ ) ,
175+ {
176+ recursive : true ,
177+ } ,
178+ )
179+
180+ // Simulate a session with a Bash tool result that has toolUseResult data
181+ const lines =
182+ [
183+ JSON . stringify ( {
184+ type : 'file-history-snapshot' ,
185+ messageId : 'm1' ,
186+ snapshot : {
187+ messageId : 'm1' ,
188+ trackedFileBackups : { } ,
189+ timestamp : new Date ( ) . toISOString ( ) ,
190+ } ,
191+ isSnapshotUpdate : false ,
192+ } ) ,
193+ JSON . stringify ( {
194+ type : 'user' ,
195+ sessionId,
196+ uuid : 'u1' ,
197+ message : { role : 'user' , content : 'run ls command' } ,
198+ } ) ,
199+ JSON . stringify ( {
200+ type : 'assistant' ,
201+ sessionId,
202+ uuid : 'a1' ,
203+ message : {
204+ id : 'msg1' ,
205+ model : 'x' ,
206+ type : 'message' ,
207+ role : 'assistant' ,
208+ content : [
209+ { type : 'text' , text : 'Running ls...' } ,
210+ { type : 'tool_use' , id : 'toolu_bash1' , name : 'Bash' , input : { command : 'ls' } } ,
211+ ] ,
212+ stop_reason : 'tool_use' ,
213+ stop_sequence : null ,
214+ usage : { input_tokens : 0 , output_tokens : 0 } ,
215+ } ,
216+ } ) ,
217+ // User message with tool_result AND toolUseResult data (as saved by kodeAgentSessionLog)
218+ JSON . stringify ( {
219+ type : 'user' ,
220+ sessionId,
221+ uuid : 'u2' ,
222+ message : {
223+ role : 'user' ,
224+ content : [
225+ {
226+ type : 'tool_result' ,
227+ tool_use_id : 'toolu_bash1' ,
228+ is_error : false ,
229+ content : 'file1.ts\nfile2.ts' ,
230+ } ,
231+ ] ,
232+ } ,
233+ toolUseResult : {
234+ stdout : 'file1.ts\nfile2.ts' ,
235+ stderr : '' ,
236+ exitCode : 0 ,
237+ interrupted : false ,
238+ } ,
239+ } ) ,
240+ ] . join ( '\n' ) + '\n'
241+ writeFileSync ( path , lines , 'utf8' )
242+
243+ const messages = loadKodeAgentSessionMessages ( {
244+ cwd : projectDir ,
245+ sessionId,
246+ } )
247+
248+ expect ( messages . length ) . toBe ( 3 )
249+
250+ // Verify the tool result message has toolUseResult restored
251+ const toolResultMsg = messages [ 2 ] as any
252+ expect ( toolResultMsg . type ) . toBe ( 'user' )
253+ expect ( toolResultMsg . toolUseResult ) . toBeDefined ( )
254+ expect ( toolResultMsg . toolUseResult . data ) . toEqual ( {
255+ stdout : 'file1.ts\nfile2.ts' ,
256+ stderr : '' ,
257+ exitCode : 0 ,
258+ interrupted : false ,
259+ } )
260+ } )
261+
262+ test ( 'loads FileEdit toolUseResult with filePath for UI rendering' , ( ) => {
263+ const sessionId = '77777777-7777-7777-7777-777777777777'
264+ const path = getSessionLogFilePath ( { cwd : projectDir , sessionId } )
265+ mkdirSync (
266+ join (
267+ configDir ,
268+ 'projects' ,
269+ sanitizeProjectNameForSessionStore ( projectDir ) ,
270+ ) ,
271+ {
272+ recursive : true ,
273+ } ,
274+ )
275+
276+ // Simulate a session with a FileEdit tool result
277+ const lines =
278+ [
279+ JSON . stringify ( {
280+ type : 'file-history-snapshot' ,
281+ messageId : 'm1' ,
282+ snapshot : {
283+ messageId : 'm1' ,
284+ trackedFileBackups : { } ,
285+ timestamp : new Date ( ) . toISOString ( ) ,
286+ } ,
287+ isSnapshotUpdate : false ,
288+ } ) ,
289+ JSON . stringify ( {
290+ type : 'user' ,
291+ sessionId,
292+ uuid : 'u1' ,
293+ message : {
294+ role : 'user' ,
295+ content : [
296+ {
297+ type : 'tool_result' ,
298+ tool_use_id : 'toolu_edit1' ,
299+ is_error : false ,
300+ content : 'File edited successfully' ,
301+ } ,
302+ ] ,
303+ } ,
304+ // This is the data shape that FileEditToolUpdatedMessage expects
305+ toolUseResult : {
306+ filePath : '/path/to/file.ts' ,
307+ structuredPatch : [
308+ {
309+ oldStart : 1 ,
310+ oldLines : 1 ,
311+ newStart : 1 ,
312+ newLines : 2 ,
313+ lines : [ '-old line' , '+new line' , '+another line' ] ,
314+ } ,
315+ ] ,
316+ } ,
317+ } ) ,
318+ ] . join ( '\n' ) + '\n'
319+ writeFileSync ( path , lines , 'utf8' )
320+
321+ const messages = loadKodeAgentSessionMessages ( {
322+ cwd : projectDir ,
323+ sessionId,
324+ } )
325+
326+ expect ( messages . length ) . toBe ( 1 )
327+
328+ const toolResultMsg = messages [ 0 ] as any
329+ expect ( toolResultMsg . toolUseResult ) . toBeDefined ( )
330+ expect ( toolResultMsg . toolUseResult . data . filePath ) . toBe ( '/path/to/file.ts' )
331+ expect ( toolResultMsg . toolUseResult . data . structuredPatch ) . toHaveLength ( 1 )
332+ } )
333+
334+ test ( 'handles user messages without toolUseResult gracefully' , ( ) => {
335+ const sessionId = '88888888-8888-8888-8888-888888888888'
336+ const path = getSessionLogFilePath ( { cwd : projectDir , sessionId } )
337+ mkdirSync (
338+ join (
339+ configDir ,
340+ 'projects' ,
341+ sanitizeProjectNameForSessionStore ( projectDir ) ,
342+ ) ,
343+ {
344+ recursive : true ,
345+ } ,
346+ )
347+
348+ // User message without toolUseResult (plain text message)
349+ const lines =
350+ [
351+ JSON . stringify ( {
352+ type : 'file-history-snapshot' ,
353+ messageId : 'm1' ,
354+ snapshot : {
355+ messageId : 'm1' ,
356+ trackedFileBackups : { } ,
357+ timestamp : new Date ( ) . toISOString ( ) ,
358+ } ,
359+ isSnapshotUpdate : false ,
360+ } ) ,
361+ JSON . stringify ( {
362+ type : 'user' ,
363+ sessionId,
364+ uuid : 'u1' ,
365+ message : { role : 'user' , content : 'hello' } ,
366+ // No toolUseResult field
367+ } ) ,
368+ ] . join ( '\n' ) + '\n'
369+ writeFileSync ( path , lines , 'utf8' )
370+
371+ const messages = loadKodeAgentSessionMessages ( {
372+ cwd : projectDir ,
373+ sessionId,
374+ } )
375+
376+ expect ( messages . length ) . toBe ( 1 )
377+ const msg = messages [ 0 ] as any
378+ expect ( msg . type ) . toBe ( 'user' )
379+ expect ( msg . toolUseResult ) . toBeUndefined ( )
380+ } )
381+
166382 test ( 'findMostRecentKodeAgentSessionId picks newest jsonl by mtime' , ( ) => {
167383 const projectRoot = join (
168384 configDir ,
0 commit comments