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,786 @@
|
|
|
1
|
+
"""ACP (Agent Client Protocol) Agent implementation."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from dataclasses import KW_ONLY, dataclass, field
|
|
6
|
+
from importlib.metadata import version as _version
|
|
7
|
+
from typing import TYPE_CHECKING, Any, ClassVar
|
|
8
|
+
|
|
9
|
+
from acp import Agent as ACPAgent
|
|
10
|
+
from acp.schema import (
|
|
11
|
+
ForkSessionResponse,
|
|
12
|
+
InitializeResponse,
|
|
13
|
+
ListSessionsResponse,
|
|
14
|
+
LoadSessionResponse,
|
|
15
|
+
ModelInfo as ACPModelInfo,
|
|
16
|
+
NewSessionResponse,
|
|
17
|
+
PromptResponse,
|
|
18
|
+
ResumeSessionResponse,
|
|
19
|
+
SessionInfo,
|
|
20
|
+
SessionModelState,
|
|
21
|
+
SessionModeState,
|
|
22
|
+
SetSessionModelRequest,
|
|
23
|
+
SetSessionModelResponse,
|
|
24
|
+
SetSessionModeRequest,
|
|
25
|
+
SetSessionModeResponse,
|
|
26
|
+
)
|
|
27
|
+
from agentpool import Agent
|
|
28
|
+
from agentpool.log import get_logger
|
|
29
|
+
from agentpool.utils.tasks import TaskManager
|
|
30
|
+
from agentpool_server.acp_server.converters import (
|
|
31
|
+
# agent_to_mode, # TODO: Re-enable when supporting agent switching via modes
|
|
32
|
+
get_confirmation_modes,
|
|
33
|
+
mode_id_to_confirmation_mode,
|
|
34
|
+
)
|
|
35
|
+
from agentpool_server.acp_server.session_manager import ACPSessionManager
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
if TYPE_CHECKING:
|
|
39
|
+
from collections.abc import Sequence
|
|
40
|
+
|
|
41
|
+
from pydantic_ai import ModelRequest, ModelResponse
|
|
42
|
+
|
|
43
|
+
from acp import AgentSideConnection, Client
|
|
44
|
+
from acp.schema import (
|
|
45
|
+
AuthenticateRequest,
|
|
46
|
+
CancelNotification,
|
|
47
|
+
ClientCapabilities,
|
|
48
|
+
ForkSessionRequest,
|
|
49
|
+
Implementation,
|
|
50
|
+
InitializeRequest,
|
|
51
|
+
ListSessionsRequest,
|
|
52
|
+
LoadSessionRequest,
|
|
53
|
+
NewSessionRequest,
|
|
54
|
+
PromptRequest,
|
|
55
|
+
ResumeSessionRequest,
|
|
56
|
+
SetSessionModelRequest,
|
|
57
|
+
SetSessionModeRequest,
|
|
58
|
+
)
|
|
59
|
+
from agentpool import AgentPool
|
|
60
|
+
from agentpool_server.acp_server.server import ACPServer
|
|
61
|
+
from agentpool_server.acp_server.session import ACPSession
|
|
62
|
+
|
|
63
|
+
logger = get_logger(__name__)
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
# Claude Code model definitions - simple IDs that the SDK understands
|
|
67
|
+
CLAUDE_CODE_MODELS: list[ACPModelInfo] = [
|
|
68
|
+
ACPModelInfo(
|
|
69
|
+
model_id="opus",
|
|
70
|
+
name="Claude Opus",
|
|
71
|
+
description="Claude Opus - most capable model",
|
|
72
|
+
),
|
|
73
|
+
ACPModelInfo(
|
|
74
|
+
model_id="sonnet",
|
|
75
|
+
name="Claude Sonnet",
|
|
76
|
+
description="Claude Sonnet - balanced performance and speed",
|
|
77
|
+
),
|
|
78
|
+
ACPModelInfo(
|
|
79
|
+
model_id="haiku",
|
|
80
|
+
name="Claude Haiku",
|
|
81
|
+
description="Claude Haiku - fast and cost-effective",
|
|
82
|
+
),
|
|
83
|
+
]
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def create_claude_code_model_state(current_model: str | None = None) -> SessionModelState:
|
|
87
|
+
"""Create SessionModelState for Claude Code agents.
|
|
88
|
+
|
|
89
|
+
Args:
|
|
90
|
+
current_model: Currently active model ID (e.g., "sonnet")
|
|
91
|
+
|
|
92
|
+
Returns:
|
|
93
|
+
SessionModelState with Claude Code models
|
|
94
|
+
"""
|
|
95
|
+
model_ids = [m.model_id for m in CLAUDE_CODE_MODELS]
|
|
96
|
+
current = current_model if current_model in model_ids else "sonnet"
|
|
97
|
+
return SessionModelState(available_models=CLAUDE_CODE_MODELS, current_model_id=current)
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
def create_session_model_state(
|
|
101
|
+
available_models: Sequence[ACPModelInfo], current_model: str | None = None
|
|
102
|
+
) -> SessionModelState | None:
|
|
103
|
+
"""Create a SessionModelState from available ACP models.
|
|
104
|
+
|
|
105
|
+
Args:
|
|
106
|
+
available_models: List of ACP ModelInfo objects (already converted from tokonomics)
|
|
107
|
+
current_model: The currently active model (defaults to first available)
|
|
108
|
+
|
|
109
|
+
Returns:
|
|
110
|
+
SessionModelState with all available models, None if no models provided
|
|
111
|
+
"""
|
|
112
|
+
if not available_models:
|
|
113
|
+
return None
|
|
114
|
+
# Use first model as current if not specified or not found
|
|
115
|
+
all_ids = [model.model_id for model in available_models]
|
|
116
|
+
current_model_id = current_model if current_model in all_ids else all_ids[0]
|
|
117
|
+
return SessionModelState(
|
|
118
|
+
available_models=list(available_models), current_model_id=current_model_id
|
|
119
|
+
)
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
@dataclass
|
|
123
|
+
class AgentPoolACPAgent(ACPAgent):
|
|
124
|
+
"""Implementation of ACP Agent protocol interface for AgentPool.
|
|
125
|
+
|
|
126
|
+
This class implements the external library's Agent protocol interface,
|
|
127
|
+
bridging AgentPool with the standard ACP JSON-RPC protocol.
|
|
128
|
+
"""
|
|
129
|
+
|
|
130
|
+
PROTOCOL_VERSION: ClassVar = 1
|
|
131
|
+
|
|
132
|
+
connection: AgentSideConnection
|
|
133
|
+
"""ACP connection for client communication."""
|
|
134
|
+
|
|
135
|
+
agent_pool: AgentPool[Any]
|
|
136
|
+
"""AgentPool containing available agents."""
|
|
137
|
+
|
|
138
|
+
_: KW_ONLY
|
|
139
|
+
|
|
140
|
+
available_models: Sequence[ACPModelInfo] = field(default_factory=list)
|
|
141
|
+
"""List of available ACP ModelInfo objects (converted from tokonomics at startup)."""
|
|
142
|
+
|
|
143
|
+
file_access: bool = True
|
|
144
|
+
"""Whether agent can access filesystem."""
|
|
145
|
+
|
|
146
|
+
terminal_access: bool = True
|
|
147
|
+
"""Whether agent can use terminal."""
|
|
148
|
+
|
|
149
|
+
debug_commands: bool = False
|
|
150
|
+
"""Whether to enable debug slash commands for testing."""
|
|
151
|
+
|
|
152
|
+
default_agent: str | None = None
|
|
153
|
+
"""Optional specific agent name to use as default."""
|
|
154
|
+
|
|
155
|
+
load_skills: bool = True
|
|
156
|
+
"""Whether to load client-side skills from .claude/skills directory."""
|
|
157
|
+
|
|
158
|
+
server: ACPServer | None = field(default=None)
|
|
159
|
+
"""Reference to the ACPServer for pool hot-switching."""
|
|
160
|
+
|
|
161
|
+
def __post_init__(self) -> None:
|
|
162
|
+
"""Initialize derived attributes and setup after field assignment."""
|
|
163
|
+
self.client: Client = self.connection
|
|
164
|
+
self.client_capabilities: ClientCapabilities | None = None
|
|
165
|
+
self.client_info: Implementation | None = None
|
|
166
|
+
self.session_manager = ACPSessionManager(pool=self.agent_pool)
|
|
167
|
+
self.tasks = TaskManager()
|
|
168
|
+
self._initialized = False
|
|
169
|
+
|
|
170
|
+
agent_count = len(self.agent_pool.agents)
|
|
171
|
+
logger.info("Created ACP agent implementation", agent_count=agent_count)
|
|
172
|
+
if self.debug_commands:
|
|
173
|
+
logger.info("Debug slash commands enabled for ACP testing")
|
|
174
|
+
|
|
175
|
+
# Note: Tool registration happens after initialize() when we know client caps
|
|
176
|
+
|
|
177
|
+
async def initialize(self, params: InitializeRequest) -> InitializeResponse:
|
|
178
|
+
"""Initialize the agent and negotiate capabilities."""
|
|
179
|
+
logger.info("Initializing ACP agent implementation")
|
|
180
|
+
version = min(params.protocol_version, self.PROTOCOL_VERSION)
|
|
181
|
+
self.client_capabilities = params.client_capabilities
|
|
182
|
+
self.client_info = params.client_info
|
|
183
|
+
logger.info(
|
|
184
|
+
"Client capabilities",
|
|
185
|
+
capabilities=self.client_capabilities,
|
|
186
|
+
client_info=self.client_info,
|
|
187
|
+
)
|
|
188
|
+
self._initialized = True
|
|
189
|
+
response = InitializeResponse.create(
|
|
190
|
+
protocol_version=version,
|
|
191
|
+
name="agentpool",
|
|
192
|
+
title="AgentPool",
|
|
193
|
+
version=_version("agentpool"),
|
|
194
|
+
load_session=True,
|
|
195
|
+
list_sessions=True,
|
|
196
|
+
http_mcp_servers=True,
|
|
197
|
+
sse_mcp_servers=True,
|
|
198
|
+
audio_prompts=True,
|
|
199
|
+
embedded_context_prompts=True,
|
|
200
|
+
image_prompts=True,
|
|
201
|
+
)
|
|
202
|
+
logger.info("ACP agent initialized successfully", response=response)
|
|
203
|
+
return response
|
|
204
|
+
|
|
205
|
+
async def new_session(self, params: NewSessionRequest) -> NewSessionResponse: # noqa: PLR0915
|
|
206
|
+
"""Create a new session."""
|
|
207
|
+
if not self._initialized:
|
|
208
|
+
raise RuntimeError("Agent not initialized")
|
|
209
|
+
|
|
210
|
+
try:
|
|
211
|
+
names = list(self.agent_pool.all_agents.keys())
|
|
212
|
+
if not names:
|
|
213
|
+
logger.error("No agents available for session creation")
|
|
214
|
+
raise RuntimeError("No agents available") # noqa: TRY301
|
|
215
|
+
|
|
216
|
+
# Use specified default agent or fall back to first agent
|
|
217
|
+
if self.default_agent and self.default_agent in names:
|
|
218
|
+
default_name = self.default_agent
|
|
219
|
+
else:
|
|
220
|
+
default_name = names[0]
|
|
221
|
+
|
|
222
|
+
logger.info("Creating new session", agents=names, default_agent=default_name)
|
|
223
|
+
session_id = await self.session_manager.create_session(
|
|
224
|
+
default_agent_name=default_name,
|
|
225
|
+
cwd=params.cwd,
|
|
226
|
+
client=self.client,
|
|
227
|
+
acp_agent=self,
|
|
228
|
+
mcp_servers=params.mcp_servers,
|
|
229
|
+
client_capabilities=self.client_capabilities,
|
|
230
|
+
client_info=self.client_info,
|
|
231
|
+
)
|
|
232
|
+
|
|
233
|
+
# Get mode information - pass through from ACPAgent or use our confirmation modes
|
|
234
|
+
from agentpool.agents.acp_agent import ACPAgent as ACPAgentClient
|
|
235
|
+
|
|
236
|
+
if session := self.session_manager.get_session(session_id):
|
|
237
|
+
if isinstance(session.agent, ACPAgentClient):
|
|
238
|
+
# Pass through nested agent's modes (e.g., Claude Code's modes)
|
|
239
|
+
if session.agent._state and session.agent._state.modes:
|
|
240
|
+
state = session.agent._state.modes
|
|
241
|
+
modes = state.available_modes
|
|
242
|
+
else:
|
|
243
|
+
# Fallback to our confirmation modes if nested agent has none
|
|
244
|
+
modes = get_confirmation_modes()
|
|
245
|
+
state = SessionModeState(current_mode_id="default", available_modes=modes)
|
|
246
|
+
else:
|
|
247
|
+
# Native Agent - use our tool confirmation modes
|
|
248
|
+
modes = get_confirmation_modes()
|
|
249
|
+
state = SessionModeState(current_mode_id="default", available_modes=modes)
|
|
250
|
+
else:
|
|
251
|
+
modes = get_confirmation_modes()
|
|
252
|
+
state = SessionModeState(current_mode_id="default", available_modes=modes)
|
|
253
|
+
# TODO: Re-enable agent switching via modes when needed:
|
|
254
|
+
# modes = [agent_to_mode(agent) for agent in self.agent_pool.all_agents.values()]
|
|
255
|
+
# state = SessionModeState(current_mode_id=default_name, available_modes=modes)
|
|
256
|
+
|
|
257
|
+
# Get model information from the default agent
|
|
258
|
+
if session := self.session_manager.get_session(session_id):
|
|
259
|
+
from agentpool.agents.claude_code_agent import ClaudeCodeAgent
|
|
260
|
+
|
|
261
|
+
if isinstance(session.agent, ClaudeCodeAgent):
|
|
262
|
+
# Claude Code uses simple model IDs (sonnet, opus, haiku)
|
|
263
|
+
models = create_claude_code_model_state(session.agent.model_name)
|
|
264
|
+
elif isinstance(session.agent, ACPAgentClient):
|
|
265
|
+
# Nested ACP agent - pass through its model state directly
|
|
266
|
+
if session.agent._state and session.agent._state.models:
|
|
267
|
+
models = session.agent._state.models
|
|
268
|
+
else:
|
|
269
|
+
models = None
|
|
270
|
+
else:
|
|
271
|
+
current_model = session.agent.model_name
|
|
272
|
+
models = create_session_model_state(self.available_models, current_model)
|
|
273
|
+
else:
|
|
274
|
+
models = None
|
|
275
|
+
except Exception:
|
|
276
|
+
logger.exception("Failed to create new session")
|
|
277
|
+
raise
|
|
278
|
+
else:
|
|
279
|
+
# Schedule available commands update after session response is returned
|
|
280
|
+
if session := self.session_manager.get_session(session_id):
|
|
281
|
+
# Schedule task to run after response is sent
|
|
282
|
+
self.tasks.create_task(session.send_available_commands_update())
|
|
283
|
+
self.tasks.create_task(session.init_project_context())
|
|
284
|
+
self.tasks.create_task(session._register_prompt_hub_commands())
|
|
285
|
+
if self.load_skills:
|
|
286
|
+
coro_4 = session.init_client_skills()
|
|
287
|
+
self.tasks.create_task(coro_4, name=f"init_client_skills_{session_id}")
|
|
288
|
+
logger.info("Created session", session_id=session_id, agent_count=len(modes))
|
|
289
|
+
return NewSessionResponse(session_id=session_id, modes=state, models=models)
|
|
290
|
+
|
|
291
|
+
async def load_session(self, params: LoadSessionRequest) -> LoadSessionResponse:
|
|
292
|
+
"""Load an existing session from storage.
|
|
293
|
+
|
|
294
|
+
This tries to:
|
|
295
|
+
1. Check if session is already active
|
|
296
|
+
2. If not, try to resume from persistent storage
|
|
297
|
+
3. Initialize MCP servers and other session resources
|
|
298
|
+
4. Replay conversation history via ACP notifications
|
|
299
|
+
5. Return session state (modes, models)
|
|
300
|
+
"""
|
|
301
|
+
if not self._initialized:
|
|
302
|
+
raise RuntimeError("Agent not initialized")
|
|
303
|
+
|
|
304
|
+
try:
|
|
305
|
+
# First check if session is already active
|
|
306
|
+
session = self.session_manager.get_session(params.session_id)
|
|
307
|
+
|
|
308
|
+
if not session:
|
|
309
|
+
# Try to resume from storage
|
|
310
|
+
msg = "Attempting to resume session from storage"
|
|
311
|
+
logger.info(msg, session_id=params.session_id)
|
|
312
|
+
session = await self.session_manager.resume_session(
|
|
313
|
+
session_id=params.session_id,
|
|
314
|
+
client=self.client,
|
|
315
|
+
acp_agent=self,
|
|
316
|
+
client_capabilities=self.client_capabilities,
|
|
317
|
+
client_info=self.client_info,
|
|
318
|
+
)
|
|
319
|
+
|
|
320
|
+
if not session:
|
|
321
|
+
logger.warning("Session not found in storage", session_id=params.session_id)
|
|
322
|
+
return LoadSessionResponse()
|
|
323
|
+
|
|
324
|
+
# Update session with new request parameters if provided
|
|
325
|
+
if params.cwd and params.cwd != session.cwd:
|
|
326
|
+
session.cwd = params.cwd
|
|
327
|
+
logger.info("Updated session cwd", session_id=params.session_id, cwd=params.cwd)
|
|
328
|
+
|
|
329
|
+
# Initialize MCP servers if provided in load request
|
|
330
|
+
if params.mcp_servers:
|
|
331
|
+
session.mcp_servers = params.mcp_servers
|
|
332
|
+
await session.initialize_mcp_servers()
|
|
333
|
+
|
|
334
|
+
# Build response with current session state
|
|
335
|
+
# Get mode information - pass through from ACPAgent or use our confirmation modes
|
|
336
|
+
from agentpool.agents.acp_agent import ACPAgent as ACPAgentClient
|
|
337
|
+
|
|
338
|
+
if isinstance(session.agent, ACPAgentClient):
|
|
339
|
+
# Pass through nested agent's modes (e.g., Claude Code's modes)
|
|
340
|
+
if session.agent._state and session.agent._state.modes:
|
|
341
|
+
mode_state = session.agent._state.modes
|
|
342
|
+
else:
|
|
343
|
+
# Fallback to our confirmation modes if nested agent has none
|
|
344
|
+
modes = get_confirmation_modes()
|
|
345
|
+
mode_state = SessionModeState(current_mode_id="default", available_modes=modes)
|
|
346
|
+
else:
|
|
347
|
+
# Native Agent - use our tool confirmation modes
|
|
348
|
+
modes = get_confirmation_modes()
|
|
349
|
+
mode_state = SessionModeState(current_mode_id="default", available_modes=modes)
|
|
350
|
+
# TODO: Re-enable agent switching via modes when needed:
|
|
351
|
+
# modes = [agent_to_mode(agent) for agent in self.agent_pool.all_agents.values()]
|
|
352
|
+
# mode_state = SessionModeState(
|
|
353
|
+
# current_mode_id=session.current_agent_name,
|
|
354
|
+
# available_modes=modes,
|
|
355
|
+
# )
|
|
356
|
+
|
|
357
|
+
# Get model information based on agent type
|
|
358
|
+
from agentpool.agents.claude_code_agent import ClaudeCodeAgent
|
|
359
|
+
|
|
360
|
+
models: SessionModelState | None
|
|
361
|
+
if session.agent and isinstance(session.agent, ClaudeCodeAgent):
|
|
362
|
+
# Claude Code uses simple model IDs (sonnet, opus, haiku)
|
|
363
|
+
models = create_claude_code_model_state(session.agent.model_name)
|
|
364
|
+
else:
|
|
365
|
+
current_model = session.agent.model_name if session.agent else None
|
|
366
|
+
models = create_session_model_state(self.available_models, current_model)
|
|
367
|
+
# Schedule post-load initialization tasks
|
|
368
|
+
self.tasks.create_task(session.send_available_commands_update())
|
|
369
|
+
self.tasks.create_task(session.init_project_context())
|
|
370
|
+
# Replay conversation history via ACP notifications
|
|
371
|
+
self.tasks.create_task(self._replay_conversation_history(session))
|
|
372
|
+
logger.info("Session loaded successfully", agent=session.current_agent_name)
|
|
373
|
+
return LoadSessionResponse(models=models, modes=mode_state)
|
|
374
|
+
|
|
375
|
+
except Exception:
|
|
376
|
+
logger.exception("Failed to load session", session_id=params.session_id)
|
|
377
|
+
return LoadSessionResponse()
|
|
378
|
+
|
|
379
|
+
async def _replay_conversation_history(self, session: ACPSession) -> None:
|
|
380
|
+
"""Replay conversation history for a loaded session via ACP notifications.
|
|
381
|
+
|
|
382
|
+
Per ACP spec, when loading a session the agent MUST replay the entire
|
|
383
|
+
conversation to the client via session/update notifications.
|
|
384
|
+
|
|
385
|
+
Args:
|
|
386
|
+
session: The ACP session to replay history for
|
|
387
|
+
"""
|
|
388
|
+
from agentpool_config.session import SessionQuery
|
|
389
|
+
|
|
390
|
+
# Get session data to find conversation_id
|
|
391
|
+
session_data = await self.session_manager.session_manager.store.load(session.session_id)
|
|
392
|
+
if not session_data:
|
|
393
|
+
logger.warning("No session data found for replay", session_id=session.session_id)
|
|
394
|
+
return
|
|
395
|
+
|
|
396
|
+
# Get storage provider
|
|
397
|
+
storage = self.agent_pool.storage
|
|
398
|
+
if not storage:
|
|
399
|
+
logger.debug("No storage provider, skipping conversation replay")
|
|
400
|
+
return
|
|
401
|
+
|
|
402
|
+
# Query messages by conversation_id
|
|
403
|
+
query = SessionQuery(name=session_data.conversation_id)
|
|
404
|
+
try:
|
|
405
|
+
chat_messages = await storage.filter_messages(query)
|
|
406
|
+
except NotImplementedError:
|
|
407
|
+
logger.debug("Storage provider doesn't support history loading")
|
|
408
|
+
return
|
|
409
|
+
|
|
410
|
+
if not chat_messages:
|
|
411
|
+
logger.debug("No messages to replay", session_id=session.session_id)
|
|
412
|
+
return
|
|
413
|
+
|
|
414
|
+
logger.info("Replaying conversation history", message_count=len(chat_messages))
|
|
415
|
+
# Extract ModelRequest/ModelResponse from ChatMessage.messages field
|
|
416
|
+
model_messages: list[ModelRequest | ModelResponse] = []
|
|
417
|
+
for chat_msg in chat_messages:
|
|
418
|
+
if chat_msg.messages:
|
|
419
|
+
model_messages.extend(chat_msg.messages)
|
|
420
|
+
|
|
421
|
+
if not model_messages:
|
|
422
|
+
logger.debug("No model messages to replay", session_id=session.session_id)
|
|
423
|
+
return
|
|
424
|
+
|
|
425
|
+
# Use ACPNotifications.replay() which handles all content types properly
|
|
426
|
+
try:
|
|
427
|
+
await session.notifications.replay(model_messages)
|
|
428
|
+
logger.info("Conversation replay complete", replayed_count=len(model_messages))
|
|
429
|
+
except Exception as e: # noqa: BLE001
|
|
430
|
+
logger.warning("Failed to replay conversation history", error=str(e))
|
|
431
|
+
|
|
432
|
+
async def list_sessions(self, params: ListSessionsRequest) -> ListSessionsResponse:
|
|
433
|
+
"""List available sessions.
|
|
434
|
+
|
|
435
|
+
Returns sessions from both active memory and persistent storage.
|
|
436
|
+
Supports pagination via cursor and optional cwd filtering.
|
|
437
|
+
"""
|
|
438
|
+
if not self._initialized:
|
|
439
|
+
raise RuntimeError("Agent not initialized")
|
|
440
|
+
|
|
441
|
+
try:
|
|
442
|
+
# Get session IDs from storage (includes both active and persisted)
|
|
443
|
+
session_ids = await self.session_manager.list_sessions(active_only=False)
|
|
444
|
+
# Build SessionInfo objects
|
|
445
|
+
sessions: list[SessionInfo] = []
|
|
446
|
+
for session_id in session_ids:
|
|
447
|
+
# Try active session first
|
|
448
|
+
active_session = self.session_manager.get_session(session_id)
|
|
449
|
+
if active_session:
|
|
450
|
+
# Filter by cwd if specified
|
|
451
|
+
if params.cwd and active_session.cwd != params.cwd:
|
|
452
|
+
continue
|
|
453
|
+
title = f"Session with {active_session.current_agent_name}"
|
|
454
|
+
sessions.append(
|
|
455
|
+
SessionInfo(session_id=session_id, cwd=active_session.cwd, title=title)
|
|
456
|
+
)
|
|
457
|
+
else:
|
|
458
|
+
# Load from storage to get details
|
|
459
|
+
data = await self.session_manager.session_manager.store.load(session_id)
|
|
460
|
+
if data:
|
|
461
|
+
# Filter by cwd if specified
|
|
462
|
+
if params.cwd and data.cwd != params.cwd:
|
|
463
|
+
continue
|
|
464
|
+
info = SessionInfo(
|
|
465
|
+
session_id=session_id,
|
|
466
|
+
cwd=data.cwd or "",
|
|
467
|
+
title=f"Session with {data.agent_name}",
|
|
468
|
+
updated_at=data.last_active.isoformat(),
|
|
469
|
+
)
|
|
470
|
+
sessions.append(info)
|
|
471
|
+
|
|
472
|
+
logger.info("Listed sessions", count=len(sessions))
|
|
473
|
+
return ListSessionsResponse(sessions=sessions)
|
|
474
|
+
|
|
475
|
+
except Exception:
|
|
476
|
+
logger.exception("Failed to list sessions")
|
|
477
|
+
return ListSessionsResponse(sessions=[])
|
|
478
|
+
|
|
479
|
+
async def fork_session(self, params: ForkSessionRequest) -> ForkSessionResponse:
|
|
480
|
+
"""Fork an existing session.
|
|
481
|
+
|
|
482
|
+
Creates a new session with the same state as the original.
|
|
483
|
+
UNSTABLE: This feature is not part of the spec yet.
|
|
484
|
+
"""
|
|
485
|
+
if not self._initialized:
|
|
486
|
+
raise RuntimeError("Agent not initialized")
|
|
487
|
+
|
|
488
|
+
logger.info("Forking session", session_id=params.session_id)
|
|
489
|
+
# For now, just create a new session - full fork implementation would copy state
|
|
490
|
+
default_agent = next(iter(self.agent_pool.manifest.agents.keys()))
|
|
491
|
+
session_id = await self.session_manager.create_session(
|
|
492
|
+
default_agent_name=default_agent,
|
|
493
|
+
cwd=params.cwd,
|
|
494
|
+
client=self.client,
|
|
495
|
+
acp_agent=self,
|
|
496
|
+
mcp_servers=params.mcp_servers,
|
|
497
|
+
session_id=None, # Let it generate a new ID
|
|
498
|
+
client_capabilities=self.client_capabilities,
|
|
499
|
+
client_info=self.client_info,
|
|
500
|
+
)
|
|
501
|
+
return ForkSessionResponse(session_id=session_id)
|
|
502
|
+
|
|
503
|
+
async def resume_session(self, params: ResumeSessionRequest) -> ResumeSessionResponse:
|
|
504
|
+
"""Resume an existing session.
|
|
505
|
+
|
|
506
|
+
Like load_session but doesn't return previous messages.
|
|
507
|
+
UNSTABLE: This feature is not part of the spec yet.
|
|
508
|
+
"""
|
|
509
|
+
if not self._initialized:
|
|
510
|
+
raise RuntimeError("Agent not initialized")
|
|
511
|
+
|
|
512
|
+
logger.info("Resuming session", session_id=params.session_id)
|
|
513
|
+
# Similar to load_session but without replaying history
|
|
514
|
+
try:
|
|
515
|
+
session = await self.session_manager.resume_session(
|
|
516
|
+
session_id=params.session_id,
|
|
517
|
+
client=self.client,
|
|
518
|
+
acp_agent=self,
|
|
519
|
+
client_capabilities=self.client_capabilities,
|
|
520
|
+
client_info=self.client_info,
|
|
521
|
+
)
|
|
522
|
+
if not session:
|
|
523
|
+
logger.warning("Session not found", session_id=params.session_id)
|
|
524
|
+
return ResumeSessionResponse()
|
|
525
|
+
except Exception:
|
|
526
|
+
logger.exception("Failed to resume session", session_id=params.session_id)
|
|
527
|
+
return ResumeSessionResponse()
|
|
528
|
+
|
|
529
|
+
async def authenticate(self, params: AuthenticateRequest) -> None:
|
|
530
|
+
"""Authenticate with the agent."""
|
|
531
|
+
logger.info("Authentication requested", method_id=params.method_id)
|
|
532
|
+
|
|
533
|
+
async def prompt(self, params: PromptRequest) -> PromptResponse:
|
|
534
|
+
"""Process a prompt request."""
|
|
535
|
+
if not self._initialized:
|
|
536
|
+
raise RuntimeError("Agent not initialized")
|
|
537
|
+
|
|
538
|
+
logger.info("Processing prompt", session_id=params.session_id)
|
|
539
|
+
session = self.session_manager.get_session(params.session_id)
|
|
540
|
+
|
|
541
|
+
# Auto-recreate session if not found (e.g., after pool swap)
|
|
542
|
+
if not session:
|
|
543
|
+
logger.info("Session not found, recreating", session_id=params.session_id)
|
|
544
|
+
try:
|
|
545
|
+
# Get default agent name
|
|
546
|
+
names = list(self.agent_pool.all_agents.keys())
|
|
547
|
+
if not names:
|
|
548
|
+
logger.error("No agents available for session recreation")
|
|
549
|
+
return PromptResponse(stop_reason="end_turn")
|
|
550
|
+
|
|
551
|
+
default_name = (
|
|
552
|
+
self.default_agent
|
|
553
|
+
if self.default_agent and self.default_agent in names
|
|
554
|
+
else names[0]
|
|
555
|
+
)
|
|
556
|
+
|
|
557
|
+
# Try to get cwd from stored session data
|
|
558
|
+
cwd = "."
|
|
559
|
+
try:
|
|
560
|
+
stored = await self.session_manager.session_manager.store.load(
|
|
561
|
+
params.session_id
|
|
562
|
+
)
|
|
563
|
+
if stored and stored.cwd:
|
|
564
|
+
cwd = stored.cwd
|
|
565
|
+
except Exception: # noqa: BLE001
|
|
566
|
+
pass # Use default cwd
|
|
567
|
+
|
|
568
|
+
# Recreate session with same ID
|
|
569
|
+
await self.session_manager.create_session(
|
|
570
|
+
default_agent_name=default_name,
|
|
571
|
+
cwd=cwd,
|
|
572
|
+
client=self.client,
|
|
573
|
+
acp_agent=self,
|
|
574
|
+
session_id=params.session_id,
|
|
575
|
+
client_capabilities=self.client_capabilities,
|
|
576
|
+
client_info=self.client_info,
|
|
577
|
+
)
|
|
578
|
+
session = self.session_manager.get_session(params.session_id)
|
|
579
|
+
if session:
|
|
580
|
+
# Initialize session extras
|
|
581
|
+
self.tasks.create_task(session.send_available_commands_update())
|
|
582
|
+
self.tasks.create_task(session.init_project_context())
|
|
583
|
+
except Exception:
|
|
584
|
+
logger.exception("Failed to recreate session", session_id=params.session_id)
|
|
585
|
+
return PromptResponse(stop_reason="end_turn")
|
|
586
|
+
|
|
587
|
+
try:
|
|
588
|
+
if not session:
|
|
589
|
+
raise ValueError(f"Session {params.session_id} not found") # noqa: TRY301
|
|
590
|
+
stop_reason = await session.process_prompt(params.prompt)
|
|
591
|
+
# Return the actual stop reason from the session
|
|
592
|
+
except Exception as e:
|
|
593
|
+
logger.exception("Failed to process prompt", session_id=params.session_id)
|
|
594
|
+
msg = f"Error processing prompt: {e}"
|
|
595
|
+
if session:
|
|
596
|
+
# Send error notification asynchronously to avoid blocking response
|
|
597
|
+
name = f"error_notification_{params.session_id}"
|
|
598
|
+
self.tasks.create_task(session._send_error_notification(msg), name=name)
|
|
599
|
+
|
|
600
|
+
return PromptResponse(stop_reason="end_turn")
|
|
601
|
+
else:
|
|
602
|
+
response = PromptResponse(stop_reason=stop_reason)
|
|
603
|
+
logger.info("Returning PromptResponse", stop_reason=stop_reason)
|
|
604
|
+
return response
|
|
605
|
+
|
|
606
|
+
async def cancel(self, params: CancelNotification) -> None:
|
|
607
|
+
"""Cancel operations for a session."""
|
|
608
|
+
logger.info("Cancelling session", session_id=params.session_id)
|
|
609
|
+
try:
|
|
610
|
+
# Get session and cancel it
|
|
611
|
+
if session := self.session_manager.get_session(params.session_id):
|
|
612
|
+
await session.cancel()
|
|
613
|
+
logger.info("Cancelled operations", session_id=params.session_id)
|
|
614
|
+
else:
|
|
615
|
+
msg = "Session not found for cancellation"
|
|
616
|
+
logger.warning(msg, session_id=params.session_id)
|
|
617
|
+
|
|
618
|
+
except Exception:
|
|
619
|
+
logger.exception("Failed to cancel session", session_id=params.session_id)
|
|
620
|
+
|
|
621
|
+
async def ext_method(self, method: str, params: dict[str, Any]) -> dict[str, Any]:
|
|
622
|
+
return {"example": "response"}
|
|
623
|
+
|
|
624
|
+
async def ext_notification(self, method: str, params: dict[str, Any]) -> None:
|
|
625
|
+
return None
|
|
626
|
+
|
|
627
|
+
async def set_session_mode(
|
|
628
|
+
self, params: SetSessionModeRequest
|
|
629
|
+
) -> SetSessionModeResponse | None:
|
|
630
|
+
"""Set the session mode (change tool confirmation level).
|
|
631
|
+
|
|
632
|
+
Maps ACP mode IDs to ToolConfirmationMode and calls set_tool_confirmation_mode
|
|
633
|
+
on the session's agent. Each agent type handles the mode change appropriately:
|
|
634
|
+
- Agent/AGUIAgent: Updates local confirmation mode
|
|
635
|
+
- ACPAgent: Updates local mode AND forwards to remote ACP server
|
|
636
|
+
|
|
637
|
+
Mode mappings:
|
|
638
|
+
- "default": per_tool (confirm tools marked as requiring it)
|
|
639
|
+
- "acceptEdits": never (auto-approve all tool calls)
|
|
640
|
+
"""
|
|
641
|
+
from agentpool.agents.acp_agent import ACPAgent as ACPAgentClient
|
|
642
|
+
|
|
643
|
+
try:
|
|
644
|
+
session = self.session_manager.get_session(params.session_id)
|
|
645
|
+
if not session:
|
|
646
|
+
logger.warning("Session not found for mode switch", session_id=params.session_id)
|
|
647
|
+
return None
|
|
648
|
+
|
|
649
|
+
# Map mode_id to confirmation mode
|
|
650
|
+
confirmation_mode = mode_id_to_confirmation_mode(params.mode_id)
|
|
651
|
+
if not confirmation_mode:
|
|
652
|
+
logger.error("Invalid mode_id", mode_id=params.mode_id)
|
|
653
|
+
return None
|
|
654
|
+
|
|
655
|
+
# All agent types support set_tool_confirmation_mode
|
|
656
|
+
# ACPAgent handles forwarding to remote server internally
|
|
657
|
+
await session.agent.set_tool_confirmation_mode(confirmation_mode)
|
|
658
|
+
|
|
659
|
+
# Update stored mode state for ACPAgent
|
|
660
|
+
if (
|
|
661
|
+
isinstance(session.agent, ACPAgentClient)
|
|
662
|
+
and session.agent._state
|
|
663
|
+
and session.agent._state.modes
|
|
664
|
+
):
|
|
665
|
+
session.agent._state.modes.current_mode_id = params.mode_id
|
|
666
|
+
|
|
667
|
+
logger.info(
|
|
668
|
+
"Set tool confirmation mode",
|
|
669
|
+
mode_id=params.mode_id,
|
|
670
|
+
confirmation_mode=confirmation_mode,
|
|
671
|
+
session_id=params.session_id,
|
|
672
|
+
agent_type=type(session.agent).__name__,
|
|
673
|
+
)
|
|
674
|
+
return SetSessionModeResponse()
|
|
675
|
+
|
|
676
|
+
except Exception:
|
|
677
|
+
logger.exception("Failed to set session mode", session_id=params.session_id)
|
|
678
|
+
return None
|
|
679
|
+
|
|
680
|
+
async def set_session_model( # noqa: PLR0911
|
|
681
|
+
self, params: SetSessionModelRequest
|
|
682
|
+
) -> SetSessionModelResponse | None:
|
|
683
|
+
"""Set the session model.
|
|
684
|
+
|
|
685
|
+
Changes the model for the active agent in the session.
|
|
686
|
+
Validates that the requested model is in the available models list:
|
|
687
|
+
- For ClaudeCodeAgent: validates against Claude Code models (sonnet, opus, haiku)
|
|
688
|
+
- For native Agent: validates against server's available_models
|
|
689
|
+
- For ACPAgent: validates against the nested agent's model list
|
|
690
|
+
"""
|
|
691
|
+
from agentpool.agents.acp_agent import ACPAgent as ACPAgentClient
|
|
692
|
+
from agentpool.agents.claude_code_agent import ClaudeCodeAgent
|
|
693
|
+
|
|
694
|
+
try:
|
|
695
|
+
session = self.session_manager.get_session(params.session_id)
|
|
696
|
+
if not session:
|
|
697
|
+
msg = "Session not found for model switch"
|
|
698
|
+
logger.warning(msg, session_id=params.session_id)
|
|
699
|
+
return None
|
|
700
|
+
|
|
701
|
+
# Validate model based on agent type
|
|
702
|
+
if isinstance(session.agent, ClaudeCodeAgent):
|
|
703
|
+
# For ClaudeCodeAgent, validate against our hardcoded models
|
|
704
|
+
available_ids = [m.model_id for m in CLAUDE_CODE_MODELS]
|
|
705
|
+
if params.model_id not in available_ids:
|
|
706
|
+
logger.warning(
|
|
707
|
+
"Model not in Claude Code available models",
|
|
708
|
+
model_id=params.model_id,
|
|
709
|
+
available=available_ids,
|
|
710
|
+
)
|
|
711
|
+
return None
|
|
712
|
+
await session.agent.set_model(params.model_id)
|
|
713
|
+
logger.info("Set model", model_id=params.model_id, session_id=params.session_id)
|
|
714
|
+
return SetSessionModelResponse()
|
|
715
|
+
|
|
716
|
+
if isinstance(session.agent, ACPAgentClient):
|
|
717
|
+
# For ACPAgent, validate against nested agent's model list
|
|
718
|
+
if session.agent._state and session.agent._state.models:
|
|
719
|
+
available_ids = [
|
|
720
|
+
m.model_id for m in session.agent._state.models.available_models
|
|
721
|
+
]
|
|
722
|
+
if params.model_id not in available_ids:
|
|
723
|
+
logger.warning(
|
|
724
|
+
"Model not in ACPAgent's available models",
|
|
725
|
+
model_id=params.model_id,
|
|
726
|
+
available=available_ids,
|
|
727
|
+
)
|
|
728
|
+
return None
|
|
729
|
+
# TODO: Use ACP protocol once set_session_model stabilizes
|
|
730
|
+
# For now, we can't actually change the model on ACPAgent
|
|
731
|
+
logger.warning(
|
|
732
|
+
"Model change for ACPAgent not yet supported (ACP protocol UNSTABLE)",
|
|
733
|
+
model_id=params.model_id,
|
|
734
|
+
)
|
|
735
|
+
return None
|
|
736
|
+
|
|
737
|
+
if isinstance(session.agent, Agent):
|
|
738
|
+
# For native Agent, validate against server's available models
|
|
739
|
+
available_ids = [m.model_id for m in self.available_models]
|
|
740
|
+
if params.model_id not in available_ids:
|
|
741
|
+
msg = "Model not in available models"
|
|
742
|
+
logger.warning(msg, model_id=params.model_id, available=available_ids)
|
|
743
|
+
return None
|
|
744
|
+
session.agent.set_model(params.model_id)
|
|
745
|
+
|
|
746
|
+
logger.info("Set model", model_id=params.model_id, session_id=params.session_id)
|
|
747
|
+
return SetSessionModelResponse()
|
|
748
|
+
except Exception:
|
|
749
|
+
logger.exception("Failed to set session model", session_id=params.session_id)
|
|
750
|
+
return None
|
|
751
|
+
|
|
752
|
+
async def swap_pool(self, config_path: str, agent: str | None = None) -> list[str]:
|
|
753
|
+
"""Swap the agent pool with a new one from configuration.
|
|
754
|
+
|
|
755
|
+
This coordinates the full pool swap:
|
|
756
|
+
1. Closes all active sessions
|
|
757
|
+
2. Delegates to server.swap_pool() for pool lifecycle
|
|
758
|
+
3. Updates internal references
|
|
759
|
+
|
|
760
|
+
Args:
|
|
761
|
+
config_path: Path to the new agent configuration file
|
|
762
|
+
agent: Optional specific agent to use as default
|
|
763
|
+
|
|
764
|
+
Returns:
|
|
765
|
+
List of agent names in the new pool
|
|
766
|
+
|
|
767
|
+
Raises:
|
|
768
|
+
RuntimeError: If server reference is not set
|
|
769
|
+
ValueError: If config is invalid or agent not found
|
|
770
|
+
"""
|
|
771
|
+
if not self.server:
|
|
772
|
+
msg = "Server reference not set - cannot swap pool"
|
|
773
|
+
raise RuntimeError(msg)
|
|
774
|
+
|
|
775
|
+
logger.info("Swapping pool", config_path=config_path, agent=agent)
|
|
776
|
+
# 1. Close all active sessions
|
|
777
|
+
closed_count = await self.session_manager.close_all_sessions()
|
|
778
|
+
logger.info("Closed sessions before pool swap", count=closed_count)
|
|
779
|
+
# 2. Delegate to server for pool lifecycle management
|
|
780
|
+
agent_names = await self.server.swap_pool(config_path, agent)
|
|
781
|
+
# 3. Update internal references
|
|
782
|
+
self.agent_pool = self.server.pool
|
|
783
|
+
self.session_manager._pool = self.server.pool
|
|
784
|
+
self.default_agent = agent
|
|
785
|
+
logger.info("Pool swap complete", agent_names=agent_names)
|
|
786
|
+
return agent_names
|