Skip to main content

bamboo_agent/agent/core/tools/
context.rs

1//! Execution context for tool calls.
2//!
3//! Tools normally return a single `ToolResult` after completion. Some tools
4//! (for example, long-running CLIs) may want to stream intermediate progress
5//! to clients. The agent loop passes a `ToolExecutionContext` that allows tools
6//! to emit `AgentEvent`s while they run.
7
8use tokio::sync::mpsc;
9
10use crate::agent::core::AgentEvent;
11
12/// Context passed to tools during execution.
13///
14/// All fields are optional and should be treated as best-effort hints.
15#[derive(Clone, Copy, Debug)]
16pub struct ToolExecutionContext<'a> {
17    /// Bamboo session id that is executing the tool.
18    pub session_id: Option<&'a str>,
19    /// Tool call id from the model (`ToolCall.id`).
20    pub tool_call_id: &'a str,
21    /// Event sender for streaming progress to clients (agent SSE stream).
22    pub event_tx: Option<&'a mpsc::Sender<AgentEvent>>,
23}
24
25impl<'a> ToolExecutionContext<'a> {
26    pub fn none(tool_call_id: &'a str) -> Self {
27        Self {
28            session_id: None,
29            tool_call_id,
30            event_tx: None,
31        }
32    }
33
34    /// Clone the sender (when present) for use in spawned tasks.
35    pub fn cloned_sender(&self) -> Option<mpsc::Sender<AgentEvent>> {
36        self.event_tx.cloned()
37    }
38
39    /// Best-effort emit of an event (ignored if no sender).
40    pub async fn emit(&self, event: AgentEvent) {
41        if let Some(tx) = self.event_tx {
42            // Tools sometimes want to stream incremental output. Historically they emitted
43            // `AgentEvent::Token`, but that mixes tool output into the assistant stream.
44            // When emitting from a tool context, treat `Token` as tool-scoped output.
45            let event = match event {
46                AgentEvent::Token { content } => AgentEvent::ToolToken {
47                    tool_call_id: self.tool_call_id.to_string(),
48                    content,
49                },
50                other => other,
51            };
52            let _ = tx.send(event).await;
53        }
54    }
55
56    /// Convenience helper for streaming tool-scoped output.
57    pub async fn emit_tool_token(&self, content: impl Into<String>) {
58        self.emit(AgentEvent::ToolToken {
59            tool_call_id: self.tool_call_id.to_string(),
60            content: content.into(),
61        })
62        .await;
63    }
64}