agentcrew-ai 0.8.3__py3-none-any.whl → 0.8.5__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- AgentCrew/__init__.py +1 -1
- AgentCrew/main.py +3 -1
- AgentCrew/modules/a2a/agent_cards.py +8 -2
- AgentCrew/modules/a2a/errors.py +72 -0
- AgentCrew/modules/a2a/server.py +21 -2
- AgentCrew/modules/a2a/task_manager.py +180 -39
- AgentCrew/modules/agents/local_agent.py +1 -1
- AgentCrew/modules/browser_automation/element_extractor.py +1 -1
- AgentCrew/modules/browser_automation/js/extract_clickable_elements.js +2 -4
- AgentCrew/modules/browser_automation/service.py +17 -7
- AgentCrew/modules/chat/message/command_processor.py +9 -1
- AgentCrew/modules/chat/message/conversation.py +1 -0
- AgentCrew/modules/chat/message/handler.py +3 -6
- AgentCrew/modules/code_analysis/service.py +39 -0
- AgentCrew/modules/code_analysis/tool.py +10 -1
- AgentCrew/modules/console/command_handlers.py +186 -1
- AgentCrew/modules/console/completers.py +67 -0
- AgentCrew/modules/console/confirmation_handler.py +83 -38
- AgentCrew/modules/console/console_ui.py +86 -28
- AgentCrew/modules/console/diff_display.py +203 -0
- AgentCrew/modules/console/display_handlers.py +15 -0
- AgentCrew/modules/console/input_handler.py +1 -1
- AgentCrew/modules/console/tool_display.py +35 -4
- AgentCrew/modules/console/ui_effects.py +30 -14
- AgentCrew/modules/custom_llm/deepinfra_service.py +20 -19
- AgentCrew/modules/custom_llm/github_copilot_service.py +157 -2
- AgentCrew/modules/custom_llm/service.py +1 -9
- AgentCrew/modules/gui/widgets/message_bubble.py +1 -6
- AgentCrew/modules/llm/model_registry.py +1 -1
- AgentCrew/modules/memory/chroma_service.py +0 -1
- AgentCrew/modules/memory/context_persistent.py +10 -4
- AgentCrew/modules/memory/tool.py +2 -3
- {agentcrew_ai-0.8.3.dist-info → agentcrew_ai-0.8.5.dist-info}/METADATA +3 -3
- {agentcrew_ai-0.8.3.dist-info → agentcrew_ai-0.8.5.dist-info}/RECORD +38 -36
- {agentcrew_ai-0.8.3.dist-info → agentcrew_ai-0.8.5.dist-info}/WHEEL +0 -0
- {agentcrew_ai-0.8.3.dist-info → agentcrew_ai-0.8.5.dist-info}/entry_points.txt +0 -0
- {agentcrew_ai-0.8.3.dist-info → agentcrew_ai-0.8.5.dist-info}/licenses/LICENSE +0 -0
- {agentcrew_ai-0.8.3.dist-info → agentcrew_ai-0.8.5.dist-info}/top_level.txt +0 -0
|
@@ -279,7 +279,7 @@ class MessageHandler(Observable):
|
|
|
279
279
|
# Notify about response progress
|
|
280
280
|
if not self.agent.is_streaming():
|
|
281
281
|
# Delays it a bit when using without stream
|
|
282
|
-
time.sleep(0.
|
|
282
|
+
time.sleep(0.3)
|
|
283
283
|
self._notify("response_chunk", (chunk_text, assistant_response))
|
|
284
284
|
if voice_sentence is not None:
|
|
285
285
|
if (
|
|
@@ -321,6 +321,8 @@ class MessageHandler(Observable):
|
|
|
321
321
|
.lstrip("\n")
|
|
322
322
|
)
|
|
323
323
|
|
|
324
|
+
self.stream_generator = None
|
|
325
|
+
|
|
324
326
|
# End thinking when break the response stream
|
|
325
327
|
if not end_thinking and start_thinking:
|
|
326
328
|
self._notify("thinking_completed", thinking_content)
|
|
@@ -386,11 +388,6 @@ class MessageHandler(Observable):
|
|
|
386
388
|
|
|
387
389
|
return await self.get_assistant_response()
|
|
388
390
|
|
|
389
|
-
self.stream_generator = None
|
|
390
|
-
|
|
391
|
-
if thinking_content:
|
|
392
|
-
self._notify("agent_continue", self.agent.name)
|
|
393
|
-
|
|
394
391
|
# Add assistant response to messages
|
|
395
392
|
if assistant_response.strip():
|
|
396
393
|
self._messages_append(
|
|
@@ -26,6 +26,7 @@ class CodeAnalysisService:
|
|
|
26
26
|
".cxx": "cpp",
|
|
27
27
|
".hxx": "cpp",
|
|
28
28
|
".rb": "ruby",
|
|
29
|
+
".sh": "bash",
|
|
29
30
|
".rake": "ruby",
|
|
30
31
|
".go": "go",
|
|
31
32
|
".rs": "rust",
|
|
@@ -36,6 +37,7 @@ class CodeAnalysisService:
|
|
|
36
37
|
".json": "config",
|
|
37
38
|
".toml": "config",
|
|
38
39
|
".yaml": "config",
|
|
40
|
+
".yml": "config",
|
|
39
41
|
# Add more languages as needed
|
|
40
42
|
}
|
|
41
43
|
|
|
@@ -784,6 +786,43 @@ class CodeAnalysisService:
|
|
|
784
786
|
return result
|
|
785
787
|
break # Only capture the first identifier
|
|
786
788
|
return result
|
|
789
|
+
else:
|
|
790
|
+
if node.type in [
|
|
791
|
+
"type_declaration",
|
|
792
|
+
"function_declaration",
|
|
793
|
+
"method_declaration",
|
|
794
|
+
"interface_declaration",
|
|
795
|
+
]:
|
|
796
|
+
for child in node.children:
|
|
797
|
+
if (
|
|
798
|
+
child.type == "identifier"
|
|
799
|
+
or child.type == "field_identifier"
|
|
800
|
+
):
|
|
801
|
+
result["name"] = self._extract_node_text(
|
|
802
|
+
child, source_code
|
|
803
|
+
)
|
|
804
|
+
result["first_line"] = (
|
|
805
|
+
self._extract_node_text(node, source_code)
|
|
806
|
+
.split("\n")[0]
|
|
807
|
+
.strip("{")
|
|
808
|
+
)
|
|
809
|
+
return result
|
|
810
|
+
return result
|
|
811
|
+
elif (
|
|
812
|
+
node.type == "var_declaration"
|
|
813
|
+
or node.type == "const_declaration"
|
|
814
|
+
):
|
|
815
|
+
# Handle Go variable and constant declarations
|
|
816
|
+
for child in node.children:
|
|
817
|
+
if child.type == "var_spec" or child.type == "const_spec":
|
|
818
|
+
for subchild in child.children:
|
|
819
|
+
if subchild.type == "identifier":
|
|
820
|
+
result["type"] = "variable_declaration"
|
|
821
|
+
result["name"] = self._extract_node_text(
|
|
822
|
+
subchild, source_code
|
|
823
|
+
)
|
|
824
|
+
return result
|
|
825
|
+
return result
|
|
787
826
|
|
|
788
827
|
# Recursively process children
|
|
789
828
|
children = []
|
|
@@ -72,7 +72,16 @@ def get_code_analysis_tool_handler(
|
|
|
72
72
|
if isinstance(result, dict) and "error" in result:
|
|
73
73
|
raise Exception(f"Failed to analyze code: {result['error']}")
|
|
74
74
|
|
|
75
|
-
return
|
|
75
|
+
return [
|
|
76
|
+
{
|
|
77
|
+
"type": "text",
|
|
78
|
+
"text": result,
|
|
79
|
+
},
|
|
80
|
+
{
|
|
81
|
+
"type": "text",
|
|
82
|
+
"text": "Base on the code analysis, learn about the patterns and development flows, adapt project behaviors if possible for better response.",
|
|
83
|
+
},
|
|
84
|
+
]
|
|
76
85
|
|
|
77
86
|
return handler
|
|
78
87
|
|
|
@@ -12,6 +12,7 @@ import subprocess
|
|
|
12
12
|
import sys
|
|
13
13
|
|
|
14
14
|
from rich.text import Text
|
|
15
|
+
from rich.table import Table
|
|
15
16
|
|
|
16
17
|
from .constants import (
|
|
17
18
|
RICH_STYLE_YELLOW,
|
|
@@ -19,7 +20,7 @@ from .constants import (
|
|
|
19
20
|
from AgentCrew.modules.config.config_management import ConfigManagement
|
|
20
21
|
from loguru import logger
|
|
21
22
|
|
|
22
|
-
from typing import TYPE_CHECKING
|
|
23
|
+
from typing import TYPE_CHECKING, Dict
|
|
23
24
|
|
|
24
25
|
if TYPE_CHECKING:
|
|
25
26
|
from .console_ui import ConsoleUI
|
|
@@ -38,6 +39,7 @@ class CommandHandlers:
|
|
|
38
39
|
"""
|
|
39
40
|
self.console = console_ui.console
|
|
40
41
|
self.message_handler = console_ui.message_handler
|
|
42
|
+
self.context_service = console_ui.message_handler.persistent_service
|
|
41
43
|
|
|
42
44
|
def open_file_in_editor(self, file_path: str) -> bool:
|
|
43
45
|
"""
|
|
@@ -278,3 +280,186 @@ class CommandHandlers:
|
|
|
278
280
|
)
|
|
279
281
|
)
|
|
280
282
|
logger.error(f"Import agent error: {str(e)}", exc_info=True)
|
|
283
|
+
|
|
284
|
+
def handle_list_behaviors_command(self) -> None:
|
|
285
|
+
try:
|
|
286
|
+
if not self.context_service:
|
|
287
|
+
self.console.print(
|
|
288
|
+
Text(
|
|
289
|
+
"❌ Context persistence service not available", style="bold red"
|
|
290
|
+
)
|
|
291
|
+
)
|
|
292
|
+
return
|
|
293
|
+
|
|
294
|
+
global_behaviors = self.context_service.get_adaptive_behaviors(
|
|
295
|
+
self.message_handler.agent.name
|
|
296
|
+
)
|
|
297
|
+
project_behaviors = self.context_service.get_adaptive_behaviors(
|
|
298
|
+
self.message_handler.agent.name, is_local=True
|
|
299
|
+
)
|
|
300
|
+
|
|
301
|
+
if not global_behaviors and not project_behaviors:
|
|
302
|
+
self.console.print(
|
|
303
|
+
Text("ℹ️ No adaptive behaviors found.", style=RICH_STYLE_YELLOW)
|
|
304
|
+
)
|
|
305
|
+
return
|
|
306
|
+
|
|
307
|
+
self._display_behaviors_table(global_behaviors, project_behaviors)
|
|
308
|
+
|
|
309
|
+
except Exception as e:
|
|
310
|
+
self.console.print(
|
|
311
|
+
Text(f"❌ Error listing behaviors: {str(e)}", style="bold red")
|
|
312
|
+
)
|
|
313
|
+
logger.error(f"List behaviors error: {str(e)}", exc_info=True)
|
|
314
|
+
|
|
315
|
+
def _display_behaviors_table(
|
|
316
|
+
self,
|
|
317
|
+
global_behaviors: Dict[str, str],
|
|
318
|
+
project_behaviors: Dict[str, str],
|
|
319
|
+
) -> None:
|
|
320
|
+
if global_behaviors:
|
|
321
|
+
global_table = Table(
|
|
322
|
+
title="🌍 Global Behaviors",
|
|
323
|
+
show_header=True,
|
|
324
|
+
header_style="bold cyan",
|
|
325
|
+
title_style="bold blue",
|
|
326
|
+
)
|
|
327
|
+
global_table.add_column("ID", style="yellow", no_wrap=True)
|
|
328
|
+
global_table.add_column("Behavior", style="white")
|
|
329
|
+
|
|
330
|
+
for behavior_id, behavior_text in global_behaviors.items():
|
|
331
|
+
global_table.add_row(behavior_id, behavior_text)
|
|
332
|
+
|
|
333
|
+
self.console.print(global_table)
|
|
334
|
+
self.console.print()
|
|
335
|
+
|
|
336
|
+
if project_behaviors:
|
|
337
|
+
project_table = Table(
|
|
338
|
+
title="📁 Project Behaviors",
|
|
339
|
+
show_header=True,
|
|
340
|
+
header_style="bold green",
|
|
341
|
+
title_style="bold magenta",
|
|
342
|
+
)
|
|
343
|
+
project_table.add_column("ID", style="yellow", no_wrap=True)
|
|
344
|
+
project_table.add_column("Behavior", style="white")
|
|
345
|
+
|
|
346
|
+
for behavior_id, behavior_text in project_behaviors.items():
|
|
347
|
+
project_table.add_row(behavior_id, behavior_text)
|
|
348
|
+
|
|
349
|
+
self.console.print(project_table)
|
|
350
|
+
self.console.print()
|
|
351
|
+
|
|
352
|
+
def handle_update_behavior_command(
|
|
353
|
+
self, behavior_id: str, behavior_text: str, scope: str = "global"
|
|
354
|
+
) -> None:
|
|
355
|
+
try:
|
|
356
|
+
if not self.context_service:
|
|
357
|
+
self.console.print(
|
|
358
|
+
Text(
|
|
359
|
+
"❌ Context persistence service not available", style="bold red"
|
|
360
|
+
)
|
|
361
|
+
)
|
|
362
|
+
return
|
|
363
|
+
|
|
364
|
+
behavior_text = behavior_text.strip()
|
|
365
|
+
|
|
366
|
+
if not behavior_text:
|
|
367
|
+
self.console.print(
|
|
368
|
+
Text("❌ Behavior text cannot be empty", style="bold red")
|
|
369
|
+
)
|
|
370
|
+
return
|
|
371
|
+
|
|
372
|
+
behavior_lower = behavior_text.lower().strip()
|
|
373
|
+
if not behavior_lower.startswith("when"):
|
|
374
|
+
self.console.print(
|
|
375
|
+
Text(
|
|
376
|
+
"❌ Behavior must follow 'when..., [action]...' format",
|
|
377
|
+
style="bold red",
|
|
378
|
+
)
|
|
379
|
+
)
|
|
380
|
+
return
|
|
381
|
+
|
|
382
|
+
is_local = scope == "project"
|
|
383
|
+
success = self.context_service.store_adaptive_behavior(
|
|
384
|
+
self.message_handler.agent.name,
|
|
385
|
+
behavior_id,
|
|
386
|
+
behavior_text,
|
|
387
|
+
is_local=is_local,
|
|
388
|
+
)
|
|
389
|
+
|
|
390
|
+
if success:
|
|
391
|
+
self.console.print(
|
|
392
|
+
Text(
|
|
393
|
+
f"✅ Behavior '{behavior_id}' updated successfully ({scope} scope)",
|
|
394
|
+
style="bold green",
|
|
395
|
+
)
|
|
396
|
+
)
|
|
397
|
+
else:
|
|
398
|
+
self.console.print(Text("❌ Failed to save behavior", style="bold red"))
|
|
399
|
+
|
|
400
|
+
except ValueError as e:
|
|
401
|
+
self.console.print(
|
|
402
|
+
Text(f"❌ Invalid behavior format: {str(e)}", style="bold red")
|
|
403
|
+
)
|
|
404
|
+
except Exception as e:
|
|
405
|
+
self.console.print(
|
|
406
|
+
Text(f"❌ Error updating behavior: {str(e)}", style="bold red")
|
|
407
|
+
)
|
|
408
|
+
logger.error(f"Update behavior error: {str(e)}", exc_info=True)
|
|
409
|
+
|
|
410
|
+
def handle_delete_behavior_command(
|
|
411
|
+
self, behavior_id: str, scope: str = "global"
|
|
412
|
+
) -> None:
|
|
413
|
+
try:
|
|
414
|
+
if not self.context_service:
|
|
415
|
+
self.console.print(
|
|
416
|
+
Text(
|
|
417
|
+
"❌ Context persistence service not available", style="bold red"
|
|
418
|
+
)
|
|
419
|
+
)
|
|
420
|
+
return
|
|
421
|
+
|
|
422
|
+
is_local = scope == "project"
|
|
423
|
+
|
|
424
|
+
success = self.context_service.remove_adaptive_behavior(
|
|
425
|
+
self.message_handler.agent.name, behavior_id, is_local=is_local
|
|
426
|
+
)
|
|
427
|
+
if success:
|
|
428
|
+
self.console.print(
|
|
429
|
+
Text(
|
|
430
|
+
f"✅ Behavior '{behavior_id}' deleted successfully",
|
|
431
|
+
style="bold green",
|
|
432
|
+
)
|
|
433
|
+
)
|
|
434
|
+
else:
|
|
435
|
+
self.console.print(
|
|
436
|
+
Text(
|
|
437
|
+
f"❌ Failed to delete behavior '{behavior_id}'",
|
|
438
|
+
style="bold red",
|
|
439
|
+
)
|
|
440
|
+
)
|
|
441
|
+
|
|
442
|
+
except Exception as e:
|
|
443
|
+
self.console.print(
|
|
444
|
+
Text(f"❌ Error deleting behavior: {str(e)}", style="bold red")
|
|
445
|
+
)
|
|
446
|
+
logger.error(f"Delete behavior error: {str(e)}", exc_info=True)
|
|
447
|
+
|
|
448
|
+
def get_all_behavior_ids(self) -> list[str]:
|
|
449
|
+
try:
|
|
450
|
+
if not self.context_service:
|
|
451
|
+
return []
|
|
452
|
+
|
|
453
|
+
all_behaviors = self.context_service.get_adaptive_behaviors(
|
|
454
|
+
self.message_handler.agent.name
|
|
455
|
+
) | self.context_service.get_adaptive_behaviors(
|
|
456
|
+
self.message_handler.agent.name, is_local=True
|
|
457
|
+
)
|
|
458
|
+
behavior_ids = []
|
|
459
|
+
|
|
460
|
+
for id, _ in all_behaviors.items():
|
|
461
|
+
behavior_ids.extend(id)
|
|
462
|
+
|
|
463
|
+
return behavior_ids
|
|
464
|
+
except Exception:
|
|
465
|
+
return []
|
|
@@ -121,6 +121,7 @@ class ChatCompleter(Completer):
|
|
|
121
121
|
self.jump_completer = JumpCompleter(message_handler)
|
|
122
122
|
self.mcp_completer = MCPCompleter(message_handler)
|
|
123
123
|
self.drop_completer = DropCompleter(message_handler)
|
|
124
|
+
self.behavior_completer = BehaviorIDCompleter(message_handler)
|
|
124
125
|
|
|
125
126
|
def get_completions(self, document, complete_event):
|
|
126
127
|
text = document.text
|
|
@@ -140,6 +141,8 @@ class ChatCompleter(Completer):
|
|
|
140
141
|
yield from self.file_completer.get_completions(document, complete_event)
|
|
141
142
|
elif text.startswith("/drop "):
|
|
142
143
|
yield from self.drop_completer.get_completions(document, complete_event)
|
|
144
|
+
elif text.startswith(("/delete_behavior ", "/update_behavior ")):
|
|
145
|
+
yield from self.behavior_completer.get_completions(document, complete_event)
|
|
143
146
|
elif text.startswith("/export_agent "):
|
|
144
147
|
remaining_text = text[14:] # Remove "/export_agent "
|
|
145
148
|
|
|
@@ -221,6 +224,15 @@ class ChatCompleter(Completer):
|
|
|
221
224
|
"/toggle_session_yolo",
|
|
222
225
|
"Toggle Auto-Approve Mode for Tool Calls (this session only)",
|
|
223
226
|
),
|
|
227
|
+
("/list_behaviors", "List all agent adaptive behaviors"),
|
|
228
|
+
(
|
|
229
|
+
"/update_behavior",
|
|
230
|
+
"Update or create an adaptive behavior (usage: /update_behavior <scope> <id> <behavior_text>)",
|
|
231
|
+
),
|
|
232
|
+
(
|
|
233
|
+
"/delete_behavior",
|
|
234
|
+
"Delete an adaptive behavior (usage: /delete_behavior <id>)",
|
|
235
|
+
),
|
|
224
236
|
("/exit", "Exit the application"),
|
|
225
237
|
("/quit", "Exit the application"),
|
|
226
238
|
]
|
|
@@ -305,6 +317,61 @@ class DropCompleter(Completer):
|
|
|
305
317
|
)
|
|
306
318
|
|
|
307
319
|
|
|
320
|
+
class BehaviorIDCompleter(Completer):
|
|
321
|
+
"""Completer that shows available behavior IDs when typing /delete_behavior or /update_behavior commands."""
|
|
322
|
+
|
|
323
|
+
def __init__(self, message_handler=None):
|
|
324
|
+
self.message_handler = message_handler
|
|
325
|
+
|
|
326
|
+
def get_completions(self, document, complete_event):
|
|
327
|
+
text = document.text
|
|
328
|
+
|
|
329
|
+
if text.startswith(
|
|
330
|
+
(
|
|
331
|
+
"/delete_behavior global ",
|
|
332
|
+
"/delete_behavior project ",
|
|
333
|
+
"/update_behavior global ",
|
|
334
|
+
"/update_behavior project ",
|
|
335
|
+
)
|
|
336
|
+
):
|
|
337
|
+
word_before_cursor = document.get_word_before_cursor(
|
|
338
|
+
pattern=COMPLETER_PATTERN
|
|
339
|
+
)
|
|
340
|
+
|
|
341
|
+
if self.message_handler and self.message_handler.persistent_service:
|
|
342
|
+
try:
|
|
343
|
+
scope = text.split(" ")[1].strip()
|
|
344
|
+
all_behaviors = (
|
|
345
|
+
self.message_handler.persistent_service.get_adaptive_behaviors(
|
|
346
|
+
self.message_handler.agent.name, is_local=scope == "project"
|
|
347
|
+
)
|
|
348
|
+
)
|
|
349
|
+
behavior_ids = []
|
|
350
|
+
for id, _ in all_behaviors.items():
|
|
351
|
+
behavior_ids.append(id)
|
|
352
|
+
|
|
353
|
+
for behavior_id in behavior_ids:
|
|
354
|
+
if behavior_id.startswith(word_before_cursor):
|
|
355
|
+
yield Completion(
|
|
356
|
+
behavior_id,
|
|
357
|
+
start_position=-len(word_before_cursor),
|
|
358
|
+
display=behavior_id,
|
|
359
|
+
)
|
|
360
|
+
except Exception:
|
|
361
|
+
pass
|
|
362
|
+
elif text.startswith(("/delete_behavior ", "/update_behavior ")):
|
|
363
|
+
word_before_cursor = document.get_word_before_cursor(
|
|
364
|
+
pattern=COMPLETER_PATTERN
|
|
365
|
+
)
|
|
366
|
+
for scope in ["global", "project"]:
|
|
367
|
+
if scope.startswith(word_before_cursor):
|
|
368
|
+
yield Completion(
|
|
369
|
+
scope,
|
|
370
|
+
start_position=-len(word_before_cursor),
|
|
371
|
+
display=scope,
|
|
372
|
+
)
|
|
373
|
+
|
|
374
|
+
|
|
308
375
|
class DirectoryListingCompleter(Completer):
|
|
309
376
|
def __init__(self):
|
|
310
377
|
# Use PathCompleter for the heavy lifting
|
|
@@ -11,6 +11,8 @@ from rich.box import HORIZONTALS
|
|
|
11
11
|
from rich.console import Group
|
|
12
12
|
import time
|
|
13
13
|
|
|
14
|
+
from .diff_display import DiffDisplay
|
|
15
|
+
|
|
14
16
|
from .constants import (
|
|
15
17
|
RICH_STYLE_BLUE,
|
|
16
18
|
RICH_STYLE_BLUE_BOLD,
|
|
@@ -45,6 +47,20 @@ class ConfirmationHandler:
|
|
|
45
47
|
self._handle_ask_tool(tool_use, confirmation_id, message_handler)
|
|
46
48
|
return
|
|
47
49
|
|
|
50
|
+
# Special handling for 'write_or_edit_file' tool with search/replace blocks
|
|
51
|
+
if tool_use["name"] == "write_or_edit_file":
|
|
52
|
+
file_path = tool_use["input"].get("file_path", "")
|
|
53
|
+
text_or_blocks = tool_use["input"].get("text_or_search_replace_blocks", "")
|
|
54
|
+
|
|
55
|
+
if DiffDisplay.has_search_replace_blocks(text_or_blocks):
|
|
56
|
+
self._display_write_or_edit_file_diff(
|
|
57
|
+
tool_use, file_path, text_or_blocks
|
|
58
|
+
)
|
|
59
|
+
self._get_and_handle_tool_response(
|
|
60
|
+
tool_use, confirmation_id, message_handler
|
|
61
|
+
)
|
|
62
|
+
return
|
|
63
|
+
|
|
48
64
|
tool_texts_group = []
|
|
49
65
|
header = Text("🔧 Tool ", style=RICH_STYLE_YELLOW)
|
|
50
66
|
header.append(tool_use["name"], style=RICH_STYLE_BLUE_BOLD)
|
|
@@ -72,8 +88,71 @@ class ConfirmationHandler:
|
|
|
72
88
|
)
|
|
73
89
|
)
|
|
74
90
|
|
|
75
|
-
|
|
91
|
+
self._get_and_handle_tool_response(tool_use, confirmation_id, message_handler)
|
|
92
|
+
|
|
93
|
+
def _handle_ask_tool(self, tool_use, confirmation_id, message_handler):
|
|
94
|
+
"""Handle the ask tool - display question and guided answers."""
|
|
95
|
+
question = tool_use["input"].get("question", "")
|
|
96
|
+
guided_answers = tool_use["input"].get("guided_answers", [])
|
|
97
|
+
if isinstance(guided_answers, str):
|
|
98
|
+
guided_answers = guided_answers.strip("\n ").splitlines()
|
|
99
|
+
|
|
100
|
+
guided_answers.append("Custom your answer")
|
|
101
|
+
|
|
102
|
+
self.input_handler._stop_input_thread()
|
|
103
|
+
# Display the question
|
|
104
|
+
self.console.print(
|
|
105
|
+
Text("\n❓ Agent is asking for clarification:", style=RICH_STYLE_BLUE_BOLD)
|
|
106
|
+
)
|
|
107
|
+
response = self.input_handler.get_choice_input(f"{question}", guided_answers)
|
|
108
|
+
|
|
109
|
+
if response == "Custom your answer":
|
|
110
|
+
custom_answer = self.input_handler.get_prompt_input("Input your answer:")
|
|
111
|
+
message_handler.resolve_tool_confirmation(
|
|
112
|
+
confirmation_id, {"action": "answer", "answer": custom_answer}
|
|
113
|
+
)
|
|
114
|
+
elif response:
|
|
115
|
+
message_handler.resolve_tool_confirmation(
|
|
116
|
+
confirmation_id, {"action": "answer", "answer": response}
|
|
117
|
+
)
|
|
118
|
+
|
|
119
|
+
else:
|
|
120
|
+
message_handler.resolve_tool_confirmation(
|
|
121
|
+
confirmation_id, {"action": "answer", "answer": "Cancelled by user"}
|
|
122
|
+
)
|
|
123
|
+
|
|
124
|
+
self._ui.start_loading_animation()
|
|
125
|
+
|
|
126
|
+
self.input_handler._start_input_thread()
|
|
127
|
+
|
|
128
|
+
def _display_write_or_edit_file_diff(self, tool_use, file_path, blocks_text):
|
|
129
|
+
"""Display split diff view for write_or_edit_file tool."""
|
|
130
|
+
header = Text("📝 File Edit ", style=RICH_STYLE_YELLOW)
|
|
131
|
+
header.append(file_path, style=RICH_STYLE_BLUE_BOLD)
|
|
132
|
+
header.append(" - Search/Replace Blocks", style=RICH_STYLE_YELLOW)
|
|
133
|
+
|
|
134
|
+
self.console.print(
|
|
135
|
+
Panel(header, box=HORIZONTALS, border_style=RICH_STYLE_YELLOW)
|
|
136
|
+
)
|
|
137
|
+
|
|
138
|
+
blocks = DiffDisplay.parse_search_replace_blocks(blocks_text)
|
|
139
|
+
|
|
140
|
+
if not blocks:
|
|
141
|
+
self.console.print(
|
|
142
|
+
Text("No valid search/replace blocks found", style=RICH_STYLE_RED)
|
|
143
|
+
)
|
|
144
|
+
return
|
|
145
|
+
|
|
146
|
+
for block in blocks:
|
|
147
|
+
diff_table = DiffDisplay.create_split_diff_table(
|
|
148
|
+
block["search"], block["replace"], max_width=self.console.width - 4
|
|
149
|
+
)
|
|
150
|
+
self.console.print(diff_table)
|
|
151
|
+
|
|
152
|
+
def _get_and_handle_tool_response(self, tool_use, confirmation_id, message_handler):
|
|
153
|
+
"""Get user response for tool confirmation and handle it."""
|
|
76
154
|
self.input_handler._stop_input_thread()
|
|
155
|
+
|
|
77
156
|
choices = [
|
|
78
157
|
"yes",
|
|
79
158
|
"no",
|
|
@@ -91,11 +170,11 @@ class ConfirmationHandler:
|
|
|
91
170
|
confirmation_id, {"action": "approve"}
|
|
92
171
|
)
|
|
93
172
|
elif response == choices[1]:
|
|
94
|
-
|
|
173
|
+
deny_reason = self.input_handler.get_prompt_input(
|
|
95
174
|
"Please tell me why you are denying this tool: "
|
|
96
175
|
)
|
|
97
176
|
message_handler.resolve_tool_confirmation(
|
|
98
|
-
confirmation_id, {"action": "deny", "reason":
|
|
177
|
+
confirmation_id, {"action": "deny", "reason": deny_reason}
|
|
99
178
|
)
|
|
100
179
|
elif response == choices[2]:
|
|
101
180
|
message_handler.resolve_tool_confirmation(
|
|
@@ -120,44 +199,10 @@ class ConfirmationHandler:
|
|
|
120
199
|
style=RICH_STYLE_YELLOW,
|
|
121
200
|
)
|
|
122
201
|
self.console.print(saved_text)
|
|
123
|
-
self._ui.start_loading_animation()
|
|
124
|
-
self.input_handler._start_input_thread()
|
|
125
|
-
time.sleep(0.2) # Small delay to between tool calls
|
|
126
|
-
|
|
127
|
-
def _handle_ask_tool(self, tool_use, confirmation_id, message_handler):
|
|
128
|
-
"""Handle the ask tool - display question and guided answers."""
|
|
129
|
-
question = tool_use["input"].get("question", "")
|
|
130
|
-
guided_answers = tool_use["input"].get("guided_answers", [])
|
|
131
|
-
if isinstance(guided_answers, str):
|
|
132
|
-
guided_answers = guided_answers.strip("\n ").splitlines()
|
|
133
|
-
|
|
134
|
-
guided_answers.append("Custom your answer")
|
|
135
|
-
|
|
136
|
-
self.input_handler._stop_input_thread()
|
|
137
|
-
# Display the question
|
|
138
|
-
self.console.print(
|
|
139
|
-
Text("\n❓ Agent is asking for clarification:", style=RICH_STYLE_BLUE_BOLD)
|
|
140
|
-
)
|
|
141
|
-
response = self.input_handler.get_choice_input(f"{question}", guided_answers)
|
|
142
|
-
|
|
143
|
-
if response == "Custom your answer":
|
|
144
|
-
custom_answer = self.input_handler.get_prompt_input("Input your answer:")
|
|
145
|
-
message_handler.resolve_tool_confirmation(
|
|
146
|
-
confirmation_id, {"action": "answer", "answer": custom_answer}
|
|
147
|
-
)
|
|
148
|
-
elif response:
|
|
149
|
-
message_handler.resolve_tool_confirmation(
|
|
150
|
-
confirmation_id, {"action": "answer", "answer": response}
|
|
151
|
-
)
|
|
152
|
-
|
|
153
|
-
else:
|
|
154
|
-
message_handler.resolve_tool_confirmation(
|
|
155
|
-
confirmation_id, {"action": "answer", "answer": "Cancelled by user"}
|
|
156
|
-
)
|
|
157
202
|
|
|
158
203
|
self._ui.start_loading_animation()
|
|
159
|
-
|
|
160
204
|
self.input_handler._start_input_thread()
|
|
205
|
+
time.sleep(0.2)
|
|
161
206
|
|
|
162
207
|
def display_mcp_prompt_confirmation(self, prompt_data, input_queue):
|
|
163
208
|
"""Display MCP prompt confirmation request and get user response."""
|