agentcrew-ai 0.8.2__py3-none-any.whl → 0.8.4__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 +11 -0
- AgentCrew/modules/browser_automation/element_extractor.py +4 -3
- AgentCrew/modules/browser_automation/js/draw_element_boxes.js +200 -0
- AgentCrew/modules/browser_automation/js/extract_clickable_elements.js +58 -26
- AgentCrew/modules/browser_automation/js/extract_elements_by_text.js +21 -19
- AgentCrew/modules/browser_automation/js/extract_input_elements.js +22 -23
- AgentCrew/modules/browser_automation/js/filter_hidden_elements.js +104 -0
- AgentCrew/modules/browser_automation/js/remove_element_boxes.js +29 -0
- AgentCrew/modules/browser_automation/js_loader.py +385 -92
- AgentCrew/modules/browser_automation/service.py +118 -347
- AgentCrew/modules/browser_automation/tool.py +28 -29
- AgentCrew/modules/chat/message/command_processor.py +7 -1
- AgentCrew/modules/chat/message/conversation.py +9 -8
- 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/console_ui.py +59 -5
- AgentCrew/modules/console/display_handlers.py +12 -0
- AgentCrew/modules/console/input_handler.py +2 -0
- AgentCrew/modules/console/ui_effects.py +3 -4
- AgentCrew/modules/custom_llm/service.py +25 -3
- AgentCrew/modules/file_editing/tool.py +9 -11
- AgentCrew/modules/google/native_service.py +13 -0
- AgentCrew/modules/gui/widgets/message_bubble.py +1 -6
- AgentCrew/modules/llm/constants.py +38 -1
- AgentCrew/modules/llm/model_registry.py +9 -0
- AgentCrew/modules/llm/types.py +12 -1
- AgentCrew/modules/memory/base_service.py +2 -2
- AgentCrew/modules/memory/chroma_service.py +79 -138
- AgentCrew/modules/memory/context_persistent.py +10 -4
- AgentCrew/modules/memory/tool.py +17 -18
- AgentCrew/modules/openai/response_service.py +19 -11
- AgentCrew/modules/openai/service.py +15 -0
- AgentCrew/modules/prompts/constants.py +27 -14
- {agentcrew_ai-0.8.2.dist-info → agentcrew_ai-0.8.4.dist-info}/METADATA +3 -3
- {agentcrew_ai-0.8.2.dist-info → agentcrew_ai-0.8.4.dist-info}/RECORD +47 -43
- {agentcrew_ai-0.8.2.dist-info → agentcrew_ai-0.8.4.dist-info}/WHEEL +0 -0
- {agentcrew_ai-0.8.2.dist-info → agentcrew_ai-0.8.4.dist-info}/entry_points.txt +0 -0
- {agentcrew_ai-0.8.2.dist-info → agentcrew_ai-0.8.4.dist-info}/licenses/LICENSE +0 -0
- {agentcrew_ai-0.8.2.dist-info → agentcrew_ai-0.8.4.dist-info}/top_level.txt +0 -0
|
@@ -52,13 +52,11 @@ def get_browser_navigate_tool_definition(provider="claude") -> Dict[str, Any]:
|
|
|
52
52
|
|
|
53
53
|
def get_browser_click_tool_definition(provider="claude") -> Dict[str, Any]:
|
|
54
54
|
"""Get tool definition for browser element clicking."""
|
|
55
|
-
tool_description =
|
|
56
|
-
"Click an element using its UUID. Get UUIDs from browser_get_content first."
|
|
57
|
-
)
|
|
55
|
+
tool_description = "Click an element using its UUID. Get UUIDs from get_browser_content tool result first."
|
|
58
56
|
tool_arguments = {
|
|
59
57
|
"element_uuid": {
|
|
60
58
|
"type": "string",
|
|
61
|
-
"description": "UUID identifier from
|
|
59
|
+
"description": "UUID identifier from get_browser_content tool result clickable elements table.",
|
|
62
60
|
}
|
|
63
61
|
}
|
|
64
62
|
tool_required = ["element_uuid"]
|
|
@@ -109,9 +107,10 @@ def get_browser_scroll_tool_definition(provider="claude") -> Dict[str, Any]:
|
|
|
109
107
|
"element_uuid": {
|
|
110
108
|
"type": "string",
|
|
111
109
|
"description": "Optional UUID for specific element to scroll. Scrolls document if not provided.",
|
|
110
|
+
"default": "document",
|
|
112
111
|
},
|
|
113
112
|
}
|
|
114
|
-
tool_required = ["direction"]
|
|
113
|
+
tool_required = ["direction", "element_uuid"]
|
|
115
114
|
|
|
116
115
|
if provider == "claude":
|
|
117
116
|
return {
|
|
@@ -142,7 +141,7 @@ def get_browser_get_content_tool_definition(provider="claude") -> Dict[str, Any]
|
|
|
142
141
|
"""Get tool definition for browser content extraction."""
|
|
143
142
|
tool_description = (
|
|
144
143
|
"Extract page content as markdown with tables of clickable, input, and scrollable elements. UUIDs reset on each call."
|
|
145
|
-
"
|
|
144
|
+
"get_browser_content tool's result is UNIQUE in whole conversation. Remember to summarize important information before calling again."
|
|
146
145
|
)
|
|
147
146
|
tool_arguments = {}
|
|
148
147
|
tool_required = []
|
|
@@ -196,7 +195,7 @@ def get_browser_get_content_tool_handler(
|
|
|
196
195
|
tool_result.append(context_image.get("screenshot", {}))
|
|
197
196
|
return tool_result
|
|
198
197
|
else:
|
|
199
|
-
raise RuntimeError(f"
|
|
198
|
+
raise RuntimeError(f"Content extraction failed: {result['error']}")
|
|
200
199
|
|
|
201
200
|
return handle_browser_get_content
|
|
202
201
|
|
|
@@ -221,9 +220,9 @@ def get_browser_navigate_tool_handler(
|
|
|
221
220
|
if result.get("profile")
|
|
222
221
|
else ""
|
|
223
222
|
)
|
|
224
|
-
return f"
|
|
223
|
+
return f"{result.get('message', 'Success')}. Call `get_browser_content` tool to read the url content.\nCurrent URL: {result.get('current_url', 'Unknown')}{profile_info}"
|
|
225
224
|
else:
|
|
226
|
-
raise RuntimeError(f"
|
|
225
|
+
raise RuntimeError(f"Navigation failed: {result['error']}")
|
|
227
226
|
|
|
228
227
|
return handle_browser_navigate
|
|
229
228
|
|
|
@@ -241,16 +240,15 @@ def get_browser_click_tool_handler(
|
|
|
241
240
|
|
|
242
241
|
result = browser_service.click_element(element_uuid)
|
|
243
242
|
|
|
244
|
-
diff_summary = _get_content_delta_changes(browser_service)
|
|
245
|
-
|
|
246
243
|
if result.get("success", True):
|
|
244
|
+
diff_summary = _get_content_delta_changes(browser_service)
|
|
247
245
|
return (
|
|
248
|
-
f"
|
|
246
|
+
f"{result.get('message', 'Success')}. Call `get_browser_content` tool to get the updated content.\n"
|
|
249
247
|
f"UUID: {element_uuid}\nClickedElement: {result.get('elementInfo', {}).get('text', 'Unknown')}.\n"
|
|
250
248
|
f"Content delta changes:\n{diff_summary}"
|
|
251
249
|
)
|
|
252
250
|
else:
|
|
253
|
-
return f"
|
|
251
|
+
return f"Click failed: {result['error']}\nUUID: {element_uuid}.\nCall `get_browser_content` tool to get the updated UUID"
|
|
254
252
|
|
|
255
253
|
return handle_browser_click
|
|
256
254
|
|
|
@@ -273,24 +271,26 @@ def get_browser_scroll_tool_handler(
|
|
|
273
271
|
"Error: Invalid scroll direction. Use 'up', 'down', 'left', or 'right'."
|
|
274
272
|
)
|
|
275
273
|
|
|
276
|
-
result = browser_service.scroll_page(
|
|
274
|
+
result = browser_service.scroll_page(
|
|
275
|
+
direction, amount, element_uuid if element_uuid != "document" else None
|
|
276
|
+
)
|
|
277
277
|
|
|
278
278
|
if result.get("success", True):
|
|
279
|
-
return f"
|
|
279
|
+
return f"{result.get('message', 'Success')}, Call `get_browser_content` tool to get the updated content."
|
|
280
280
|
else:
|
|
281
281
|
uuid_info = f"\nUUID: {element_uuid}" if element_uuid else ""
|
|
282
|
-
raise RuntimeError(f"
|
|
282
|
+
raise RuntimeError(f"Scroll failed: {result['error']}{uuid_info}")
|
|
283
283
|
|
|
284
284
|
return handle_browser_scroll
|
|
285
285
|
|
|
286
286
|
|
|
287
287
|
def get_browser_input_tool_definition(provider="claude") -> Dict[str, Any]:
|
|
288
288
|
"""Get tool definition for browser input."""
|
|
289
|
-
tool_description = "Input data into form fields using UUID. Get UUIDs from
|
|
289
|
+
tool_description = "Input data into form fields using UUID. Get UUIDs from get_browser_content tool first."
|
|
290
290
|
tool_arguments = {
|
|
291
291
|
"element_uuid": {
|
|
292
292
|
"type": "string",
|
|
293
|
-
"description": "UUID identifier from
|
|
293
|
+
"description": "UUID identifier from get_browser_content tool result's input elements table.",
|
|
294
294
|
},
|
|
295
295
|
"value": {
|
|
296
296
|
"type": "string",
|
|
@@ -340,13 +340,13 @@ def get_browser_input_tool_handler(
|
|
|
340
340
|
return "Error: No value provided for input."
|
|
341
341
|
|
|
342
342
|
result = browser_service.input_data(element_uuid, str(value))
|
|
343
|
-
diff_summary = _get_content_delta_changes(browser_service)
|
|
344
343
|
|
|
345
344
|
if result.get("success", True):
|
|
346
|
-
|
|
345
|
+
diff_summary = _get_content_delta_changes(browser_service)
|
|
346
|
+
return f"{result.get('message', 'Success')}\nUUID: {element_uuid}\nValue: {value}\nContent delta changes:\n{diff_summary}"
|
|
347
347
|
else:
|
|
348
348
|
raise RuntimeError(
|
|
349
|
-
f"
|
|
349
|
+
f"Input failed: {result['error']}\nUUID: {element_uuid}\nValue: {value}.\n Call `get_browser_content` tool to get updated UUID."
|
|
350
350
|
)
|
|
351
351
|
|
|
352
352
|
return handle_browser_input
|
|
@@ -405,16 +405,15 @@ def get_browser_get_elements_by_text_tool_handler(
|
|
|
405
405
|
if result.get("success", False):
|
|
406
406
|
elements_found = result.get("elements_found", 0)
|
|
407
407
|
if elements_found == 0:
|
|
408
|
-
return f"
|
|
408
|
+
return f"No elements found containing text: '{text}'"
|
|
409
409
|
|
|
410
410
|
content = result.get("content", "")
|
|
411
411
|
return (
|
|
412
|
-
f"
|
|
413
|
-
+ content
|
|
412
|
+
f"Found {elements_found} elements containing text: '{text}'\n" + content
|
|
414
413
|
)
|
|
415
414
|
else:
|
|
416
415
|
raise RuntimeError(
|
|
417
|
-
f"
|
|
416
|
+
f"Search failed: {result.get('error', 'Unknown error')}\nSearch text: '{text}'"
|
|
418
417
|
)
|
|
419
418
|
|
|
420
419
|
return handle_browser_get_elements_by_text
|
|
@@ -422,7 +421,7 @@ def get_browser_get_elements_by_text_tool_handler(
|
|
|
422
421
|
|
|
423
422
|
def get_browser_capture_screenshot_tool_definition(provider="claude") -> Dict[str, Any]:
|
|
424
423
|
"""Get tool definition for browser screenshot capture."""
|
|
425
|
-
tool_description = "Capture page screenshot as base64 image data. Supports different formats and full page capture."
|
|
424
|
+
tool_description = "Capture page screenshot as base64 image data with colored boxes and UUID labels drawn over all identified elements. Supports different formats and full page capture."
|
|
426
425
|
tool_arguments = {
|
|
427
426
|
"format": {
|
|
428
427
|
"type": "string",
|
|
@@ -500,7 +499,7 @@ def get_browser_capture_screenshot_tool_handler(
|
|
|
500
499
|
return [screenshot_data]
|
|
501
500
|
else:
|
|
502
501
|
raise RuntimeError(
|
|
503
|
-
f"
|
|
502
|
+
f"Screenshot capture failed: {result.get('error', 'Unknown error')}"
|
|
504
503
|
)
|
|
505
504
|
|
|
506
505
|
return handle_browser_capture_screenshot
|
|
@@ -617,13 +616,13 @@ def get_browser_send_key_tool_handler(
|
|
|
617
616
|
else ""
|
|
618
617
|
)
|
|
619
618
|
diff_summary = _get_content_delta_changes(browser_service)
|
|
620
|
-
success_msg = f"
|
|
619
|
+
success_msg = f"{result.get('message', 'Success')}. {key_info}\nContent delta changes:\n{diff_summary}"
|
|
621
620
|
if modifiers_info:
|
|
622
621
|
success_msg += f". {modifiers_info}"
|
|
623
622
|
return success_msg
|
|
624
623
|
else:
|
|
625
624
|
raise RuntimeError(
|
|
626
|
-
f"
|
|
625
|
+
f"Key send failed: {result.get('error', 'Unknown error')}"
|
|
627
626
|
)
|
|
628
627
|
|
|
629
628
|
return handle_browser_send_key
|
|
@@ -105,8 +105,14 @@ class CommandProcessor:
|
|
|
105
105
|
asssistant_messages_iterator = reversed(
|
|
106
106
|
[
|
|
107
107
|
msg
|
|
108
|
-
for msg in self.message_handler.streamline_messages
|
|
108
|
+
for i, msg in enumerate(self.message_handler.streamline_messages)
|
|
109
109
|
if msg.get("role") == "assistant"
|
|
110
|
+
and (
|
|
111
|
+
self.message_handler.streamline_messages[i + 1].get("role")
|
|
112
|
+
== "user"
|
|
113
|
+
if i + 1 < len(self.message_handler.streamline_messages)
|
|
114
|
+
else True
|
|
115
|
+
)
|
|
110
116
|
]
|
|
111
117
|
)
|
|
112
118
|
latest_assistant_blk = None
|
|
@@ -112,14 +112,6 @@ class ConversationManager:
|
|
|
112
112
|
msg["tool_call_id"] = tool_result.get("tool_use_id", "")
|
|
113
113
|
|
|
114
114
|
self.message_handler.current_conversation_id = conversation_id
|
|
115
|
-
if self.message_handler.memory_service:
|
|
116
|
-
self.message_handler.memory_service.session_id = (
|
|
117
|
-
self.message_handler.current_conversation_id
|
|
118
|
-
)
|
|
119
|
-
self.message_handler.memory_service.loaded_conversation = True
|
|
120
|
-
self.message_handler.memory_service.load_conversation_context(
|
|
121
|
-
self.message_handler.current_conversation_id
|
|
122
|
-
)
|
|
123
115
|
last_agent_name = history[-1].get("agent", "")
|
|
124
116
|
if last_agent_name and self.message_handler.agent_manager.select_agent(
|
|
125
117
|
last_agent_name
|
|
@@ -129,6 +121,15 @@ class ConversationManager:
|
|
|
129
121
|
)
|
|
130
122
|
self.message_handler._notify("agent_changed", last_agent_name)
|
|
131
123
|
|
|
124
|
+
if self.message_handler.memory_service:
|
|
125
|
+
self.message_handler.memory_service.session_id = (
|
|
126
|
+
self.message_handler.current_conversation_id
|
|
127
|
+
)
|
|
128
|
+
self.message_handler.memory_service.loaded_conversation = True
|
|
129
|
+
self.message_handler.memory_service.load_conversation_context(
|
|
130
|
+
self.message_handler.current_conversation_id, last_agent_name
|
|
131
|
+
)
|
|
132
|
+
|
|
132
133
|
self.message_handler.streamline_messages = history
|
|
133
134
|
self.message_handler.agent_manager.rebuild_agents_messages(
|
|
134
135
|
self.message_handler.streamline_messages
|
|
@@ -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
|