Skip to main content

brainwires_agents/
context.rs

1//! Agent Context - environment for autonomous task execution
2//!
3//! [`AgentContext`] bundles the stable environment that a [`TaskAgent`][super::task_agent::TaskAgent]
4//! operates in: working directory, tool executor, inter-agent communication,
5//! file lock coordination, and the working set of files currently in context.
6//!
7//! Conversation history and tool definitions are maintained *internally* by
8//! the agent; they are not part of this context.
9
10use std::collections::HashMap;
11use std::sync::Arc;
12
13use tokio::sync::RwLock;
14
15use brainwires_core::WorkingSet;
16use brainwires_tool_system::{ToolExecutor, ToolPreHook};
17
18use crate::agent_hooks::AgentLifecycleHooks;
19use crate::communication::CommunicationHub;
20use crate::file_locks::FileLockManager;
21
22/// Environment context for a task agent.
23///
24/// Pass this to [`TaskAgent::new`][super::task_agent::TaskAgent::new] at
25/// construction time. All fields are cheaply cloneable via `Arc`.
26#[non_exhaustive]
27pub struct AgentContext {
28    /// Working directory used for resolving relative file paths.
29    pub working_directory: String,
30
31    /// Executes tools on behalf of the agent.
32    pub tool_executor: Arc<dyn ToolExecutor>,
33
34    /// Inter-agent message bus.
35    pub communication_hub: Arc<CommunicationHub>,
36
37    /// Coordinates exclusive/shared file access across concurrent agents.
38    pub file_lock_manager: Arc<FileLockManager>,
39
40    /// Tracks files currently loaded into the agent's context window.
41    pub working_set: Arc<RwLock<WorkingSet>>,
42
43    /// Application-specific metadata passed through to tools.
44    pub metadata: HashMap<String, String>,
45
46    /// Optional pre-execution hook for semantic tool validation.
47    ///
48    /// When set, the hook is called before every tool execution. Returning
49    /// [`PreHookDecision::Reject`](crate::PreHookDecision::Reject) causes the tool call to be skipped and
50    /// the rejection message injected as a `ToolResult::error`.
51    pub pre_execute_hook: Option<Arc<dyn ToolPreHook>>,
52
53    /// Optional lifecycle hooks for granular loop control.
54    ///
55    /// When set, the agent loop calls these hooks at every phase: iteration
56    /// boundaries, provider calls, tool execution, completion, and context
57    /// management. All hook methods have default no-op implementations.
58    ///
59    /// See [`AgentLifecycleHooks`] for the full hook surface.
60    pub lifecycle_hooks: Option<Arc<dyn AgentLifecycleHooks>>,
61}
62
63impl AgentContext {
64    /// Create a new agent context with the given environment.
65    ///
66    /// A fresh, empty [`WorkingSet`] is created automatically. Use
67    /// [`AgentContext::with_working_set`] to supply a pre-populated one.
68    pub fn new(
69        working_directory: impl Into<String>,
70        tool_executor: Arc<dyn ToolExecutor>,
71        communication_hub: Arc<CommunicationHub>,
72        file_lock_manager: Arc<FileLockManager>,
73    ) -> Self {
74        Self {
75            working_directory: working_directory.into(),
76            tool_executor,
77            communication_hub,
78            file_lock_manager,
79            working_set: Arc::new(RwLock::new(WorkingSet::new())),
80            metadata: HashMap::new(),
81            pre_execute_hook: None,
82            lifecycle_hooks: None,
83        }
84    }
85
86    /// Create a context that shares an existing [`WorkingSet`].
87    pub fn with_working_set(
88        working_directory: impl Into<String>,
89        tool_executor: Arc<dyn ToolExecutor>,
90        communication_hub: Arc<CommunicationHub>,
91        file_lock_manager: Arc<FileLockManager>,
92        working_set: Arc<RwLock<WorkingSet>>,
93    ) -> Self {
94        Self {
95            working_directory: working_directory.into(),
96            tool_executor,
97            communication_hub,
98            file_lock_manager,
99            working_set,
100            metadata: HashMap::new(),
101            pre_execute_hook: None,
102            lifecycle_hooks: None,
103        }
104    }
105
106    /// Add application-specific metadata.
107    pub fn with_metadata(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
108        self.metadata.insert(key.into(), value.into());
109        self
110    }
111
112    /// Set a pre-execution hook for semantic tool validation.
113    pub fn with_pre_execute_hook(mut self, hook: Arc<dyn ToolPreHook>) -> Self {
114        self.pre_execute_hook = Some(hook);
115        self
116    }
117
118    /// Set lifecycle hooks for granular loop control.
119    pub fn with_lifecycle_hooks(mut self, hooks: Arc<dyn AgentLifecycleHooks>) -> Self {
120        self.lifecycle_hooks = Some(hooks);
121        self
122    }
123}