agentpool 2.1.9__py3-none-any.whl โ 2.5.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.
- acp/__init__.py +13 -4
- acp/acp_requests.py +20 -77
- acp/agent/connection.py +8 -0
- acp/agent/implementations/debug_server/debug_server.py +6 -2
- acp/agent/protocol.py +6 -0
- acp/bridge/README.md +15 -2
- acp/bridge/__init__.py +3 -2
- acp/bridge/__main__.py +60 -19
- acp/bridge/ws_server.py +173 -0
- acp/bridge/ws_server_cli.py +89 -0
- acp/client/connection.py +38 -29
- acp/client/implementations/default_client.py +3 -2
- acp/client/implementations/headless_client.py +2 -2
- acp/connection.py +2 -2
- acp/notifications.py +20 -50
- acp/schema/__init__.py +2 -0
- acp/schema/agent_responses.py +21 -0
- acp/schema/client_requests.py +3 -3
- acp/schema/session_state.py +63 -29
- acp/stdio.py +39 -9
- acp/task/supervisor.py +2 -2
- acp/transports.py +362 -2
- acp/utils.py +17 -4
- agentpool/__init__.py +6 -1
- agentpool/agents/__init__.py +2 -0
- agentpool/agents/acp_agent/acp_agent.py +407 -277
- agentpool/agents/acp_agent/acp_converters.py +196 -38
- agentpool/agents/acp_agent/client_handler.py +191 -26
- agentpool/agents/acp_agent/session_state.py +17 -6
- agentpool/agents/agent.py +607 -572
- agentpool/agents/agui_agent/__init__.py +0 -2
- agentpool/agents/agui_agent/agui_agent.py +176 -110
- agentpool/agents/agui_agent/agui_converters.py +0 -131
- agentpool/agents/agui_agent/helpers.py +3 -4
- agentpool/agents/base_agent.py +632 -17
- agentpool/agents/claude_code_agent/FORKING.md +191 -0
- agentpool/agents/claude_code_agent/__init__.py +13 -1
- agentpool/agents/claude_code_agent/claude_code_agent.py +1058 -291
- agentpool/agents/claude_code_agent/converters.py +74 -143
- agentpool/agents/claude_code_agent/history.py +474 -0
- agentpool/agents/claude_code_agent/models.py +77 -0
- agentpool/agents/claude_code_agent/static_info.py +100 -0
- agentpool/agents/claude_code_agent/usage.py +242 -0
- agentpool/agents/context.py +40 -0
- agentpool/agents/events/__init__.py +24 -0
- agentpool/agents/events/builtin_handlers.py +67 -1
- agentpool/agents/events/event_emitter.py +32 -2
- agentpool/agents/events/events.py +104 -3
- agentpool/agents/events/infer_info.py +145 -0
- agentpool/agents/events/processors.py +254 -0
- agentpool/agents/interactions.py +41 -6
- agentpool/agents/modes.py +67 -0
- agentpool/agents/slashed_agent.py +5 -4
- agentpool/agents/tool_call_accumulator.py +213 -0
- agentpool/agents/tool_wrapping.py +18 -6
- agentpool/common_types.py +56 -21
- agentpool/config_resources/__init__.py +38 -1
- agentpool/config_resources/acp_assistant.yml +2 -2
- agentpool/config_resources/agents.yml +3 -0
- agentpool/config_resources/agents_template.yml +1 -0
- agentpool/config_resources/claude_code_agent.yml +10 -6
- agentpool/config_resources/external_acp_agents.yml +2 -1
- agentpool/delegation/base_team.py +4 -30
- agentpool/delegation/pool.py +136 -289
- agentpool/delegation/team.py +58 -57
- agentpool/delegation/teamrun.py +51 -55
- agentpool/diagnostics/__init__.py +53 -0
- agentpool/diagnostics/lsp_manager.py +1593 -0
- agentpool/diagnostics/lsp_proxy.py +41 -0
- agentpool/diagnostics/lsp_proxy_script.py +229 -0
- agentpool/diagnostics/models.py +398 -0
- agentpool/functional/run.py +10 -4
- agentpool/mcp_server/__init__.py +0 -2
- agentpool/mcp_server/client.py +76 -32
- agentpool/mcp_server/conversions.py +54 -13
- agentpool/mcp_server/manager.py +34 -54
- agentpool/mcp_server/registries/official_registry_client.py +35 -1
- agentpool/mcp_server/tool_bridge.py +186 -139
- agentpool/messaging/__init__.py +0 -2
- agentpool/messaging/compaction.py +72 -197
- agentpool/messaging/connection_manager.py +11 -10
- agentpool/messaging/event_manager.py +5 -5
- agentpool/messaging/message_container.py +6 -30
- agentpool/messaging/message_history.py +99 -8
- agentpool/messaging/messagenode.py +52 -14
- agentpool/messaging/messages.py +54 -35
- agentpool/messaging/processing.py +12 -22
- agentpool/models/__init__.py +1 -1
- agentpool/models/acp_agents/base.py +6 -24
- agentpool/models/acp_agents/mcp_capable.py +126 -157
- agentpool/models/acp_agents/non_mcp.py +129 -95
- agentpool/models/agents.py +98 -76
- agentpool/models/agui_agents.py +1 -1
- agentpool/models/claude_code_agents.py +144 -19
- agentpool/models/file_parsing.py +0 -1
- agentpool/models/manifest.py +113 -50
- agentpool/prompts/conversion_manager.py +1 -1
- agentpool/prompts/prompts.py +5 -2
- agentpool/repomap.py +1 -1
- agentpool/resource_providers/__init__.py +11 -1
- agentpool/resource_providers/aggregating.py +56 -5
- agentpool/resource_providers/base.py +70 -4
- agentpool/resource_providers/codemode/code_executor.py +72 -5
- agentpool/resource_providers/codemode/helpers.py +2 -2
- agentpool/resource_providers/codemode/provider.py +64 -12
- agentpool/resource_providers/codemode/remote_mcp_execution.py +2 -2
- agentpool/resource_providers/codemode/remote_provider.py +9 -12
- agentpool/resource_providers/filtering.py +3 -1
- agentpool/resource_providers/mcp_provider.py +89 -12
- agentpool/resource_providers/plan_provider.py +228 -46
- agentpool/resource_providers/pool.py +7 -3
- agentpool/resource_providers/resource_info.py +111 -0
- agentpool/resource_providers/static.py +4 -2
- agentpool/sessions/__init__.py +4 -1
- agentpool/sessions/manager.py +33 -5
- agentpool/sessions/models.py +59 -6
- agentpool/sessions/protocol.py +28 -0
- agentpool/sessions/session.py +11 -55
- agentpool/skills/registry.py +13 -8
- agentpool/storage/manager.py +572 -49
- agentpool/talk/registry.py +4 -4
- agentpool/talk/talk.py +9 -10
- agentpool/testing.py +538 -20
- agentpool/tool_impls/__init__.py +6 -0
- agentpool/tool_impls/agent_cli/__init__.py +42 -0
- agentpool/tool_impls/agent_cli/tool.py +95 -0
- agentpool/tool_impls/bash/__init__.py +64 -0
- agentpool/tool_impls/bash/helpers.py +35 -0
- agentpool/tool_impls/bash/tool.py +171 -0
- agentpool/tool_impls/delete_path/__init__.py +70 -0
- agentpool/tool_impls/delete_path/tool.py +142 -0
- agentpool/tool_impls/download_file/__init__.py +80 -0
- agentpool/tool_impls/download_file/tool.py +183 -0
- agentpool/tool_impls/execute_code/__init__.py +55 -0
- agentpool/tool_impls/execute_code/tool.py +163 -0
- agentpool/tool_impls/grep/__init__.py +80 -0
- agentpool/tool_impls/grep/tool.py +200 -0
- agentpool/tool_impls/list_directory/__init__.py +73 -0
- agentpool/tool_impls/list_directory/tool.py +197 -0
- agentpool/tool_impls/question/__init__.py +42 -0
- agentpool/tool_impls/question/tool.py +127 -0
- agentpool/tool_impls/read/__init__.py +104 -0
- agentpool/tool_impls/read/tool.py +305 -0
- agentpool/tools/__init__.py +2 -1
- agentpool/tools/base.py +114 -34
- agentpool/tools/manager.py +57 -1
- agentpool/ui/base.py +2 -2
- agentpool/ui/mock_provider.py +2 -2
- agentpool/ui/stdlib_provider.py +2 -2
- agentpool/utils/file_watcher.py +269 -0
- agentpool/utils/identifiers.py +121 -0
- agentpool/utils/pydantic_ai_helpers.py +46 -0
- agentpool/utils/streams.py +616 -2
- agentpool/utils/subprocess_utils.py +155 -0
- agentpool/utils/token_breakdown.py +461 -0
- agentpool/vfs_registry.py +7 -2
- {agentpool-2.1.9.dist-info โ agentpool-2.5.0.dist-info}/METADATA +41 -27
- agentpool-2.5.0.dist-info/RECORD +579 -0
- {agentpool-2.1.9.dist-info โ agentpool-2.5.0.dist-info}/WHEEL +1 -1
- agentpool_cli/__main__.py +24 -0
- agentpool_cli/create.py +1 -1
- agentpool_cli/serve_acp.py +100 -21
- agentpool_cli/serve_agui.py +87 -0
- agentpool_cli/serve_opencode.py +119 -0
- agentpool_cli/ui.py +557 -0
- agentpool_commands/__init__.py +42 -5
- agentpool_commands/agents.py +75 -2
- agentpool_commands/history.py +62 -0
- agentpool_commands/mcp.py +176 -0
- agentpool_commands/models.py +56 -3
- agentpool_commands/pool.py +260 -0
- agentpool_commands/session.py +1 -1
- agentpool_commands/text_sharing/__init__.py +119 -0
- agentpool_commands/text_sharing/base.py +123 -0
- agentpool_commands/text_sharing/github_gist.py +80 -0
- agentpool_commands/text_sharing/opencode.py +462 -0
- agentpool_commands/text_sharing/paste_rs.py +59 -0
- agentpool_commands/text_sharing/pastebin.py +116 -0
- agentpool_commands/text_sharing/shittycodingagent.py +112 -0
- agentpool_commands/tools.py +57 -0
- agentpool_commands/utils.py +80 -30
- agentpool_config/__init__.py +30 -2
- agentpool_config/agentpool_tools.py +498 -0
- agentpool_config/builtin_tools.py +77 -22
- agentpool_config/commands.py +24 -1
- agentpool_config/compaction.py +258 -0
- agentpool_config/converters.py +1 -1
- agentpool_config/event_handlers.py +42 -0
- agentpool_config/events.py +1 -1
- agentpool_config/forward_targets.py +1 -4
- agentpool_config/jinja.py +3 -3
- agentpool_config/mcp_server.py +132 -6
- agentpool_config/nodes.py +1 -1
- agentpool_config/observability.py +44 -0
- agentpool_config/session.py +0 -3
- agentpool_config/storage.py +82 -38
- agentpool_config/task.py +3 -3
- agentpool_config/tools.py +11 -22
- agentpool_config/toolsets.py +109 -233
- agentpool_server/a2a_server/agent_worker.py +307 -0
- agentpool_server/a2a_server/server.py +23 -18
- agentpool_server/acp_server/acp_agent.py +234 -181
- agentpool_server/acp_server/commands/acp_commands.py +151 -156
- agentpool_server/acp_server/commands/docs_commands/fetch_repo.py +18 -17
- agentpool_server/acp_server/event_converter.py +651 -0
- agentpool_server/acp_server/input_provider.py +53 -10
- agentpool_server/acp_server/server.py +24 -90
- agentpool_server/acp_server/session.py +173 -331
- agentpool_server/acp_server/session_manager.py +8 -34
- agentpool_server/agui_server/server.py +3 -1
- agentpool_server/mcp_server/server.py +5 -2
- agentpool_server/opencode_server/.rules +95 -0
- agentpool_server/opencode_server/ENDPOINTS.md +401 -0
- agentpool_server/opencode_server/OPENCODE_UI_TOOLS_COMPLETE.md +202 -0
- agentpool_server/opencode_server/__init__.py +19 -0
- agentpool_server/opencode_server/command_validation.py +172 -0
- agentpool_server/opencode_server/converters.py +975 -0
- agentpool_server/opencode_server/dependencies.py +24 -0
- agentpool_server/opencode_server/input_provider.py +421 -0
- agentpool_server/opencode_server/models/__init__.py +250 -0
- agentpool_server/opencode_server/models/agent.py +53 -0
- agentpool_server/opencode_server/models/app.py +72 -0
- agentpool_server/opencode_server/models/base.py +26 -0
- agentpool_server/opencode_server/models/common.py +23 -0
- agentpool_server/opencode_server/models/config.py +37 -0
- agentpool_server/opencode_server/models/events.py +821 -0
- agentpool_server/opencode_server/models/file.py +88 -0
- agentpool_server/opencode_server/models/mcp.py +44 -0
- agentpool_server/opencode_server/models/message.py +179 -0
- agentpool_server/opencode_server/models/parts.py +323 -0
- agentpool_server/opencode_server/models/provider.py +81 -0
- agentpool_server/opencode_server/models/pty.py +43 -0
- agentpool_server/opencode_server/models/question.py +56 -0
- agentpool_server/opencode_server/models/session.py +111 -0
- agentpool_server/opencode_server/routes/__init__.py +29 -0
- agentpool_server/opencode_server/routes/agent_routes.py +473 -0
- agentpool_server/opencode_server/routes/app_routes.py +202 -0
- agentpool_server/opencode_server/routes/config_routes.py +302 -0
- agentpool_server/opencode_server/routes/file_routes.py +571 -0
- agentpool_server/opencode_server/routes/global_routes.py +94 -0
- agentpool_server/opencode_server/routes/lsp_routes.py +319 -0
- agentpool_server/opencode_server/routes/message_routes.py +761 -0
- agentpool_server/opencode_server/routes/permission_routes.py +63 -0
- agentpool_server/opencode_server/routes/pty_routes.py +300 -0
- agentpool_server/opencode_server/routes/question_routes.py +128 -0
- agentpool_server/opencode_server/routes/session_routes.py +1276 -0
- agentpool_server/opencode_server/routes/tui_routes.py +139 -0
- agentpool_server/opencode_server/server.py +475 -0
- agentpool_server/opencode_server/state.py +151 -0
- agentpool_server/opencode_server/time_utils.py +8 -0
- agentpool_storage/__init__.py +12 -0
- agentpool_storage/base.py +184 -2
- agentpool_storage/claude_provider/ARCHITECTURE.md +433 -0
- agentpool_storage/claude_provider/__init__.py +42 -0
- agentpool_storage/claude_provider/provider.py +1089 -0
- agentpool_storage/file_provider.py +278 -15
- agentpool_storage/memory_provider.py +193 -12
- agentpool_storage/models.py +3 -0
- agentpool_storage/opencode_provider/ARCHITECTURE.md +386 -0
- agentpool_storage/opencode_provider/__init__.py +16 -0
- agentpool_storage/opencode_provider/helpers.py +414 -0
- agentpool_storage/opencode_provider/provider.py +895 -0
- agentpool_storage/project_store.py +325 -0
- agentpool_storage/session_store.py +26 -6
- agentpool_storage/sql_provider/__init__.py +4 -2
- agentpool_storage/sql_provider/models.py +48 -0
- agentpool_storage/sql_provider/sql_provider.py +269 -3
- agentpool_storage/sql_provider/utils.py +12 -13
- agentpool_storage/zed_provider/__init__.py +16 -0
- agentpool_storage/zed_provider/helpers.py +281 -0
- agentpool_storage/zed_provider/models.py +130 -0
- agentpool_storage/zed_provider/provider.py +442 -0
- agentpool_storage/zed_provider.py +803 -0
- agentpool_toolsets/__init__.py +0 -2
- agentpool_toolsets/builtin/__init__.py +2 -12
- agentpool_toolsets/builtin/code.py +96 -57
- agentpool_toolsets/builtin/debug.py +118 -48
- agentpool_toolsets/builtin/execution_environment.py +115 -230
- agentpool_toolsets/builtin/file_edit/file_edit.py +115 -7
- agentpool_toolsets/builtin/skills.py +9 -4
- agentpool_toolsets/builtin/subagent_tools.py +64 -51
- agentpool_toolsets/builtin/workers.py +4 -2
- agentpool_toolsets/composio_toolset.py +2 -2
- agentpool_toolsets/entry_points.py +3 -1
- agentpool_toolsets/fsspec_toolset/__init__.py +13 -1
- agentpool_toolsets/fsspec_toolset/diagnostics.py +860 -73
- agentpool_toolsets/fsspec_toolset/grep.py +99 -7
- agentpool_toolsets/fsspec_toolset/helpers.py +3 -2
- agentpool_toolsets/fsspec_toolset/image_utils.py +161 -0
- agentpool_toolsets/fsspec_toolset/toolset.py +500 -95
- agentpool_toolsets/mcp_discovery/__init__.py +5 -0
- agentpool_toolsets/mcp_discovery/data/mcp_servers.parquet +0 -0
- agentpool_toolsets/mcp_discovery/toolset.py +511 -0
- agentpool_toolsets/mcp_run_toolset.py +87 -12
- agentpool_toolsets/notifications.py +33 -33
- agentpool_toolsets/openapi.py +3 -1
- agentpool_toolsets/search_toolset.py +3 -1
- agentpool-2.1.9.dist-info/RECORD +0 -474
- agentpool_config/resources.py +0 -33
- agentpool_server/acp_server/acp_tools.py +0 -43
- agentpool_server/acp_server/commands/spawn.py +0 -210
- agentpool_storage/text_log_provider.py +0 -275
- agentpool_toolsets/builtin/agent_management.py +0 -239
- agentpool_toolsets/builtin/chain.py +0 -288
- agentpool_toolsets/builtin/history.py +0 -36
- agentpool_toolsets/builtin/integration.py +0 -85
- agentpool_toolsets/builtin/tool_management.py +0 -90
- agentpool_toolsets/builtin/user_interaction.py +0 -52
- agentpool_toolsets/semantic_memory_toolset.py +0 -536
- {agentpool-2.1.9.dist-info โ agentpool-2.5.0.dist-info}/entry_points.txt +0 -0
- {agentpool-2.1.9.dist-info โ agentpool-2.5.0.dist-info}/licenses/LICENSE +0 -0
agentpool_commands/agents.py
CHANGED
|
@@ -2,15 +2,21 @@
|
|
|
2
2
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
|
+
from typing import TYPE_CHECKING, Any, Literal
|
|
6
|
+
|
|
5
7
|
from slashed import CommandContext, CommandError # noqa: TC002
|
|
6
8
|
from slashed.completers import CallbackCompleter
|
|
7
9
|
|
|
8
10
|
from agentpool.messaging.context import NodeContext # noqa: TC001
|
|
9
11
|
from agentpool_commands.base import NodeCommand
|
|
10
|
-
from agentpool_commands.completers import get_available_agents
|
|
12
|
+
from agentpool_commands.completers import get_available_agents, get_available_nodes
|
|
11
13
|
from agentpool_commands.markdown_utils import format_table
|
|
12
14
|
|
|
13
15
|
|
|
16
|
+
if TYPE_CHECKING:
|
|
17
|
+
from agentpool.delegation import BaseTeam
|
|
18
|
+
|
|
19
|
+
|
|
14
20
|
class CreateAgentCommand(NodeCommand):
|
|
15
21
|
"""Create a new agent in the current session.
|
|
16
22
|
|
|
@@ -71,7 +77,7 @@ class CreateAgentCommand(NodeCommand):
|
|
|
71
77
|
# Create and register the new agent
|
|
72
78
|
await ctx.context.pool.add_agent(
|
|
73
79
|
name=agent_name,
|
|
74
|
-
model=model or current_agent.model_name,
|
|
80
|
+
model=model or current_agent.model_name or "openai:gpt-4o-mini",
|
|
75
81
|
system_prompt=system_prompt or (),
|
|
76
82
|
description=description,
|
|
77
83
|
tools=tool_list,
|
|
@@ -197,3 +203,70 @@ class SwitchAgentCommand(NodeCommand):
|
|
|
197
203
|
def get_completer(self) -> CallbackCompleter:
|
|
198
204
|
"""Get completer for agent names."""
|
|
199
205
|
return CallbackCompleter(get_available_agents)
|
|
206
|
+
|
|
207
|
+
|
|
208
|
+
class CreateTeamCommand(NodeCommand):
|
|
209
|
+
"""Create a team from existing agents/nodes.
|
|
210
|
+
|
|
211
|
+
Teams allow multiple agents to work together, either in parallel
|
|
212
|
+
or sequentially (pipeline).
|
|
213
|
+
|
|
214
|
+
Options:
|
|
215
|
+
--mode sequential|parallel How the team operates (default: sequential)
|
|
216
|
+
|
|
217
|
+
Examples:
|
|
218
|
+
# Create a team named "crew" with alice and bob
|
|
219
|
+
/create-team crew alice bob
|
|
220
|
+
|
|
221
|
+
# Create a parallel team
|
|
222
|
+
/create-team reviewers alice bob --mode parallel
|
|
223
|
+
"""
|
|
224
|
+
|
|
225
|
+
name = "create-team"
|
|
226
|
+
category = "agents"
|
|
227
|
+
|
|
228
|
+
async def execute_command(
|
|
229
|
+
self,
|
|
230
|
+
ctx: CommandContext[NodeContext],
|
|
231
|
+
name: str,
|
|
232
|
+
*nodes: str,
|
|
233
|
+
mode: Literal["sequential", "parallel"] = "sequential",
|
|
234
|
+
) -> None:
|
|
235
|
+
"""Create a team from existing nodes.
|
|
236
|
+
|
|
237
|
+
Args:
|
|
238
|
+
ctx: Command context
|
|
239
|
+
name: Name for the team
|
|
240
|
+
nodes: Names of agents/teams to include
|
|
241
|
+
mode: How the team operates (sequential or parallel)
|
|
242
|
+
"""
|
|
243
|
+
if not ctx.context.pool:
|
|
244
|
+
msg = "No agent pool available"
|
|
245
|
+
raise CommandError(msg)
|
|
246
|
+
|
|
247
|
+
if len(nodes) < 2: # noqa: PLR2004
|
|
248
|
+
msg = "At least 2 members are required to create a team"
|
|
249
|
+
raise CommandError(msg)
|
|
250
|
+
|
|
251
|
+
# Verify all nodes exist
|
|
252
|
+
node_list = list(nodes)
|
|
253
|
+
for node_name in node_list:
|
|
254
|
+
if node_name not in ctx.context.pool.nodes:
|
|
255
|
+
available = ", ".join(ctx.context.pool.nodes.keys())
|
|
256
|
+
msg = f"Node '{node_name}' not found. Available: {available}"
|
|
257
|
+
raise CommandError(msg)
|
|
258
|
+
|
|
259
|
+
# Create the team
|
|
260
|
+
if mode == "sequential":
|
|
261
|
+
team: BaseTeam[Any, Any] = ctx.context.pool.create_team_run(node_list, name=name)
|
|
262
|
+
else:
|
|
263
|
+
team = ctx.context.pool.create_team(node_list, name=name)
|
|
264
|
+
|
|
265
|
+
mode_str = "pipeline" if mode == "sequential" else "parallel"
|
|
266
|
+
await ctx.print(
|
|
267
|
+
f"**Created {mode_str} team** `{team.name}` with nodes: `{', '.join(node_list)}`"
|
|
268
|
+
)
|
|
269
|
+
|
|
270
|
+
def get_completer(self) -> CallbackCompleter:
|
|
271
|
+
"""Get completer for node names."""
|
|
272
|
+
return CallbackCompleter(get_available_nodes)
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
"""History management commands."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from slashed import CommandContext, CommandError # noqa: TC002
|
|
6
|
+
|
|
7
|
+
from agentpool.messaging.context import NodeContext # noqa: TC001
|
|
8
|
+
from agentpool_commands.base import NodeCommand
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class SearchHistoryCommand(NodeCommand):
|
|
12
|
+
"""Search conversation history.
|
|
13
|
+
|
|
14
|
+
Search through past conversations with optional filtering.
|
|
15
|
+
|
|
16
|
+
Options:
|
|
17
|
+
--hours N Look back N hours (default: 24)
|
|
18
|
+
--limit N Maximum results to return (default: 5)
|
|
19
|
+
|
|
20
|
+
Examples:
|
|
21
|
+
/search-history # Recent conversations
|
|
22
|
+
/search-history "error handling" # Search for specific topic
|
|
23
|
+
/search-history --hours 48 --limit 10
|
|
24
|
+
"""
|
|
25
|
+
|
|
26
|
+
name = "search-history"
|
|
27
|
+
category = "history"
|
|
28
|
+
|
|
29
|
+
async def execute_command(
|
|
30
|
+
self,
|
|
31
|
+
ctx: CommandContext[NodeContext],
|
|
32
|
+
query: str | None = None,
|
|
33
|
+
*,
|
|
34
|
+
hours: int = 24,
|
|
35
|
+
limit: int = 5,
|
|
36
|
+
) -> None:
|
|
37
|
+
"""Search conversation history.
|
|
38
|
+
|
|
39
|
+
Args:
|
|
40
|
+
ctx: Command context
|
|
41
|
+
query: Optional search query
|
|
42
|
+
hours: Look back period in hours
|
|
43
|
+
limit: Maximum results to return
|
|
44
|
+
"""
|
|
45
|
+
if not ctx.context.pool:
|
|
46
|
+
msg = "No agent pool available for history search"
|
|
47
|
+
raise CommandError(msg)
|
|
48
|
+
|
|
49
|
+
try:
|
|
50
|
+
from agentpool_storage.formatters import format_output
|
|
51
|
+
|
|
52
|
+
provider = ctx.context.pool.storage.get_history_provider()
|
|
53
|
+
results = await provider.get_filtered_conversations(
|
|
54
|
+
query=query,
|
|
55
|
+
period=f"{hours}h",
|
|
56
|
+
limit=limit,
|
|
57
|
+
)
|
|
58
|
+
output = format_output(results)
|
|
59
|
+
await ctx.print(f"## Search Results\n\n{output}")
|
|
60
|
+
except Exception as e:
|
|
61
|
+
msg = f"Failed to search history: {e}"
|
|
62
|
+
raise CommandError(msg) from e
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
"""MCP server management commands."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import TYPE_CHECKING, Any, Literal
|
|
6
|
+
|
|
7
|
+
from pydantic import HttpUrl
|
|
8
|
+
from slashed import CommandContext, CommandError # noqa: TC002
|
|
9
|
+
|
|
10
|
+
from agentpool_commands.base import NodeCommand
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
if TYPE_CHECKING:
|
|
14
|
+
from agentpool.agents.context import AgentContext
|
|
15
|
+
from agentpool.messaging import MessageNode
|
|
16
|
+
from agentpool_config.mcp_server import MCPServerConfig
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class AddMCPServerCommand(NodeCommand):
|
|
20
|
+
"""Add a local MCP server via stdio transport.
|
|
21
|
+
|
|
22
|
+
Registers a new MCP server that runs as a subprocess.
|
|
23
|
+
|
|
24
|
+
Options:
|
|
25
|
+
--args "arg1|arg2" Pipe-separated command arguments
|
|
26
|
+
--env "KEY=val|KEY2=val" Pipe-separated environment variables
|
|
27
|
+
|
|
28
|
+
Examples:
|
|
29
|
+
/add-mcp-server myserver npx --args "mcp-server-filesystem|/tmp"
|
|
30
|
+
/add-mcp-server graphql npx --args "mcp-graphql" --env "ENDPOINT=https://api.example.com/graphql"
|
|
31
|
+
"""
|
|
32
|
+
|
|
33
|
+
name = "add-mcp-server"
|
|
34
|
+
category = "integrations"
|
|
35
|
+
|
|
36
|
+
@classmethod
|
|
37
|
+
def supports_node(cls, node: MessageNode[Any, Any]) -> bool:
|
|
38
|
+
from agentpool import Agent
|
|
39
|
+
|
|
40
|
+
return isinstance(node, Agent)
|
|
41
|
+
|
|
42
|
+
async def execute_command(
|
|
43
|
+
self,
|
|
44
|
+
ctx: CommandContext[AgentContext],
|
|
45
|
+
name: str,
|
|
46
|
+
command: str,
|
|
47
|
+
*,
|
|
48
|
+
args: str | None = None,
|
|
49
|
+
env: str | None = None,
|
|
50
|
+
) -> None:
|
|
51
|
+
"""Add a local MCP server.
|
|
52
|
+
|
|
53
|
+
Args:
|
|
54
|
+
ctx: Command context
|
|
55
|
+
name: Unique name for the MCP server
|
|
56
|
+
command: Command to execute for the server
|
|
57
|
+
args: Pipe-separated command arguments
|
|
58
|
+
env: Pipe-separated environment variables (KEY=value format)
|
|
59
|
+
"""
|
|
60
|
+
from agentpool_config.mcp_server import StdioMCPServerConfig
|
|
61
|
+
|
|
62
|
+
try:
|
|
63
|
+
# Parse arguments
|
|
64
|
+
arg_list = [a.strip() for a in args.split("|")] if args else []
|
|
65
|
+
# Parse environment variables
|
|
66
|
+
env_dict: dict[str, str] = {}
|
|
67
|
+
if env:
|
|
68
|
+
for pair in env.split("|"):
|
|
69
|
+
if "=" in pair:
|
|
70
|
+
key, value = pair.split("=", 1)
|
|
71
|
+
env_dict[key.strip()] = value.strip()
|
|
72
|
+
|
|
73
|
+
config = StdioMCPServerConfig(name=name, command=command, args=arg_list, env=env_dict)
|
|
74
|
+
await ctx.context.agent.mcp.setup_server(config, add_to_config=True)
|
|
75
|
+
await ctx.print(f"**Added MCP server** `{name}` with command: `{command}`")
|
|
76
|
+
|
|
77
|
+
except Exception as e:
|
|
78
|
+
msg = f"Failed to add MCP server: {e}"
|
|
79
|
+
raise CommandError(msg) from e
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
class AddRemoteMCPServerCommand(NodeCommand):
|
|
83
|
+
"""Add a remote MCP server via HTTP transport.
|
|
84
|
+
|
|
85
|
+
Connects to an MCP server running on a remote URL.
|
|
86
|
+
|
|
87
|
+
Options:
|
|
88
|
+
--transport sse|streamable-http Transport type (default: streamable-http)
|
|
89
|
+
|
|
90
|
+
Examples:
|
|
91
|
+
/add-remote-mcp-server myapi https://api.example.com/mcp
|
|
92
|
+
/add-remote-mcp-server legacy https://old.api.com/mcp --transport sse
|
|
93
|
+
"""
|
|
94
|
+
|
|
95
|
+
name = "add-remote-mcp-server"
|
|
96
|
+
category = "integrations"
|
|
97
|
+
|
|
98
|
+
@classmethod
|
|
99
|
+
def supports_node(cls, node: MessageNode[Any, Any]) -> bool:
|
|
100
|
+
from agentpool import Agent
|
|
101
|
+
|
|
102
|
+
return isinstance(node, Agent)
|
|
103
|
+
|
|
104
|
+
async def execute_command(
|
|
105
|
+
self,
|
|
106
|
+
ctx: CommandContext[AgentContext],
|
|
107
|
+
name: str,
|
|
108
|
+
url: str,
|
|
109
|
+
*,
|
|
110
|
+
transport: Literal["sse", "streamable-http"] = "streamable-http",
|
|
111
|
+
) -> None:
|
|
112
|
+
"""Add a remote MCP server.
|
|
113
|
+
|
|
114
|
+
Args:
|
|
115
|
+
ctx: Command context
|
|
116
|
+
name: Unique name for the MCP server
|
|
117
|
+
url: Server URL endpoint
|
|
118
|
+
transport: HTTP transport type (sse or streamable-http)
|
|
119
|
+
"""
|
|
120
|
+
from agentpool_config.mcp_server import SSEMCPServerConfig, StreamableHTTPMCPServerConfig
|
|
121
|
+
|
|
122
|
+
try:
|
|
123
|
+
config: MCPServerConfig
|
|
124
|
+
if transport == "sse":
|
|
125
|
+
config = SSEMCPServerConfig(name=name, url=HttpUrl(url))
|
|
126
|
+
else:
|
|
127
|
+
config = StreamableHTTPMCPServerConfig(name=name, url=HttpUrl(url))
|
|
128
|
+
|
|
129
|
+
await ctx.context.agent.mcp.setup_server(config, add_to_config=True)
|
|
130
|
+
await ctx.print(f"**Added remote MCP server** `{name}` at `{url}` using {transport}")
|
|
131
|
+
|
|
132
|
+
except Exception as e:
|
|
133
|
+
msg = f"Failed to add remote MCP server: {e}"
|
|
134
|
+
raise CommandError(msg) from e
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
class ListMCPServersCommand(NodeCommand):
|
|
138
|
+
"""List all configured MCP servers.
|
|
139
|
+
|
|
140
|
+
Shows server name, type, and connection status.
|
|
141
|
+
"""
|
|
142
|
+
|
|
143
|
+
name = "list-mcp-servers"
|
|
144
|
+
category = "integrations"
|
|
145
|
+
|
|
146
|
+
@classmethod
|
|
147
|
+
def supports_node(cls, node: MessageNode[Any, Any]) -> bool:
|
|
148
|
+
from agentpool import Agent
|
|
149
|
+
|
|
150
|
+
return isinstance(node, Agent)
|
|
151
|
+
|
|
152
|
+
async def execute_command(self, ctx: CommandContext[AgentContext]) -> None:
|
|
153
|
+
"""List all MCP servers.
|
|
154
|
+
|
|
155
|
+
Args:
|
|
156
|
+
ctx: Command context
|
|
157
|
+
"""
|
|
158
|
+
from agentpool_commands.markdown_utils import format_table
|
|
159
|
+
|
|
160
|
+
servers = ctx.context.agent.mcp.providers
|
|
161
|
+
if not servers:
|
|
162
|
+
await ctx.print("No MCP servers configured.")
|
|
163
|
+
return
|
|
164
|
+
|
|
165
|
+
rows = []
|
|
166
|
+
for server in servers:
|
|
167
|
+
server_type = type(server.server).__name__.replace("MCPServerConfig", "")
|
|
168
|
+
rows.append({
|
|
169
|
+
"Name": server.name,
|
|
170
|
+
"Type": server_type,
|
|
171
|
+
"Status": "connected" if server.client.connected else "disconnected",
|
|
172
|
+
})
|
|
173
|
+
|
|
174
|
+
headers = ["Name", "Type", "Status"]
|
|
175
|
+
table = format_table(headers, rows)
|
|
176
|
+
await ctx.print(f"## MCP Servers\n\n{table}")
|
agentpool_commands/models.py
CHANGED
|
@@ -16,6 +16,59 @@ if TYPE_CHECKING:
|
|
|
16
16
|
from agentpool.messaging import MessageNode
|
|
17
17
|
|
|
18
18
|
|
|
19
|
+
class ListModelsCommand(NodeCommand):
|
|
20
|
+
"""List available models for the current agent.
|
|
21
|
+
|
|
22
|
+
Displays all models that can be used with the current agent,
|
|
23
|
+
formatted as a markdown table with model ID, name, and description.
|
|
24
|
+
|
|
25
|
+
Examples:
|
|
26
|
+
/list-models
|
|
27
|
+
"""
|
|
28
|
+
|
|
29
|
+
name = "list-models"
|
|
30
|
+
category = "model"
|
|
31
|
+
|
|
32
|
+
async def execute_command(
|
|
33
|
+
self,
|
|
34
|
+
ctx: CommandContext[AgentContext],
|
|
35
|
+
) -> None:
|
|
36
|
+
"""List available models.
|
|
37
|
+
|
|
38
|
+
Args:
|
|
39
|
+
ctx: Command context
|
|
40
|
+
"""
|
|
41
|
+
agent = ctx.context.agent
|
|
42
|
+
models = await agent.get_available_models()
|
|
43
|
+
|
|
44
|
+
if not models:
|
|
45
|
+
await ctx.print("No models available for this agent.")
|
|
46
|
+
return
|
|
47
|
+
|
|
48
|
+
# Build markdown table
|
|
49
|
+
lines = [
|
|
50
|
+
"| Model ID | Name | Description |",
|
|
51
|
+
"|----------|------|-------------|",
|
|
52
|
+
]
|
|
53
|
+
|
|
54
|
+
for model in models:
|
|
55
|
+
model_id = model.id_override if model.id_override else model.id
|
|
56
|
+
name = model.name or ""
|
|
57
|
+
description = model.description or ""
|
|
58
|
+
# Escape pipe characters in fields
|
|
59
|
+
name = name.replace("|", "\\|")
|
|
60
|
+
description = description.replace("|", "\\|")
|
|
61
|
+
lines.append(f"| `{model_id}` | {name} | {description} |")
|
|
62
|
+
|
|
63
|
+
await ctx.print("\n".join(lines))
|
|
64
|
+
|
|
65
|
+
@classmethod
|
|
66
|
+
def supports_node(cls, node: MessageNode[Any, Any]) -> bool:
|
|
67
|
+
from agentpool import ACPAgent, Agent, ClaudeCodeAgent
|
|
68
|
+
|
|
69
|
+
return isinstance(node, Agent | ACPAgent | ClaudeCodeAgent)
|
|
70
|
+
|
|
71
|
+
|
|
19
72
|
class SetModelCommand(NodeCommand):
|
|
20
73
|
"""Change the language model for the current conversation.
|
|
21
74
|
|
|
@@ -46,7 +99,7 @@ class SetModelCommand(NodeCommand):
|
|
|
46
99
|
"""
|
|
47
100
|
try:
|
|
48
101
|
# Create new session with model override
|
|
49
|
-
ctx.context.
|
|
102
|
+
await ctx.context.agent.set_model(model)
|
|
50
103
|
await ctx.print(f"โ
**Model changed to:** `{model}`")
|
|
51
104
|
except Exception as e: # noqa: BLE001
|
|
52
105
|
await ctx.print(f"โ **Failed to change model:** {e}")
|
|
@@ -57,6 +110,6 @@ class SetModelCommand(NodeCommand):
|
|
|
57
110
|
|
|
58
111
|
@classmethod
|
|
59
112
|
def supports_node(cls, node: MessageNode[Any, Any]) -> bool:
|
|
60
|
-
from agentpool import Agent
|
|
113
|
+
from agentpool import ACPAgent, Agent, ClaudeCodeAgent
|
|
61
114
|
|
|
62
|
-
return isinstance(node, Agent)
|
|
115
|
+
return isinstance(node, Agent | ACPAgent | ClaudeCodeAgent)
|
|
@@ -0,0 +1,260 @@
|
|
|
1
|
+
"""Pool-level commands for managing agent pools and configurations."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import TYPE_CHECKING, Any
|
|
6
|
+
|
|
7
|
+
from agentpool.agents.base_agent import BaseAgent
|
|
8
|
+
from agentpool_commands.base import NodeCommand
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
if TYPE_CHECKING:
|
|
12
|
+
from slashed import CommandContext
|
|
13
|
+
|
|
14
|
+
from agentpool.messaging.context import NodeContext
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class ListPoolsCommand(NodeCommand):
|
|
18
|
+
"""List available agent pool configurations.
|
|
19
|
+
|
|
20
|
+
Examples:
|
|
21
|
+
/list-pools
|
|
22
|
+
"""
|
|
23
|
+
|
|
24
|
+
name = "list-pools"
|
|
25
|
+
category = "pool"
|
|
26
|
+
|
|
27
|
+
async def execute_command(self, ctx: CommandContext[NodeContext[Any]]) -> None:
|
|
28
|
+
"""List available pool configurations.
|
|
29
|
+
|
|
30
|
+
Args:
|
|
31
|
+
ctx: Command context with node context
|
|
32
|
+
"""
|
|
33
|
+
from agentpool_cli import agent_store
|
|
34
|
+
|
|
35
|
+
try:
|
|
36
|
+
output_lines = ["## ๐ Agent Pool Configurations\n"]
|
|
37
|
+
|
|
38
|
+
# Show current pool info
|
|
39
|
+
output_lines.append("### ๐ Current Pool")
|
|
40
|
+
|
|
41
|
+
# Get config path from context config (works for all agent types)
|
|
42
|
+
current_config = ctx.context.config.config_file_path if ctx.context.config else None
|
|
43
|
+
if current_config:
|
|
44
|
+
output_lines.append(f"**Config:** `{current_config}`")
|
|
45
|
+
else:
|
|
46
|
+
output_lines.append("**Config:** *(default/built-in)*")
|
|
47
|
+
|
|
48
|
+
# Show agents in current pool
|
|
49
|
+
pool = ctx.context.pool
|
|
50
|
+
if pool:
|
|
51
|
+
agent_names = list(pool.all_agents.keys())
|
|
52
|
+
output_lines.append(f"**Agents:** {', '.join(f'`{n}`' for n in agent_names)}")
|
|
53
|
+
output_lines.append(f"**Active agent:** `{ctx.context.node.name}`")
|
|
54
|
+
else:
|
|
55
|
+
output_lines.append("**No pool available**")
|
|
56
|
+
output_lines.append("")
|
|
57
|
+
|
|
58
|
+
# Show stored configurations
|
|
59
|
+
output_lines.append("### ๐พ Stored Configurations")
|
|
60
|
+
stored_configs = agent_store.list_configs()
|
|
61
|
+
active_config = agent_store.get_active()
|
|
62
|
+
|
|
63
|
+
if not stored_configs:
|
|
64
|
+
output_lines.append("*No stored configurations*")
|
|
65
|
+
output_lines.append("")
|
|
66
|
+
output_lines.append("Use `agentpool add <name> <path>` to add configurations.")
|
|
67
|
+
else:
|
|
68
|
+
# Build markdown table
|
|
69
|
+
output_lines.append("| Name | Path |")
|
|
70
|
+
output_lines.append("|------|------|")
|
|
71
|
+
for name, path in stored_configs:
|
|
72
|
+
is_active = active_config and active_config.name == name
|
|
73
|
+
is_current = current_config and path == current_config
|
|
74
|
+
markers = []
|
|
75
|
+
if is_active:
|
|
76
|
+
markers.append("default")
|
|
77
|
+
if is_current:
|
|
78
|
+
markers.append("current")
|
|
79
|
+
name_col = f"{name} ({', '.join(markers)})" if markers else name
|
|
80
|
+
output_lines.append(f"| {name_col} | `{path}` |")
|
|
81
|
+
|
|
82
|
+
output_lines.append("")
|
|
83
|
+
output_lines.append("*Use `/set-pool <name>` or `/set-pool <path>` to switch pools.*")
|
|
84
|
+
|
|
85
|
+
await ctx.output.print("\n".join(output_lines))
|
|
86
|
+
|
|
87
|
+
except Exception as e: # noqa: BLE001
|
|
88
|
+
await ctx.output.print(f"โ **Error listing pools:** {e}")
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
class CompactCommand(NodeCommand):
|
|
92
|
+
"""Compact the conversation history to reduce context size.
|
|
93
|
+
|
|
94
|
+
Uses the configured compaction pipeline from the agent pool manifest,
|
|
95
|
+
or falls back to a default summarizing pipeline.
|
|
96
|
+
|
|
97
|
+
Options:
|
|
98
|
+
--preset <name> Use a specific preset (minimal, balanced, summarizing)
|
|
99
|
+
|
|
100
|
+
Examples:
|
|
101
|
+
/compact
|
|
102
|
+
/compact --preset=minimal
|
|
103
|
+
"""
|
|
104
|
+
|
|
105
|
+
name = "compact"
|
|
106
|
+
category = "pool"
|
|
107
|
+
|
|
108
|
+
async def execute_command(
|
|
109
|
+
self,
|
|
110
|
+
ctx: CommandContext[NodeContext[Any]],
|
|
111
|
+
*,
|
|
112
|
+
preset: str | None = None,
|
|
113
|
+
) -> None:
|
|
114
|
+
"""Compact the conversation history.
|
|
115
|
+
|
|
116
|
+
Args:
|
|
117
|
+
ctx: Command context with node context
|
|
118
|
+
preset: Optional preset name (minimal, balanced, summarizing)
|
|
119
|
+
"""
|
|
120
|
+
from agentpool.agents.base_agent import BaseAgent
|
|
121
|
+
|
|
122
|
+
# Get agent from context
|
|
123
|
+
agent = ctx.context.node
|
|
124
|
+
if not isinstance(agent, BaseAgent):
|
|
125
|
+
await ctx.output.print(
|
|
126
|
+
"โ **This command requires an agent with conversation history**"
|
|
127
|
+
)
|
|
128
|
+
return
|
|
129
|
+
|
|
130
|
+
# Check if there's any history to compact
|
|
131
|
+
if not agent.conversation.get_history():
|
|
132
|
+
await ctx.output.print("๐ญ **No message history to compact**")
|
|
133
|
+
return
|
|
134
|
+
|
|
135
|
+
try:
|
|
136
|
+
# Get compaction pipeline
|
|
137
|
+
from agentpool.messaging.compaction import (
|
|
138
|
+
balanced_context,
|
|
139
|
+
minimal_context,
|
|
140
|
+
summarizing_context,
|
|
141
|
+
)
|
|
142
|
+
|
|
143
|
+
pipeline = None
|
|
144
|
+
|
|
145
|
+
# Check for preset override
|
|
146
|
+
if preset:
|
|
147
|
+
match preset.lower():
|
|
148
|
+
case "minimal":
|
|
149
|
+
pipeline = minimal_context()
|
|
150
|
+
case "balanced":
|
|
151
|
+
pipeline = balanced_context()
|
|
152
|
+
case "summarizing":
|
|
153
|
+
pipeline = summarizing_context()
|
|
154
|
+
case _:
|
|
155
|
+
await ctx.output.print(
|
|
156
|
+
f"โ ๏ธ **Unknown preset:** `{preset}`\n"
|
|
157
|
+
"Available: minimal, balanced, summarizing"
|
|
158
|
+
)
|
|
159
|
+
return
|
|
160
|
+
|
|
161
|
+
# Fall back to pool's configured pipeline
|
|
162
|
+
if pipeline is None and ctx.context.pool:
|
|
163
|
+
pipeline = ctx.context.pool.compaction_pipeline
|
|
164
|
+
|
|
165
|
+
# Fall back to default summarizing pipeline
|
|
166
|
+
if pipeline is None:
|
|
167
|
+
pipeline = summarizing_context()
|
|
168
|
+
|
|
169
|
+
await ctx.output.print("๐ **Compacting conversation history...**")
|
|
170
|
+
|
|
171
|
+
# Apply the pipeline using shared helper
|
|
172
|
+
from agentpool.messaging.compaction import compact_conversation
|
|
173
|
+
|
|
174
|
+
original_count, compacted_count = await compact_conversation(
|
|
175
|
+
pipeline, agent.conversation
|
|
176
|
+
)
|
|
177
|
+
reduction = original_count - compacted_count
|
|
178
|
+
|
|
179
|
+
await ctx.output.print(
|
|
180
|
+
f"โ
**Compaction complete**\n"
|
|
181
|
+
f"- Messages: {original_count} โ {compacted_count} ({reduction} removed)\n"
|
|
182
|
+
f"- Reduction: {reduction / original_count * 100:.1f}%"
|
|
183
|
+
if original_count > 0
|
|
184
|
+
else "โ
**Compaction complete** (no messages)"
|
|
185
|
+
)
|
|
186
|
+
|
|
187
|
+
except Exception as e: # noqa: BLE001
|
|
188
|
+
await ctx.output.print(f"โ **Error compacting history:** {e}")
|
|
189
|
+
|
|
190
|
+
|
|
191
|
+
class SpawnCommand(NodeCommand):
|
|
192
|
+
"""Spawn a subagent to execute a specific task.
|
|
193
|
+
|
|
194
|
+
The subagent runs and its progress is streamed back through the event system.
|
|
195
|
+
How the progress is displayed depends on the protocol (tool box in ACP, inline, etc.).
|
|
196
|
+
|
|
197
|
+
Examples:
|
|
198
|
+
/spawn agent-name "task description"
|
|
199
|
+
/spawn code-reviewer "Review main.py for bugs"
|
|
200
|
+
"""
|
|
201
|
+
|
|
202
|
+
name = "spawn"
|
|
203
|
+
category = "pool"
|
|
204
|
+
|
|
205
|
+
@classmethod
|
|
206
|
+
def supports_node(cls, node: Any) -> bool:
|
|
207
|
+
"""Only available when running from an agent (needs events)."""
|
|
208
|
+
return isinstance(node, BaseAgent)
|
|
209
|
+
|
|
210
|
+
async def execute_command(
|
|
211
|
+
self,
|
|
212
|
+
ctx: CommandContext[NodeContext[Any]],
|
|
213
|
+
agent_name: str,
|
|
214
|
+
task_prompt: str,
|
|
215
|
+
) -> None:
|
|
216
|
+
"""Spawn a subagent to execute a task.
|
|
217
|
+
|
|
218
|
+
Args:
|
|
219
|
+
ctx: Command context with node context
|
|
220
|
+
agent_name: Name of the agent to spawn
|
|
221
|
+
task_prompt: Task prompt for the subagent
|
|
222
|
+
"""
|
|
223
|
+
from agentpool.agents.events import SubAgentEvent
|
|
224
|
+
|
|
225
|
+
pool = ctx.context.pool
|
|
226
|
+
if not pool:
|
|
227
|
+
await ctx.output.print("โ **No agent pool available**")
|
|
228
|
+
return
|
|
229
|
+
|
|
230
|
+
if agent_name not in pool.nodes:
|
|
231
|
+
available = list(pool.nodes.keys())
|
|
232
|
+
await ctx.output.print(
|
|
233
|
+
f"โ **Agent** `{agent_name}` **not found**\n\n"
|
|
234
|
+
f"Available agents: {', '.join(available)}"
|
|
235
|
+
)
|
|
236
|
+
return
|
|
237
|
+
|
|
238
|
+
from agentpool.common_types import SupportsRunStream
|
|
239
|
+
|
|
240
|
+
agent = pool.nodes[agent_name]
|
|
241
|
+
|
|
242
|
+
# Check if node supports streaming
|
|
243
|
+
if not isinstance(agent, SupportsRunStream):
|
|
244
|
+
await ctx.output.print(f"โ **Agent** `{agent_name}` **does not support streaming**")
|
|
245
|
+
return
|
|
246
|
+
|
|
247
|
+
# Stream subagent execution by wrapping events in SubAgentEvent
|
|
248
|
+
# The event handler system (ACP, OpenCode, CLI, etc.) handles rendering
|
|
249
|
+
# Get parent agent's context to access event emitter
|
|
250
|
+
parent_ctx = ctx.context.agent.get_context()
|
|
251
|
+
|
|
252
|
+
async for event in agent.run_stream(task_prompt):
|
|
253
|
+
wrapped = SubAgentEvent(
|
|
254
|
+
source_name=agent_name,
|
|
255
|
+
source_type="agent",
|
|
256
|
+
event=event,
|
|
257
|
+
depth=1,
|
|
258
|
+
)
|
|
259
|
+
# Emit to parent agent's event stream
|
|
260
|
+
await parent_ctx.events.emit_event(wrapped)
|