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,291 @@
|
|
|
1
|
+
"""Debug toolset for agent self-introspection and runtime debugging."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from collections import deque
|
|
6
|
+
from dataclasses import dataclass, field
|
|
7
|
+
import logging
|
|
8
|
+
from typing import Any, Literal
|
|
9
|
+
|
|
10
|
+
from pydantic_ai import RunContext # noqa: TC002
|
|
11
|
+
|
|
12
|
+
from agentpool.agents.context import AgentContext # noqa: TC001
|
|
13
|
+
from agentpool.resource_providers import StaticResourceProvider
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
LogLevel = Literal["DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"]
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
@dataclass
|
|
20
|
+
class LogRecord:
|
|
21
|
+
"""A captured log record."""
|
|
22
|
+
|
|
23
|
+
level: str
|
|
24
|
+
logger: str
|
|
25
|
+
message: str
|
|
26
|
+
timestamp: str
|
|
27
|
+
extra: dict[str, Any] = field(default_factory=dict)
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
LEVEL_PRIORITY = {
|
|
31
|
+
"DEBUG": 10,
|
|
32
|
+
"INFO": 20,
|
|
33
|
+
"WARNING": 30,
|
|
34
|
+
"ERROR": 40,
|
|
35
|
+
"CRITICAL": 50,
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
class MemoryLogHandler(logging.Handler):
|
|
40
|
+
"""A logging handler that stores records in memory for later retrieval."""
|
|
41
|
+
|
|
42
|
+
def __init__(self, max_records: int = 1000) -> None:
|
|
43
|
+
super().__init__()
|
|
44
|
+
self.max_records = max_records
|
|
45
|
+
self.records: deque[LogRecord] = deque(maxlen=max_records)
|
|
46
|
+
self.setFormatter(logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s"))
|
|
47
|
+
|
|
48
|
+
def emit(self, record: logging.LogRecord) -> None:
|
|
49
|
+
"""Store the log record."""
|
|
50
|
+
log_record = LogRecord(
|
|
51
|
+
level=record.levelname,
|
|
52
|
+
logger=record.name,
|
|
53
|
+
message=self.format(record),
|
|
54
|
+
timestamp=self.formatter.formatTime(record) if self.formatter else "",
|
|
55
|
+
extra={k: v for k, v in record.__dict__.items() if k not in logging.LogRecord.__dict__},
|
|
56
|
+
)
|
|
57
|
+
self.records.append(log_record)
|
|
58
|
+
|
|
59
|
+
def get_records(
|
|
60
|
+
self,
|
|
61
|
+
level: LogLevel | None = None,
|
|
62
|
+
logger_filter: str | None = None,
|
|
63
|
+
limit: int | None = None,
|
|
64
|
+
) -> list[LogRecord]:
|
|
65
|
+
"""Get filtered log records.
|
|
66
|
+
|
|
67
|
+
Args:
|
|
68
|
+
level: Minimum log level to include
|
|
69
|
+
logger_filter: Only include loggers containing this string
|
|
70
|
+
limit: Maximum number of records to return (newest first)
|
|
71
|
+
|
|
72
|
+
Returns:
|
|
73
|
+
List of matching log records
|
|
74
|
+
"""
|
|
75
|
+
min_level = LEVEL_PRIORITY.get(level, 0) if level else 0
|
|
76
|
+
filtered = []
|
|
77
|
+
for record in reversed(self.records):
|
|
78
|
+
if LEVEL_PRIORITY.get(record.level, 0) < min_level:
|
|
79
|
+
continue
|
|
80
|
+
if logger_filter and logger_filter not in record.logger:
|
|
81
|
+
continue
|
|
82
|
+
filtered.append(record)
|
|
83
|
+
if limit and len(filtered) >= limit:
|
|
84
|
+
break
|
|
85
|
+
|
|
86
|
+
return filtered
|
|
87
|
+
|
|
88
|
+
def clear(self) -> None:
|
|
89
|
+
"""Clear all stored records."""
|
|
90
|
+
self.records.clear()
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
# Global memory handler instance
|
|
94
|
+
_memory_handler: MemoryLogHandler | None = None
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
def get_memory_handler() -> MemoryLogHandler:
|
|
98
|
+
"""Get or create the global memory log handler."""
|
|
99
|
+
global _memory_handler # noqa: PLW0603
|
|
100
|
+
if _memory_handler is None:
|
|
101
|
+
_memory_handler = MemoryLogHandler()
|
|
102
|
+
# Attach to root logger to capture everything
|
|
103
|
+
logging.getLogger().addHandler(_memory_handler)
|
|
104
|
+
return _memory_handler
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
def install_memory_handler(max_records: int = 1000) -> MemoryLogHandler:
|
|
108
|
+
"""Install the memory handler if not already installed.
|
|
109
|
+
|
|
110
|
+
Args:
|
|
111
|
+
max_records: Maximum number of records to keep in memory
|
|
112
|
+
|
|
113
|
+
Returns:
|
|
114
|
+
The memory handler instance
|
|
115
|
+
"""
|
|
116
|
+
global _memory_handler # noqa: PLW0603
|
|
117
|
+
if _memory_handler is None:
|
|
118
|
+
_memory_handler = MemoryLogHandler(max_records=max_records)
|
|
119
|
+
logging.getLogger().addHandler(_memory_handler)
|
|
120
|
+
return _memory_handler
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
# =============================================================================
|
|
124
|
+
# Introspection Tool
|
|
125
|
+
# =============================================================================
|
|
126
|
+
|
|
127
|
+
INTROSPECTION_USAGE = """
|
|
128
|
+
Execute Python code with full access to your runtime context.
|
|
129
|
+
|
|
130
|
+
Available in namespace:
|
|
131
|
+
- ctx: AgentContext (ctx.agent, ctx.pool, ctx.config, ctx.definition, etc.)
|
|
132
|
+
- run_ctx: pydantic-ai RunContext (current run state)
|
|
133
|
+
- me: Shortcut for ctx.agent (your Agent instance)
|
|
134
|
+
|
|
135
|
+
You can inspect yourself, the pool, other agents, your tools, and more.
|
|
136
|
+
Write an async main() function that returns the result.
|
|
137
|
+
|
|
138
|
+
Example - inspect your own tools:
|
|
139
|
+
```python
|
|
140
|
+
async def main():
|
|
141
|
+
tools = me.tools.list_tools()
|
|
142
|
+
return [t.name for t in tools]
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
Example - check pool state:
|
|
146
|
+
```python
|
|
147
|
+
async def main():
|
|
148
|
+
if ctx.pool:
|
|
149
|
+
agents = list(ctx.pool.agents.keys())
|
|
150
|
+
return f"Agents in pool: {agents}"
|
|
151
|
+
return "No pool available"
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
Example - explore with dir():
|
|
155
|
+
```python
|
|
156
|
+
async def main():
|
|
157
|
+
return dir(ctx)
|
|
158
|
+
```
|
|
159
|
+
"""
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
async def execute_introspection(ctx: AgentContext, run_ctx: RunContext[Any], code: str) -> str: # noqa: D417
|
|
163
|
+
"""Execute Python code with access to your own runtime context.
|
|
164
|
+
|
|
165
|
+
This is a debugging/development tool that gives you full access to
|
|
166
|
+
inspect and interact with your runtime environment.
|
|
167
|
+
|
|
168
|
+
Args:
|
|
169
|
+
code: Python code with async main() function to execute
|
|
170
|
+
|
|
171
|
+
Returns:
|
|
172
|
+
Result of execution or error message
|
|
173
|
+
"""
|
|
174
|
+
# Build namespace with runtime context
|
|
175
|
+
namespace: dict[str, Any] = {"ctx": ctx, "run_ctx": run_ctx, "me": ctx.agent}
|
|
176
|
+
try:
|
|
177
|
+
exec(code, namespace)
|
|
178
|
+
if "main" not in namespace:
|
|
179
|
+
return "Error: Code must define an async main() function"
|
|
180
|
+
result = await namespace["main"]()
|
|
181
|
+
return str(result) if result is not None else "Code executed successfully (no return value)"
|
|
182
|
+
except Exception as e: # noqa: BLE001
|
|
183
|
+
return f"Error executing code: {type(e).__name__}: {e}"
|
|
184
|
+
|
|
185
|
+
|
|
186
|
+
# =============================================================================
|
|
187
|
+
# Log Tools
|
|
188
|
+
# =============================================================================
|
|
189
|
+
|
|
190
|
+
|
|
191
|
+
async def get_logs(
|
|
192
|
+
level: LogLevel = "INFO",
|
|
193
|
+
logger_filter: str | None = None,
|
|
194
|
+
limit: int = 50,
|
|
195
|
+
) -> str:
|
|
196
|
+
"""Get recent log entries from memory.
|
|
197
|
+
|
|
198
|
+
Args:
|
|
199
|
+
level: Minimum log level (DEBUG, INFO, WARNING, ERROR, CRITICAL)
|
|
200
|
+
logger_filter: Only show logs from loggers containing this string
|
|
201
|
+
limit: Maximum number of log entries to return
|
|
202
|
+
|
|
203
|
+
Returns:
|
|
204
|
+
Formatted log entries
|
|
205
|
+
"""
|
|
206
|
+
handler = get_memory_handler()
|
|
207
|
+
records = handler.get_records(level=level, logger_filter=logger_filter, limit=limit)
|
|
208
|
+
|
|
209
|
+
if not records:
|
|
210
|
+
return "No log entries found matching criteria"
|
|
211
|
+
|
|
212
|
+
lines = [f"=== {len(records)} log entries (newest first) ===\n"]
|
|
213
|
+
for record in records:
|
|
214
|
+
lines.append(record.message) # noqa: PERF401
|
|
215
|
+
|
|
216
|
+
return "\n".join(lines)
|
|
217
|
+
|
|
218
|
+
|
|
219
|
+
async def clear_logs() -> str:
|
|
220
|
+
"""Clear all captured log entries from memory.
|
|
221
|
+
|
|
222
|
+
Returns:
|
|
223
|
+
Confirmation message
|
|
224
|
+
"""
|
|
225
|
+
handler = get_memory_handler()
|
|
226
|
+
count = len(handler.records)
|
|
227
|
+
handler.clear()
|
|
228
|
+
return f"Cleared {count} log entries"
|
|
229
|
+
|
|
230
|
+
|
|
231
|
+
# =============================================================================
|
|
232
|
+
# Path Tools
|
|
233
|
+
# =============================================================================
|
|
234
|
+
|
|
235
|
+
|
|
236
|
+
async def get_platform_paths() -> str:
|
|
237
|
+
"""Get platform-specific paths for agentpool.
|
|
238
|
+
|
|
239
|
+
Returns:
|
|
240
|
+
Dictionary of platform paths
|
|
241
|
+
"""
|
|
242
|
+
import platformdirs
|
|
243
|
+
|
|
244
|
+
paths = {
|
|
245
|
+
"config": platformdirs.user_config_dir("agentpool"),
|
|
246
|
+
"data": platformdirs.user_data_dir("agentpool"),
|
|
247
|
+
"cache": platformdirs.user_cache_dir("agentpool"),
|
|
248
|
+
"logs": platformdirs.user_log_dir("agentpool"),
|
|
249
|
+
"state": platformdirs.user_state_dir("agentpool"),
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
lines = ["Platform paths for agentpool:", ""]
|
|
253
|
+
for name, path in paths.items():
|
|
254
|
+
lines.append(f" {name}: {path}")
|
|
255
|
+
|
|
256
|
+
return "\n".join(lines)
|
|
257
|
+
|
|
258
|
+
|
|
259
|
+
# =============================================================================
|
|
260
|
+
# Toolset Class
|
|
261
|
+
# =============================================================================
|
|
262
|
+
|
|
263
|
+
|
|
264
|
+
class DebugTools(StaticResourceProvider):
|
|
265
|
+
"""Debug and introspection tools for agent development.
|
|
266
|
+
|
|
267
|
+
Provides tools for:
|
|
268
|
+
- Self-introspection via code execution with runtime context access
|
|
269
|
+
- Log inspection and management
|
|
270
|
+
- Platform path discovery
|
|
271
|
+
"""
|
|
272
|
+
|
|
273
|
+
def __init__(self, name: str = "debug", install_log_handler: bool = True) -> None:
|
|
274
|
+
"""Initialize debug tools.
|
|
275
|
+
|
|
276
|
+
Args:
|
|
277
|
+
name: Toolset name/namespace
|
|
278
|
+
install_log_handler: Whether to install the memory log handler
|
|
279
|
+
"""
|
|
280
|
+
super().__init__(name=name)
|
|
281
|
+
|
|
282
|
+
if install_log_handler:
|
|
283
|
+
install_memory_handler()
|
|
284
|
+
|
|
285
|
+
desc = (execute_introspection.__doc__ or "") + "\n\n" + INTROSPECTION_USAGE
|
|
286
|
+
self._tools = [
|
|
287
|
+
self.create_tool(execute_introspection, category="other", description_override=desc),
|
|
288
|
+
self.create_tool(get_logs, category="other", read_only=True, idempotent=True),
|
|
289
|
+
self.create_tool(clear_logs, category="other"),
|
|
290
|
+
self.create_tool(get_platform_paths, category="other", read_only=True, idempotent=True),
|
|
291
|
+
]
|
|
@@ -0,0 +1,381 @@
|
|
|
1
|
+
"""Provider for execution environment tools with event emission."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import TYPE_CHECKING, Any
|
|
6
|
+
import uuid
|
|
7
|
+
|
|
8
|
+
from exxec.events import OutputEvent, ProcessCompletedEvent, ProcessErrorEvent, ProcessStartedEvent
|
|
9
|
+
|
|
10
|
+
from agentpool import log
|
|
11
|
+
from agentpool.agents.context import AgentContext # noqa: TC001
|
|
12
|
+
from agentpool.resource_providers import ResourceProvider
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
logger = log.get_logger(__name__)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
if TYPE_CHECKING:
|
|
19
|
+
from exxec import ExecutionEnvironment
|
|
20
|
+
|
|
21
|
+
from agentpool.tools.base import Tool
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class ExecutionEnvironmentTools(ResourceProvider):
|
|
25
|
+
"""Provider for execution environment tools.
|
|
26
|
+
|
|
27
|
+
Combines code execution and process management capabilities
|
|
28
|
+
using any ExecutionEnvironment backend. Emits events via AgentContext.
|
|
29
|
+
|
|
30
|
+
NOTE: The ACP execution environment used handles the Terminal events of the protocol,
|
|
31
|
+
the toolset should deal with the ToolCall events for UI display purposes.
|
|
32
|
+
"""
|
|
33
|
+
|
|
34
|
+
def __init__(self, env: ExecutionEnvironment | None = None, name: str = "execution") -> None:
|
|
35
|
+
"""Initialize execution environment toolset.
|
|
36
|
+
|
|
37
|
+
Args:
|
|
38
|
+
env: Execution environment to use (defaults to LocalExecutionEnvironment)
|
|
39
|
+
name: The name of the toolset
|
|
40
|
+
"""
|
|
41
|
+
super().__init__(name=name)
|
|
42
|
+
self._env = env
|
|
43
|
+
|
|
44
|
+
def get_env(self, agent_ctx: AgentContext) -> ExecutionEnvironment:
|
|
45
|
+
"""Get execution environment, falling back to agent's env if not set.
|
|
46
|
+
|
|
47
|
+
Args:
|
|
48
|
+
agent_ctx: Agent context to get fallback env from
|
|
49
|
+
"""
|
|
50
|
+
if self._env is not None:
|
|
51
|
+
return self._env
|
|
52
|
+
return agent_ctx.agent.env
|
|
53
|
+
|
|
54
|
+
async def get_tools(self) -> list[Tool]:
|
|
55
|
+
return [
|
|
56
|
+
# Code execution tools
|
|
57
|
+
self.create_tool(self.execute_code, category="execute"),
|
|
58
|
+
self.create_tool(self.execute_command, category="execute", open_world=True),
|
|
59
|
+
# Process management tools
|
|
60
|
+
self.create_tool(self.start_process, category="execute", open_world=True),
|
|
61
|
+
self.create_tool(
|
|
62
|
+
self.get_process_output, category="execute", read_only=True, idempotent=True
|
|
63
|
+
),
|
|
64
|
+
self.create_tool(
|
|
65
|
+
self.wait_for_process, category="execute", read_only=True, idempotent=True
|
|
66
|
+
),
|
|
67
|
+
self.create_tool(self.kill_process, category="execute", destructive=True),
|
|
68
|
+
self.create_tool(self.release_process, category="execute"),
|
|
69
|
+
self.create_tool(
|
|
70
|
+
self.list_processes, category="search", read_only=True, idempotent=True
|
|
71
|
+
),
|
|
72
|
+
]
|
|
73
|
+
|
|
74
|
+
async def execute_code(self, agent_ctx: AgentContext, code: str) -> dict[str, Any]: # noqa: D417
|
|
75
|
+
"""Execute Python code and return the result.
|
|
76
|
+
|
|
77
|
+
Args:
|
|
78
|
+
code: Python code to execute
|
|
79
|
+
"""
|
|
80
|
+
process_id: str | None = None
|
|
81
|
+
output_parts: list[str] = []
|
|
82
|
+
exit_code: int | None = None
|
|
83
|
+
error_msg: str | None = None
|
|
84
|
+
duration: float | None = None
|
|
85
|
+
try:
|
|
86
|
+
async for event in self.get_env(agent_ctx).stream_code(code):
|
|
87
|
+
match event:
|
|
88
|
+
case ProcessStartedEvent(process_id=pid, command=cmd):
|
|
89
|
+
process_id = pid # save for later on.
|
|
90
|
+
await agent_ctx.events.process_started(pid, cmd, success=True)
|
|
91
|
+
case OutputEvent(data=data):
|
|
92
|
+
output_parts.append(data)
|
|
93
|
+
if process_id:
|
|
94
|
+
await agent_ctx.events.process_output(process_id, data)
|
|
95
|
+
case ProcessCompletedEvent(exit_code=code_, duration=dur):
|
|
96
|
+
exit_code = code_
|
|
97
|
+
duration = dur
|
|
98
|
+
out = "".join(output_parts)
|
|
99
|
+
if process_id:
|
|
100
|
+
await agent_ctx.events.process_exit(
|
|
101
|
+
process_id, exit_code, final_output=out
|
|
102
|
+
)
|
|
103
|
+
case ProcessErrorEvent(error=err, exit_code=code_):
|
|
104
|
+
error_msg = err
|
|
105
|
+
exit_code = code_
|
|
106
|
+
if process_id:
|
|
107
|
+
await agent_ctx.events.process_exit(
|
|
108
|
+
process_id, exit_code or 1, final_output=err
|
|
109
|
+
)
|
|
110
|
+
|
|
111
|
+
combined_output = "".join(output_parts)
|
|
112
|
+
if error_msg:
|
|
113
|
+
return {"error": error_msg, "output": combined_output, "exit_code": exit_code}
|
|
114
|
+
|
|
115
|
+
except Exception as e: # noqa: BLE001
|
|
116
|
+
error_id = process_id or f"code_{uuid.uuid4().hex[:8]}"
|
|
117
|
+
await agent_ctx.events.process_started(
|
|
118
|
+
error_id, "execute_code", success=False, error=str(e)
|
|
119
|
+
)
|
|
120
|
+
return {"error": f"Error executing code: {e}"}
|
|
121
|
+
else:
|
|
122
|
+
return {"output": combined_output, "exit_code": exit_code, "duration": duration}
|
|
123
|
+
|
|
124
|
+
async def execute_command( # noqa: PLR0915, D417
|
|
125
|
+
self,
|
|
126
|
+
agent_ctx: AgentContext,
|
|
127
|
+
command: str,
|
|
128
|
+
output_limit: int | None = None,
|
|
129
|
+
) -> dict[str, Any]:
|
|
130
|
+
"""Execute a shell command and return the output.
|
|
131
|
+
|
|
132
|
+
Args:
|
|
133
|
+
command: Shell command to execute
|
|
134
|
+
output_limit: Maximum bytes of output to return
|
|
135
|
+
"""
|
|
136
|
+
# process_id comes from exxec events (is terminal_id when using ACP)
|
|
137
|
+
process_id: str | None = None
|
|
138
|
+
stdout_parts: list[str] = []
|
|
139
|
+
stderr_parts: list[str] = []
|
|
140
|
+
exit_code: int | None = None
|
|
141
|
+
error_msg: str | None = None
|
|
142
|
+
duration: float | None = None
|
|
143
|
+
try:
|
|
144
|
+
async for event in self.get_env(agent_ctx).stream_command(command):
|
|
145
|
+
match event:
|
|
146
|
+
case ProcessStartedEvent(process_id=pid, command=cmd):
|
|
147
|
+
process_id = pid
|
|
148
|
+
if pid:
|
|
149
|
+
await agent_ctx.events.process_started(pid, cmd, success=True)
|
|
150
|
+
else:
|
|
151
|
+
logger.warning("ProcessStartedEvent missing process_id", command=cmd)
|
|
152
|
+
case OutputEvent(process_id=pid, data=data, stream=stream):
|
|
153
|
+
if stream == "stderr":
|
|
154
|
+
stderr_parts.append(data)
|
|
155
|
+
else:
|
|
156
|
+
stdout_parts.append(data)
|
|
157
|
+
if pid:
|
|
158
|
+
await agent_ctx.events.process_output(pid, data)
|
|
159
|
+
else:
|
|
160
|
+
logger.warning("OutputEvent missing process_id", stream=stream)
|
|
161
|
+
case ProcessCompletedEvent(process_id=pid, exit_code=code_, duration=dur):
|
|
162
|
+
exit_code = code_
|
|
163
|
+
duration = dur
|
|
164
|
+
combined = "".join(stdout_parts) + "".join(stderr_parts)
|
|
165
|
+
if pid:
|
|
166
|
+
await agent_ctx.events.process_exit(
|
|
167
|
+
pid, exit_code, final_output=combined
|
|
168
|
+
)
|
|
169
|
+
else:
|
|
170
|
+
msg = "ProcessCompletedEvent missing process_id,"
|
|
171
|
+
logger.warning(msg, exit_code=code_)
|
|
172
|
+
case ProcessErrorEvent(process_id=pid, error=err, exit_code=code_):
|
|
173
|
+
error_msg = err
|
|
174
|
+
exit_code = code_
|
|
175
|
+
|
|
176
|
+
stdout = "".join(stdout_parts)
|
|
177
|
+
stderr = "".join(stderr_parts)
|
|
178
|
+
# Apply output limit if specified
|
|
179
|
+
truncated = False
|
|
180
|
+
if output_limit:
|
|
181
|
+
if len(stdout.encode()) > output_limit:
|
|
182
|
+
out = stdout.encode()[-output_limit:].decode(errors="ignore")
|
|
183
|
+
stdout = "...[truncated]\n" + out
|
|
184
|
+
truncated = True
|
|
185
|
+
if len(stderr.encode()) > output_limit:
|
|
186
|
+
out = stderr.encode()[-output_limit:].decode(errors="ignore")
|
|
187
|
+
stderr = "...[truncated]\n" + out
|
|
188
|
+
truncated = True
|
|
189
|
+
if error_msg:
|
|
190
|
+
return {
|
|
191
|
+
"error": error_msg,
|
|
192
|
+
"stdout": stdout,
|
|
193
|
+
"stderr": stderr,
|
|
194
|
+
"exit_code": exit_code,
|
|
195
|
+
}
|
|
196
|
+
except Exception as e: # noqa: BLE001
|
|
197
|
+
# Use process_id from events if available, otherwise generate fallback
|
|
198
|
+
error_id = process_id or f"cmd_{uuid.uuid4().hex[:8]}"
|
|
199
|
+
await agent_ctx.events.process_started(error_id, command, success=False, error=str(e))
|
|
200
|
+
return {"success": False, "error": f"Error executing command: {e}"}
|
|
201
|
+
else:
|
|
202
|
+
return {
|
|
203
|
+
"stdout": stdout,
|
|
204
|
+
"stderr": stderr,
|
|
205
|
+
"exit_code": exit_code,
|
|
206
|
+
"duration": duration,
|
|
207
|
+
"truncated": truncated,
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
async def start_process( # noqa: D417
|
|
211
|
+
self,
|
|
212
|
+
agent_ctx: AgentContext,
|
|
213
|
+
command: str,
|
|
214
|
+
args: list[str] | None = None,
|
|
215
|
+
cwd: str | None = None,
|
|
216
|
+
env: dict[str, str] | None = None,
|
|
217
|
+
output_limit: int | None = None,
|
|
218
|
+
) -> dict[str, Any]:
|
|
219
|
+
"""Start a command in the background and return process ID.
|
|
220
|
+
|
|
221
|
+
Args:
|
|
222
|
+
command: Command to execute
|
|
223
|
+
args: Command arguments
|
|
224
|
+
cwd: Working directory
|
|
225
|
+
env: Environment variables (added to current env)
|
|
226
|
+
output_limit: Maximum bytes of output to retain
|
|
227
|
+
"""
|
|
228
|
+
manager = self.get_env(agent_ctx).process_manager
|
|
229
|
+
try:
|
|
230
|
+
process_id = await manager.start_process(
|
|
231
|
+
command=command,
|
|
232
|
+
args=args,
|
|
233
|
+
cwd=cwd,
|
|
234
|
+
env=env,
|
|
235
|
+
output_limit=output_limit,
|
|
236
|
+
)
|
|
237
|
+
await agent_ctx.events.process_started(process_id, command, success=True)
|
|
238
|
+
|
|
239
|
+
except Exception as e: # noqa: BLE001
|
|
240
|
+
await agent_ctx.events.process_started("", command, success=False, error=str(e))
|
|
241
|
+
return {"error": f"Failed to start process: {e}"}
|
|
242
|
+
else:
|
|
243
|
+
return {
|
|
244
|
+
"process_id": process_id,
|
|
245
|
+
"command": command,
|
|
246
|
+
"args": args or [],
|
|
247
|
+
"cwd": cwd,
|
|
248
|
+
"status": "started",
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
async def get_process_output(self, agent_ctx: AgentContext, process_id: str) -> dict[str, Any]: # noqa: D417
|
|
252
|
+
"""Get current output from a background process.
|
|
253
|
+
|
|
254
|
+
Args:
|
|
255
|
+
process_id: Process identifier from start_process
|
|
256
|
+
"""
|
|
257
|
+
manager = self.get_env(agent_ctx).process_manager
|
|
258
|
+
try:
|
|
259
|
+
output = await manager.get_output(process_id)
|
|
260
|
+
await agent_ctx.events.process_output(process_id, output.combined or "")
|
|
261
|
+
result: dict[str, Any] = {
|
|
262
|
+
"process_id": process_id,
|
|
263
|
+
"stdout": output.stdout or "",
|
|
264
|
+
"stderr": output.stderr or "",
|
|
265
|
+
"combined": output.combined or "",
|
|
266
|
+
"truncated": output.truncated,
|
|
267
|
+
}
|
|
268
|
+
if output.exit_code is not None:
|
|
269
|
+
result["exit_code"] = output.exit_code
|
|
270
|
+
result["status"] = "completed"
|
|
271
|
+
else:
|
|
272
|
+
result["status"] = "running"
|
|
273
|
+
except ValueError as e:
|
|
274
|
+
return {"error": str(e)}
|
|
275
|
+
except Exception as e: # noqa: BLE001
|
|
276
|
+
return {"error": f"Error getting process output: {e}"}
|
|
277
|
+
else:
|
|
278
|
+
return result
|
|
279
|
+
|
|
280
|
+
async def wait_for_process(self, agent_ctx: AgentContext, process_id: str) -> dict[str, Any]: # noqa: D417
|
|
281
|
+
"""Wait for background process to complete and return final output.
|
|
282
|
+
|
|
283
|
+
Args:
|
|
284
|
+
process_id: Process identifier from start_process
|
|
285
|
+
"""
|
|
286
|
+
manager = self.get_env(agent_ctx).process_manager
|
|
287
|
+
try:
|
|
288
|
+
exit_code = await manager.wait_for_exit(process_id)
|
|
289
|
+
output = await manager.get_output(process_id)
|
|
290
|
+
await agent_ctx.events.process_exit(process_id, exit_code, final_output=output.combined)
|
|
291
|
+
|
|
292
|
+
except ValueError as e:
|
|
293
|
+
return {"error": str(e)}
|
|
294
|
+
except Exception as e: # noqa: BLE001
|
|
295
|
+
return {"error": f"Error waiting for process: {e}"}
|
|
296
|
+
else:
|
|
297
|
+
return {
|
|
298
|
+
"process_id": process_id,
|
|
299
|
+
"exit_code": exit_code,
|
|
300
|
+
"status": "completed",
|
|
301
|
+
"stdout": output.stdout or "",
|
|
302
|
+
"stderr": output.stderr or "",
|
|
303
|
+
"combined": output.combined or "",
|
|
304
|
+
"truncated": output.truncated,
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
async def kill_process(self, agent_ctx: AgentContext, process_id: str) -> dict[str, Any]: # noqa: D417
|
|
308
|
+
"""Terminate a background process.
|
|
309
|
+
|
|
310
|
+
Args:
|
|
311
|
+
process_id: Process identifier from start_process
|
|
312
|
+
"""
|
|
313
|
+
try:
|
|
314
|
+
await self.get_env(agent_ctx).process_manager.kill_process(process_id)
|
|
315
|
+
await agent_ctx.events.process_killed(process_id=process_id, success=True)
|
|
316
|
+
except ValueError as e:
|
|
317
|
+
await agent_ctx.events.process_killed(process_id, success=False, error=str(e))
|
|
318
|
+
return {"error": str(e)}
|
|
319
|
+
except Exception as e: # noqa: BLE001
|
|
320
|
+
await agent_ctx.events.process_killed(process_id, success=False, error=str(e))
|
|
321
|
+
return {"error": f"Error killing process: {e}"}
|
|
322
|
+
else:
|
|
323
|
+
return {
|
|
324
|
+
"process_id": process_id,
|
|
325
|
+
"status": "killed",
|
|
326
|
+
"message": f"Process {process_id} has been terminated",
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
async def release_process(self, agent_ctx: AgentContext, process_id: str) -> dict[str, Any]: # noqa: D417
|
|
330
|
+
"""Release resources for a background process.
|
|
331
|
+
|
|
332
|
+
Args:
|
|
333
|
+
process_id: Process identifier from start_process
|
|
334
|
+
"""
|
|
335
|
+
try:
|
|
336
|
+
await self.get_env(agent_ctx).process_manager.release_process(process_id)
|
|
337
|
+
await agent_ctx.events.process_released(process_id=process_id, success=True)
|
|
338
|
+
|
|
339
|
+
except ValueError as e:
|
|
340
|
+
await agent_ctx.events.process_released(process_id, success=False, error=str(e))
|
|
341
|
+
return {"error": str(e)}
|
|
342
|
+
except Exception as e: # noqa: BLE001
|
|
343
|
+
await agent_ctx.events.process_released(process_id, success=False, error=str(e))
|
|
344
|
+
return {"error": f"Error releasing process: {e}"}
|
|
345
|
+
else:
|
|
346
|
+
return {
|
|
347
|
+
"process_id": process_id,
|
|
348
|
+
"status": "released",
|
|
349
|
+
"message": f"Process {process_id} resources have been released",
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
async def list_processes(self, agent_ctx: AgentContext) -> dict[str, Any]:
|
|
353
|
+
"""List all active background processes."""
|
|
354
|
+
env = self.get_env(agent_ctx)
|
|
355
|
+
try:
|
|
356
|
+
process_ids = await env.process_manager.list_processes()
|
|
357
|
+
if not process_ids:
|
|
358
|
+
return {"processes": [], "count": 0, "message": "No active processes"}
|
|
359
|
+
|
|
360
|
+
processes = []
|
|
361
|
+
for process_id in process_ids:
|
|
362
|
+
try:
|
|
363
|
+
info = await env.process_manager.get_process_info(process_id)
|
|
364
|
+
processes.append({
|
|
365
|
+
"process_id": process_id,
|
|
366
|
+
"command": info["command"],
|
|
367
|
+
"args": info.get("args", []),
|
|
368
|
+
"cwd": info.get("cwd"),
|
|
369
|
+
"is_running": info.get("is_running", False),
|
|
370
|
+
"exit_code": info.get("exit_code"),
|
|
371
|
+
"created_at": info.get("created_at"),
|
|
372
|
+
})
|
|
373
|
+
except Exception as e: # noqa: BLE001
|
|
374
|
+
processes.append({
|
|
375
|
+
"process_id": process_id,
|
|
376
|
+
"error": f"Error getting info: {e}",
|
|
377
|
+
})
|
|
378
|
+
|
|
379
|
+
return {"processes": processes, "count": len(processes)}
|
|
380
|
+
except Exception as e: # noqa: BLE001
|
|
381
|
+
return {"error": f"Error listing processes: {e}"}
|