@@ -21,7 +21,8 @@ use crate::sessions::lcm::{
2121 LcmGrepSort , LcmLoadSessionRequest , LcmPreflightRequest , LcmScope , LcmSessionBoundaryRequest ,
2222 LcmSummarizerMode , LCM_EXPAND_QUERY_SYNTHESIS_SYSTEM_PROMPT ,
2323} ;
24- use crate :: sessions:: { ProviderScope , SessionSearchScope } ;
24+ use crate :: sessions:: { ProviderScope , SessionSearchScope , SessionSearchTimeRange } ;
25+ use crate :: timeutil:: SearchTimeBound ;
2526use crate :: tracedecay:: { current_timestamp, TraceDecay } ;
2627
2728const DEFAULT_LCM_CONTENT_LIMIT : usize = 4096 ;
@@ -1094,26 +1095,32 @@ fn non_negative_i64_arg_alias(args: &Value, primary: &str, alias: &str) -> Resul
10941095 }
10951096}
10961097
1097- fn non_negative_timestamp_arg_alias (
1098+ fn non_negative_timestamp_arg_aliases (
10981099 args : & Value ,
1099- primary : & str ,
1100- alias : & str ,
1100+ names : & [ & str ] ,
1101+ bound : SearchTimeBound ,
11011102) -> Result < Option < i64 > > {
1102- match non_negative_timestamp_arg ( args, primary) ? {
1103- Some ( value) => Ok ( Some ( value) ) ,
1104- None => non_negative_timestamp_arg ( args, alias) ,
1103+ for name in names {
1104+ if args. get ( name) . is_some ( ) {
1105+ return non_negative_timestamp_arg ( args, name, bound) ;
1106+ }
11051107 }
1108+ Ok ( None )
11061109}
11071110
1108- fn non_negative_timestamp_arg ( args : & Value , name : & str ) -> Result < Option < i64 > > {
1111+ fn non_negative_timestamp_arg (
1112+ args : & Value ,
1113+ name : & str ,
1114+ bound : SearchTimeBound ,
1115+ ) -> Result < Option < i64 > > {
11091116 let Some ( value) = args. get ( name) else {
11101117 return Ok ( None ) ;
11111118 } ;
11121119 let timestamp = match value {
11131120 Value :: Number ( number) => number
11141121 . as_i64 ( )
11151122 . ok_or_else ( || timestamp_argument_error ( name) ) ?,
1116- Value :: String ( text) => parse_timestamp_string ( text, name) ?,
1123+ Value :: String ( text) => parse_timestamp_string ( text, name, bound ) ?,
11171124 _ => return Err ( timestamp_argument_error ( name) ) ,
11181125 } ;
11191126 if timestamp < 0 {
@@ -1122,7 +1129,7 @@ fn non_negative_timestamp_arg(args: &Value, name: &str) -> Result<Option<i64>> {
11221129 Ok ( Some ( timestamp) )
11231130}
11241131
1125- fn parse_timestamp_string ( value : & str , name : & str ) -> Result < i64 > {
1132+ fn parse_timestamp_string ( value : & str , name : & str , bound : SearchTimeBound ) -> Result < i64 > {
11261133 let text = value. trim ( ) ;
11271134 if text. is_empty ( ) {
11281135 return Err ( argument_error ( format ! ( "{name} must not be empty" ) ) ) ;
@@ -1133,12 +1140,29 @@ fn parse_timestamp_string(value: &str, name: &str) -> Result<i64> {
11331140 }
11341141 return Err ( argument_error ( format ! ( "{name} must be >= 0" ) ) ) ;
11351142 }
1136- crate :: timeutil:: parse_rfc3339_timestamp ( text) . ok_or_else ( || timestamp_argument_error ( name) )
1143+ let now = crate :: tracedecay:: current_timestamp ( ) ;
1144+ crate :: timeutil:: parse_search_time_filter_bound ( text, now, bound)
1145+ . ok_or_else ( || timestamp_argument_error ( name) )
1146+ }
1147+
1148+ fn message_search_time_range ( args : & Value ) -> Result < SessionSearchTimeRange > {
1149+ Ok ( SessionSearchTimeRange {
1150+ start_time : non_negative_timestamp_arg_aliases (
1151+ args,
1152+ & [ "since" , "start_time" , "time_from" ] ,
1153+ SearchTimeBound :: Start ,
1154+ ) ?,
1155+ end_time : non_negative_timestamp_arg_aliases (
1156+ args,
1157+ & [ "until" , "end_time" , "time_to" ] ,
1158+ SearchTimeBound :: End ,
1159+ ) ?,
1160+ } )
11371161}
11381162
11391163fn timestamp_argument_error ( name : & str ) -> TraceDecayError {
11401164 argument_error ( format ! (
1141- "{name} must be a non-negative Unix timestamp or timezone-aware ISO/RFC3339 string"
1165+ "{name} must be a non-negative Unix timestamp, timezone-aware ISO/RFC3339 string, YYYY-MM-DD date, or relative time like 'last hour' "
11421166 ) )
11431167}
11441168
@@ -1779,6 +1803,7 @@ pub(super) async fn handle_message_search(
17791803 . and_then ( Value :: as_u64)
17801804 . unwrap_or ( 10 )
17811805 . clamp ( 1 , 50 ) as usize ;
1806+ let time_range = message_search_time_range ( & args) ?;
17821807
17831808 let Some ( ( db_path, target_root) ) = selected_project_session_db_path (
17841809 cg. project_root ( ) ,
@@ -1829,6 +1854,7 @@ pub(super) async fn handle_message_search(
18291854 limit,
18301855 scope,
18311856 parent_session_id,
1857+ time_range,
18321858 )
18331859 . await
18341860 } else {
@@ -1838,6 +1864,7 @@ pub(super) async fn handle_message_search(
18381864 limit,
18391865 scope,
18401866 parent_session_id,
1867+ time_range,
18411868 )
18421869 . await
18431870 } ;
@@ -1858,6 +1885,8 @@ pub(super) async fn handle_message_search(
18581885 SessionSearchScope :: ParentsOnly => "parents_only" ,
18591886 SessionSearchScope :: SubagentsOnly => "subagents_only" ,
18601887 } ,
1888+ "since" : time_range. start_time,
1889+ "until" : time_range. end_time,
18611890 "query" : query,
18621891 "count" : results. len( ) ,
18631892 "results" : results,
@@ -2069,8 +2098,16 @@ pub(super) async fn handle_lcm_grep(
20692098 sort : parse_lcm_grep_sort ( & args) ?,
20702099 source : string_arg ( & args, "source" ) . map ( str:: to_string) ,
20712100 role : string_arg ( & args, "role" ) . map ( str:: to_string) ,
2072- start_time : non_negative_timestamp_arg_alias ( & args, "start_time" , "time_from" ) ?,
2073- end_time : non_negative_timestamp_arg_alias ( & args, "end_time" , "time_to" ) ?,
2101+ start_time : non_negative_timestamp_arg_aliases (
2102+ & args,
2103+ & [ "since" , "start_time" , "time_from" ] ,
2104+ SearchTimeBound :: Start ,
2105+ ) ?,
2106+ end_time : non_negative_timestamp_arg_aliases (
2107+ & args,
2108+ & [ "until" , "end_time" , "time_to" ] ,
2109+ SearchTimeBound :: End ,
2110+ ) ?,
20742111 } )
20752112 . await
20762113 . map_err ( lcm_error) ?;
0 commit comments