11use std:: collections:: HashMap ;
22use std:: env;
33
4+ use chrono:: {
5+ DateTime ,
6+ Utc ,
7+ } ;
48use serde:: {
59 Deserialize ,
610 Serialize ,
@@ -14,6 +18,10 @@ use super::consts::{
1418 MAX_CURRENT_WORKING_DIRECTORY_LEN ,
1519 MAX_USER_MESSAGE_SIZE ,
1620} ;
21+ use super :: conversation:: {
22+ CONTEXT_ENTRY_END_HEADER ,
23+ CONTEXT_ENTRY_START_HEADER ,
24+ } ;
1725use super :: tools:: {
1826 InvokeOutput ,
1927 OutputKind ,
@@ -46,6 +54,7 @@ pub struct UserMessage {
4654 pub additional_context : String ,
4755 pub env_context : UserEnvContext ,
4856 pub content : UserMessageContent ,
57+ pub timestamp : DateTime < Utc > ,
4958 pub images : Option < Vec < ImageBlock > > ,
5059}
5160
@@ -101,6 +110,7 @@ impl UserMessage {
101110 pub fn new_prompt ( prompt : String ) -> Self {
102111 Self {
103112 images : None ,
113+ timestamp : Utc :: now ( ) ,
104114 additional_context : String :: new ( ) ,
105115 env_context : UserEnvContext :: generate_new ( ) ,
106116 content : UserMessageContent :: Prompt { prompt } ,
@@ -110,6 +120,7 @@ impl UserMessage {
110120 pub fn new_cancelled_tool_uses < ' a > ( prompt : Option < String > , tool_use_ids : impl Iterator < Item = & ' a str > ) -> Self {
111121 Self {
112122 images : None ,
123+ timestamp : Utc :: now ( ) ,
113124 additional_context : String :: new ( ) ,
114125 env_context : UserEnvContext :: generate_new ( ) ,
115126 content : UserMessageContent :: CancelledToolUses {
@@ -130,6 +141,7 @@ impl UserMessage {
130141 pub fn new_tool_use_results ( results : Vec < ToolUseResult > ) -> Self {
131142 Self {
132143 additional_context : String :: new ( ) ,
144+ timestamp : Utc :: now ( ) ,
133145 env_context : UserEnvContext :: generate_new ( ) ,
134146 content : UserMessageContent :: ToolUseResults {
135147 tool_use_results : results,
@@ -141,6 +153,7 @@ impl UserMessage {
141153 pub fn new_tool_use_results_with_images ( results : Vec < ToolUseResult > , images : Vec < ImageBlock > ) -> Self {
142154 Self {
143155 additional_context : String :: new ( ) ,
156+ timestamp : Utc :: now ( ) ,
144157 env_context : UserEnvContext :: generate_new ( ) ,
145158 content : UserMessageContent :: ToolUseResults {
146159 tool_use_results : results,
@@ -267,13 +280,25 @@ impl UserMessage {
267280
268281 /// Returns a formatted [String] containing [Self::additional_context] and [Self::prompt].
269282 fn content_with_context ( & self ) -> String {
270- match ( self . additional_context . is_empty ( ) , self . prompt ( ) ) {
283+ // Format the time with iso8601 format using Z, e.g. 2025-08-08T17:43:28.672Z
284+ let timestamp = self . timestamp . to_rfc3339_opts ( chrono:: SecondsFormat :: Millis , true ) ;
285+
286+ let prompt_with_timestamp = self . prompt ( ) . map ( |p| {
287+ format ! (
288+ "{}Current UTC time: {}{}{}{}{}" ,
289+ CONTEXT_ENTRY_START_HEADER ,
290+ timestamp,
291+ CONTEXT_ENTRY_END_HEADER ,
292+ USER_ENTRY_START_HEADER ,
293+ p,
294+ USER_ENTRY_END_HEADER
295+ )
296+ } ) ;
297+
298+ match ( self . additional_context . is_empty ( ) , prompt_with_timestamp) {
271299 // Only add special delimiters if we have both a prompt and additional context
272- ( false , Some ( prompt) ) => format ! (
273- "{} {}{}{}" ,
274- self . additional_context, USER_ENTRY_START_HEADER , prompt, USER_ENTRY_END_HEADER
275- ) ,
276- ( true , Some ( prompt) ) => prompt. to_string ( ) ,
300+ ( false , Some ( prompt) ) => format ! ( "{}\n {}" , self . additional_context, prompt) ,
301+ ( true , Some ( prompt) ) => prompt,
277302 _ => self . additional_context . clone ( ) ,
278303 }
279304 . trim ( )
@@ -521,4 +546,23 @@ mod tests {
521546 assert ! ( env_state. operating_system. as_ref( ) . is_some_and( |os| !os. is_empty( ) ) ) ;
522547 println ! ( "{env_state:?}" ) ;
523548 }
549+
550+ #[ test]
551+ fn test_user_input_message_timestamp_formatting ( ) {
552+ let msg = UserMessage :: new_prompt ( "hello world" . to_string ( ) ) ;
553+
554+ let msgs = [
555+ msg. clone ( ) . into_user_input_message ( None , & HashMap :: new ( ) ) ,
556+ msg. clone ( ) . into_history_entry ( ) ,
557+ ] ;
558+
559+ for m in msgs {
560+ m. content . contains ( CONTEXT_ENTRY_START_HEADER ) ;
561+ m. content . contains ( "Current UTC time" ) ;
562+ m. content . contains ( CONTEXT_ENTRY_END_HEADER ) ;
563+ m. content . contains ( USER_ENTRY_START_HEADER ) ;
564+ m. content . contains ( "hello world" ) ;
565+ m. content . contains ( USER_ENTRY_END_HEADER ) ;
566+ }
567+ }
524568}
0 commit comments