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}