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_config/resources.py
DELETED
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
"""Models for resource information."""
|
|
2
|
-
|
|
3
|
-
from __future__ import annotations
|
|
4
|
-
|
|
5
|
-
from dataclasses import dataclass
|
|
6
|
-
from typing import TYPE_CHECKING, Self
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
if TYPE_CHECKING:
|
|
10
|
-
from mcp.types import Resource as MCPResource
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
@dataclass
|
|
14
|
-
class ResourceInfo:
|
|
15
|
-
"""Information about an available resource.
|
|
16
|
-
|
|
17
|
-
This class provides essential information about a resource that can be loaded.
|
|
18
|
-
Use the resource name with load_resource() to access the actual content.
|
|
19
|
-
"""
|
|
20
|
-
|
|
21
|
-
name: str
|
|
22
|
-
"""Name of the resource, use this with load_resource()"""
|
|
23
|
-
|
|
24
|
-
uri: str
|
|
25
|
-
"""URI identifying the resource location"""
|
|
26
|
-
|
|
27
|
-
description: str | None = None
|
|
28
|
-
"""Optional description of the resource's content or purpose"""
|
|
29
|
-
|
|
30
|
-
@classmethod
|
|
31
|
-
async def from_mcp_resource(cls, resource: MCPResource) -> Self:
|
|
32
|
-
"""Create ResourceInfo from MCP resource."""
|
|
33
|
-
return cls(name=resource.name, uri=str(resource.uri), description=resource.description)
|
|
@@ -1,43 +0,0 @@
|
|
|
1
|
-
"""ACP resource providers."""
|
|
2
|
-
|
|
3
|
-
from __future__ import annotations
|
|
4
|
-
|
|
5
|
-
from typing import TYPE_CHECKING
|
|
6
|
-
|
|
7
|
-
from exxec.acp_provider import ACPExecutionEnvironment
|
|
8
|
-
|
|
9
|
-
from agentpool.resource_providers import PlanProvider
|
|
10
|
-
from agentpool_toolsets.builtin import CodeTools, ExecutionEnvironmentTools
|
|
11
|
-
from agentpool_toolsets.fsspec_toolset import FSSpecTools
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
if TYPE_CHECKING:
|
|
15
|
-
from agentpool.resource_providers.aggregating import AggregatingResourceProvider
|
|
16
|
-
from agentpool_server.acp_server.session import ACPSession
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
def get_acp_provider(session: ACPSession) -> AggregatingResourceProvider:
|
|
20
|
-
"""Create aggregated resource provider with ACP-specific toolsets.
|
|
21
|
-
|
|
22
|
-
Args:
|
|
23
|
-
session: The ACP session to create providers for
|
|
24
|
-
|
|
25
|
-
Returns:
|
|
26
|
-
AggregatingResourceProvider with execution, filesystem, and code tools
|
|
27
|
-
"""
|
|
28
|
-
from agentpool.resource_providers.aggregating import AggregatingResourceProvider
|
|
29
|
-
|
|
30
|
-
execution_env = ACPExecutionEnvironment(
|
|
31
|
-
fs=session.fs, requests=session.requests, cwd=session.cwd
|
|
32
|
-
)
|
|
33
|
-
|
|
34
|
-
providers = [
|
|
35
|
-
PlanProvider(),
|
|
36
|
-
ExecutionEnvironmentTools(env=execution_env, name=f"acp_execution_{session.session_id}"),
|
|
37
|
-
FSSpecTools(execution_env, name=f"acp_fs_{session.session_id}", cwd=session.cwd),
|
|
38
|
-
CodeTools(execution_env, name=f"acp_code_{session.session_id}", cwd=session.cwd),
|
|
39
|
-
]
|
|
40
|
-
return AggregatingResourceProvider(providers=providers, name=f"acp_{session.session_id}")
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
__all__ = ["get_acp_provider"]
|
|
@@ -1,210 +0,0 @@
|
|
|
1
|
-
"""Spawn subagent slash command."""
|
|
2
|
-
|
|
3
|
-
from __future__ import annotations
|
|
4
|
-
|
|
5
|
-
from typing import TYPE_CHECKING, Any
|
|
6
|
-
import uuid
|
|
7
|
-
|
|
8
|
-
from pydantic_ai import (
|
|
9
|
-
FinalResultEvent,
|
|
10
|
-
FunctionToolCallEvent,
|
|
11
|
-
FunctionToolResultEvent,
|
|
12
|
-
PartDeltaEvent,
|
|
13
|
-
PartStartEvent,
|
|
14
|
-
RetryPromptPart,
|
|
15
|
-
TextPart,
|
|
16
|
-
TextPartDelta,
|
|
17
|
-
ThinkingPart,
|
|
18
|
-
ThinkingPartDelta,
|
|
19
|
-
ToolCallPartDelta,
|
|
20
|
-
ToolReturnPart,
|
|
21
|
-
)
|
|
22
|
-
from slashed import CommandContext, CommandError # noqa: TC002
|
|
23
|
-
|
|
24
|
-
from agentpool.agents.events import StreamCompleteEvent, ToolCallProgressEvent
|
|
25
|
-
from agentpool.log import get_logger
|
|
26
|
-
from agentpool.messaging.context import NodeContext # noqa: TC001
|
|
27
|
-
from agentpool_commands.base import NodeCommand
|
|
28
|
-
from agentpool_server.acp_server.session import ACPSession # noqa: TC001
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
if TYPE_CHECKING:
|
|
32
|
-
from agentpool.agents.events import RichAgentStreamEvent
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
logger = get_logger(__name__)
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
class SpawnSubagentCommand(NodeCommand):
|
|
39
|
-
"""Spawn a subagent to execute a specific task.
|
|
40
|
-
|
|
41
|
-
The subagent runs concurrently and reports progress in a dedicated tool call box.
|
|
42
|
-
|
|
43
|
-
Usage:
|
|
44
|
-
/spawn "agent-name" "prompt for the subagent"
|
|
45
|
-
/spawn "code-reviewer" "Review the main.py file for potential bugs"
|
|
46
|
-
"""
|
|
47
|
-
|
|
48
|
-
name = "spawn"
|
|
49
|
-
category = "agents"
|
|
50
|
-
|
|
51
|
-
async def execute_command(
|
|
52
|
-
self,
|
|
53
|
-
ctx: CommandContext[NodeContext[ACPSession]],
|
|
54
|
-
agent_name: str,
|
|
55
|
-
task_prompt: str,
|
|
56
|
-
) -> None:
|
|
57
|
-
"""Spawn a subagent to execute a task.
|
|
58
|
-
|
|
59
|
-
Args:
|
|
60
|
-
ctx: Command context with ACP session
|
|
61
|
-
agent_name: Name of the agent to spawn
|
|
62
|
-
task_prompt: Task prompt for the subagent
|
|
63
|
-
"""
|
|
64
|
-
session = ctx.context.data
|
|
65
|
-
assert session, "ACP session required for spawn command"
|
|
66
|
-
# Generate unique tool call ID
|
|
67
|
-
tool_call_id = f"spawn-{agent_name}-{uuid.uuid4().hex[:8]}"
|
|
68
|
-
try:
|
|
69
|
-
# Check if agent exists in pool
|
|
70
|
-
if not session.agent_pool or agent_name not in session.agent_pool.agents:
|
|
71
|
-
available = list(session.agent_pool.agents.keys())
|
|
72
|
-
error_msg = f"Agent {agent_name!r} not found. Available agents: {available}"
|
|
73
|
-
await ctx.print(f"❌ {error_msg}")
|
|
74
|
-
return
|
|
75
|
-
|
|
76
|
-
target_agent = session.agent_pool.get_agent(agent_name)
|
|
77
|
-
await session.notifications.tool_call_start(
|
|
78
|
-
tool_call_id=tool_call_id,
|
|
79
|
-
title=f"Spawning agent: {agent_name}",
|
|
80
|
-
kind="execute",
|
|
81
|
-
raw_input={
|
|
82
|
-
"agent_name": agent_name,
|
|
83
|
-
"task_prompt": task_prompt,
|
|
84
|
-
},
|
|
85
|
-
)
|
|
86
|
-
|
|
87
|
-
aggregated_content: list[str] = [] # Aggregate output as we stream
|
|
88
|
-
try:
|
|
89
|
-
# Run the subagent and handle events
|
|
90
|
-
async for event in target_agent.run_stream(task_prompt):
|
|
91
|
-
await _handle_subagent_event(event, tool_call_id, aggregated_content, session)
|
|
92
|
-
|
|
93
|
-
final_content = "".join(aggregated_content).strip()
|
|
94
|
-
await session.notifications.tool_call_progress(
|
|
95
|
-
tool_call_id=tool_call_id,
|
|
96
|
-
status="completed",
|
|
97
|
-
content=[final_content] if final_content else None,
|
|
98
|
-
)
|
|
99
|
-
except Exception as e:
|
|
100
|
-
error_msg = f"Subagent execution failed: {e}"
|
|
101
|
-
logger.exception("Subagent execution error", error=str(e))
|
|
102
|
-
await session.notifications.tool_call_progress(
|
|
103
|
-
tool_call_id=tool_call_id,
|
|
104
|
-
status="failed",
|
|
105
|
-
raw_output=error_msg,
|
|
106
|
-
)
|
|
107
|
-
|
|
108
|
-
except Exception as e:
|
|
109
|
-
error_msg = f"Failed to spawn agent '{agent_name}': {e}"
|
|
110
|
-
logger.exception("Spawn command error", error=str(e))
|
|
111
|
-
raise CommandError(error_msg) from e
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
async def _handle_subagent_event(
|
|
115
|
-
event: RichAgentStreamEvent[Any],
|
|
116
|
-
tool_call_id: str,
|
|
117
|
-
aggregated_content: list[str],
|
|
118
|
-
session: ACPSession,
|
|
119
|
-
) -> None:
|
|
120
|
-
"""Handle events from spawned subagent and convert to tool_call_progress.
|
|
121
|
-
|
|
122
|
-
Args:
|
|
123
|
-
event: Event from the subagent stream
|
|
124
|
-
tool_call_id: ID of the tool call box
|
|
125
|
-
aggregated_content: List to accumulate content for final display
|
|
126
|
-
session: ACP session for notifications
|
|
127
|
-
"""
|
|
128
|
-
match event:
|
|
129
|
-
case (
|
|
130
|
-
PartStartEvent(part=TextPart(content=delta))
|
|
131
|
-
| PartDeltaEvent(delta=TextPartDelta(content_delta=delta))
|
|
132
|
-
):
|
|
133
|
-
# Subagent text output → accumulate and update progress
|
|
134
|
-
aggregated_content.append(delta)
|
|
135
|
-
await session.notifications.tool_call_progress(
|
|
136
|
-
tool_call_id=tool_call_id,
|
|
137
|
-
status="in_progress",
|
|
138
|
-
content=["".join(aggregated_content)],
|
|
139
|
-
)
|
|
140
|
-
|
|
141
|
-
case (
|
|
142
|
-
PartStartEvent(part=ThinkingPart(content=delta))
|
|
143
|
-
| PartDeltaEvent(delta=ThinkingPartDelta(content_delta=delta))
|
|
144
|
-
):
|
|
145
|
-
# Subagent thinking → show thinking indicator
|
|
146
|
-
if delta:
|
|
147
|
-
thinking_text = f"💭 {delta}"
|
|
148
|
-
aggregated_content.append(thinking_text)
|
|
149
|
-
await session.notifications.tool_call_progress(
|
|
150
|
-
tool_call_id=tool_call_id,
|
|
151
|
-
status="in_progress",
|
|
152
|
-
content=["".join(aggregated_content)],
|
|
153
|
-
)
|
|
154
|
-
|
|
155
|
-
case FunctionToolCallEvent(part=part):
|
|
156
|
-
# Subagent calls a tool → show nested tool call
|
|
157
|
-
tool_text = f"\n🔧 Using tool: {part.tool_name}\n"
|
|
158
|
-
aggregated_content.append(tool_text)
|
|
159
|
-
await session.notifications.tool_call_progress(
|
|
160
|
-
tool_call_id=tool_call_id,
|
|
161
|
-
status="in_progress",
|
|
162
|
-
content=["".join(aggregated_content)],
|
|
163
|
-
)
|
|
164
|
-
|
|
165
|
-
case FunctionToolResultEvent(
|
|
166
|
-
result=ToolReturnPart(content=content, tool_name=tool_name),
|
|
167
|
-
):
|
|
168
|
-
# Subagent tool completes → show tool result
|
|
169
|
-
result_text = f"✅ {tool_name}: {content}\n"
|
|
170
|
-
aggregated_content.append(result_text)
|
|
171
|
-
await session.notifications.tool_call_progress(
|
|
172
|
-
tool_call_id=tool_call_id,
|
|
173
|
-
status="in_progress",
|
|
174
|
-
content=["".join(aggregated_content)],
|
|
175
|
-
)
|
|
176
|
-
|
|
177
|
-
case FunctionToolResultEvent(
|
|
178
|
-
result=RetryPromptPart(tool_name=tool_name) as result,
|
|
179
|
-
):
|
|
180
|
-
# Tool call failed and needs retry
|
|
181
|
-
error_message = result.model_response()
|
|
182
|
-
error_text = f"❌ {tool_name or 'unknown'}: Error: {error_message}\n"
|
|
183
|
-
aggregated_content.append(error_text)
|
|
184
|
-
await session.notifications.tool_call_progress(
|
|
185
|
-
tool_call_id=tool_call_id,
|
|
186
|
-
status="in_progress",
|
|
187
|
-
content=["".join(aggregated_content)],
|
|
188
|
-
)
|
|
189
|
-
|
|
190
|
-
case ToolCallProgressEvent(message=message, tool_name=tool_name):
|
|
191
|
-
# Progress event from tools
|
|
192
|
-
if message:
|
|
193
|
-
progress_text = f"🔄 {tool_name}: {message}\n"
|
|
194
|
-
aggregated_content.append(progress_text)
|
|
195
|
-
await session.notifications.tool_call_progress(
|
|
196
|
-
tool_call_id=tool_call_id,
|
|
197
|
-
status="in_progress",
|
|
198
|
-
content=["".join(aggregated_content)],
|
|
199
|
-
)
|
|
200
|
-
|
|
201
|
-
case (
|
|
202
|
-
PartStartEvent()
|
|
203
|
-
| PartDeltaEvent(delta=ToolCallPartDelta())
|
|
204
|
-
| FinalResultEvent()
|
|
205
|
-
| StreamCompleteEvent()
|
|
206
|
-
):
|
|
207
|
-
pass # These events don't need special handling
|
|
208
|
-
|
|
209
|
-
case _:
|
|
210
|
-
logger.debug("Unhandled subagent event", event_type=type(event).__name__)
|
|
@@ -1,275 +0,0 @@
|
|
|
1
|
-
"""Text-based storage provider with dynamic paths."""
|
|
2
|
-
|
|
3
|
-
from __future__ import annotations
|
|
4
|
-
|
|
5
|
-
from typing import TYPE_CHECKING, Any, ClassVar
|
|
6
|
-
|
|
7
|
-
from upathtools import to_upath
|
|
8
|
-
|
|
9
|
-
from agentpool.log import get_logger
|
|
10
|
-
from agentpool.utils.now import get_now
|
|
11
|
-
from agentpool_storage.base import StorageProvider
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
if TYPE_CHECKING:
|
|
15
|
-
from datetime import datetime
|
|
16
|
-
|
|
17
|
-
from jinja2 import Template
|
|
18
|
-
from upathtools import JoinablePathLike, UPath
|
|
19
|
-
|
|
20
|
-
from agentpool.common_types import JsonValue
|
|
21
|
-
from agentpool_config.storage import LogFormat, TextLogConfig
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
logger = get_logger(__name__)
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
CONVERSATIONS_TEMPLATE = """\
|
|
28
|
-
=== AgentPool Log ===
|
|
29
|
-
|
|
30
|
-
{%- for conv_id, conv in conversations.items() %}
|
|
31
|
-
=== Conversation {{ conv_id }} (agent: {{ conv.agent_name }}, started: {{ conv.start_time.strftime('%Y-%m-%d %H:%M:%S') }}) ===
|
|
32
|
-
|
|
33
|
-
{%- for msg in messages if msg.conversation_id == conv_id %}
|
|
34
|
-
[{{ msg.timestamp.strftime('%Y-%m-%d %H:%M:%S') }}] {{ msg.sender }}{% if msg.model %} ({{ msg.model }}){% endif %}: {{ msg.content }}
|
|
35
|
-
{%- if msg.cost_info %}
|
|
36
|
-
Tokens: {{ msg.cost_info.token_usage.total }} (prompt: {{ msg.cost_info.token_usage.prompt }}, completion: {{ msg.cost_info.token_usage.completion }})
|
|
37
|
-
Cost: ${{ "%.4f"|format(msg.cost_info.total_cost) }}
|
|
38
|
-
{%- endif %}
|
|
39
|
-
{%- if msg.response_time %}
|
|
40
|
-
Response time: {{ "%.1f"|format(msg.response_time) }}s
|
|
41
|
-
{%- endif %}
|
|
42
|
-
{%- if msg.forwarded_from %}
|
|
43
|
-
Forwarded via: {{ msg.forwarded_from|join(' -> ') }}
|
|
44
|
-
{%- endif %}
|
|
45
|
-
|
|
46
|
-
{%- for tool in tool_calls if tool.message_id == msg.id %}
|
|
47
|
-
Tool Call: {{ tool.tool_name }}
|
|
48
|
-
Args: {{ tool.args|pprint }}
|
|
49
|
-
Result: {{ tool.result }}
|
|
50
|
-
{%- endfor %}
|
|
51
|
-
{%- endfor %}
|
|
52
|
-
{%- endfor %}
|
|
53
|
-
|
|
54
|
-
=== Commands ===
|
|
55
|
-
{%- for cmd in commands %}
|
|
56
|
-
[{{ cmd.timestamp.strftime('%Y-%m-%d %H:%M:%S') }}] {{ cmd.agent_name }} ({{ cmd.session_id }}): {{ cmd.command }}
|
|
57
|
-
{%- endfor %}
|
|
58
|
-
""" # noqa: E501
|
|
59
|
-
|
|
60
|
-
CHRONOLOGICAL_TEMPLATE = """\
|
|
61
|
-
=== AgentPool Log ===
|
|
62
|
-
|
|
63
|
-
{%- for entry in entries|sort(attribute="timestamp") %}
|
|
64
|
-
{%- if entry.type == "conversation_start" %}
|
|
65
|
-
=== Conversation {{ entry.conversation_id }} (agent: {{ entry.agent_name }}) started ===
|
|
66
|
-
|
|
67
|
-
{%- elif entry.type == "message" %}
|
|
68
|
-
[{{ entry.timestamp.strftime('%Y-%m-%d %H:%M:%S') }}] {{ entry.sender }}{% if entry.model %} ({{ entry.model }}){% endif %}: {{ entry.content }}
|
|
69
|
-
{%- if entry.cost_info %}
|
|
70
|
-
Tokens: {{ entry.cost_info.token_usage.total }} (prompt: {{ entry.cost_info.token_usage.prompt }}, completion: {{ entry.cost_info.token_usage.completion }})
|
|
71
|
-
Cost: ${{ "%.4f"|format(entry.cost_info.total_cost) }}
|
|
72
|
-
{%- endif %}
|
|
73
|
-
{%- if entry.response_time %}
|
|
74
|
-
Response time: {{ "%.1f"|format(entry.response_time) }}s
|
|
75
|
-
{%- endif %}
|
|
76
|
-
{%- if entry.forwarded_from %}
|
|
77
|
-
Forwarded via: {{ entry.forwarded_from|join(' -> ') }}
|
|
78
|
-
{%- endif %}
|
|
79
|
-
|
|
80
|
-
{%- elif entry.type == "tool_call" %}
|
|
81
|
-
[{{ entry.timestamp.strftime('%Y-%m-%d %H:%M:%S') }}] Tool Call: {{ entry.tool_name }}
|
|
82
|
-
Args: {{ entry.args|pprint }}
|
|
83
|
-
Result: {{ entry.result }}
|
|
84
|
-
|
|
85
|
-
{%- elif entry.type == "command" %}
|
|
86
|
-
[{{ entry.timestamp.strftime('%Y-%m-%d %H:%M:%S') }}] Command by {{ entry.agent_name }}: {{ entry.command }}
|
|
87
|
-
|
|
88
|
-
{%- endif %}
|
|
89
|
-
{%- endfor %}
|
|
90
|
-
""" # noqa: E501
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
class TextLogProvider(StorageProvider):
|
|
94
|
-
"""Human-readable text log provider with dynamic paths.
|
|
95
|
-
|
|
96
|
-
Available template variables:
|
|
97
|
-
- now: datetime - Current timestamp
|
|
98
|
-
- date: date - Current date
|
|
99
|
-
- operation: str - Type of operation (message/conversation/tool_call/command)
|
|
100
|
-
- conversation_id: str - ID of current conversation
|
|
101
|
-
- agent_name: str - Name of the agent
|
|
102
|
-
- content: str - Message content
|
|
103
|
-
- role: str - Message role
|
|
104
|
-
- model: str - Model name
|
|
105
|
-
- session_id: str - Session ID
|
|
106
|
-
- tool_name: str - Name of tool being called
|
|
107
|
-
- command: str - Command being executed
|
|
108
|
-
|
|
109
|
-
All variables default to empty string if not available for current operation.
|
|
110
|
-
"""
|
|
111
|
-
|
|
112
|
-
TEMPLATES: ClassVar[dict[LogFormat, str]] = {
|
|
113
|
-
"chronological": CHRONOLOGICAL_TEMPLATE,
|
|
114
|
-
"conversations": CONVERSATIONS_TEMPLATE,
|
|
115
|
-
}
|
|
116
|
-
can_load_history = False
|
|
117
|
-
|
|
118
|
-
def __init__(self, config: TextLogConfig) -> None:
|
|
119
|
-
"""Initialize text log provider."""
|
|
120
|
-
from jinja2 import Environment, Undefined
|
|
121
|
-
|
|
122
|
-
class EmptyStringUndefined(Undefined):
|
|
123
|
-
"""Return empty string for undefined variables."""
|
|
124
|
-
|
|
125
|
-
def __str__(self) -> str:
|
|
126
|
-
return ""
|
|
127
|
-
|
|
128
|
-
super().__init__(config)
|
|
129
|
-
self.encoding = config.encoding
|
|
130
|
-
self.content_template = self._load_template(config.template)
|
|
131
|
-
# Configure Jinja env with empty string for undefined
|
|
132
|
-
env = Environment(undefined=EmptyStringUndefined, enable_async=True)
|
|
133
|
-
self.path_template = env.from_string(config.path)
|
|
134
|
-
|
|
135
|
-
self._entries: list[dict[str, Any]] = []
|
|
136
|
-
|
|
137
|
-
def _load_template(
|
|
138
|
-
self,
|
|
139
|
-
template: LogFormat | JoinablePathLike | None,
|
|
140
|
-
) -> Template:
|
|
141
|
-
"""Load template from predefined or file."""
|
|
142
|
-
from jinja2 import Template
|
|
143
|
-
|
|
144
|
-
if template is None:
|
|
145
|
-
template_str = self.TEMPLATES["chronological"]
|
|
146
|
-
elif template in self.TEMPLATES:
|
|
147
|
-
template_str = self.TEMPLATES[template] # type: ignore
|
|
148
|
-
else: # Assume it's a path
|
|
149
|
-
template_str = to_upath(template).read_text()
|
|
150
|
-
return Template(template_str)
|
|
151
|
-
|
|
152
|
-
def _get_base_context(self, operation: str) -> dict[str, Any]:
|
|
153
|
-
"""Get base context with defaults.
|
|
154
|
-
|
|
155
|
-
Args:
|
|
156
|
-
operation: Type of operation being logged
|
|
157
|
-
|
|
158
|
-
Returns:
|
|
159
|
-
Base context dict with defaults
|
|
160
|
-
"""
|
|
161
|
-
# All other variables will default to empty string via EmptyStringUndefined
|
|
162
|
-
return {"now": get_now(), "date": get_now().date(), "operation": operation}
|
|
163
|
-
|
|
164
|
-
async def _get_path(self, operation: str, **context: Any) -> UPath:
|
|
165
|
-
"""Render path template with context.
|
|
166
|
-
|
|
167
|
-
Args:
|
|
168
|
-
operation: Type of operation being logged
|
|
169
|
-
**context: Additional context variables
|
|
170
|
-
|
|
171
|
-
Returns:
|
|
172
|
-
Concrete path for current operation
|
|
173
|
-
"""
|
|
174
|
-
# Combine base context with provided values
|
|
175
|
-
path_context = self._get_base_context(operation)
|
|
176
|
-
path_context.update(context)
|
|
177
|
-
|
|
178
|
-
path = await self.path_template.render_async(**path_context)
|
|
179
|
-
resolved_path = to_upath(path)
|
|
180
|
-
resolved_path.parent.mkdir(parents=True, exist_ok=True)
|
|
181
|
-
return resolved_path
|
|
182
|
-
|
|
183
|
-
async def log_message(
|
|
184
|
-
self,
|
|
185
|
-
*,
|
|
186
|
-
conversation_id: str,
|
|
187
|
-
message_id: str,
|
|
188
|
-
content: str,
|
|
189
|
-
role: str,
|
|
190
|
-
name: str | None = None,
|
|
191
|
-
cost_info: Any | None = None,
|
|
192
|
-
model: str | None = None,
|
|
193
|
-
response_time: float | None = None,
|
|
194
|
-
forwarded_from: list[str] | None = None,
|
|
195
|
-
provider_name: str | None = None,
|
|
196
|
-
provider_response_id: str | None = None,
|
|
197
|
-
messages: str | None = None,
|
|
198
|
-
finish_reason: str | None = None,
|
|
199
|
-
) -> None:
|
|
200
|
-
"""Store message and update log."""
|
|
201
|
-
entry = {
|
|
202
|
-
"type": "message",
|
|
203
|
-
"timestamp": get_now(),
|
|
204
|
-
"conversation_id": conversation_id,
|
|
205
|
-
"message_id": message_id,
|
|
206
|
-
"content": content,
|
|
207
|
-
"role": role,
|
|
208
|
-
"agent_name": name,
|
|
209
|
-
"model": model,
|
|
210
|
-
"cost_info": cost_info,
|
|
211
|
-
"response_time": response_time,
|
|
212
|
-
"forwarded_from": forwarded_from,
|
|
213
|
-
"provider_name": provider_name,
|
|
214
|
-
"provider_response_id": provider_response_id,
|
|
215
|
-
"messages": messages,
|
|
216
|
-
"finish_reason": finish_reason,
|
|
217
|
-
}
|
|
218
|
-
self._entries.append(entry)
|
|
219
|
-
|
|
220
|
-
path = await self._get_path("message", **entry)
|
|
221
|
-
self._write(path)
|
|
222
|
-
|
|
223
|
-
async def log_conversation(
|
|
224
|
-
self,
|
|
225
|
-
*,
|
|
226
|
-
conversation_id: str,
|
|
227
|
-
node_name: str,
|
|
228
|
-
start_time: datetime | None = None,
|
|
229
|
-
) -> None:
|
|
230
|
-
"""Store conversation start."""
|
|
231
|
-
entry = {
|
|
232
|
-
"type": "conversation",
|
|
233
|
-
"timestamp": start_time or get_now(),
|
|
234
|
-
"conversation_id": conversation_id,
|
|
235
|
-
"agent_name": node_name,
|
|
236
|
-
}
|
|
237
|
-
self._entries.append(entry)
|
|
238
|
-
|
|
239
|
-
path = await self._get_path("conversation", **entry)
|
|
240
|
-
self._write(path)
|
|
241
|
-
|
|
242
|
-
async def log_command(
|
|
243
|
-
self,
|
|
244
|
-
*,
|
|
245
|
-
agent_name: str,
|
|
246
|
-
session_id: str,
|
|
247
|
-
command: str,
|
|
248
|
-
context_type: type | None = None,
|
|
249
|
-
metadata: dict[str, JsonValue] | None = None,
|
|
250
|
-
) -> None:
|
|
251
|
-
"""Store command."""
|
|
252
|
-
entry = {
|
|
253
|
-
"type": "command",
|
|
254
|
-
"timestamp": get_now(),
|
|
255
|
-
"agent_name": agent_name,
|
|
256
|
-
"session_id": session_id,
|
|
257
|
-
"command": command,
|
|
258
|
-
"context_type": context_type.__name__ if context_type else "",
|
|
259
|
-
"metadata": metadata or {},
|
|
260
|
-
}
|
|
261
|
-
self._entries.append(entry)
|
|
262
|
-
|
|
263
|
-
path = await self._get_path("command", **entry)
|
|
264
|
-
self._write(path)
|
|
265
|
-
|
|
266
|
-
def _write(self, path: UPath) -> None:
|
|
267
|
-
"""Write current state to file at given path."""
|
|
268
|
-
context = {"entries": self._entries}
|
|
269
|
-
try:
|
|
270
|
-
text = self.content_template.render(context)
|
|
271
|
-
path.write_text(text, encoding=self.encoding)
|
|
272
|
-
except Exception as e:
|
|
273
|
-
logger.exception("Failed to write to log file", path=path)
|
|
274
|
-
msg = f"Failed to write to log file: {e}"
|
|
275
|
-
raise RuntimeError(msg) from e
|