diff --git a/src/agent/agent_loop.rs b/src/agent/agent_loop.rs index 0126edd..36cdfc8 100644 --- a/src/agent/agent_loop.rs +++ b/src/agent/agent_loop.rs @@ -308,6 +308,7 @@ fn filter_images_by_age_and_count( tool_call_id: message.tool_call_id.clone(), tool_name: message.tool_name.clone(), tool_state: message.tool_state.clone(), + tool_duration_ms: message.tool_duration_ms, tool_calls: message.tool_calls.clone(), }); } @@ -1025,7 +1026,8 @@ impl AgentLoop { } else { ToolMessageState::Completed }, - ); + ) + .with_tool_duration(result.duration.as_millis() as u64); messages.push(tool_message.clone()); emitted_messages.push(tool_message.clone()); let duration_ms = Some(result.duration.as_millis() as u64); @@ -1041,7 +1043,8 @@ impl AgentLoop { } else { ToolMessageState::Completed }, - ); + ) + .with_tool_duration(result.duration.as_millis() as u64); messages.push(tool_message.clone()); emitted_messages.push(tool_message.clone()); let duration_ms = Some(result.duration.as_millis() as u64); diff --git a/src/bus/message.rs b/src/bus/message.rs index 43f6e22..5f2d3b7 100644 --- a/src/bus/message.rs +++ b/src/bus/message.rs @@ -62,6 +62,8 @@ pub struct ChatMessage { pub tool_name: Option, #[serde(skip_serializing_if = "Option::is_none")] pub tool_state: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub tool_duration_ms: Option, #[serde(skip_serializing_if = "Option::is_none")] pub tool_calls: Option>, } @@ -78,6 +80,7 @@ impl ChatMessage { reasoning_content: None, tool_call_id: None, tool_name: None, + tool_duration_ms: None, tool_state: None, tool_calls: None, } @@ -94,6 +97,7 @@ impl ChatMessage { reasoning_content: None, tool_call_id: None, tool_name: None, + tool_duration_ms: None, tool_state: None, tool_calls: None, } @@ -110,6 +114,7 @@ impl ChatMessage { reasoning_content: None, tool_call_id: None, tool_name: None, + tool_duration_ms: None, tool_state: None, tool_calls: None, } @@ -138,6 +143,7 @@ impl ChatMessage { reasoning_content: None, tool_call_id: None, tool_name: None, + tool_duration_ms: None, tool_state: None, tool_calls: Some(tool_calls), } @@ -171,6 +177,7 @@ impl ChatMessage { reasoning_content: None, tool_call_id: None, tool_name: None, + tool_duration_ms: None, tool_state: None, tool_calls: None, } @@ -205,11 +212,17 @@ impl ChatMessage { reasoning_content: None, tool_call_id: Some(tool_call_id.into()), tool_name: Some(tool_name.into()), + tool_duration_ms: None, tool_state: Some(tool_state), tool_calls: None, } } + pub fn with_tool_duration(mut self, ms: u64) -> Self { + self.tool_duration_ms = Some(ms); + self + } + pub fn has_system_context(&self, expected: &str) -> bool { self.system_context.as_deref() == Some(expected) } diff --git a/src/gateway/ws.rs b/src/gateway/ws.rs index 1ce674e..71c49d0 100644 --- a/src/gateway/ws.rs +++ b/src/gateway/ws.rs @@ -662,7 +662,7 @@ fn chat_message_to_ws_outbound(msg: &crate::bus::ChatMessage) -> Option Some(WsOutbound::ToolPending { id: msg.id.clone(), diff --git a/src/storage/mod.rs b/src/storage/mod.rs index 0b7af0f..378b111 100644 --- a/src/storage/mod.rs +++ b/src/storage/mod.rs @@ -1455,6 +1455,7 @@ impl SessionStore { tool_call_id: row.get(7)?, tool_name: row.get(8)?, tool_state: None, + tool_duration_ms: None, tool_calls, }) })?; @@ -1780,6 +1781,7 @@ fn clone_message_for_compaction(message: &ChatMessage, timestamp: i64) -> ChatMe tool_call_id: message.tool_call_id.clone(), tool_name: message.tool_name.clone(), tool_state: message.tool_state.clone(), + tool_duration_ms: message.tool_duration_ms, tool_calls: message.tool_calls.clone(), } } @@ -1836,6 +1838,7 @@ fn load_messages_between( tool_call_id: row.get(7)?, tool_name: row.get(8)?, tool_state: None, + tool_duration_ms: None, tool_calls, }) }, @@ -1896,6 +1899,7 @@ fn load_messages_after( tool_call_id: row.get(7)?, tool_name: row.get(8)?, tool_state: None, + tool_duration_ms: None, tool_calls, }) })?; diff --git a/web/src/hooks/useChat.ts b/web/src/hooks/useChat.ts index 68cca92..72c910e 100644 --- a/web/src/hooks/useChat.ts +++ b/web/src/hooks/useChat.ts @@ -346,6 +346,7 @@ export function useChat(): UseChatReturn { toolName: msg.tool_name, toolCallId: msg.tool_call_id, subagentTaskId: msg.subagent_task_id, + durationMs: msg.duration_ms, }, ]) break