Fix timing footer being saved to context window

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
This commit is contained in:
Dhanji R. Prasanna
2026-01-10 15:55:59 +11:00
parent 0c2a978225
commit 6be0a03c4c
3 changed files with 27 additions and 9 deletions

View File

@@ -851,8 +851,14 @@ impl<W: UiWriter> Agent<W> {
// 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");

View File

@@ -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<W: UiWriter>(
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()