agentcrew-ai 0.8.4__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 CHANGED
@@ -1 +1 @@
1
- __version__ = "0.8.4"
1
+ __version__ = "0.8.5"
@@ -604,7 +604,7 @@ class AgentTaskManager(TaskManager):
604
604
  Yields:
605
605
  Streaming responses with task updates
606
606
  """
607
- task_id = request.params.task_id
607
+ task_id = request.params.id
608
608
 
609
609
  if task_id not in self.tasks:
610
610
  error = A2AError.task_not_found(task_id)
@@ -642,7 +642,7 @@ Check if `when` condition in <Global_Behavior> or <Project_Behavior> matches, up
642
642
  adaptive_messages["content"].append(
643
643
  {
644
644
  "type": "text",
645
- "text": f"Here are conversations that we have discussed:\n- {'\n- '.join(memory_headers)}",
645
+ "text": f"Here are conversations that we have discussed from oldest to latest:\n - {'\n - '.join(memory_headers)}",
646
646
  }
647
647
  )
648
648
  if len(adaptive_messages["content"]) > 0:
@@ -68,7 +68,7 @@ def clean_markdown_images(markdown_content: str) -> str:
68
68
  Cleaned markdown content
69
69
  """
70
70
  # Pattern for markdown images: ![alt](url)
71
- markdown_img_pattern = r"!\[([^\]]*)\]\(([^)]+)\)"
71
+ markdown_img_pattern = r"!?\[([^\]]*)\]\(([^)]+)\)"
72
72
 
73
73
  def replace_markdown_img(match):
74
74
  alt_text = match.group(1)
@@ -7,7 +7,7 @@ scroll content, and extract page information using Chrome DevTools Protocol.
7
7
 
8
8
  import time
9
9
  from typing import Dict, Any, Optional, List
10
- from html_to_markdown import convert_to_markdown
10
+ from html_to_markdown import convert, ConversionOptions, PreprocessingOptions
11
11
  import urllib.parse
12
12
 
13
13
  from html.parser import HTMLParser
@@ -344,13 +344,23 @@ class BrowserAutomationService:
344
344
  filtered_html = self._filter_hidden_elements(raw_html)
345
345
 
346
346
  # Convert HTML to markdown
347
- raw_markdown_content = convert_to_markdown(
347
+ # raw_markdown_content = convert_to_markdown(
348
+ # filtered_html,
349
+ # source_encoding="utf-8",
350
+ # strip_newlines=True,
351
+ # extract_metadata=False,
352
+ # remove_forms=False,
353
+ # remove_navigation=False,
354
+ # )
355
+ raw_markdown_content = convert(
348
356
  filtered_html,
349
- source_encoding="utf-8",
350
- strip_newlines=True,
351
- extract_metadata=False,
352
- remove_forms=False,
353
- remove_navigation=False,
357
+ ConversionOptions(
358
+ strip_newlines=True,
359
+ extract_metadata=False,
360
+ ),
361
+ PreprocessingOptions(
362
+ remove_navigation=False, remove_forms=False, preset="minimal"
363
+ ),
354
364
  )
355
365
  if not raw_markdown_content:
356
366
  return {"success": False, "error": "Could not convert HTML to markdown"}
@@ -101,6 +101,9 @@ class CommandProcessor:
101
101
 
102
102
  async def _handle_copy_command(self, user_input: str) -> CommandResult:
103
103
  copy_idx = user_input[5:].strip() or 1
104
+ user_input_idxs = [
105
+ turn.message_index for turn in self.message_handler.conversation_turns
106
+ ]
104
107
 
105
108
  asssistant_messages_iterator = reversed(
106
109
  [
@@ -108,8 +111,7 @@ class CommandProcessor:
108
111
  for i, msg in enumerate(self.message_handler.streamline_messages)
109
112
  if msg.get("role") == "assistant"
110
113
  and (
111
- self.message_handler.streamline_messages[i + 1].get("role")
112
- == "user"
114
+ i + 1 in user_input_idxs
113
115
  if i + 1 < len(self.message_handler.streamline_messages)
114
116
  else True
115
117
  )
@@ -162,6 +162,7 @@ class ConversationManager:
162
162
  "Memories related to the user request:"
163
163
  )
164
164
  and not message_content.startswith("Content of ")
165
+ and not message_content.startswith("<Transfer_Tool>")
165
166
  ):
166
167
  self.store_conversation_turn(message_content, i)
167
168
 
@@ -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.5)
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(
@@ -346,8 +346,8 @@ class CommandHandlers:
346
346
  for behavior_id, behavior_text in project_behaviors.items():
347
347
  project_table.add_row(behavior_id, behavior_text)
348
348
 
349
- self.console.print(project_table)
350
- self.console.print()
349
+ self.console.print(project_table)
350
+ self.console.print()
351
351
 
352
352
  def handle_update_behavior_command(
353
353
  self, behavior_id: str, behavior_text: str, scope: str = "global"
@@ -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
- # Get user response
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
- response = self.input_handler.get_prompt_input(
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": response}
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."""
@@ -20,7 +20,6 @@ from .constants import (
20
20
  RICH_STYLE_GREEN,
21
21
  RICH_STYLE_BLUE,
22
22
  RICH_STYLE_YELLOW,
23
- RICH_STYLE_GREEN_BOLD,
24
23
  RICH_STYLE_YELLOW_BOLD,
25
24
  PROMPT_CHAR,
26
25
  )
@@ -83,11 +82,17 @@ class ConsoleUI(Observer):
83
82
 
84
83
  if event == "thinking_started":
85
84
  self.ui_effects.stop_loading_animation() # Stop loading on first chunk
86
- self.display_handlers.display_thinking_started(data) # data is agent_name
85
+ # self.display_handlers.display_thinking_started(data) # data is agent_name
87
86
  elif event == "thinking_chunk":
88
- self.display_handlers.display_thinking_chunk(
89
- data
90
- ) # data is the thinking chunk
87
+ self.ui_effects.update_live_display(data, is_thinking=True)
88
+ # self.display_handlers.display_thinking_chunk(
89
+ # data
90
+ # ) # data is the thinking chunk
91
+ #
92
+ elif event == "thinking_completed":
93
+ self.ui_effects.finish_response(
94
+ self.ui_effects.updated_text, is_thinking=True
95
+ )
91
96
  elif event == "user_message_created":
92
97
  pass
93
98
  elif event == "response_chunk":
@@ -178,10 +183,6 @@ class ConsoleUI(Observer):
178
183
  f"{data['agent_name'] if 'agent_name' in data else 'other'} agent"
179
184
  )
180
185
  self.display_handlers.display_message(transfer_text)
181
- elif event == "agent_continue":
182
- self.display_handlers.display_message(
183
- Text(f"\n🤖 {data.upper()}:", style=RICH_STYLE_GREEN_BOLD)
184
- )
185
186
  elif event == "jump_performed":
186
187
  jump_text = Text(
187
188
  f"🕰️ Jumping to turn {data['turn_number']}...\n",
@@ -189,12 +190,11 @@ class ConsoleUI(Observer):
189
190
  )
190
191
  preview_text = Text("Conversation rewound to: ", style=RICH_STYLE_YELLOW)
191
192
  preview_text.append(data["preview"])
193
+ self._clean_and_reprint_chat()
192
194
 
193
195
  self.display_handlers.display_message(jump_text)
194
196
  self.display_handlers.display_message(preview_text)
195
197
  self.input_handler.set_current_buffer(data["message"])
196
- elif event == "thinking_completed":
197
- self.display_handlers.display_divider()
198
198
  elif event == "file_processing":
199
199
  self.ui_effects.stop_loading_animation() # Stop loading on first chunk
200
200
  self.display_handlers.add_file(data["file_path"])
@@ -286,25 +286,14 @@ class ConsoleUI(Observer):
286
286
  Signal handler for SIGWINCH.
287
287
  This function is called when the terminal window is resized.
288
288
  """
289
- import os
290
289
  import time
291
290
 
292
291
  if self.input_handler.is_message_processing or self._is_resizing:
293
292
  return # Ignore resize during message processing
294
293
  self._is_resizing = True
295
294
  time.sleep(0.5) # brief pause to allow resize to complete
296
- os.system("cls" if os.name == "nt" else "printf '\033c'")
297
- self.display_handlers.display_loaded_conversation(
298
- self.message_handler.streamline_messages, self.message_handler.agent.name
299
- )
300
- self.display_handlers.print_prompt_prefix(
301
- self.message_handler.agent.name,
302
- self.message_handler.agent.get_model(),
303
- self.message_handler.tool_manager.get_effective_yolo_mode(),
304
- )
305
-
295
+ self._clean_and_reprint_chat()
306
296
  self.display_handlers.print_divider("👤 YOU: ", with_time=True)
307
-
308
297
  prompt = Text(
309
298
  PROMPT_CHAR,
310
299
  style=RICH_STYLE_BLUE,
@@ -318,6 +307,21 @@ class ConsoleUI(Observer):
318
307
  self.console.print(prompt, end="")
319
308
  self._is_resizing = False
320
309
 
310
+ def _clean_and_reprint_chat(self):
311
+ """Clear and reprint the chat display."""
312
+
313
+ import os
314
+
315
+ os.system("cls" if os.name == "nt" else "printf '\033c'")
316
+ self.display_handlers.display_loaded_conversation(
317
+ self.message_handler.streamline_messages, self.message_handler.agent.name
318
+ )
319
+ self.display_handlers.print_prompt_prefix(
320
+ self.message_handler.agent.name,
321
+ self.message_handler.agent.get_model(),
322
+ self.message_handler.tool_manager.get_effective_yolo_mode(),
323
+ )
324
+
321
325
  def start_streaming_response(self, agent_name: str):
322
326
  """Start streaming the assistant's response."""
323
327
  self.ui_effects.start_streaming_response(agent_name)
@@ -0,0 +1,203 @@
1
+ """
2
+ Diff display helper for showing file changes in console UI.
3
+ Provides split view diff display with syntax highlighting.
4
+ """
5
+
6
+ import difflib
7
+ from rich.text import Text
8
+ from rich.table import Table
9
+ from rich.box import SIMPLE_HEAD
10
+ from .constants import (
11
+ RICH_STYLE_BLUE_BOLD,
12
+ )
13
+
14
+
15
+ class DiffDisplay:
16
+ """Helper class for creating split diff views."""
17
+
18
+ @staticmethod
19
+ def has_search_replace_blocks(text: str) -> bool:
20
+ """Check if text contains search/replace blocks."""
21
+ return (
22
+ "<<<<<<< SEARCH" in text and "=======" in text and ">>>>>>> REPLACE" in text
23
+ )
24
+
25
+ @staticmethod
26
+ def parse_search_replace_blocks(blocks_text: str) -> list:
27
+ """
28
+ Parse search/replace blocks from text.
29
+
30
+ Args:
31
+ blocks_text: Text containing search/replace blocks
32
+
33
+ Returns:
34
+ List of dicts with 'index', 'search', and 'replace' keys
35
+ """
36
+ blocks = []
37
+ lines = blocks_text.split("\n")
38
+ i = 0
39
+ block_index = 0
40
+
41
+ while i < len(lines):
42
+ if lines[i].strip() == "<<<<<<< SEARCH":
43
+ search_lines = []
44
+ i += 1
45
+
46
+ while i < len(lines) and lines[i].strip() != "=======":
47
+ search_lines.append(lines[i])
48
+ i += 1
49
+
50
+ if i >= len(lines):
51
+ break
52
+
53
+ i += 1
54
+ replace_lines = []
55
+
56
+ while (
57
+ i < len(lines)
58
+ and lines[i].strip() != ">>>>>>> REPLACE"
59
+ and lines[i].strip() != "======="
60
+ ):
61
+ replace_lines.append(lines[i])
62
+ i += 1
63
+
64
+ if i >= len(lines):
65
+ break
66
+
67
+ blocks.append(
68
+ {
69
+ "index": block_index,
70
+ "search": "\n".join(search_lines),
71
+ "replace": "\n".join(replace_lines),
72
+ }
73
+ )
74
+ block_index += 1
75
+ i += 1
76
+ else:
77
+ i += 1
78
+
79
+ return blocks
80
+
81
+ @staticmethod
82
+ def create_split_diff_table(
83
+ original: str, modified: str, max_width: int = 60
84
+ ) -> Table:
85
+ """
86
+ Create a split diff display table using difflib for intelligent comparison.
87
+
88
+ Args:
89
+ original: Original text content
90
+ modified: Modified text content
91
+ max_width: Maximum width for each column
92
+
93
+ Returns:
94
+ Rich Table object with split diff view
95
+ """
96
+ table = Table(
97
+ show_header=True,
98
+ header_style=RICH_STYLE_BLUE_BOLD,
99
+ box=SIMPLE_HEAD,
100
+ expand=False,
101
+ padding=(0, 1),
102
+ )
103
+ table.add_column("Original", style="", width=max_width, no_wrap=False)
104
+ table.add_column("Modified", style="", width=max_width, no_wrap=False)
105
+
106
+ original_lines = original.split("\n")
107
+ modified_lines = modified.split("\n")
108
+
109
+ matcher = difflib.SequenceMatcher(None, original_lines, modified_lines)
110
+
111
+ for tag, i1, i2, j1, j2 in matcher.get_opcodes():
112
+ if tag == "equal":
113
+ for i, j in zip(range(i1, i2), range(j1, j2)):
114
+ orig_text = Text(original_lines[i], style="dim")
115
+ mod_text = Text(modified_lines[j], style="dim")
116
+ table.add_row(orig_text, mod_text)
117
+
118
+ elif tag == "delete":
119
+ for i in range(i1, i2):
120
+ orig_text = Text(original_lines[i], style="strike red")
121
+ mod_text = Text("", style="dim")
122
+ table.add_row(orig_text, mod_text)
123
+
124
+ elif tag == "insert":
125
+ for j in range(j1, j2):
126
+ orig_text = Text("", style="dim")
127
+ mod_text = Text(modified_lines[j], style="bold green")
128
+ table.add_row(orig_text, mod_text)
129
+
130
+ elif tag == "replace":
131
+ max_lines = max(i2 - i1, j2 - j1)
132
+
133
+ for offset in range(max_lines):
134
+ orig_idx = i1 + offset
135
+ mod_idx = j1 + offset
136
+
137
+ if orig_idx < i2 and mod_idx < j2:
138
+ orig_line = original_lines[orig_idx]
139
+ mod_line = modified_lines[mod_idx]
140
+
141
+ char_matcher = difflib.SequenceMatcher(
142
+ None, orig_line, mod_line
143
+ )
144
+ if char_matcher.ratio() > 0.5:
145
+ orig_text = DiffDisplay._highlight_char_diff(
146
+ orig_line, mod_line, is_original=True
147
+ )
148
+ mod_text = DiffDisplay._highlight_char_diff(
149
+ orig_line, mod_line, is_original=False
150
+ )
151
+ else:
152
+ orig_text = Text(orig_line, style="strike red")
153
+ mod_text = Text(mod_line, style="bold green")
154
+
155
+ table.add_row(orig_text, mod_text)
156
+
157
+ elif orig_idx < i2:
158
+ orig_text = Text(original_lines[orig_idx], style="strike red")
159
+ mod_text = Text("", style="dim")
160
+ table.add_row(orig_text, mod_text)
161
+
162
+ elif mod_idx < j2:
163
+ orig_text = Text("", style="dim")
164
+ mod_text = Text(modified_lines[mod_idx], style="bold green")
165
+ table.add_row(orig_text, mod_text)
166
+
167
+ return table
168
+
169
+ @staticmethod
170
+ def _highlight_char_diff(orig_line: str, mod_line: str, is_original: bool) -> Text:
171
+ """
172
+ Highlight character-level differences within a line.
173
+
174
+ Args:
175
+ orig_line: Original line text
176
+ mod_line: Modified line text
177
+ is_original: True to highlight original, False for modified
178
+
179
+ Returns:
180
+ Rich Text with character-level highlighting
181
+ """
182
+ result = Text()
183
+ matcher = difflib.SequenceMatcher(None, orig_line, mod_line)
184
+
185
+ for tag, i1, i2, j1, j2 in matcher.get_opcodes():
186
+ if is_original:
187
+ segment = orig_line[i1:i2]
188
+ if tag == "equal":
189
+ result.append(segment, style="dim")
190
+ elif tag == "delete":
191
+ result.append(segment, style="bold red on #3d0000")
192
+ elif tag == "replace":
193
+ result.append(segment, style="bold red on #3d0000")
194
+ else:
195
+ segment = mod_line[j1:j2]
196
+ if tag == "equal":
197
+ result.append(segment, style="dim")
198
+ elif tag == "insert":
199
+ result.append(segment, style="bold green on #003d00")
200
+ elif tag == "replace":
201
+ result.append(segment, style="bold green on #003d00")
202
+
203
+ return result
@@ -217,6 +217,9 @@ class DisplayHandlers:
217
217
  transfer_text.append(f"{msg.get('agent', 'unknown')} agent")
218
218
  self.display_message(transfer_text)
219
219
  continue
220
+
221
+ elif content.startswith("Content of "):
222
+ continue
220
223
  self.display_user_message(content)
221
224
  elif role == "assistant":
222
225
  agent_name = msg.get("agent") or default_agent_name
@@ -163,7 +163,7 @@ class InputHandler:
163
163
  else:
164
164
  if (
165
165
  hasattr(self.message_handler, "stream_generator")
166
- and self.message_handler.stream_generator
166
+ and not self.message_handler.stop_streaming
167
167
  ):
168
168
  try:
169
169
  self.message_handler.stop_streaming = True
@@ -11,6 +11,7 @@ from rich.panel import Panel
11
11
  from rich.box import HORIZONTALS
12
12
  from rich.text import Text
13
13
 
14
+ from .diff_display import DiffDisplay
14
15
  from .constants import (
15
16
  RICH_STYLE_GRAY,
16
17
  RICH_STYLE_YELLOW,
@@ -59,7 +60,19 @@ class ToolDisplayHandlers:
59
60
  header.append(tool_use["name"], style=RICH_STYLE_GRAY)
60
61
 
61
62
  tool_parameters = tool_use.get("input") or tool_use.get("arguments")
62
- # Format tool input parameters
63
+
64
+ if tool_use["name"] == "write_or_edit_file" and isinstance(
65
+ tool_parameters, dict
66
+ ):
67
+ file_path = tool_parameters.get("file_path", "")
68
+ text_or_blocks = tool_parameters.get("text_or_search_replace_blocks", "")
69
+
70
+ if DiffDisplay.has_search_replace_blocks(text_or_blocks):
71
+ self._display_write_or_edit_file_use(
72
+ tool_use, file_path, text_or_blocks
73
+ )
74
+ return
75
+
63
76
  if isinstance(tool_parameters, dict):
64
77
  tool_texts_group.append(Text("Parameters:", style=RICH_STYLE_YELLOW))
65
78
  for key, value in tool_parameters.items():
@@ -96,6 +109,27 @@ class ToolDisplayHandlers:
96
109
  )
97
110
  )
98
111
 
112
+ def _display_write_or_edit_file_use(
113
+ self, tool_use: Dict, file_path: str, blocks_text: str
114
+ ):
115
+ """Display write_or_edit_file tool with split diff view."""
116
+ tool_icon = self.get_tool_icon(tool_use["name"])
117
+
118
+ header = Text(f"{tool_icon} Tool: ", style=RICH_STYLE_GRAY)
119
+ header.append("write_or_edit_file", style=RICH_STYLE_GRAY)
120
+ header.append(f" → {file_path}", style=RICH_STYLE_BLUE)
121
+
122
+ self.console.print(Panel(header, box=HORIZONTALS, title_align="left"))
123
+
124
+ blocks = DiffDisplay.parse_search_replace_blocks(blocks_text)
125
+
126
+ if blocks:
127
+ for block in blocks:
128
+ diff_table = DiffDisplay.create_split_diff_table(
129
+ block["search"], block["replace"], max_width=self.console.width - 4
130
+ )
131
+ self.console.print(diff_table)
132
+
99
133
  def display_tool_result(self, data: Dict):
100
134
  """Display the result of a tool execution."""
101
135
  tool_use = data["tool_use"]
@@ -104,13 +138,10 @@ class ToolDisplayHandlers:
104
138
 
105
139
  tool_texts_group = []
106
140
 
107
- # Display tool result with better formatting
108
141
  header = Text(f"{tool_icon} Tool Result: ", style=RICH_STYLE_GREEN)
109
142
  header.append(tool_use["name"], style=RICH_STYLE_GREEN_BOLD)
110
143
 
111
- # Format the result based on type
112
144
  result_str = str(tool_result)
113
- # If result is very long, try to format it
114
145
  if len(result_str) > 500:
115
146
  result_line = Text(result_str[:500] + "...", style=RICH_STYLE_GREEN)
116
147
  tool_texts_group.append(result_line)
@@ -93,15 +93,23 @@ class UIEffects:
93
93
  self._loading_thread.join(timeout=0.5)
94
94
  self._loading_thread = None
95
95
 
96
- def start_streaming_response(self, agent_name: str):
96
+ def start_streaming_response(self, agent_name: str, is_thinking=False):
97
97
  """Start streaming the assistant's response."""
98
98
  from .constants import RICH_STYLE_GREEN_BOLD
99
99
  from rich.text import Text
100
100
 
101
- header = Text(f"🤖 {agent_name.upper()}:", style=RICH_STYLE_GREEN_BOLD)
101
+ header = Text(
102
+ f"💭 {agent_name.upper()}'s thinking:"
103
+ if is_thinking
104
+ else f"🤖 {agent_name.upper()}:",
105
+ style=RICH_STYLE_GRAY if is_thinking else RICH_STYLE_GREEN_BOLD,
106
+ )
102
107
 
103
108
  live_panel = Panel(
104
- "", title=header, box=HORIZONTALS, border_style=RICH_STYLE_GREEN
109
+ "",
110
+ title=header,
111
+ box=HORIZONTALS,
112
+ border_style=RICH_STYLE_GRAY if is_thinking else RICH_STYLE_GREEN,
105
113
  )
106
114
 
107
115
  self.live = Live(
@@ -123,13 +131,13 @@ class UIEffects:
123
131
  if self.live:
124
132
  self.update_live_display(self.updated_text)
125
133
 
126
- def update_live_display(self, chunk: str):
134
+ def update_live_display(self, chunk: str, is_thinking: bool = False):
127
135
  """Update the live display with a new chunk of the response."""
128
136
  if not self.live:
129
- self.start_streaming_response(self.message_handler.agent.name)
137
+ self.start_streaming_response(self.message_handler.agent.name, is_thinking)
130
138
 
131
139
  if chunk != self.updated_text:
132
- self.updated_text = chunk
140
+ self.updated_text = self.updated_text + chunk if is_thinking else chunk
133
141
 
134
142
  # Only show the last part that fits in the console
135
143
  lines = self.updated_text.split("\n")
@@ -151,9 +159,13 @@ class UIEffects:
151
159
  from .constants import RICH_STYLE_GREEN_BOLD
152
160
  from rich.text import Text
153
161
 
162
+ agent_name = self.message_handler.agent.name
163
+
154
164
  header = Text(
155
- f"🤖 {self.message_handler.agent.name.upper()}:",
156
- style=RICH_STYLE_GREEN_BOLD,
165
+ f"💭 {agent_name.upper()}'s thinking:"
166
+ if is_thinking
167
+ else f"🤖 {agent_name.upper()}:",
168
+ style=RICH_STYLE_GRAY if is_thinking else RICH_STYLE_GREEN_BOLD,
157
169
  )
158
170
  subtitle = Text(
159
171
  f"{next(self.spinner)}(Use Ctrl+U/Ctrl+D to scroll)",
@@ -163,11 +175,11 @@ class UIEffects:
163
175
  Markdown("\n".join(lines), code_theme=CODE_THEME),
164
176
  title=header,
165
177
  box=HORIZONTALS,
166
- subtitle=subtitle,
178
+ subtitle=subtitle if not is_thinking else None,
167
179
  title_align="left",
168
180
  expand=False,
169
181
  height=min(height_limit, len(lines)),
170
- border_style=RICH_STYLE_GREEN,
182
+ border_style=RICH_STYLE_GRAY if is_thinking else RICH_STYLE_GREEN,
171
183
  )
172
184
  self.live.update(live_panel, refresh=True)
173
185
 
@@ -182,7 +194,7 @@ class UIEffects:
182
194
  self.live.stop()
183
195
  self.live = None
184
196
 
185
- def finish_response(self, response: str):
197
+ def finish_response(self, response: str, is_thinking: bool = False):
186
198
  """Finalize and display the complete response."""
187
199
  from .constants import RICH_STYLE_GREEN_BOLD
188
200
  from rich.text import Text
@@ -202,16 +214,20 @@ class UIEffects:
202
214
  if not markdown_formatted_response.strip():
203
215
  return
204
216
 
217
+ agent_name = self.message_handler.agent.name
218
+
205
219
  header = Text(
206
- f"🤖 {self.message_handler.agent.name.upper()}:",
207
- style=RICH_STYLE_GREEN_BOLD,
220
+ f"💭 {agent_name.upper()}'s thinking:"
221
+ if is_thinking
222
+ else f"🤖 {agent_name.upper()}:",
223
+ style=RICH_STYLE_GRAY if is_thinking else RICH_STYLE_GREEN_BOLD,
208
224
  )
209
225
  assistant_panel = Panel(
210
226
  Markdown(markdown_formatted_response, code_theme=CODE_THEME),
211
227
  title=header,
212
228
  box=HORIZONTALS,
213
229
  title_align="left",
214
- border_style=RICH_STYLE_GREEN,
230
+ border_style=RICH_STYLE_GRAY if is_thinking else RICH_STYLE_GREEN,
215
231
  )
216
232
  self.console.print(assistant_panel)
217
233
 
@@ -45,19 +45,32 @@ class DeepInfraService(CustomLLMService):
45
45
  thinking_data
46
46
  )
47
47
  """
48
- chunk_text = None
48
+ chunk_text = ""
49
49
  input_tokens = 0
50
50
  output_tokens = 0
51
51
  thinking_content = None # OpenAI doesn't support thinking mode
52
52
 
53
+ if (not chunk.choices) or (len(chunk.choices) == 0):
54
+ return (
55
+ assistant_response or " ",
56
+ tool_uses,
57
+ input_tokens,
58
+ output_tokens,
59
+ "",
60
+ (thinking_content, None) if thinking_content else None,
61
+ )
62
+
63
+ delta_chunk = chunk.choices[0].delta
53
64
  # Handle regular content chunks
65
+ #
54
66
  if (
55
- chunk.choices
56
- and len(chunk.choices) > 0
57
- and hasattr(chunk.choices[0].delta, "content")
58
- and chunk.choices[0].delta.content is not None
67
+ hasattr(delta_chunk, "reasoning_content")
68
+ and delta_chunk.reasoning_content is not None
59
69
  ):
60
- chunk_text = chunk.choices[0].delta.content
70
+ thinking_content = delta_chunk.reasoning_content
71
+
72
+ if hasattr(delta_chunk, "content") and delta_chunk.content is not None:
73
+ chunk_text = delta_chunk.content
61
74
  if "<think>" in chunk_text:
62
75
  self._is_thinking = True
63
76
 
@@ -87,11 +100,7 @@ class DeepInfraService(CustomLLMService):
87
100
  output_tokens = chunk.usage.completion_tokens
88
101
 
89
102
  # Handle tool call chunks
90
- if (
91
- chunk.choices
92
- and len(chunk.choices) > 0
93
- and hasattr(chunk.choices[0].delta, "tool_calls")
94
- ):
103
+ if hasattr(delta_chunk, "tool_calls"):
95
104
  delta_tool_calls = chunk.choices[0].delta.tool_calls
96
105
  if delta_tool_calls:
97
106
  # Process each tool call in the delta
@@ -162,14 +171,6 @@ class DeepInfraService(CustomLLMService):
162
171
  except json.JSONDecodeError:
163
172
  # Arguments JSON is still incomplete, keep accumulating
164
173
  pass
165
- return (
166
- assistant_response or " ",
167
- tool_uses,
168
- input_tokens,
169
- output_tokens,
170
- "",
171
- (thinking_content, None) if thinking_content else None,
172
- )
173
174
 
174
175
  return (
175
176
  assistant_response or " ",
@@ -3,7 +3,7 @@ from .service import CustomLLMService
3
3
  import os
4
4
  from dotenv import load_dotenv
5
5
  from loguru import logger
6
- from typing import Dict, Any, Optional, List
6
+ from typing import Dict, Any, Optional, List, Tuple
7
7
  from datetime import datetime
8
8
  import json
9
9
  from uuid import uuid4
@@ -76,8 +76,26 @@ class GithubCopilotService(CustomLLMService):
76
76
  return False
77
77
 
78
78
  def _convert_internal_format(self, messages: List[Dict[str, Any]]):
79
- for msg in messages:
79
+ thinking_block = None
80
+ for i, msg in enumerate(messages):
80
81
  msg.pop("agent", None)
82
+ if msg.get("role") == "assistant":
83
+ if thinking_block:
84
+ msg["reasoning_text"] = thinking_block.get("thinking", "")
85
+ msg["reasoning_opaque"] = thinking_block.get("signature", "")
86
+ thinking_block = None
87
+ del messages[i - 1]
88
+ if isinstance(msg.get("content", ""), List):
89
+ thinking_block = next(
90
+ (
91
+ block
92
+ for block in msg.get("content", [])
93
+ if block.get("type", "text") == "thinking"
94
+ ),
95
+ None,
96
+ )
97
+ msg["content"] = []
98
+
81
99
  if "tool_calls" in msg and msg.get("tool_calls", []):
82
100
  for tool_call in msg["tool_calls"]:
83
101
  tool_call["function"] = {}
@@ -85,6 +103,7 @@ class GithubCopilotService(CustomLLMService):
85
103
  tool_call["function"]["arguments"] = json.dumps(
86
104
  tool_call.pop("arguments", {})
87
105
  )
106
+
88
107
  if msg.get("role") == "tool":
89
108
  # Special treatment for GitHub Copilot GPT-4.1 model
90
109
  # At the the time of writing, GitHub Copilot GPT-4.1 model cannot read tool results with array content
@@ -115,6 +134,8 @@ class GithubCopilotService(CustomLLMService):
115
134
  msg["content"] = (
116
135
  "\n".join(parsed_tool_result) if parsed_tool_result else ""
117
136
  )
137
+ elif isinstance(msg.get("content", ""), str):
138
+ msg["content"] = [{"type": "text", "text": msg["content"]}]
118
139
 
119
140
  return messages
120
141
 
@@ -127,6 +148,140 @@ class GithubCopilotService(CustomLLMService):
127
148
  self.extra_headers["X-Request-Id"] = str(uuid4())
128
149
  return await super().process_message(prompt, temperature)
129
150
 
151
+ def _process_stream_chunk(
152
+ self, chunk, assistant_response: str, tool_uses: List[Dict]
153
+ ) -> Tuple[str, List[Dict], int, int, Optional[str], Optional[tuple]]:
154
+ """
155
+ Process a single chunk from the streaming response.
156
+
157
+ Args:
158
+ chunk: The chunk from the stream
159
+ assistant_response: Current accumulated assistant response
160
+ tool_uses: Current tool use information
161
+
162
+ Returns:
163
+ tuple: (
164
+ updated_assistant_response,
165
+ updated_tool_uses,
166
+ input_tokens,
167
+ output_tokens,
168
+ chunk_text,
169
+ thinking_data
170
+ )
171
+ """
172
+ chunk_text = ""
173
+ input_tokens = 0
174
+ output_tokens = 0
175
+ thinking_content = None # OpenAI doesn't support thinking mode
176
+ thinking_signature = None
177
+
178
+ if (not chunk.choices) or (len(chunk.choices) == 0):
179
+ return (
180
+ assistant_response or " ",
181
+ tool_uses,
182
+ input_tokens,
183
+ output_tokens,
184
+ "",
185
+ (thinking_content, None) if thinking_content else None,
186
+ )
187
+
188
+ delta_chunk = chunk.choices[0].delta
189
+
190
+ # Handle thinking content
191
+ if (
192
+ hasattr(delta_chunk, "reasoning_text")
193
+ and delta_chunk.reasoning_text is not None
194
+ ):
195
+ thinking_content = delta_chunk.reasoning_text
196
+
197
+ if (
198
+ hasattr(delta_chunk, "reasoning_opaque")
199
+ and delta_chunk.reasoning_opaque is not None
200
+ ):
201
+ thinking_signature = delta_chunk.reasoning_opaque
202
+ # Handle regular content chunks
203
+ if hasattr(delta_chunk, "content") and delta_chunk.content is not None:
204
+ chunk_text = chunk.choices[0].delta.content
205
+ assistant_response += chunk_text
206
+
207
+ # Handle final chunk with usage information
208
+ if hasattr(chunk, "usage"):
209
+ if hasattr(chunk.usage, "prompt_tokens"):
210
+ input_tokens = chunk.usage.prompt_tokens
211
+ if hasattr(chunk.usage, "completion_tokens"):
212
+ output_tokens = chunk.usage.completion_tokens
213
+
214
+ # Handle tool call chunks
215
+ if hasattr(delta_chunk, "tool_calls"):
216
+ delta_tool_calls = chunk.choices[0].delta.tool_calls
217
+ if delta_tool_calls:
218
+ # Process each tool call in the delta
219
+ for tool_call_delta in delta_tool_calls:
220
+ # Check if this is a new tool call
221
+ if getattr(tool_call_delta, "id"):
222
+ # Create a new tool call entry
223
+ tool_uses.append(
224
+ {
225
+ "id": getattr(tool_call_delta, "id")
226
+ if hasattr(tool_call_delta, "id")
227
+ else f"toolu_{len(tool_uses)}",
228
+ "name": getattr(tool_call_delta.function, "name", "")
229
+ if hasattr(tool_call_delta, "function")
230
+ else "",
231
+ "input": {},
232
+ "type": "function",
233
+ "response": "",
234
+ }
235
+ )
236
+ tool_call_index = len(tool_uses) - 1
237
+
238
+ # # Update existing tool call with new data
239
+ # if hasattr(tool_call_delta, "id") and tool_call_delta.id:
240
+ # tool_uses[tool_call_index]["id"] = tool_call_delta.id
241
+
242
+ if hasattr(tool_call_delta, "function"):
243
+ if (
244
+ hasattr(tool_call_delta.function, "name")
245
+ and tool_call_delta.function.name
246
+ ):
247
+ tool_uses[tool_call_index]["name"] = (
248
+ tool_call_delta.function.name
249
+ )
250
+
251
+ if (
252
+ hasattr(tool_call_delta.function, "arguments")
253
+ and tool_call_delta.function.arguments
254
+ ):
255
+ # Accumulate arguments as they come in chunks
256
+ current_args = tool_uses[tool_call_index].get(
257
+ "args_json", ""
258
+ )
259
+ tool_uses[tool_call_index]["args_json"] = (
260
+ current_args + tool_call_delta.function.arguments
261
+ )
262
+
263
+ # Try to parse JSON if it seems complete
264
+ try:
265
+ args_json = tool_uses[tool_call_index]["args_json"]
266
+ tool_uses[tool_call_index]["input"] = json.loads(
267
+ args_json
268
+ )
269
+ # Keep args_json for accumulation but use input for execution
270
+ except json.JSONDecodeError:
271
+ # Arguments JSON is still incomplete, keep accumulating
272
+ pass
273
+
274
+ return (
275
+ assistant_response or " ",
276
+ tool_uses,
277
+ input_tokens,
278
+ output_tokens,
279
+ chunk_text,
280
+ (thinking_content, thinking_signature)
281
+ if thinking_content or thinking_signature
282
+ else None,
283
+ )
284
+
130
285
  async def stream_assistant_response(self, messages):
131
286
  """Stream the assistant's response with tool support."""
132
287
 
@@ -358,7 +358,7 @@ class CustomLLMService(OpenAIService):
358
358
  thinking_data
359
359
  )
360
360
  """
361
- chunk_text = None
361
+ chunk_text = ""
362
362
  input_tokens = 0
363
363
  output_tokens = 0
364
364
  thinking_content = None # OpenAI doesn't support thinking mode
@@ -462,14 +462,6 @@ class CustomLLMService(OpenAIService):
462
462
  except json.JSONDecodeError:
463
463
  # Arguments JSON is still incomplete, keep accumulating
464
464
  pass
465
- return (
466
- assistant_response or " ",
467
- tool_uses,
468
- input_tokens,
469
- output_tokens,
470
- "",
471
- (thinking_content, None) if thinking_content else None,
472
- )
473
465
 
474
466
  return (
475
467
  assistant_response or " ",
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: agentcrew-ai
3
- Version: 0.8.4
3
+ Version: 0.8.5
4
4
  Summary: Multi-Agents Interactive Chat Tool
5
5
  Author-email: Quy Truong <quy.truong@saigontechnology.com>
6
6
  License-Expression: Apache-2.0
@@ -47,7 +47,7 @@ Requires-Dist: sounddevice>=0.5.2
47
47
  Requires-Dist: soundfile>=0.13.1
48
48
  Requires-Dist: jsonref>=1.1.0
49
49
  Requires-Dist: pychromedevtools>=0.3.3
50
- Requires-Dist: html-to-markdown<2,>=1.14.0
50
+ Requires-Dist: html-to-markdown>=2.9.1
51
51
  Requires-Dist: pip-system-certs>=5.2
52
52
  Requires-Dist: loguru>=0.7.3
53
53
  Requires-Dist: jsonschema>=4.25.1
@@ -1,4 +1,4 @@
1
- AgentCrew/__init__.py,sha256=jhHEJFZWhkQDemoZMomBYq-RNrKXknYzUaeIU9A6XsI,22
1
+ AgentCrew/__init__.py,sha256=K0kGrhh1kzVisZcoSkeuJdC06rTwxufV05Vy2hOVGoo,22
2
2
  AgentCrew/app.py,sha256=dXjwoHLKf1tSOq1Vc-9wsF1dpjS5CaGqdiFSksKKwKA,36584
3
3
  AgentCrew/main.py,sha256=tjyw4Z29YKKyzVKqDgF5icI1IM59DUiJrXZw6Sv_xN8,11187
4
4
  AgentCrew/main_docker.py,sha256=1jpB-XOm-t3GWTKYidGHHkCSzJ-p39ua0E6-_Nj8_9Y,5472
@@ -10,7 +10,7 @@ AgentCrew/modules/a2a/agent_cards.py,sha256=SWmo3jVNSrPUqpL8Tv6T3h1a-0oXrOgqJrbP
10
10
  AgentCrew/modules/a2a/errors.py,sha256=Yaw7Ew5LYDvrYgKZ34heBnEqlIXFF3_5GqqpNhNlveI,2353
11
11
  AgentCrew/modules/a2a/registry.py,sha256=1PkasOOpRqulE7mMl1UNSt5GrqGT8AhP9UCWWSAF34I,2671
12
12
  AgentCrew/modules/a2a/server.py,sha256=CT_0Te4BBWJEwgbX0wLvHcu6gJAMFsGswwS4IOC5L_k,12345
13
- AgentCrew/modules/a2a/task_manager.py,sha256=CnvB9m_mqA4S1GThPRUZqyz6jeZOv9GDCznMIzjOLHQ,25629
13
+ AgentCrew/modules/a2a/task_manager.py,sha256=u-3Wfk4gVqScGtqQd4E9A7lcACFFKOxsGmPWG_kXzIk,25624
14
14
  AgentCrew/modules/a2a/common/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
15
15
  AgentCrew/modules/a2a/common/client/__init__.py,sha256=PoBTKucH8G2onta-vHT4qUJcBYa3TIabGqCO5GVVyT0,118
16
16
  AgentCrew/modules/a2a/common/client/card_resolver.py,sha256=imtwwMNb1yLxTPcrct1ToH9fA18mzgAFYKR5vYTHHRw,637
@@ -22,7 +22,7 @@ AgentCrew/modules/a2a/common/server/utils.py,sha256=pmIBG5cyzaVNXbyESLux2AcogwPa
22
22
  AgentCrew/modules/agents/__init__.py,sha256=rMwchcGZzapLLZdUQUaJtCLowHoG-dOn8_VljKz8NrI,165
23
23
  AgentCrew/modules/agents/base.py,sha256=i7w0X7mcTRWwgyQwqN-iOIniLce9cSM2wGG3njlZpHk,2641
24
24
  AgentCrew/modules/agents/example.py,sha256=_-Nd7EKHprKXZLN9_ava0b9vR7wGyUzsXSEV7_js7Ho,6894
25
- AgentCrew/modules/agents/local_agent.py,sha256=CMBDYwJsemPrH-dvp3PsDDULlCcVE3KFXS7Er7WLi-o,31856
25
+ AgentCrew/modules/agents/local_agent.py,sha256=LiylFxS7dgAC0-w3ZXJ92SE1ZUyWdrJR3j_zH9xnGuM,31880
26
26
  AgentCrew/modules/agents/manager.py,sha256=JHwEp-GwlYMRTv2RItfJOnVJlePZOLK31xYi3taEuF0,24863
27
27
  AgentCrew/modules/agents/remote_agent.py,sha256=bBgO8E3dXZuu9ofklXLfL5-8mxF_XZOLyqj6rrwTXR8,6432
28
28
  AgentCrew/modules/agents/tools/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -33,9 +33,9 @@ AgentCrew/modules/anthropic/__init__.py,sha256=OcpRL0PKADMAS7hxTOH6U7J-NeiQJBm6x
33
33
  AgentCrew/modules/anthropic/service.py,sha256=olpiiptIVubc11zmbALOoAMFU-XVx_bIHcj82sUBTS4,18839
34
34
  AgentCrew/modules/browser_automation/__init__.py,sha256=1Qq6Rqz4Mv4arPKWKSDgIOV-G72R5Jey2-qdkLMLVKA,86
35
35
  AgentCrew/modules/browser_automation/chrome_manager.py,sha256=iBFcImVoeUF5qc8eRzSsihuBSiPhDF0DOSYfda7OHas,11901
36
- AgentCrew/modules/browser_automation/element_extractor.py,sha256=wzPYoZ_ReXA1qg6Gf-uTSvOJCvySO6RNVnpc0zkSme4,15913
36
+ AgentCrew/modules/browser_automation/element_extractor.py,sha256=J1Gp6xDSWFDt7690JgBhX6ryXfNeVUCLwYBA3IGtCqo,15914
37
37
  AgentCrew/modules/browser_automation/js_loader.py,sha256=vdm-cQA5I4sL5Y0s7iaMnxSwcu0IZEdtczCgoHXAkK4,18315
38
- AgentCrew/modules/browser_automation/service.py,sha256=xle7R7JZkEvdcjrzurCG468YzUMdc3JWcDRxWxQKrlw,27835
38
+ AgentCrew/modules/browser_automation/service.py,sha256=VFgDOoCqamL_YTRrJ7G596KKsOz3UF6Kg2u4FRfYYwU,28247
39
39
  AgentCrew/modules/browser_automation/tool.py,sha256=q62Cmo8bgGFs-8PrXKqOYJRNniNAUkCc54wUirOKG2s,23997
40
40
  AgentCrew/modules/browser_automation/js/click_element.js,sha256=eNkbPX9wXyj9mPnfNILJjtbiPF0iKCT52bfTFQRhT1Y,1652
41
41
  AgentCrew/modules/browser_automation/js/draw_element_boxes.js,sha256=4ToM8UYy2HUyXCC2l219Zghjj2lG3JZD1X2mjsLgdtU,5781
@@ -55,9 +55,9 @@ AgentCrew/modules/chat/history.py,sha256=I7q1_s16h975uAtq6fxC4UcS9iuk1aUpKkMMYCc
55
55
  AgentCrew/modules/chat/message_handler.py,sha256=A8DfuIPjW0R7s3a0P8VzNa2PF6HgXcRt8CEm2nL8fEE,306
56
56
  AgentCrew/modules/chat/message/__init__.py,sha256=SllV6nacLgBSTwkUhI7VbEW-7gwQxQh6cCjRtbU02O8,146
57
57
  AgentCrew/modules/chat/message/base.py,sha256=Xnes4tQCfMfWShSHG8f3ZZ5y5Q0wTWy5g0EwnNmflK0,1016
58
- AgentCrew/modules/chat/message/command_processor.py,sha256=HNtCvpj8waMgyzBa7jSQ9_kUVvSQotq36kv6lPXVXWg,33485
59
- AgentCrew/modules/chat/message/conversation.py,sha256=-I2q_DJFTA6EhBQmpT7VKnrCiU6NXoApIzEy65NARFs,10759
60
- AgentCrew/modules/chat/message/handler.py,sha256=NOvOcS93WMf7ioeO1-8epqd2YMmaDNVOSPlnCO-CzGk,23606
58
+ AgentCrew/modules/chat/message/command_processor.py,sha256=3yz5bCRybFDmmBl0lK-MMQZlQCiDvCr0JdXOVTUX8S8,33541
59
+ AgentCrew/modules/chat/message/conversation.py,sha256=olGWFPi3u1wbnqpbPllMhKo4qGepvCj0O_Bmdh-ovwY,10841
60
+ AgentCrew/modules/chat/message/handler.py,sha256=bUL-BfNxWs1-1IQ-CkN0Gq6QkhDSZqQDrCtlV0fFxIg,23508
61
61
  AgentCrew/modules/chat/message/tool_manager.py,sha256=JZTmAQ08MMO42t9FeSKYzuSunkaBVJaragDfZBnIEkM,12852
62
62
  AgentCrew/modules/clipboard/__init__.py,sha256=Xb0wwQ-9dwwLT3R3TYB87pwtDTNX5HAzhkr8iuR6mBc,77
63
63
  AgentCrew/modules/clipboard/service.py,sha256=FCo3zSJH0zDdvW73u4D_ty9fTMsXmOeVsAIOfyJ54Ws,6246
@@ -74,22 +74,23 @@ AgentCrew/modules/command_execution/types.py,sha256=KVxSmqTF_DdUoGBMMVt2reRLItw_
74
74
  AgentCrew/modules/config/__init__.py,sha256=ehO0aAkK98f9BmMjG9uR15Hc9Lwj9CnJZl24XUtUT_M,80
75
75
  AgentCrew/modules/config/config_management.py,sha256=yxmfx1nQAZxTzJ4g3F3ybYlmTTFpMmKxfNETyzsQ9uA,33648
76
76
  AgentCrew/modules/console/__init__.py,sha256=nO53lUaMEAshdIqDEmgNZ_r35jyg6CuMa7Tsj55Y09g,66
77
- AgentCrew/modules/console/command_handlers.py,sha256=oDEmxm6reNXtejCfPF6_qqrZ31upKIuGLwjKBduocug,16240
77
+ AgentCrew/modules/console/command_handlers.py,sha256=qBl5GjZVnuaMJdilJKcPsYpU1E-XbI_Nv5veINU-cic,16232
78
78
  AgentCrew/modules/console/completers.py,sha256=WeP5rJvCWq4Ept2_ajK9wjpDiTT4C5blcX5TeV3GSzU,17682
79
- AgentCrew/modules/console/confirmation_handler.py,sha256=8xY_hdCqQ7DDrnPSU9rIPJ7AeM6Th2qlrOIZjSWVZPI,8406
80
- AgentCrew/modules/console/console_ui.py,sha256=Slk0SFRg-_6ovplkjfGNqKjROzvAPaWqz-ZM8du0v9Y,29709
79
+ AgentCrew/modules/console/confirmation_handler.py,sha256=sOhJVmrMgiqTlUI6G9xjzseeeWn54G-aOc6WevWcXL8,10221
80
+ AgentCrew/modules/console/console_ui.py,sha256=09XkGsIivVQK4z1-gOyuHtdXfiMMAyc4DvnWHlvIqho,29836
81
81
  AgentCrew/modules/console/constants.py,sha256=fwLj52O96_t6m1qb0SOiaotM2dMLwXH83KAERm9ltLA,704
82
82
  AgentCrew/modules/console/conversation_handler.py,sha256=vVtGxQJ4sEZJ77svBFJMIGiWiEfE47yDxvt7gZ9bRCA,3632
83
- AgentCrew/modules/console/display_handlers.py,sha256=fuh_rY0LbzvY-BHbYlMNPEsIsH4FRpKH5jEQ4brtomM,17565
84
- AgentCrew/modules/console/input_handler.py,sha256=U6gyTuCdPRkjnqa_g1KRPp00r1mOc9kXL5N5mkh_mmw,18139
85
- AgentCrew/modules/console/tool_display.py,sha256=qMdtc7zIOTPEDwuia8um8fxtVyGxjwBTQMVsKSGRUB8,6107
86
- AgentCrew/modules/console/ui_effects.py,sha256=mr74XyweXkyXZIYUYczpTfYzGAQjSflIojr4q3Ayelg,7436
83
+ AgentCrew/modules/console/diff_display.py,sha256=rXkrCQBuQ9K6V22uS-PKd6qz6MtC8ZfdvDuIolCuGok,7063
84
+ AgentCrew/modules/console/display_handlers.py,sha256=ZSpBZxvX5X9VB2dvAB679KQwPTVhRPHRrmsgCMhANyA,17651
85
+ AgentCrew/modules/console/input_handler.py,sha256=f31LiMwOTZ3No0ktWLMxQAnm7Fij0GN9-ECyDWInYlI,18141
86
+ AgentCrew/modules/console/tool_display.py,sha256=aTjNWcfzmJXwpXU64PqMfNFRCiVrtMD-JFn_Wd2cNYY,7306
87
+ AgentCrew/modules/console/ui_effects.py,sha256=n5w863kI0VKU_afGmXjscYg8FmsaX4IJyNQRGCDoOLI,8218
87
88
  AgentCrew/modules/console/utils.py,sha256=TFIyyYVFlWMB0FCAq4H09Yp6UCezUEHg3HNEZXuVmiA,273
88
89
  AgentCrew/modules/custom_llm/__init__.py,sha256=NvgE3c6rYd52SmRITqGeHqaGOnHrK9vU8RBqD9_6Mdo,337
89
90
  AgentCrew/modules/custom_llm/copilot_response_service.py,sha256=tySzxnLMzMt3WjwhvBegtDP7Qk43D2Wc5bxZAi0Z3sA,4605
90
- AgentCrew/modules/custom_llm/deepinfra_service.py,sha256=wwcAQ92QKbr4mkH6Jqgtacym2VNqU6G-hFEnmt5Pjmw,7514
91
- AgentCrew/modules/custom_llm/github_copilot_service.py,sha256=KKJXXZ4FMoNzYWSLMUBhfQmmYtmLbL2624Qu_UUC6-A,7904
92
- AgentCrew/modules/custom_llm/service.py,sha256=BfMhkoql7k_WwZQ_83PxSJjmdlbvBZuYrA3gv1SGJxk,19729
91
+ AgentCrew/modules/custom_llm/deepinfra_service.py,sha256=uY7f-lg-hnR7A1ZYwKSE10c4WlJ9C693eBiZxLV6jlw,7553
92
+ AgentCrew/modules/custom_llm/github_copilot_service.py,sha256=yCWpD5Sn6LiWMeMtR6sKMTq-xeEXxge5WFpHP84SvjU,14391
93
+ AgentCrew/modules/custom_llm/service.py,sha256=UkF8RtCw56eY1Tusc-SUSobz7IvnYBIgGvZNctuIWQk,19437
93
94
  AgentCrew/modules/file_editing/__init__.py,sha256=Q6oDyP3bHslFpmxHdrCCLVx1M_awaHD2WqFTSEU3VIY,241
94
95
  AgentCrew/modules/file_editing/safety_validator.py,sha256=lLNb_GpIIX2QtN86U2NKbM-b3VYW3b6jNkXL5hKPlok,5575
95
96
  AgentCrew/modules/file_editing/search_replace_engine.py,sha256=i8e18VIvxOwCazjnfrmLp637laDCsJSpZs5YsW9Ic-Y,10751
@@ -185,9 +186,9 @@ AgentCrew/modules/voice/text_cleaner.py,sha256=NgAVBPkP2hFI330nJOyMK_oqP3R2AGZ22
185
186
  AgentCrew/modules/web_search/__init__.py,sha256=sVf_z6nH2JghK46pG92PUtDghPIkceiX1F0Ul30euXU,111
186
187
  AgentCrew/modules/web_search/service.py,sha256=DKcOdRSHB5AEvbK8pvTXdffSnk6rFRTzaM1FXf2B70E,4006
187
188
  AgentCrew/modules/web_search/tool.py,sha256=GV4xleVFs0UwiPS9toSzPzZei3ehsDZWxTQCJCRaEs8,6655
188
- agentcrew_ai-0.8.4.dist-info/licenses/LICENSE,sha256=O51CIaOUcxVLNf0_PE_a8ap5bf3iXe4SrWN_5NO1PSU,11348
189
- agentcrew_ai-0.8.4.dist-info/METADATA,sha256=ItgD9ALLC_B5Vqq47NyAedgvjdX7eWhfx-z_4hUV52k,18060
190
- agentcrew_ai-0.8.4.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
191
- agentcrew_ai-0.8.4.dist-info/entry_points.txt,sha256=N9R5jslrBYA8dTaNMRJ_JdSosJ6jkpGEtnJFzlcZD6k,54
192
- agentcrew_ai-0.8.4.dist-info/top_level.txt,sha256=bSsmhCOn6g-JytoVMpxB_QAnCgb7lV56fcnxUhbfrvg,10
193
- agentcrew_ai-0.8.4.dist-info/RECORD,,
189
+ agentcrew_ai-0.8.5.dist-info/licenses/LICENSE,sha256=O51CIaOUcxVLNf0_PE_a8ap5bf3iXe4SrWN_5NO1PSU,11348
190
+ agentcrew_ai-0.8.5.dist-info/METADATA,sha256=abjIS_DBdZu09Ird2G5jkFNgc4njvOQflV0ttMlZyYg,18056
191
+ agentcrew_ai-0.8.5.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
192
+ agentcrew_ai-0.8.5.dist-info/entry_points.txt,sha256=N9R5jslrBYA8dTaNMRJ_JdSosJ6jkpGEtnJFzlcZD6k,54
193
+ agentcrew_ai-0.8.5.dist-info/top_level.txt,sha256=bSsmhCOn6g-JytoVMpxB_QAnCgb7lV56fcnxUhbfrvg,10
194
+ agentcrew_ai-0.8.5.dist-info/RECORD,,