@@ -75,6 +75,7 @@ func SessionFromEvents(events []map[string]any, title, question string) *session
7575 var currentModel string
7676 var currentUsage * chat.Usage
7777 var currentCost float64
78+ var currentTimestamp string
7879
7980 // Helper to flush current assistant message
8081 flushAssistantMessage := func () {
@@ -87,7 +88,7 @@ func SessionFromEvents(events []map[string]any, title, question string) *session
8788 ReasoningContent : currentReasoningContent .String (),
8889 ToolCalls : currentToolCalls ,
8990 ToolDefinitions : currentToolDefinitions ,
90- CreatedAt : time . Now (). Format ( time . RFC3339 ) ,
91+ CreatedAt : currentTimestamp ,
9192 Model : currentModel ,
9293 Usage : currentUsage ,
9394 Cost : currentCost ,
@@ -101,11 +102,13 @@ func SessionFromEvents(events []map[string]any, title, question string) *session
101102 currentModel = ""
102103 currentUsage = nil
103104 currentCost = 0
105+ currentTimestamp = ""
104106 }
105107 }
106108
107109 for _ , event := range events {
108110 eventType , _ := event ["type" ].(string )
111+ eventTimestamp := parseEventTimestamp (event )
109112
110113 switch eventType {
111114 case "agent_choice" :
@@ -116,6 +119,9 @@ func SessionFromEvents(events []map[string]any, title, question string) *session
116119 if agentName , ok := event ["agent_name" ].(string ); ok && agentName != "" {
117120 currentAgentName = agentName
118121 }
122+ if eventTimestamp != "" {
123+ currentTimestamp = eventTimestamp
124+ }
119125
120126 case "agent_choice_reasoning" :
121127 // Accumulate reasoning content (for models like DeepSeek, Claude with extended thinking)
@@ -125,6 +131,9 @@ func SessionFromEvents(events []map[string]any, title, question string) *session
125131 if agentName , ok := event ["agent_name" ].(string ); ok && agentName != "" {
126132 currentAgentName = agentName
127133 }
134+ if eventTimestamp != "" {
135+ currentTimestamp = eventTimestamp
136+ }
128137
129138 case "tool_call" :
130139 // Parse tool call and add to current message
@@ -143,6 +152,9 @@ func SessionFromEvents(events []map[string]any, title, question string) *session
143152 if agentName , ok := event ["agent_name" ].(string ); ok && agentName != "" {
144153 currentAgentName = agentName
145154 }
155+ if eventTimestamp != "" {
156+ currentTimestamp = eventTimestamp
157+ }
146158
147159 case "tool_call_response" :
148160 // Flush any pending assistant message before adding tool response
@@ -158,7 +170,7 @@ func SessionFromEvents(events []map[string]any, title, question string) *session
158170 Role : chat .MessageRoleTool ,
159171 Content : response ,
160172 ToolCallID : toolCallID ,
161- CreatedAt : time . Now (). Format ( time . RFC3339 ) ,
173+ CreatedAt : eventTimestamp ,
162174 },
163175 }
164176 sess .AddMessage (msg )
@@ -198,7 +210,7 @@ func SessionFromEvents(events []map[string]any, title, question string) *session
198210 Message : chat.Message {
199211 Role : chat .MessageRoleSystem ,
200212 Content : "Error: " + errorMsg ,
201- CreatedAt : time . Now (). Format ( time . RFC3339 ) ,
213+ CreatedAt : eventTimestamp ,
202214 },
203215 }
204216 sess .AddMessage (msg )
@@ -301,6 +313,19 @@ func parseMessageUsage(m map[string]any) *chat.Usage {
301313 return usage
302314}
303315
316+ // parseEventTimestamp extracts the timestamp from an event map.
317+ // Returns the timestamp string, falling back to current time if not present or invalid.
318+ func parseEventTimestamp (event map [string ]any ) string {
319+ if ts , ok := event ["timestamp" ].(string ); ok && ts != "" {
320+ // Validate RFC3339 format
321+ if _ , err := time .Parse (time .RFC3339 , ts ); err == nil {
322+ return ts
323+ }
324+ // Invalid timestamp format - fall back to current time
325+ }
326+ return time .Now ().Format (time .RFC3339 )
327+ }
328+
304329// SaveRunJSON saves the eval run results to a JSON file.
305330// This is kept for backward compatibility and debugging purposes.
306331func SaveRunJSON (run * EvalRun , outputDir string ) (string , error ) {
0 commit comments