agentpool 2.1.9__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of agentpool might be problematic. Click here for more details.
- acp/README.md +64 -0
- acp/__init__.py +172 -0
- acp/__main__.py +10 -0
- acp/acp_requests.py +285 -0
- acp/agent/__init__.py +6 -0
- acp/agent/connection.py +256 -0
- acp/agent/implementations/__init__.py +6 -0
- acp/agent/implementations/debug_server/__init__.py +1 -0
- acp/agent/implementations/debug_server/cli.py +79 -0
- acp/agent/implementations/debug_server/debug.html +234 -0
- acp/agent/implementations/debug_server/debug_server.py +496 -0
- acp/agent/implementations/testing.py +91 -0
- acp/agent/protocol.py +65 -0
- acp/bridge/README.md +162 -0
- acp/bridge/__init__.py +6 -0
- acp/bridge/__main__.py +91 -0
- acp/bridge/bridge.py +246 -0
- acp/bridge/py.typed +0 -0
- acp/bridge/settings.py +15 -0
- acp/client/__init__.py +7 -0
- acp/client/connection.py +251 -0
- acp/client/implementations/__init__.py +7 -0
- acp/client/implementations/default_client.py +185 -0
- acp/client/implementations/headless_client.py +266 -0
- acp/client/implementations/noop_client.py +110 -0
- acp/client/protocol.py +61 -0
- acp/connection.py +280 -0
- acp/exceptions.py +46 -0
- acp/filesystem.py +524 -0
- acp/notifications.py +832 -0
- acp/py.typed +0 -0
- acp/schema/__init__.py +265 -0
- acp/schema/agent_plan.py +30 -0
- acp/schema/agent_requests.py +126 -0
- acp/schema/agent_responses.py +256 -0
- acp/schema/base.py +39 -0
- acp/schema/capabilities.py +230 -0
- acp/schema/client_requests.py +247 -0
- acp/schema/client_responses.py +96 -0
- acp/schema/common.py +81 -0
- acp/schema/content_blocks.py +188 -0
- acp/schema/mcp.py +82 -0
- acp/schema/messages.py +171 -0
- acp/schema/notifications.py +82 -0
- acp/schema/protocol_stuff.md +3 -0
- acp/schema/session_state.py +160 -0
- acp/schema/session_updates.py +419 -0
- acp/schema/slash_commands.py +51 -0
- acp/schema/terminal.py +15 -0
- acp/schema/tool_call.py +347 -0
- acp/stdio.py +250 -0
- acp/task/__init__.py +53 -0
- acp/task/debug.py +197 -0
- acp/task/dispatcher.py +93 -0
- acp/task/queue.py +69 -0
- acp/task/sender.py +82 -0
- acp/task/state.py +87 -0
- acp/task/supervisor.py +93 -0
- acp/terminal_handle.py +30 -0
- acp/tool_call_reporter.py +199 -0
- acp/tool_call_state.py +178 -0
- acp/transports.py +104 -0
- acp/utils.py +240 -0
- agentpool/__init__.py +63 -0
- agentpool/__main__.py +7 -0
- agentpool/agents/__init__.py +30 -0
- agentpool/agents/acp_agent/__init__.py +5 -0
- agentpool/agents/acp_agent/acp_agent.py +837 -0
- agentpool/agents/acp_agent/acp_converters.py +294 -0
- agentpool/agents/acp_agent/client_handler.py +317 -0
- agentpool/agents/acp_agent/session_state.py +44 -0
- agentpool/agents/agent.py +1264 -0
- agentpool/agents/agui_agent/__init__.py +19 -0
- agentpool/agents/agui_agent/agui_agent.py +677 -0
- agentpool/agents/agui_agent/agui_converters.py +423 -0
- agentpool/agents/agui_agent/chunk_transformer.py +204 -0
- agentpool/agents/agui_agent/event_types.py +83 -0
- agentpool/agents/agui_agent/helpers.py +192 -0
- agentpool/agents/architect.py +71 -0
- agentpool/agents/base_agent.py +177 -0
- agentpool/agents/claude_code_agent/__init__.py +11 -0
- agentpool/agents/claude_code_agent/claude_code_agent.py +1021 -0
- agentpool/agents/claude_code_agent/converters.py +243 -0
- agentpool/agents/context.py +105 -0
- agentpool/agents/events/__init__.py +61 -0
- agentpool/agents/events/builtin_handlers.py +129 -0
- agentpool/agents/events/event_emitter.py +320 -0
- agentpool/agents/events/events.py +561 -0
- agentpool/agents/events/tts_handlers.py +186 -0
- agentpool/agents/interactions.py +419 -0
- agentpool/agents/slashed_agent.py +244 -0
- agentpool/agents/sys_prompts.py +178 -0
- agentpool/agents/tool_wrapping.py +184 -0
- agentpool/base_provider.py +28 -0
- agentpool/common_types.py +226 -0
- agentpool/config_resources/__init__.py +16 -0
- agentpool/config_resources/acp_assistant.yml +24 -0
- agentpool/config_resources/agents.yml +109 -0
- agentpool/config_resources/agents_template.yml +18 -0
- agentpool/config_resources/agui_test.yml +18 -0
- agentpool/config_resources/claude_code_agent.yml +16 -0
- agentpool/config_resources/claude_style_subagent.md +30 -0
- agentpool/config_resources/external_acp_agents.yml +77 -0
- agentpool/config_resources/opencode_style_subagent.md +19 -0
- agentpool/config_resources/tts_test_agents.yml +78 -0
- agentpool/delegation/__init__.py +8 -0
- agentpool/delegation/base_team.py +504 -0
- agentpool/delegation/message_flow_tracker.py +39 -0
- agentpool/delegation/pool.py +1129 -0
- agentpool/delegation/team.py +325 -0
- agentpool/delegation/teamrun.py +343 -0
- agentpool/docs/__init__.py +5 -0
- agentpool/docs/gen_examples.py +42 -0
- agentpool/docs/utils.py +370 -0
- agentpool/functional/__init__.py +20 -0
- agentpool/functional/py.typed +0 -0
- agentpool/functional/run.py +80 -0
- agentpool/functional/structure.py +136 -0
- agentpool/hooks/__init__.py +20 -0
- agentpool/hooks/agent_hooks.py +247 -0
- agentpool/hooks/base.py +119 -0
- agentpool/hooks/callable.py +140 -0
- agentpool/hooks/command.py +180 -0
- agentpool/hooks/prompt.py +122 -0
- agentpool/jinja_filters.py +132 -0
- agentpool/log.py +224 -0
- agentpool/mcp_server/__init__.py +17 -0
- agentpool/mcp_server/client.py +429 -0
- agentpool/mcp_server/constants.py +32 -0
- agentpool/mcp_server/conversions.py +172 -0
- agentpool/mcp_server/helpers.py +47 -0
- agentpool/mcp_server/manager.py +232 -0
- agentpool/mcp_server/message_handler.py +164 -0
- agentpool/mcp_server/registries/__init__.py +1 -0
- agentpool/mcp_server/registries/official_registry_client.py +345 -0
- agentpool/mcp_server/registries/pulsemcp_client.py +88 -0
- agentpool/mcp_server/tool_bridge.py +548 -0
- agentpool/messaging/__init__.py +58 -0
- agentpool/messaging/compaction.py +928 -0
- agentpool/messaging/connection_manager.py +319 -0
- agentpool/messaging/context.py +66 -0
- agentpool/messaging/event_manager.py +426 -0
- agentpool/messaging/events.py +39 -0
- agentpool/messaging/message_container.py +209 -0
- agentpool/messaging/message_history.py +491 -0
- agentpool/messaging/messagenode.py +377 -0
- agentpool/messaging/messages.py +655 -0
- agentpool/messaging/processing.py +76 -0
- agentpool/mime_utils.py +95 -0
- agentpool/models/__init__.py +21 -0
- agentpool/models/acp_agents/__init__.py +22 -0
- agentpool/models/acp_agents/base.py +308 -0
- agentpool/models/acp_agents/mcp_capable.py +790 -0
- agentpool/models/acp_agents/non_mcp.py +842 -0
- agentpool/models/agents.py +450 -0
- agentpool/models/agui_agents.py +89 -0
- agentpool/models/claude_code_agents.py +238 -0
- agentpool/models/file_agents.py +116 -0
- agentpool/models/file_parsing.py +367 -0
- agentpool/models/manifest.py +658 -0
- agentpool/observability/__init__.py +9 -0
- agentpool/observability/observability_registry.py +97 -0
- agentpool/prompts/__init__.py +1 -0
- agentpool/prompts/base.py +27 -0
- agentpool/prompts/builtin_provider.py +75 -0
- agentpool/prompts/conversion_manager.py +95 -0
- agentpool/prompts/convert.py +96 -0
- agentpool/prompts/manager.py +204 -0
- agentpool/prompts/parts/zed.md +33 -0
- agentpool/prompts/prompts.py +581 -0
- agentpool/py.typed +0 -0
- agentpool/queries/tree-sitter-language-pack/README.md +7 -0
- agentpool/queries/tree-sitter-language-pack/arduino-tags.scm +5 -0
- agentpool/queries/tree-sitter-language-pack/c-tags.scm +9 -0
- agentpool/queries/tree-sitter-language-pack/chatito-tags.scm +16 -0
- agentpool/queries/tree-sitter-language-pack/clojure-tags.scm +7 -0
- agentpool/queries/tree-sitter-language-pack/commonlisp-tags.scm +122 -0
- agentpool/queries/tree-sitter-language-pack/cpp-tags.scm +15 -0
- agentpool/queries/tree-sitter-language-pack/csharp-tags.scm +26 -0
- agentpool/queries/tree-sitter-language-pack/d-tags.scm +26 -0
- agentpool/queries/tree-sitter-language-pack/dart-tags.scm +92 -0
- agentpool/queries/tree-sitter-language-pack/elisp-tags.scm +5 -0
- agentpool/queries/tree-sitter-language-pack/elixir-tags.scm +54 -0
- agentpool/queries/tree-sitter-language-pack/elm-tags.scm +19 -0
- agentpool/queries/tree-sitter-language-pack/gleam-tags.scm +41 -0
- agentpool/queries/tree-sitter-language-pack/go-tags.scm +42 -0
- agentpool/queries/tree-sitter-language-pack/java-tags.scm +20 -0
- agentpool/queries/tree-sitter-language-pack/javascript-tags.scm +88 -0
- agentpool/queries/tree-sitter-language-pack/lua-tags.scm +34 -0
- agentpool/queries/tree-sitter-language-pack/matlab-tags.scm +10 -0
- agentpool/queries/tree-sitter-language-pack/ocaml-tags.scm +115 -0
- agentpool/queries/tree-sitter-language-pack/ocaml_interface-tags.scm +98 -0
- agentpool/queries/tree-sitter-language-pack/pony-tags.scm +39 -0
- agentpool/queries/tree-sitter-language-pack/properties-tags.scm +5 -0
- agentpool/queries/tree-sitter-language-pack/python-tags.scm +14 -0
- agentpool/queries/tree-sitter-language-pack/r-tags.scm +21 -0
- agentpool/queries/tree-sitter-language-pack/racket-tags.scm +12 -0
- agentpool/queries/tree-sitter-language-pack/ruby-tags.scm +64 -0
- agentpool/queries/tree-sitter-language-pack/rust-tags.scm +60 -0
- agentpool/queries/tree-sitter-language-pack/solidity-tags.scm +43 -0
- agentpool/queries/tree-sitter-language-pack/swift-tags.scm +51 -0
- agentpool/queries/tree-sitter-language-pack/udev-tags.scm +20 -0
- agentpool/queries/tree-sitter-languages/README.md +24 -0
- agentpool/queries/tree-sitter-languages/c-tags.scm +9 -0
- agentpool/queries/tree-sitter-languages/c_sharp-tags.scm +46 -0
- agentpool/queries/tree-sitter-languages/cpp-tags.scm +15 -0
- agentpool/queries/tree-sitter-languages/dart-tags.scm +91 -0
- agentpool/queries/tree-sitter-languages/elisp-tags.scm +8 -0
- agentpool/queries/tree-sitter-languages/elixir-tags.scm +54 -0
- agentpool/queries/tree-sitter-languages/elm-tags.scm +19 -0
- agentpool/queries/tree-sitter-languages/fortran-tags.scm +15 -0
- agentpool/queries/tree-sitter-languages/go-tags.scm +30 -0
- agentpool/queries/tree-sitter-languages/haskell-tags.scm +3 -0
- agentpool/queries/tree-sitter-languages/hcl-tags.scm +77 -0
- agentpool/queries/tree-sitter-languages/java-tags.scm +20 -0
- agentpool/queries/tree-sitter-languages/javascript-tags.scm +88 -0
- agentpool/queries/tree-sitter-languages/julia-tags.scm +60 -0
- agentpool/queries/tree-sitter-languages/kotlin-tags.scm +27 -0
- agentpool/queries/tree-sitter-languages/matlab-tags.scm +10 -0
- agentpool/queries/tree-sitter-languages/ocaml-tags.scm +115 -0
- agentpool/queries/tree-sitter-languages/ocaml_interface-tags.scm +98 -0
- agentpool/queries/tree-sitter-languages/php-tags.scm +26 -0
- agentpool/queries/tree-sitter-languages/python-tags.scm +12 -0
- agentpool/queries/tree-sitter-languages/ql-tags.scm +26 -0
- agentpool/queries/tree-sitter-languages/ruby-tags.scm +64 -0
- agentpool/queries/tree-sitter-languages/rust-tags.scm +60 -0
- agentpool/queries/tree-sitter-languages/scala-tags.scm +65 -0
- agentpool/queries/tree-sitter-languages/typescript-tags.scm +41 -0
- agentpool/queries/tree-sitter-languages/zig-tags.scm +3 -0
- agentpool/repomap.py +1231 -0
- agentpool/resource_providers/__init__.py +17 -0
- agentpool/resource_providers/aggregating.py +54 -0
- agentpool/resource_providers/base.py +172 -0
- agentpool/resource_providers/codemode/__init__.py +9 -0
- agentpool/resource_providers/codemode/code_executor.py +215 -0
- agentpool/resource_providers/codemode/default_prompt.py +19 -0
- agentpool/resource_providers/codemode/helpers.py +83 -0
- agentpool/resource_providers/codemode/progress_executor.py +212 -0
- agentpool/resource_providers/codemode/provider.py +150 -0
- agentpool/resource_providers/codemode/remote_mcp_execution.py +143 -0
- agentpool/resource_providers/codemode/remote_provider.py +171 -0
- agentpool/resource_providers/filtering.py +42 -0
- agentpool/resource_providers/mcp_provider.py +246 -0
- agentpool/resource_providers/plan_provider.py +196 -0
- agentpool/resource_providers/pool.py +69 -0
- agentpool/resource_providers/static.py +289 -0
- agentpool/running/__init__.py +20 -0
- agentpool/running/decorators.py +56 -0
- agentpool/running/discovery.py +101 -0
- agentpool/running/executor.py +284 -0
- agentpool/running/injection.py +111 -0
- agentpool/running/py.typed +0 -0
- agentpool/running/run_nodes.py +87 -0
- agentpool/server.py +122 -0
- agentpool/sessions/__init__.py +13 -0
- agentpool/sessions/manager.py +302 -0
- agentpool/sessions/models.py +71 -0
- agentpool/sessions/session.py +239 -0
- agentpool/sessions/store.py +163 -0
- agentpool/skills/__init__.py +5 -0
- agentpool/skills/manager.py +120 -0
- agentpool/skills/registry.py +210 -0
- agentpool/skills/skill.py +36 -0
- agentpool/storage/__init__.py +17 -0
- agentpool/storage/manager.py +419 -0
- agentpool/storage/serialization.py +136 -0
- agentpool/talk/__init__.py +13 -0
- agentpool/talk/registry.py +128 -0
- agentpool/talk/stats.py +159 -0
- agentpool/talk/talk.py +604 -0
- agentpool/tasks/__init__.py +20 -0
- agentpool/tasks/exceptions.py +25 -0
- agentpool/tasks/registry.py +33 -0
- agentpool/testing.py +129 -0
- agentpool/text_templates/__init__.py +39 -0
- agentpool/text_templates/system_prompt.jinja +30 -0
- agentpool/text_templates/tool_call_default.jinja +13 -0
- agentpool/text_templates/tool_call_markdown.jinja +25 -0
- agentpool/text_templates/tool_call_simple.jinja +5 -0
- agentpool/tools/__init__.py +16 -0
- agentpool/tools/base.py +269 -0
- agentpool/tools/exceptions.py +9 -0
- agentpool/tools/manager.py +255 -0
- agentpool/tools/tool_call_info.py +87 -0
- agentpool/ui/__init__.py +2 -0
- agentpool/ui/base.py +89 -0
- agentpool/ui/mock_provider.py +81 -0
- agentpool/ui/stdlib_provider.py +150 -0
- agentpool/utils/__init__.py +44 -0
- agentpool/utils/baseregistry.py +185 -0
- agentpool/utils/count_tokens.py +62 -0
- agentpool/utils/dag.py +184 -0
- agentpool/utils/importing.py +206 -0
- agentpool/utils/inspection.py +334 -0
- agentpool/utils/model_capabilities.py +25 -0
- agentpool/utils/network.py +28 -0
- agentpool/utils/now.py +22 -0
- agentpool/utils/parse_time.py +87 -0
- agentpool/utils/result_utils.py +35 -0
- agentpool/utils/signatures.py +305 -0
- agentpool/utils/streams.py +112 -0
- agentpool/utils/tasks.py +186 -0
- agentpool/vfs_registry.py +250 -0
- agentpool-2.1.9.dist-info/METADATA +336 -0
- agentpool-2.1.9.dist-info/RECORD +474 -0
- agentpool-2.1.9.dist-info/WHEEL +4 -0
- agentpool-2.1.9.dist-info/entry_points.txt +14 -0
- agentpool-2.1.9.dist-info/licenses/LICENSE +22 -0
- agentpool_cli/__init__.py +34 -0
- agentpool_cli/__main__.py +66 -0
- agentpool_cli/agent.py +175 -0
- agentpool_cli/cli_types.py +23 -0
- agentpool_cli/common.py +163 -0
- agentpool_cli/create.py +175 -0
- agentpool_cli/history.py +217 -0
- agentpool_cli/log.py +78 -0
- agentpool_cli/py.typed +0 -0
- agentpool_cli/run.py +84 -0
- agentpool_cli/serve_acp.py +177 -0
- agentpool_cli/serve_api.py +69 -0
- agentpool_cli/serve_mcp.py +74 -0
- agentpool_cli/serve_vercel.py +233 -0
- agentpool_cli/store.py +171 -0
- agentpool_cli/task.py +84 -0
- agentpool_cli/utils.py +104 -0
- agentpool_cli/watch.py +54 -0
- agentpool_commands/__init__.py +180 -0
- agentpool_commands/agents.py +199 -0
- agentpool_commands/base.py +45 -0
- agentpool_commands/commands.py +58 -0
- agentpool_commands/completers.py +110 -0
- agentpool_commands/connections.py +175 -0
- agentpool_commands/markdown_utils.py +31 -0
- agentpool_commands/models.py +62 -0
- agentpool_commands/prompts.py +78 -0
- agentpool_commands/py.typed +0 -0
- agentpool_commands/read.py +77 -0
- agentpool_commands/resources.py +210 -0
- agentpool_commands/session.py +48 -0
- agentpool_commands/tools.py +269 -0
- agentpool_commands/utils.py +189 -0
- agentpool_commands/workers.py +163 -0
- agentpool_config/__init__.py +53 -0
- agentpool_config/builtin_tools.py +265 -0
- agentpool_config/commands.py +237 -0
- agentpool_config/conditions.py +301 -0
- agentpool_config/converters.py +30 -0
- agentpool_config/durable.py +331 -0
- agentpool_config/event_handlers.py +600 -0
- agentpool_config/events.py +153 -0
- agentpool_config/forward_targets.py +251 -0
- agentpool_config/hook_conditions.py +331 -0
- agentpool_config/hooks.py +241 -0
- agentpool_config/jinja.py +206 -0
- agentpool_config/knowledge.py +41 -0
- agentpool_config/loaders.py +350 -0
- agentpool_config/mcp_server.py +243 -0
- agentpool_config/nodes.py +202 -0
- agentpool_config/observability.py +191 -0
- agentpool_config/output_types.py +55 -0
- agentpool_config/pool_server.py +267 -0
- agentpool_config/prompt_hubs.py +105 -0
- agentpool_config/prompts.py +185 -0
- agentpool_config/py.typed +0 -0
- agentpool_config/resources.py +33 -0
- agentpool_config/session.py +119 -0
- agentpool_config/skills.py +17 -0
- agentpool_config/storage.py +288 -0
- agentpool_config/system_prompts.py +190 -0
- agentpool_config/task.py +162 -0
- agentpool_config/teams.py +52 -0
- agentpool_config/tools.py +112 -0
- agentpool_config/toolsets.py +1033 -0
- agentpool_config/workers.py +86 -0
- agentpool_prompts/__init__.py +1 -0
- agentpool_prompts/braintrust_hub.py +235 -0
- agentpool_prompts/fabric.py +75 -0
- agentpool_prompts/langfuse_hub.py +79 -0
- agentpool_prompts/promptlayer_provider.py +59 -0
- agentpool_prompts/py.typed +0 -0
- agentpool_server/__init__.py +9 -0
- agentpool_server/a2a_server/__init__.py +5 -0
- agentpool_server/a2a_server/a2a_types.py +41 -0
- agentpool_server/a2a_server/server.py +190 -0
- agentpool_server/a2a_server/storage.py +81 -0
- agentpool_server/acp_server/__init__.py +22 -0
- agentpool_server/acp_server/acp_agent.py +786 -0
- agentpool_server/acp_server/acp_tools.py +43 -0
- agentpool_server/acp_server/commands/__init__.py +18 -0
- agentpool_server/acp_server/commands/acp_commands.py +594 -0
- agentpool_server/acp_server/commands/debug_commands.py +376 -0
- agentpool_server/acp_server/commands/docs_commands/__init__.py +39 -0
- agentpool_server/acp_server/commands/docs_commands/fetch_repo.py +169 -0
- agentpool_server/acp_server/commands/docs_commands/get_schema.py +176 -0
- agentpool_server/acp_server/commands/docs_commands/get_source.py +110 -0
- agentpool_server/acp_server/commands/docs_commands/git_diff.py +111 -0
- agentpool_server/acp_server/commands/docs_commands/helpers.py +33 -0
- agentpool_server/acp_server/commands/docs_commands/url_to_markdown.py +90 -0
- agentpool_server/acp_server/commands/spawn.py +210 -0
- agentpool_server/acp_server/converters.py +235 -0
- agentpool_server/acp_server/input_provider.py +338 -0
- agentpool_server/acp_server/server.py +288 -0
- agentpool_server/acp_server/session.py +969 -0
- agentpool_server/acp_server/session_manager.py +313 -0
- agentpool_server/acp_server/syntax_detection.py +250 -0
- agentpool_server/acp_server/zed_tools.md +90 -0
- agentpool_server/aggregating_server.py +309 -0
- agentpool_server/agui_server/__init__.py +11 -0
- agentpool_server/agui_server/server.py +128 -0
- agentpool_server/base.py +189 -0
- agentpool_server/http_server.py +164 -0
- agentpool_server/mcp_server/__init__.py +6 -0
- agentpool_server/mcp_server/server.py +314 -0
- agentpool_server/mcp_server/zed_wrapper.py +110 -0
- agentpool_server/openai_api_server/__init__.py +5 -0
- agentpool_server/openai_api_server/completions/__init__.py +1 -0
- agentpool_server/openai_api_server/completions/helpers.py +81 -0
- agentpool_server/openai_api_server/completions/models.py +98 -0
- agentpool_server/openai_api_server/responses/__init__.py +1 -0
- agentpool_server/openai_api_server/responses/helpers.py +74 -0
- agentpool_server/openai_api_server/responses/models.py +96 -0
- agentpool_server/openai_api_server/server.py +242 -0
- agentpool_server/py.typed +0 -0
- agentpool_storage/__init__.py +9 -0
- agentpool_storage/base.py +310 -0
- agentpool_storage/file_provider.py +378 -0
- agentpool_storage/formatters.py +129 -0
- agentpool_storage/memory_provider.py +396 -0
- agentpool_storage/models.py +108 -0
- agentpool_storage/py.typed +0 -0
- agentpool_storage/session_store.py +262 -0
- agentpool_storage/sql_provider/__init__.py +21 -0
- agentpool_storage/sql_provider/cli.py +146 -0
- agentpool_storage/sql_provider/models.py +249 -0
- agentpool_storage/sql_provider/queries.py +15 -0
- agentpool_storage/sql_provider/sql_provider.py +444 -0
- agentpool_storage/sql_provider/utils.py +234 -0
- agentpool_storage/text_log_provider.py +275 -0
- agentpool_toolsets/__init__.py +15 -0
- agentpool_toolsets/builtin/__init__.py +33 -0
- agentpool_toolsets/builtin/agent_management.py +239 -0
- agentpool_toolsets/builtin/chain.py +288 -0
- agentpool_toolsets/builtin/code.py +398 -0
- agentpool_toolsets/builtin/debug.py +291 -0
- agentpool_toolsets/builtin/execution_environment.py +381 -0
- agentpool_toolsets/builtin/file_edit/__init__.py +11 -0
- agentpool_toolsets/builtin/file_edit/file_edit.py +747 -0
- agentpool_toolsets/builtin/file_edit/fuzzy_matcher/__init__.py +5 -0
- agentpool_toolsets/builtin/file_edit/fuzzy_matcher/example_usage.py +311 -0
- agentpool_toolsets/builtin/file_edit/fuzzy_matcher/streaming_fuzzy_matcher.py +443 -0
- agentpool_toolsets/builtin/history.py +36 -0
- agentpool_toolsets/builtin/integration.py +85 -0
- agentpool_toolsets/builtin/skills.py +77 -0
- agentpool_toolsets/builtin/subagent_tools.py +324 -0
- agentpool_toolsets/builtin/tool_management.py +90 -0
- agentpool_toolsets/builtin/user_interaction.py +52 -0
- agentpool_toolsets/builtin/workers.py +128 -0
- agentpool_toolsets/composio_toolset.py +96 -0
- agentpool_toolsets/config_creation.py +192 -0
- agentpool_toolsets/entry_points.py +47 -0
- agentpool_toolsets/fsspec_toolset/__init__.py +7 -0
- agentpool_toolsets/fsspec_toolset/diagnostics.py +115 -0
- agentpool_toolsets/fsspec_toolset/grep.py +450 -0
- agentpool_toolsets/fsspec_toolset/helpers.py +631 -0
- agentpool_toolsets/fsspec_toolset/streaming_diff_parser.py +249 -0
- agentpool_toolsets/fsspec_toolset/toolset.py +1384 -0
- agentpool_toolsets/mcp_run_toolset.py +61 -0
- agentpool_toolsets/notifications.py +146 -0
- agentpool_toolsets/openapi.py +118 -0
- agentpool_toolsets/py.typed +0 -0
- agentpool_toolsets/search_toolset.py +202 -0
- agentpool_toolsets/semantic_memory_toolset.py +536 -0
- agentpool_toolsets/streaming_tools.py +265 -0
- agentpool_toolsets/vfs_toolset.py +124 -0
|
@@ -0,0 +1,338 @@
|
|
|
1
|
+
"""ACP-based input provider for agent interactions."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import TYPE_CHECKING, Any
|
|
6
|
+
import webbrowser
|
|
7
|
+
|
|
8
|
+
from mcp import types
|
|
9
|
+
|
|
10
|
+
from acp import AllowedOutcome, PermissionOption
|
|
11
|
+
from acp.utils import DEFAULT_PERMISSION_OPTIONS
|
|
12
|
+
from agentpool.log import get_logger
|
|
13
|
+
from agentpool.ui.base import InputProvider
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
if TYPE_CHECKING:
|
|
17
|
+
from acp import RequestPermissionResponse
|
|
18
|
+
from agentpool.agents.context import ConfirmationResult
|
|
19
|
+
from agentpool.messaging import ChatMessage
|
|
20
|
+
from agentpool.messaging.context import NodeContext
|
|
21
|
+
from agentpool.tools.base import Tool
|
|
22
|
+
from agentpool_server.acp_server.session import ACPSession
|
|
23
|
+
|
|
24
|
+
logger = get_logger(__name__)
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def _create_enum_elicitation_options(schema: dict[str, Any]) -> list[PermissionOption] | None:
|
|
28
|
+
"""Create permission options for enum elicitation.
|
|
29
|
+
|
|
30
|
+
max 4 options (not more ids available)
|
|
31
|
+
"""
|
|
32
|
+
enum_values = schema.get("enum", [])
|
|
33
|
+
enum_names = schema.get("enumNames", enum_values) # Use enumNames if available
|
|
34
|
+
# Limit to 3 choices + cancel to keep ACP UI reasonable
|
|
35
|
+
if len(enum_values) > 3: # noqa: PLR2004
|
|
36
|
+
logger.warning("Enum truncated for UI", enum_count=len(enum_values), showing_count=3)
|
|
37
|
+
enum_values = enum_values[:3]
|
|
38
|
+
enum_names = enum_names[:3]
|
|
39
|
+
|
|
40
|
+
if not enum_values:
|
|
41
|
+
return None
|
|
42
|
+
|
|
43
|
+
options = []
|
|
44
|
+
for i, (value, name) in enumerate(zip(enum_values, enum_names, strict=False)):
|
|
45
|
+
opt_id = f"enum_{i}_{value}"
|
|
46
|
+
option = PermissionOption(option_id=opt_id, name=str(name), kind="allow_once")
|
|
47
|
+
options.append(option)
|
|
48
|
+
# Add cancel option
|
|
49
|
+
options.append(PermissionOption(option_id="cancel", name="Cancel", kind="reject_always"))
|
|
50
|
+
|
|
51
|
+
return options
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def _is_boolean_schema(schema: dict[str, Any]) -> bool:
|
|
55
|
+
"""Check if the elicitation schema is requesting a boolean value."""
|
|
56
|
+
# Direct boolean type
|
|
57
|
+
if schema.get("type") == "boolean":
|
|
58
|
+
return True
|
|
59
|
+
|
|
60
|
+
# Check if it's an object with a single boolean property (common pattern)
|
|
61
|
+
if schema.get("type") == "object":
|
|
62
|
+
properties = schema.get("properties", {})
|
|
63
|
+
if len(properties) == 1:
|
|
64
|
+
prop_schema = next(iter(properties.values()))
|
|
65
|
+
return bool(prop_schema.get("type") == "boolean")
|
|
66
|
+
|
|
67
|
+
return False
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def _is_enum_schema(schema: dict[str, Any]) -> bool:
|
|
71
|
+
"""Check if the elicitation schema is requesting an enum value."""
|
|
72
|
+
return schema.get("type") == "string" and "enum" in schema
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def _create_boolean_elicitation_options() -> list[PermissionOption]:
|
|
76
|
+
"""Create permission options for boolean elicitation (Yes/No)."""
|
|
77
|
+
return [
|
|
78
|
+
PermissionOption(option_id="true", name="Yes", kind="allow_once"),
|
|
79
|
+
PermissionOption(option_id="false", name="No", kind="reject_once"),
|
|
80
|
+
PermissionOption(option_id="cancel", name="Cancel", kind="reject_always"),
|
|
81
|
+
]
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
class ACPInputProvider(InputProvider):
|
|
85
|
+
"""Input provider that uses ACP session for user interactions.
|
|
86
|
+
|
|
87
|
+
This provider enables tool confirmation and elicitation requests
|
|
88
|
+
through the ACP protocol, allowing clients to interact with agents
|
|
89
|
+
for permission requests and additional input.
|
|
90
|
+
"""
|
|
91
|
+
|
|
92
|
+
def __init__(self, session: ACPSession) -> None:
|
|
93
|
+
"""Initialize ACP input provider.
|
|
94
|
+
|
|
95
|
+
Args:
|
|
96
|
+
session: Active ACP session for handling requests
|
|
97
|
+
"""
|
|
98
|
+
self.session = session
|
|
99
|
+
self._tool_approvals: dict[str, str] = {} # tool_name -> "allow_always" | "reject_always"
|
|
100
|
+
|
|
101
|
+
async def get_tool_confirmation(
|
|
102
|
+
self,
|
|
103
|
+
context: NodeContext[Any],
|
|
104
|
+
tool: Tool,
|
|
105
|
+
args: dict[str, Any],
|
|
106
|
+
message_history: list[ChatMessage[Any]] | None = None,
|
|
107
|
+
) -> ConfirmationResult:
|
|
108
|
+
"""Get tool execution confirmation via ACP request permission.
|
|
109
|
+
|
|
110
|
+
Uses the ACP session's request_permission mechanism to ask
|
|
111
|
+
the client for confirmation before executing the tool.
|
|
112
|
+
|
|
113
|
+
Args:
|
|
114
|
+
context: Current node context
|
|
115
|
+
tool: Information about the tool to be executed
|
|
116
|
+
args: Tool arguments that will be passed to the tool
|
|
117
|
+
message_history: Optional conversation history
|
|
118
|
+
|
|
119
|
+
Returns:
|
|
120
|
+
Confirmation result indicating whether to allow, skip, or abort
|
|
121
|
+
"""
|
|
122
|
+
try:
|
|
123
|
+
# Check if we have a standing approval/rejection for this tool
|
|
124
|
+
if tool.name in self._tool_approvals:
|
|
125
|
+
standing_decision = self._tool_approvals[tool.name]
|
|
126
|
+
if standing_decision == "allow_always":
|
|
127
|
+
logger.debug("Auto-allowing tool", tool_name=tool.name, reason="allow_always")
|
|
128
|
+
return "allow"
|
|
129
|
+
if standing_decision == "reject_always":
|
|
130
|
+
logger.debug("Auto-rejecting tool", tool_name=tool.name, reason="reject_always")
|
|
131
|
+
return "skip"
|
|
132
|
+
|
|
133
|
+
# Create a descriptive title for the permission request
|
|
134
|
+
args_str = ", ".join(f"{k}={v}" for k, v in args.items())
|
|
135
|
+
response = await self.session.requests.request_permission(
|
|
136
|
+
tool_call_id=f"{tool.name}_{hash(frozenset(args.items()))}",
|
|
137
|
+
title=f"Execute tool {tool.name!r} with args: {args_str}",
|
|
138
|
+
options=DEFAULT_PERMISSION_OPTIONS,
|
|
139
|
+
)
|
|
140
|
+
# Map ACP permission response to our confirmation result
|
|
141
|
+
if isinstance(response.outcome, AllowedOutcome):
|
|
142
|
+
return self._handle_permission_response(response.outcome.option_id, tool.name)
|
|
143
|
+
if response.outcome.outcome == "cancelled":
|
|
144
|
+
return "skip"
|
|
145
|
+
# Handle other outcomes
|
|
146
|
+
logger.warning("Unexpected permission outcome", outcome=response.outcome.outcome)
|
|
147
|
+
|
|
148
|
+
except Exception:
|
|
149
|
+
logger.exception("Failed to get tool confirmation")
|
|
150
|
+
# Default to abort on error to be safe
|
|
151
|
+
return "abort_run"
|
|
152
|
+
else:
|
|
153
|
+
return "abort_run"
|
|
154
|
+
|
|
155
|
+
def _handle_permission_response(self, option_id: str, tool_name: str) -> ConfirmationResult:
|
|
156
|
+
"""Handle permission response and update tool approval state."""
|
|
157
|
+
match option_id:
|
|
158
|
+
case "allow-once":
|
|
159
|
+
return "allow"
|
|
160
|
+
case "allow-always":
|
|
161
|
+
self._tool_approvals[tool_name] = "allow_always"
|
|
162
|
+
logger.info("Tool approval set", tool_name=tool_name, approval="allow_always")
|
|
163
|
+
return "allow"
|
|
164
|
+
case "reject-once":
|
|
165
|
+
return "skip"
|
|
166
|
+
case "reject-always":
|
|
167
|
+
self._tool_approvals[tool_name] = "reject_always"
|
|
168
|
+
logger.info("Tool approval set", tool_name=tool_name, approval="reject_always")
|
|
169
|
+
return "skip"
|
|
170
|
+
case _:
|
|
171
|
+
logger.warning("Unknown permission option", option_id=option_id)
|
|
172
|
+
return "abort_run"
|
|
173
|
+
|
|
174
|
+
async def get_elicitation( # noqa: PLR0911
|
|
175
|
+
self,
|
|
176
|
+
params: types.ElicitRequestParams,
|
|
177
|
+
) -> types.ElicitResult | types.ErrorData:
|
|
178
|
+
"""Get user response to elicitation request with basic schema support.
|
|
179
|
+
|
|
180
|
+
Currently supports boolean schemas via ACP permission options.
|
|
181
|
+
Other schemas fall back to accept/decline options.
|
|
182
|
+
|
|
183
|
+
Args:
|
|
184
|
+
params: MCP elicit request parameters
|
|
185
|
+
|
|
186
|
+
Returns:
|
|
187
|
+
Elicit result with user's response or error data
|
|
188
|
+
"""
|
|
189
|
+
try:
|
|
190
|
+
# Handle URL mode elicitation (OAuth, credentials, payments)
|
|
191
|
+
if isinstance(params, types.ElicitRequestURLParams):
|
|
192
|
+
msg = "URL elicitation request"
|
|
193
|
+
elicit_id = params.elicitationId
|
|
194
|
+
logger.info(msg, message=params.message, url=params.url, elicitation_id=elicit_id)
|
|
195
|
+
tool_call_id = f"elicit_url_{elicit_id}"
|
|
196
|
+
title = f"URL Authorization: {params.message}"
|
|
197
|
+
url_options = [
|
|
198
|
+
PermissionOption(option_id="accept", name="Open URL", kind="allow_once"),
|
|
199
|
+
PermissionOption(option_id="decline", name="Decline", kind="reject_once"),
|
|
200
|
+
]
|
|
201
|
+
response = await self.session.requests.request_permission(
|
|
202
|
+
tool_call_id=tool_call_id,
|
|
203
|
+
title=title,
|
|
204
|
+
options=url_options,
|
|
205
|
+
)
|
|
206
|
+
if isinstance(response.outcome, AllowedOutcome):
|
|
207
|
+
if response.outcome.option_id == "accept":
|
|
208
|
+
webbrowser.open(params.url)
|
|
209
|
+
return types.ElicitResult(action="accept")
|
|
210
|
+
return types.ElicitResult(action="decline")
|
|
211
|
+
return types.ElicitResult(action="cancel")
|
|
212
|
+
|
|
213
|
+
# Form mode elicitation
|
|
214
|
+
schema = params.requestedSchema
|
|
215
|
+
logger.info("Elicitation request", message=params.message, schema=schema)
|
|
216
|
+
tool_call_id = f"elicit_{hash(params.message)}"
|
|
217
|
+
title = f"Elicitation: {params.message}"
|
|
218
|
+
|
|
219
|
+
if _is_boolean_schema(schema):
|
|
220
|
+
options: list[PermissionOption] | None = _create_boolean_elicitation_options()
|
|
221
|
+
response = await self.session.requests.request_permission(
|
|
222
|
+
tool_call_id=tool_call_id,
|
|
223
|
+
title=title,
|
|
224
|
+
options=options,
|
|
225
|
+
)
|
|
226
|
+
return self._handle_boolean_elicitation_response(response, schema)
|
|
227
|
+
if _is_enum_schema(schema) and (options := _create_enum_elicitation_options(schema)):
|
|
228
|
+
response = await self.session.requests.request_permission(
|
|
229
|
+
tool_call_id=tool_call_id,
|
|
230
|
+
title=title,
|
|
231
|
+
options=options,
|
|
232
|
+
)
|
|
233
|
+
return _handle_enum_elicitation_response(response, schema)
|
|
234
|
+
|
|
235
|
+
options = [
|
|
236
|
+
PermissionOption(option_id="accept", name="Accept", kind="allow_once"),
|
|
237
|
+
PermissionOption(option_id="decline", name="Decline", kind="reject_once"),
|
|
238
|
+
]
|
|
239
|
+
response = await self.session.requests.request_permission(
|
|
240
|
+
tool_call_id=tool_call_id,
|
|
241
|
+
title=title,
|
|
242
|
+
options=options,
|
|
243
|
+
)
|
|
244
|
+
|
|
245
|
+
# Convert permission response to elicitation result
|
|
246
|
+
if isinstance(response.outcome, AllowedOutcome):
|
|
247
|
+
if response.outcome.option_id == "accept":
|
|
248
|
+
# For non-boolean schemas, return empty content
|
|
249
|
+
return types.ElicitResult(action="accept", content={})
|
|
250
|
+
return types.ElicitResult(action="decline")
|
|
251
|
+
if response.outcome.outcome == "cancelled":
|
|
252
|
+
return types.ElicitResult(action="cancel")
|
|
253
|
+
return types.ElicitResult(action="cancel")
|
|
254
|
+
|
|
255
|
+
except Exception as e:
|
|
256
|
+
logger.exception("Failed to handle elicitation")
|
|
257
|
+
return types.ErrorData(code=types.INTERNAL_ERROR, message=f"Elicitation failed: {e}")
|
|
258
|
+
|
|
259
|
+
def _handle_boolean_elicitation_response( # noqa: PLR0911
|
|
260
|
+
self, response: RequestPermissionResponse, schema: dict[str, Any]
|
|
261
|
+
) -> types.ElicitResult | types.ErrorData:
|
|
262
|
+
"""Handle ACP response for boolean elicitation."""
|
|
263
|
+
if isinstance(response.outcome, AllowedOutcome):
|
|
264
|
+
if response.outcome.option_id == "true":
|
|
265
|
+
# Check if we need to wrap in object structure
|
|
266
|
+
if schema.get("type") == "object":
|
|
267
|
+
properties = schema.get("properties", {})
|
|
268
|
+
if len(properties) == 1:
|
|
269
|
+
prop_name = next(iter(properties.keys()))
|
|
270
|
+
return types.ElicitResult(action="accept", content={prop_name: True})
|
|
271
|
+
return types.ElicitResult(action="accept", content={"value": True})
|
|
272
|
+
if response.outcome.option_id == "false":
|
|
273
|
+
# Check if we need to wrap in object structure
|
|
274
|
+
if schema.get("type") == "object":
|
|
275
|
+
properties = schema.get("properties", {})
|
|
276
|
+
if len(properties) == 1:
|
|
277
|
+
prop_name = next(iter(properties.keys()))
|
|
278
|
+
return types.ElicitResult(action="accept", content={prop_name: False})
|
|
279
|
+
return types.ElicitResult(action="accept", content={"value": False})
|
|
280
|
+
if response.outcome.option_id == "cancel":
|
|
281
|
+
return types.ElicitResult(action="cancel")
|
|
282
|
+
|
|
283
|
+
# Handle cancelled outcome
|
|
284
|
+
if response.outcome.outcome == "cancelled":
|
|
285
|
+
return types.ElicitResult(action="cancel")
|
|
286
|
+
|
|
287
|
+
return types.ElicitResult(action="cancel")
|
|
288
|
+
|
|
289
|
+
def clear_tool_approvals(self) -> None:
|
|
290
|
+
"""Clear all stored tool approval decisions.
|
|
291
|
+
|
|
292
|
+
This resets the "allow_always" and "reject_always" states,
|
|
293
|
+
so tools will ask for permission again.
|
|
294
|
+
"""
|
|
295
|
+
approval_count = len(self._tool_approvals)
|
|
296
|
+
self._tool_approvals.clear()
|
|
297
|
+
logger.info("Cleared tool approval decisions", count=approval_count)
|
|
298
|
+
|
|
299
|
+
def get_tool_approval_state(self) -> dict[str, str]:
|
|
300
|
+
"""Get current tool approval state for debugging/inspection.
|
|
301
|
+
|
|
302
|
+
Returns:
|
|
303
|
+
Dictionary mapping tool names to their approval state
|
|
304
|
+
("allow_always" or "reject_always")
|
|
305
|
+
"""
|
|
306
|
+
return self._tool_approvals.copy()
|
|
307
|
+
|
|
308
|
+
|
|
309
|
+
def _handle_enum_elicitation_response(
|
|
310
|
+
response: RequestPermissionResponse, schema: dict[str, Any]
|
|
311
|
+
) -> types.ElicitResult | types.ErrorData:
|
|
312
|
+
"""Handle ACP response for enum elicitation."""
|
|
313
|
+
from mcp import types
|
|
314
|
+
|
|
315
|
+
if isinstance(response.outcome, AllowedOutcome):
|
|
316
|
+
option_id = response.outcome.option_id
|
|
317
|
+
|
|
318
|
+
if option_id == "cancel":
|
|
319
|
+
return types.ElicitResult(action="cancel")
|
|
320
|
+
|
|
321
|
+
# Extract enum value from option_id format: "enum_{index}_{value}"
|
|
322
|
+
if option_id.startswith("enum_"):
|
|
323
|
+
try:
|
|
324
|
+
parts = option_id.split("_", 2) # Split into ["enum", index, value]
|
|
325
|
+
if len(parts) >= 3: # noqa: PLR2004
|
|
326
|
+
enum_index = int(parts[1])
|
|
327
|
+
enum_values = schema.get("enum", [])
|
|
328
|
+
if 0 <= enum_index < len(enum_values):
|
|
329
|
+
selected_value = enum_values[enum_index]
|
|
330
|
+
return types.ElicitResult(action="accept", content=selected_value)
|
|
331
|
+
except (ValueError, IndexError):
|
|
332
|
+
pass
|
|
333
|
+
|
|
334
|
+
# Fallback if parsing fails
|
|
335
|
+
logger.warning("Failed to parse enum option_id", option_id=option_id)
|
|
336
|
+
return types.ElicitResult(action="cancel")
|
|
337
|
+
|
|
338
|
+
return types.ElicitResult(action="cancel")
|
|
@@ -0,0 +1,288 @@
|
|
|
1
|
+
"""ACP (Agent Client Protocol) server implementation for agentpool.
|
|
2
|
+
|
|
3
|
+
This module provides the main server class for exposing AgentPool via
|
|
4
|
+
the Agent Client Protocol.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
import asyncio
|
|
10
|
+
from datetime import timedelta
|
|
11
|
+
import functools
|
|
12
|
+
from typing import TYPE_CHECKING, Any, Self
|
|
13
|
+
|
|
14
|
+
import logfire
|
|
15
|
+
|
|
16
|
+
from acp import AgentSideConnection
|
|
17
|
+
from acp.stdio import stdio_streams
|
|
18
|
+
from agentpool import AgentPool
|
|
19
|
+
from agentpool.log import get_logger
|
|
20
|
+
from agentpool.models.manifest import AgentsManifest
|
|
21
|
+
from agentpool_server import BaseServer
|
|
22
|
+
from agentpool_server.acp_server.acp_agent import AgentPoolACPAgent
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
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
|
+
from upathtools import JoinablePathLike
|
|
31
|
+
|
|
32
|
+
from acp.schema import ModelInfo as ACPModelInfo
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
logger = get_logger(__name__)
|
|
36
|
+
|
|
37
|
+
|
|
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
|
+
class ACPServer(BaseServer):
|
|
62
|
+
"""ACP (Agent Client Protocol) server for agentpool using external library.
|
|
63
|
+
|
|
64
|
+
Provides a bridge between agentpool's Agent system and the standard ACP
|
|
65
|
+
JSON-RPC protocol using the external acp library for robust communication.
|
|
66
|
+
|
|
67
|
+
The actual client communication happens via the AgentSideConnection created
|
|
68
|
+
when start() is called, which communicates with the external process over stdio.
|
|
69
|
+
"""
|
|
70
|
+
|
|
71
|
+
def __init__(
|
|
72
|
+
self,
|
|
73
|
+
pool: AgentPool[Any],
|
|
74
|
+
*,
|
|
75
|
+
name: str | None = None,
|
|
76
|
+
file_access: bool = True,
|
|
77
|
+
terminal_access: bool = True,
|
|
78
|
+
providers: list[ProviderType] | None = None,
|
|
79
|
+
debug_messages: bool = False,
|
|
80
|
+
debug_file: str | None = None,
|
|
81
|
+
debug_commands: bool = False,
|
|
82
|
+
agent: str | None = None,
|
|
83
|
+
load_skills: bool = True,
|
|
84
|
+
config_path: str | None = None,
|
|
85
|
+
) -> None:
|
|
86
|
+
"""Initialize ACP server with configuration.
|
|
87
|
+
|
|
88
|
+
Args:
|
|
89
|
+
pool: AgentPool containing available agents
|
|
90
|
+
name: Optional Server name (auto-generated if None)
|
|
91
|
+
file_access: Whether to support file access operations
|
|
92
|
+
terminal_access: Whether to support terminal access operations
|
|
93
|
+
providers: List of providers to use for model discovery (None = openrouter)
|
|
94
|
+
debug_messages: Whether to enable debug message logging
|
|
95
|
+
debug_file: File path for debug message logging
|
|
96
|
+
debug_commands: Whether to enable debug slash commands for testing
|
|
97
|
+
agent: Optional specific agent name to use (defaults to first agent)
|
|
98
|
+
load_skills: Whether to load client-side skills from .claude/skills
|
|
99
|
+
config_path: Path to the configuration file (for tracking/hot-switching)
|
|
100
|
+
"""
|
|
101
|
+
super().__init__(pool, name=name, raise_exceptions=True)
|
|
102
|
+
self.file_access = file_access
|
|
103
|
+
self.terminal_access = terminal_access
|
|
104
|
+
self.providers = providers or ["openai", "anthropic", "gemini"]
|
|
105
|
+
self.debug_messages = debug_messages
|
|
106
|
+
self.debug_file = debug_file
|
|
107
|
+
self.debug_commands = debug_commands
|
|
108
|
+
self.agent = agent
|
|
109
|
+
self.load_skills = load_skills
|
|
110
|
+
self.config_path = config_path
|
|
111
|
+
|
|
112
|
+
self._available_models: list[ACPModelInfo] = []
|
|
113
|
+
self._models_initialized = False
|
|
114
|
+
|
|
115
|
+
@classmethod
|
|
116
|
+
def from_config(
|
|
117
|
+
cls,
|
|
118
|
+
config_path: JoinablePathLike,
|
|
119
|
+
*,
|
|
120
|
+
file_access: bool = True,
|
|
121
|
+
terminal_access: bool = True,
|
|
122
|
+
providers: list[ProviderType] | None = None,
|
|
123
|
+
debug_messages: bool = False,
|
|
124
|
+
debug_file: str | None = None,
|
|
125
|
+
debug_commands: bool = False,
|
|
126
|
+
agent: str | None = None,
|
|
127
|
+
load_skills: bool = True,
|
|
128
|
+
) -> Self:
|
|
129
|
+
"""Create ACP server from existing agentpool configuration.
|
|
130
|
+
|
|
131
|
+
Args:
|
|
132
|
+
config_path: Path to agentpool YAML config file
|
|
133
|
+
file_access: Enable file system access
|
|
134
|
+
terminal_access: Enable terminal access
|
|
135
|
+
providers: List of provider types to use for model discovery
|
|
136
|
+
debug_messages: Enable saving JSON messages to file
|
|
137
|
+
debug_file: Path to debug file
|
|
138
|
+
debug_commands: Enable debug slash commands for testing
|
|
139
|
+
agent: Optional specific agent name to use (defaults to first agent)
|
|
140
|
+
load_skills: Whether to load client-side skills from .claude/skills
|
|
141
|
+
|
|
142
|
+
Returns:
|
|
143
|
+
Configured ACP server instance with agent pool from config
|
|
144
|
+
"""
|
|
145
|
+
manifest = AgentsManifest.from_file(config_path)
|
|
146
|
+
pool = AgentPool(manifest=manifest)
|
|
147
|
+
server = cls(
|
|
148
|
+
pool,
|
|
149
|
+
file_access=file_access,
|
|
150
|
+
terminal_access=terminal_access,
|
|
151
|
+
providers=providers,
|
|
152
|
+
debug_messages=debug_messages,
|
|
153
|
+
debug_file=debug_file or "acp-debug.jsonl" if debug_messages else None,
|
|
154
|
+
debug_commands=debug_commands,
|
|
155
|
+
agent=agent,
|
|
156
|
+
load_skills=load_skills,
|
|
157
|
+
config_path=str(config_path),
|
|
158
|
+
)
|
|
159
|
+
agent_names = list(server.pool.agents.keys())
|
|
160
|
+
|
|
161
|
+
# Validate specified agent exists if provided
|
|
162
|
+
if agent and agent not in agent_names:
|
|
163
|
+
msg = f"Specified agent {agent!r} not found in config. Available agents: {agent_names}"
|
|
164
|
+
raise ValueError(msg)
|
|
165
|
+
|
|
166
|
+
server.log.info("Created ACP server with agent pool", agent_names=agent_names)
|
|
167
|
+
if agent:
|
|
168
|
+
server.log.info("ACP session agent", agent=agent)
|
|
169
|
+
return server
|
|
170
|
+
|
|
171
|
+
async def _start_async(self) -> None:
|
|
172
|
+
"""Start the ACP server (blocking async - runs until stopped)."""
|
|
173
|
+
agent_names = list(self.pool.agents.keys())
|
|
174
|
+
self.log.info("Starting ACP server on stdio", agent_names=agent_names)
|
|
175
|
+
await self._initialize_models() # Initialize models on first run
|
|
176
|
+
create_acp_agent = functools.partial(
|
|
177
|
+
AgentPoolACPAgent,
|
|
178
|
+
agent_pool=self.pool,
|
|
179
|
+
available_models=self._available_models,
|
|
180
|
+
file_access=self.file_access,
|
|
181
|
+
terminal_access=self.terminal_access,
|
|
182
|
+
debug_commands=self.debug_commands,
|
|
183
|
+
default_agent=self.agent,
|
|
184
|
+
load_skills=self.load_skills,
|
|
185
|
+
server=self,
|
|
186
|
+
)
|
|
187
|
+
reader, writer = await stdio_streams()
|
|
188
|
+
file = self.debug_file if self.debug_messages else None
|
|
189
|
+
conn = AgentSideConnection(create_acp_agent, writer, reader, debug_file=file)
|
|
190
|
+
self.log.info("ACP server started", file=self.file_access, terminal=self.terminal_access)
|
|
191
|
+
try: # Keep the connection alive until shutdown
|
|
192
|
+
await self._shutdown_event.wait()
|
|
193
|
+
except asyncio.CancelledError:
|
|
194
|
+
self.log.info("ACP server shutdown requested")
|
|
195
|
+
raise
|
|
196
|
+
except KeyboardInterrupt:
|
|
197
|
+
self.log.info("ACP server shutdown requested")
|
|
198
|
+
except Exception:
|
|
199
|
+
self.log.exception("Connection receive task failed")
|
|
200
|
+
finally:
|
|
201
|
+
await conn.close()
|
|
202
|
+
|
|
203
|
+
async def swap_pool(
|
|
204
|
+
self,
|
|
205
|
+
config_path: str,
|
|
206
|
+
agent: str | None = None,
|
|
207
|
+
) -> list[str]:
|
|
208
|
+
"""Swap the current pool with a new one from config.
|
|
209
|
+
|
|
210
|
+
This method handles the full lifecycle of swapping pools:
|
|
211
|
+
1. Validates the new configuration
|
|
212
|
+
2. Creates and initializes the new pool
|
|
213
|
+
3. Cleans up the old pool
|
|
214
|
+
4. Updates internal references
|
|
215
|
+
|
|
216
|
+
Args:
|
|
217
|
+
config_path: Path to the new agent configuration file
|
|
218
|
+
agent: Optional specific agent to use as default
|
|
219
|
+
|
|
220
|
+
Returns:
|
|
221
|
+
List of agent names in the new pool
|
|
222
|
+
|
|
223
|
+
Raises:
|
|
224
|
+
ValueError: If config is invalid or specified agent not found
|
|
225
|
+
FileNotFoundError: If config file doesn't exist
|
|
226
|
+
"""
|
|
227
|
+
# 1. Parse and validate new config before touching current pool
|
|
228
|
+
self.log.info("Loading new pool configuration", config_path=config_path)
|
|
229
|
+
new_manifest = AgentsManifest.from_file(config_path)
|
|
230
|
+
new_pool = AgentPool(manifest=new_manifest)
|
|
231
|
+
|
|
232
|
+
# 2. Validate agent exists in new pool if specified
|
|
233
|
+
agent_names = list(new_pool.all_agents.keys())
|
|
234
|
+
if not agent_names:
|
|
235
|
+
msg = "New configuration contains no agents"
|
|
236
|
+
raise ValueError(msg)
|
|
237
|
+
|
|
238
|
+
if agent and agent not in agent_names:
|
|
239
|
+
msg = f"Agent {agent!r} not found in new config. Available: {agent_names}"
|
|
240
|
+
raise ValueError(msg)
|
|
241
|
+
|
|
242
|
+
# 3. Enter new pool context first (so we can roll back if it fails)
|
|
243
|
+
try:
|
|
244
|
+
await new_pool.__aenter__()
|
|
245
|
+
except Exception as e:
|
|
246
|
+
self.log.exception("Failed to initialize new pool")
|
|
247
|
+
msg = f"Failed to initialize new pool: {e}"
|
|
248
|
+
raise ValueError(msg) from e
|
|
249
|
+
|
|
250
|
+
# 4. Exit old pool context
|
|
251
|
+
old_pool = self.pool
|
|
252
|
+
try:
|
|
253
|
+
await old_pool.__aexit__(None, None, None)
|
|
254
|
+
except Exception:
|
|
255
|
+
self.log.exception("Error closing old pool (continuing with swap)")
|
|
256
|
+
|
|
257
|
+
# 5. Update references
|
|
258
|
+
self.pool = new_pool
|
|
259
|
+
self.agent = agent
|
|
260
|
+
self.config_path = config_path
|
|
261
|
+
|
|
262
|
+
self.log.info("Pool swapped successfully", agent_names=agent_names, default_agent=agent)
|
|
263
|
+
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
|