From 6be0a03c4cc32b5e0ac04a14c32a7f3fcb652951 Mon Sep 17 00:00:00 2001 From: "Dhanji R. Prasanna" Date: Sat, 10 Jan 2026 15:55:59 +1100 Subject: [PATCH] Fix timing footer being saved to context window MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The timing footer (e.g., ⏱️ 19.4s | 💭 4.7s) was being saved to the conversation history as a separate assistant message. This happened because stream_completion_with_tools returns the timing footer in TaskResult.response for display, but the caller was also saving it to context. Fix: Strip the timing footer (identified by \n\n⏱️) before saving to context window. The timing footer remains display-only. Also includes: - Research tool blank line fix: only add visual separator for research tool output, not all tools - Research tool webdriver propagation: pass parent's webdriver browser choice (Safari vs Chrome headless) to scout subprocess --- crates/g3-cli/src/ui_writer_impl.rs | 8 ++++++-- crates/g3-core/src/lib.rs | 10 ++++++++-- crates/g3-core/src/tools/research.rs | 18 +++++++++++++----- 3 files changed, 27 insertions(+), 9 deletions(-) diff --git a/crates/g3-cli/src/ui_writer_impl.rs b/crates/g3-cli/src/ui_writer_impl.rs index 96e2ed4..09c1b4e 100644 --- a/crates/g3-cli/src/ui_writer_impl.rs +++ b/crates/g3-cli/src/ui_writer_impl.rs @@ -231,8 +231,6 @@ impl UiWriter for ConsoleUiWriter { } fn print_tool_timing(&self, duration_str: &str, tokens_delta: u32, context_percentage: f32) { - // Add blank line before footer for visual separation - println!(); // Parse the duration string to determine color // Format is like "1.5s", "500ms", "2m 30.0s" let color_code = if duration_str.ends_with("ms") { @@ -274,6 +272,12 @@ impl UiWriter for ConsoleUiWriter { "" }; + // Add blank line before footer for research tool (its output is a full report) + if let Some(tool_name) = self.current_tool_name.lock().unwrap().as_ref() { + if tool_name == "research" { + println!(); + } + } println!("└─ ⚡️ {}{}\x1b[0m \x1b[2m{} ◉ | {:.0}%\x1b[0m", color_code, duration_str, tokens_delta, context_percentage); println!(); // Clear the stored tool info diff --git a/crates/g3-core/src/lib.rs b/crates/g3-core/src/lib.rs index 7505959..d5bdb7f 100644 --- a/crates/g3-core/src/lib.rs +++ b/crates/g3-core/src/lib.rs @@ -851,8 +851,14 @@ impl Agent { // Add assistant response to context window only if not empty // This prevents the "Skipping empty message" warning when only tools were executed - if !response_content.trim().is_empty() { - let assistant_message = Message::new(MessageRole::Assistant, response_content.clone()); + // Also strip timing footer - it's display-only and shouldn't be in context + let content_for_context = if let Some(timing_pos) = response_content.rfind("\n\n⏱️") { + response_content[..timing_pos].to_string() + } else { + response_content.clone() + }; + if !content_for_context.trim().is_empty() { + let assistant_message = Message::new(MessageRole::Assistant, content_for_context); self.context_window.add_message(assistant_message); } else { debug!("Assistant response was empty (likely only tool execution), skipping message addition"); diff --git a/crates/g3-core/src/tools/research.rs b/crates/g3-core/src/tools/research.rs index 14181a0..6032b36 100644 --- a/crates/g3-core/src/tools/research.rs +++ b/crates/g3-core/src/tools/research.rs @@ -7,6 +7,7 @@ use tokio::process::Command; use crate::ui_writer::UiWriter; use crate::ToolCall; +use g3_config::WebDriverBrowser; use super::executor::ToolContext; @@ -156,14 +157,21 @@ pub async fn execute_research( let g3_path = std::env::current_exe() .unwrap_or_else(|_| std::path::PathBuf::from("g3")); - // Spawn the scout agent - let mut child = Command::new(&g3_path) + // Build the command with appropriate webdriver flags + let mut cmd = Command::new(&g3_path); + cmd .arg("--agent") .arg("scout") - .arg("--webdriver") // Scout needs webdriver for web research .arg("--new-session") // Always start fresh for research - .arg("--quiet") // Suppress log file creation - .arg(query) + .arg("--quiet"); // Suppress log file creation + + // Propagate the webdriver browser choice from the parent g3 instance + match ctx.config.webdriver.browser { + WebDriverBrowser::ChromeHeadless => { cmd.arg("--chrome-headless"); } + WebDriverBrowser::Safari => { cmd.arg("--webdriver"); } + } + + let mut child = cmd.arg(query) .stdout(Stdio::piped()) .stderr(Stdio::piped()) .spawn()