Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions crates/zeph-core/src/agent/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1159,6 +1159,7 @@ pub(super) mod agent_tests {
blocks_executed: 1,
filter_stats: None,
diff: None,
streamed: false,
}))]);

let agent_channel = MockChannel::new(vec!["execute tool".to_string()]);
Expand Down Expand Up @@ -1358,6 +1359,7 @@ pub(super) mod agent_tests {
blocks_executed: 1,
filter_stats: None,
diff: None,
streamed: false,
})),
Ok(None),
]);
Expand All @@ -1379,6 +1381,7 @@ pub(super) mod agent_tests {
blocks_executed: 1,
filter_stats: None,
diff: None,
streamed: false,
}))]);

let mut agent = Agent::new(provider, channel, registry, None, 5, executor);
Expand Down Expand Up @@ -1472,6 +1475,7 @@ pub(super) mod agent_tests {
blocks_executed: 1,
filter_stats: None,
diff: None,
streamed: false,
})),
Ok(None),
]);
Expand Down Expand Up @@ -1501,6 +1505,7 @@ pub(super) mod agent_tests {
blocks_executed: 1,
filter_stats: None,
diff: None,
streamed: false,
})));
}
let executor = MockToolExecutor::new(outputs);
Expand Down
30 changes: 14 additions & 16 deletions crates/zeph-core/src/agent/streaming.rs
Original file line number Diff line number Diff line change
Expand Up @@ -661,7 +661,7 @@ impl<C: Channel, T: ToolExecutor> Agent<C, T> {
// Process results sequentially (metrics, channel sends, message parts)
let mut result_parts: Vec<MessagePart> = Vec::new();
for (tc, tool_result) in tool_calls.iter().zip(tool_results) {
let (output, is_error, diff, inline_stats) = match tool_result {
let (output, is_error, diff, inline_stats, already_streamed) = match tool_result {
Ok(Some(out)) => {
if let Some(ref fs) = out.filter_stats {
let saved = fs.estimated_tokens_saved() as u64;
Expand Down Expand Up @@ -694,10 +694,11 @@ impl<C: Channel, T: ToolExecutor> Agent<C, T> {
let inline_stats = out.filter_stats.as_ref().and_then(|fs| {
(fs.filtered_chars < fs.raw_chars).then(|| fs.format_inline(&tc.name))
});
(out.summary, false, out.diff, inline_stats)
let streamed = out.streamed;
(out.summary, false, out.diff, inline_stats, streamed)
}
Ok(None) => ("(no output)".to_owned(), false, None, None),
Err(e) => (format!("[error] {e}"), true, None, None),
Ok(None) => ("(no output)".to_owned(), false, None, None, false),
Err(e) => (format!("[error] {e}"), true, None, None, false),
};

let processed = self.maybe_summarize_tool_output(&output).await;
Expand All @@ -708,18 +709,13 @@ impl<C: Channel, T: ToolExecutor> Agent<C, T> {
};
let formatted = format_tool_output(&tc.name, &body);
let display = self.maybe_redact(&formatted);
// Bundle diff and filter stats into a single atomic send so TUI can attach
// them to the tool message without a race between DiffReady and FullMessage.
let disp_len = display.len();
tracing::debug!(
tool_name = %tc.name,
has_diff = diff.is_some(),
disp_len,
"about to call send_tool_output"
);
self.channel
.send_tool_output(&tc.name, &display, diff, inline_stats)
.await?;
// Tools that already streamed via ToolEvent channel (e.g. bash) have their
// output displayed by the TUI event forwarder; skip duplicate send.
if !already_streamed {
self.channel
.send_tool_output(&tc.name, &display, diff, inline_stats)
.await?;
}

result_parts.push(MessagePart::ToolResult {
tool_use_id: tc.id.clone(),
Expand Down Expand Up @@ -808,6 +804,7 @@ mod tests {
blocks_executed: 1,
diff: None,
filter_stats: None,
streamed: false,
}))
}
}
Expand Down Expand Up @@ -846,6 +843,7 @@ mod tests {
blocks_executed: 1,
diff: None,
filter_stats: None,
streamed: false,
}))
}
}
Expand Down
1 change: 1 addition & 0 deletions crates/zeph-mcp/src/executor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ impl ToolExecutor for McpToolExecutor {
blocks_executed,
filter_stats: None,
diff: None,
streamed: false,
}))
}
}
Expand Down
4 changes: 4 additions & 0 deletions crates/zeph-tools/src/composite.rs
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ mod tests {
blocks_executed: 1,
filter_stats: None,
diff: None,
streamed: false,
}))
}
}
Expand Down Expand Up @@ -93,6 +94,7 @@ mod tests {
blocks_executed: 1,
filter_stats: None,
diff: None,
streamed: false,
}))
}
}
Expand Down Expand Up @@ -170,6 +172,7 @@ mod tests {
blocks_executed: 1,
filter_stats: None,
diff: None,
streamed: false,
}))
} else {
Ok(None)
Expand All @@ -194,6 +197,7 @@ mod tests {
blocks_executed: 1,
filter_stats: None,
diff: None,
streamed: false,
}))
} else {
Ok(None)
Expand Down
3 changes: 3 additions & 0 deletions crates/zeph-tools/src/executor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,8 @@ pub struct ToolOutput {
pub blocks_executed: u32,
pub filter_stats: Option<FilterStats>,
pub diff: Option<DiffData>,
/// Whether this tool already streamed its output via `ToolEvent` channel.
pub streamed: bool,
}

impl fmt::Display for ToolOutput {
Expand Down Expand Up @@ -213,6 +215,7 @@ mod tests {
blocks_executed: 1,
filter_stats: None,
diff: None,
streamed: false,
};
assert_eq!(output.to_string(), "$ echo hello\nhello");
}
Expand Down
5 changes: 5 additions & 0 deletions crates/zeph-tools/src/file.rs
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,7 @@ impl FileExecutor {
blocks_executed: 1,
filter_stats: None,
diff: None,
streamed: false,
}))
}

Expand Down Expand Up @@ -173,6 +174,7 @@ impl FileExecutor {
old_content,
new_content: content,
}),
streamed: false,
}))
}

Expand Down Expand Up @@ -206,6 +208,7 @@ impl FileExecutor {
old_content: content,
new_content,
}),
streamed: false,
}))
}

Expand Down Expand Up @@ -239,6 +242,7 @@ impl FileExecutor {
blocks_executed: 1,
filter_stats: None,
diff: None,
streamed: false,
}))
}

Expand Down Expand Up @@ -282,6 +286,7 @@ impl FileExecutor {
blocks_executed: 1,
filter_stats: None,
diff: None,
streamed: false,
}))
}
}
Expand Down
2 changes: 2 additions & 0 deletions crates/zeph-tools/src/scrape.rs
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,7 @@ impl ToolExecutor for WebScrapeExecutor {
blocks_executed,
filter_stats: None,
diff: None,
streamed: false,
}))
}

Expand Down Expand Up @@ -136,6 +137,7 @@ impl ToolExecutor for WebScrapeExecutor {
blocks_executed: 1,
filter_stats: None,
diff: None,
streamed: false,
}))
}
}
Expand Down
1 change: 1 addition & 0 deletions crates/zeph-tools/src/shell.rs
Original file line number Diff line number Diff line change
Expand Up @@ -268,6 +268,7 @@ impl ShellExecutor {
blocks_executed,
filter_stats: cumulative_filter_stats,
diff: None,
streamed: self.tool_event_tx.is_some(),
}))
}

Expand Down
1 change: 1 addition & 0 deletions crates/zeph-tools/src/trust_gate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,7 @@ mod tests {
blocks_executed: 1,
filter_stats: None,
diff: None,
streamed: false,
}))
}
}
Expand Down
6 changes: 6 additions & 0 deletions tests/integration.rs
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,7 @@ impl ToolExecutor for OutputToolExecutor {
blocks_executed: 1,
filter_stats: None,
diff: None,
streamed: false,
}))
}
}
Expand All @@ -181,6 +182,7 @@ impl ToolExecutor for EmptyOutputToolExecutor {
blocks_executed: 1,
filter_stats: None,
diff: None,
streamed: false,
}))
}
}
Expand All @@ -195,6 +197,7 @@ impl ToolExecutor for ErrorOutputToolExecutor {
blocks_executed: 1,
filter_stats: None,
diff: None,
streamed: false,
}))
}
}
Expand Down Expand Up @@ -225,6 +228,7 @@ impl ToolExecutor for ConfirmToolExecutor {
blocks_executed: 1,
filter_stats: None,
diff: None,
streamed: false,
}))
}
}
Expand Down Expand Up @@ -260,6 +264,7 @@ impl ToolExecutor for ExitCodeToolExecutor {
blocks_executed: 1,
filter_stats: None,
diff: None,
streamed: false,
}))
}
}
Expand Down Expand Up @@ -2167,6 +2172,7 @@ mod self_learning {
blocks_executed: 1,
filter_stats: None,
diff: None,
streamed: false,
}))
}
}
Expand Down
1 change: 1 addition & 0 deletions tests/performance_agent_integration.rs
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@ impl ToolExecutor for InstrumentedMockExecutor {
blocks_executed: 1,
filter_stats: None,
diff: None,
streamed: false,
}))
} else {
Ok(None)
Expand Down
Loading