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
|
@@ -15,9 +15,9 @@ from agentpool.ui.base import InputProvider
|
|
|
15
15
|
|
|
16
16
|
if TYPE_CHECKING:
|
|
17
17
|
from acp import RequestPermissionResponse
|
|
18
|
+
from agentpool import AgentContext
|
|
18
19
|
from agentpool.agents.context import ConfirmationResult
|
|
19
20
|
from agentpool.messaging import ChatMessage
|
|
20
|
-
from agentpool.messaging.context import NodeContext
|
|
21
21
|
from agentpool.tools.base import Tool
|
|
22
22
|
from agentpool_server.acp_server.session import ACPSession
|
|
23
23
|
|
|
@@ -100,7 +100,7 @@ class ACPInputProvider(InputProvider):
|
|
|
100
100
|
|
|
101
101
|
async def get_tool_confirmation(
|
|
102
102
|
self,
|
|
103
|
-
context:
|
|
103
|
+
context: AgentContext[Any],
|
|
104
104
|
tool: Tool,
|
|
105
105
|
args: dict[str, Any],
|
|
106
106
|
message_history: list[ChatMessage[Any]] | None = None,
|
|
@@ -131,18 +131,53 @@ class ACPInputProvider(InputProvider):
|
|
|
131
131
|
return "skip"
|
|
132
132
|
|
|
133
133
|
# Create a descriptive title for the permission request
|
|
134
|
-
args_str = ", ".join(f"{k}={v}" for k, v in args.items())
|
|
134
|
+
# args_str = ", ".join(f"{k}={v}" for k, v in args.items())
|
|
135
|
+
# Use the tool_call_id from context - this must match the UI tool call
|
|
136
|
+
actual_tool_call_id = getattr(context, "tool_call_id", None)
|
|
137
|
+
if not actual_tool_call_id:
|
|
138
|
+
msg = (
|
|
139
|
+
f"No tool_call_id in context for tool {tool.name!r}. "
|
|
140
|
+
"This indicates a bug in tool call tracking."
|
|
141
|
+
)
|
|
142
|
+
logger.error(msg)
|
|
143
|
+
raise RuntimeError(msg) # noqa: TRY301
|
|
144
|
+
logger.debug(
|
|
145
|
+
"Requesting permission",
|
|
146
|
+
tool_name=tool.name,
|
|
147
|
+
tool_call_id=actual_tool_call_id,
|
|
148
|
+
)
|
|
149
|
+
# Note: We no longer send tool_call_start/progress notifications here.
|
|
150
|
+
# The streaming loop (via ACPEventConverter) already sends ToolCallStart
|
|
151
|
+
# before the permission callback is invoked, so Zed already knows about
|
|
152
|
+
# the tool call. Sending duplicates was causing sync issues.
|
|
153
|
+
|
|
154
|
+
from acp.utils import generate_tool_title
|
|
155
|
+
|
|
156
|
+
title = generate_tool_title(tool.name, args)
|
|
135
157
|
response = await self.session.requests.request_permission(
|
|
136
|
-
tool_call_id=
|
|
137
|
-
title=
|
|
158
|
+
tool_call_id=actual_tool_call_id,
|
|
159
|
+
title=title,
|
|
160
|
+
raw_input=args,
|
|
138
161
|
options=DEFAULT_PERMISSION_OPTIONS,
|
|
139
162
|
)
|
|
163
|
+
logger.info(
|
|
164
|
+
"Permission response received",
|
|
165
|
+
tool_name=tool.name,
|
|
166
|
+
outcome=response.outcome,
|
|
167
|
+
outcome_type=type(response.outcome).__name__,
|
|
168
|
+
)
|
|
140
169
|
# Map ACP permission response to our confirmation result
|
|
141
170
|
if isinstance(response.outcome, AllowedOutcome):
|
|
171
|
+
logger.info(
|
|
172
|
+
"AllowedOutcome detected",
|
|
173
|
+
option_id=response.outcome.option_id,
|
|
174
|
+
tool_name=tool.name,
|
|
175
|
+
)
|
|
142
176
|
return self._handle_permission_response(response.outcome.option_id, tool.name)
|
|
143
177
|
if response.outcome.outcome == "cancelled":
|
|
178
|
+
logger.debug("Permission cancelled", tool_name=tool.name)
|
|
144
179
|
return "skip"
|
|
145
|
-
# Handle other outcomes
|
|
180
|
+
# Handle other unexpected outcomes
|
|
146
181
|
logger.warning("Unexpected permission outcome", outcome=response.outcome.outcome)
|
|
147
182
|
|
|
148
183
|
except Exception:
|
|
@@ -150,20 +185,28 @@ class ACPInputProvider(InputProvider):
|
|
|
150
185
|
# Default to abort on error to be safe
|
|
151
186
|
return "abort_run"
|
|
152
187
|
else:
|
|
153
|
-
return "
|
|
188
|
+
return "skip" # Default to skip for unknown outcomes
|
|
154
189
|
|
|
155
190
|
def _handle_permission_response(self, option_id: str, tool_name: str) -> ConfirmationResult:
|
|
156
191
|
"""Handle permission response and update tool approval state."""
|
|
157
|
-
|
|
192
|
+
# Normalize to hyphen format for matching
|
|
193
|
+
normalized = option_id.replace("_", "-")
|
|
194
|
+
logger.info(
|
|
195
|
+
"Handling permission response",
|
|
196
|
+
option_id=option_id,
|
|
197
|
+
normalized=normalized,
|
|
198
|
+
tool_name=tool_name,
|
|
199
|
+
)
|
|
200
|
+
match normalized:
|
|
158
201
|
case "allow-once":
|
|
159
202
|
return "allow"
|
|
160
203
|
case "allow-always":
|
|
161
204
|
self._tool_approvals[tool_name] = "allow_always"
|
|
162
205
|
logger.info("Tool approval set", tool_name=tool_name, approval="allow_always")
|
|
163
206
|
return "allow"
|
|
164
|
-
case "reject-once":
|
|
207
|
+
case "reject-once" | "deny-once":
|
|
165
208
|
return "skip"
|
|
166
|
-
case "reject-always":
|
|
209
|
+
case "reject-always" | "deny-always":
|
|
167
210
|
self._tool_approvals[tool_name] = "reject_always"
|
|
168
211
|
logger.info("Tool approval set", tool_name=tool_name, approval="reject_always")
|
|
169
212
|
return "skip"
|
|
@@ -7,14 +7,10 @@ the Agent Client Protocol.
|
|
|
7
7
|
from __future__ import annotations
|
|
8
8
|
|
|
9
9
|
import asyncio
|
|
10
|
-
from datetime import timedelta
|
|
11
10
|
import functools
|
|
12
11
|
from typing import TYPE_CHECKING, Any, Self
|
|
13
12
|
|
|
14
|
-
import
|
|
15
|
-
|
|
16
|
-
from acp import AgentSideConnection
|
|
17
|
-
from acp.stdio import stdio_streams
|
|
13
|
+
from acp import serve
|
|
18
14
|
from agentpool import AgentPool
|
|
19
15
|
from agentpool.log import get_logger
|
|
20
16
|
from agentpool.models.manifest import AgentsManifest
|
|
@@ -23,41 +19,14 @@ from agentpool_server.acp_server.acp_agent import AgentPoolACPAgent
|
|
|
23
19
|
|
|
24
20
|
|
|
25
21
|
if TYPE_CHECKING:
|
|
26
|
-
from collections.abc import Sequence
|
|
27
|
-
|
|
28
|
-
from tokonomics.model_discovery import ProviderType
|
|
29
|
-
from tokonomics.model_discovery.model_info import ModelInfo
|
|
30
22
|
from upathtools import JoinablePathLike
|
|
31
23
|
|
|
32
|
-
from acp
|
|
24
|
+
from acp import Transport
|
|
33
25
|
|
|
34
26
|
|
|
35
27
|
logger = get_logger(__name__)
|
|
36
28
|
|
|
37
29
|
|
|
38
|
-
def _convert_to_acp_model_info(
|
|
39
|
-
toko_models: Sequence[ModelInfo],
|
|
40
|
-
) -> list[ACPModelInfo]:
|
|
41
|
-
"""Convert tokonomics ModelInfo list to ACP ModelInfo list.
|
|
42
|
-
|
|
43
|
-
Args:
|
|
44
|
-
toko_models: List of tokonomics ModelInfo objects
|
|
45
|
-
|
|
46
|
-
Returns:
|
|
47
|
-
List of ACP ModelInfo objects with pydantic_ai_id as model_id
|
|
48
|
-
"""
|
|
49
|
-
from acp.schema import ModelInfo as ACPModelInfo
|
|
50
|
-
|
|
51
|
-
return [
|
|
52
|
-
ACPModelInfo(
|
|
53
|
-
model_id=model.pydantic_ai_id,
|
|
54
|
-
name=f"{model.provider}: {model.name}" if model.provider else model.name,
|
|
55
|
-
description=model.format(),
|
|
56
|
-
)
|
|
57
|
-
for model in toko_models
|
|
58
|
-
]
|
|
59
|
-
|
|
60
|
-
|
|
61
30
|
class ACPServer(BaseServer):
|
|
62
31
|
"""ACP (Agent Client Protocol) server for agentpool using external library.
|
|
63
32
|
|
|
@@ -75,13 +44,13 @@ class ACPServer(BaseServer):
|
|
|
75
44
|
name: str | None = None,
|
|
76
45
|
file_access: bool = True,
|
|
77
46
|
terminal_access: bool = True,
|
|
78
|
-
providers: list[ProviderType] | None = None,
|
|
79
47
|
debug_messages: bool = False,
|
|
80
48
|
debug_file: str | None = None,
|
|
81
49
|
debug_commands: bool = False,
|
|
82
50
|
agent: str | None = None,
|
|
83
51
|
load_skills: bool = True,
|
|
84
52
|
config_path: str | None = None,
|
|
53
|
+
transport: Transport = "stdio",
|
|
85
54
|
) -> None:
|
|
86
55
|
"""Initialize ACP server with configuration.
|
|
87
56
|
|
|
@@ -90,27 +59,24 @@ class ACPServer(BaseServer):
|
|
|
90
59
|
name: Optional Server name (auto-generated if None)
|
|
91
60
|
file_access: Whether to support file access operations
|
|
92
61
|
terminal_access: Whether to support terminal access operations
|
|
93
|
-
providers: List of providers to use for model discovery (None = openrouter)
|
|
94
62
|
debug_messages: Whether to enable debug message logging
|
|
95
63
|
debug_file: File path for debug message logging
|
|
96
64
|
debug_commands: Whether to enable debug slash commands for testing
|
|
97
65
|
agent: Optional specific agent name to use (defaults to first agent)
|
|
98
66
|
load_skills: Whether to load client-side skills from .claude/skills
|
|
99
67
|
config_path: Path to the configuration file (for tracking/hot-switching)
|
|
68
|
+
transport: Transport configuration ("stdio", "websocket", or transport object)
|
|
100
69
|
"""
|
|
101
70
|
super().__init__(pool, name=name, raise_exceptions=True)
|
|
102
71
|
self.file_access = file_access
|
|
103
72
|
self.terminal_access = terminal_access
|
|
104
|
-
self.providers = providers or ["openai", "anthropic", "gemini"]
|
|
105
73
|
self.debug_messages = debug_messages
|
|
106
74
|
self.debug_file = debug_file
|
|
107
75
|
self.debug_commands = debug_commands
|
|
108
76
|
self.agent = agent
|
|
109
77
|
self.load_skills = load_skills
|
|
110
78
|
self.config_path = config_path
|
|
111
|
-
|
|
112
|
-
self._available_models: list[ACPModelInfo] = []
|
|
113
|
-
self._models_initialized = False
|
|
79
|
+
self.transport: Transport = transport
|
|
114
80
|
|
|
115
81
|
@classmethod
|
|
116
82
|
def from_config(
|
|
@@ -119,12 +85,12 @@ class ACPServer(BaseServer):
|
|
|
119
85
|
*,
|
|
120
86
|
file_access: bool = True,
|
|
121
87
|
terminal_access: bool = True,
|
|
122
|
-
providers: list[ProviderType] | None = None,
|
|
123
88
|
debug_messages: bool = False,
|
|
124
89
|
debug_file: str | None = None,
|
|
125
90
|
debug_commands: bool = False,
|
|
126
91
|
agent: str | None = None,
|
|
127
92
|
load_skills: bool = True,
|
|
93
|
+
transport: Transport = "stdio",
|
|
128
94
|
) -> Self:
|
|
129
95
|
"""Create ACP server from existing agentpool configuration.
|
|
130
96
|
|
|
@@ -132,12 +98,12 @@ class ACPServer(BaseServer):
|
|
|
132
98
|
config_path: Path to agentpool YAML config file
|
|
133
99
|
file_access: Enable file system access
|
|
134
100
|
terminal_access: Enable terminal access
|
|
135
|
-
providers: List of provider types to use for model discovery
|
|
136
101
|
debug_messages: Enable saving JSON messages to file
|
|
137
102
|
debug_file: Path to debug file
|
|
138
103
|
debug_commands: Enable debug slash commands for testing
|
|
139
104
|
agent: Optional specific agent name to use (defaults to first agent)
|
|
140
105
|
load_skills: Whether to load client-side skills from .claude/skills
|
|
106
|
+
transport: Transport configuration ("stdio", "websocket", or transport object)
|
|
141
107
|
|
|
142
108
|
Returns:
|
|
143
109
|
Configured ACP server instance with agent pool from config
|
|
@@ -148,13 +114,13 @@ class ACPServer(BaseServer):
|
|
|
148
114
|
pool,
|
|
149
115
|
file_access=file_access,
|
|
150
116
|
terminal_access=terminal_access,
|
|
151
|
-
providers=providers,
|
|
152
117
|
debug_messages=debug_messages,
|
|
153
118
|
debug_file=debug_file or "acp-debug.jsonl" if debug_messages else None,
|
|
154
119
|
debug_commands=debug_commands,
|
|
155
120
|
agent=agent,
|
|
156
121
|
load_skills=load_skills,
|
|
157
122
|
config_path=str(config_path),
|
|
123
|
+
transport=transport,
|
|
158
124
|
)
|
|
159
125
|
agent_names = list(server.pool.agents.keys())
|
|
160
126
|
|
|
@@ -170,13 +136,13 @@ class ACPServer(BaseServer):
|
|
|
170
136
|
|
|
171
137
|
async def _start_async(self) -> None:
|
|
172
138
|
"""Start the ACP server (blocking async - runs until stopped)."""
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
139
|
+
transport_name = (
|
|
140
|
+
type(self.transport).__name__ if not isinstance(self.transport, str) else self.transport
|
|
141
|
+
)
|
|
142
|
+
self.log.info("Starting ACP server", transport=transport_name)
|
|
176
143
|
create_acp_agent = functools.partial(
|
|
177
144
|
AgentPoolACPAgent,
|
|
178
145
|
agent_pool=self.pool,
|
|
179
|
-
available_models=self._available_models,
|
|
180
146
|
file_access=self.file_access,
|
|
181
147
|
terminal_access=self.terminal_access,
|
|
182
148
|
debug_commands=self.debug_commands,
|
|
@@ -184,27 +150,26 @@ class ACPServer(BaseServer):
|
|
|
184
150
|
load_skills=self.load_skills,
|
|
185
151
|
server=self,
|
|
186
152
|
)
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
conn = AgentSideConnection(create_acp_agent, writer, reader, debug_file=file)
|
|
153
|
+
|
|
154
|
+
debug_file = self.debug_file if self.debug_messages else None
|
|
190
155
|
self.log.info("ACP server started", file=self.file_access, terminal=self.terminal_access)
|
|
191
|
-
|
|
192
|
-
|
|
156
|
+
|
|
157
|
+
try:
|
|
158
|
+
await serve(
|
|
159
|
+
create_acp_agent,
|
|
160
|
+
transport=self.transport,
|
|
161
|
+
shutdown_event=self._shutdown_event,
|
|
162
|
+
debug_file=debug_file,
|
|
163
|
+
)
|
|
193
164
|
except asyncio.CancelledError:
|
|
194
165
|
self.log.info("ACP server shutdown requested")
|
|
195
166
|
raise
|
|
196
167
|
except KeyboardInterrupt:
|
|
197
168
|
self.log.info("ACP server shutdown requested")
|
|
198
169
|
except Exception:
|
|
199
|
-
self.log.exception("
|
|
200
|
-
finally:
|
|
201
|
-
await conn.close()
|
|
170
|
+
self.log.exception("ACP server error")
|
|
202
171
|
|
|
203
|
-
async def swap_pool(
|
|
204
|
-
self,
|
|
205
|
-
config_path: str,
|
|
206
|
-
agent: str | None = None,
|
|
207
|
-
) -> list[str]:
|
|
172
|
+
async def swap_pool(self, config_path: str, agent: str | None = None) -> list[str]:
|
|
208
173
|
"""Swap the current pool with a new one from config.
|
|
209
174
|
|
|
210
175
|
This method handles the full lifecycle of swapping pools:
|
|
@@ -228,17 +193,14 @@ class ACPServer(BaseServer):
|
|
|
228
193
|
self.log.info("Loading new pool configuration", config_path=config_path)
|
|
229
194
|
new_manifest = AgentsManifest.from_file(config_path)
|
|
230
195
|
new_pool = AgentPool(manifest=new_manifest)
|
|
231
|
-
|
|
232
196
|
# 2. Validate agent exists in new pool if specified
|
|
233
197
|
agent_names = list(new_pool.all_agents.keys())
|
|
234
198
|
if not agent_names:
|
|
235
199
|
msg = "New configuration contains no agents"
|
|
236
200
|
raise ValueError(msg)
|
|
237
|
-
|
|
238
201
|
if agent and agent not in agent_names:
|
|
239
202
|
msg = f"Agent {agent!r} not found in new config. Available: {agent_names}"
|
|
240
203
|
raise ValueError(msg)
|
|
241
|
-
|
|
242
204
|
# 3. Enter new pool context first (so we can roll back if it fails)
|
|
243
205
|
try:
|
|
244
206
|
await new_pool.__aenter__()
|
|
@@ -246,43 +208,15 @@ class ACPServer(BaseServer):
|
|
|
246
208
|
self.log.exception("Failed to initialize new pool")
|
|
247
209
|
msg = f"Failed to initialize new pool: {e}"
|
|
248
210
|
raise ValueError(msg) from e
|
|
249
|
-
|
|
250
211
|
# 4. Exit old pool context
|
|
251
212
|
old_pool = self.pool
|
|
252
213
|
try:
|
|
253
214
|
await old_pool.__aexit__(None, None, None)
|
|
254
215
|
except Exception:
|
|
255
216
|
self.log.exception("Error closing old pool (continuing with swap)")
|
|
256
|
-
|
|
257
217
|
# 5. Update references
|
|
258
218
|
self.pool = new_pool
|
|
259
219
|
self.agent = agent
|
|
260
220
|
self.config_path = config_path
|
|
261
|
-
|
|
262
221
|
self.log.info("Pool swapped successfully", agent_names=agent_names, default_agent=agent)
|
|
263
222
|
return agent_names
|
|
264
|
-
|
|
265
|
-
@logfire.instrument("ACP: Initializing models.")
|
|
266
|
-
async def _initialize_models(self) -> None:
|
|
267
|
-
"""Initialize available models using tokonomics model discovery.
|
|
268
|
-
|
|
269
|
-
Converts tokonomics ModelInfo to ACP ModelInfo format at startup
|
|
270
|
-
so all downstream code works with ACP types consistently.
|
|
271
|
-
"""
|
|
272
|
-
from tokonomics.model_discovery import get_all_models
|
|
273
|
-
|
|
274
|
-
if self._models_initialized:
|
|
275
|
-
return
|
|
276
|
-
try:
|
|
277
|
-
self.log.info("Discovering available models...")
|
|
278
|
-
delta = timedelta(days=200)
|
|
279
|
-
toko_models = await get_all_models(providers=self.providers, max_age=delta)
|
|
280
|
-
# Convert to ACP format once at startup
|
|
281
|
-
self._available_models = _convert_to_acp_model_info(toko_models)
|
|
282
|
-
self._models_initialized = True
|
|
283
|
-
self.log.info("Discovered models", count=len(self._available_models))
|
|
284
|
-
except Exception:
|
|
285
|
-
self.log.exception("Failed to discover models")
|
|
286
|
-
self._available_models = []
|
|
287
|
-
finally:
|
|
288
|
-
self._models_initialized = True
|