1use crate::env::EnvConfig;
3use crate::error::AgentError;
4use crate::query_engine::{QueryEngine, QueryEngineConfig};
5use crate::stream::{CancelGuard, EventBroadcasters, EventSubscriber};
6use crate::tools::ask::AskUserQuestionTool;
7use crate::tools::bash::BashTool;
8use crate::tools::brief::BriefTool;
9use crate::tools::synthetic_output::SyntheticOutputTool;
10use crate::tools::config::ConfigTool;
11use crate::tools::cron::{CronCreateTool, CronDeleteTool, CronListTool};
12use crate::tools::edit::FileEditTool;
13use crate::tools::glob::GlobTool;
14use crate::tools::grep::GrepTool;
15use crate::tools::lsp::LSPTool;
16use crate::tools::mcp_resource_reader::ReadMcpResourceTool;
17use crate::tools::mcp_resources::ListMcpResourcesTool;
18use crate::tools::mcp_tool::McpTool;
19use crate::tools::mcp_auth::McpAuthTool;
20use crate::tools::monitor::MonitorTool;
21use crate::tools::notebook_edit::NotebookEditTool;
22use crate::tools::plan::{EnterPlanModeTool, ExitPlanModeTool};
23use crate::tools::read::FileReadTool as ReadTool;
24use crate::tools::remote_trigger::RemoteTriggerTool;
25use crate::tools::search::ToolSearchTool;
26use crate::tools::send_user_file::SendUserFileTool;
27use crate::tools::skill::SkillTool;
28use crate::tools::skill::register_skills_from_dir;
29use crate::skills::loader::load_all_skills;
30use crate::utils::hooks::register_hooks_from_skills;
31use crate::tools::sleep_tool::SleepTool;
32use crate::tools::powershell::powershell_tool::PowerShellTool;
33use crate::tools::task_output::TaskOutputTool;
34use crate::tools::tasks::{TaskCreateTool, TaskGetTool, TaskListTool, TaskUpdateTool};
35use crate::tools::team::{SendMessageTool, TeamCreateTool, TeamDeleteTool};
36use crate::tools::todo::TodoWriteTool;
37use crate::tools::web_browser::WebBrowserTool;
38use crate::tools::web_fetch::WebFetchTool;
39use crate::tools::web_search::WebSearchTool;
40use crate::tools::worktree::{EnterWorktreeTool, ExitWorktreeTool};
41use crate::tools::write::FileWriteTool as WriteTool;
42use crate::permission::{PermissionResult, PermissionAllowDecision, PermissionDenyDecision, PermissionDecisionReason};
43use crate::types::AgentEvent;
44use crate::types::*;
45use crate::types::ToolRender;
46use std::sync::Arc;
47use tokio::sync::mpsc;
48
49impl ToolRender for BashTool {
51 fn user_facing_name(&self, input: Option<&serde_json::Value>) -> String {
52 <BashTool>::user_facing_name(self, input)
53 }
54 fn get_tool_use_summary(&self, input: Option<&serde_json::Value>) -> Option<String> {
55 <BashTool>::get_tool_use_summary(self, input)
56 }
57 fn render_tool_result_message(&self, content: &serde_json::Value) -> Option<String> {
58 <BashTool>::render_tool_result_message(self, content)
59 }
60}
61impl ToolRender for ReadTool {
62 fn user_facing_name(&self, input: Option<&serde_json::Value>) -> String {
63 <ReadTool>::user_facing_name(self, input)
64 }
65 fn get_tool_use_summary(&self, input: Option<&serde_json::Value>) -> Option<String> {
66 <ReadTool>::get_tool_use_summary(self, input)
67 }
68 fn render_tool_result_message(&self, content: &serde_json::Value) -> Option<String> {
69 <ReadTool>::render_tool_result_message(self, content)
70 }
71}
72impl ToolRender for WriteTool {
73 fn user_facing_name(&self, input: Option<&serde_json::Value>) -> String {
74 <WriteTool>::user_facing_name(self, input)
75 }
76 fn get_tool_use_summary(&self, input: Option<&serde_json::Value>) -> Option<String> {
77 <WriteTool>::get_tool_use_summary(self, input)
78 }
79 fn render_tool_result_message(&self, content: &serde_json::Value) -> Option<String> {
80 <WriteTool>::render_tool_result_message(self, content)
81 }
82}
83impl ToolRender for GlobTool {
84 fn user_facing_name(&self, input: Option<&serde_json::Value>) -> String {
85 <GlobTool>::user_facing_name(self, input)
86 }
87 fn get_tool_use_summary(&self, input: Option<&serde_json::Value>) -> Option<String> {
88 <GlobTool>::get_tool_use_summary(self, input)
89 }
90 fn render_tool_result_message(&self, content: &serde_json::Value) -> Option<String> {
91 <GlobTool>::render_tool_result_message(self, content)
92 }
93}
94impl ToolRender for GrepTool {
95 fn user_facing_name(&self, input: Option<&serde_json::Value>) -> String {
96 <GrepTool>::user_facing_name(self, input)
97 }
98 fn get_tool_use_summary(&self, input: Option<&serde_json::Value>) -> Option<String> {
99 <GrepTool>::get_tool_use_summary(self, input)
100 }
101 fn render_tool_result_message(&self, content: &serde_json::Value) -> Option<String> {
102 <GrepTool>::render_tool_result_message(self, content)
103 }
104}
105impl ToolRender for FileEditTool {
106 fn user_facing_name(&self, input: Option<&serde_json::Value>) -> String {
107 <FileEditTool>::user_facing_name(self, input)
108 }
109 fn get_tool_use_summary(&self, input: Option<&serde_json::Value>) -> Option<String> {
110 <FileEditTool>::get_tool_use_summary(self, input)
111 }
112 fn render_tool_result_message(&self, content: &serde_json::Value) -> Option<String> {
113 <FileEditTool>::render_tool_result_message(self, content)
114 }
115}
116impl ToolRender for SkillTool {
117 fn user_facing_name(&self, input: Option<&serde_json::Value>) -> String {
118 <SkillTool>::user_facing_name(self, input)
119 }
120 fn get_tool_use_summary(&self, input: Option<&serde_json::Value>) -> Option<String> {
121 <SkillTool>::get_tool_use_summary(self, input)
122 }
123 fn render_tool_result_message(&self, content: &serde_json::Value) -> Option<String> {
124 <SkillTool>::render_tool_result_message(self, content)
125 }
126}
127impl ToolRender for MonitorTool {
128 fn user_facing_name(&self, input: Option<&serde_json::Value>) -> String {
129 <MonitorTool>::user_facing_name(self, input)
130 }
131 fn get_tool_use_summary(&self, input: Option<&serde_json::Value>) -> Option<String> {
132 <MonitorTool>::get_tool_use_summary(self, input)
133 }
134 fn render_tool_result_message(&self, content: &serde_json::Value) -> Option<String> {
135 <MonitorTool>::render_tool_result_message(self, content)
136 }
137}
138impl ToolRender for SendUserFileTool {
139 fn user_facing_name(&self, input: Option<&serde_json::Value>) -> String {
140 <SendUserFileTool>::user_facing_name(self, input)
141 }
142 fn get_tool_use_summary(&self, input: Option<&serde_json::Value>) -> Option<String> {
143 <SendUserFileTool>::get_tool_use_summary(self, input)
144 }
145 fn render_tool_result_message(&self, content: &serde_json::Value) -> Option<String> {
146 <SendUserFileTool>::render_tool_result_message(self, content)
147 }
148}
149impl ToolRender for WebBrowserTool {
150 fn user_facing_name(&self, input: Option<&serde_json::Value>) -> String {
151 <WebBrowserTool>::user_facing_name(self, input)
152 }
153 fn get_tool_use_summary(&self, input: Option<&serde_json::Value>) -> Option<String> {
154 <WebBrowserTool>::get_tool_use_summary(self, input)
155 }
156 fn render_tool_result_message(&self, content: &serde_json::Value) -> Option<String> {
157 <WebBrowserTool>::render_tool_result_message(self, content)
158 }
159}
160impl ToolRender for WebFetchTool {
161 fn user_facing_name(&self, input: Option<&serde_json::Value>) -> String {
162 <WebFetchTool>::user_facing_name(self, input)
163 }
164 fn get_tool_use_summary(&self, input: Option<&serde_json::Value>) -> Option<String> {
165 <WebFetchTool>::get_tool_use_summary(self, input)
166 }
167 fn render_tool_result_message(&self, content: &serde_json::Value) -> Option<String> {
168 <WebFetchTool>::render_tool_result_message(self, content)
169 }
170}
171impl ToolRender for WebSearchTool {
172 fn user_facing_name(&self, input: Option<&serde_json::Value>) -> String {
173 <WebSearchTool>::user_facing_name(self, input)
174 }
175 fn get_tool_use_summary(&self, input: Option<&serde_json::Value>) -> Option<String> {
176 <WebSearchTool>::get_tool_use_summary(self, input)
177 }
178 fn render_tool_result_message(&self, content: &serde_json::Value) -> Option<String> {
179 <WebSearchTool>::render_tool_result_message(self, content)
180 }
181}
182impl ToolRender for NotebookEditTool {
183 fn user_facing_name(&self, input: Option<&serde_json::Value>) -> String {
184 <NotebookEditTool>::user_facing_name(self, input)
185 }
186 fn get_tool_use_summary(&self, input: Option<&serde_json::Value>) -> Option<String> {
187 <NotebookEditTool>::get_tool_use_summary(self, input)
188 }
189 fn render_tool_result_message(&self, content: &serde_json::Value) -> Option<String> {
190 <NotebookEditTool>::render_tool_result_message(self, content)
191 }
192}
193impl ToolRender for TaskCreateTool {
194 fn user_facing_name(&self, input: Option<&serde_json::Value>) -> String {
195 <TaskCreateTool>::user_facing_name(self, input)
196 }
197 fn get_tool_use_summary(&self, input: Option<&serde_json::Value>) -> Option<String> {
198 <TaskCreateTool>::get_tool_use_summary(self, input)
199 }
200 fn render_tool_result_message(&self, content: &serde_json::Value) -> Option<String> {
201 <TaskCreateTool>::render_tool_result_message(self, content)
202 }
203}
204impl ToolRender for TaskListTool {
205 fn user_facing_name(&self, input: Option<&serde_json::Value>) -> String {
206 <TaskListTool>::user_facing_name(self, input)
207 }
208 fn get_tool_use_summary(&self, input: Option<&serde_json::Value>) -> Option<String> {
209 <TaskListTool>::get_tool_use_summary(self, input)
210 }
211 fn render_tool_result_message(&self, content: &serde_json::Value) -> Option<String> {
212 <TaskListTool>::render_tool_result_message(self, content)
213 }
214}
215impl ToolRender for TaskUpdateTool {
216 fn user_facing_name(&self, input: Option<&serde_json::Value>) -> String {
217 <TaskUpdateTool>::user_facing_name(self, input)
218 }
219 fn get_tool_use_summary(&self, input: Option<&serde_json::Value>) -> Option<String> {
220 <TaskUpdateTool>::get_tool_use_summary(self, input)
221 }
222 fn render_tool_result_message(&self, content: &serde_json::Value) -> Option<String> {
223 <TaskUpdateTool>::render_tool_result_message(self, content)
224 }
225}
226impl ToolRender for TaskGetTool {
227 fn user_facing_name(&self, input: Option<&serde_json::Value>) -> String {
228 <TaskGetTool>::user_facing_name(self, input)
229 }
230 fn get_tool_use_summary(&self, input: Option<&serde_json::Value>) -> Option<String> {
231 <TaskGetTool>::get_tool_use_summary(self, input)
232 }
233 fn render_tool_result_message(&self, content: &serde_json::Value) -> Option<String> {
234 <TaskGetTool>::render_tool_result_message(self, content)
235 }
236}
237impl ToolRender for TaskOutputTool {
238 fn user_facing_name(&self, input: Option<&serde_json::Value>) -> String {
239 <TaskOutputTool>::user_facing_name(self, input)
240 }
241 fn get_tool_use_summary(&self, input: Option<&serde_json::Value>) -> Option<String> {
242 <TaskOutputTool>::get_tool_use_summary(self, input)
243 }
244 fn render_tool_result_message(&self, content: &serde_json::Value) -> Option<String> {
245 <TaskOutputTool>::render_tool_result_message(self, content)
246 }
247}
248impl ToolRender for TodoWriteTool {
249 fn user_facing_name(&self, input: Option<&serde_json::Value>) -> String {
250 <TodoWriteTool>::user_facing_name(self, input)
251 }
252 fn get_tool_use_summary(&self, input: Option<&serde_json::Value>) -> Option<String> {
253 <TodoWriteTool>::get_tool_use_summary(self, input)
254 }
255 fn render_tool_result_message(&self, content: &serde_json::Value) -> Option<String> {
256 <TodoWriteTool>::render_tool_result_message(self, content)
257 }
258}
259impl ToolRender for CronCreateTool {
260 fn user_facing_name(&self, input: Option<&serde_json::Value>) -> String {
261 <CronCreateTool>::user_facing_name(self, input)
262 }
263 fn get_tool_use_summary(&self, input: Option<&serde_json::Value>) -> Option<String> {
264 <CronCreateTool>::get_tool_use_summary(self, input)
265 }
266 fn render_tool_result_message(&self, content: &serde_json::Value) -> Option<String> {
267 <CronCreateTool>::render_tool_result_message(self, content)
268 }
269}
270impl ToolRender for CronDeleteTool {
271 fn user_facing_name(&self, input: Option<&serde_json::Value>) -> String {
272 <CronDeleteTool>::user_facing_name(self, input)
273 }
274 fn get_tool_use_summary(&self, input: Option<&serde_json::Value>) -> Option<String> {
275 <CronDeleteTool>::get_tool_use_summary(self, input)
276 }
277 fn render_tool_result_message(&self, content: &serde_json::Value) -> Option<String> {
278 <CronDeleteTool>::render_tool_result_message(self, content)
279 }
280}
281impl ToolRender for CronListTool {
282 fn user_facing_name(&self, input: Option<&serde_json::Value>) -> String {
283 <CronListTool>::user_facing_name(self, input)
284 }
285 fn get_tool_use_summary(&self, input: Option<&serde_json::Value>) -> Option<String> {
286 <CronListTool>::get_tool_use_summary(self, input)
287 }
288 fn render_tool_result_message(&self, content: &serde_json::Value) -> Option<String> {
289 <CronListTool>::render_tool_result_message(self, content)
290 }
291}
292impl ToolRender for ConfigTool {
293 fn user_facing_name(&self, input: Option<&serde_json::Value>) -> String {
294 <ConfigTool>::user_facing_name(self, input)
295 }
296 fn get_tool_use_summary(&self, input: Option<&serde_json::Value>) -> Option<String> {
297 <ConfigTool>::get_tool_use_summary(self, input)
298 }
299 fn render_tool_result_message(&self, content: &serde_json::Value) -> Option<String> {
300 <ConfigTool>::render_tool_result_message(self, content)
301 }
302}
303impl ToolRender for EnterWorktreeTool {
304 fn user_facing_name(&self, input: Option<&serde_json::Value>) -> String {
305 <EnterWorktreeTool>::user_facing_name(self, input)
306 }
307 fn get_tool_use_summary(&self, input: Option<&serde_json::Value>) -> Option<String> {
308 <EnterWorktreeTool>::get_tool_use_summary(self, input)
309 }
310 fn render_tool_result_message(&self, content: &serde_json::Value) -> Option<String> {
311 <EnterWorktreeTool>::render_tool_result_message(self, content)
312 }
313}
314impl ToolRender for ExitWorktreeTool {
315 fn user_facing_name(&self, input: Option<&serde_json::Value>) -> String {
316 <ExitWorktreeTool>::user_facing_name(self, input)
317 }
318 fn get_tool_use_summary(&self, input: Option<&serde_json::Value>) -> Option<String> {
319 <ExitWorktreeTool>::get_tool_use_summary(self, input)
320 }
321 fn render_tool_result_message(&self, content: &serde_json::Value) -> Option<String> {
322 <ExitWorktreeTool>::render_tool_result_message(self, content)
323 }
324}
325impl ToolRender for EnterPlanModeTool {
326 fn user_facing_name(&self, input: Option<&serde_json::Value>) -> String {
327 <EnterPlanModeTool>::user_facing_name(self, input)
328 }
329 fn get_tool_use_summary(&self, input: Option<&serde_json::Value>) -> Option<String> {
330 <EnterPlanModeTool>::get_tool_use_summary(self, input)
331 }
332 fn render_tool_result_message(&self, content: &serde_json::Value) -> Option<String> {
333 <EnterPlanModeTool>::render_tool_result_message(self, content)
334 }
335}
336impl ToolRender for ExitPlanModeTool {
337 fn user_facing_name(&self, input: Option<&serde_json::Value>) -> String {
338 <ExitPlanModeTool>::user_facing_name(self, input)
339 }
340 fn get_tool_use_summary(&self, input: Option<&serde_json::Value>) -> Option<String> {
341 <ExitPlanModeTool>::get_tool_use_summary(self, input)
342 }
343 fn render_tool_result_message(&self, content: &serde_json::Value) -> Option<String> {
344 <ExitPlanModeTool>::render_tool_result_message(self, content)
345 }
346}
347impl ToolRender for AskUserQuestionTool {
348 fn user_facing_name(&self, input: Option<&serde_json::Value>) -> String {
349 <AskUserQuestionTool>::user_facing_name(self, input)
350 }
351 fn get_tool_use_summary(&self, input: Option<&serde_json::Value>) -> Option<String> {
352 <AskUserQuestionTool>::get_tool_use_summary(self, input)
353 }
354 fn render_tool_result_message(&self, content: &serde_json::Value) -> Option<String> {
355 <AskUserQuestionTool>::render_tool_result_message(self, content)
356 }
357}
358impl ToolRender for ToolSearchTool {
359 fn user_facing_name(&self, input: Option<&serde_json::Value>) -> String {
360 <ToolSearchTool>::user_facing_name(self, input)
361 }
362 fn get_tool_use_summary(&self, input: Option<&serde_json::Value>) -> Option<String> {
363 <ToolSearchTool>::get_tool_use_summary(self, input)
364 }
365 fn render_tool_result_message(&self, content: &serde_json::Value) -> Option<String> {
366 <ToolSearchTool>::render_tool_result_message(self, content)
367 }
368}
369impl ToolRender for TeamCreateTool {
370 fn user_facing_name(&self, input: Option<&serde_json::Value>) -> String {
371 <TeamCreateTool>::user_facing_name(self, input)
372 }
373 fn get_tool_use_summary(&self, input: Option<&serde_json::Value>) -> Option<String> {
374 <TeamCreateTool>::get_tool_use_summary(self, input)
375 }
376 fn render_tool_result_message(&self, content: &serde_json::Value) -> Option<String> {
377 <TeamCreateTool>::render_tool_result_message(self, content)
378 }
379}
380impl ToolRender for TeamDeleteTool {
381 fn user_facing_name(&self, input: Option<&serde_json::Value>) -> String {
382 <TeamDeleteTool>::user_facing_name(self, input)
383 }
384 fn get_tool_use_summary(&self, input: Option<&serde_json::Value>) -> Option<String> {
385 <TeamDeleteTool>::get_tool_use_summary(self, input)
386 }
387 fn render_tool_result_message(&self, content: &serde_json::Value) -> Option<String> {
388 <TeamDeleteTool>::render_tool_result_message(self, content)
389 }
390}
391impl ToolRender for SendMessageTool {
392 fn user_facing_name(&self, input: Option<&serde_json::Value>) -> String {
393 <SendMessageTool>::user_facing_name(self, input)
394 }
395 fn get_tool_use_summary(&self, input: Option<&serde_json::Value>) -> Option<String> {
396 <SendMessageTool>::get_tool_use_summary(self, input)
397 }
398 fn render_tool_result_message(&self, content: &serde_json::Value) -> Option<String> {
399 <SendMessageTool>::render_tool_result_message(self, content)
400 }
401}
402impl ToolRender for SleepTool {
403 fn user_facing_name(&self, input: Option<&serde_json::Value>) -> String {
404 <SleepTool>::user_facing_name(self, input)
405 }
406 fn get_tool_use_summary(&self, input: Option<&serde_json::Value>) -> Option<String> {
407 <SleepTool>::get_tool_use_summary(self, input)
408 }
409 fn render_tool_result_message(&self, content: &serde_json::Value) -> Option<String> {
410 <SleepTool>::render_tool_result_message(self, content)
411 }
412}
413impl ToolRender for PowerShellTool {
414 fn user_facing_name(&self, input: Option<&serde_json::Value>) -> String {
415 <PowerShellTool>::user_facing_name(self, input)
416 }
417 fn get_tool_use_summary(&self, input: Option<&serde_json::Value>) -> Option<String> {
418 <PowerShellTool>::get_tool_use_summary(self, input)
419 }
420 fn render_tool_result_message(&self, content: &serde_json::Value) -> Option<String> {
421 <PowerShellTool>::render_tool_result_message(self, content)
422 }
423}
424impl ToolRender for LSPTool {
425 fn user_facing_name(&self, input: Option<&serde_json::Value>) -> String {
426 <LSPTool>::user_facing_name(self, input)
427 }
428 fn get_tool_use_summary(&self, input: Option<&serde_json::Value>) -> Option<String> {
429 <LSPTool>::get_tool_use_summary(self, input)
430 }
431 fn render_tool_result_message(&self, content: &serde_json::Value) -> Option<String> {
432 <LSPTool>::render_tool_result_message(self, content)
433 }
434}
435impl ToolRender for RemoteTriggerTool {
436 fn user_facing_name(&self, input: Option<&serde_json::Value>) -> String {
437 <RemoteTriggerTool>::user_facing_name(self, input)
438 }
439 fn get_tool_use_summary(&self, input: Option<&serde_json::Value>) -> Option<String> {
440 <RemoteTriggerTool>::get_tool_use_summary(self, input)
441 }
442 fn render_tool_result_message(&self, content: &serde_json::Value) -> Option<String> {
443 <RemoteTriggerTool>::render_tool_result_message(self, content)
444 }
445}
446impl ToolRender for ListMcpResourcesTool {
447 fn user_facing_name(&self, input: Option<&serde_json::Value>) -> String {
448 <ListMcpResourcesTool>::user_facing_name(self, input)
449 }
450 fn get_tool_use_summary(&self, input: Option<&serde_json::Value>) -> Option<String> {
451 <ListMcpResourcesTool>::get_tool_use_summary(self, input)
452 }
453 fn render_tool_result_message(&self, content: &serde_json::Value) -> Option<String> {
454 <ListMcpResourcesTool>::render_tool_result_message(self, content)
455 }
456}
457impl ToolRender for ReadMcpResourceTool {
458 fn user_facing_name(&self, input: Option<&serde_json::Value>) -> String {
459 <ReadMcpResourceTool>::user_facing_name(self, input)
460 }
461 fn get_tool_use_summary(&self, input: Option<&serde_json::Value>) -> Option<String> {
462 <ReadMcpResourceTool>::get_tool_use_summary(self, input)
463 }
464 fn render_tool_result_message(&self, content: &serde_json::Value) -> Option<String> {
465 <ReadMcpResourceTool>::render_tool_result_message(self, content)
466 }
467}
468impl ToolRender for McpTool {
469 fn user_facing_name(&self, input: Option<&serde_json::Value>) -> String {
470 <McpTool>::user_facing_name(self, input)
471 }
472 fn get_tool_use_summary(&self, input: Option<&serde_json::Value>) -> Option<String> {
473 <McpTool>::get_tool_use_summary(self, input)
474 }
475 fn render_tool_result_message(&self, content: &serde_json::Value) -> Option<String> {
476 <McpTool>::render_tool_result_message(self, content)
477 }
478}
479impl ToolRender for McpAuthTool {
480 fn user_facing_name(&self, input: Option<&serde_json::Value>) -> String {
481 <McpAuthTool>::user_facing_name(self, input)
482 }
483 fn get_tool_use_summary(&self, input: Option<&serde_json::Value>) -> Option<String> {
484 <McpAuthTool>::get_tool_use_summary(self, input)
485 }
486 fn render_tool_result_message(&self, content: &serde_json::Value) -> Option<String> {
487 <McpAuthTool>::render_tool_result_message(self, content)
488 }
489}
490impl ToolRender for BriefTool {
491 fn user_facing_name(&self, input: Option<&serde_json::Value>) -> String {
492 <BriefTool>::user_facing_name(self, input)
493 }
494 fn get_tool_use_summary(&self, input: Option<&serde_json::Value>) -> Option<String> {
495 <BriefTool>::get_tool_use_summary(self, input)
496 }
497 fn render_tool_result_message(&self, content: &serde_json::Value) -> Option<String> {
498 <BriefTool>::render_tool_result_message(self, content)
499 }
500}
501impl ToolRender for SyntheticOutputTool {
502 fn user_facing_name(&self, input: Option<&serde_json::Value>) -> String {
503 <SyntheticOutputTool>::user_facing_name(self, input)
504 }
505 fn get_tool_use_summary(&self, input: Option<&serde_json::Value>) -> Option<String> {
506 <SyntheticOutputTool>::get_tool_use_summary(self, input)
507 }
508 fn render_tool_result_message(&self, content: &serde_json::Value) -> Option<String> {
509 <SyntheticOutputTool>::render_tool_result_message(self, content)
510 }
511}
512
513fn make_render_fns<T: ToolRender + 'static>(tool: T) -> crate::query_engine::ToolRenderFns {
516 let tool = Arc::new(tool);
517 let t2 = Arc::clone(&tool);
518 let t3 = Arc::clone(&tool);
519 crate::query_engine::ToolRenderFns {
520 user_facing_name: Arc::new(move |input| tool.user_facing_name(input)),
521 get_tool_use_summary: Some(Arc::new(move |input| t2.get_tool_use_summary(input))),
522 get_activity_description: None,
523 render_tool_result_message: Some(Arc::new(
524 move |content, _progress, _options| t3.render_tool_result_message(content),
525 )),
526 }
527}
528
529pub(crate) fn register_all_tool_executors(engine: &mut QueryEngine) {
531 type BoxFuture<T> = std::pin::Pin<Box<dyn std::future::Future<Output = T> + Send>>;
532
533 let bash_executor = move |input: serde_json::Value,
535 ctx: &ToolContext|
536 -> BoxFuture<Result<ToolResult, AgentError>> {
537 let tool_clone = BashTool::new();
538 let cwd = ctx.cwd.clone();
539 let abort_signal = ctx.abort_signal.clone();
540 Box::pin(async move {
541 let ctx2 = ToolContext {
542 cwd,
543 abort_signal: abort_signal.clone(),
544 };
545 tool_clone.execute(input, &ctx2).await
546 })
547 };
548 let bash_tool = BashTool::new();
549 let bash_rf = self::make_render_fns(bash_tool);
550 engine.register_tool_with_render("Bash".to_string(), bash_executor, bash_rf);
551
552 engine.register_tool_backfill(
554 "Read".to_string(),
555 |input: &mut serde_json::Value| {
556 if let Some(fp) = input.get("file_path").and_then(|v| v.as_str()) {
557 let expanded = crate::utils::path::expand_path(fp);
558 if let Some(obj) = input.as_object_mut() {
559 obj.insert("file_path".to_string(), serde_json::json!(expanded));
560 }
561 }
562 },
563 );
564
565 let read_executor = move |input: serde_json::Value,
567 ctx: &ToolContext|
568 -> BoxFuture<Result<ToolResult, AgentError>> {
569 let tool_clone = ReadTool::new();
570 let cwd = ctx.cwd.clone();
571 let abort_signal = ctx.abort_signal.clone();
572 Box::pin(async move {
573 let ctx2 = ToolContext {
574 cwd,
575 abort_signal: abort_signal.clone(),
576 };
577 tool_clone.execute(input, &ctx2).await
578 })
579 };
580 let read_tool = ReadTool::new();
581 let read_rf = self::make_render_fns(read_tool);
582 engine.register_tool_with_render("Read".to_string(), read_executor, read_rf);
583
584 let write_executor = move |input: serde_json::Value,
586 ctx: &ToolContext|
587 -> BoxFuture<Result<ToolResult, AgentError>> {
588 let tool_clone = WriteTool::new();
589 let cwd = ctx.cwd.clone();
590 let abort_signal = ctx.abort_signal.clone();
591 Box::pin(async move {
592 let ctx2 = ToolContext {
593 cwd,
594 abort_signal: abort_signal.clone(),
595 };
596 tool_clone.execute(input, &ctx2).await
597 })
598 };
599 let write_tool = WriteTool::new();
600 let write_rf = self::make_render_fns(write_tool);
601 engine.register_tool_with_render("Write".to_string(), write_executor, write_rf);
602 engine.register_tool_backfill(
604 "Write".to_string(),
605 |input: &mut serde_json::Value| {
606 if let Some(fp) = input.get("file_path").and_then(|v| v.as_str()) {
607 let expanded = crate::utils::path::expand_path(fp);
608 if let Some(obj) = input.as_object_mut() {
609 obj.insert("file_path".to_string(), serde_json::json!(expanded));
610 }
611 }
612 },
613 );
614
615 let glob_executor = move |input: serde_json::Value,
617 ctx: &ToolContext|
618 -> BoxFuture<Result<ToolResult, AgentError>> {
619 let tool_clone = GlobTool::new();
620 let cwd = ctx.cwd.clone();
621 let abort_signal = ctx.abort_signal.clone();
622 Box::pin(async move {
623 let ctx2 = ToolContext {
624 cwd,
625 abort_signal: abort_signal.clone(),
626 };
627 tool_clone.execute(input, &ctx2).await
628 })
629 };
630 let glob_tool = GlobTool::new();
631 let glob_rf = self::make_render_fns(glob_tool);
632 engine.register_tool_with_render("Glob".to_string(), glob_executor, glob_rf);
633
634 let grep_executor = move |input: serde_json::Value,
636 ctx: &ToolContext|
637 -> BoxFuture<Result<ToolResult, AgentError>> {
638 let tool_clone = GrepTool::new();
639 let cwd = ctx.cwd.clone();
640 let abort_signal = ctx.abort_signal.clone();
641 Box::pin(async move {
642 let ctx2 = ToolContext {
643 cwd,
644 abort_signal: abort_signal.clone(),
645 };
646 tool_clone.execute(input, &ctx2).await
647 })
648 };
649 let grep_tool = GrepTool::new();
650 let grep_rf = self::make_render_fns(grep_tool);
651 engine.register_tool_with_render("Grep".to_string(), grep_executor, grep_rf);
652
653 let edit_executor = move |input: serde_json::Value,
655 ctx: &ToolContext|
656 -> BoxFuture<Result<ToolResult, AgentError>> {
657 let tool_clone = FileEditTool::new();
658 let cwd = ctx.cwd.clone();
659 let abort_signal = ctx.abort_signal.clone();
660 Box::pin(async move {
661 let ctx2 = ToolContext {
662 cwd,
663 abort_signal: abort_signal.clone(),
664 };
665 tool_clone.execute(input, &ctx2).await
666 })
667 };
668 let edit_rf = make_render_fns(FileEditTool::new());
669 engine.register_tool_with_render("FileEdit".to_string(), edit_executor, edit_rf);
670 engine.register_tool_backfill(
672 "FileEdit".to_string(),
673 |input: &mut serde_json::Value| {
674 if let Some(fp) = input.get("file_path").and_then(|v| v.as_str()) {
675 let expanded = crate::utils::path::expand_path(fp);
676 if let Some(obj) = input.as_object_mut() {
677 obj.insert("file_path".to_string(), serde_json::json!(expanded));
678 }
679 }
680 },
681 );
682
683 use std::path::Path;
685 register_skills_from_dir(Path::new("examples/skills"));
686
687 let skill_executor = move |input: serde_json::Value,
688 ctx: &ToolContext|
689 -> BoxFuture<Result<ToolResult, AgentError>> {
690 let tool_clone = SkillTool::new();
691 let cwd = ctx.cwd.clone();
692 let abort_signal = ctx.abort_signal.clone();
693 Box::pin(async move {
694 let ctx2 = ToolContext {
695 cwd,
696 abort_signal: abort_signal.clone(),
697 };
698 tool_clone.execute(input, &ctx2).await
699 })
700 };
701 let skill_tool = SkillTool::new();
702 let skill_rf = self::make_render_fns(skill_tool);
703 engine.register_tool_with_render("Skill".to_string(), skill_executor, skill_rf);
704
705 let monitor_executor = move |input: serde_json::Value,
707 ctx: &ToolContext|
708 -> BoxFuture<Result<ToolResult, AgentError>> {
709 let tool_clone = MonitorTool::new();
710 let cwd = ctx.cwd.clone();
711 let abort_signal = ctx.abort_signal.clone();
712 Box::pin(async move {
713 let ctx2 = ToolContext {
714 cwd,
715 abort_signal: abort_signal.clone(),
716 };
717 tool_clone.execute(input, &ctx2).await
718 })
719 };
720 let monitor_tool = MonitorTool::new();
721 let monitor_rf = self::make_render_fns(monitor_tool);
722 engine.register_tool_with_render("Monitor".to_string(), monitor_executor, monitor_rf);
723
724 let send_user_file_executor = move |input: serde_json::Value,
726 ctx: &ToolContext|
727 -> BoxFuture<Result<ToolResult, AgentError>> {
728 let tool_clone = SendUserFileTool::new();
729 let cwd = ctx.cwd.clone();
730 let abort_signal = ctx.abort_signal.clone();
731 Box::pin(async move {
732 let ctx2 = ToolContext {
733 cwd,
734 abort_signal: abort_signal.clone(),
735 };
736 tool_clone.execute(input, &ctx2).await
737 })
738 };
739 let send_user_file_tool = SendUserFileTool::new();
740 let send_user_file_rf = self::make_render_fns(send_user_file_tool);
741 engine.register_tool_with_render("send_user_file".to_string(), send_user_file_executor, send_user_file_rf);
742
743 let web_browser_executor = move |input: serde_json::Value,
745 ctx: &ToolContext|
746 -> BoxFuture<Result<ToolResult, AgentError>> {
747 let tool_clone = WebBrowserTool::new();
748 let cwd = ctx.cwd.clone();
749 let abort_signal = ctx.abort_signal.clone();
750 Box::pin(async move {
751 let ctx2 = ToolContext {
752 cwd,
753 abort_signal: abort_signal.clone(),
754 };
755 tool_clone.execute(input, &ctx2).await
756 })
757 };
758 let web_browser_tool = WebBrowserTool::new();
759 let web_browser_rf = self::make_render_fns(web_browser_tool);
760 engine.register_tool_with_render("WebBrowser".to_string(), web_browser_executor, web_browser_rf);
761
762 let web_fetch_executor = move |input: serde_json::Value,
764 ctx: &ToolContext|
765 -> BoxFuture<Result<ToolResult, AgentError>> {
766 let tool_clone = WebFetchTool::new();
767 let cwd = ctx.cwd.clone();
768 let abort_signal = ctx.abort_signal.clone();
769 Box::pin(async move {
770 let ctx2 = ToolContext {
771 cwd,
772 abort_signal: abort_signal.clone(),
773 };
774 tool_clone.execute(input, &ctx2).await
775 })
776 };
777 let web_fetch_tool = WebFetchTool::new();
778 let web_fetch_rf = self::make_render_fns(web_fetch_tool);
779 engine.register_tool_with_render("WebFetch".to_string(), web_fetch_executor, web_fetch_rf);
780
781 let web_search_executor = move |input: serde_json::Value,
783 ctx: &ToolContext|
784 -> BoxFuture<Result<ToolResult, AgentError>> {
785 let tool_clone = WebSearchTool::new();
786 let cwd = ctx.cwd.clone();
787 let abort_signal = ctx.abort_signal.clone();
788 Box::pin(async move {
789 let ctx2 = ToolContext {
790 cwd,
791 abort_signal: abort_signal.clone(),
792 };
793 tool_clone.execute(input, &ctx2).await
794 })
795 };
796 let web_search_tool = WebSearchTool::new();
797 let web_search_rf = self::make_render_fns(web_search_tool);
798 engine.register_tool_with_render("WebSearch".to_string(), web_search_executor, web_search_rf);
799
800 let notebook_edit_executor = move |input: serde_json::Value,
802 ctx: &ToolContext|
803 -> BoxFuture<Result<ToolResult, AgentError>> {
804 let tool_clone = NotebookEditTool::new();
805 let cwd = ctx.cwd.clone();
806 let abort_signal = ctx.abort_signal.clone();
807 Box::pin(async move {
808 let ctx2 = ToolContext {
809 cwd,
810 abort_signal: abort_signal.clone(),
811 };
812 tool_clone.execute(input, &ctx2).await
813 })
814 };
815 let notebook_edit_tool = NotebookEditTool::new();
816 let notebook_edit_rf = self::make_render_fns(notebook_edit_tool);
817 engine.register_tool_with_render("NotebookEdit".to_string(), notebook_edit_executor, notebook_edit_rf);
818
819 let task_create_executor = move |input: serde_json::Value,
821 ctx: &ToolContext|
822 -> BoxFuture<Result<ToolResult, AgentError>> {
823 let tool_clone = TaskCreateTool::new();
824 let cwd = ctx.cwd.clone();
825 let abort_signal = ctx.abort_signal.clone();
826 Box::pin(async move {
827 let ctx2 = ToolContext {
828 cwd,
829 abort_signal: abort_signal.clone(),
830 };
831 tool_clone.execute(input, &ctx2).await
832 })
833 };
834 let task_create_tool = TaskCreateTool::new();
835 let task_create_rf = self::make_render_fns(task_create_tool);
836 engine.register_tool_with_render("TaskCreate".to_string(), task_create_executor, task_create_rf);
837
838 let task_list_executor = move |input: serde_json::Value,
840 ctx: &ToolContext|
841 -> BoxFuture<Result<ToolResult, AgentError>> {
842 let tool_clone = TaskListTool::new();
843 let cwd = ctx.cwd.clone();
844 let abort_signal = ctx.abort_signal.clone();
845 Box::pin(async move {
846 let ctx2 = ToolContext {
847 cwd,
848 abort_signal: abort_signal.clone(),
849 };
850 tool_clone.execute(input, &ctx2).await
851 })
852 };
853 let task_list_tool = TaskListTool::new();
854 let task_list_rf = self::make_render_fns(task_list_tool);
855 engine.register_tool_with_render("TaskList".to_string(), task_list_executor, task_list_rf);
856
857 let task_update_executor = move |input: serde_json::Value,
859 ctx: &ToolContext|
860 -> BoxFuture<Result<ToolResult, AgentError>> {
861 let tool_clone = TaskUpdateTool::new();
862 let cwd = ctx.cwd.clone();
863 let abort_signal = ctx.abort_signal.clone();
864 Box::pin(async move {
865 let ctx2 = ToolContext {
866 cwd,
867 abort_signal: abort_signal.clone(),
868 };
869 tool_clone.execute(input, &ctx2).await
870 })
871 };
872 let task_update_tool = TaskUpdateTool::new();
873 let task_update_rf = self::make_render_fns(task_update_tool);
874 engine.register_tool_with_render("TaskUpdate".to_string(), task_update_executor, task_update_rf);
875
876 let task_get_executor = move |input: serde_json::Value,
878 ctx: &ToolContext|
879 -> BoxFuture<Result<ToolResult, AgentError>> {
880 let tool_clone = TaskGetTool::new();
881 let cwd = ctx.cwd.clone();
882 let abort_signal = ctx.abort_signal.clone();
883 Box::pin(async move {
884 let ctx2 = ToolContext {
885 cwd,
886 abort_signal: abort_signal.clone(),
887 };
888 tool_clone.execute(input, &ctx2).await
889 })
890 };
891 let task_get_tool = TaskGetTool::new();
892 let task_get_rf = self::make_render_fns(task_get_tool);
893 engine.register_tool_with_render("TaskGet".to_string(), task_get_executor, task_get_rf);
894
895 let task_output_executor = move |input: serde_json::Value,
897 ctx: &ToolContext|
898 -> BoxFuture<Result<ToolResult, AgentError>> {
899 let tool_clone = TaskOutputTool::new();
900 let cwd = ctx.cwd.clone();
901 let abort_signal = ctx.abort_signal.clone();
902 Box::pin(async move {
903 let ctx2 = ToolContext {
904 cwd,
905 abort_signal: abort_signal.clone(),
906 };
907 tool_clone.execute(input, &ctx2).await
908 })
909 };
910 let task_output_tool = TaskOutputTool::new();
911 let task_output_rf = self::make_render_fns(task_output_tool);
912 engine.register_tool_with_render("TaskOutput".to_string(), task_output_executor, task_output_rf);
913
914 let todo_write_executor = move |input: serde_json::Value,
916 ctx: &ToolContext|
917 -> BoxFuture<Result<ToolResult, AgentError>> {
918 let tool_clone = TodoWriteTool::new();
919 let cwd = ctx.cwd.clone();
920 let abort_signal = ctx.abort_signal.clone();
921 Box::pin(async move {
922 let ctx2 = ToolContext {
923 cwd,
924 abort_signal: abort_signal.clone(),
925 };
926 tool_clone.execute(input, &ctx2).await
927 })
928 };
929 let todo_write_tool = TodoWriteTool::new();
930 let todo_write_rf = self::make_render_fns(todo_write_tool);
931 engine.register_tool_with_render("TodoWrite".to_string(), todo_write_executor, todo_write_rf);
932
933 let cron_create_executor = move |input: serde_json::Value,
935 ctx: &ToolContext|
936 -> BoxFuture<Result<ToolResult, AgentError>> {
937 let tool_clone = CronCreateTool::new();
938 let cwd = ctx.cwd.clone();
939 let abort_signal = ctx.abort_signal.clone();
940 Box::pin(async move {
941 let ctx2 = ToolContext {
942 cwd,
943 abort_signal: abort_signal.clone(),
944 };
945 tool_clone.execute(input, &ctx2).await
946 })
947 };
948 let cron_create_tool = CronCreateTool::new();
949 let cron_create_rf = self::make_render_fns(cron_create_tool);
950 engine.register_tool_with_render("CronCreate".to_string(), cron_create_executor, cron_create_rf);
951
952 let cron_delete_executor = move |input: serde_json::Value,
954 ctx: &ToolContext|
955 -> BoxFuture<Result<ToolResult, AgentError>> {
956 let tool_clone = CronDeleteTool::new();
957 let cwd = ctx.cwd.clone();
958 let abort_signal = ctx.abort_signal.clone();
959 Box::pin(async move {
960 let ctx2 = ToolContext {
961 cwd,
962 abort_signal: abort_signal.clone(),
963 };
964 tool_clone.execute(input, &ctx2).await
965 })
966 };
967 let cron_delete_tool = CronDeleteTool::new();
968 let cron_delete_rf = self::make_render_fns(cron_delete_tool);
969 engine.register_tool_with_render("CronDelete".to_string(), cron_delete_executor, cron_delete_rf);
970
971 let cron_list_executor = move |input: serde_json::Value,
973 ctx: &ToolContext|
974 -> BoxFuture<Result<ToolResult, AgentError>> {
975 let tool_clone = CronListTool::new();
976 let cwd = ctx.cwd.clone();
977 let abort_signal = ctx.abort_signal.clone();
978 Box::pin(async move {
979 let ctx2 = ToolContext {
980 cwd,
981 abort_signal: abort_signal.clone(),
982 };
983 tool_clone.execute(input, &ctx2).await
984 })
985 };
986 let cron_list_tool = CronListTool::new();
987 let cron_list_rf = self::make_render_fns(cron_list_tool);
988 engine.register_tool_with_render("CronList".to_string(), cron_list_executor, cron_list_rf);
989
990 let config_executor = move |input: serde_json::Value,
992 ctx: &ToolContext|
993 -> BoxFuture<Result<ToolResult, AgentError>> {
994 let tool_clone = ConfigTool::new();
995 let cwd = ctx.cwd.clone();
996 let abort_signal = ctx.abort_signal.clone();
997 Box::pin(async move {
998 let ctx2 = ToolContext {
999 cwd,
1000 abort_signal: abort_signal.clone(),
1001 };
1002 tool_clone.execute(input, &ctx2).await
1003 })
1004 };
1005 let config_tool = ConfigTool::new();
1006 let config_rf = self::make_render_fns(config_tool);
1007 engine.register_tool_with_render("Config".to_string(), config_executor, config_rf);
1008
1009 let enter_worktree_executor = move |input: serde_json::Value,
1011 ctx: &ToolContext|
1012 -> BoxFuture<Result<ToolResult, AgentError>> {
1013 let tool_clone = EnterWorktreeTool::new();
1014 let cwd = ctx.cwd.clone();
1015 let abort_signal = ctx.abort_signal.clone();
1016 Box::pin(async move {
1017 let ctx2 = ToolContext {
1018 cwd,
1019 abort_signal: abort_signal.clone(),
1020 };
1021 tool_clone.execute(input, &ctx2).await
1022 })
1023 };
1024 let enter_worktree_tool = EnterWorktreeTool::new();
1025 let enter_worktree_rf = self::make_render_fns(enter_worktree_tool);
1026 engine.register_tool_with_render("EnterWorktree".to_string(), enter_worktree_executor, enter_worktree_rf);
1027
1028 let exit_worktree_executor = move |input: serde_json::Value,
1030 ctx: &ToolContext|
1031 -> BoxFuture<Result<ToolResult, AgentError>> {
1032 let tool_clone = ExitWorktreeTool::new();
1033 let cwd = ctx.cwd.clone();
1034 let abort_signal = ctx.abort_signal.clone();
1035 Box::pin(async move {
1036 let ctx2 = ToolContext {
1037 cwd,
1038 abort_signal: abort_signal.clone(),
1039 };
1040 tool_clone.execute(input, &ctx2).await
1041 })
1042 };
1043 let exit_worktree_tool = ExitWorktreeTool::new();
1044 let exit_worktree_rf = self::make_render_fns(exit_worktree_tool);
1045 engine.register_tool_with_render("ExitWorktree".to_string(), exit_worktree_executor, exit_worktree_rf);
1046
1047 let enter_plan_mode_executor = move |input: serde_json::Value,
1049 ctx: &ToolContext|
1050 -> BoxFuture<Result<ToolResult, AgentError>> {
1051 let tool_clone = EnterPlanModeTool::new();
1052 let cwd = ctx.cwd.clone();
1053 let abort_signal = ctx.abort_signal.clone();
1054 Box::pin(async move {
1055 let ctx2 = ToolContext {
1056 cwd,
1057 abort_signal: abort_signal.clone(),
1058 };
1059 tool_clone.execute(input, &ctx2).await
1060 })
1061 };
1062 let enter_plan_mode_tool = EnterPlanModeTool::new();
1063 let enter_plan_mode_rf = self::make_render_fns(enter_plan_mode_tool);
1064 engine.register_tool_with_render("EnterPlanMode".to_string(), enter_plan_mode_executor, enter_plan_mode_rf);
1065
1066 let exit_plan_mode_executor = move |input: serde_json::Value,
1068 ctx: &ToolContext|
1069 -> BoxFuture<Result<ToolResult, AgentError>> {
1070 let tool_clone = ExitPlanModeTool::new();
1071 let cwd = ctx.cwd.clone();
1072 let abort_signal = ctx.abort_signal.clone();
1073 Box::pin(async move {
1074 let ctx2 = ToolContext {
1075 cwd,
1076 abort_signal: abort_signal.clone(),
1077 };
1078 tool_clone.execute(input, &ctx2).await
1079 })
1080 };
1081 let exit_plan_mode_tool = ExitPlanModeTool::new();
1082 let exit_plan_mode_rf = self::make_render_fns(exit_plan_mode_tool);
1083 engine.register_tool_with_render("ExitPlanMode".to_string(), exit_plan_mode_executor, exit_plan_mode_rf);
1084
1085 let ask_user_question_executor = move |input: serde_json::Value,
1087 ctx: &ToolContext|
1088 -> BoxFuture<Result<ToolResult, AgentError>> {
1089 let tool_clone = AskUserQuestionTool::new();
1090 let cwd = ctx.cwd.clone();
1091 let abort_signal = ctx.abort_signal.clone();
1092 Box::pin(async move {
1093 let ctx2 = ToolContext {
1094 cwd,
1095 abort_signal: abort_signal.clone(),
1096 };
1097 tool_clone.execute(input, &ctx2).await
1098 })
1099 };
1100 let ask_user_question_tool = AskUserQuestionTool::new();
1101 let ask_user_question_rf = self::make_render_fns(ask_user_question_tool);
1102 engine.register_tool_with_render("AskUserQuestion".to_string(), ask_user_question_executor, ask_user_question_rf);
1103
1104 let tool_search_executor = move |input: serde_json::Value,
1106 ctx: &ToolContext|
1107 -> BoxFuture<Result<ToolResult, AgentError>> {
1108 let tool_clone = ToolSearchTool::new();
1109 let cwd = ctx.cwd.clone();
1110 let abort_signal = ctx.abort_signal.clone();
1111 Box::pin(async move {
1112 let ctx2 = ToolContext {
1113 cwd,
1114 abort_signal: abort_signal.clone(),
1115 };
1116 tool_clone.execute(input, &ctx2).await
1117 })
1118 };
1119 let tool_search_tool = ToolSearchTool::new();
1120 let tool_search_rf = self::make_render_fns(tool_search_tool);
1121 engine.register_tool_with_render("ToolSearch".to_string(), tool_search_executor, tool_search_rf);
1122
1123 let team_create_executor = move |input: serde_json::Value,
1125 ctx: &ToolContext|
1126 -> BoxFuture<Result<ToolResult, AgentError>> {
1127 let tool_clone = TeamCreateTool::new();
1128 let cwd = ctx.cwd.clone();
1129 let abort_signal = ctx.abort_signal.clone();
1130 Box::pin(async move {
1131 let ctx2 = ToolContext {
1132 cwd,
1133 abort_signal: abort_signal.clone(),
1134 };
1135 tool_clone.execute(input, &ctx2).await
1136 })
1137 };
1138 let team_create_tool = TeamCreateTool::new();
1139 let team_create_rf = self::make_render_fns(team_create_tool);
1140 engine.register_tool_with_render("TeamCreate".to_string(), team_create_executor, team_create_rf);
1141
1142 let team_delete_executor = move |input: serde_json::Value,
1144 ctx: &ToolContext|
1145 -> BoxFuture<Result<ToolResult, AgentError>> {
1146 let tool_clone = TeamDeleteTool::new();
1147 let cwd = ctx.cwd.clone();
1148 let abort_signal = ctx.abort_signal.clone();
1149 Box::pin(async move {
1150 let ctx2 = ToolContext {
1151 cwd,
1152 abort_signal: abort_signal.clone(),
1153 };
1154 tool_clone.execute(input, &ctx2).await
1155 })
1156 };
1157 let team_delete_tool = TeamDeleteTool::new();
1158 let team_delete_rf = self::make_render_fns(team_delete_tool);
1159 engine.register_tool_with_render("TeamDelete".to_string(), team_delete_executor, team_delete_rf);
1160
1161 let send_message_executor = move |input: serde_json::Value,
1163 ctx: &ToolContext|
1164 -> BoxFuture<Result<ToolResult, AgentError>> {
1165 let tool_clone = SendMessageTool::new();
1166 let cwd = ctx.cwd.clone();
1167 let abort_signal = ctx.abort_signal.clone();
1168 Box::pin(async move {
1169 let ctx2 = ToolContext {
1170 cwd,
1171 abort_signal: abort_signal.clone(),
1172 };
1173 tool_clone.execute(input, &ctx2).await
1174 })
1175 };
1176 let send_message_tool = SendMessageTool::new();
1177 let send_message_rf = self::make_render_fns(send_message_tool);
1178 engine.register_tool_with_render("SendMessage".to_string(), send_message_executor, send_message_rf);
1179
1180 let sleep_executor = move |input: serde_json::Value,
1182 ctx: &ToolContext|
1183 -> BoxFuture<Result<ToolResult, AgentError>> {
1184 let tool_clone = SleepTool::new();
1185 let cwd = ctx.cwd.clone();
1186 let abort_signal = ctx.abort_signal.clone();
1187 Box::pin(async move {
1188 let ctx2 = ToolContext {
1189 cwd,
1190 abort_signal: abort_signal.clone(),
1191 };
1192 tool_clone.execute(input, &ctx2).await
1193 })
1194 };
1195 let sleep_tool = SleepTool::new();
1196 let sleep_rf = self::make_render_fns(sleep_tool);
1197 engine.register_tool_with_render("Sleep".to_string(), sleep_executor, sleep_rf);
1198
1199 let powershell_executor = move |input: serde_json::Value,
1201 ctx: &ToolContext|
1202 -> BoxFuture<Result<ToolResult, AgentError>> {
1203 let tool_clone = PowerShellTool::new();
1204 let cwd = ctx.cwd.clone();
1205 let abort_signal = ctx.abort_signal.clone();
1206 Box::pin(async move {
1207 let ctx2 = ToolContext {
1208 cwd,
1209 abort_signal: abort_signal.clone(),
1210 };
1211 tool_clone.execute(input, &ctx2).await
1212 })
1213 };
1214 let powershell_tool = PowerShellTool::new();
1215 let powershell_rf = self::make_render_fns(powershell_tool);
1216 engine.register_tool_with_render("PowerShell".to_string(), powershell_executor, powershell_rf);
1217
1218 let lsp_executor = move |input: serde_json::Value,
1220 ctx: &ToolContext|
1221 -> BoxFuture<Result<ToolResult, AgentError>> {
1222 let tool_clone = LSPTool::new();
1223 let cwd = ctx.cwd.clone();
1224 let abort_signal = ctx.abort_signal.clone();
1225 Box::pin(async move {
1226 let ctx2 = ToolContext {
1227 cwd,
1228 abort_signal: abort_signal.clone(),
1229 };
1230 tool_clone.execute(input, &ctx2).await
1231 })
1232 };
1233 let lsp_tool = LSPTool::new();
1234 let lsp_rf = self::make_render_fns(lsp_tool);
1235 engine.register_tool_with_render("LSP".to_string(), lsp_executor, lsp_rf);
1236
1237 let remote_trigger_executor = move |input: serde_json::Value,
1239 ctx: &ToolContext|
1240 -> BoxFuture<Result<ToolResult, AgentError>> {
1241 let tool_clone = RemoteTriggerTool::new();
1242 let cwd = ctx.cwd.clone();
1243 let abort_signal = ctx.abort_signal.clone();
1244 Box::pin(async move {
1245 let ctx2 = ToolContext {
1246 cwd,
1247 abort_signal: abort_signal.clone(),
1248 };
1249 tool_clone.execute(input, &ctx2).await
1250 })
1251 };
1252 let remote_trigger_tool = RemoteTriggerTool::new();
1253 let remote_trigger_rf = self::make_render_fns(remote_trigger_tool);
1254 engine.register_tool_with_render("RemoteTrigger".to_string(), remote_trigger_executor, remote_trigger_rf);
1255
1256 let list_mcp_resources_executor = move |input: serde_json::Value,
1258 ctx: &ToolContext|
1259 -> BoxFuture<Result<ToolResult, AgentError>> {
1260 let tool_clone = ListMcpResourcesTool::new();
1261 let cwd = ctx.cwd.clone();
1262 let abort_signal = ctx.abort_signal.clone();
1263 Box::pin(async move {
1264 let ctx2 = ToolContext {
1265 cwd,
1266 abort_signal: abort_signal.clone(),
1267 };
1268 tool_clone.execute(input, &ctx2).await
1269 })
1270 };
1271 let list_mcp_resources_tool = ListMcpResourcesTool::new();
1272 let list_mcp_resources_rf = self::make_render_fns(list_mcp_resources_tool);
1273 engine.register_tool_with_render(
1274 "ListMcpResourcesTool".to_string(),
1275 list_mcp_resources_executor,
1276 list_mcp_resources_rf,
1277 );
1278
1279 let read_mcp_resource_executor = move |input: serde_json::Value,
1281 ctx: &ToolContext|
1282 -> BoxFuture<Result<ToolResult, AgentError>> {
1283 let tool_clone = ReadMcpResourceTool::new();
1284 let cwd = ctx.cwd.clone();
1285 let abort_signal = ctx.abort_signal.clone();
1286 Box::pin(async move {
1287 let ctx2 = ToolContext {
1288 cwd,
1289 abort_signal: abort_signal.clone(),
1290 };
1291 tool_clone.execute(input, &ctx2).await
1292 })
1293 };
1294 let read_mcp_resource_tool = ReadMcpResourceTool::new();
1295 let read_mcp_resource_rf = self::make_render_fns(read_mcp_resource_tool);
1296 engine.register_tool_with_render(
1297 "ReadMcpResourceTool".to_string(),
1298 read_mcp_resource_executor,
1299 read_mcp_resource_rf,
1300 );
1301
1302 let brief_executor = move |input: serde_json::Value,
1304 ctx: &ToolContext|
1305 -> BoxFuture<Result<ToolResult, AgentError>> {
1306 let tool_clone = BriefTool::new();
1307 let cwd = ctx.cwd.clone();
1308 let abort_signal = ctx.abort_signal.clone();
1309 Box::pin(async move {
1310 let ctx2 = ToolContext {
1311 cwd,
1312 abort_signal: abort_signal.clone(),
1313 };
1314 tool_clone.execute(input, &ctx2).await
1315 })
1316 };
1317 let brief_tool = BriefTool::new();
1318 let brief_rf = self::make_render_fns(brief_tool);
1319 engine.register_tool_with_render("SendUserMessage".to_string(), brief_executor, brief_rf);
1320
1321 let synthetic_output_executor =
1323 move |input: serde_json::Value, ctx: &ToolContext|
1324 -> BoxFuture<Result<ToolResult, AgentError>> {
1325 let tool_clone = SyntheticOutputTool::new();
1326 let cwd = ctx.cwd.clone();
1327 let abort_signal = ctx.abort_signal.clone();
1328 Box::pin(async move {
1329 let ctx2 = ToolContext {
1330 cwd,
1331 abort_signal: abort_signal.clone(),
1332 };
1333 tool_clone.execute(input, &ctx2).await
1334 })
1335 };
1336 let synthetic_output_tool = SyntheticOutputTool::new();
1337 let synthetic_output_rf = self::make_render_fns(synthetic_output_tool);
1338 engine.register_tool_with_render("StructuredOutput".to_string(), synthetic_output_executor, synthetic_output_rf);
1339
1340 let mcp_tool_executor =
1342 move |input: serde_json::Value, ctx: &ToolContext|
1343 -> BoxFuture<Result<ToolResult, AgentError>> {
1344 let tool_clone = McpTool::new();
1345 let cwd = ctx.cwd.clone();
1346 let abort_signal = ctx.abort_signal.clone();
1347 Box::pin(async move {
1348 let ctx2 = ToolContext {
1349 cwd,
1350 abort_signal: abort_signal.clone(),
1351 };
1352 tool_clone.execute(input, &ctx2).await
1353 })
1354 };
1355 let mcp_tool = McpTool::new();
1356 let mcp_tool_rf = self::make_render_fns(mcp_tool);
1357 engine.register_tool_with_render("MCPTool".to_string(), mcp_tool_executor, mcp_tool_rf);
1358
1359 let mcp_auth_executor =
1361 move |input: serde_json::Value, ctx: &ToolContext|
1362 -> BoxFuture<Result<ToolResult, AgentError>> {
1363 let tool_clone = McpAuthTool::new();
1364 let cwd = ctx.cwd.clone();
1365 let abort_signal = ctx.abort_signal.clone();
1366 Box::pin(async move {
1367 let ctx2 = ToolContext {
1368 cwd,
1369 abort_signal: abort_signal.clone(),
1370 };
1371 tool_clone.execute(input, &ctx2).await
1372 })
1373 };
1374 let mcp_auth_tool = McpAuthTool::new();
1375 let mcp_auth_rf = self::make_render_fns(mcp_auth_tool);
1376 engine.register_tool_with_render("McpAuth".to_string(), mcp_auth_executor, mcp_auth_rf);
1377}
1378
1379#[derive(Clone)]
1400pub struct Agent {
1401 pub(crate) inner: std::sync::Arc<parking_lot::Mutex<AgentInner>>,
1402}
1403
1404#[cfg(test)]
1405impl Agent {
1406 pub(crate) fn inner_for_test(&self) -> &std::sync::Arc<parking_lot::Mutex<AgentInner>> {
1408 &self.inner
1409 }
1410}
1411
1412pub(crate) struct AgentInner {
1413 model: String,
1414 api_key: Option<String>,
1415 base_url: Option<String>,
1416 cwd: String,
1417 system_prompt: Option<String>,
1418 max_turns: u32,
1419 max_budget_usd: Option<f64>,
1420 max_tokens: u32,
1421 fallback_model: Option<String>,
1422 pub(crate) thinking: Option<ThinkingConfig>,
1423 mcp_servers: Option<std::collections::HashMap<String, crate::mcp::McpServerConfig>>,
1424 tool_pool: Vec<ToolDefinition>,
1425 pub(crate) allowed_tools: Vec<String>,
1426 pub(crate) disallowed_tools: Vec<String>,
1427 #[cfg(test)]
1428 pub on_event: Option<std::sync::Arc<dyn Fn(AgentEvent) + Send + Sync>>,
1429 #[cfg(not(test))]
1430 pub(crate) on_event: Option<std::sync::Arc<dyn Fn(AgentEvent) + Send + Sync>>,
1431 session_id: String,
1432 abort_controller: std::sync::Arc<crate::utils::AbortController>,
1433 engine: Option<Arc<parking_lot::RwLock<QueryEngine>>>,
1439 broadcasters: EventBroadcasters,
1441}
1442
1443impl Agent {
1444 pub fn new(model: &str) -> Self {
1462 let env_config = EnvConfig::load();
1463 let model = env_config.model.unwrap_or_else(|| model.to_string());
1464 let api_key = env_config.auth_token.clone();
1465 let base_url = env_config.base_url.clone();
1466 let cwd = std::env::current_dir()
1467 .map(|p| p.to_string_lossy().to_string())
1468 .unwrap_or_else(|_| ".".to_string());
1469 let default_max_tokens =
1470 crate::utils::context::get_max_output_tokens_for_model(&model) as u32;
1471
1472 Self {
1473 inner: std::sync::Arc::new(parking_lot::Mutex::new(AgentInner {
1474 model,
1475 api_key,
1476 base_url,
1477 cwd,
1478 system_prompt: None,
1479 max_turns: 10,
1480 max_budget_usd: None,
1481 max_tokens: default_max_tokens,
1482 fallback_model: None,
1483 thinking: None,
1484 mcp_servers: None,
1485 tool_pool: vec![],
1486 allowed_tools: vec![],
1487 disallowed_tools: vec![],
1488 on_event: None,
1489 session_id: uuid::Uuid::new_v4().to_string(),
1490 abort_controller: std::sync::Arc::new(
1491 crate::utils::create_abort_controller_default(),
1492 ),
1493 engine: None,
1494 broadcasters: EventBroadcasters::new(),
1495 })),
1496 }
1497 }
1498
1499 pub fn model(mut self, model: &str) -> Self {
1503 self.inner.lock().model = model.to_string();
1504 self
1505 }
1506
1507 pub fn api_key(mut self, api_key: &str) -> Self {
1511 self.inner.lock().api_key = Some(api_key.to_string());
1512 self
1513 }
1514
1515 pub fn base_url(mut self, base_url: &str) -> Self {
1519 self.inner.lock().base_url = Some(base_url.to_string());
1520 self
1521 }
1522
1523 pub fn cwd(mut self, cwd: &str) -> Self {
1527 self.inner.lock().cwd = cwd.to_string();
1528 self
1529 }
1530
1531 pub fn system_prompt(mut self, prompt: &str) -> Self {
1533 self.inner.lock().system_prompt = Some(prompt.to_string());
1534 self
1535 }
1536
1537 pub fn max_turns(mut self, max_turns: u32) -> Self {
1541 self.inner.lock().max_turns = max_turns;
1542 self
1543 }
1544
1545 pub fn max_budget_usd(mut self, budget: f64) -> Self {
1547 self.inner.lock().max_budget_usd = Some(budget);
1548 self
1549 }
1550
1551 pub fn max_tokens(mut self, max_tokens: u32) -> Self {
1553 self.inner.lock().max_tokens = max_tokens;
1554 self
1555 }
1556
1557 pub fn fallback_model(mut self, model: &str) -> Self {
1559 self.inner.lock().fallback_model = Some(model.to_string());
1560 self
1561 }
1562
1563 pub fn thinking(mut self, thinking: ThinkingConfig) -> Self {
1565 self.inner.lock().thinking = Some(thinking);
1566 self
1567 }
1568
1569 pub fn tools(mut self, tools: Vec<ToolDefinition>) -> Self {
1571 self.inner.lock().tool_pool = tools;
1572 self
1573 }
1574
1575 pub fn allowed_tools(mut self, tools: Vec<String>) -> Self {
1577 self.inner.lock().allowed_tools = tools;
1578 self
1579 }
1580
1581 pub fn disallowed_tools(mut self, tools: Vec<String>) -> Self {
1583 self.inner.lock().disallowed_tools = tools;
1584 self
1585 }
1586
1587 pub fn mcp_servers(
1589 mut self,
1590 servers: std::collections::HashMap<String, crate::mcp::McpServerConfig>,
1591 ) -> Self {
1592 self.inner.lock().mcp_servers = Some(servers);
1593 self
1594 }
1595
1596 pub fn on_event<F>(mut self, callback: F) -> Self
1604 where
1605 F: Fn(AgentEvent) + Send + Sync + 'static,
1606 {
1607 self.inner.lock().on_event = Some(std::sync::Arc::new(callback));
1608 self
1609 }
1610
1611 fn init_engine(&self) {
1614 let mut inner = self.inner.lock();
1615 if inner.engine.is_none() {
1616 let cwd = inner.cwd.clone();
1617 let allowed_tools = inner.allowed_tools.clone();
1618 let disallowed_tools = inner.disallowed_tools.clone();
1619 let tool_pool = inner.tool_pool.clone();
1620 let can_use_tool: Option<
1621 std::sync::Arc<dyn Fn(ToolDefinition, serde_json::Value) -> PermissionResult + Send + Sync>,
1622 > = if !allowed_tools.is_empty() || !disallowed_tools.is_empty() {
1623 Some(std::sync::Arc::new(
1624 move |tool_def: ToolDefinition, _input: serde_json::Value| {
1625 if !allowed_tools.is_empty() && !allowed_tools.contains(&tool_def.name) {
1626 return PermissionResult::Deny(PermissionDenyDecision::new(
1627 &format!("Tool '{}' is not in the allowed tools list", tool_def.name),
1628 PermissionDecisionReason::Other { reason: "allowed tools filter".to_string() },
1629 ));
1630 }
1631 if disallowed_tools.contains(&tool_def.name) {
1632 return PermissionResult::Deny(PermissionDenyDecision::new(
1633 &format!("Tool '{}' is in the disallowed tools list", tool_def.name),
1634 PermissionDecisionReason::Other { reason: "disallowed tools filter".to_string() },
1635 ));
1636 }
1637 PermissionResult::Allow(PermissionAllowDecision::default())
1638 },
1639 ))
1640 } else {
1641 None
1642 };
1643 let config = QueryEngineConfig {
1644 cwd: cwd.clone(),
1645 model: inner.model.clone(),
1646 api_key: inner.api_key.clone(),
1647 base_url: inner.base_url.clone(),
1648 tools: tool_pool,
1649 system_prompt: None,
1650 max_turns: inner.max_turns,
1651 max_budget_usd: inner.max_budget_usd,
1652 max_tokens: inner.max_tokens,
1653 fallback_model: inner.fallback_model.clone(),
1654 user_context: std::collections::HashMap::new(),
1655 system_context: std::collections::HashMap::new(),
1656 can_use_tool,
1657 on_event: inner.on_event.clone(),
1658 thinking: inner.thinking.clone(),
1659 abort_controller: Some(inner.abort_controller.clone()),
1660 token_budget: None,
1661 agent_id: None,
1662 session_state: None,
1663 loaded_nested_memory_paths: std::collections::HashSet::new(),
1664 task_budget: None,
1665 orphaned_permission: None,
1666 };
1667 let mut engine = QueryEngine::new(config);
1668 register_all_tool_executors(&mut engine);
1669
1670 let session_id = inner.session_id.clone();
1672 if let Ok(skills) = load_all_skills(&cwd) {
1673 let _set_app_state = Arc::new(|_: &dyn Fn(&mut serde_json::Value)| {})
1674 as Arc<dyn Fn(&dyn Fn(&mut serde_json::Value)) + Send + Sync>;
1675 register_hooks_from_skills(_set_app_state, &session_id, &skills);
1676 }
1677
1678 inner.engine = Some(Arc::new(parking_lot::RwLock::new(engine)));
1679 }
1680 }
1681
1682 pub async fn prompt(model: &str, prompt: &str) -> Result<String, AgentError> {
1695 let agent = Self::new(model);
1696 let result = agent.query(prompt).await?;
1697 Ok(result.text)
1698 }
1699
1700 pub fn get_model(&self) -> String {
1702 self.inner.lock().model.clone()
1703 }
1704
1705 pub fn get_session_id(&self) -> String {
1707 self.inner.lock().session_id.clone()
1708 }
1709
1710 pub fn get_messages(&self) -> Vec<Message> {
1714 let engine_opt = {
1715 let inner = self.inner.lock();
1716 inner.engine.clone()
1717 };
1718 if let Some(engine) = engine_opt {
1719 let eng = engine.read();
1720 eng.get_messages()
1721 } else {
1722 Vec::new()
1723 }
1724 }
1725
1726 pub fn get_tools(&self) -> Vec<ToolDefinition> {
1728 self.inner.lock().tool_pool.clone()
1729 }
1730
1731 pub fn set_system_prompt(&self, prompt: &str) {
1733 self.inner.lock().system_prompt = Some(prompt.to_string());
1734 }
1735
1736 pub fn set_cwd(&self, cwd: &str) {
1738 self.inner.lock().cwd = cwd.to_string();
1739 }
1740
1741
1742 pub fn set_thinking(&self, thinking: Option<ThinkingConfig>) {
1744 self.inner.lock().thinking = thinking;
1745 }
1746
1747 pub fn set_model(&self, model: &str) {
1752 self.inner.lock().model = model.to_string();
1753 }
1754
1755 pub(crate) async fn execute_tool(
1757 &self,
1758 name: &str,
1759 input: serde_json::Value,
1760 ) -> Result<ToolResult, AgentError> {
1761 let (cwd, model, api_key, base_url, abort_controller, allowed_tools, disallowed_tools, on_event, thinking) = {
1763 let inner = self.inner.lock();
1764 (
1765 inner.cwd.clone(),
1766 inner.model.clone(),
1767 inner.api_key.clone(),
1768 inner.base_url.clone(),
1769 inner.abort_controller.clone(),
1770 inner.allowed_tools.clone(),
1771 inner.disallowed_tools.clone(),
1772 inner.on_event.clone(),
1773 inner.thinking.clone(),
1774 )
1775 };
1776
1777 let mut engine = QueryEngine::new(QueryEngineConfig {
1778 cwd: cwd.clone(),
1779 model: model.clone(),
1780 api_key: api_key.clone(),
1781 base_url: base_url.clone(),
1782 tools: vec![],
1783 system_prompt: None,
1784 max_turns: 10,
1785 max_budget_usd: None,
1786 max_tokens: crate::utils::context::get_max_output_tokens_for_model(&model) as u32,
1787 fallback_model: None,
1788 user_context: std::collections::HashMap::new(),
1789 system_context: std::collections::HashMap::new(),
1790 can_use_tool: None,
1791 on_event: None,
1792 thinking: None,
1793 abort_controller: Some(abort_controller.clone()),
1794 token_budget: None,
1795 agent_id: None,
1796 session_state: None,
1797 loaded_nested_memory_paths: std::collections::HashSet::new(),
1798 task_budget: None,
1799 orphaned_permission: None,
1800 });
1801
1802 let parent_can_use_tool: Option<
1804 std::sync::Arc<dyn Fn(ToolDefinition, serde_json::Value) -> PermissionResult + Send + Sync>,
1805 > = {
1806 if !allowed_tools.is_empty() || !disallowed_tools.is_empty() {
1807 Some(std::sync::Arc::new(
1808 move |tool_def: ToolDefinition, _input: serde_json::Value| {
1809 if !allowed_tools.is_empty() && !allowed_tools.contains(&tool_def.name) {
1810 return PermissionResult::Deny(PermissionDenyDecision::new(
1811 &format!("Tool '{}' is not in the allowed tools list", tool_def.name),
1812 PermissionDecisionReason::Other { reason: "allowed tools filter".to_string() },
1813 ));
1814 }
1815 if disallowed_tools.contains(&tool_def.name) {
1816 return PermissionResult::Deny(PermissionDenyDecision::new(
1817 &format!("Tool '{}' is in the disallowed tools list", tool_def.name),
1818 PermissionDecisionReason::Other { reason: "disallowed tools filter".to_string() },
1819 ));
1820 }
1821 PermissionResult::Allow(PermissionAllowDecision::default())
1822 },
1823 ))
1824 } else {
1825 None
1826 }
1827 };
1828 let parent_on_event = on_event;
1829 let parent_thinking = thinking;
1830
1831 register_all_tool_executors(&mut engine);
1833
1834 {
1836 use crate::tools::agent::{AgentTool, AgentToolConfig, create_agent_tool_executor};
1837 let agent_tool = Arc::new(AgentTool::new(AgentToolConfig {
1838 cwd: cwd.clone(),
1839 api_key: api_key.clone(),
1840 base_url: base_url.clone(),
1841 model: model.clone(),
1842 tool_pool: crate::tools::get_all_base_tools(),
1843 abort_controller: abort_controller.clone(),
1844 can_use_tool: parent_can_use_tool,
1845 on_event: parent_on_event,
1846 thinking: parent_thinking,
1847 parent_messages: Vec::new(),
1848 parent_user_context: std::collections::HashMap::new(),
1849 parent_system_context: std::collections::HashMap::new(),
1850 parent_session_id: None,
1851 }));
1852 engine.register_tool(
1853 "Agent".to_string(),
1854 create_agent_tool_executor(agent_tool),
1855 );
1856 }
1857 let tool_call_id = uuid::Uuid::new_v4().to_string();
1858 engine.execute_tool(name, input, tool_call_id).await
1859 }
1860
1861 fn build_system_prompt(&self, cwd: &std::path::Path) -> Option<String> {
1864 use crate::ai_md::load_ai_md;
1865 use crate::memdir::load_memory_prompt_sync;
1866 use crate::prompts::build_system_prompt as base_build_system_prompt;
1867
1868 let inner = &*self.inner.lock();
1869 let ai_md_prompt = load_ai_md(cwd).ok().flatten();
1870 let memory_mechanics_prompt =
1871 if inner.system_prompt.is_some() && crate::memdir::has_auto_mem_path_override() {
1872 load_memory_prompt_sync()
1873 } else {
1874 None
1875 };
1876 let base_system_prompt = base_build_system_prompt();
1877
1878 let system_prompt = match (
1879 &ai_md_prompt,
1880 &memory_mechanics_prompt,
1881 &inner.system_prompt,
1882 ) {
1883 (Some(ai_md), Some(mem), Some(custom)) => Some(format!(
1884 "{}\n\n{}\n\n{}\n\n{}",
1885 ai_md, mem, base_system_prompt, custom
1886 )),
1887 (Some(ai_md), Some(mem), None) => {
1888 Some(format!("{}\n\n{}\n\n{}", ai_md, mem, base_system_prompt))
1889 }
1890 (Some(ai_md), None, Some(custom)) => {
1891 Some(format!("{}\n\n{}\n\n{}", ai_md, base_system_prompt, custom))
1892 }
1893 (Some(ai_md), None, None) => Some(format!("{}\n\n{}", ai_md, base_system_prompt)),
1894 (None, Some(mem), Some(custom)) => {
1895 Some(format!("{}\n\n{}\n\n{}", mem, base_system_prompt, custom))
1896 }
1897 (None, Some(mem), None) => Some(format!("{}\n\n{}", mem, base_system_prompt)),
1898 (None, None, Some(custom)) => Some(format!("{}\n\n{}", base_system_prompt, custom)),
1899 (None, None, None) => Some(base_system_prompt),
1900 };
1901 system_prompt
1902 }
1903
1904 fn select_tools(&self) -> Vec<ToolDefinition> {
1908 let inner = &*self.inner.lock();
1909 let tools = if inner.tool_pool.is_empty() {
1910 crate::tools::get_all_base_tools()
1911 } else {
1912 inner.tool_pool.clone()
1913 };
1914 let disallowed_tools = inner.disallowed_tools.clone();
1915
1916 let mut sorted = tools;
1918 sorted.sort_by(|a, b| a.name.cmp(&b.name));
1919 let mut seen = std::collections::HashSet::new();
1921 sorted.retain(|t| seen.insert(t.name.clone()));
1922
1923 if !disallowed_tools.is_empty() {
1925 sorted = crate::tools::filter_tools_by_deny_rules(&sorted, &disallowed_tools);
1926 }
1927 sorted
1928 }
1929
1930 pub async fn query(&self, prompt: &str) -> Result<QueryResult, AgentError> {
1938 self.init_engine();
1939
1940 let (cwd, on_event, thinking, abort_controller, engine, broadcasters) = {
1943 let inner = self.inner.lock();
1944 (
1945 inner.cwd.clone(),
1946 inner.on_event.clone(),
1947 inner.thinking.clone(),
1948 inner.abort_controller.clone(),
1949 Arc::clone(inner.engine.as_ref().unwrap()),
1950 inner.broadcasters.clone(),
1951 )
1952 };
1953 let cwd_path = std::path::Path::new(&cwd);
1954
1955 let system_prompt = self.build_system_prompt(&cwd_path);
1956 let tools = self.select_tools();
1957
1958 let start = std::time::Instant::now();
1959 let query_result: Result<
1960 (String, ExitReason, String, crate::types::TokenUsage, u32),
1961 AgentError,
1962 > = {
1963 let mut eng = engine.write();
1964
1965 eng.config.system_prompt = system_prompt;
1967 eng.config.tools = tools;
1968 let on_event = {
1970 let cb = on_event;
1971 Some(Arc::new(move |event: crate::types::AgentEvent| {
1972 if let Some(ref f) = cb {
1973 f(event.clone());
1974 }
1975 broadcasters.broadcast(&event);
1976 }) as std::sync::Arc<dyn Fn(crate::types::AgentEvent) + Send + Sync>)
1977 };
1978 eng.config.on_event = on_event;
1979 eng.config.thinking = thinking;
1980
1981 let engine_tools = eng.config.tools.clone();
1983 let engine_model = eng.config.model.clone();
1984 let engine_api_key = eng.config.api_key.clone();
1985 let engine_base_url = eng.config.base_url.clone();
1986 let engine_cwd = eng.config.cwd.clone();
1987 let subagent_abort = abort_controller.clone();
1988 let engine_messages = eng.messages.clone();
1990 let engine_user_context = eng.config.user_context.clone();
1991 let engine_system_context = eng.config.system_context.clone();
1992 let engine_thinking = eng.config.thinking.clone();
1993 let engine_can_use_tool = eng.config.can_use_tool.clone();
1994 let engine_on_event = eng.config.on_event.clone();
1995
1996 {
1998 use crate::tools::agent::{AgentTool, AgentToolConfig, create_agent_tool_executor};
1999 let agent_tool = Arc::new(AgentTool::new(AgentToolConfig {
2000 cwd: engine_cwd,
2001 api_key: engine_api_key,
2002 base_url: engine_base_url,
2003 model: engine_model,
2004 tool_pool: engine_tools,
2005 abort_controller: subagent_abort,
2006 can_use_tool: engine_can_use_tool,
2007 on_event: engine_on_event,
2008 thinking: engine_thinking,
2009 parent_messages: engine_messages,
2010 parent_user_context: engine_user_context,
2011 parent_system_context: engine_system_context,
2012 parent_session_id: None,
2013 }));
2014 eng.register_tool(
2015 "Agent".to_string(),
2016 create_agent_tool_executor(agent_tool),
2017 );
2018 }
2019
2020 match eng.submit_message(prompt).await {
2022 Ok(r) => Ok((
2023 r.0,
2024 r.1,
2025 eng.config.model.clone(),
2026 eng.get_usage(),
2027 eng.get_turn_count(),
2028 )),
2029 Err(e) => {
2030 let duration_ms = start.elapsed().as_millis() as u64;
2031 eng.reset_counters();
2034
2035 let is_image_error = crate::services::api::errors::is_media_size_error(&e.to_string())
2038 && e.to_string().to_lowercase().contains("image");
2039
2040 if is_image_error {
2041 eng.messages.push(crate::types::Message {
2043 role: crate::types::MessageRole::Assistant,
2044 content: e.to_string(),
2045 is_api_error_message: Some(true),
2046 error_details: Some(e.to_string()),
2047 ..Default::default()
2048 });
2049 let _ = crate::utils::session_storage::flush_session_storage();
2051 if let Some(ref cb) = eng.config.on_event {
2053 cb(AgentEvent::Done {
2054 result: QueryResult {
2055 text: String::new(),
2056 exit_reason: ExitReason::ImageError {
2057 error: e.to_string(),
2058 },
2059 usage: Default::default(),
2060 num_turns: 0,
2061 duration_ms,
2062 },
2063 });
2064 }
2065 } else {
2066 let api_err = crate::services::api::errors::error_to_api_message(&e.to_string(), None);
2068 eng.messages.push(crate::types::Message {
2069 role: crate::types::MessageRole::Assistant,
2070 content: api_err.content.clone().unwrap_or_default(),
2071 is_api_error_message: Some(true),
2072 error_details: api_err.error_details.clone(),
2073 ..Default::default()
2074 });
2075 let _ = crate::utils::session_storage::flush_session_storage();
2077 if let Some(ref cb) = eng.config.on_event {
2079 cb(AgentEvent::Done {
2080 result: QueryResult {
2081 text: String::new(),
2082 exit_reason: ExitReason::ModelError {
2083 error: e.to_string(),
2084 },
2085 usage: Default::default(),
2086 num_turns: 0,
2087 duration_ms,
2088 },
2089 });
2090 }
2091 }
2092 Err(e)
2093 }
2094 }
2095 }; let (response_text, exit_reason, current_model, usage, turns) = query_result?;
2098
2099 if current_model != self.get_model() {
2101 self.inner.lock().model = current_model;
2102 }
2103
2104 Ok(QueryResult {
2105 text: response_text,
2106 usage: TokenUsage {
2107 input_tokens: usage.input_tokens,
2108 output_tokens: usage.output_tokens,
2109 cache_creation_input_tokens: usage.cache_creation_input_tokens,
2110 cache_read_input_tokens: usage.cache_read_input_tokens,
2111 iterations: usage.iterations,
2112 },
2113 num_turns: turns,
2114 duration_ms: start.elapsed().as_millis() as u64,
2115 exit_reason,
2116 })
2117 }
2118
2119 pub fn reset(&self) {
2124 let engine_opt = {
2125 let inner = self.inner.lock();
2126 inner.engine.clone()
2127 };
2128 if let Some(engine) = engine_opt {
2129 let mut eng = engine.write();
2130 eng.reset();
2131 }
2132 }
2133
2134 pub fn subscribe(&self) -> (EventSubscriber, CancelGuard) {
2160 let inner = self.inner.lock();
2161 inner.broadcasters.subscribe()
2162 }
2163
2164 pub fn interrupt(&self) {
2180 let inner = self.inner.lock();
2181 inner.abort_controller.abort(None);
2182 }
2183
2184 pub async fn recap(
2205 &self,
2206 ) -> crate::services::away_summary::AwaySummaryResult {
2207 let engine_opt = {
2208 let inner = self.inner.lock();
2209 inner.engine.clone()
2210 };
2211 let messages = if let Some(engine) = engine_opt.as_ref() {
2212 let eng = engine.read();
2213 eng.get_messages()
2214 } else {
2215 Vec::new()
2216 };
2217 let (api_key, abort_ctrl) = {
2218 let inner = self.inner.lock();
2219 (inner.api_key.clone(), inner.abort_controller.clone())
2220 };
2221
2222 let api_key = match api_key {
2223 Some(k) if !k.is_empty() => k,
2224 _ => std::env::var("AI_AUTH_TOKEN")
2225 .or_else(|_| std::env::var("AI_API_KEY"))
2226 .unwrap_or_default(),
2227 };
2228
2229 crate::services::away_summary::generate_away_summary(
2230 &messages,
2231 &api_key,
2232 abort_ctrl.signal().abort_flag(),
2233 )
2234 .await
2235 }
2236}
2237
2238pub(crate) fn build_agent_system_prompt(
2240 agent_description: &str,
2241 agent_type: Option<&str>,
2242) -> String {
2243 let base_prompt = "You are an agent that helps users with software engineering tasks. Use the tools available to you to assist the user.\n\nComplete the task fully—don't gold-plate, but don't leave it half-done. When you complete the task, respond with a concise report covering what was done and any key findings.";
2244
2245 match agent_type {
2246 Some("Explore") => {
2247 format!(
2248 "{}\n\nYou are an Explore agent. Your goal is to explore and understand the codebase thoroughly. Use search and read tools to investigate. Report your findings in detail.",
2249 base_prompt
2250 )
2251 }
2252 Some("Plan") => {
2253 format!(
2254 "{}\n\nYou are a Plan agent. Your goal is to plan and analyze tasks before execution. Break down complex tasks into steps. Provide a detailed plan.",
2255 base_prompt
2256 )
2257 }
2258 Some("Review") => {
2259 format!(
2260 "{}\n\nYou are a Review agent. Your goal is to review code and provide constructive feedback. Be thorough and focus on best practices.",
2261 base_prompt
2262 )
2263 }
2264 _ => {
2265 format!("{}\n\nTask description: {}", base_prompt, agent_description)
2267 }
2268 }
2269}