@@ -25,12 +25,19 @@ class SearchResult:
2525 session_id : str
2626 thread_name : str
2727 updated_at : str
28+ last_user_message_at : str
2829 description : str
2930 session_path : str
3031 score : int
3132 reasons : list [str ]
3233
3334
35+ @dataclass
36+ class UserMessage :
37+ text : str
38+ timestamp : str
39+
40+
3441def parse_args () -> argparse .Namespace :
3542 parser = argparse .ArgumentParser (
3643 description = "Search local Codex conversations under ~/.codex."
@@ -207,8 +214,18 @@ def merge_index_and_session_files(entries: list[SessionIndexEntry], session_path
207214 return merged
208215
209216
210- def extract_user_messages (session_path : Path ) -> list [str ]:
211- messages : list [str ] = []
217+ def extract_event_timestamp (event : dict [str , object ], payload : dict [str , object ]) -> str :
218+ timestamp = (
219+ event .get ("timestamp" )
220+ or payload .get ("timestamp" )
221+ or event .get ("created_at" )
222+ or payload .get ("created_at" )
223+ )
224+ return str (timestamp ).strip () if timestamp else ""
225+
226+
227+ def extract_user_messages (session_path : Path ) -> list [UserMessage ]:
228+ messages : list [UserMessage ] = []
212229 try :
213230 with session_path .open ("r" , encoding = "utf-8" ) as handle :
214231 for line in handle :
@@ -224,7 +241,9 @@ def extract_user_messages(session_path: Path) -> list[str]:
224241 if event_type == "event_msg" and payload .get ("type" ) in USER_MESSAGE_TYPES :
225242 text = payload .get ("message" , "" ).strip ()
226243 if text :
227- messages .append (text )
244+ messages .append (
245+ UserMessage (text = text , timestamp = extract_event_timestamp (event , payload ))
246+ )
228247 continue
229248 if event_type == "response_item" and payload .get ("type" ) == "message" and payload .get ("role" ) == "user" :
230249 contents = payload .get ("content" , [])
@@ -235,7 +254,12 @@ def extract_user_messages(session_path: Path) -> list[str]:
235254 if text :
236255 text_parts .append (text )
237256 if text_parts :
238- messages .append (" " .join (text_parts ))
257+ messages .append (
258+ UserMessage (
259+ text = " " .join (text_parts ),
260+ timestamp = extract_event_timestamp (event , payload ),
261+ )
262+ )
239263 except OSError :
240264 return []
241265 return messages
@@ -281,14 +305,14 @@ def is_noise_line(line: str) -> bool:
281305 return False
282306
283307
284- def extract_description (messages : list [str ], fallback : str ) -> str :
308+ def extract_description (messages : list [UserMessage ], fallback : str ) -> str :
285309 for message in messages :
286- if "AGENTS.md instructions for" in message and "## My request for Codex:" not in message :
310+ if "AGENTS.md instructions for" in message . text and "## My request for Codex:" not in message . text :
287311 continue
288- candidates = [message ]
312+ candidates = [message . text ]
289313 marker = "## My request for Codex:"
290- if marker in message :
291- candidates .insert (0 , message .split (marker , 1 )[1 ])
314+ if marker in message . text :
315+ candidates .insert (0 , message .text . split (marker , 1 )[1 ])
292316 for candidate_block in candidates :
293317 for line in candidate_block .splitlines ():
294318 if is_noise_line (line ):
@@ -327,7 +351,8 @@ def score_entry(
327351 score = 0
328352 reasons : list [str ] = []
329353 description = ""
330- messages : list [str ] = []
354+ messages : list [UserMessage ] = []
355+ last_user_message_at = ""
331356
332357 if exact_date and entry .updated_at .startswith (exact_date .strftime ("%Y-%m-%d" )):
333358 score += 25
@@ -343,11 +368,17 @@ def score_entry(
343368 messages = extract_user_messages (session_path )
344369 if messages :
345370 description = extract_description (messages , entry .thread_name )
371+ last_user_message_at = messages [- 1 ].timestamp
346372 for idx , message in enumerate (messages [:6 ]):
347373 for query in queries :
348374 exact_weight = 45 if idx == 0 else 20
349375 token_weight = 8 if idx == 0 else 4
350- query_score , query_reasons = score_text_match (query , message , exact_weight = exact_weight , token_weight = token_weight )
376+ query_score , query_reasons = score_text_match (
377+ query ,
378+ message .text ,
379+ exact_weight = exact_weight ,
380+ token_weight = token_weight ,
381+ )
351382 score += query_score
352383 prefix = "first-user" if idx == 0 else "user"
353384 reasons .extend ([f"{ prefix } :{ reason } " for reason in query_reasons ])
@@ -359,6 +390,7 @@ def score_entry(
359390 session_id = entry .session_id ,
360391 thread_name = entry .thread_name or "Sin nombre" ,
361392 updated_at = entry .updated_at ,
393+ last_user_message_at = last_user_message_at ,
362394 description = description ,
363395 session_path = str (session_path ) if session_path else "" ,
364396 score = score ,
@@ -385,6 +417,7 @@ def render_text(results: list[SearchResult], verbose: bool = False) -> str:
385417 lines = [
386418 f"Nombre: { result .thread_name } " ,
387419 f"Fecha: { result .updated_at or 'Unknown' } " ,
420+ f"Ultimo mensaje usuario: { result .last_user_message_at or 'Unknown' } " ,
388421 f"Descripcion: { result .description } " ,
389422 ]
390423 if verbose :
@@ -405,6 +438,7 @@ def render_json(results: list[SearchResult], verbose: bool = False) -> str:
405438 item : dict [str , object ] = {
406439 "thread_name" : result .thread_name ,
407440 "updated_at" : result .updated_at ,
441+ "last_user_message_at" : result .last_user_message_at ,
408442 "description" : result .description ,
409443 }
410444 if verbose :
0 commit comments