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,288 @@
|
|
|
1
|
+
"""Tool to chain multiple function calls."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import asyncio
|
|
6
|
+
from dataclasses import dataclass
|
|
7
|
+
from enum import StrEnum
|
|
8
|
+
from typing import Any, Literal, assert_never
|
|
9
|
+
|
|
10
|
+
import anyio
|
|
11
|
+
from pydantic import Field
|
|
12
|
+
from pydantic_ai import ModelRetry
|
|
13
|
+
from schemez import Schema
|
|
14
|
+
|
|
15
|
+
from agentpool.agents.context import AgentContext # noqa: TC001
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class ErrorStrategy(StrEnum):
|
|
19
|
+
"""Strategy for handling errors in the pipeline."""
|
|
20
|
+
|
|
21
|
+
STOP = "stop" # Stop pipeline on error
|
|
22
|
+
SKIP = "skip" # Skip failed step, continue with previous result
|
|
23
|
+
DEFAULT = "default" # Use provided default value
|
|
24
|
+
RETRY = "retry" # Retry the step
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class StepCondition(Schema):
|
|
28
|
+
"""Condition for conditional execution."""
|
|
29
|
+
|
|
30
|
+
field: str # Field to check in result
|
|
31
|
+
operator: Literal["eq", "gt", "lt", "contains", "exists"]
|
|
32
|
+
value: Any = None
|
|
33
|
+
|
|
34
|
+
def evaluate_with_value(self, value: Any) -> bool:
|
|
35
|
+
"""Evaluate this condition against a value.
|
|
36
|
+
|
|
37
|
+
Args:
|
|
38
|
+
value: The value to evaluate against the condition.
|
|
39
|
+
|
|
40
|
+
Returns:
|
|
41
|
+
bool: True if the condition is met, False otherwise.
|
|
42
|
+
"""
|
|
43
|
+
field_value = value.get(self.field) if isinstance(value, dict) else value
|
|
44
|
+
|
|
45
|
+
match self.operator:
|
|
46
|
+
case "eq":
|
|
47
|
+
return bool(field_value == self.value)
|
|
48
|
+
case "gt":
|
|
49
|
+
return bool(field_value > self.value)
|
|
50
|
+
case "lt":
|
|
51
|
+
return bool(field_value < self.value)
|
|
52
|
+
case "contains":
|
|
53
|
+
try:
|
|
54
|
+
return self.value in field_value # type: ignore[operator]
|
|
55
|
+
except TypeError:
|
|
56
|
+
return False
|
|
57
|
+
case "exists":
|
|
58
|
+
return field_value is not None
|
|
59
|
+
case _ as unreachable:
|
|
60
|
+
assert_never(unreachable)
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
@dataclass
|
|
64
|
+
class StepResult:
|
|
65
|
+
"""Result of a pipeline step execution."""
|
|
66
|
+
|
|
67
|
+
success: bool
|
|
68
|
+
result: Any
|
|
69
|
+
error: Exception | None = None
|
|
70
|
+
retries: int = 0
|
|
71
|
+
duration: float = 0.0
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
# Type alias for step results during execution
|
|
75
|
+
type StepResults = dict[str, StepResult]
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
class PipelineStep(Schema):
|
|
79
|
+
"""Single step in a tool pipeline."""
|
|
80
|
+
|
|
81
|
+
tool: str
|
|
82
|
+
input_kwarg: str = "text"
|
|
83
|
+
keyword_args: dict[str, Any] = Field(default_factory=dict)
|
|
84
|
+
name: str | None = None # Optional step name for referencing
|
|
85
|
+
condition: StepCondition | None = None # Conditional execution
|
|
86
|
+
error_strategy: ErrorStrategy = ErrorStrategy.STOP
|
|
87
|
+
default_value: Any = None # Used with ErrorStrategy.DEFAULT
|
|
88
|
+
max_retries: int = 0
|
|
89
|
+
retry_delay: float = 1.0
|
|
90
|
+
timeout: float | None = None
|
|
91
|
+
depends_on: list[str] = Field(default_factory=list) # Step dependencies
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
class Pipeline(Schema):
|
|
95
|
+
"""A pipeline of tool operations."""
|
|
96
|
+
|
|
97
|
+
input: str | dict[str, Any]
|
|
98
|
+
steps: list[PipelineStep]
|
|
99
|
+
mode: Literal["sequential", "parallel"] = "sequential"
|
|
100
|
+
max_parallel: int = 5 # Max concurrent steps
|
|
101
|
+
collect_metrics: bool = False # Collect execution metrics
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
async def _execute_step(
|
|
105
|
+
ctx: AgentContext,
|
|
106
|
+
step: PipelineStep,
|
|
107
|
+
input_value: Any,
|
|
108
|
+
results: StepResults,
|
|
109
|
+
) -> StepResult:
|
|
110
|
+
"""Execute a single pipeline step."""
|
|
111
|
+
start_time = asyncio.get_event_loop().time()
|
|
112
|
+
retries = 0
|
|
113
|
+
|
|
114
|
+
while True:
|
|
115
|
+
try:
|
|
116
|
+
# Check condition if any
|
|
117
|
+
if step.condition and not step.condition.evaluate_with_value(input_value):
|
|
118
|
+
return StepResult(success=True, result=input_value, duration=0)
|
|
119
|
+
|
|
120
|
+
tool_info = await ctx.agent.tools.get_tool(step.tool) # Get the tool
|
|
121
|
+
if isinstance(input_value, dict): # Prepare kwargs
|
|
122
|
+
kwargs = {**input_value, **step.keyword_args}
|
|
123
|
+
else:
|
|
124
|
+
kwargs = {step.input_kwarg: input_value, **step.keyword_args}
|
|
125
|
+
|
|
126
|
+
# Execute with timeout if specified
|
|
127
|
+
if step.timeout:
|
|
128
|
+
fut = tool_info.execute(ctx, **kwargs)
|
|
129
|
+
result = await asyncio.wait_for(fut, timeout=step.timeout)
|
|
130
|
+
else:
|
|
131
|
+
result = await tool_info.execute(ctx, **kwargs)
|
|
132
|
+
|
|
133
|
+
duration = asyncio.get_event_loop().time() - start_time
|
|
134
|
+
return StepResult(success=True, result=result, duration=duration)
|
|
135
|
+
|
|
136
|
+
except Exception as exc:
|
|
137
|
+
match step.error_strategy:
|
|
138
|
+
case ErrorStrategy.STOP:
|
|
139
|
+
raise
|
|
140
|
+
|
|
141
|
+
case ErrorStrategy.SKIP:
|
|
142
|
+
duration = asyncio.get_event_loop().time() - start_time
|
|
143
|
+
return StepResult(
|
|
144
|
+
success=False,
|
|
145
|
+
result=input_value,
|
|
146
|
+
error=exc,
|
|
147
|
+
duration=duration,
|
|
148
|
+
)
|
|
149
|
+
|
|
150
|
+
case ErrorStrategy.DEFAULT:
|
|
151
|
+
duration = asyncio.get_event_loop().time() - start_time
|
|
152
|
+
return StepResult(
|
|
153
|
+
success=False,
|
|
154
|
+
result=step.default_value,
|
|
155
|
+
error=exc,
|
|
156
|
+
duration=duration,
|
|
157
|
+
)
|
|
158
|
+
|
|
159
|
+
case ErrorStrategy.RETRY:
|
|
160
|
+
retries += 1
|
|
161
|
+
if retries <= step.max_retries:
|
|
162
|
+
await anyio.sleep(step.retry_delay)
|
|
163
|
+
continue
|
|
164
|
+
raise # Max retries exceeded
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
async def _execute_sequential(ctx: AgentContext, pipeline: Pipeline, results: StepResults) -> Any:
|
|
168
|
+
"""Execute steps sequentially."""
|
|
169
|
+
current = pipeline.input
|
|
170
|
+
for step in pipeline.steps:
|
|
171
|
+
result = await _execute_step(ctx, step, current, results)
|
|
172
|
+
if step.name:
|
|
173
|
+
results[step.name] = result
|
|
174
|
+
current = result.result
|
|
175
|
+
return current
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
async def _execute_parallel(ctx: AgentContext, pipeline: Pipeline, results: StepResults) -> Any:
|
|
179
|
+
"""Execute independent steps in parallel."""
|
|
180
|
+
semaphore = asyncio.Semaphore(pipeline.max_parallel)
|
|
181
|
+
|
|
182
|
+
async def run_step(step: PipelineStep) -> None:
|
|
183
|
+
async with semaphore:
|
|
184
|
+
# Wait for dependencies
|
|
185
|
+
for dep in step.depends_on:
|
|
186
|
+
while dep not in results:
|
|
187
|
+
await anyio.sleep(0.1)
|
|
188
|
+
|
|
189
|
+
# Get input from dependency or pipeline input
|
|
190
|
+
input_value = results[step.depends_on[-1]].result if step.depends_on else pipeline.input
|
|
191
|
+
result = await _execute_step(ctx, step, input_value, results)
|
|
192
|
+
if step.name:
|
|
193
|
+
results[step.name] = result
|
|
194
|
+
|
|
195
|
+
# Create tasks for all steps
|
|
196
|
+
tasks = [run_step(step) for step in pipeline.steps]
|
|
197
|
+
await asyncio.gather(*tasks)
|
|
198
|
+
# Return last result
|
|
199
|
+
return results[name].result if (name := pipeline.steps[-1].name) else None
|
|
200
|
+
|
|
201
|
+
|
|
202
|
+
async def chain_tools(
|
|
203
|
+
ctx: AgentContext,
|
|
204
|
+
input_data: str | dict[str, Any],
|
|
205
|
+
steps: list[dict[str, Any]],
|
|
206
|
+
mode: Literal["sequential", "parallel"] = "sequential",
|
|
207
|
+
max_parallel: int = 5,
|
|
208
|
+
collect_metrics: bool = False,
|
|
209
|
+
) -> Any:
|
|
210
|
+
"""Execute multiple tool operations in sequence or parallel.
|
|
211
|
+
|
|
212
|
+
WHEN TO USE THIS TOOL:
|
|
213
|
+
- Use this when you can plan multiple operations confidently in advance
|
|
214
|
+
- Use this for common sequences you've successfully used before
|
|
215
|
+
- Use this to reduce interaction rounds for known operation patterns
|
|
216
|
+
- Use this when all steps are independent of intermediate results
|
|
217
|
+
|
|
218
|
+
DO NOT USE THIS TOOL:
|
|
219
|
+
- When you need to inspect intermediate results
|
|
220
|
+
- When next steps depend on analyzing previous results
|
|
221
|
+
- When you're unsure about the complete sequence
|
|
222
|
+
- When you need to handle errors at each step individually
|
|
223
|
+
|
|
224
|
+
Args:
|
|
225
|
+
ctx: Agent context for tool execution
|
|
226
|
+
input_data: Initial input for the pipeline
|
|
227
|
+
steps: List of step configurations, each containing:
|
|
228
|
+
- tool: Name of the tool to execute
|
|
229
|
+
- input_kwarg: Keyword argument name for input (default: "text")
|
|
230
|
+
- keyword_args: Additional keyword arguments
|
|
231
|
+
- name: Optional step name for referencing
|
|
232
|
+
- condition: Optional execution condition
|
|
233
|
+
- error_strategy: How to handle errors ("stop", "skip", "default", "retry")
|
|
234
|
+
- default_value: Value to use with "default" error strategy
|
|
235
|
+
- max_retries: Maximum retry attempts
|
|
236
|
+
- retry_delay: Delay between retries in seconds
|
|
237
|
+
- timeout: Step timeout in seconds
|
|
238
|
+
- depends_on: List of step names this depends on
|
|
239
|
+
mode: Execution mode - "sequential" or "parallel"
|
|
240
|
+
max_parallel: Maximum concurrent steps for parallel mode
|
|
241
|
+
collect_metrics: Whether to collect execution metrics
|
|
242
|
+
|
|
243
|
+
Examples:
|
|
244
|
+
# Sequential processing
|
|
245
|
+
await chain_tools(
|
|
246
|
+
ctx,
|
|
247
|
+
input_data="main.py",
|
|
248
|
+
steps=[
|
|
249
|
+
{"tool": "load_resource", "input_kwarg": "name"},
|
|
250
|
+
{"tool": "analyze_code", "input_kwarg": "code"},
|
|
251
|
+
{"tool": "format_output", "input_kwarg": "text"}
|
|
252
|
+
]
|
|
253
|
+
)
|
|
254
|
+
|
|
255
|
+
# Parallel processing with dependencies
|
|
256
|
+
await chain_tools(
|
|
257
|
+
ctx,
|
|
258
|
+
input_data="test.py",
|
|
259
|
+
mode="parallel",
|
|
260
|
+
steps=[
|
|
261
|
+
{"tool": "load_resource", "input_kwarg": "name", "name": "load"},
|
|
262
|
+
{"tool": "analyze_code", "input_kwarg": "code", "depends_on": ["load"]},
|
|
263
|
+
{"tool": "count_tokens", "input_kwarg": "text", "depends_on": ["load"]}
|
|
264
|
+
]
|
|
265
|
+
)
|
|
266
|
+
"""
|
|
267
|
+
try:
|
|
268
|
+
pipeline = Pipeline(
|
|
269
|
+
input=input_data,
|
|
270
|
+
steps=[PipelineStep.model_validate(step) for step in steps],
|
|
271
|
+
mode=mode,
|
|
272
|
+
max_parallel=max_parallel,
|
|
273
|
+
collect_metrics=collect_metrics,
|
|
274
|
+
)
|
|
275
|
+
except Exception as e:
|
|
276
|
+
msg = f"Invalid pipeline configuration: {e}"
|
|
277
|
+
raise ModelRetry(msg) from e
|
|
278
|
+
results: StepResults = {}
|
|
279
|
+
|
|
280
|
+
try:
|
|
281
|
+
match pipeline.mode:
|
|
282
|
+
case "sequential":
|
|
283
|
+
return await _execute_sequential(ctx, pipeline, results)
|
|
284
|
+
case "parallel":
|
|
285
|
+
return await _execute_parallel(ctx, pipeline, results)
|
|
286
|
+
except Exception as e:
|
|
287
|
+
msg = f"Failed to execute pipeline: {e}"
|
|
288
|
+
raise ModelRetry(msg) from e
|
|
@@ -0,0 +1,398 @@
|
|
|
1
|
+
"""Provider for code formatting and linting tools."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import importlib.util
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
import re
|
|
8
|
+
from typing import TYPE_CHECKING, Any
|
|
9
|
+
|
|
10
|
+
from fsspec import AbstractFileSystem
|
|
11
|
+
from upathtools import is_directory
|
|
12
|
+
|
|
13
|
+
from agentpool.agents.context import AgentContext # noqa: TC001
|
|
14
|
+
from agentpool.log import get_logger
|
|
15
|
+
from agentpool.resource_providers import ResourceProvider
|
|
16
|
+
from agentpool_toolsets.fsspec_toolset.diagnostics import DiagnosticsManager
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
if TYPE_CHECKING:
|
|
20
|
+
from anyenv.lsp_servers import Diagnostic
|
|
21
|
+
from exxec.base import ExecutionEnvironment
|
|
22
|
+
|
|
23
|
+
from agentpool.tools.base import Tool
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
logger = get_logger(__name__)
|
|
27
|
+
|
|
28
|
+
# Map file extensions to ast-grep language identifiers
|
|
29
|
+
EXTENSION_TO_LANGUAGE: dict[str, str] = {
|
|
30
|
+
".py": "python",
|
|
31
|
+
".js": "javascript",
|
|
32
|
+
".jsx": "javascript",
|
|
33
|
+
".ts": "typescript",
|
|
34
|
+
".tsx": "tsx",
|
|
35
|
+
".rs": "rust",
|
|
36
|
+
".go": "go",
|
|
37
|
+
".c": "c",
|
|
38
|
+
".h": "c",
|
|
39
|
+
".cpp": "cpp",
|
|
40
|
+
".cc": "cpp",
|
|
41
|
+
".cxx": "cpp",
|
|
42
|
+
".hpp": "cpp",
|
|
43
|
+
".java": "java",
|
|
44
|
+
".kt": "kotlin",
|
|
45
|
+
".rb": "ruby",
|
|
46
|
+
".swift": "swift",
|
|
47
|
+
".lua": "lua",
|
|
48
|
+
".cs": "csharp",
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def _substitute_metavars(match: Any, fix_pattern: str, source_code: str) -> str:
|
|
53
|
+
"""Substitute $METAVARS and $$$METAVARS in fix pattern with captured values."""
|
|
54
|
+
result = fix_pattern
|
|
55
|
+
|
|
56
|
+
# Handle $$$ multi-match metavars first (greedy match)
|
|
57
|
+
for metavar in re.findall(r"\$\$\$([A-Z_][A-Z0-9_]*)", fix_pattern):
|
|
58
|
+
captured_list = match.get_multiple_matches(metavar)
|
|
59
|
+
if captured_list:
|
|
60
|
+
first = captured_list[0]
|
|
61
|
+
last = captured_list[-1]
|
|
62
|
+
start_idx = first.range().start.index
|
|
63
|
+
end_idx = last.range().end.index
|
|
64
|
+
original_span = source_code[start_idx:end_idx]
|
|
65
|
+
result = result.replace(f"$$${metavar}", original_span)
|
|
66
|
+
|
|
67
|
+
# Handle single $ metavars
|
|
68
|
+
for metavar in re.findall(r"(?<!\$)\$([A-Z_][A-Z0-9_]*)", fix_pattern):
|
|
69
|
+
captured = match.get_match(metavar)
|
|
70
|
+
if captured:
|
|
71
|
+
result = result.replace(f"${metavar}", captured.text())
|
|
72
|
+
|
|
73
|
+
return result
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def _detect_language(path: str) -> str | None:
|
|
77
|
+
"""Detect ast-grep language from file extension."""
|
|
78
|
+
suffix = Path(path).suffix.lower()
|
|
79
|
+
return EXTENSION_TO_LANGUAGE.get(suffix)
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
class CodeTools(ResourceProvider):
|
|
83
|
+
"""Provider for code analysis and transformation tools."""
|
|
84
|
+
|
|
85
|
+
def __init__(
|
|
86
|
+
self,
|
|
87
|
+
env: ExecutionEnvironment | None = None,
|
|
88
|
+
name: str = "code",
|
|
89
|
+
cwd: str | None = None,
|
|
90
|
+
) -> None:
|
|
91
|
+
"""Initialize with an optional execution environment.
|
|
92
|
+
|
|
93
|
+
Args:
|
|
94
|
+
env: Execution environment to operate on. If None, falls back to agent.env
|
|
95
|
+
name: Name for this toolset provider
|
|
96
|
+
cwd: Optional cwd to resolve relative paths against
|
|
97
|
+
"""
|
|
98
|
+
from fsspec.asyn import AsyncFileSystem
|
|
99
|
+
from fsspec.implementations.asyn_wrapper import AsyncFileSystemWrapper
|
|
100
|
+
from morefs.asyn_local import AsyncLocalFileSystem
|
|
101
|
+
|
|
102
|
+
super().__init__(name=name)
|
|
103
|
+
|
|
104
|
+
self.execution_env = env
|
|
105
|
+
self.cwd = cwd or (env.cwd if env else None)
|
|
106
|
+
fs = env.get_fs() if env else None
|
|
107
|
+
match fs:
|
|
108
|
+
case AsyncFileSystem():
|
|
109
|
+
self.fs = fs
|
|
110
|
+
case AbstractFileSystem():
|
|
111
|
+
self.fs = AsyncFileSystemWrapper(fs)
|
|
112
|
+
case None:
|
|
113
|
+
self.fs = AsyncLocalFileSystem()
|
|
114
|
+
self._tools: list[Tool] | None = None
|
|
115
|
+
self._diagnostics: DiagnosticsManager | None = None
|
|
116
|
+
|
|
117
|
+
def _get_env(self, agent_ctx: AgentContext) -> ExecutionEnvironment | None:
|
|
118
|
+
"""Get execution environment (explicit or from agent)."""
|
|
119
|
+
return self.execution_env or agent_ctx.agent.env
|
|
120
|
+
|
|
121
|
+
def _resolve_path(self, path: str, agent_ctx: AgentContext) -> str:
|
|
122
|
+
"""Resolve a potentially relative path to an absolute path.
|
|
123
|
+
|
|
124
|
+
Gets cwd from self.cwd, execution_env.cwd, or agent.env.cwd.
|
|
125
|
+
If cwd is set and path is relative, resolves relative to cwd.
|
|
126
|
+
Otherwise returns the path as-is.
|
|
127
|
+
"""
|
|
128
|
+
env = self._get_env(agent_ctx)
|
|
129
|
+
cwd = self.cwd or (env.cwd if env else None)
|
|
130
|
+
if cwd and not (path.startswith("/") or (len(path) > 1 and path[1] == ":")):
|
|
131
|
+
return str(Path(cwd) / path)
|
|
132
|
+
return path
|
|
133
|
+
|
|
134
|
+
async def get_tools(self) -> list[Tool]:
|
|
135
|
+
"""Get code analysis tools."""
|
|
136
|
+
if self._tools is not None:
|
|
137
|
+
return self._tools
|
|
138
|
+
|
|
139
|
+
self._tools = [self.create_tool(self.format_code, category="execute")]
|
|
140
|
+
if importlib.util.find_spec("ast_grep_py"):
|
|
141
|
+
self._tools.append(self.create_tool(self.ast_grep, category="search", idempotent=True))
|
|
142
|
+
# Always register - checks for env at runtime (self.execution_env or agent.env)
|
|
143
|
+
self._tools.append(
|
|
144
|
+
self.create_tool(self.run_diagnostics, category="search", idempotent=True)
|
|
145
|
+
)
|
|
146
|
+
return self._tools
|
|
147
|
+
|
|
148
|
+
async def format_code( # noqa: D417
|
|
149
|
+
self, agent_ctx: AgentContext, path: str, language: str | None = None
|
|
150
|
+
) -> str:
|
|
151
|
+
"""Format and lint a code file, returning a concise summary.
|
|
152
|
+
|
|
153
|
+
Args:
|
|
154
|
+
path: Path to the file to format
|
|
155
|
+
language: Programming language (auto-detected from extension if not provided)
|
|
156
|
+
|
|
157
|
+
Returns:
|
|
158
|
+
Short status message about formatting/linting results
|
|
159
|
+
"""
|
|
160
|
+
from anyenv.language_formatters import FormatterRegistry
|
|
161
|
+
|
|
162
|
+
resolved = self._resolve_path(path, agent_ctx)
|
|
163
|
+
try:
|
|
164
|
+
content = await self.fs._cat_file(resolved)
|
|
165
|
+
code = content.decode("utf-8") if isinstance(content, bytes) else content
|
|
166
|
+
except FileNotFoundError:
|
|
167
|
+
return f"❌ File not found: {path}"
|
|
168
|
+
|
|
169
|
+
registry = FormatterRegistry("local")
|
|
170
|
+
registry.register_default_formatters()
|
|
171
|
+
# Get formatter by language or detect from extension/content
|
|
172
|
+
formatter = None
|
|
173
|
+
if language:
|
|
174
|
+
formatter = registry.get_formatter_by_language(language)
|
|
175
|
+
if not formatter:
|
|
176
|
+
detected = _detect_language(path)
|
|
177
|
+
if detected:
|
|
178
|
+
formatter = registry.get_formatter_by_language(detected)
|
|
179
|
+
if not formatter:
|
|
180
|
+
detected = registry.detect_language_from_content(code)
|
|
181
|
+
if detected:
|
|
182
|
+
formatter = registry.get_formatter_by_language(detected)
|
|
183
|
+
if not formatter:
|
|
184
|
+
return f"❌ Unsupported language: {language or 'unknown'}"
|
|
185
|
+
|
|
186
|
+
try:
|
|
187
|
+
result = await formatter.format_and_lint_string(code, fix=True)
|
|
188
|
+
|
|
189
|
+
if result.success:
|
|
190
|
+
# Write back if formatted
|
|
191
|
+
if result.format_result.formatted and result.format_result.output:
|
|
192
|
+
await self.fs._pipe_file(resolved, result.format_result.output.encode("utf-8"))
|
|
193
|
+
changes = "formatted and saved"
|
|
194
|
+
else:
|
|
195
|
+
changes = "no changes needed"
|
|
196
|
+
lint_status = "clean" if result.lint_result.success else "has issues"
|
|
197
|
+
duration = f"{result.total_duration:.2f}s"
|
|
198
|
+
return f"✅ {path}: {changes}, {lint_status} ({duration})"
|
|
199
|
+
|
|
200
|
+
errors = []
|
|
201
|
+
if not result.format_result.success:
|
|
202
|
+
errors.append(f"format: {result.format_result.error_type}")
|
|
203
|
+
if not result.lint_result.success:
|
|
204
|
+
errors.append(f"lint: {result.lint_result.error_type}")
|
|
205
|
+
return f"❌ Failed: {', '.join(errors)}"
|
|
206
|
+
|
|
207
|
+
except Exception as e: # noqa: BLE001
|
|
208
|
+
return f"❌ Error: {type(e).__name__}: {e}"
|
|
209
|
+
|
|
210
|
+
async def ast_grep( # noqa: D417
|
|
211
|
+
self,
|
|
212
|
+
agent_ctx: AgentContext,
|
|
213
|
+
path: str,
|
|
214
|
+
rule: dict[str, Any],
|
|
215
|
+
fix: str | None = None,
|
|
216
|
+
dry_run: bool = True,
|
|
217
|
+
) -> dict[str, Any]:
|
|
218
|
+
"""Search or transform code in a file using AST patterns.
|
|
219
|
+
|
|
220
|
+
Uses ast-grep for structural code search and rewriting based on abstract
|
|
221
|
+
syntax trees. More precise than regex - understands code structure.
|
|
222
|
+
|
|
223
|
+
Args:
|
|
224
|
+
path: Path to the file to analyze
|
|
225
|
+
rule: AST matching rule dict (see examples below)
|
|
226
|
+
fix: Optional replacement pattern using $METAVARS from the rule
|
|
227
|
+
dry_run: If True, show changes without applying. If False, write changes.
|
|
228
|
+
|
|
229
|
+
Returns:
|
|
230
|
+
Dict with matches and optionally transformed code
|
|
231
|
+
|
|
232
|
+
## Pattern Syntax
|
|
233
|
+
|
|
234
|
+
- `$NAME` - captures single node (identifier, expression, etc.)
|
|
235
|
+
- `$$$ITEMS` - captures multiple nodes (arguments, statements, etc.)
|
|
236
|
+
- Patterns match structurally, not textually
|
|
237
|
+
|
|
238
|
+
## Rule Keys
|
|
239
|
+
|
|
240
|
+
| Key | Description | Example |
|
|
241
|
+
|-----|-------------|---------|
|
|
242
|
+
| pattern | Code pattern with metavars | `"print($MSG)"` |
|
|
243
|
+
| kind | AST node type | `"function_definition"` |
|
|
244
|
+
| regex | Regex on node text | `"^test_"` |
|
|
245
|
+
| inside | Must be inside matching node | `{"kind": "class_definition"}` |
|
|
246
|
+
| has | Must contain matching node | `{"pattern": "return"}` |
|
|
247
|
+
| all | All rules must match | `[{"kind": "call"}, {"has": ...}]` |
|
|
248
|
+
| any | Any rule must match | `[{"pattern": "print"}, {"pattern": "log"}]` |
|
|
249
|
+
|
|
250
|
+
## Examples
|
|
251
|
+
|
|
252
|
+
**Find all print calls:**
|
|
253
|
+
```
|
|
254
|
+
rule={"pattern": "print($MSG)"}
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
**Find and replace console.log:**
|
|
258
|
+
```
|
|
259
|
+
rule={"pattern": "console.log($MSG)"}
|
|
260
|
+
fix="logger.info($MSG)"
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
**Find functions containing await:**
|
|
264
|
+
```
|
|
265
|
+
rule={
|
|
266
|
+
"kind": "function_definition",
|
|
267
|
+
"has": {"pattern": "await $EXPR"}
|
|
268
|
+
}
|
|
269
|
+
```
|
|
270
|
+
"""
|
|
271
|
+
from ast_grep_py import SgRoot
|
|
272
|
+
|
|
273
|
+
resolved = self._resolve_path(path, agent_ctx)
|
|
274
|
+
|
|
275
|
+
# Detect language from extension
|
|
276
|
+
language = _detect_language(path)
|
|
277
|
+
if not language:
|
|
278
|
+
return {"error": f"Cannot detect language for: {path}"}
|
|
279
|
+
|
|
280
|
+
# Read file
|
|
281
|
+
try:
|
|
282
|
+
content = await self.fs._cat_file(resolved)
|
|
283
|
+
code = content.decode("utf-8") if isinstance(content, bytes) else content
|
|
284
|
+
except FileNotFoundError:
|
|
285
|
+
return {"error": f"File not found: {path}"}
|
|
286
|
+
|
|
287
|
+
root = SgRoot(code, language)
|
|
288
|
+
node = root.root()
|
|
289
|
+
matches = node.find_all(**rule)
|
|
290
|
+
|
|
291
|
+
result: dict[str, Any] = {
|
|
292
|
+
"path": path,
|
|
293
|
+
"language": language,
|
|
294
|
+
"match_count": len(matches),
|
|
295
|
+
"matches": [
|
|
296
|
+
{
|
|
297
|
+
"text": m.text(),
|
|
298
|
+
"range": {
|
|
299
|
+
"start": {
|
|
300
|
+
"line": m.range().start.line,
|
|
301
|
+
"column": m.range().start.column,
|
|
302
|
+
},
|
|
303
|
+
"end": {
|
|
304
|
+
"line": m.range().end.line,
|
|
305
|
+
"column": m.range().end.column,
|
|
306
|
+
},
|
|
307
|
+
},
|
|
308
|
+
"kind": m.kind(),
|
|
309
|
+
}
|
|
310
|
+
for m in matches
|
|
311
|
+
],
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
if fix and matches:
|
|
315
|
+
edits = [m.replace(_substitute_metavars(m, fix, code)) for m in matches]
|
|
316
|
+
fixed_code = node.commit_edits(edits)
|
|
317
|
+
result["fixed_code"] = fixed_code
|
|
318
|
+
result["dry_run"] = dry_run
|
|
319
|
+
|
|
320
|
+
if not dry_run:
|
|
321
|
+
await self.fs._pipe_file(resolved, fixed_code.encode("utf-8"))
|
|
322
|
+
result["written"] = True
|
|
323
|
+
|
|
324
|
+
return result
|
|
325
|
+
|
|
326
|
+
async def run_diagnostics(self, agent_ctx: AgentContext, path: str) -> str: # noqa: PLR0911, D417
|
|
327
|
+
"""Run LSP diagnostics (type checking, linting) on files.
|
|
328
|
+
|
|
329
|
+
Uses available CLI diagnostic tools (pyright, mypy, ty, oxlint, biome, etc.)
|
|
330
|
+
to check code for errors, warnings, and style issues.
|
|
331
|
+
|
|
332
|
+
Args:
|
|
333
|
+
path: Path to file or directory to check. For directories, checks all
|
|
334
|
+
supported files recursively.
|
|
335
|
+
|
|
336
|
+
Returns:
|
|
337
|
+
Formatted diagnostic output showing errors, warnings, and info messages.
|
|
338
|
+
Returns a message if no diagnostics tools are available.
|
|
339
|
+
|
|
340
|
+
Note:
|
|
341
|
+
Only available when CodeTools is initialized with an ExecutionEnvironment.
|
|
342
|
+
Automatically detects available diagnostic tools and runs appropriate
|
|
343
|
+
ones based on file extensions.
|
|
344
|
+
"""
|
|
345
|
+
resolved = self._resolve_path(path, agent_ctx)
|
|
346
|
+
|
|
347
|
+
# Get or create diagnostics manager
|
|
348
|
+
if self._diagnostics is None:
|
|
349
|
+
env = self._get_env(agent_ctx)
|
|
350
|
+
if not env:
|
|
351
|
+
return "Diagnostics unavailable: no execution environment configured"
|
|
352
|
+
self._diagnostics = DiagnosticsManager(env)
|
|
353
|
+
|
|
354
|
+
# Check if path is directory or file
|
|
355
|
+
try:
|
|
356
|
+
is_dir = await self.fs._isdir(resolved)
|
|
357
|
+
except Exception: # noqa: BLE001
|
|
358
|
+
is_dir = False
|
|
359
|
+
|
|
360
|
+
if is_dir:
|
|
361
|
+
# Collect all files in directory
|
|
362
|
+
try:
|
|
363
|
+
files = await self.fs._find(resolved, detail=True)
|
|
364
|
+
# Filter to only include files (not directories)
|
|
365
|
+
file_paths = [
|
|
366
|
+
path
|
|
367
|
+
for path, info in files.items() # pyright: ignore[reportAttributeAccessIssue]
|
|
368
|
+
if not await is_directory(self.fs, path, entry_type=info["type"])
|
|
369
|
+
]
|
|
370
|
+
except Exception as e: # noqa: BLE001
|
|
371
|
+
return f"Error scanning directory: {e}"
|
|
372
|
+
|
|
373
|
+
if not file_paths:
|
|
374
|
+
return f"No files found in: {path}"
|
|
375
|
+
|
|
376
|
+
all_diagnostics: list[Diagnostic] = []
|
|
377
|
+
for file_path in file_paths:
|
|
378
|
+
diagnostics = await self._diagnostics.run_for_file(file_path)
|
|
379
|
+
all_diagnostics.extend(diagnostics)
|
|
380
|
+
|
|
381
|
+
if not all_diagnostics:
|
|
382
|
+
return f"No issues found in {len(file_paths)} files"
|
|
383
|
+
|
|
384
|
+
formatted = self._diagnostics.format_diagnostics(all_diagnostics)
|
|
385
|
+
return f"Found {len(all_diagnostics)} issues:\n{formatted}"
|
|
386
|
+
# Single file
|
|
387
|
+
try:
|
|
388
|
+
diagnostics = await self._diagnostics.run_for_file(resolved)
|
|
389
|
+
except FileNotFoundError:
|
|
390
|
+
return f"File not found: {path}"
|
|
391
|
+
except Exception as e: # noqa: BLE001
|
|
392
|
+
return f"Error running diagnostics: {e}"
|
|
393
|
+
|
|
394
|
+
if not diagnostics:
|
|
395
|
+
return f"No issues found in: {path}"
|
|
396
|
+
|
|
397
|
+
formatted = self._diagnostics.format_diagnostics(diagnostics)
|
|
398
|
+
return f"Found {len(diagnostics)} issues:\n{formatted}"
|