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,294 @@
|
|
|
1
|
+
"""ACP to native event converters.
|
|
2
|
+
|
|
3
|
+
This module provides conversion from ACP session updates to native agentpool
|
|
4
|
+
streaming events, enabling ACPAgent to yield the same event types as native agents.
|
|
5
|
+
|
|
6
|
+
This is the reverse of the conversion done in acp_server/session.py handle_event().
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
import base64
|
|
12
|
+
from typing import TYPE_CHECKING, Any, overload
|
|
13
|
+
|
|
14
|
+
from pydantic_ai import (
|
|
15
|
+
AudioUrl,
|
|
16
|
+
BinaryContent,
|
|
17
|
+
BinaryImage,
|
|
18
|
+
DocumentUrl,
|
|
19
|
+
ImageUrl,
|
|
20
|
+
PartDeltaEvent,
|
|
21
|
+
TextPartDelta,
|
|
22
|
+
ThinkingPartDelta,
|
|
23
|
+
VideoUrl,
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
from acp.schema import (
|
|
27
|
+
AgentMessageChunk,
|
|
28
|
+
AgentPlanUpdate,
|
|
29
|
+
AgentThoughtChunk,
|
|
30
|
+
AudioContentBlock,
|
|
31
|
+
BlobResourceContents,
|
|
32
|
+
ContentToolCallContent,
|
|
33
|
+
EmbeddedResourceContentBlock,
|
|
34
|
+
FileEditToolCallContent,
|
|
35
|
+
ImageContentBlock,
|
|
36
|
+
ResourceContentBlock,
|
|
37
|
+
TerminalToolCallContent,
|
|
38
|
+
TextContentBlock,
|
|
39
|
+
ToolCallProgress,
|
|
40
|
+
ToolCallStart,
|
|
41
|
+
UserMessageChunk,
|
|
42
|
+
)
|
|
43
|
+
from agentpool.agents.events import (
|
|
44
|
+
DiffContentItem,
|
|
45
|
+
LocationContentItem,
|
|
46
|
+
PlanUpdateEvent,
|
|
47
|
+
TerminalContentItem,
|
|
48
|
+
ToolCallProgressEvent,
|
|
49
|
+
ToolCallStartEvent,
|
|
50
|
+
)
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
if TYPE_CHECKING:
|
|
54
|
+
from collections.abc import Sequence
|
|
55
|
+
|
|
56
|
+
from pydantic_ai import UserContent
|
|
57
|
+
|
|
58
|
+
from acp.schema import ContentBlock, SessionUpdate
|
|
59
|
+
from acp.schema.mcp import HttpMcpServer, McpServer, SseMcpServer, StdioMcpServer
|
|
60
|
+
from acp.schema.tool_call import ToolCallContent, ToolCallLocation
|
|
61
|
+
from agentpool.agents.events import RichAgentStreamEvent, ToolCallContentItem
|
|
62
|
+
from agentpool_config.mcp_server import (
|
|
63
|
+
MCPServerConfig,
|
|
64
|
+
SSEMCPServerConfig,
|
|
65
|
+
StdioMCPServerConfig,
|
|
66
|
+
StreamableHTTPMCPServerConfig,
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def convert_acp_locations(
|
|
71
|
+
locations: list[ToolCallLocation] | None,
|
|
72
|
+
) -> list[LocationContentItem]:
|
|
73
|
+
"""Convert ACP ToolCallLocation list to native LocationContentItem list."""
|
|
74
|
+
return [LocationContentItem(path=loc.path, line=loc.line) for loc in locations or []]
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def convert_acp_content(content: list[ToolCallContent] | None) -> list[ToolCallContentItem]:
|
|
78
|
+
"""Convert ACP ToolCallContent list to native ToolCallContentItem list."""
|
|
79
|
+
if not content:
|
|
80
|
+
return []
|
|
81
|
+
|
|
82
|
+
result: list[ToolCallContentItem] = []
|
|
83
|
+
for item in content:
|
|
84
|
+
match item:
|
|
85
|
+
case TerminalToolCallContent(terminal_id=terminal_id):
|
|
86
|
+
result.append(TerminalContentItem(terminal_id=terminal_id))
|
|
87
|
+
case FileEditToolCallContent(path=path, old_text=old_text, new_text=new_text):
|
|
88
|
+
result.append(DiffContentItem(path=path, old_text=old_text, new_text=new_text))
|
|
89
|
+
case ContentToolCallContent(content=TextContentBlock(text=text)):
|
|
90
|
+
from agentpool.agents.events import TextContentItem
|
|
91
|
+
|
|
92
|
+
result.append(TextContentItem(text=text))
|
|
93
|
+
return result
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
def convert_to_acp_content(prompts: Sequence[UserContent]) -> list[ContentBlock]:
|
|
97
|
+
"""Convert pydantic-ai UserContent to ACP ContentBlock format.
|
|
98
|
+
|
|
99
|
+
Handles text, images, audio, video, and document content types.
|
|
100
|
+
|
|
101
|
+
Args:
|
|
102
|
+
prompts: pydantic-ai UserContent items
|
|
103
|
+
|
|
104
|
+
Returns:
|
|
105
|
+
List of ACP ContentBlock items
|
|
106
|
+
"""
|
|
107
|
+
content_blocks: list[ContentBlock] = []
|
|
108
|
+
|
|
109
|
+
for item in prompts:
|
|
110
|
+
match item:
|
|
111
|
+
case str(text):
|
|
112
|
+
content_blocks.append(TextContentBlock(text=text))
|
|
113
|
+
|
|
114
|
+
case BinaryImage(data=data, media_type=media_type):
|
|
115
|
+
encoded = base64.b64encode(data).decode("utf-8")
|
|
116
|
+
content_blocks.append(ImageContentBlock(data=encoded, mime_type=media_type))
|
|
117
|
+
|
|
118
|
+
case BinaryContent(data=data, media_type=media_type):
|
|
119
|
+
encoded = base64.b64encode(data).decode("utf-8")
|
|
120
|
+
# Handle different media types
|
|
121
|
+
if media_type and media_type.startswith("image/"):
|
|
122
|
+
content_blocks.append(ImageContentBlock(data=encoded, mime_type=media_type))
|
|
123
|
+
elif media_type and media_type.startswith("audio/"):
|
|
124
|
+
content_blocks.append(AudioContentBlock(data=encoded, mime_type=media_type))
|
|
125
|
+
elif media_type == "application/pdf":
|
|
126
|
+
blob_resource = BlobResourceContents(
|
|
127
|
+
blob=encoded,
|
|
128
|
+
mime_type="application/pdf",
|
|
129
|
+
uri=f"data:application/pdf;base64,{encoded[:50]}...",
|
|
130
|
+
)
|
|
131
|
+
content_blocks.append(EmbeddedResourceContentBlock(resource=blob_resource))
|
|
132
|
+
else:
|
|
133
|
+
# Generic binary as embedded resource
|
|
134
|
+
blob_resource = BlobResourceContents(
|
|
135
|
+
blob=encoded,
|
|
136
|
+
mime_type=media_type or "application/octet-stream",
|
|
137
|
+
uri=f"data:{media_type or 'application/octet-stream'};base64,...",
|
|
138
|
+
)
|
|
139
|
+
content_blocks.append(EmbeddedResourceContentBlock(resource=blob_resource))
|
|
140
|
+
|
|
141
|
+
case ImageUrl(url=url, media_type=typ):
|
|
142
|
+
content_blocks.append(
|
|
143
|
+
ResourceContentBlock(uri=url, name="Image", mime_type=typ or "image/jpeg")
|
|
144
|
+
)
|
|
145
|
+
|
|
146
|
+
case AudioUrl(url=url, media_type=media_type):
|
|
147
|
+
content_blocks.append(
|
|
148
|
+
ResourceContentBlock(
|
|
149
|
+
uri=url,
|
|
150
|
+
name="Audio",
|
|
151
|
+
mime_type=media_type or "audio/wav",
|
|
152
|
+
description="Audio content",
|
|
153
|
+
)
|
|
154
|
+
)
|
|
155
|
+
|
|
156
|
+
case DocumentUrl(url=url, media_type=media_type):
|
|
157
|
+
content_blocks.append(
|
|
158
|
+
ResourceContentBlock(
|
|
159
|
+
uri=url,
|
|
160
|
+
name="Document",
|
|
161
|
+
mime_type=media_type or "application/pdf",
|
|
162
|
+
description="Document",
|
|
163
|
+
)
|
|
164
|
+
)
|
|
165
|
+
|
|
166
|
+
case VideoUrl(url=url, media_type=media_type):
|
|
167
|
+
content_blocks.append(
|
|
168
|
+
ResourceContentBlock(
|
|
169
|
+
uri=url,
|
|
170
|
+
name="Video",
|
|
171
|
+
mime_type=media_type or "video/mp4",
|
|
172
|
+
description="Video content",
|
|
173
|
+
)
|
|
174
|
+
)
|
|
175
|
+
|
|
176
|
+
return content_blocks
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
def acp_to_native_event(update: SessionUpdate) -> RichAgentStreamEvent[Any] | None: # noqa: PLR0911
|
|
180
|
+
"""Convert ACP session update to native streaming event.
|
|
181
|
+
|
|
182
|
+
Args:
|
|
183
|
+
update: ACP SessionUpdate from session/update notification
|
|
184
|
+
|
|
185
|
+
Returns:
|
|
186
|
+
Corresponding native event, or None if no mapping exists
|
|
187
|
+
"""
|
|
188
|
+
match update:
|
|
189
|
+
# Text message chunks -> PartDeltaEvent with TextPartDelta
|
|
190
|
+
case AgentMessageChunk(content=TextContentBlock(text=text)):
|
|
191
|
+
return PartDeltaEvent(index=0, delta=TextPartDelta(content_delta=text))
|
|
192
|
+
# Thought chunks -> PartDeltaEvent with ThinkingPartDelta
|
|
193
|
+
case AgentThoughtChunk(content=TextContentBlock(text=text)):
|
|
194
|
+
return PartDeltaEvent(index=0, delta=ThinkingPartDelta(content_delta=text))
|
|
195
|
+
# User message echo - could emit as PartStartEvent if needed
|
|
196
|
+
case UserMessageChunk():
|
|
197
|
+
return None # Usually ignored
|
|
198
|
+
# Tool call start -> ToolCallStartEvent
|
|
199
|
+
case ToolCallStart() as tc:
|
|
200
|
+
return ToolCallStartEvent(
|
|
201
|
+
tool_call_id=tc.tool_call_id,
|
|
202
|
+
tool_name=tc.title, # ACP uses title, not separate tool_name
|
|
203
|
+
title=tc.title,
|
|
204
|
+
kind=tc.kind or "other",
|
|
205
|
+
content=convert_acp_content(list(tc.content) if tc.content else None),
|
|
206
|
+
locations=convert_acp_locations(list(tc.locations) if tc.locations else None),
|
|
207
|
+
raw_input=tc.raw_input or {},
|
|
208
|
+
)
|
|
209
|
+
|
|
210
|
+
# Tool call progress -> ToolCallProgressEvent
|
|
211
|
+
case ToolCallProgress() as tc:
|
|
212
|
+
items = convert_acp_content(list(tc.content) if tc.content else None)
|
|
213
|
+
return ToolCallProgressEvent(
|
|
214
|
+
tool_call_id=tc.tool_call_id,
|
|
215
|
+
status=tc.status or "in_progress",
|
|
216
|
+
title=tc.title,
|
|
217
|
+
items=items,
|
|
218
|
+
message=str(tc.raw_output) if tc.raw_output else None,
|
|
219
|
+
)
|
|
220
|
+
|
|
221
|
+
# Plan update -> PlanUpdateEvent
|
|
222
|
+
case AgentPlanUpdate(entries=acp_entries):
|
|
223
|
+
from agentpool.resource_providers.plan_provider import PlanEntry
|
|
224
|
+
|
|
225
|
+
native_entries = [
|
|
226
|
+
PlanEntry(content=e.content, priority=e.priority, status=e.status)
|
|
227
|
+
for e in acp_entries
|
|
228
|
+
]
|
|
229
|
+
return PlanUpdateEvent(entries=native_entries)
|
|
230
|
+
|
|
231
|
+
case _:
|
|
232
|
+
return None
|
|
233
|
+
|
|
234
|
+
|
|
235
|
+
@overload
|
|
236
|
+
def mcp_config_to_acp(config: StdioMCPServerConfig) -> StdioMcpServer | None: ...
|
|
237
|
+
|
|
238
|
+
|
|
239
|
+
@overload
|
|
240
|
+
def mcp_config_to_acp(config: SSEMCPServerConfig) -> SseMcpServer | None: ...
|
|
241
|
+
|
|
242
|
+
|
|
243
|
+
@overload
|
|
244
|
+
def mcp_config_to_acp(config: StreamableHTTPMCPServerConfig) -> HttpMcpServer | None: ...
|
|
245
|
+
|
|
246
|
+
|
|
247
|
+
@overload
|
|
248
|
+
def mcp_config_to_acp(config: MCPServerConfig) -> McpServer | None: ...
|
|
249
|
+
|
|
250
|
+
|
|
251
|
+
def mcp_config_to_acp(config: MCPServerConfig) -> McpServer | None:
|
|
252
|
+
"""Convert native MCPServerConfig to ACP McpServer format.
|
|
253
|
+
|
|
254
|
+
Args:
|
|
255
|
+
config: agentpool MCP server configuration
|
|
256
|
+
|
|
257
|
+
Returns:
|
|
258
|
+
ACP-compatible McpServer instance, or None if conversion not possible
|
|
259
|
+
"""
|
|
260
|
+
from acp.schema.common import EnvVariable
|
|
261
|
+
from acp.schema.mcp import HttpMcpServer, SseMcpServer, StdioMcpServer
|
|
262
|
+
from agentpool_config.mcp_server import (
|
|
263
|
+
SSEMCPServerConfig,
|
|
264
|
+
StdioMCPServerConfig,
|
|
265
|
+
StreamableHTTPMCPServerConfig,
|
|
266
|
+
)
|
|
267
|
+
|
|
268
|
+
match config:
|
|
269
|
+
case StdioMCPServerConfig(command=command, args=args):
|
|
270
|
+
env_vars = config.get_env_vars() if hasattr(config, "get_env_vars") else {}
|
|
271
|
+
return StdioMcpServer(
|
|
272
|
+
name=config.name or command,
|
|
273
|
+
command=command,
|
|
274
|
+
args=list(args) if args else [],
|
|
275
|
+
env=[EnvVariable(name=k, value=v) for k, v in env_vars.items()],
|
|
276
|
+
)
|
|
277
|
+
case SSEMCPServerConfig(url=url):
|
|
278
|
+
return SseMcpServer(name=config.name or str(url), url=url, headers=[])
|
|
279
|
+
case StreamableHTTPMCPServerConfig(url=url):
|
|
280
|
+
return HttpMcpServer(name=config.name or str(url), url=url, headers=[])
|
|
281
|
+
case _:
|
|
282
|
+
return None
|
|
283
|
+
|
|
284
|
+
|
|
285
|
+
def mcp_configs_to_acp(configs: Sequence[MCPServerConfig]) -> list[McpServer]:
|
|
286
|
+
"""Convert a sequence of MCPServerConfig to ACP McpServer list.
|
|
287
|
+
|
|
288
|
+
Args:
|
|
289
|
+
configs: Sequence of agentpool MCP server configurations
|
|
290
|
+
|
|
291
|
+
Returns:
|
|
292
|
+
List of ACP-compatible McpServer instances (skips unconvertible configs)
|
|
293
|
+
"""
|
|
294
|
+
return [converted for config in configs if (converted := mcp_config_to_acp(config)) is not None]
|
|
@@ -0,0 +1,317 @@
|
|
|
1
|
+
"""ACP Agent - MessageNode wrapping an external ACP subprocess."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import asyncio
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
from typing import TYPE_CHECKING, Any
|
|
8
|
+
import uuid
|
|
9
|
+
|
|
10
|
+
import anyio
|
|
11
|
+
|
|
12
|
+
from acp.client.protocol import Client
|
|
13
|
+
from acp.schema import (
|
|
14
|
+
CreateTerminalResponse,
|
|
15
|
+
KillTerminalCommandResponse,
|
|
16
|
+
ReadTextFileResponse,
|
|
17
|
+
ReleaseTerminalResponse,
|
|
18
|
+
RequestPermissionResponse,
|
|
19
|
+
TerminalOutputResponse,
|
|
20
|
+
WaitForTerminalExitResponse,
|
|
21
|
+
WriteTextFileResponse,
|
|
22
|
+
)
|
|
23
|
+
from agentpool.log import get_logger
|
|
24
|
+
from agentpool.tools.base import Tool
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
if TYPE_CHECKING:
|
|
28
|
+
from exxec import ExecutionEnvironment
|
|
29
|
+
|
|
30
|
+
from acp.schema import (
|
|
31
|
+
CreateTerminalRequest,
|
|
32
|
+
KillTerminalCommandRequest,
|
|
33
|
+
ReadTextFileRequest,
|
|
34
|
+
ReleaseTerminalRequest,
|
|
35
|
+
RequestPermissionRequest,
|
|
36
|
+
SessionNotification,
|
|
37
|
+
TerminalOutputRequest,
|
|
38
|
+
WaitForTerminalExitRequest,
|
|
39
|
+
WriteTextFileRequest,
|
|
40
|
+
)
|
|
41
|
+
from agentpool.agents.acp_agent import ACPAgent
|
|
42
|
+
from agentpool.agents.acp_agent.session_state import ACPSessionState
|
|
43
|
+
from agentpool.ui.base import InputProvider
|
|
44
|
+
from agentpool_config.nodes import ToolConfirmationMode
|
|
45
|
+
|
|
46
|
+
logger = get_logger(__name__)
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
class ACPClientHandler(Client):
|
|
50
|
+
"""Client handler that collects session updates and handles agent requests.
|
|
51
|
+
|
|
52
|
+
This implements the full ACP Client protocol including:
|
|
53
|
+
- Session update collection (text chunks, thoughts, tool calls)
|
|
54
|
+
- Filesystem operations (read/write files) via ExecutionEnvironment
|
|
55
|
+
- Terminal operations (create, output, kill, release) via ProcessManager
|
|
56
|
+
- Permission request handling via InputProvider
|
|
57
|
+
|
|
58
|
+
The handler accumulates session updates in an ACPSessionState instance,
|
|
59
|
+
allowing the ACPAgent to build the final response from streamed chunks.
|
|
60
|
+
|
|
61
|
+
Uses ExecutionEnvironment for all file and process operations, enabling
|
|
62
|
+
swappable backends (local, Docker, E2B, SSH, etc.).
|
|
63
|
+
|
|
64
|
+
The handler holds a reference to the parent ACPAgent, delegating env access
|
|
65
|
+
to ensure the env stays in sync when reassigned externally.
|
|
66
|
+
"""
|
|
67
|
+
|
|
68
|
+
def __init__(
|
|
69
|
+
self,
|
|
70
|
+
agent: ACPAgent[Any],
|
|
71
|
+
state: ACPSessionState,
|
|
72
|
+
input_provider: InputProvider | None = None,
|
|
73
|
+
) -> None:
|
|
74
|
+
self._agent = agent
|
|
75
|
+
self.state = state
|
|
76
|
+
self._input_provider = input_provider
|
|
77
|
+
self._update_event = asyncio.Event()
|
|
78
|
+
# Map ACP terminal IDs to process manager IDs
|
|
79
|
+
self._terminal_to_process: dict[str, str] = {}
|
|
80
|
+
# Copy tool confirmation mode from agent (can be updated via set_tool_confirmation_mode)
|
|
81
|
+
self.tool_confirmation_mode: ToolConfirmationMode = agent.tool_confirmation_mode
|
|
82
|
+
|
|
83
|
+
@property
|
|
84
|
+
def env(self) -> ExecutionEnvironment:
|
|
85
|
+
"""Get execution environment for subprocess requests.
|
|
86
|
+
|
|
87
|
+
Uses the agent's client_env which handles subprocess file/terminal
|
|
88
|
+
operations. Falls back to agent's main env if not explicitly configured.
|
|
89
|
+
"""
|
|
90
|
+
return self._agent.client_env
|
|
91
|
+
|
|
92
|
+
@property
|
|
93
|
+
def allow_file_operations(self) -> bool:
|
|
94
|
+
return self._agent.config.allow_file_operations
|
|
95
|
+
|
|
96
|
+
@property
|
|
97
|
+
def allow_terminal(self) -> bool:
|
|
98
|
+
return self._agent.config.allow_terminal
|
|
99
|
+
|
|
100
|
+
async def session_update(self, params: SessionNotification[Any]) -> None:
|
|
101
|
+
"""Handle session update notifications from the agent."""
|
|
102
|
+
from agentpool.agents.acp_agent.acp_converters import acp_to_native_event
|
|
103
|
+
|
|
104
|
+
update = params.update
|
|
105
|
+
# Convert to native event and queue it
|
|
106
|
+
if native_event := acp_to_native_event(update):
|
|
107
|
+
self.state.events.append(native_event)
|
|
108
|
+
self._update_event.set()
|
|
109
|
+
|
|
110
|
+
async def request_permission( # noqa: PLR0911
|
|
111
|
+
self, params: RequestPermissionRequest
|
|
112
|
+
) -> RequestPermissionResponse:
|
|
113
|
+
"""Handle permission requests via InputProvider."""
|
|
114
|
+
name = params.tool_call.title or "operation"
|
|
115
|
+
logger.info("Permission requested", tool_name=name)
|
|
116
|
+
|
|
117
|
+
# Check tool_confirmation_mode FIRST, before any forwarding
|
|
118
|
+
# This ensures "bypass permissions" mode works even for nested ACP agents
|
|
119
|
+
if self.tool_confirmation_mode == "never" and params.options:
|
|
120
|
+
option_id = params.options[0].option_id
|
|
121
|
+
logger.debug("Auto-granting permission (tool_confirmation_mode=never)", tool_name=name)
|
|
122
|
+
return RequestPermissionResponse.allowed(option_id)
|
|
123
|
+
|
|
124
|
+
# Try callback second (forwards to parent session for nested ACP agents)
|
|
125
|
+
if self._agent.acp_permission_callback:
|
|
126
|
+
# return RequestPermissionResponse.allowed(option_id=params.options[0].option_id) # "acceptEdits" # noqa: E501
|
|
127
|
+
try:
|
|
128
|
+
logger.debug("Forwarding permission via callback", tool_name=name)
|
|
129
|
+
response = await self._agent.acp_permission_callback(params)
|
|
130
|
+
logger.debug(
|
|
131
|
+
"Permission response received", tool_name=name, outcome=response.outcome.outcome
|
|
132
|
+
)
|
|
133
|
+
except Exception:
|
|
134
|
+
logger.exception("Failed to forward permission via callback")
|
|
135
|
+
# Fall through to old logic
|
|
136
|
+
else:
|
|
137
|
+
return response
|
|
138
|
+
|
|
139
|
+
if self._input_provider:
|
|
140
|
+
ctx = self._agent.get_context() # Use the agent's NodeContext
|
|
141
|
+
# Create a dummy tool representation from ACP params
|
|
142
|
+
tool = Tool(callable=lambda: None, name=params.tool_call.tool_call_id, description=name)
|
|
143
|
+
# Extract arguments - ACP doesn't expose them in ToolCall
|
|
144
|
+
try:
|
|
145
|
+
result = await self._input_provider.get_tool_confirmation(ctx, tool=tool, args={})
|
|
146
|
+
# Map confirmation result to ACP response
|
|
147
|
+
if result == "allow":
|
|
148
|
+
option_id = params.options[0].option_id if params.options else "allow"
|
|
149
|
+
return RequestPermissionResponse.allowed(option_id)
|
|
150
|
+
if result == "skip":
|
|
151
|
+
return RequestPermissionResponse.denied()
|
|
152
|
+
return RequestPermissionResponse.denied() # abort_run
|
|
153
|
+
|
|
154
|
+
except Exception:
|
|
155
|
+
logger.exception("Failed to get permission via input provider")
|
|
156
|
+
return RequestPermissionResponse.denied()
|
|
157
|
+
|
|
158
|
+
logger.debug("Denying permission (no input provider)", tool_name=name)
|
|
159
|
+
return RequestPermissionResponse.denied()
|
|
160
|
+
|
|
161
|
+
async def read_text_file(self, params: ReadTextFileRequest) -> ReadTextFileResponse:
|
|
162
|
+
"""Read text from file via ExecutionEnvironment filesystem."""
|
|
163
|
+
if not self.allow_file_operations:
|
|
164
|
+
raise RuntimeError("File operations not allowed")
|
|
165
|
+
|
|
166
|
+
fs = self.env.get_fs()
|
|
167
|
+
try:
|
|
168
|
+
content_bytes = await fs._cat_file(params.path)
|
|
169
|
+
content = content_bytes.decode("utf-8")
|
|
170
|
+
# Apply line filtering if requested
|
|
171
|
+
if params.line is not None or params.limit is not None:
|
|
172
|
+
lines = content.splitlines(keepends=True)
|
|
173
|
+
start_line = (params.line - 1) if params.line else 0
|
|
174
|
+
end_line = start_line + params.limit if params.limit else len(lines)
|
|
175
|
+
content = "".join(lines[start_line:end_line])
|
|
176
|
+
|
|
177
|
+
logger.debug("Read file", path=params.path, num_chars=len(content))
|
|
178
|
+
return ReadTextFileResponse(content=content)
|
|
179
|
+
|
|
180
|
+
except FileNotFoundError:
|
|
181
|
+
logger.exception("File not found", path=params.path)
|
|
182
|
+
raise
|
|
183
|
+
except Exception:
|
|
184
|
+
logger.exception("Failed to read file", path=params.path)
|
|
185
|
+
raise
|
|
186
|
+
|
|
187
|
+
async def write_text_file(self, params: WriteTextFileRequest) -> WriteTextFileResponse:
|
|
188
|
+
"""Write text to file via ExecutionEnvironment filesystem."""
|
|
189
|
+
if not self.allow_file_operations:
|
|
190
|
+
raise RuntimeError("File operations not allowed")
|
|
191
|
+
fs = self.env.get_fs()
|
|
192
|
+
content_bytes = params.content.encode("utf-8")
|
|
193
|
+
parent = str(Path(params.path).parent)
|
|
194
|
+
try:
|
|
195
|
+
if parent and parent != ".": # Ensure parent directory exists
|
|
196
|
+
await fs._makedirs(parent, exist_ok=True)
|
|
197
|
+
await fs._pipe_file(params.path, content_bytes)
|
|
198
|
+
logger.debug("Wrote file", path=params.path, num_chars=len(params.content))
|
|
199
|
+
return WriteTextFileResponse()
|
|
200
|
+
except Exception:
|
|
201
|
+
logger.exception("Failed to write file", path=params.path)
|
|
202
|
+
raise
|
|
203
|
+
|
|
204
|
+
async def create_terminal(self, params: CreateTerminalRequest) -> CreateTerminalResponse:
|
|
205
|
+
"""Create a new terminal session via ProcessManager."""
|
|
206
|
+
if not self.allow_terminal:
|
|
207
|
+
raise RuntimeError("Terminal operations not allowed")
|
|
208
|
+
try:
|
|
209
|
+
process_id = await self.env.process_manager.start_process(
|
|
210
|
+
command=params.command,
|
|
211
|
+
args=list(params.args) if params.args else None,
|
|
212
|
+
cwd=params.cwd,
|
|
213
|
+
env={var.name: var.value for var in params.env or []},
|
|
214
|
+
)
|
|
215
|
+
except Exception:
|
|
216
|
+
logger.exception("Failed to create terminal", command=params.command)
|
|
217
|
+
raise
|
|
218
|
+
else:
|
|
219
|
+
terminal_id = f"term_{uuid.uuid4().hex[:8]}"
|
|
220
|
+
self._terminal_to_process[terminal_id] = process_id
|
|
221
|
+
msg = "Created terminal"
|
|
222
|
+
logger.info(msg, terminal_id=terminal_id, process_id=process_id, cmd=params.command)
|
|
223
|
+
return CreateTerminalResponse(terminal_id=terminal_id)
|
|
224
|
+
|
|
225
|
+
async def terminal_output(self, params: TerminalOutputRequest) -> TerminalOutputResponse:
|
|
226
|
+
"""Get output from terminal via ProcessManager."""
|
|
227
|
+
if not self.allow_terminal:
|
|
228
|
+
raise RuntimeError("Terminal operations not allowed")
|
|
229
|
+
|
|
230
|
+
terminal_id = params.terminal_id
|
|
231
|
+
if terminal_id not in self._terminal_to_process:
|
|
232
|
+
raise ValueError(f"Terminal {terminal_id} not found")
|
|
233
|
+
|
|
234
|
+
process_id = self._terminal_to_process[terminal_id]
|
|
235
|
+
proc_output = await self.env.process_manager.get_output(process_id)
|
|
236
|
+
output = proc_output.combined or proc_output.stdout or ""
|
|
237
|
+
return TerminalOutputResponse(output=output, truncated=proc_output.truncated)
|
|
238
|
+
|
|
239
|
+
async def wait_for_terminal_exit(
|
|
240
|
+
self, params: WaitForTerminalExitRequest
|
|
241
|
+
) -> WaitForTerminalExitResponse:
|
|
242
|
+
"""Wait for terminal process to exit via ProcessManager."""
|
|
243
|
+
if not self.allow_terminal:
|
|
244
|
+
raise RuntimeError("Terminal operations not allowed")
|
|
245
|
+
|
|
246
|
+
terminal_id = params.terminal_id
|
|
247
|
+
if terminal_id not in self._terminal_to_process:
|
|
248
|
+
raise ValueError(f"Terminal {terminal_id} not found")
|
|
249
|
+
|
|
250
|
+
process_id = self._terminal_to_process[terminal_id]
|
|
251
|
+
exit_code = await self.env.process_manager.wait_for_exit(process_id)
|
|
252
|
+
logger.debug("Terminal exited", terminal_id=terminal_id, exit_code=exit_code)
|
|
253
|
+
return WaitForTerminalExitResponse(exit_code=exit_code)
|
|
254
|
+
|
|
255
|
+
async def kill_terminal(
|
|
256
|
+
self, params: KillTerminalCommandRequest
|
|
257
|
+
) -> KillTerminalCommandResponse:
|
|
258
|
+
"""Kill terminal process via ProcessManager."""
|
|
259
|
+
if not self.allow_terminal:
|
|
260
|
+
raise RuntimeError("Terminal operations not allowed")
|
|
261
|
+
|
|
262
|
+
terminal_id = params.terminal_id
|
|
263
|
+
if terminal_id not in self._terminal_to_process:
|
|
264
|
+
raise ValueError(f"Terminal {terminal_id} not found")
|
|
265
|
+
|
|
266
|
+
process_id = self._terminal_to_process[terminal_id]
|
|
267
|
+
await self.env.process_manager.kill_process(process_id)
|
|
268
|
+
logger.info("Killed terminal", terminal_id=terminal_id)
|
|
269
|
+
return KillTerminalCommandResponse()
|
|
270
|
+
|
|
271
|
+
async def release_terminal(self, params: ReleaseTerminalRequest) -> ReleaseTerminalResponse:
|
|
272
|
+
"""Release terminal resources via ProcessManager."""
|
|
273
|
+
if not self.allow_terminal:
|
|
274
|
+
raise RuntimeError("Terminal operations not allowed")
|
|
275
|
+
|
|
276
|
+
terminal_id = params.terminal_id
|
|
277
|
+
if terminal_id not in self._terminal_to_process:
|
|
278
|
+
raise ValueError(f"Terminal {terminal_id} not found")
|
|
279
|
+
process_id = self._terminal_to_process[terminal_id]
|
|
280
|
+
await self.env.process_manager.release_process(process_id)
|
|
281
|
+
del self._terminal_to_process[terminal_id]
|
|
282
|
+
logger.info("Released terminal", terminal_id=terminal_id)
|
|
283
|
+
return ReleaseTerminalResponse()
|
|
284
|
+
|
|
285
|
+
async def cleanup(self) -> None:
|
|
286
|
+
"""Clean up all resources."""
|
|
287
|
+
for terminal_id, process_id in list(self._terminal_to_process.items()):
|
|
288
|
+
try:
|
|
289
|
+
await self.env.process_manager.release_process(process_id)
|
|
290
|
+
except Exception:
|
|
291
|
+
logger.exception("Error cleaning up terminal", terminal_id=terminal_id)
|
|
292
|
+
|
|
293
|
+
self._terminal_to_process.clear()
|
|
294
|
+
|
|
295
|
+
async def ext_method(self, method: str, params: dict[str, Any]) -> dict[str, Any]:
|
|
296
|
+
"""Handle extension methods."""
|
|
297
|
+
logger.debug("Extension method called", method=method)
|
|
298
|
+
return {"ok": True, "method": method}
|
|
299
|
+
|
|
300
|
+
async def ext_notification(self, method: str, params: dict[str, Any]) -> None:
|
|
301
|
+
"""Handle extension notifications."""
|
|
302
|
+
logger.debug("Extension notification", method=method)
|
|
303
|
+
|
|
304
|
+
|
|
305
|
+
if __name__ == "__main__":
|
|
306
|
+
from agentpool.agents.acp_agent import ACPAgent
|
|
307
|
+
|
|
308
|
+
async def main() -> None:
|
|
309
|
+
"""Demo: Basic call to an ACP agent."""
|
|
310
|
+
args = ["run", "agentpool", "serve-acp", "--model-provider", "openai"]
|
|
311
|
+
cwd = str(Path.cwd())
|
|
312
|
+
async with ACPAgent(command="uv", args=args, cwd=cwd, event_handlers=["detailed"]) as agent:
|
|
313
|
+
print("Response (streaming): ", end="", flush=True)
|
|
314
|
+
async for chunk in agent.run_stream("Say hello briefly."):
|
|
315
|
+
print(chunk, end="", flush=True)
|
|
316
|
+
|
|
317
|
+
anyio.run(main)
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
"""ACP Agent - MessageNode wrapping an external ACP subprocess."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from dataclasses import dataclass, field as dataclass_field
|
|
6
|
+
from typing import TYPE_CHECKING, Any
|
|
7
|
+
|
|
8
|
+
from agentpool.log import get_logger
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
if TYPE_CHECKING:
|
|
12
|
+
from acp.schema import SessionModelState, SessionModeState
|
|
13
|
+
from agentpool.agents.events import RichAgentStreamEvent
|
|
14
|
+
|
|
15
|
+
logger = get_logger(__name__)
|
|
16
|
+
|
|
17
|
+
PROTOCOL_VERSION = 1
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
@dataclass
|
|
21
|
+
class ACPSessionState:
|
|
22
|
+
"""Tracks state of an ACP session."""
|
|
23
|
+
|
|
24
|
+
session_id: str
|
|
25
|
+
"""The session ID from the ACP server."""
|
|
26
|
+
|
|
27
|
+
events: list[RichAgentStreamEvent[Any]] = dataclass_field(default_factory=list)
|
|
28
|
+
"""Queue of native events converted from ACP updates."""
|
|
29
|
+
|
|
30
|
+
current_model_id: str | None = None
|
|
31
|
+
"""Current model ID from session state."""
|
|
32
|
+
|
|
33
|
+
models: SessionModelState | None = None
|
|
34
|
+
"""Full model state including available models from nested ACP agent."""
|
|
35
|
+
|
|
36
|
+
modes: SessionModeState | None = None
|
|
37
|
+
"""Full mode state including available modes from nested ACP agent."""
|
|
38
|
+
|
|
39
|
+
current_mode_id: str | None = None
|
|
40
|
+
"""Current mode ID."""
|
|
41
|
+
|
|
42
|
+
def clear(self) -> None:
|
|
43
|
+
self.events.clear()
|
|
44
|
+
# Note: Don't clear current_model_id or models - those persist across prompts
|