hanzo-mcp 0.8.8__py3-none-any.whl → 0.9.0__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.

Potentially problematic release.


This version of hanzo-mcp might be problematic. Click here for more details.

Files changed (167) hide show
  1. hanzo_mcp/__init__.py +1 -3
  2. hanzo_mcp/analytics/posthog_analytics.py +4 -17
  3. hanzo_mcp/bridge.py +9 -25
  4. hanzo_mcp/cli.py +8 -17
  5. hanzo_mcp/cli_enhanced.py +5 -14
  6. hanzo_mcp/cli_plugin.py +3 -9
  7. hanzo_mcp/config/settings.py +6 -20
  8. hanzo_mcp/config/tool_config.py +2 -4
  9. hanzo_mcp/core/base_agent.py +88 -88
  10. hanzo_mcp/core/model_registry.py +238 -210
  11. hanzo_mcp/dev_server.py +5 -15
  12. hanzo_mcp/prompts/__init__.py +2 -6
  13. hanzo_mcp/prompts/project_todo_reminder.py +3 -9
  14. hanzo_mcp/prompts/tool_explorer.py +1 -3
  15. hanzo_mcp/prompts/utils.py +7 -21
  16. hanzo_mcp/server.py +6 -7
  17. hanzo_mcp/tools/__init__.py +29 -32
  18. hanzo_mcp/tools/agent/__init__.py +2 -1
  19. hanzo_mcp/tools/agent/agent.py +10 -30
  20. hanzo_mcp/tools/agent/agent_tool.py +23 -17
  21. hanzo_mcp/tools/agent/claude_desktop_auth.py +3 -9
  22. hanzo_mcp/tools/agent/cli_agent_base.py +7 -24
  23. hanzo_mcp/tools/agent/cli_tools.py +76 -75
  24. hanzo_mcp/tools/agent/code_auth.py +1 -3
  25. hanzo_mcp/tools/agent/code_auth_tool.py +2 -6
  26. hanzo_mcp/tools/agent/critic_tool.py +8 -24
  27. hanzo_mcp/tools/agent/iching_tool.py +12 -36
  28. hanzo_mcp/tools/agent/network_tool.py +7 -18
  29. hanzo_mcp/tools/agent/prompt.py +1 -5
  30. hanzo_mcp/tools/agent/review_tool.py +10 -25
  31. hanzo_mcp/tools/agent/swarm_alias.py +1 -3
  32. hanzo_mcp/tools/agent/unified_cli_tools.py +38 -38
  33. hanzo_mcp/tools/common/batch_tool.py +15 -45
  34. hanzo_mcp/tools/common/config_tool.py +9 -28
  35. hanzo_mcp/tools/common/context.py +1 -3
  36. hanzo_mcp/tools/common/critic_tool.py +1 -3
  37. hanzo_mcp/tools/common/decorators.py +2 -6
  38. hanzo_mcp/tools/common/enhanced_base.py +2 -6
  39. hanzo_mcp/tools/common/fastmcp_pagination.py +4 -12
  40. hanzo_mcp/tools/common/forgiving_edit.py +9 -28
  41. hanzo_mcp/tools/common/mode.py +1 -5
  42. hanzo_mcp/tools/common/paginated_base.py +3 -11
  43. hanzo_mcp/tools/common/paginated_response.py +10 -30
  44. hanzo_mcp/tools/common/pagination.py +3 -9
  45. hanzo_mcp/tools/common/path_utils.py +34 -0
  46. hanzo_mcp/tools/common/permissions.py +14 -13
  47. hanzo_mcp/tools/common/personality.py +983 -701
  48. hanzo_mcp/tools/common/plugin_loader.py +3 -15
  49. hanzo_mcp/tools/common/stats.py +7 -19
  50. hanzo_mcp/tools/common/thinking_tool.py +1 -3
  51. hanzo_mcp/tools/common/tool_disable.py +2 -6
  52. hanzo_mcp/tools/common/tool_list.py +2 -6
  53. hanzo_mcp/tools/common/validation.py +1 -3
  54. hanzo_mcp/tools/compiler/__init__.py +8 -0
  55. hanzo_mcp/tools/compiler/sandboxed_compiler.py +681 -0
  56. hanzo_mcp/tools/config/config_tool.py +7 -13
  57. hanzo_mcp/tools/config/index_config.py +1 -3
  58. hanzo_mcp/tools/config/mode_tool.py +5 -15
  59. hanzo_mcp/tools/database/database_manager.py +3 -9
  60. hanzo_mcp/tools/database/graph.py +1 -3
  61. hanzo_mcp/tools/database/graph_add.py +3 -9
  62. hanzo_mcp/tools/database/graph_query.py +11 -34
  63. hanzo_mcp/tools/database/graph_remove.py +3 -9
  64. hanzo_mcp/tools/database/graph_search.py +6 -20
  65. hanzo_mcp/tools/database/graph_stats.py +11 -33
  66. hanzo_mcp/tools/database/sql.py +4 -12
  67. hanzo_mcp/tools/database/sql_query.py +6 -10
  68. hanzo_mcp/tools/database/sql_search.py +2 -6
  69. hanzo_mcp/tools/database/sql_stats.py +5 -15
  70. hanzo_mcp/tools/editor/neovim_command.py +1 -3
  71. hanzo_mcp/tools/editor/neovim_session.py +7 -13
  72. hanzo_mcp/tools/environment/__init__.py +8 -0
  73. hanzo_mcp/tools/environment/environment_detector.py +594 -0
  74. hanzo_mcp/tools/filesystem/__init__.py +28 -26
  75. hanzo_mcp/tools/filesystem/ast_multi_edit.py +14 -43
  76. hanzo_mcp/tools/filesystem/ast_tool.py +3 -0
  77. hanzo_mcp/tools/filesystem/base.py +20 -12
  78. hanzo_mcp/tools/filesystem/content_replace.py +7 -12
  79. hanzo_mcp/tools/filesystem/diff.py +2 -10
  80. hanzo_mcp/tools/filesystem/directory_tree.py +285 -51
  81. hanzo_mcp/tools/filesystem/edit.py +10 -18
  82. hanzo_mcp/tools/filesystem/find.py +312 -179
  83. hanzo_mcp/tools/filesystem/git_search.py +12 -24
  84. hanzo_mcp/tools/filesystem/multi_edit.py +10 -18
  85. hanzo_mcp/tools/filesystem/read.py +14 -30
  86. hanzo_mcp/tools/filesystem/rules_tool.py +9 -17
  87. hanzo_mcp/tools/filesystem/search.py +1160 -0
  88. hanzo_mcp/tools/filesystem/watch.py +2 -4
  89. hanzo_mcp/tools/filesystem/write.py +7 -10
  90. hanzo_mcp/tools/framework/__init__.py +8 -0
  91. hanzo_mcp/tools/framework/framework_modes.py +714 -0
  92. hanzo_mcp/tools/jupyter/base.py +6 -20
  93. hanzo_mcp/tools/jupyter/jupyter.py +4 -12
  94. hanzo_mcp/tools/llm/consensus_tool.py +8 -24
  95. hanzo_mcp/tools/llm/llm_manage.py +2 -6
  96. hanzo_mcp/tools/llm/llm_tool.py +17 -58
  97. hanzo_mcp/tools/llm/llm_unified.py +18 -59
  98. hanzo_mcp/tools/llm/provider_tools.py +1 -3
  99. hanzo_mcp/tools/lsp/lsp_tool.py +621 -481
  100. hanzo_mcp/tools/mcp/mcp_add.py +3 -5
  101. hanzo_mcp/tools/mcp/mcp_remove.py +1 -1
  102. hanzo_mcp/tools/mcp/mcp_stats.py +1 -3
  103. hanzo_mcp/tools/mcp/mcp_tool.py +9 -23
  104. hanzo_mcp/tools/memory/__init__.py +33 -40
  105. hanzo_mcp/tools/memory/conversation_memory.py +636 -0
  106. hanzo_mcp/tools/memory/knowledge_tools.py +7 -25
  107. hanzo_mcp/tools/memory/memory_tools.py +7 -19
  108. hanzo_mcp/tools/search/find_tool.py +12 -34
  109. hanzo_mcp/tools/search/unified_search.py +27 -81
  110. hanzo_mcp/tools/shell/__init__.py +16 -4
  111. hanzo_mcp/tools/shell/auto_background.py +2 -6
  112. hanzo_mcp/tools/shell/base.py +1 -5
  113. hanzo_mcp/tools/shell/base_process.py +5 -7
  114. hanzo_mcp/tools/shell/bash_session.py +7 -24
  115. hanzo_mcp/tools/shell/bash_session_executor.py +5 -15
  116. hanzo_mcp/tools/shell/bash_tool.py +3 -7
  117. hanzo_mcp/tools/shell/command_executor.py +26 -79
  118. hanzo_mcp/tools/shell/logs.py +4 -16
  119. hanzo_mcp/tools/shell/npx.py +2 -8
  120. hanzo_mcp/tools/shell/npx_tool.py +1 -3
  121. hanzo_mcp/tools/shell/pkill.py +4 -12
  122. hanzo_mcp/tools/shell/process_tool.py +2 -8
  123. hanzo_mcp/tools/shell/processes.py +5 -17
  124. hanzo_mcp/tools/shell/run_background.py +1 -3
  125. hanzo_mcp/tools/shell/run_command.py +1 -3
  126. hanzo_mcp/tools/shell/run_command_windows.py +1 -3
  127. hanzo_mcp/tools/shell/run_tool.py +56 -0
  128. hanzo_mcp/tools/shell/session_manager.py +2 -6
  129. hanzo_mcp/tools/shell/session_storage.py +2 -6
  130. hanzo_mcp/tools/shell/streaming_command.py +7 -23
  131. hanzo_mcp/tools/shell/uvx.py +4 -14
  132. hanzo_mcp/tools/shell/uvx_background.py +2 -6
  133. hanzo_mcp/tools/shell/uvx_tool.py +1 -3
  134. hanzo_mcp/tools/shell/zsh_tool.py +12 -20
  135. hanzo_mcp/tools/todo/todo.py +1 -3
  136. hanzo_mcp/tools/vector/__init__.py +97 -50
  137. hanzo_mcp/tools/vector/ast_analyzer.py +6 -20
  138. hanzo_mcp/tools/vector/git_ingester.py +10 -30
  139. hanzo_mcp/tools/vector/index_tool.py +3 -9
  140. hanzo_mcp/tools/vector/infinity_store.py +11 -30
  141. hanzo_mcp/tools/vector/mock_infinity.py +159 -0
  142. hanzo_mcp/tools/vector/node_tool.py +538 -0
  143. hanzo_mcp/tools/vector/project_manager.py +4 -12
  144. hanzo_mcp/tools/vector/unified_vector.py +384 -0
  145. hanzo_mcp/tools/vector/vector.py +2 -6
  146. hanzo_mcp/tools/vector/vector_index.py +8 -8
  147. hanzo_mcp/tools/vector/vector_search.py +7 -21
  148. {hanzo_mcp-0.8.8.dist-info → hanzo_mcp-0.9.0.dist-info}/METADATA +2 -2
  149. hanzo_mcp-0.9.0.dist-info/RECORD +191 -0
  150. hanzo_mcp/tools/agent/agent_tool_v1_deprecated.py +0 -645
  151. hanzo_mcp/tools/agent/swarm_tool.py +0 -723
  152. hanzo_mcp/tools/agent/swarm_tool_v1_deprecated.py +0 -577
  153. hanzo_mcp/tools/filesystem/batch_search.py +0 -900
  154. hanzo_mcp/tools/filesystem/directory_tree_paginated.py +0 -350
  155. hanzo_mcp/tools/filesystem/find_files.py +0 -369
  156. hanzo_mcp/tools/filesystem/grep.py +0 -467
  157. hanzo_mcp/tools/filesystem/search_tool.py +0 -767
  158. hanzo_mcp/tools/filesystem/symbols_tool.py +0 -515
  159. hanzo_mcp/tools/filesystem/tree.py +0 -270
  160. hanzo_mcp/tools/jupyter/notebook_edit.py +0 -317
  161. hanzo_mcp/tools/jupyter/notebook_read.py +0 -147
  162. hanzo_mcp/tools/todo/todo_read.py +0 -143
  163. hanzo_mcp/tools/todo/todo_write.py +0 -374
  164. hanzo_mcp-0.8.8.dist-info/RECORD +0 -192
  165. {hanzo_mcp-0.8.8.dist-info → hanzo_mcp-0.9.0.dist-info}/WHEEL +0 -0
  166. {hanzo_mcp-0.8.8.dist-info → hanzo_mcp-0.9.0.dist-info}/entry_points.txt +0 -0
  167. {hanzo_mcp-0.8.8.dist-info → hanzo_mcp-0.9.0.dist-info}/top_level.txt +0 -0
@@ -28,11 +28,11 @@ TResult = TypeVar("TResult")
28
28
  @runtime_checkable
29
29
  class AgentContext(Protocol):
30
30
  """Protocol for agent execution context."""
31
-
31
+
32
32
  async def log(self, message: str, level: str = "info") -> None:
33
33
  """Log a message."""
34
34
  pass
35
-
35
+
36
36
  async def progress(self, message: str, percentage: Optional[float] = None) -> None:
37
37
  """Report progress."""
38
38
  pass
@@ -41,7 +41,7 @@ class AgentContext(Protocol):
41
41
  @dataclass
42
42
  class AgentConfig:
43
43
  """Configuration for agent execution."""
44
-
44
+
45
45
  model: str = "claude-3-5-sonnet-20241022"
46
46
  timeout: int = 300
47
47
  max_retries: int = 3
@@ -49,7 +49,7 @@ class AgentConfig:
49
49
  environment: Dict[str, str] = field(default_factory=dict)
50
50
  stream_output: bool = False
51
51
  use_worktree: bool = False
52
-
52
+
53
53
  def __post_init__(self) -> None:
54
54
  """Resolve model name and validate configuration."""
55
55
  self.model = registry.resolve(self.model)
@@ -60,13 +60,13 @@ class AgentConfig:
60
60
  @dataclass
61
61
  class AgentResult:
62
62
  """Result from agent execution."""
63
-
63
+
64
64
  success: bool
65
65
  output: Optional[str] = None
66
66
  error: Optional[str] = None
67
67
  duration: Optional[float] = None
68
68
  metadata: Dict[str, Any] = field(default_factory=dict)
69
-
69
+
70
70
  @property
71
71
  def content(self) -> str:
72
72
  """Get the primary content (output or error)."""
@@ -75,33 +75,33 @@ class AgentResult:
75
75
 
76
76
  class BaseAgent(ABC, Generic[TContext]):
77
77
  """Base class for all AI agents.
78
-
78
+
79
79
  This is the single foundation for all agent implementations,
80
80
  ensuring consistent behavior and eliminating code duplication.
81
81
  """
82
-
82
+
83
83
  def __init__(self, config: Optional[AgentConfig] = None) -> None:
84
84
  """Initialize agent with configuration.
85
-
85
+
86
86
  Args:
87
87
  config: Agent configuration
88
88
  """
89
89
  self.config = config or AgentConfig()
90
90
  self._start_time: Optional[datetime] = None
91
91
  self._end_time: Optional[datetime] = None
92
-
92
+
93
93
  @property
94
94
  @abstractmethod
95
95
  def name(self) -> str:
96
96
  """Agent name."""
97
97
  pass
98
-
98
+
99
99
  @property
100
100
  @abstractmethod
101
101
  def description(self) -> str:
102
102
  """Agent description."""
103
103
  pass
104
-
104
+
105
105
  async def execute(
106
106
  self,
107
107
  prompt: str,
@@ -109,76 +109,76 @@ class BaseAgent(ABC, Generic[TContext]):
109
109
  **kwargs: Any,
110
110
  ) -> AgentResult:
111
111
  """Execute agent with prompt.
112
-
112
+
113
113
  Args:
114
114
  prompt: The prompt or task
115
115
  context: Execution context
116
116
  **kwargs: Additional parameters
117
-
117
+
118
118
  Returns:
119
119
  Agent execution result
120
120
  """
121
121
  self._start_time = datetime.now()
122
-
122
+
123
123
  try:
124
124
  # Setup environment
125
125
  env = self._prepare_environment()
126
-
126
+
127
127
  # Log start
128
128
  if context and isinstance(context, AgentContext):
129
129
  await context.log(f"Starting {self.name} with model {self.config.model}")
130
-
130
+
131
131
  # Execute with retries
132
132
  result = await self._execute_with_retries(prompt, context, env, **kwargs)
133
-
133
+
134
134
  # Calculate duration
135
135
  self._end_time = datetime.now()
136
136
  duration = (self._end_time - self._start_time).total_seconds()
137
-
137
+
138
138
  return AgentResult(
139
139
  success=True,
140
140
  output=result,
141
141
  duration=duration,
142
142
  metadata={"model": self.config.model, "agent": self.name},
143
143
  )
144
-
144
+
145
145
  except Exception as e:
146
146
  self._end_time = datetime.now()
147
147
  duration = (self._end_time - self._start_time).total_seconds() if self._start_time else None
148
-
148
+
149
149
  logger.error(f"Agent {self.name} failed: {e}")
150
-
150
+
151
151
  return AgentResult(
152
152
  success=False,
153
153
  error=str(e),
154
154
  duration=duration,
155
155
  metadata={"model": self.config.model, "agent": self.name},
156
156
  )
157
-
157
+
158
158
  def _prepare_environment(self) -> Dict[str, str]:
159
159
  """Prepare environment variables for execution.
160
-
160
+
161
161
  Returns:
162
162
  Environment variables dictionary
163
163
  """
164
164
  env = os.environ.copy()
165
-
165
+
166
166
  # Add model-specific API key
167
167
  model_config = registry.get(self.config.model)
168
168
  if model_config and model_config.api_key_env:
169
169
  key_var = model_config.api_key_env
170
170
  if key_var in os.environ:
171
171
  env[key_var] = os.environ[key_var]
172
-
172
+
173
173
  # Add Hanzo unified auth
174
174
  if "HANZO_API_KEY" in os.environ:
175
175
  env["HANZO_API_KEY"] = os.environ["HANZO_API_KEY"]
176
-
176
+
177
177
  # Add custom environment
178
178
  env.update(self.config.environment)
179
-
179
+
180
180
  return env
181
-
181
+
182
182
  async def _execute_with_retries(
183
183
  self,
184
184
  prompt: str,
@@ -187,47 +187,47 @@ class BaseAgent(ABC, Generic[TContext]):
187
187
  **kwargs: Any,
188
188
  ) -> str:
189
189
  """Execute with retry logic.
190
-
190
+
191
191
  Args:
192
192
  prompt: The prompt
193
193
  context: Execution context
194
194
  env: Environment variables
195
195
  **kwargs: Additional parameters
196
-
196
+
197
197
  Returns:
198
198
  Execution output
199
-
199
+
200
200
  Raises:
201
201
  Exception: If all retries fail
202
202
  """
203
203
  last_error = None
204
-
204
+
205
205
  for attempt in range(self.config.max_retries):
206
206
  try:
207
207
  # Call the implementation
208
208
  result = await self._execute_impl(prompt, context, env, **kwargs)
209
209
  return result
210
-
210
+
211
211
  except asyncio.TimeoutError:
212
212
  last_error = f"Timeout after {self.config.timeout} seconds"
213
213
  if context and isinstance(context, AgentContext):
214
214
  await context.log(f"Attempt {attempt + 1} timed out", "warning")
215
-
215
+
216
216
  except Exception as e:
217
217
  last_error = str(e)
218
218
  if context and isinstance(context, AgentContext):
219
219
  await context.log(f"Attempt {attempt + 1} failed: {e}", "warning")
220
-
220
+
221
221
  # Don't retry on certain errors
222
222
  if "unauthorized" in str(e).lower() or "forbidden" in str(e).lower():
223
223
  raise
224
-
224
+
225
225
  # Wait before retry (exponential backoff)
226
226
  if attempt < self.config.max_retries - 1:
227
- await asyncio.sleep(2 ** attempt)
228
-
227
+ await asyncio.sleep(2**attempt)
228
+
229
229
  raise Exception(f"All {self.config.max_retries} attempts failed. Last error: {last_error}")
230
-
230
+
231
231
  @abstractmethod
232
232
  async def _execute_impl(
233
233
  self,
@@ -237,13 +237,13 @@ class BaseAgent(ABC, Generic[TContext]):
237
237
  **kwargs: Any,
238
238
  ) -> str:
239
239
  """Implementation-specific execution.
240
-
240
+
241
241
  Args:
242
242
  prompt: The prompt
243
243
  context: Execution context
244
244
  env: Environment variables
245
245
  **kwargs: Additional parameters
246
-
246
+
247
247
  Returns:
248
248
  Execution output
249
249
  """
@@ -252,35 +252,35 @@ class BaseAgent(ABC, Generic[TContext]):
252
252
 
253
253
  class CLIAgent(BaseAgent[TContext]):
254
254
  """Base class for CLI-based agents."""
255
-
255
+
256
256
  @property
257
257
  @abstractmethod
258
258
  def cli_command(self) -> str:
259
259
  """CLI command to execute."""
260
260
  pass
261
-
261
+
262
262
  def build_command(self, prompt: str, **kwargs: Any) -> List[str]:
263
263
  """Build the CLI command.
264
-
264
+
265
265
  Args:
266
266
  prompt: The prompt
267
267
  **kwargs: Additional parameters
268
-
268
+
269
269
  Returns:
270
270
  Command arguments list
271
271
  """
272
272
  command = [self.cli_command]
273
-
273
+
274
274
  # Add model if specified
275
275
  model_config = registry.get(self.config.model)
276
276
  if model_config:
277
277
  command.extend(["--model", model_config.full_name])
278
-
278
+
279
279
  # Add prompt
280
280
  command.append(prompt)
281
-
281
+
282
282
  return command
283
-
283
+
284
284
  async def _execute_impl(
285
285
  self,
286
286
  prompt: str,
@@ -289,21 +289,21 @@ class CLIAgent(BaseAgent[TContext]):
289
289
  **kwargs: Any,
290
290
  ) -> str:
291
291
  """Execute CLI command.
292
-
292
+
293
293
  Args:
294
294
  prompt: The prompt
295
295
  context: Execution context
296
296
  env: Environment variables
297
297
  **kwargs: Additional parameters
298
-
298
+
299
299
  Returns:
300
300
  Command output
301
301
  """
302
302
  command = self.build_command(prompt, **kwargs)
303
-
303
+
304
304
  # Determine if we need stdin
305
305
  needs_stdin = self.cli_command in ["claude", "cline"]
306
-
306
+
307
307
  # Execute command
308
308
  process = await asyncio.create_subprocess_exec(
309
309
  *command,
@@ -313,7 +313,7 @@ class CLIAgent(BaseAgent[TContext]):
313
313
  cwd=str(self.config.working_dir) if self.config.working_dir else None,
314
314
  env=env,
315
315
  )
316
-
316
+
317
317
  # Handle timeout
318
318
  try:
319
319
  stdout, stderr = await asyncio.wait_for(
@@ -323,18 +323,18 @@ class CLIAgent(BaseAgent[TContext]):
323
323
  except asyncio.TimeoutError:
324
324
  process.kill()
325
325
  raise asyncio.TimeoutError(f"Command timed out after {self.config.timeout} seconds")
326
-
326
+
327
327
  # Check for errors
328
328
  if process.returncode != 0:
329
329
  error_msg = stderr.decode() if stderr else "Command failed"
330
330
  raise Exception(error_msg)
331
-
331
+
332
332
  return stdout.decode()
333
333
 
334
334
 
335
335
  class APIAgent(BaseAgent[TContext]):
336
336
  """Base class for API-based agents."""
337
-
337
+
338
338
  async def _execute_impl(
339
339
  self,
340
340
  prompt: str,
@@ -343,13 +343,13 @@ class APIAgent(BaseAgent[TContext]):
343
343
  **kwargs: Any,
344
344
  ) -> str:
345
345
  """Execute via API.
346
-
346
+
347
347
  Args:
348
348
  prompt: The prompt
349
349
  context: Execution context
350
350
  env: Environment variables
351
351
  **kwargs: Additional parameters
352
-
352
+
353
353
  Returns:
354
354
  API response
355
355
  """
@@ -360,36 +360,36 @@ class APIAgent(BaseAgent[TContext]):
360
360
 
361
361
  class AgentOrchestrator:
362
362
  """Orchestrator for managing multiple agents."""
363
-
363
+
364
364
  def __init__(self, default_config: Optional[AgentConfig] = None) -> None:
365
365
  """Initialize orchestrator.
366
-
366
+
367
367
  Args:
368
368
  default_config: Default configuration for agents
369
369
  """
370
370
  self.default_config = default_config or AgentConfig()
371
371
  self._agents: Dict[str, BaseAgent] = {}
372
372
  self._semaphore: Optional[asyncio.Semaphore] = None
373
-
373
+
374
374
  def register(self, agent: BaseAgent) -> None:
375
375
  """Register an agent.
376
-
376
+
377
377
  Args:
378
378
  agent: Agent to register
379
379
  """
380
380
  self._agents[agent.name] = agent
381
-
381
+
382
382
  def get_agent(self, name: str) -> Optional[BaseAgent]:
383
383
  """Get agent by name.
384
-
384
+
385
385
  Args:
386
386
  name: Agent name
387
-
387
+
388
388
  Returns:
389
389
  Agent instance or None
390
390
  """
391
391
  return self._agents.get(name)
392
-
392
+
393
393
  async def execute_single(
394
394
  self,
395
395
  agent_name: str,
@@ -398,13 +398,13 @@ class AgentOrchestrator:
398
398
  **kwargs: Any,
399
399
  ) -> AgentResult:
400
400
  """Execute single agent.
401
-
401
+
402
402
  Args:
403
403
  agent_name: Name of agent to use
404
404
  prompt: The prompt
405
405
  context: Execution context
406
406
  **kwargs: Additional parameters
407
-
407
+
408
408
  Returns:
409
409
  Execution result
410
410
  """
@@ -414,25 +414,25 @@ class AgentOrchestrator:
414
414
  success=False,
415
415
  error=f"Agent '{agent_name}' not found",
416
416
  )
417
-
417
+
418
418
  return await agent.execute(prompt, context, **kwargs)
419
-
419
+
420
420
  async def execute_parallel(
421
421
  self,
422
422
  tasks: List[Dict[str, Any]],
423
423
  max_concurrent: int = 5,
424
424
  ) -> List[AgentResult]:
425
425
  """Execute multiple agents in parallel.
426
-
426
+
427
427
  Args:
428
428
  tasks: List of task definitions
429
429
  max_concurrent: Maximum concurrent executions
430
-
430
+
431
431
  Returns:
432
432
  List of results
433
433
  """
434
434
  self._semaphore = asyncio.Semaphore(max_concurrent)
435
-
435
+
436
436
  async def run_with_semaphore(task: Dict[str, Any]) -> AgentResult:
437
437
  async with self._semaphore:
438
438
  return await self.execute_single(
@@ -441,12 +441,12 @@ class AgentOrchestrator:
441
441
  task.get("context"),
442
442
  **task.get("kwargs", {}),
443
443
  )
444
-
444
+
445
445
  return await asyncio.gather(
446
446
  *[run_with_semaphore(task) for task in tasks],
447
447
  return_exceptions=False,
448
448
  )
449
-
449
+
450
450
  async def execute_consensus(
451
451
  self,
452
452
  prompt: str,
@@ -454,58 +454,58 @@ class AgentOrchestrator:
454
454
  threshold: float = 0.66,
455
455
  ) -> Dict[str, Any]:
456
456
  """Execute consensus operation with multiple agents.
457
-
457
+
458
458
  Args:
459
459
  prompt: The prompt
460
460
  agents: List of agent names
461
461
  threshold: Agreement threshold
462
-
462
+
463
463
  Returns:
464
464
  Consensus results
465
465
  """
466
466
  # Execute all agents in parallel
467
467
  tasks = [{"agent": agent, "prompt": prompt} for agent in agents]
468
468
  results = await self.execute_parallel(tasks)
469
-
469
+
470
470
  # Analyze consensus
471
471
  successful = [r for r in results if r.success]
472
472
  agreement = len(successful) / len(results) if results else 0
473
-
473
+
474
474
  return {
475
475
  "consensus_reached": agreement >= threshold,
476
476
  "agreement_score": agreement,
477
477
  "individual_results": results,
478
478
  "agents_used": agents,
479
479
  }
480
-
480
+
481
481
  async def execute_chain(
482
482
  self,
483
483
  initial_prompt: str,
484
484
  agents: List[str],
485
485
  ) -> List[AgentResult]:
486
486
  """Execute agents in a chain, passing output forward.
487
-
487
+
488
488
  Args:
489
489
  initial_prompt: Initial prompt
490
490
  agents: List of agent names
491
-
491
+
492
492
  Returns:
493
493
  List of results from each step
494
494
  """
495
495
  results = []
496
496
  current_prompt = initial_prompt
497
-
497
+
498
498
  for agent_name in agents:
499
499
  result = await self.execute_single(agent_name, current_prompt)
500
500
  results.append(result)
501
-
501
+
502
502
  if result.success and result.output:
503
503
  # Use output as input for next agent
504
504
  current_prompt = f"Review and improve:\n{result.output}"
505
505
  else:
506
506
  # Chain broken
507
507
  break
508
-
508
+
509
509
  return results
510
510
 
511
511
 
@@ -517,4 +517,4 @@ __all__ = [
517
517
  "CLIAgent",
518
518
  "APIAgent",
519
519
  "AgentOrchestrator",
520
- ]
520
+ ]