agentcrew-ai 0.8.5__tar.gz → 0.8.6__tar.gz
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.
- agentcrew_ai-0.8.6/AgentCrew/__init__.py +1 -0
- {agentcrew_ai-0.8.5 → agentcrew_ai-0.8.6}/AgentCrew/modules/a2a/task_manager.py +153 -29
- {agentcrew_ai-0.8.5 → agentcrew_ai-0.8.6}/AgentCrew/modules/agents/local_agent.py +8 -8
- {agentcrew_ai-0.8.5 → agentcrew_ai-0.8.6}/AgentCrew/modules/command_execution/constants.py +2 -2
- {agentcrew_ai-0.8.5 → agentcrew_ai-0.8.6}/AgentCrew/modules/command_execution/service.py +37 -83
- {agentcrew_ai-0.8.5 → agentcrew_ai-0.8.6}/AgentCrew/modules/command_execution/tool.py +5 -7
- {agentcrew_ai-0.8.5 → agentcrew_ai-0.8.6}/AgentCrew/modules/command_execution/types.py +3 -4
- {agentcrew_ai-0.8.5 → agentcrew_ai-0.8.6}/AgentCrew/modules/console/diff_display.py +13 -13
- {agentcrew_ai-0.8.5 → agentcrew_ai-0.8.6}/AgentCrew/modules/console/input_handler.py +2 -3
- {agentcrew_ai-0.8.5 → agentcrew_ai-0.8.6}/AgentCrew/modules/llm/constants.py +24 -3
- {agentcrew_ai-0.8.5/agentcrew_ai.egg-info → agentcrew_ai-0.8.6}/PKG-INFO +1 -1
- {agentcrew_ai-0.8.5 → agentcrew_ai-0.8.6/agentcrew_ai.egg-info}/PKG-INFO +1 -1
- {agentcrew_ai-0.8.5 → agentcrew_ai-0.8.6}/agentcrew_ai.egg-info/SOURCES.txt +0 -1
- {agentcrew_ai-0.8.5 → agentcrew_ai-0.8.6}/pyproject.toml +1 -1
- agentcrew_ai-0.8.5/AgentCrew/__init__.py +0 -1
- agentcrew_ai-0.8.5/AgentCrew/modules/command_execution/metric.py +0 -55
- {agentcrew_ai-0.8.5 → agentcrew_ai-0.8.6}/AgentCrew/app.py +0 -0
- {agentcrew_ai-0.8.5 → agentcrew_ai-0.8.6}/AgentCrew/assets/agentcrew_logo.png +0 -0
- {agentcrew_ai-0.8.5 → agentcrew_ai-0.8.6}/AgentCrew/main.py +0 -0
- {agentcrew_ai-0.8.5 → agentcrew_ai-0.8.6}/AgentCrew/main_docker.py +0 -0
- {agentcrew_ai-0.8.5 → agentcrew_ai-0.8.6}/AgentCrew/modules/__init__.py +0 -0
- {agentcrew_ai-0.8.5 → agentcrew_ai-0.8.6}/AgentCrew/modules/a2a/__init__.py +0 -0
- {agentcrew_ai-0.8.5 → agentcrew_ai-0.8.6}/AgentCrew/modules/a2a/adapters.py +0 -0
- {agentcrew_ai-0.8.5 → agentcrew_ai-0.8.6}/AgentCrew/modules/a2a/agent_cards.py +0 -0
- {agentcrew_ai-0.8.5 → agentcrew_ai-0.8.6}/AgentCrew/modules/a2a/common/__init__.py +0 -0
- {agentcrew_ai-0.8.5 → agentcrew_ai-0.8.6}/AgentCrew/modules/a2a/common/client/__init__.py +0 -0
- {agentcrew_ai-0.8.5 → agentcrew_ai-0.8.6}/AgentCrew/modules/a2a/common/client/card_resolver.py +0 -0
- {agentcrew_ai-0.8.5 → agentcrew_ai-0.8.6}/AgentCrew/modules/a2a/common/client/client.py +0 -0
- {agentcrew_ai-0.8.5 → agentcrew_ai-0.8.6}/AgentCrew/modules/a2a/common/server/__init__.py +0 -0
- {agentcrew_ai-0.8.5 → agentcrew_ai-0.8.6}/AgentCrew/modules/a2a/common/server/auth_middleware.py +0 -0
- {agentcrew_ai-0.8.5 → agentcrew_ai-0.8.6}/AgentCrew/modules/a2a/common/server/task_manager.py +0 -0
- {agentcrew_ai-0.8.5 → agentcrew_ai-0.8.6}/AgentCrew/modules/a2a/common/server/utils.py +0 -0
- {agentcrew_ai-0.8.5 → agentcrew_ai-0.8.6}/AgentCrew/modules/a2a/errors.py +0 -0
- {agentcrew_ai-0.8.5 → agentcrew_ai-0.8.6}/AgentCrew/modules/a2a/registry.py +0 -0
- {agentcrew_ai-0.8.5 → agentcrew_ai-0.8.6}/AgentCrew/modules/a2a/server.py +0 -0
- {agentcrew_ai-0.8.5 → agentcrew_ai-0.8.6}/AgentCrew/modules/agents/__init__.py +0 -0
- {agentcrew_ai-0.8.5 → agentcrew_ai-0.8.6}/AgentCrew/modules/agents/base.py +0 -0
- {agentcrew_ai-0.8.5 → agentcrew_ai-0.8.6}/AgentCrew/modules/agents/example.py +0 -0
- {agentcrew_ai-0.8.5 → agentcrew_ai-0.8.6}/AgentCrew/modules/agents/manager.py +0 -0
- {agentcrew_ai-0.8.5 → agentcrew_ai-0.8.6}/AgentCrew/modules/agents/remote_agent.py +0 -0
- {agentcrew_ai-0.8.5 → agentcrew_ai-0.8.6}/AgentCrew/modules/agents/tools/__init__.py +0 -0
- {agentcrew_ai-0.8.5 → agentcrew_ai-0.8.6}/AgentCrew/modules/agents/tools/ask.py +0 -0
- {agentcrew_ai-0.8.5 → agentcrew_ai-0.8.6}/AgentCrew/modules/agents/tools/delegate.py +0 -0
- {agentcrew_ai-0.8.5 → agentcrew_ai-0.8.6}/AgentCrew/modules/agents/tools/transfer.py +0 -0
- {agentcrew_ai-0.8.5 → agentcrew_ai-0.8.6}/AgentCrew/modules/anthropic/__init__.py +0 -0
- {agentcrew_ai-0.8.5 → agentcrew_ai-0.8.6}/AgentCrew/modules/anthropic/service.py +0 -0
- {agentcrew_ai-0.8.5 → agentcrew_ai-0.8.6}/AgentCrew/modules/browser_automation/__init__.py +0 -0
- {agentcrew_ai-0.8.5 → agentcrew_ai-0.8.6}/AgentCrew/modules/browser_automation/chrome_manager.py +0 -0
- {agentcrew_ai-0.8.5 → agentcrew_ai-0.8.6}/AgentCrew/modules/browser_automation/element_extractor.py +0 -0
- {agentcrew_ai-0.8.5 → agentcrew_ai-0.8.6}/AgentCrew/modules/browser_automation/js/click_element.js +0 -0
- {agentcrew_ai-0.8.5 → agentcrew_ai-0.8.6}/AgentCrew/modules/browser_automation/js/draw_element_boxes.js +0 -0
- {agentcrew_ai-0.8.5 → agentcrew_ai-0.8.6}/AgentCrew/modules/browser_automation/js/extract_clickable_elements.js +0 -0
- {agentcrew_ai-0.8.5 → agentcrew_ai-0.8.6}/AgentCrew/modules/browser_automation/js/extract_elements_by_text.js +0 -0
- {agentcrew_ai-0.8.5 → agentcrew_ai-0.8.6}/AgentCrew/modules/browser_automation/js/extract_input_elements.js +0 -0
- {agentcrew_ai-0.8.5 → agentcrew_ai-0.8.6}/AgentCrew/modules/browser_automation/js/extract_scrollable_elements.js +0 -0
- {agentcrew_ai-0.8.5 → agentcrew_ai-0.8.6}/AgentCrew/modules/browser_automation/js/filter_hidden_elements.js +0 -0
- {agentcrew_ai-0.8.5 → agentcrew_ai-0.8.6}/AgentCrew/modules/browser_automation/js/focus_and_clear_element.js +0 -0
- {agentcrew_ai-0.8.5 → agentcrew_ai-0.8.6}/AgentCrew/modules/browser_automation/js/remove_element_boxes.js +0 -0
- {agentcrew_ai-0.8.5 → agentcrew_ai-0.8.6}/AgentCrew/modules/browser_automation/js/scroll_page.js +0 -0
- {agentcrew_ai-0.8.5 → agentcrew_ai-0.8.6}/AgentCrew/modules/browser_automation/js/trigger_input_events.js +0 -0
- {agentcrew_ai-0.8.5 → agentcrew_ai-0.8.6}/AgentCrew/modules/browser_automation/js_loader.py +0 -0
- {agentcrew_ai-0.8.5 → agentcrew_ai-0.8.6}/AgentCrew/modules/browser_automation/service.py +0 -0
- {agentcrew_ai-0.8.5 → agentcrew_ai-0.8.6}/AgentCrew/modules/browser_automation/tool.py +0 -0
- {agentcrew_ai-0.8.5 → agentcrew_ai-0.8.6}/AgentCrew/modules/chat/__init__.py +0 -0
- {agentcrew_ai-0.8.5 → agentcrew_ai-0.8.6}/AgentCrew/modules/chat/consolidation.py +0 -0
- {agentcrew_ai-0.8.5 → agentcrew_ai-0.8.6}/AgentCrew/modules/chat/file_handler.py +0 -0
- {agentcrew_ai-0.8.5 → agentcrew_ai-0.8.6}/AgentCrew/modules/chat/history.py +0 -0
- {agentcrew_ai-0.8.5 → agentcrew_ai-0.8.6}/AgentCrew/modules/chat/message/__init__.py +0 -0
- {agentcrew_ai-0.8.5 → agentcrew_ai-0.8.6}/AgentCrew/modules/chat/message/base.py +0 -0
- {agentcrew_ai-0.8.5 → agentcrew_ai-0.8.6}/AgentCrew/modules/chat/message/command_processor.py +0 -0
- {agentcrew_ai-0.8.5 → agentcrew_ai-0.8.6}/AgentCrew/modules/chat/message/conversation.py +0 -0
- {agentcrew_ai-0.8.5 → agentcrew_ai-0.8.6}/AgentCrew/modules/chat/message/handler.py +0 -0
- {agentcrew_ai-0.8.5 → agentcrew_ai-0.8.6}/AgentCrew/modules/chat/message/tool_manager.py +0 -0
- {agentcrew_ai-0.8.5 → agentcrew_ai-0.8.6}/AgentCrew/modules/chat/message_handler.py +0 -0
- {agentcrew_ai-0.8.5 → agentcrew_ai-0.8.6}/AgentCrew/modules/clipboard/__init__.py +0 -0
- {agentcrew_ai-0.8.5 → agentcrew_ai-0.8.6}/AgentCrew/modules/clipboard/service.py +0 -0
- {agentcrew_ai-0.8.5 → agentcrew_ai-0.8.6}/AgentCrew/modules/clipboard/tool.py +0 -0
- {agentcrew_ai-0.8.5 → agentcrew_ai-0.8.6}/AgentCrew/modules/code_analysis/__init__.py +0 -0
- {agentcrew_ai-0.8.5 → agentcrew_ai-0.8.6}/AgentCrew/modules/code_analysis/service.py +0 -0
- {agentcrew_ai-0.8.5 → agentcrew_ai-0.8.6}/AgentCrew/modules/code_analysis/tool.py +0 -0
- {agentcrew_ai-0.8.5 → agentcrew_ai-0.8.6}/AgentCrew/modules/command_execution/__init__.py +0 -0
- {agentcrew_ai-0.8.5 → agentcrew_ai-0.8.6}/AgentCrew/modules/config/__init__.py +0 -0
- {agentcrew_ai-0.8.5 → agentcrew_ai-0.8.6}/AgentCrew/modules/config/config_management.py +0 -0
- {agentcrew_ai-0.8.5 → agentcrew_ai-0.8.6}/AgentCrew/modules/console/__init__.py +0 -0
- {agentcrew_ai-0.8.5 → agentcrew_ai-0.8.6}/AgentCrew/modules/console/command_handlers.py +0 -0
- {agentcrew_ai-0.8.5 → agentcrew_ai-0.8.6}/AgentCrew/modules/console/completers.py +0 -0
- {agentcrew_ai-0.8.5 → agentcrew_ai-0.8.6}/AgentCrew/modules/console/confirmation_handler.py +0 -0
- {agentcrew_ai-0.8.5 → agentcrew_ai-0.8.6}/AgentCrew/modules/console/console_ui.py +0 -0
- {agentcrew_ai-0.8.5 → agentcrew_ai-0.8.6}/AgentCrew/modules/console/constants.py +0 -0
- {agentcrew_ai-0.8.5 → agentcrew_ai-0.8.6}/AgentCrew/modules/console/conversation_handler.py +0 -0
- {agentcrew_ai-0.8.5 → agentcrew_ai-0.8.6}/AgentCrew/modules/console/display_handlers.py +0 -0
- {agentcrew_ai-0.8.5 → agentcrew_ai-0.8.6}/AgentCrew/modules/console/tool_display.py +0 -0
- {agentcrew_ai-0.8.5 → agentcrew_ai-0.8.6}/AgentCrew/modules/console/ui_effects.py +0 -0
- {agentcrew_ai-0.8.5 → agentcrew_ai-0.8.6}/AgentCrew/modules/console/utils.py +0 -0
- {agentcrew_ai-0.8.5 → agentcrew_ai-0.8.6}/AgentCrew/modules/custom_llm/__init__.py +0 -0
- {agentcrew_ai-0.8.5 → agentcrew_ai-0.8.6}/AgentCrew/modules/custom_llm/copilot_response_service.py +0 -0
- {agentcrew_ai-0.8.5 → agentcrew_ai-0.8.6}/AgentCrew/modules/custom_llm/deepinfra_service.py +0 -0
- {agentcrew_ai-0.8.5 → agentcrew_ai-0.8.6}/AgentCrew/modules/custom_llm/github_copilot_service.py +0 -0
- {agentcrew_ai-0.8.5 → agentcrew_ai-0.8.6}/AgentCrew/modules/custom_llm/service.py +0 -0
- {agentcrew_ai-0.8.5 → agentcrew_ai-0.8.6}/AgentCrew/modules/file_editing/__init__.py +0 -0
- {agentcrew_ai-0.8.5 → agentcrew_ai-0.8.6}/AgentCrew/modules/file_editing/safety_validator.py +0 -0
- {agentcrew_ai-0.8.5 → agentcrew_ai-0.8.6}/AgentCrew/modules/file_editing/search_replace_engine.py +0 -0
- {agentcrew_ai-0.8.5 → agentcrew_ai-0.8.6}/AgentCrew/modules/file_editing/service.py +0 -0
- {agentcrew_ai-0.8.5 → agentcrew_ai-0.8.6}/AgentCrew/modules/file_editing/tool.py +0 -0
- {agentcrew_ai-0.8.5 → agentcrew_ai-0.8.6}/AgentCrew/modules/file_editing/tree_sitter_checker.py +0 -0
- {agentcrew_ai-0.8.5 → agentcrew_ai-0.8.6}/AgentCrew/modules/google/__init__.py +0 -0
- {agentcrew_ai-0.8.5 → agentcrew_ai-0.8.6}/AgentCrew/modules/google/native_service.py +0 -0
- {agentcrew_ai-0.8.5 → agentcrew_ai-0.8.6}/AgentCrew/modules/google/service.py +0 -0
- {agentcrew_ai-0.8.5 → agentcrew_ai-0.8.6}/AgentCrew/modules/groq/__init__.py +0 -0
- {agentcrew_ai-0.8.5 → agentcrew_ai-0.8.6}/AgentCrew/modules/groq/service.py +0 -0
- {agentcrew_ai-0.8.5 → agentcrew_ai-0.8.6}/AgentCrew/modules/gui/__init__.py +0 -0
- {agentcrew_ai-0.8.5 → agentcrew_ai-0.8.6}/AgentCrew/modules/gui/components/__init__.py +0 -0
- {agentcrew_ai-0.8.5 → agentcrew_ai-0.8.6}/AgentCrew/modules/gui/components/chat_components.py +0 -0
- {agentcrew_ai-0.8.5 → agentcrew_ai-0.8.6}/AgentCrew/modules/gui/components/command_handler.py +0 -0
- {agentcrew_ai-0.8.5 → agentcrew_ai-0.8.6}/AgentCrew/modules/gui/components/completers.py +0 -0
- {agentcrew_ai-0.8.5 → agentcrew_ai-0.8.6}/AgentCrew/modules/gui/components/conversation_components.py +0 -0
- {agentcrew_ai-0.8.5 → agentcrew_ai-0.8.6}/AgentCrew/modules/gui/components/input_components.py +0 -0
- {agentcrew_ai-0.8.5 → agentcrew_ai-0.8.6}/AgentCrew/modules/gui/components/keyboard_handler.py +0 -0
- {agentcrew_ai-0.8.5 → agentcrew_ai-0.8.6}/AgentCrew/modules/gui/components/menu_components.py +0 -0
- {agentcrew_ai-0.8.5 → agentcrew_ai-0.8.6}/AgentCrew/modules/gui/components/message_handlers.py +0 -0
- {agentcrew_ai-0.8.5 → agentcrew_ai-0.8.6}/AgentCrew/modules/gui/components/tool_handlers.py +0 -0
- {agentcrew_ai-0.8.5 → agentcrew_ai-0.8.6}/AgentCrew/modules/gui/components/ui_state_manager.py +0 -0
- {agentcrew_ai-0.8.5 → agentcrew_ai-0.8.6}/AgentCrew/modules/gui/qt_ui.py +0 -0
- {agentcrew_ai-0.8.5 → agentcrew_ai-0.8.6}/AgentCrew/modules/gui/themes/README.md +0 -0
- {agentcrew_ai-0.8.5 → agentcrew_ai-0.8.6}/AgentCrew/modules/gui/themes/__init__.py +0 -0
- {agentcrew_ai-0.8.5 → agentcrew_ai-0.8.6}/AgentCrew/modules/gui/themes/atom_light.py +0 -0
- {agentcrew_ai-0.8.5 → agentcrew_ai-0.8.6}/AgentCrew/modules/gui/themes/catppuccin.py +0 -0
- {agentcrew_ai-0.8.5 → agentcrew_ai-0.8.6}/AgentCrew/modules/gui/themes/dracula.py +0 -0
- {agentcrew_ai-0.8.5 → agentcrew_ai-0.8.6}/AgentCrew/modules/gui/themes/nord.py +0 -0
- {agentcrew_ai-0.8.5 → agentcrew_ai-0.8.6}/AgentCrew/modules/gui/themes/saigontech.py +0 -0
- {agentcrew_ai-0.8.5 → agentcrew_ai-0.8.6}/AgentCrew/modules/gui/themes/style_provider.py +0 -0
- {agentcrew_ai-0.8.5 → agentcrew_ai-0.8.6}/AgentCrew/modules/gui/themes/unicorn.py +0 -0
- {agentcrew_ai-0.8.5 → agentcrew_ai-0.8.6}/AgentCrew/modules/gui/utils/__init__.py +0 -0
- {agentcrew_ai-0.8.5 → agentcrew_ai-0.8.6}/AgentCrew/modules/gui/utils/macos_clipboard.py +0 -0
- {agentcrew_ai-0.8.5 → agentcrew_ai-0.8.6}/AgentCrew/modules/gui/utils/strings.py +0 -0
- {agentcrew_ai-0.8.5 → agentcrew_ai-0.8.6}/AgentCrew/modules/gui/utils/wins_clipboard.py +0 -0
- {agentcrew_ai-0.8.5 → agentcrew_ai-0.8.6}/AgentCrew/modules/gui/widgets/__init__.py +0 -0
- {agentcrew_ai-0.8.5 → agentcrew_ai-0.8.6}/AgentCrew/modules/gui/widgets/config_window.py +0 -0
- {agentcrew_ai-0.8.5 → agentcrew_ai-0.8.6}/AgentCrew/modules/gui/widgets/configs/__init__.py +0 -0
- {agentcrew_ai-0.8.5 → agentcrew_ai-0.8.6}/AgentCrew/modules/gui/widgets/configs/agent_config.py +0 -0
- {agentcrew_ai-0.8.5 → agentcrew_ai-0.8.6}/AgentCrew/modules/gui/widgets/configs/custom_llm_provider.py +0 -0
- {agentcrew_ai-0.8.5 → agentcrew_ai-0.8.6}/AgentCrew/modules/gui/widgets/configs/global_settings.py +0 -0
- {agentcrew_ai-0.8.5 → agentcrew_ai-0.8.6}/AgentCrew/modules/gui/widgets/configs/mcp_config.py +0 -0
- {agentcrew_ai-0.8.5 → agentcrew_ai-0.8.6}/AgentCrew/modules/gui/widgets/configs/save_worker.py +0 -0
- {agentcrew_ai-0.8.5 → agentcrew_ai-0.8.6}/AgentCrew/modules/gui/widgets/history_sidebar.py +0 -0
- {agentcrew_ai-0.8.5 → agentcrew_ai-0.8.6}/AgentCrew/modules/gui/widgets/json_editor.py +0 -0
- {agentcrew_ai-0.8.5 → agentcrew_ai-0.8.6}/AgentCrew/modules/gui/widgets/loading_overlay.py +0 -0
- {agentcrew_ai-0.8.5 → agentcrew_ai-0.8.6}/AgentCrew/modules/gui/widgets/markdown_editor.py +0 -0
- {agentcrew_ai-0.8.5 → agentcrew_ai-0.8.6}/AgentCrew/modules/gui/widgets/message_bubble.py +0 -0
- {agentcrew_ai-0.8.5 → agentcrew_ai-0.8.6}/AgentCrew/modules/gui/widgets/paste_aware_textedit.py +0 -0
- {agentcrew_ai-0.8.5 → agentcrew_ai-0.8.6}/AgentCrew/modules/gui/widgets/system_message.py +0 -0
- {agentcrew_ai-0.8.5 → agentcrew_ai-0.8.6}/AgentCrew/modules/gui/widgets/token_usage.py +0 -0
- {agentcrew_ai-0.8.5 → agentcrew_ai-0.8.6}/AgentCrew/modules/gui/widgets/tool_widget.py +0 -0
- {agentcrew_ai-0.8.5 → agentcrew_ai-0.8.6}/AgentCrew/modules/gui/worker.py +0 -0
- {agentcrew_ai-0.8.5 → agentcrew_ai-0.8.6}/AgentCrew/modules/image_generation/__init__.py +0 -0
- {agentcrew_ai-0.8.5 → agentcrew_ai-0.8.6}/AgentCrew/modules/image_generation/service.py +0 -0
- {agentcrew_ai-0.8.5 → agentcrew_ai-0.8.6}/AgentCrew/modules/image_generation/tool.py +0 -0
- {agentcrew_ai-0.8.5 → agentcrew_ai-0.8.6}/AgentCrew/modules/llm/__init__.py +0 -0
- {agentcrew_ai-0.8.5 → agentcrew_ai-0.8.6}/AgentCrew/modules/llm/base.py +0 -0
- {agentcrew_ai-0.8.5 → agentcrew_ai-0.8.6}/AgentCrew/modules/llm/model_registry.py +0 -0
- {agentcrew_ai-0.8.5 → agentcrew_ai-0.8.6}/AgentCrew/modules/llm/service_manager.py +0 -0
- {agentcrew_ai-0.8.5 → agentcrew_ai-0.8.6}/AgentCrew/modules/llm/types.py +0 -0
- {agentcrew_ai-0.8.5 → agentcrew_ai-0.8.6}/AgentCrew/modules/mcpclient/__init__.py +0 -0
- {agentcrew_ai-0.8.5 → agentcrew_ai-0.8.6}/AgentCrew/modules/mcpclient/auth.py +0 -0
- {agentcrew_ai-0.8.5 → agentcrew_ai-0.8.6}/AgentCrew/modules/mcpclient/config.py +0 -0
- {agentcrew_ai-0.8.5 → agentcrew_ai-0.8.6}/AgentCrew/modules/mcpclient/manager.py +0 -0
- {agentcrew_ai-0.8.5 → agentcrew_ai-0.8.6}/AgentCrew/modules/mcpclient/service.py +0 -0
- {agentcrew_ai-0.8.5 → agentcrew_ai-0.8.6}/AgentCrew/modules/mcpclient/tool.py +0 -0
- {agentcrew_ai-0.8.5 → agentcrew_ai-0.8.6}/AgentCrew/modules/memory/__init__.py +0 -0
- {agentcrew_ai-0.8.5 → agentcrew_ai-0.8.6}/AgentCrew/modules/memory/base_service.py +0 -0
- {agentcrew_ai-0.8.5 → agentcrew_ai-0.8.6}/AgentCrew/modules/memory/chroma_service.py +0 -0
- {agentcrew_ai-0.8.5 → agentcrew_ai-0.8.6}/AgentCrew/modules/memory/context_persistent.py +0 -0
- {agentcrew_ai-0.8.5 → agentcrew_ai-0.8.6}/AgentCrew/modules/memory/github_copilot_ef.py +0 -0
- {agentcrew_ai-0.8.5 → agentcrew_ai-0.8.6}/AgentCrew/modules/memory/google_genai_ef.py +0 -0
- {agentcrew_ai-0.8.5 → agentcrew_ai-0.8.6}/AgentCrew/modules/memory/tool.py +0 -0
- {agentcrew_ai-0.8.5 → agentcrew_ai-0.8.6}/AgentCrew/modules/memory/voyageai_ef.py +0 -0
- {agentcrew_ai-0.8.5 → agentcrew_ai-0.8.6}/AgentCrew/modules/openai/__init__.py +0 -0
- {agentcrew_ai-0.8.5 → agentcrew_ai-0.8.6}/AgentCrew/modules/openai/response_service.py +0 -0
- {agentcrew_ai-0.8.5 → agentcrew_ai-0.8.6}/AgentCrew/modules/openai/service.py +0 -0
- {agentcrew_ai-0.8.5 → agentcrew_ai-0.8.6}/AgentCrew/modules/prompts/__init__.py +0 -0
- {agentcrew_ai-0.8.5 → agentcrew_ai-0.8.6}/AgentCrew/modules/prompts/constants.py +0 -0
- {agentcrew_ai-0.8.5 → agentcrew_ai-0.8.6}/AgentCrew/modules/tools/README.md +0 -0
- {agentcrew_ai-0.8.5 → agentcrew_ai-0.8.6}/AgentCrew/modules/tools/registration.py +0 -0
- {agentcrew_ai-0.8.5 → agentcrew_ai-0.8.6}/AgentCrew/modules/tools/registry.py +0 -0
- {agentcrew_ai-0.8.5 → agentcrew_ai-0.8.6}/AgentCrew/modules/voice/__init__.py +0 -0
- {agentcrew_ai-0.8.5 → agentcrew_ai-0.8.6}/AgentCrew/modules/voice/audio_handler.py +0 -0
- {agentcrew_ai-0.8.5 → agentcrew_ai-0.8.6}/AgentCrew/modules/voice/base.py +0 -0
- {agentcrew_ai-0.8.5 → agentcrew_ai-0.8.6}/AgentCrew/modules/voice/deepinfra_service.py +0 -0
- {agentcrew_ai-0.8.5 → agentcrew_ai-0.8.6}/AgentCrew/modules/voice/elevenlabs_service.py +0 -0
- {agentcrew_ai-0.8.5 → agentcrew_ai-0.8.6}/AgentCrew/modules/voice/text_cleaner.py +0 -0
- {agentcrew_ai-0.8.5 → agentcrew_ai-0.8.6}/AgentCrew/modules/web_search/__init__.py +0 -0
- {agentcrew_ai-0.8.5 → agentcrew_ai-0.8.6}/AgentCrew/modules/web_search/service.py +0 -0
- {agentcrew_ai-0.8.5 → agentcrew_ai-0.8.6}/AgentCrew/modules/web_search/tool.py +0 -0
- {agentcrew_ai-0.8.5 → agentcrew_ai-0.8.6}/CONTRIBUTING.md +0 -0
- {agentcrew_ai-0.8.5 → agentcrew_ai-0.8.6}/LICENSE +0 -0
- {agentcrew_ai-0.8.5 → agentcrew_ai-0.8.6}/MANIFEST.in +0 -0
- {agentcrew_ai-0.8.5 → agentcrew_ai-0.8.6}/README.md +0 -0
- {agentcrew_ai-0.8.5 → agentcrew_ai-0.8.6}/agentcrew_ai.egg-info/dependency_links.txt +0 -0
- {agentcrew_ai-0.8.5 → agentcrew_ai-0.8.6}/agentcrew_ai.egg-info/entry_points.txt +0 -0
- {agentcrew_ai-0.8.5 → agentcrew_ai-0.8.6}/agentcrew_ai.egg-info/requires.txt +0 -0
- {agentcrew_ai-0.8.5 → agentcrew_ai-0.8.6}/agentcrew_ai.egg-info/top_level.txt +0 -0
- {agentcrew_ai-0.8.5 → agentcrew_ai-0.8.6}/setup.cfg +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "0.8.6"
|
|
@@ -27,6 +27,11 @@ from a2a.types import (
|
|
|
27
27
|
TaskState,
|
|
28
28
|
TaskStatusUpdateEvent,
|
|
29
29
|
TaskArtifactUpdateEvent,
|
|
30
|
+
Part,
|
|
31
|
+
TextPart,
|
|
32
|
+
DataPart,
|
|
33
|
+
Role,
|
|
34
|
+
Message,
|
|
30
35
|
)
|
|
31
36
|
|
|
32
37
|
from AgentCrew.modules.agents import LocalAgent
|
|
@@ -58,6 +63,7 @@ class AgentTaskManager(TaskManager):
|
|
|
58
63
|
"""Manages tasks for a specific agent"""
|
|
59
64
|
|
|
60
65
|
TERMINAL_STATES = {TaskState.completed, TaskState.canceled, TaskState.failed}
|
|
66
|
+
INPUT_REQUIRED_STATES = {TaskState.input_required}
|
|
61
67
|
|
|
62
68
|
def __init__(self, agent_name: str, agent_manager: AgentManager):
|
|
63
69
|
self.agent_name = agent_name
|
|
@@ -72,6 +78,9 @@ class AgentTaskManager(TaskManager):
|
|
|
72
78
|
] = defaultdict(list)
|
|
73
79
|
self.streaming_enabled_tasks: set[str] = set()
|
|
74
80
|
|
|
81
|
+
self.pending_ask_responses: Dict[str, asyncio.Event] = {}
|
|
82
|
+
self.ask_responses: Dict[str, str] = {}
|
|
83
|
+
|
|
75
84
|
self.agent = self.agent_manager.get_agent(self.agent_name)
|
|
76
85
|
if self.agent is None or not isinstance(self.agent, LocalAgent):
|
|
77
86
|
raise ValueError(f"Agent {agent_name} not found or is not a LocalAgent")
|
|
@@ -82,6 +91,19 @@ class AgentTaskManager(TaskManager):
|
|
|
82
91
|
"""Check if a state is terminal."""
|
|
83
92
|
return state in self.TERMINAL_STATES
|
|
84
93
|
|
|
94
|
+
def _extract_text_from_message(self, message: Dict[str, Any]) -> str:
|
|
95
|
+
"""Extract text content from a message."""
|
|
96
|
+
content = message.get("content", [])
|
|
97
|
+
if isinstance(content, str):
|
|
98
|
+
return content
|
|
99
|
+
text_parts = []
|
|
100
|
+
for part in content:
|
|
101
|
+
if isinstance(part, str):
|
|
102
|
+
text_parts.append(part)
|
|
103
|
+
elif isinstance(part, dict) and part.get("type") == "text":
|
|
104
|
+
text_parts.append(part.get("text", ""))
|
|
105
|
+
return " ".join(text_parts)
|
|
106
|
+
|
|
85
107
|
def _validate_task_not_terminal(
|
|
86
108
|
self, task: Task, operation: str
|
|
87
109
|
) -> Optional[TaskNotCancelableError]:
|
|
@@ -121,7 +143,6 @@ class AgentTaskManager(TaskManager):
|
|
|
121
143
|
)
|
|
122
144
|
)
|
|
123
145
|
|
|
124
|
-
# Generate task ID from message
|
|
125
146
|
task_id = (
|
|
126
147
|
request.params.message.task_id
|
|
127
148
|
or f"task_{request.params.message.message_id}"
|
|
@@ -135,8 +156,19 @@ class AgentTaskManager(TaskManager):
|
|
|
135
156
|
root=JSONRPCErrorResponse(id=request.id, error=error)
|
|
136
157
|
)
|
|
137
158
|
|
|
159
|
+
if existing_task.status.state == TaskState.input_required:
|
|
160
|
+
message = convert_a2a_message_to_agent(request.params.message)
|
|
161
|
+
user_response = self._extract_text_from_message(message)
|
|
162
|
+
|
|
163
|
+
if task_id in self.pending_ask_responses:
|
|
164
|
+
self.ask_responses[task_id] = user_response
|
|
165
|
+
self.pending_ask_responses[task_id].set()
|
|
166
|
+
|
|
167
|
+
return SendMessageResponse(
|
|
168
|
+
root=SendMessageSuccessResponse(id=request.id, result=existing_task)
|
|
169
|
+
)
|
|
170
|
+
|
|
138
171
|
if task_id not in self.tasks:
|
|
139
|
-
# Create task with initial state
|
|
140
172
|
task = Task(
|
|
141
173
|
id=task_id,
|
|
142
174
|
context_id=request.params.message.context_id or f"ctx_{task_id}",
|
|
@@ -147,8 +179,8 @@ class AgentTaskManager(TaskManager):
|
|
|
147
179
|
self.tasks[task.id] = task
|
|
148
180
|
|
|
149
181
|
task = self.tasks[task_id]
|
|
150
|
-
if
|
|
151
|
-
self.task_history[
|
|
182
|
+
if task.context_id not in self.task_history:
|
|
183
|
+
self.task_history[task.context_id] = []
|
|
152
184
|
|
|
153
185
|
# Convert A2A message to SwissKnife format
|
|
154
186
|
message = convert_a2a_message_to_agent(request.params.message)
|
|
@@ -185,9 +217,8 @@ class AgentTaskManager(TaskManager):
|
|
|
185
217
|
|
|
186
218
|
message["content"] = new_parts
|
|
187
219
|
|
|
188
|
-
self.task_history[
|
|
220
|
+
self.task_history[task.context_id].append(message)
|
|
189
221
|
|
|
190
|
-
# Process with agent (non-blocking)
|
|
191
222
|
asyncio.create_task(self._process_agent_task(self.agent, task))
|
|
192
223
|
|
|
193
224
|
# Return initial task state
|
|
@@ -243,6 +274,35 @@ class AgentTaskManager(TaskManager):
|
|
|
243
274
|
# Clean up
|
|
244
275
|
self.streaming_tasks.pop(task_id, None)
|
|
245
276
|
|
|
277
|
+
def _create_ask_tool_message(
|
|
278
|
+
self, question: str, guided_answers: list[str]
|
|
279
|
+
) -> Message:
|
|
280
|
+
"""
|
|
281
|
+
Create an A2A message for the ask tool's input-required state.
|
|
282
|
+
|
|
283
|
+
Args:
|
|
284
|
+
question: The question to ask the user
|
|
285
|
+
guided_answers: List of suggested answers
|
|
286
|
+
|
|
287
|
+
Returns:
|
|
288
|
+
A2A Message with the question and guided answers
|
|
289
|
+
"""
|
|
290
|
+
ask_data = {
|
|
291
|
+
"type": "ask",
|
|
292
|
+
"question": question,
|
|
293
|
+
"guided_answers": guided_answers,
|
|
294
|
+
"instruction": "Please respond with one of the guided answers or provide a custom response.",
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
return Message(
|
|
298
|
+
message_id=f"ask_{hash(question)}",
|
|
299
|
+
role=Role.agent,
|
|
300
|
+
parts=[
|
|
301
|
+
Part(root=TextPart(text=f"❓ {question}")),
|
|
302
|
+
Part(root=DataPart(data=ask_data)),
|
|
303
|
+
],
|
|
304
|
+
)
|
|
305
|
+
|
|
246
306
|
def _record_and_emit_event(
|
|
247
307
|
self, task_id: str, event: Union[TaskStatusUpdateEvent, TaskArtifactUpdateEvent]
|
|
248
308
|
):
|
|
@@ -281,7 +341,7 @@ class AgentTaskManager(TaskManager):
|
|
|
281
341
|
|
|
282
342
|
try:
|
|
283
343
|
artifacts = []
|
|
284
|
-
if task.
|
|
344
|
+
if task.context_id not in self.task_history:
|
|
285
345
|
raise ValueError("Task history is not existed")
|
|
286
346
|
|
|
287
347
|
input_tokens = 0
|
|
@@ -308,7 +368,7 @@ class AgentTaskManager(TaskManager):
|
|
|
308
368
|
chunk_text,
|
|
309
369
|
thinking_chunk,
|
|
310
370
|
) in agent.process_messages(
|
|
311
|
-
self.task_history[task.
|
|
371
|
+
self.task_history[task.context_id], callback=process_result
|
|
312
372
|
):
|
|
313
373
|
# Update current response
|
|
314
374
|
if response_message:
|
|
@@ -388,9 +448,8 @@ class AgentTaskManager(TaskManager):
|
|
|
388
448
|
MessageType.Thinking, {"thinking": thinking_data}
|
|
389
449
|
)
|
|
390
450
|
if thinking_message:
|
|
391
|
-
self.task_history[task.
|
|
451
|
+
self.task_history[task.context_id].append(thinking_message)
|
|
392
452
|
|
|
393
|
-
# Format assistant message with the response and tool uses
|
|
394
453
|
assistant_message = agent.format_message(
|
|
395
454
|
MessageType.Assistant,
|
|
396
455
|
{
|
|
@@ -401,34 +460,99 @@ class AgentTaskManager(TaskManager):
|
|
|
401
460
|
},
|
|
402
461
|
)
|
|
403
462
|
if assistant_message:
|
|
404
|
-
self.task_history[task.
|
|
463
|
+
self.task_history[task.context_id].append(assistant_message)
|
|
405
464
|
|
|
406
|
-
# Process each tool use
|
|
407
465
|
for tool_use in tool_uses:
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
466
|
+
tool_name = tool_use["name"]
|
|
467
|
+
|
|
468
|
+
if tool_name == "ask":
|
|
469
|
+
question = tool_use["input"].get("question", "")
|
|
470
|
+
guided_answers = tool_use["input"].get("guided_answers", [])
|
|
471
|
+
|
|
472
|
+
task.status.state = TaskState.input_required
|
|
473
|
+
task.status.timestamp = datetime.now().isoformat()
|
|
474
|
+
task.status.message = self._create_ask_tool_message(
|
|
475
|
+
question, guided_answers
|
|
476
|
+
)
|
|
477
|
+
|
|
478
|
+
self._record_and_emit_event(
|
|
479
|
+
task.id,
|
|
480
|
+
TaskStatusUpdateEvent(
|
|
481
|
+
task_id=task.id,
|
|
482
|
+
context_id=task.context_id,
|
|
483
|
+
status=task.status,
|
|
484
|
+
final=False,
|
|
485
|
+
),
|
|
412
486
|
)
|
|
413
487
|
|
|
488
|
+
wait_event = asyncio.Event()
|
|
489
|
+
self.pending_ask_responses[task.id] = wait_event
|
|
490
|
+
|
|
491
|
+
try:
|
|
492
|
+
await asyncio.wait_for(wait_event.wait(), timeout=300)
|
|
493
|
+
user_answer = self.ask_responses.get(
|
|
494
|
+
task.id, "No response received"
|
|
495
|
+
)
|
|
496
|
+
except asyncio.TimeoutError:
|
|
497
|
+
user_answer = "User did not respond in time."
|
|
498
|
+
finally:
|
|
499
|
+
self.pending_ask_responses.pop(task.id, None)
|
|
500
|
+
self.ask_responses.pop(task.id, None)
|
|
501
|
+
|
|
502
|
+
tool_result = f"User's answer: {user_answer}"
|
|
503
|
+
|
|
504
|
+
task.status.state = TaskState.working
|
|
505
|
+
task.status.timestamp = datetime.now().isoformat()
|
|
506
|
+
task.status.message = None
|
|
507
|
+
|
|
414
508
|
tool_result_message = agent.format_message(
|
|
415
509
|
MessageType.ToolResult,
|
|
416
510
|
{"tool_use": tool_use, "tool_result": tool_result},
|
|
417
511
|
)
|
|
418
512
|
if tool_result_message:
|
|
419
|
-
self.task_history[task.
|
|
513
|
+
self.task_history[task.context_id].append(
|
|
514
|
+
tool_result_message
|
|
515
|
+
)
|
|
420
516
|
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
517
|
+
self._record_and_emit_event(
|
|
518
|
+
task.id,
|
|
519
|
+
TaskStatusUpdateEvent(
|
|
520
|
+
task_id=task.id,
|
|
521
|
+
context_id=task.context_id,
|
|
522
|
+
status=task.status,
|
|
523
|
+
final=False,
|
|
524
|
+
),
|
|
429
525
|
)
|
|
430
|
-
|
|
431
|
-
|
|
526
|
+
|
|
527
|
+
else:
|
|
528
|
+
try:
|
|
529
|
+
tool_result = await agent.execute_tool_call(
|
|
530
|
+
tool_name,
|
|
531
|
+
tool_use["input"],
|
|
532
|
+
)
|
|
533
|
+
|
|
534
|
+
tool_result_message = agent.format_message(
|
|
535
|
+
MessageType.ToolResult,
|
|
536
|
+
{"tool_use": tool_use, "tool_result": tool_result},
|
|
537
|
+
)
|
|
538
|
+
if tool_result_message:
|
|
539
|
+
self.task_history[task.context_id].append(
|
|
540
|
+
tool_result_message
|
|
541
|
+
)
|
|
542
|
+
|
|
543
|
+
except Exception as e:
|
|
544
|
+
error_message = agent.format_message(
|
|
545
|
+
MessageType.ToolResult,
|
|
546
|
+
{
|
|
547
|
+
"tool_use": tool_use,
|
|
548
|
+
"tool_result": str(e),
|
|
549
|
+
"is_error": True,
|
|
550
|
+
},
|
|
551
|
+
)
|
|
552
|
+
if error_message:
|
|
553
|
+
self.task_history[task.context_id].append(
|
|
554
|
+
error_message
|
|
555
|
+
)
|
|
432
556
|
|
|
433
557
|
return await _process_task()
|
|
434
558
|
return current_response
|
|
@@ -442,9 +566,9 @@ class AgentTaskManager(TaskManager):
|
|
|
442
566
|
},
|
|
443
567
|
)
|
|
444
568
|
if assistant_message:
|
|
445
|
-
self.task_history[task.
|
|
569
|
+
self.task_history[task.context_id].append(assistant_message)
|
|
446
570
|
user_message = (
|
|
447
|
-
self.task_history[task.
|
|
571
|
+
self.task_history[task.context_id][0]
|
|
448
572
|
.get("content", [{}])[0]
|
|
449
573
|
.get("text", "")
|
|
450
574
|
)
|
|
@@ -111,25 +111,25 @@ class LocalAgent(BaseAgent):
|
|
|
111
111
|
# self.tool_prompts.append(
|
|
112
112
|
# delegate_tool_prompt(self.services["agent_manager"])
|
|
113
113
|
# )
|
|
114
|
+
from AgentCrew.modules.agents.tools.ask import (
|
|
115
|
+
register as register_ask,
|
|
116
|
+
ask_tool_prompt,
|
|
117
|
+
)
|
|
118
|
+
|
|
119
|
+
register_ask(self)
|
|
120
|
+
self.tool_prompts.append(ask_tool_prompt())
|
|
121
|
+
|
|
114
122
|
if not self.is_remoting_mode:
|
|
115
123
|
from AgentCrew.modules.agents.tools.transfer import (
|
|
116
124
|
register as register_transfer,
|
|
117
125
|
transfer_tool_prompt,
|
|
118
126
|
)
|
|
119
|
-
from AgentCrew.modules.agents.tools.ask import (
|
|
120
|
-
register as register_ask,
|
|
121
|
-
ask_tool_prompt,
|
|
122
|
-
)
|
|
123
127
|
|
|
124
128
|
register_transfer(self.services["agent_manager"], self)
|
|
125
129
|
self.tool_prompts.append(
|
|
126
130
|
transfer_tool_prompt(self.services["agent_manager"])
|
|
127
131
|
)
|
|
128
132
|
|
|
129
|
-
# Register the ask tool (always available)
|
|
130
|
-
register_ask(self)
|
|
131
|
-
self.tool_prompts.append(ask_tool_prompt())
|
|
132
|
-
|
|
133
133
|
for tool_name in self.tools:
|
|
134
134
|
if self.services and tool_name in self.services:
|
|
135
135
|
service = self.services[tool_name]
|
|
@@ -15,8 +15,8 @@ MAX_CONCURRENT_COMMANDS = 3
|
|
|
15
15
|
# Maximum lifetime for a single command execution (seconds)
|
|
16
16
|
MAX_COMMAND_LIFETIME = 600
|
|
17
17
|
|
|
18
|
-
# Maximum output
|
|
19
|
-
|
|
18
|
+
# Maximum output lines to keep in rolling buffer per stream (stdout/stderr)
|
|
19
|
+
MAX_OUTPUT_LINES = 300
|
|
20
20
|
|
|
21
21
|
# Maximum number of commands allowed per minute (application-wide rate limit)
|
|
22
22
|
MAX_COMMANDS_PER_MINUTE = 10
|
|
@@ -2,7 +2,6 @@ import os
|
|
|
2
2
|
import sys
|
|
3
3
|
import time
|
|
4
4
|
import uuid
|
|
5
|
-
import queue
|
|
6
5
|
import threading
|
|
7
6
|
import subprocess
|
|
8
7
|
import re
|
|
@@ -10,12 +9,11 @@ import atexit
|
|
|
10
9
|
import hashlib
|
|
11
10
|
from typing import Dict, Any, Optional, Tuple, List
|
|
12
11
|
from datetime import datetime
|
|
13
|
-
from .metric import CommandMetrics
|
|
14
12
|
from .types import CommandState, CommandProcess
|
|
15
13
|
from .constants import (
|
|
16
14
|
MAX_CONCURRENT_COMMANDS,
|
|
17
15
|
MAX_COMMAND_LIFETIME,
|
|
18
|
-
|
|
16
|
+
MAX_OUTPUT_LINES,
|
|
19
17
|
MAX_COMMANDS_PER_MINUTE,
|
|
20
18
|
MAX_INPUT_SIZE,
|
|
21
19
|
BLOCKED_PATTERNS,
|
|
@@ -68,9 +66,6 @@ class CommandExecutionService:
|
|
|
68
66
|
# Rate limiting (application-wide)
|
|
69
67
|
self._rate_limiter: List[float] = []
|
|
70
68
|
|
|
71
|
-
# Metrics
|
|
72
|
-
self.metrics = CommandMetrics()
|
|
73
|
-
|
|
74
69
|
# Register cleanup on shutdown
|
|
75
70
|
atexit.register(self.shutdown)
|
|
76
71
|
|
|
@@ -242,40 +237,40 @@ class CommandExecutionService:
|
|
|
242
237
|
def _reader_thread(
|
|
243
238
|
self,
|
|
244
239
|
stream,
|
|
245
|
-
|
|
240
|
+
output_list: list,
|
|
241
|
+
output_lock: threading.Lock,
|
|
246
242
|
stop_event: threading.Event,
|
|
247
|
-
|
|
243
|
+
max_lines: int,
|
|
248
244
|
):
|
|
249
245
|
"""
|
|
250
|
-
Read stream line by line into
|
|
246
|
+
Read stream line by line into persistent list with rolling buffer.
|
|
251
247
|
|
|
252
|
-
|
|
253
|
-
- ('data', line): Normal output line
|
|
254
|
-
- ('eof', None): End of stream
|
|
255
|
-
- ('error', msg): Error occurred
|
|
256
|
-
- ('size_limit', None): Output size limit reached
|
|
257
|
-
"""
|
|
258
|
-
total_bytes = 0
|
|
248
|
+
When output exceeds max_lines, old lines are removed to keep recent output.
|
|
259
249
|
|
|
250
|
+
Args:
|
|
251
|
+
stream: Process stdout or stderr stream
|
|
252
|
+
output_list: Persistent list to append output lines
|
|
253
|
+
output_lock: Threading lock for thread-safe list access
|
|
254
|
+
stop_event: Event to signal thread stop
|
|
255
|
+
max_lines: Maximum number of lines to keep (rolling buffer)
|
|
256
|
+
"""
|
|
260
257
|
try:
|
|
261
258
|
for line in iter(stream.readline, b""):
|
|
262
259
|
if stop_event.is_set():
|
|
263
260
|
break
|
|
264
261
|
|
|
265
|
-
total_bytes += len(line)
|
|
266
|
-
if total_bytes > max_size:
|
|
267
|
-
output_queue.put(("size_limit", None))
|
|
268
|
-
logger.warning(f"Output size limit ({max_size} bytes) exceeded")
|
|
269
|
-
break
|
|
270
|
-
|
|
271
262
|
decoded = line.decode("utf-8", errors="replace")
|
|
272
|
-
|
|
263
|
+
|
|
264
|
+
with output_lock:
|
|
265
|
+
output_list.append(decoded)
|
|
266
|
+
|
|
267
|
+
# Keep only recent lines using slice
|
|
268
|
+
if len(output_list) > max_lines:
|
|
269
|
+
output_list[:] = output_list[-max_lines:]
|
|
273
270
|
|
|
274
271
|
except Exception as e:
|
|
275
272
|
logger.error(f"Reader thread error: {e}")
|
|
276
|
-
output_queue.put(("error", str(e)))
|
|
277
273
|
finally:
|
|
278
|
-
output_queue.put(("eof", None))
|
|
279
274
|
stream.close()
|
|
280
275
|
|
|
281
276
|
def execute_command(
|
|
@@ -371,9 +366,10 @@ class CommandExecutionService:
|
|
|
371
366
|
target=self._reader_thread,
|
|
372
367
|
args=(
|
|
373
368
|
process.stdout,
|
|
374
|
-
cmd_process.
|
|
369
|
+
cmd_process.stdout_lines,
|
|
370
|
+
cmd_process.output_lock,
|
|
375
371
|
cmd_process.stop_event,
|
|
376
|
-
|
|
372
|
+
MAX_OUTPUT_LINES,
|
|
377
373
|
),
|
|
378
374
|
daemon=True,
|
|
379
375
|
name=f"stdout-reader-{command_id}",
|
|
@@ -383,9 +379,10 @@ class CommandExecutionService:
|
|
|
383
379
|
target=self._reader_thread,
|
|
384
380
|
args=(
|
|
385
381
|
process.stderr,
|
|
386
|
-
cmd_process.
|
|
382
|
+
cmd_process.stderr_lines,
|
|
383
|
+
cmd_process.output_lock,
|
|
387
384
|
cmd_process.stop_event,
|
|
388
|
-
|
|
385
|
+
MAX_OUTPUT_LINES,
|
|
389
386
|
),
|
|
390
387
|
daemon=True,
|
|
391
388
|
name=f"stderr-reader-{command_id}",
|
|
@@ -405,21 +402,10 @@ class CommandExecutionService:
|
|
|
405
402
|
cmd_process.exit_code = process.returncode
|
|
406
403
|
cmd_process.transition_to(CommandState.COMPLETING)
|
|
407
404
|
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
msg_type, data = cmd_process.output_queue.get()
|
|
413
|
-
if msg_type == "data":
|
|
414
|
-
output_lines.append(data)
|
|
415
|
-
|
|
416
|
-
while not cmd_process.error_queue.empty():
|
|
417
|
-
msg_type, data = cmd_process.error_queue.get()
|
|
418
|
-
if msg_type == "data":
|
|
419
|
-
error_lines.append(data)
|
|
420
|
-
|
|
421
|
-
output = "".join(output_lines)
|
|
422
|
-
error_output = "".join(error_lines)
|
|
405
|
+
# Get output from persistent storage (thread-safe)
|
|
406
|
+
with cmd_process.output_lock:
|
|
407
|
+
output = "".join(cmd_process.stdout_lines)
|
|
408
|
+
error_output = "".join(cmd_process.stderr_lines)
|
|
423
409
|
|
|
424
410
|
duration = time.time() - start_time
|
|
425
411
|
|
|
@@ -433,7 +419,6 @@ class CommandExecutionService:
|
|
|
433
419
|
len(output) + len(error_output),
|
|
434
420
|
)
|
|
435
421
|
|
|
436
|
-
self.metrics.record_execution(command, duration, "completed")
|
|
437
422
|
self._cleanup_command_internal(command_id)
|
|
438
423
|
|
|
439
424
|
result = {
|
|
@@ -466,22 +451,20 @@ class CommandExecutionService:
|
|
|
466
451
|
logger.error(f"Command execution error: {e}")
|
|
467
452
|
|
|
468
453
|
self._audit_log(command, "error", command_id)
|
|
469
|
-
self.metrics.record_execution(command, time.time() - start_time, "error")
|
|
470
454
|
|
|
471
455
|
if command_id in self._instances:
|
|
472
456
|
self._cleanup_command_internal(command_id)
|
|
473
457
|
|
|
474
458
|
return {"status": "error", "error": f"Execution failed: {str(e)}"}
|
|
475
459
|
|
|
476
|
-
def get_command_status(
|
|
477
|
-
self, command_id: str, consume_output: bool = True
|
|
478
|
-
) -> Dict[str, Any]:
|
|
460
|
+
def get_command_status(self, command_id: str) -> Dict[str, Any]:
|
|
479
461
|
"""
|
|
480
462
|
Check status of running command.
|
|
481
463
|
|
|
464
|
+
Output is persistent and will be returned in full on every call.
|
|
465
|
+
|
|
482
466
|
Args:
|
|
483
467
|
command_id: Command identifier
|
|
484
|
-
consume_output: If True, drain and return queued output
|
|
485
468
|
|
|
486
469
|
Returns:
|
|
487
470
|
Dict with status, output, exit_code, elapsed_time
|
|
@@ -494,30 +477,11 @@ class CommandExecutionService:
|
|
|
494
477
|
|
|
495
478
|
exit_code = cmd_process.process.poll()
|
|
496
479
|
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
while not cmd_process.output_queue.empty():
|
|
502
|
-
try:
|
|
503
|
-
msg_type, data = cmd_process.output_queue.get_nowait()
|
|
504
|
-
if msg_type == "data":
|
|
505
|
-
output_lines.append(data)
|
|
506
|
-
elif msg_type == "size_limit":
|
|
507
|
-
output_lines.append("\n[OUTPUT SIZE LIMIT REACHED]\n")
|
|
508
|
-
except queue.Empty:
|
|
509
|
-
break
|
|
510
|
-
|
|
511
|
-
while not cmd_process.error_queue.empty():
|
|
512
|
-
try:
|
|
513
|
-
msg_type, data = cmd_process.error_queue.get_nowait()
|
|
514
|
-
if msg_type == "data":
|
|
515
|
-
error_lines.append(data)
|
|
516
|
-
except queue.Empty:
|
|
517
|
-
break
|
|
480
|
+
# Get output from persistent storage (thread-safe)
|
|
481
|
+
with cmd_process.output_lock:
|
|
482
|
+
output = "".join(cmd_process.stdout_lines)
|
|
483
|
+
error_output = "".join(cmd_process.stderr_lines)
|
|
518
484
|
|
|
519
|
-
output = "".join(output_lines)
|
|
520
|
-
error_output = "".join(error_lines)
|
|
521
485
|
elapsed = time.time() - cmd_process.start_time
|
|
522
486
|
|
|
523
487
|
if elapsed > MAX_COMMAND_LIFETIME:
|
|
@@ -546,7 +510,6 @@ class CommandExecutionService:
|
|
|
546
510
|
duration,
|
|
547
511
|
len(output) + len(error_output),
|
|
548
512
|
)
|
|
549
|
-
self.metrics.record_execution(cmd_process.command, duration, "completed")
|
|
550
513
|
self._cleanup_command_internal(command_id)
|
|
551
514
|
|
|
552
515
|
return {
|
|
@@ -690,11 +653,6 @@ class CommandExecutionService:
|
|
|
690
653
|
pass
|
|
691
654
|
|
|
692
655
|
cmd_process.transition_to(CommandState.KILLED)
|
|
693
|
-
self.metrics.record_execution(
|
|
694
|
-
cmd_process.command,
|
|
695
|
-
time.time() - cmd_process.start_time,
|
|
696
|
-
"killed",
|
|
697
|
-
)
|
|
698
656
|
|
|
699
657
|
except Exception as e:
|
|
700
658
|
logger.error(f"Process termination error: {e}")
|
|
@@ -767,10 +725,6 @@ class CommandExecutionService:
|
|
|
767
725
|
"""
|
|
768
726
|
return self.cleanup_command(command_id)
|
|
769
727
|
|
|
770
|
-
def get_metrics(self) -> Dict[str, Any]:
|
|
771
|
-
"""Get command execution metrics"""
|
|
772
|
-
return self.metrics.get_report()
|
|
773
|
-
|
|
774
728
|
def shutdown(self):
|
|
775
729
|
"""Shutdown service and cleanup all running commands"""
|
|
776
730
|
logger.info("Shutting down CommandExecutionService")
|
|
@@ -6,6 +6,7 @@ Tool definitions and handlers for secure shell command execution.
|
|
|
6
6
|
|
|
7
7
|
from typing import Dict, Any, Callable
|
|
8
8
|
from .service import CommandExecutionService
|
|
9
|
+
import os
|
|
9
10
|
|
|
10
11
|
|
|
11
12
|
def get_run_command_tool_definition(provider="claude") -> Dict[str, Any]:
|
|
@@ -34,8 +35,7 @@ def get_run_command_tool_definition(provider="claude") -> Dict[str, Any]:
|
|
|
34
35
|
},
|
|
35
36
|
"working_dir": {
|
|
36
37
|
"type": "string",
|
|
37
|
-
"
|
|
38
|
-
"description": "Working directory.",
|
|
38
|
+
"description": f"Working directory. Current working directory is {os.getcwd()}. Use ./ for current dir.",
|
|
39
39
|
},
|
|
40
40
|
"env_vars": {
|
|
41
41
|
"type": "object",
|
|
@@ -50,7 +50,7 @@ def get_run_command_tool_definition(provider="claude") -> Dict[str, Any]:
|
|
|
50
50
|
"input_schema": {
|
|
51
51
|
"type": "object",
|
|
52
52
|
"properties": args,
|
|
53
|
-
"required": ["command"],
|
|
53
|
+
"required": ["command", "working_dir"],
|
|
54
54
|
},
|
|
55
55
|
}
|
|
56
56
|
else:
|
|
@@ -62,7 +62,7 @@ def get_run_command_tool_definition(provider="claude") -> Dict[str, Any]:
|
|
|
62
62
|
"parameters": {
|
|
63
63
|
"type": "object",
|
|
64
64
|
"properties": args,
|
|
65
|
-
"required": ["command"],
|
|
65
|
+
"required": ["command", "working_dir"],
|
|
66
66
|
},
|
|
67
67
|
},
|
|
68
68
|
}
|
|
@@ -258,9 +258,7 @@ def get_check_command_status_tool_handler(
|
|
|
258
258
|
if not command_id:
|
|
259
259
|
raise ValueError("Missing required parameter: command_id")
|
|
260
260
|
|
|
261
|
-
result = command_service.get_command_status(
|
|
262
|
-
command_id=command_id, consume_output=True
|
|
263
|
-
)
|
|
261
|
+
result = command_service.get_command_status(command_id=command_id)
|
|
264
262
|
|
|
265
263
|
if result["status"] == "completed":
|
|
266
264
|
response = f"Command completed.\nExit Code: {result['exit_code']}\nDuration: {result['duration_seconds']}s\n\n"
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import queue
|
|
2
1
|
import threading
|
|
3
2
|
import subprocess
|
|
4
3
|
from enum import Enum
|
|
@@ -31,13 +30,13 @@ class CommandProcess:
|
|
|
31
30
|
process: subprocess.Popen
|
|
32
31
|
platform: str
|
|
33
32
|
start_time: float
|
|
34
|
-
|
|
35
|
-
|
|
33
|
+
stdout_lines: List[str] = field(default_factory=list)
|
|
34
|
+
stderr_lines: List[str] = field(default_factory=list)
|
|
35
|
+
output_lock: threading.Lock = field(default_factory=threading.Lock)
|
|
36
36
|
state: CommandState = CommandState.QUEUED
|
|
37
37
|
exit_code: Optional[int] = None
|
|
38
38
|
reader_threads: List[threading.Thread] = field(default_factory=list)
|
|
39
39
|
stop_event: threading.Event = field(default_factory=threading.Event)
|
|
40
|
-
total_output_size: int = 0
|
|
41
40
|
working_dir: Optional[str] = None
|
|
42
41
|
|
|
43
42
|
def transition_to(self, new_state: CommandState):
|