auto-coder 1.0.0__py3-none-any.whl → 2.0.0__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 auto-coder might be problematic. Click here for more details.
- auto_coder-2.0.0.dist-info/LICENSE +158 -0
- auto_coder-2.0.0.dist-info/METADATA +558 -0
- auto_coder-2.0.0.dist-info/RECORD +795 -0
- {auto_coder-1.0.0.dist-info → auto_coder-2.0.0.dist-info}/WHEEL +1 -1
- {auto_coder-1.0.0.dist-info → auto_coder-2.0.0.dist-info}/entry_points.txt +3 -3
- autocoder/__init__.py +31 -0
- autocoder/agent/auto_filegroup.py +32 -13
- autocoder/agent/auto_learn_from_commit.py +9 -1
- autocoder/agent/base_agentic/__init__.py +3 -0
- autocoder/agent/base_agentic/agent_hub.py +1 -1
- autocoder/agent/base_agentic/base_agent.py +235 -136
- autocoder/agent/base_agentic/default_tools.py +119 -118
- autocoder/agent/base_agentic/test_base_agent.py +1 -1
- autocoder/agent/base_agentic/tool_registry.py +32 -20
- autocoder/agent/base_agentic/tools/read_file_tool_resolver.py +24 -3
- autocoder/agent/base_agentic/tools/write_to_file_tool_resolver.py +24 -11
- autocoder/agent/base_agentic/types.py +42 -0
- autocoder/agent/entry_command_agent/chat.py +73 -59
- autocoder/auto_coder.py +31 -40
- autocoder/auto_coder_rag.py +11 -1084
- autocoder/auto_coder_runner.py +970 -2345
- autocoder/auto_coder_terminal.py +26 -0
- autocoder/auto_coder_terminal_v3.py +190 -0
- autocoder/chat/conf_command.py +224 -124
- autocoder/chat/models_command.py +361 -299
- autocoder/chat/rules_command.py +79 -31
- autocoder/chat_auto_coder.py +988 -398
- autocoder/chat_auto_coder_lang.py +23 -732
- autocoder/commands/auto_command.py +25 -8
- autocoder/commands/auto_web.py +1 -1
- autocoder/commands/tools.py +44 -44
- autocoder/common/__init__.py +150 -128
- autocoder/common/ac_style_command_parser/__init__.py +39 -2
- autocoder/common/ac_style_command_parser/config.py +422 -0
- autocoder/common/ac_style_command_parser/parser.py +292 -78
- autocoder/common/ac_style_command_parser/test_parser.py +241 -16
- autocoder/common/ac_style_command_parser/test_typed_parser.py +342 -0
- autocoder/common/ac_style_command_parser/typed_parser.py +653 -0
- autocoder/common/action_yml_file_manager.py +25 -13
- autocoder/common/agent_events/__init__.py +52 -0
- autocoder/common/agent_events/agent_event_emitter.py +193 -0
- autocoder/common/agent_events/event_factory.py +177 -0
- autocoder/common/agent_events/examples.py +307 -0
- autocoder/common/agent_events/types.py +113 -0
- autocoder/common/agent_events/utils.py +68 -0
- autocoder/common/agent_hooks/__init__.py +44 -0
- autocoder/common/agent_hooks/examples.py +582 -0
- autocoder/common/agent_hooks/hook_executor.py +217 -0
- autocoder/common/agent_hooks/hook_manager.py +288 -0
- autocoder/common/agent_hooks/types.py +133 -0
- autocoder/common/agent_hooks/utils.py +99 -0
- autocoder/common/agent_query_queue/queue_executor.py +324 -0
- autocoder/common/agent_query_queue/queue_manager.py +325 -0
- autocoder/common/agents/__init__.py +11 -0
- autocoder/common/agents/agent_manager.py +323 -0
- autocoder/common/agents/agent_parser.py +189 -0
- autocoder/common/agents/example_usage.py +344 -0
- autocoder/common/agents/integration_example.py +330 -0
- autocoder/common/agents/test_agent_parser.py +545 -0
- autocoder/common/async_utils.py +101 -0
- autocoder/common/auto_coder_lang.py +23 -972
- autocoder/common/autocoderargs_parser/__init__.py +14 -0
- autocoder/common/autocoderargs_parser/parser.py +184 -0
- autocoder/common/autocoderargs_parser/tests/__init__.py +1 -0
- autocoder/common/autocoderargs_parser/tests/test_args_parser.py +235 -0
- autocoder/common/autocoderargs_parser/tests/test_token_parser.py +195 -0
- autocoder/common/autocoderargs_parser/token_parser.py +290 -0
- autocoder/common/buildin_tokenizer.py +2 -4
- autocoder/common/code_auto_generate.py +149 -74
- autocoder/common/code_auto_generate_diff.py +163 -70
- autocoder/common/code_auto_generate_editblock.py +179 -89
- autocoder/common/code_auto_generate_strict_diff.py +167 -72
- autocoder/common/code_auto_merge_editblock.py +13 -6
- autocoder/common/code_modification_ranker.py +1 -1
- autocoder/common/command_completer.py +3 -3
- autocoder/common/command_file_manager/manager.py +183 -47
- autocoder/common/command_file_manager/test_command_file_manager.py +507 -0
- autocoder/common/command_templates.py +1 -1
- autocoder/common/conf_utils.py +2 -4
- autocoder/common/conversations/config.py +11 -3
- autocoder/common/conversations/get_conversation_manager.py +100 -2
- autocoder/common/conversations/llm_stats_models.py +264 -0
- autocoder/common/conversations/manager.py +112 -28
- autocoder/common/conversations/models.py +16 -2
- autocoder/common/conversations/storage/index_manager.py +134 -10
- autocoder/common/core_config/__init__.py +63 -0
- autocoder/common/core_config/agentic_mode_manager.py +109 -0
- autocoder/common/core_config/base_manager.py +123 -0
- autocoder/common/core_config/compatibility.py +151 -0
- autocoder/common/core_config/config_manager.py +156 -0
- autocoder/common/core_config/conversation_manager.py +31 -0
- autocoder/common/core_config/exclude_manager.py +72 -0
- autocoder/common/core_config/file_manager.py +177 -0
- autocoder/common/core_config/human_as_model_manager.py +129 -0
- autocoder/common/core_config/lib_manager.py +54 -0
- autocoder/common/core_config/main_manager.py +81 -0
- autocoder/common/core_config/mode_manager.py +126 -0
- autocoder/common/core_config/models.py +70 -0
- autocoder/common/core_config/test_memory_manager.py +1056 -0
- autocoder/common/env_manager.py +282 -0
- autocoder/common/env_manager_usage_example.py +211 -0
- autocoder/common/file_checkpoint/conversation_checkpoint.py +19 -19
- autocoder/common/file_checkpoint/manager.py +264 -48
- autocoder/common/file_checkpoint/test_backup.py +1 -18
- autocoder/common/file_checkpoint/test_manager.py +270 -1
- autocoder/common/file_checkpoint/test_store.py +1 -17
- autocoder/common/file_handler/__init__.py +23 -0
- autocoder/common/file_handler/active_context_handler.py +159 -0
- autocoder/common/file_handler/add_files_handler.py +409 -0
- autocoder/common/file_handler/chat_handler.py +180 -0
- autocoder/common/file_handler/coding_handler.py +401 -0
- autocoder/common/file_handler/commit_handler.py +200 -0
- autocoder/common/file_handler/lib_handler.py +156 -0
- autocoder/common/file_handler/list_files_handler.py +111 -0
- autocoder/common/file_handler/mcp_handler.py +268 -0
- autocoder/common/file_handler/models_handler.py +493 -0
- autocoder/common/file_handler/remove_files_handler.py +172 -0
- autocoder/common/git_utils.py +44 -8
- autocoder/common/global_cancel.py +15 -6
- autocoder/common/ignorefiles/test_ignore_file_utils.py +1 -1
- autocoder/common/international/__init__.py +31 -0
- autocoder/common/international/demo_international.py +92 -0
- autocoder/common/international/message_manager.py +157 -0
- autocoder/common/international/messages/__init__.py +56 -0
- autocoder/common/international/messages/async_command_messages.py +507 -0
- autocoder/common/international/messages/auto_coder_messages.py +2208 -0
- autocoder/common/international/messages/chat_auto_coder_messages.py +1547 -0
- autocoder/common/international/messages/command_help_messages.py +986 -0
- autocoder/common/international/messages/conversation_command_messages.py +191 -0
- autocoder/common/international/messages/git_helper_plugin_messages.py +159 -0
- autocoder/common/international/messages/queue_command_messages.py +751 -0
- autocoder/common/international/messages/rules_command_messages.py +77 -0
- autocoder/common/international/messages/sdk_messages.py +1707 -0
- autocoder/common/international/messages/token_helper_plugin_messages.py +361 -0
- autocoder/common/international/messages/tool_display_messages.py +1212 -0
- autocoder/common/international/messages/workflow_exception_messages.py +473 -0
- autocoder/common/international/test_international.py +612 -0
- autocoder/common/linter_core/__init__.py +28 -0
- autocoder/common/linter_core/base_linter.py +61 -0
- autocoder/common/linter_core/config_loader.py +271 -0
- autocoder/common/linter_core/formatters/__init__.py +0 -0
- autocoder/common/linter_core/formatters/base_formatter.py +38 -0
- autocoder/common/linter_core/formatters/raw_formatter.py +17 -0
- autocoder/common/linter_core/linter.py +166 -0
- autocoder/common/linter_core/linter_factory.py +216 -0
- autocoder/common/linter_core/linter_manager.py +333 -0
- autocoder/common/linter_core/linters/__init__.py +9 -0
- autocoder/common/linter_core/linters/java_linter.py +342 -0
- autocoder/common/linter_core/linters/python_linter.py +115 -0
- autocoder/common/linter_core/linters/typescript_linter.py +119 -0
- autocoder/common/linter_core/models/__init__.py +7 -0
- autocoder/common/linter_core/models/lint_result.py +91 -0
- autocoder/common/linter_core/models.py +33 -0
- autocoder/common/linter_core/tests/__init__.py +3 -0
- autocoder/common/linter_core/tests/test_config_loader.py +323 -0
- autocoder/common/linter_core/tests/test_config_loading.py +308 -0
- autocoder/common/linter_core/tests/test_factory_manager.py +234 -0
- autocoder/common/linter_core/tests/test_formatters.py +147 -0
- autocoder/common/linter_core/tests/test_integration.py +317 -0
- autocoder/common/linter_core/tests/test_java_linter.py +496 -0
- autocoder/common/linter_core/tests/test_linters.py +265 -0
- autocoder/common/linter_core/tests/test_models.py +81 -0
- autocoder/common/linter_core/tests/verify_config_loading.py +296 -0
- autocoder/common/linter_core/tests/verify_fixes.py +183 -0
- autocoder/common/llm_friendly_package/__init__.py +31 -0
- autocoder/common/llm_friendly_package/base_manager.py +102 -0
- autocoder/common/llm_friendly_package/docs_manager.py +121 -0
- autocoder/common/llm_friendly_package/library_manager.py +171 -0
- autocoder/common/{llm_friendly_package.py → llm_friendly_package/main_manager.py} +204 -231
- autocoder/common/llm_friendly_package/models.py +40 -0
- autocoder/common/llm_friendly_package/test_llm_friendly_package.py +536 -0
- autocoder/common/llms/__init__.py +15 -0
- autocoder/common/llms/demo_error_handling.py +85 -0
- autocoder/common/llms/factory.py +142 -0
- autocoder/common/llms/manager.py +264 -0
- autocoder/common/llms/pricing.py +121 -0
- autocoder/common/llms/registry.py +288 -0
- autocoder/common/llms/schema.py +77 -0
- autocoder/common/llms/simple_demo.py +45 -0
- autocoder/common/llms/test_quick_model.py +116 -0
- autocoder/common/llms/test_remove_functionality.py +182 -0
- autocoder/common/llms/tests/__init__.py +1 -0
- autocoder/common/llms/tests/test_manager.py +330 -0
- autocoder/common/llms/tests/test_registry.py +364 -0
- autocoder/common/mcp_tools/__init__.py +62 -0
- autocoder/common/{mcp_tools.py → mcp_tools/executor.py} +49 -40
- autocoder/common/{mcp_hub.py → mcp_tools/hub.py} +42 -68
- autocoder/common/{mcp_server_install.py → mcp_tools/installer.py} +16 -28
- autocoder/common/{mcp_server.py → mcp_tools/server.py} +176 -48
- autocoder/common/mcp_tools/test_keyboard_interrupt.py +93 -0
- autocoder/common/mcp_tools/test_mcp_tools.py +391 -0
- autocoder/common/{mcp_server_types.py → mcp_tools/types.py} +121 -48
- autocoder/common/mcp_tools/verify_functionality.py +202 -0
- autocoder/common/model_speed_tester.py +32 -26
- autocoder/common/priority_directory_finder/__init__.py +142 -0
- autocoder/common/priority_directory_finder/examples.py +230 -0
- autocoder/common/priority_directory_finder/finder.py +283 -0
- autocoder/common/priority_directory_finder/models.py +236 -0
- autocoder/common/priority_directory_finder/test_priority_directory_finder.py +431 -0
- autocoder/common/project_scanner/__init__.py +18 -0
- autocoder/common/project_scanner/compat.py +77 -0
- autocoder/common/project_scanner/scanner.py +436 -0
- autocoder/common/project_tracker/__init__.py +27 -0
- autocoder/common/project_tracker/api.py +228 -0
- autocoder/common/project_tracker/demo.py +272 -0
- autocoder/common/project_tracker/tracker.py +487 -0
- autocoder/common/project_tracker/types.py +53 -0
- autocoder/common/pruner/__init__.py +67 -0
- autocoder/common/pruner/agentic_conversation_pruner.py +651 -102
- autocoder/common/pruner/conversation_message_ids_api.py +386 -0
- autocoder/common/pruner/conversation_message_ids_manager.py +347 -0
- autocoder/common/pruner/conversation_message_ids_pruner.py +473 -0
- autocoder/common/pruner/conversation_normalizer.py +347 -0
- autocoder/common/pruner/conversation_pruner.py +26 -6
- autocoder/common/pruner/test_agentic_conversation_pruner.py +554 -112
- autocoder/common/pruner/test_conversation_normalizer.py +502 -0
- autocoder/common/pruner/test_tool_content_detector.py +324 -0
- autocoder/common/pruner/tool_content_detector.py +227 -0
- autocoder/common/pruner/tools/__init__.py +18 -0
- autocoder/common/pruner/tools/query_message_ids.py +264 -0
- autocoder/common/pruner/tools/test_agentic_pruning_logic.py +432 -0
- autocoder/common/pruner/tools/test_message_ids_pruning_only.py +192 -0
- autocoder/common/pull_requests/__init__.py +9 -1
- autocoder/common/pull_requests/utils.py +122 -1
- autocoder/common/rag_manager/rag_manager.py +36 -40
- autocoder/common/rulefiles/__init__.py +53 -1
- autocoder/common/rulefiles/api.py +250 -0
- autocoder/common/rulefiles/core/__init__.py +14 -0
- autocoder/common/rulefiles/core/manager.py +241 -0
- autocoder/common/rulefiles/core/selector.py +805 -0
- autocoder/common/rulefiles/models/__init__.py +20 -0
- autocoder/common/rulefiles/models/index.py +16 -0
- autocoder/common/rulefiles/models/init_rule.py +18 -0
- autocoder/common/rulefiles/models/rule_file.py +18 -0
- autocoder/common/rulefiles/models/rule_relevance.py +14 -0
- autocoder/common/rulefiles/models/summary.py +16 -0
- autocoder/common/rulefiles/test_rulefiles.py +776 -0
- autocoder/common/rulefiles/utils/__init__.py +34 -0
- autocoder/common/rulefiles/utils/monitor.py +86 -0
- autocoder/common/rulefiles/utils/parser.py +230 -0
- autocoder/common/save_formatted_log.py +67 -10
- autocoder/common/search_replace.py +8 -1
- autocoder/common/search_replace_patch/__init__.py +24 -0
- autocoder/common/search_replace_patch/base.py +115 -0
- autocoder/common/search_replace_patch/manager.py +248 -0
- autocoder/common/search_replace_patch/patch_replacer.py +304 -0
- autocoder/common/search_replace_patch/similarity_replacer.py +306 -0
- autocoder/common/search_replace_patch/string_replacer.py +181 -0
- autocoder/common/search_replace_patch/tests/__init__.py +3 -0
- autocoder/common/search_replace_patch/tests/run_tests.py +126 -0
- autocoder/common/search_replace_patch/tests/test_base.py +188 -0
- autocoder/common/search_replace_patch/tests/test_empty_line_insert.py +233 -0
- autocoder/common/search_replace_patch/tests/test_integration.py +389 -0
- autocoder/common/search_replace_patch/tests/test_manager.py +351 -0
- autocoder/common/search_replace_patch/tests/test_patch_replacer.py +316 -0
- autocoder/common/search_replace_patch/tests/test_regex_replacer.py +306 -0
- autocoder/common/search_replace_patch/tests/test_similarity_replacer.py +384 -0
- autocoder/common/shell_commands/__init__.py +197 -0
- autocoder/common/shell_commands/background_process_notifier.py +346 -0
- autocoder/common/shell_commands/command_executor.py +1127 -0
- autocoder/common/shell_commands/error_recovery.py +541 -0
- autocoder/common/shell_commands/exceptions.py +120 -0
- autocoder/common/shell_commands/interactive_executor.py +476 -0
- autocoder/common/shell_commands/interactive_pexpect_process.py +623 -0
- autocoder/common/shell_commands/interactive_process.py +744 -0
- autocoder/common/shell_commands/interactive_session_manager.py +1014 -0
- autocoder/common/shell_commands/monitoring.py +529 -0
- autocoder/common/shell_commands/process_cleanup.py +386 -0
- autocoder/common/shell_commands/process_manager.py +606 -0
- autocoder/common/shell_commands/test_interactive_pexpect_process.py +281 -0
- autocoder/common/shell_commands/tests/__init__.py +6 -0
- autocoder/common/shell_commands/tests/conftest.py +118 -0
- autocoder/common/shell_commands/tests/test_background_process_notifier.py +703 -0
- autocoder/common/shell_commands/tests/test_command_executor.py +448 -0
- autocoder/common/shell_commands/tests/test_error_recovery.py +305 -0
- autocoder/common/shell_commands/tests/test_exceptions.py +299 -0
- autocoder/common/shell_commands/tests/test_execute_batch.py +588 -0
- autocoder/common/shell_commands/tests/test_indented_batch_commands.py +244 -0
- autocoder/common/shell_commands/tests/test_integration.py +664 -0
- autocoder/common/shell_commands/tests/test_monitoring.py +546 -0
- autocoder/common/shell_commands/tests/test_performance.py +632 -0
- autocoder/common/shell_commands/tests/test_process_cleanup.py +397 -0
- autocoder/common/shell_commands/tests/test_process_manager.py +606 -0
- autocoder/common/shell_commands/tests/test_timeout_config.py +343 -0
- autocoder/common/shell_commands/tests/test_timeout_manager.py +520 -0
- autocoder/common/shell_commands/timeout_config.py +315 -0
- autocoder/common/shell_commands/timeout_manager.py +352 -0
- autocoder/common/terminal_paste/__init__.py +14 -0
- autocoder/common/terminal_paste/demo.py +145 -0
- autocoder/common/terminal_paste/demo_paste_functionality.py +95 -0
- autocoder/common/terminal_paste/paste_handler.py +200 -0
- autocoder/common/terminal_paste/paste_manager.py +118 -0
- autocoder/common/terminal_paste/tests/__init__.py +1 -0
- autocoder/common/terminal_paste/tests/test_paste_handler.py +182 -0
- autocoder/common/terminal_paste/tests/test_paste_manager.py +126 -0
- autocoder/common/terminal_paste/utils.py +163 -0
- autocoder/common/test_autocoder_args.py +232 -0
- autocoder/common/test_env_manager.py +173 -0
- autocoder/common/test_env_manager_integration.py +159 -0
- autocoder/common/text_similarity/__init__.py +9 -0
- autocoder/common/text_similarity/demo.py +216 -0
- autocoder/common/text_similarity/examples.py +266 -0
- autocoder/common/text_similarity/test_text_similarity.py +306 -0
- autocoder/common/text_similarity/text_similarity.py +194 -0
- autocoder/common/text_similarity/utils.py +125 -0
- autocoder/common/todos/__init__.py +61 -0
- autocoder/common/todos/cache/__init__.py +16 -0
- autocoder/common/todos/cache/base_cache.py +89 -0
- autocoder/common/todos/cache/cache_manager.py +228 -0
- autocoder/common/todos/cache/memory_cache.py +225 -0
- autocoder/common/todos/config.py +155 -0
- autocoder/common/todos/exceptions.py +35 -0
- autocoder/common/todos/get_todo_manager.py +161 -0
- autocoder/common/todos/manager.py +537 -0
- autocoder/common/todos/models.py +239 -0
- autocoder/common/todos/storage/__init__.py +14 -0
- autocoder/common/todos/storage/base_storage.py +76 -0
- autocoder/common/todos/storage/file_storage.py +278 -0
- autocoder/common/tokens/counter.py +24 -2
- autocoder/common/tools_manager/__init__.py +17 -0
- autocoder/common/tools_manager/examples.py +162 -0
- autocoder/common/tools_manager/manager.py +385 -0
- autocoder/common/tools_manager/models.py +39 -0
- autocoder/common/tools_manager/test_tools_manager.py +303 -0
- autocoder/common/tools_manager/utils.py +191 -0
- autocoder/common/v2/agent/agentic_callbacks.py +270 -0
- autocoder/common/v2/agent/agentic_edit.py +2699 -1856
- autocoder/common/v2/agent/agentic_edit_change_manager.py +474 -0
- autocoder/common/v2/agent/agentic_edit_tools/__init__.py +35 -1
- autocoder/common/v2/agent/agentic_edit_tools/ac_mod_list_tool_resolver.py +279 -0
- autocoder/common/v2/agent/agentic_edit_tools/ac_mod_write_tool_resolver.py +10 -1
- autocoder/common/v2/agent/agentic_edit_tools/background_task_tool_resolver.py +1167 -0
- autocoder/common/v2/agent/agentic_edit_tools/base_tool_resolver.py +2 -2
- autocoder/common/v2/agent/agentic_edit_tools/conversation_message_ids_read_tool_resolver.py +214 -0
- autocoder/common/v2/agent/agentic_edit_tools/conversation_message_ids_write_tool_resolver.py +299 -0
- autocoder/common/v2/agent/agentic_edit_tools/count_tokens_tool_resolver.py +290 -0
- autocoder/common/v2/agent/agentic_edit_tools/execute_command_tool_resolver.py +564 -29
- autocoder/common/v2/agent/agentic_edit_tools/execute_workflow_tool_resolver.py +485 -0
- autocoder/common/v2/agent/agentic_edit_tools/extract_to_text_tool_resolver.py +225 -0
- autocoder/common/v2/agent/agentic_edit_tools/lint_report.py +79 -0
- autocoder/common/v2/agent/agentic_edit_tools/linter_config_models.py +343 -0
- autocoder/common/v2/agent/agentic_edit_tools/linter_enabled_tool_resolver.py +189 -0
- autocoder/common/v2/agent/agentic_edit_tools/list_files_tool_resolver.py +169 -101
- autocoder/common/v2/agent/agentic_edit_tools/load_extra_document_tool_resolver.py +349 -0
- autocoder/common/v2/agent/agentic_edit_tools/read_file_tool_resolver.py +243 -50
- autocoder/common/v2/agent/agentic_edit_tools/replace_in_file_tool_resolver.py +667 -147
- autocoder/common/v2/agent/agentic_edit_tools/run_named_subagents_tool_resolver.py +691 -0
- autocoder/common/v2/agent/agentic_edit_tools/search_files_tool_resolver.py +410 -86
- autocoder/common/v2/agent/agentic_edit_tools/session_interactive_tool_resolver.py +115 -0
- autocoder/common/v2/agent/agentic_edit_tools/session_start_tool_resolver.py +190 -0
- autocoder/common/v2/agent/agentic_edit_tools/session_stop_tool_resolver.py +76 -0
- autocoder/common/v2/agent/agentic_edit_tools/test_write_to_file_tool_resolver.py +207 -192
- autocoder/common/v2/agent/agentic_edit_tools/todo_read_tool_resolver.py +80 -63
- autocoder/common/v2/agent/agentic_edit_tools/todo_write_tool_resolver.py +237 -233
- autocoder/common/v2/agent/agentic_edit_tools/use_mcp_tool_resolver.py +2 -2
- autocoder/common/v2/agent/agentic_edit_tools/web_crawl_tool_resolver.py +557 -0
- autocoder/common/v2/agent/agentic_edit_tools/web_search_tool_resolver.py +600 -0
- autocoder/common/v2/agent/agentic_edit_tools/write_to_file_tool_resolver.py +56 -121
- autocoder/common/v2/agent/agentic_edit_types.py +343 -9
- autocoder/common/v2/agent/runner/__init__.py +3 -3
- autocoder/common/v2/agent/runner/base_runner.py +12 -26
- autocoder/common/v2/agent/runner/{event_runner.py → file_based_event_runner.py} +3 -2
- autocoder/common/v2/agent/runner/sdk_runner.py +150 -8
- autocoder/common/v2/agent/runner/terminal_runner.py +170 -57
- autocoder/common/v2/agent/runner/tool_display.py +557 -159
- autocoder/common/v2/agent/test_agentic_callbacks.py +265 -0
- autocoder/common/v2/agent/test_agentic_edit.py +194 -0
- autocoder/common/v2/agent/tool_caller/__init__.py +24 -0
- autocoder/common/v2/agent/tool_caller/default_tool_resolver_map.py +135 -0
- autocoder/common/v2/agent/tool_caller/integration_test.py +172 -0
- autocoder/common/v2/agent/tool_caller/plugins/__init__.py +14 -0
- autocoder/common/v2/agent/tool_caller/plugins/base_plugin.py +126 -0
- autocoder/common/v2/agent/tool_caller/plugins/examples/__init__.py +13 -0
- autocoder/common/v2/agent/tool_caller/plugins/examples/logging_plugin.py +164 -0
- autocoder/common/v2/agent/tool_caller/plugins/examples/security_filter_plugin.py +198 -0
- autocoder/common/v2/agent/tool_caller/plugins/plugin_interface.py +141 -0
- autocoder/common/v2/agent/tool_caller/test_tool_caller.py +278 -0
- autocoder/common/v2/agent/tool_caller/tool_call_plugin_manager.py +331 -0
- autocoder/common/v2/agent/tool_caller/tool_caller.py +337 -0
- autocoder/common/v2/agent/tool_caller/usage_example.py +193 -0
- autocoder/common/v2/code_agentic_editblock_manager.py +4 -4
- autocoder/common/v2/code_auto_generate.py +136 -78
- autocoder/common/v2/code_auto_generate_diff.py +135 -79
- autocoder/common/v2/code_auto_generate_editblock.py +174 -99
- autocoder/common/v2/code_auto_generate_strict_diff.py +151 -71
- autocoder/common/v2/code_auto_merge.py +1 -1
- autocoder/common/v2/code_auto_merge_editblock.py +13 -1
- autocoder/common/v2/code_diff_manager.py +3 -3
- autocoder/common/v2/code_editblock_manager.py +4 -14
- autocoder/common/v2/code_manager.py +1 -1
- autocoder/common/v2/code_strict_diff_manager.py +2 -2
- autocoder/common/wrap_llm_hint/__init__.py +10 -0
- autocoder/common/wrap_llm_hint/test_wrap_llm_hint.py +1067 -0
- autocoder/common/wrap_llm_hint/utils.py +432 -0
- autocoder/common/wrap_llm_hint/wrap_llm_hint.py +323 -0
- autocoder/completer/__init__.py +8 -0
- autocoder/completer/command_completer_v2.py +1051 -0
- autocoder/default_project/__init__.py +501 -0
- autocoder/dispacher/__init__.py +4 -12
- autocoder/dispacher/actions/action.py +165 -7
- autocoder/dispacher/actions/plugins/action_regex_project.py +2 -2
- autocoder/index/entry.py +116 -124
- autocoder/{agent → index/filter}/agentic_filter.py +322 -333
- autocoder/index/filter/normal_filter.py +5 -11
- autocoder/index/filter/quick_filter.py +1 -1
- autocoder/index/index.py +36 -9
- autocoder/index/tests/__init__.py +1 -0
- autocoder/index/tests/run_tests.py +195 -0
- autocoder/index/tests/test_entry.py +303 -0
- autocoder/index/tests/test_index_manager.py +314 -0
- autocoder/index/tests/test_module_integration.py +300 -0
- autocoder/index/tests/test_symbols_utils.py +183 -0
- autocoder/inner/__init__.py +4 -0
- autocoder/inner/agentic.py +932 -0
- autocoder/inner/async_command_handler.py +992 -0
- autocoder/inner/conversation_command_handlers.py +623 -0
- autocoder/inner/merge_command_handler.py +213 -0
- autocoder/inner/queue_command_handler.py +684 -0
- autocoder/models.py +95 -266
- autocoder/plugins/git_helper_plugin.py +31 -29
- autocoder/plugins/token_helper_plugin.py +65 -46
- autocoder/pyproject/__init__.py +32 -29
- autocoder/rag/agentic_rag.py +215 -75
- autocoder/rag/cache/simple_cache.py +1 -2
- autocoder/rag/loaders/image_loader.py +1 -1
- autocoder/rag/long_context_rag.py +42 -26
- autocoder/rag/qa_conversation_strategy.py +1 -1
- autocoder/rag/terminal/__init__.py +17 -0
- autocoder/rag/terminal/args.py +581 -0
- autocoder/rag/terminal/bootstrap.py +61 -0
- autocoder/rag/terminal/command_handlers.py +653 -0
- autocoder/rag/terminal/formatters/__init__.py +20 -0
- autocoder/rag/terminal/formatters/base.py +70 -0
- autocoder/rag/terminal/formatters/json_format.py +66 -0
- autocoder/rag/terminal/formatters/stream_json.py +95 -0
- autocoder/rag/terminal/formatters/text.py +28 -0
- autocoder/rag/terminal/init.py +120 -0
- autocoder/rag/terminal/utils.py +106 -0
- autocoder/rag/test_agentic_rag.py +389 -0
- autocoder/rag/test_doc_filter.py +3 -3
- autocoder/rag/test_long_context_rag.py +1 -1
- autocoder/rag/test_token_limiter.py +517 -10
- autocoder/rag/token_counter.py +3 -0
- autocoder/rag/token_limiter.py +19 -15
- autocoder/rag/tools/__init__.py +26 -2
- autocoder/rag/tools/bochaai_example.py +343 -0
- autocoder/rag/tools/bochaai_sdk.py +541 -0
- autocoder/rag/tools/metaso_example.py +268 -0
- autocoder/rag/tools/metaso_sdk.py +417 -0
- autocoder/rag/tools/recall_tool.py +28 -7
- autocoder/rag/tools/run_integration_tests.py +204 -0
- autocoder/rag/tools/test_all_providers.py +318 -0
- autocoder/rag/tools/test_bochaai_integration.py +482 -0
- autocoder/rag/tools/test_final_integration.py +215 -0
- autocoder/rag/tools/test_metaso_integration.py +424 -0
- autocoder/rag/tools/test_metaso_real.py +171 -0
- autocoder/rag/tools/test_web_crawl_tool.py +639 -0
- autocoder/rag/tools/test_web_search_tool.py +509 -0
- autocoder/rag/tools/todo_read_tool.py +202 -0
- autocoder/rag/tools/todo_write_tool.py +412 -0
- autocoder/rag/tools/web_crawl_tool.py +634 -0
- autocoder/rag/tools/web_search_tool.py +558 -0
- autocoder/rag/tools/web_tools_example.py +119 -0
- autocoder/rag/types.py +16 -0
- autocoder/rag/variable_holder.py +4 -2
- autocoder/rags.py +86 -79
- autocoder/regexproject/__init__.py +23 -21
- autocoder/sdk/__init__.py +46 -190
- autocoder/sdk/api.py +370 -0
- autocoder/sdk/async_runner/__init__.py +26 -0
- autocoder/sdk/async_runner/async_executor.py +650 -0
- autocoder/sdk/async_runner/async_handler.py +356 -0
- autocoder/sdk/async_runner/markdown_processor.py +595 -0
- autocoder/sdk/async_runner/task_metadata.py +284 -0
- autocoder/sdk/async_runner/worktree_manager.py +438 -0
- autocoder/sdk/cli/__init__.py +2 -5
- autocoder/sdk/cli/formatters.py +28 -204
- autocoder/sdk/cli/handlers.py +77 -44
- autocoder/sdk/cli/main.py +154 -171
- autocoder/sdk/cli/options.py +95 -22
- autocoder/sdk/constants.py +139 -51
- autocoder/sdk/core/auto_coder_core.py +484 -109
- autocoder/sdk/core/bridge.py +297 -115
- autocoder/sdk/exceptions.py +18 -12
- autocoder/sdk/formatters/__init__.py +19 -0
- autocoder/sdk/formatters/input.py +64 -0
- autocoder/sdk/formatters/output.py +247 -0
- autocoder/sdk/formatters/stream.py +54 -0
- autocoder/sdk/models/__init__.py +6 -5
- autocoder/sdk/models/options.py +55 -18
- autocoder/sdk/utils/formatters.py +27 -195
- autocoder/suffixproject/__init__.py +28 -25
- autocoder/terminal/__init__.py +14 -0
- autocoder/terminal/app.py +454 -0
- autocoder/terminal/args.py +32 -0
- autocoder/terminal/bootstrap.py +178 -0
- autocoder/terminal/command_processor.py +521 -0
- autocoder/terminal/command_registry.py +57 -0
- autocoder/terminal/help.py +97 -0
- autocoder/terminal/tasks/__init__.py +5 -0
- autocoder/terminal/tasks/background.py +77 -0
- autocoder/terminal/tasks/task_event.py +70 -0
- autocoder/terminal/ui/__init__.py +13 -0
- autocoder/terminal/ui/completer.py +268 -0
- autocoder/terminal/ui/keybindings.py +75 -0
- autocoder/terminal/ui/session.py +41 -0
- autocoder/terminal/ui/toolbar.py +64 -0
- autocoder/terminal/utils/__init__.py +13 -0
- autocoder/terminal/utils/errors.py +18 -0
- autocoder/terminal/utils/paths.py +19 -0
- autocoder/terminal/utils/shell.py +43 -0
- autocoder/terminal_v3/__init__.py +10 -0
- autocoder/terminal_v3/app.py +201 -0
- autocoder/terminal_v3/handlers/__init__.py +5 -0
- autocoder/terminal_v3/handlers/command_handler.py +131 -0
- autocoder/terminal_v3/models/__init__.py +6 -0
- autocoder/terminal_v3/models/conversation_buffer.py +214 -0
- autocoder/terminal_v3/models/message.py +50 -0
- autocoder/terminal_v3/models/tool_display.py +247 -0
- autocoder/terminal_v3/ui/__init__.py +7 -0
- autocoder/terminal_v3/ui/keybindings.py +56 -0
- autocoder/terminal_v3/ui/layout.py +141 -0
- autocoder/terminal_v3/ui/styles.py +43 -0
- autocoder/tsproject/__init__.py +23 -23
- autocoder/utils/auto_coder_utils/chat_stream_out.py +1 -1
- autocoder/utils/llms.py +88 -80
- autocoder/utils/math_utils.py +101 -0
- autocoder/utils/model_provider_selector.py +16 -4
- autocoder/utils/operate_config_api.py +33 -5
- autocoder/utils/thread_utils.py +2 -2
- autocoder/version.py +4 -2
- autocoder/workflow_agents/__init__.py +84 -0
- autocoder/workflow_agents/agent.py +143 -0
- autocoder/workflow_agents/exceptions.py +573 -0
- autocoder/workflow_agents/executor.py +489 -0
- autocoder/workflow_agents/loader.py +737 -0
- autocoder/workflow_agents/runner.py +267 -0
- autocoder/workflow_agents/types.py +172 -0
- autocoder/workflow_agents/utils.py +434 -0
- autocoder/workflow_agents/workflow_manager.py +211 -0
- auto_coder-1.0.0.dist-info/METADATA +0 -396
- auto_coder-1.0.0.dist-info/RECORD +0 -442
- auto_coder-1.0.0.dist-info/licenses/LICENSE +0 -201
- autocoder/auto_coder_server.py +0 -672
- autocoder/benchmark.py +0 -138
- autocoder/common/ac_style_command_parser/example.py +0 -7
- autocoder/common/cleaner.py +0 -31
- autocoder/common/command_completer_v2.py +0 -615
- autocoder/common/context_pruner.py +0 -477
- autocoder/common/conversation_pruner.py +0 -132
- autocoder/common/directory_cache/__init__.py +0 -1
- autocoder/common/directory_cache/cache.py +0 -192
- autocoder/common/directory_cache/test_cache.py +0 -190
- autocoder/common/file_checkpoint/examples.py +0 -217
- autocoder/common/llm_friendly_package_example.py +0 -138
- autocoder/common/llm_friendly_package_test.py +0 -63
- autocoder/common/pull_requests/test_module.py +0 -1
- autocoder/common/rulefiles/autocoderrules_utils.py +0 -484
- autocoder/common/text.py +0 -30
- autocoder/common/v2/agent/agentic_edit_tools/list_package_info_tool_resolver.py +0 -42
- autocoder/common/v2/agent/agentic_edit_tools/test_execute_command_tool_resolver.py +0 -70
- autocoder/common/v2/agent/agentic_edit_tools/test_search_files_tool_resolver.py +0 -163
- autocoder/common/v2/agent/agentic_tool_display.py +0 -183
- autocoder/plugins/dynamic_completion_example.py +0 -148
- autocoder/plugins/sample_plugin.py +0 -160
- autocoder/sdk/cli/__main__.py +0 -26
- autocoder/sdk/cli/completion_wrapper.py +0 -38
- autocoder/sdk/cli/install_completion.py +0 -301
- autocoder/sdk/models/messages.py +0 -209
- autocoder/sdk/session/__init__.py +0 -32
- autocoder/sdk/session/session.py +0 -106
- autocoder/sdk/session/session_manager.py +0 -56
- {auto_coder-1.0.0.dist-info → auto_coder-2.0.0.dist-info}/top_level.txt +0 -0
- /autocoder/{sdk/example.py → common/agent_query_queue/__init__.py} +0 -0
|
@@ -0,0 +1,703 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Tests for BackgroundProcessNotifier module.
|
|
3
|
+
|
|
4
|
+
This module provides comprehensive tests for the BackgroundProcessNotifier
|
|
5
|
+
class including process registration, monitoring, message handling,
|
|
6
|
+
and integration with the global process manager.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
import unittest
|
|
10
|
+
import pytest
|
|
11
|
+
import time
|
|
12
|
+
import threading
|
|
13
|
+
import tempfile
|
|
14
|
+
import os
|
|
15
|
+
import uuid
|
|
16
|
+
from unittest.mock import Mock, patch, MagicMock
|
|
17
|
+
from typing import Dict, Any
|
|
18
|
+
from collections import deque
|
|
19
|
+
|
|
20
|
+
from autocoder.common.shell_commands.background_process_notifier import (
|
|
21
|
+
BackgroundProcessNotifier,
|
|
22
|
+
BackgroundTaskRegistration,
|
|
23
|
+
AsyncTaskMessage,
|
|
24
|
+
get_background_process_notifier
|
|
25
|
+
)
|
|
26
|
+
from autocoder.common.shell_commands import (
|
|
27
|
+
execute_command_background,
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def _indent_tail(text: str) -> str:
|
|
32
|
+
"""Helper function to indent text output."""
|
|
33
|
+
return "\n".join([" " + line for line in text.splitlines()])
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
class TestBackgroundTaskRegistration(unittest.TestCase):
|
|
37
|
+
"""Test BackgroundTaskRegistration dataclass."""
|
|
38
|
+
|
|
39
|
+
def test_basic_creation(self):
|
|
40
|
+
"""Test basic registration creation."""
|
|
41
|
+
registration = BackgroundTaskRegistration(
|
|
42
|
+
conversation_id="conv_123",
|
|
43
|
+
pid=1234,
|
|
44
|
+
tool_name="execute_command",
|
|
45
|
+
command="echo test",
|
|
46
|
+
cwd="/tmp",
|
|
47
|
+
agent_name="test_agent",
|
|
48
|
+
task="Test task",
|
|
49
|
+
task_id="task_123",
|
|
50
|
+
start_time=time.time()
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
self.assertEqual(registration.conversation_id, "conv_123")
|
|
54
|
+
self.assertEqual(registration.pid, 1234)
|
|
55
|
+
self.assertEqual(registration.tool_name, "execute_command")
|
|
56
|
+
self.assertEqual(registration.command, "echo test")
|
|
57
|
+
self.assertEqual(registration.cwd, "/tmp")
|
|
58
|
+
self.assertEqual(registration.agent_name, "test_agent")
|
|
59
|
+
self.assertEqual(registration.task, "Test task")
|
|
60
|
+
self.assertEqual(registration.task_id, "task_123")
|
|
61
|
+
self.assertIsInstance(registration.start_time, float)
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
class TestAsyncTaskMessage(unittest.TestCase):
|
|
65
|
+
"""Test AsyncTaskMessage dataclass."""
|
|
66
|
+
|
|
67
|
+
def test_basic_creation(self):
|
|
68
|
+
"""Test basic message creation."""
|
|
69
|
+
message = AsyncTaskMessage(
|
|
70
|
+
conversation_id="conv_123",
|
|
71
|
+
task_id="task_123",
|
|
72
|
+
pid=1234,
|
|
73
|
+
tool_name="execute_command",
|
|
74
|
+
status="completed",
|
|
75
|
+
exit_code=0,
|
|
76
|
+
duration_sec=5.5,
|
|
77
|
+
command="echo test",
|
|
78
|
+
cwd="/tmp",
|
|
79
|
+
agent_name="test_agent",
|
|
80
|
+
task="Test task",
|
|
81
|
+
output_tail="Test output",
|
|
82
|
+
error_tail=None,
|
|
83
|
+
completed_at=time.time()
|
|
84
|
+
)
|
|
85
|
+
|
|
86
|
+
self.assertEqual(message.conversation_id, "conv_123")
|
|
87
|
+
self.assertEqual(message.task_id, "task_123")
|
|
88
|
+
self.assertEqual(message.pid, 1234)
|
|
89
|
+
self.assertEqual(message.tool_name, "execute_command")
|
|
90
|
+
self.assertEqual(message.status, "completed")
|
|
91
|
+
self.assertEqual(message.exit_code, 0)
|
|
92
|
+
self.assertEqual(message.duration_sec, 5.5)
|
|
93
|
+
self.assertEqual(message.command, "echo test")
|
|
94
|
+
self.assertEqual(message.cwd, "/tmp")
|
|
95
|
+
self.assertEqual(message.agent_name, "test_agent")
|
|
96
|
+
self.assertEqual(message.task, "Test task")
|
|
97
|
+
self.assertEqual(message.output_tail, "Test output")
|
|
98
|
+
self.assertIsNone(message.error_tail)
|
|
99
|
+
self.assertIsInstance(message.completed_at, float)
|
|
100
|
+
|
|
101
|
+
def test_failed_message(self):
|
|
102
|
+
"""Test message for failed task."""
|
|
103
|
+
message = AsyncTaskMessage(
|
|
104
|
+
conversation_id="conv_123",
|
|
105
|
+
task_id="task_123",
|
|
106
|
+
pid=1234,
|
|
107
|
+
tool_name="execute_command",
|
|
108
|
+
status="failed",
|
|
109
|
+
exit_code=1,
|
|
110
|
+
duration_sec=2.0,
|
|
111
|
+
command="false",
|
|
112
|
+
cwd="/tmp",
|
|
113
|
+
agent_name="test_agent",
|
|
114
|
+
task="Test task",
|
|
115
|
+
output_tail="",
|
|
116
|
+
error_tail="Command failed",
|
|
117
|
+
completed_at=time.time()
|
|
118
|
+
)
|
|
119
|
+
|
|
120
|
+
self.assertEqual(message.status, "failed")
|
|
121
|
+
self.assertEqual(message.exit_code, 1)
|
|
122
|
+
self.assertEqual(message.error_tail, "Command failed")
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
class TestBackgroundProcessNotifier(unittest.TestCase):
|
|
126
|
+
"""Test BackgroundProcessNotifier main functionality."""
|
|
127
|
+
|
|
128
|
+
def setUp(self):
|
|
129
|
+
"""Set up test environment."""
|
|
130
|
+
# Reset singleton instance for clean testing
|
|
131
|
+
BackgroundProcessNotifier._instance = None
|
|
132
|
+
self.notifier = BackgroundProcessNotifier()
|
|
133
|
+
|
|
134
|
+
def tearDown(self):
|
|
135
|
+
"""Clean up after tests."""
|
|
136
|
+
try:
|
|
137
|
+
self.notifier.stop()
|
|
138
|
+
except:
|
|
139
|
+
pass
|
|
140
|
+
# Reset singleton
|
|
141
|
+
BackgroundProcessNotifier._instance = None
|
|
142
|
+
|
|
143
|
+
def test_singleton_pattern(self):
|
|
144
|
+
"""Test that notifier follows singleton pattern."""
|
|
145
|
+
notifier1 = BackgroundProcessNotifier.get_instance()
|
|
146
|
+
notifier2 = BackgroundProcessNotifier.get_instance()
|
|
147
|
+
self.assertIs(notifier1, notifier2)
|
|
148
|
+
|
|
149
|
+
def test_initialization(self):
|
|
150
|
+
"""Test notifier initialization."""
|
|
151
|
+
self.assertIsInstance(self.notifier._registrations, dict)
|
|
152
|
+
self.assertIsInstance(self.notifier._pid_to_key, dict)
|
|
153
|
+
self.assertIsInstance(self.notifier._reported, set)
|
|
154
|
+
self.assertIsInstance(self.notifier._pending_messages, dict)
|
|
155
|
+
self.assertEqual(self.notifier._poll_interval_sec, 0.5)
|
|
156
|
+
self.assertEqual(self.notifier._max_output_bytes, 16 * 1024)
|
|
157
|
+
self.assertEqual(self.notifier._max_output_lines, 200)
|
|
158
|
+
|
|
159
|
+
def test_set_options(self):
|
|
160
|
+
"""Test setting notifier options."""
|
|
161
|
+
self.notifier.set_options(
|
|
162
|
+
poll_interval_sec=1.0,
|
|
163
|
+
max_output_bytes=32 * 1024,
|
|
164
|
+
max_output_lines=500
|
|
165
|
+
)
|
|
166
|
+
|
|
167
|
+
self.assertEqual(self.notifier._poll_interval_sec, 1.0)
|
|
168
|
+
self.assertEqual(self.notifier._max_output_bytes, 32 * 1024)
|
|
169
|
+
self.assertEqual(self.notifier._max_output_lines, 500)
|
|
170
|
+
|
|
171
|
+
def test_set_options_invalid_values(self):
|
|
172
|
+
"""Test setting invalid options (should be ignored)."""
|
|
173
|
+
original_interval = self.notifier._poll_interval_sec
|
|
174
|
+
original_bytes = self.notifier._max_output_bytes
|
|
175
|
+
original_lines = self.notifier._max_output_lines
|
|
176
|
+
|
|
177
|
+
# Invalid values should be ignored
|
|
178
|
+
self.notifier.set_options(
|
|
179
|
+
poll_interval_sec=-1.0,
|
|
180
|
+
max_output_bytes=-1,
|
|
181
|
+
max_output_lines=-1
|
|
182
|
+
)
|
|
183
|
+
|
|
184
|
+
# Values should remain unchanged
|
|
185
|
+
self.assertEqual(self.notifier._poll_interval_sec, original_interval)
|
|
186
|
+
self.assertEqual(self.notifier._max_output_bytes, original_bytes)
|
|
187
|
+
self.assertEqual(self.notifier._max_output_lines, original_lines)
|
|
188
|
+
|
|
189
|
+
@patch('autocoder.common.shell_commands.background_process_notifier.get_background_process_info')
|
|
190
|
+
def test_register_process(self, mock_get_info):
|
|
191
|
+
"""Test process registration."""
|
|
192
|
+
# Mock process info
|
|
193
|
+
mock_get_info.return_value = {"start_time": time.time()}
|
|
194
|
+
|
|
195
|
+
task_id = self.notifier.register_process(
|
|
196
|
+
conversation_id="conv_123",
|
|
197
|
+
pid=1234,
|
|
198
|
+
tool_name="execute_command",
|
|
199
|
+
command="echo test",
|
|
200
|
+
cwd="/tmp",
|
|
201
|
+
agent_name="test_agent",
|
|
202
|
+
task="Test task"
|
|
203
|
+
)
|
|
204
|
+
|
|
205
|
+
# Verify task_id is returned
|
|
206
|
+
self.assertIsInstance(task_id, str)
|
|
207
|
+
self.assertTrue(len(task_id) > 0)
|
|
208
|
+
|
|
209
|
+
# Verify registration is stored
|
|
210
|
+
with self.notifier._lock:
|
|
211
|
+
self.assertTrue(len(self.notifier._registrations) > 0)
|
|
212
|
+
self.assertIn(1234, self.notifier._pid_to_key)
|
|
213
|
+
|
|
214
|
+
# Verify registration content
|
|
215
|
+
key = self.notifier._pid_to_key[1234]
|
|
216
|
+
registration = self.notifier._registrations[key]
|
|
217
|
+
self.assertEqual(registration.conversation_id, "conv_123")
|
|
218
|
+
self.assertEqual(registration.pid, 1234)
|
|
219
|
+
self.assertEqual(registration.tool_name, "execute_command")
|
|
220
|
+
self.assertEqual(registration.command, "echo test")
|
|
221
|
+
self.assertEqual(registration.task_id, task_id)
|
|
222
|
+
|
|
223
|
+
def test_poll_messages_empty(self):
|
|
224
|
+
"""Test polling messages from empty conversation."""
|
|
225
|
+
messages = self.notifier.poll_messages("nonexistent_conv")
|
|
226
|
+
self.assertEqual(messages, [])
|
|
227
|
+
|
|
228
|
+
def test_poll_messages_with_content(self):
|
|
229
|
+
"""Test polling messages with content."""
|
|
230
|
+
# Add some test messages
|
|
231
|
+
test_message = AsyncTaskMessage(
|
|
232
|
+
conversation_id="conv_123",
|
|
233
|
+
task_id="task_123",
|
|
234
|
+
pid=1234,
|
|
235
|
+
tool_name="execute_command",
|
|
236
|
+
status="completed",
|
|
237
|
+
exit_code=0,
|
|
238
|
+
duration_sec=1.0,
|
|
239
|
+
command="echo test",
|
|
240
|
+
cwd="/tmp",
|
|
241
|
+
agent_name="test_agent",
|
|
242
|
+
task="Test task",
|
|
243
|
+
output_tail="test output",
|
|
244
|
+
error_tail=None,
|
|
245
|
+
completed_at=time.time()
|
|
246
|
+
)
|
|
247
|
+
|
|
248
|
+
with self.notifier._lock:
|
|
249
|
+
self.notifier._pending_messages["conv_123"].append(test_message)
|
|
250
|
+
|
|
251
|
+
# Poll messages
|
|
252
|
+
messages = self.notifier.poll_messages("conv_123")
|
|
253
|
+
|
|
254
|
+
# Verify message is returned and removed from queue
|
|
255
|
+
self.assertEqual(len(messages), 1)
|
|
256
|
+
self.assertEqual(messages[0].task_id, "task_123")
|
|
257
|
+
|
|
258
|
+
# Verify queue is now empty
|
|
259
|
+
with self.notifier._lock:
|
|
260
|
+
self.assertNotIn("conv_123", self.notifier._pending_messages)
|
|
261
|
+
|
|
262
|
+
def test_poll_messages_with_limit(self):
|
|
263
|
+
"""Test polling messages with max_items limit."""
|
|
264
|
+
# Add multiple test messages
|
|
265
|
+
for i in range(5):
|
|
266
|
+
test_message = AsyncTaskMessage(
|
|
267
|
+
conversation_id="conv_123",
|
|
268
|
+
task_id=f"task_{i}",
|
|
269
|
+
pid=1234 + i,
|
|
270
|
+
tool_name="execute_command",
|
|
271
|
+
status="completed",
|
|
272
|
+
exit_code=0,
|
|
273
|
+
duration_sec=1.0,
|
|
274
|
+
command=f"echo test{i}",
|
|
275
|
+
cwd="/tmp",
|
|
276
|
+
agent_name="test_agent",
|
|
277
|
+
task=f"Test task {i}",
|
|
278
|
+
output_tail=f"test output {i}",
|
|
279
|
+
error_tail=None,
|
|
280
|
+
completed_at=time.time()
|
|
281
|
+
)
|
|
282
|
+
|
|
283
|
+
with self.notifier._lock:
|
|
284
|
+
self.notifier._pending_messages["conv_123"].append(test_message)
|
|
285
|
+
|
|
286
|
+
# Poll with limit
|
|
287
|
+
messages = self.notifier.poll_messages("conv_123", max_items=3)
|
|
288
|
+
|
|
289
|
+
# Verify only 3 messages returned
|
|
290
|
+
self.assertEqual(len(messages), 3)
|
|
291
|
+
|
|
292
|
+
# Verify remaining messages still in queue
|
|
293
|
+
with self.notifier._lock:
|
|
294
|
+
remaining = len(self.notifier._pending_messages["conv_123"])
|
|
295
|
+
self.assertEqual(remaining, 2)
|
|
296
|
+
|
|
297
|
+
def test_has_messages(self):
|
|
298
|
+
"""Test checking for pending messages."""
|
|
299
|
+
# Initially no messages
|
|
300
|
+
self.assertFalse(self.notifier.has_messages("conv_123"))
|
|
301
|
+
|
|
302
|
+
# Add a message
|
|
303
|
+
test_message = AsyncTaskMessage(
|
|
304
|
+
conversation_id="conv_123",
|
|
305
|
+
task_id="task_123",
|
|
306
|
+
pid=1234,
|
|
307
|
+
tool_name="execute_command",
|
|
308
|
+
status="completed",
|
|
309
|
+
exit_code=0,
|
|
310
|
+
duration_sec=1.0,
|
|
311
|
+
command="echo test",
|
|
312
|
+
cwd="/tmp",
|
|
313
|
+
agent_name="test_agent",
|
|
314
|
+
task="Test task",
|
|
315
|
+
output_tail="test output",
|
|
316
|
+
error_tail=None,
|
|
317
|
+
completed_at=time.time()
|
|
318
|
+
)
|
|
319
|
+
|
|
320
|
+
with self.notifier._lock:
|
|
321
|
+
self.notifier._pending_messages["conv_123"].append(test_message)
|
|
322
|
+
|
|
323
|
+
# Now should have messages
|
|
324
|
+
self.assertTrue(self.notifier.has_messages("conv_123"))
|
|
325
|
+
|
|
326
|
+
# Poll messages to empty queue
|
|
327
|
+
self.notifier.poll_messages("conv_123")
|
|
328
|
+
|
|
329
|
+
# Should no longer have messages
|
|
330
|
+
self.assertFalse(self.notifier.has_messages("conv_123"))
|
|
331
|
+
|
|
332
|
+
def test_stop(self):
|
|
333
|
+
"""Test stopping the notifier."""
|
|
334
|
+
# Notifier should be running initially
|
|
335
|
+
self.assertTrue(self.notifier._monitor_thread.is_alive())
|
|
336
|
+
|
|
337
|
+
# Stop notifier
|
|
338
|
+
self.notifier.stop()
|
|
339
|
+
|
|
340
|
+
# Give some time for thread to stop
|
|
341
|
+
time.sleep(0.1)
|
|
342
|
+
|
|
343
|
+
# Monitor thread should be stopped
|
|
344
|
+
self.assertFalse(self.notifier._monitor_thread.is_alive())
|
|
345
|
+
|
|
346
|
+
|
|
347
|
+
class TestBackgroundProcessNotifierFileOperations(unittest.TestCase):
|
|
348
|
+
"""Test file operations for background process notifier."""
|
|
349
|
+
|
|
350
|
+
def setUp(self):
|
|
351
|
+
"""Set up test environment with temp directory."""
|
|
352
|
+
BackgroundProcessNotifier._instance = None
|
|
353
|
+
self.notifier = BackgroundProcessNotifier()
|
|
354
|
+
self.temp_dir = tempfile.mkdtemp()
|
|
355
|
+
|
|
356
|
+
def tearDown(self):
|
|
357
|
+
"""Clean up test environment."""
|
|
358
|
+
try:
|
|
359
|
+
self.notifier.stop()
|
|
360
|
+
except:
|
|
361
|
+
pass
|
|
362
|
+
BackgroundProcessNotifier._instance = None
|
|
363
|
+
|
|
364
|
+
# Clean up temp directory
|
|
365
|
+
import shutil
|
|
366
|
+
try:
|
|
367
|
+
shutil.rmtree(self.temp_dir)
|
|
368
|
+
except:
|
|
369
|
+
pass
|
|
370
|
+
|
|
371
|
+
def test_read_file_tail_nonexistent(self):
|
|
372
|
+
"""Test reading tail of non-existent file."""
|
|
373
|
+
result = self.notifier._read_file_tail("/nonexistent/file.txt")
|
|
374
|
+
self.assertIsNone(result)
|
|
375
|
+
|
|
376
|
+
def test_read_file_tail_empty(self):
|
|
377
|
+
"""Test reading tail of empty file."""
|
|
378
|
+
empty_file = os.path.join(self.temp_dir, "empty.txt")
|
|
379
|
+
with open(empty_file, 'w') as f:
|
|
380
|
+
pass # Create empty file
|
|
381
|
+
|
|
382
|
+
result = self.notifier._read_file_tail(empty_file)
|
|
383
|
+
self.assertEqual(result, "")
|
|
384
|
+
|
|
385
|
+
def test_read_file_tail_small_file(self):
|
|
386
|
+
"""Test reading tail of small file."""
|
|
387
|
+
small_file = os.path.join(self.temp_dir, "small.txt")
|
|
388
|
+
content = "Line 1\nLine 2\nLine 3\n"
|
|
389
|
+
|
|
390
|
+
with open(small_file, 'w') as f:
|
|
391
|
+
f.write(content)
|
|
392
|
+
|
|
393
|
+
result = self.notifier._read_file_tail(small_file)
|
|
394
|
+
self.assertEqual(result, content.strip())
|
|
395
|
+
|
|
396
|
+
def test_read_file_tail_large_file(self):
|
|
397
|
+
"""Test reading tail of large file with size limit."""
|
|
398
|
+
large_file = os.path.join(self.temp_dir, "large.txt")
|
|
399
|
+
|
|
400
|
+
# Create file larger than max_output_bytes
|
|
401
|
+
lines = [f"Line {i}\n" for i in range(1000)]
|
|
402
|
+
content = "".join(lines)
|
|
403
|
+
|
|
404
|
+
with open(large_file, 'w') as f:
|
|
405
|
+
f.write(content)
|
|
406
|
+
|
|
407
|
+
# Set small limit for testing
|
|
408
|
+
self.notifier._max_output_bytes = 100
|
|
409
|
+
self.notifier._max_output_lines = 10
|
|
410
|
+
|
|
411
|
+
result = self.notifier._read_file_tail(large_file)
|
|
412
|
+
|
|
413
|
+
# Should be truncated
|
|
414
|
+
self.assertLess(len(result), len(content))
|
|
415
|
+
self.assertLessEqual(len(result.split('\n')), 10)
|
|
416
|
+
|
|
417
|
+
def test_tail_text_line_limit(self):
|
|
418
|
+
"""Test text tailing with line limit."""
|
|
419
|
+
text = "\n".join([f"Line {i}" for i in range(50)])
|
|
420
|
+
|
|
421
|
+
# Set line limit
|
|
422
|
+
self.notifier._max_output_lines = 10
|
|
423
|
+
|
|
424
|
+
result = self.notifier._tail_text(text)
|
|
425
|
+
lines = result.split('\n')
|
|
426
|
+
|
|
427
|
+
self.assertLessEqual(len(lines), 10)
|
|
428
|
+
self.assertTrue(result.endswith("Line 49")) # Should end with last line
|
|
429
|
+
|
|
430
|
+
def test_tail_text_byte_limit(self):
|
|
431
|
+
"""Test text tailing with byte limit."""
|
|
432
|
+
text = "A" * 1000 # 1000 character string
|
|
433
|
+
|
|
434
|
+
# Set small byte limit
|
|
435
|
+
self.notifier._max_output_bytes = 100
|
|
436
|
+
|
|
437
|
+
result = self.notifier._tail_text(text)
|
|
438
|
+
|
|
439
|
+
# Should be truncated to approximately byte limit
|
|
440
|
+
self.assertLessEqual(len(result.encode('utf-8')), self.notifier._max_output_bytes + 10) # Small tolerance
|
|
441
|
+
|
|
442
|
+
def test_read_process_output_tails_success(self):
|
|
443
|
+
"""Test reading process output tails successfully."""
|
|
444
|
+
# Create test output files
|
|
445
|
+
pid = 12345
|
|
446
|
+
process_uniq_id = "test_proc_123"
|
|
447
|
+
backgrounds_dir = os.path.join(self.temp_dir, '.auto-coder', 'backgrounds')
|
|
448
|
+
os.makedirs(backgrounds_dir, exist_ok=True)
|
|
449
|
+
|
|
450
|
+
stdout_file = os.path.join(backgrounds_dir, f"{process_uniq_id}.out")
|
|
451
|
+
stderr_file = os.path.join(backgrounds_dir, f"{process_uniq_id}.err")
|
|
452
|
+
|
|
453
|
+
# Create test files
|
|
454
|
+
with open(stdout_file, 'w') as f:
|
|
455
|
+
f.write("Standard output content")
|
|
456
|
+
with open(stderr_file, 'w') as f:
|
|
457
|
+
f.write("Standard error content")
|
|
458
|
+
|
|
459
|
+
# Test reading
|
|
460
|
+
info = {
|
|
461
|
+
"pid": pid,
|
|
462
|
+
"process_uniq_id": process_uniq_id,
|
|
463
|
+
"cwd": self.temp_dir
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
stdout_tail, stderr_tail = self.notifier._read_process_output_tails(info)
|
|
467
|
+
|
|
468
|
+
self.assertEqual(stdout_tail, "Standard output content")
|
|
469
|
+
self.assertEqual(stderr_tail, "Standard error content")
|
|
470
|
+
|
|
471
|
+
def test_read_process_output_tails_missing_process_uniq_id(self):
|
|
472
|
+
"""Test reading process output tails without process_uniq_id."""
|
|
473
|
+
info = {
|
|
474
|
+
"pid": 12345,
|
|
475
|
+
"cwd": self.temp_dir
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
stdout_tail, stderr_tail = self.notifier._read_process_output_tails(info)
|
|
479
|
+
|
|
480
|
+
# Should return None for both since process_uniq_id is required
|
|
481
|
+
self.assertIsNone(stdout_tail)
|
|
482
|
+
self.assertIsNone(stderr_tail)
|
|
483
|
+
|
|
484
|
+
|
|
485
|
+
class TestBackgroundProcessNotifierIntegration(unittest.TestCase):
|
|
486
|
+
"""Integration tests for BackgroundProcessNotifier."""
|
|
487
|
+
|
|
488
|
+
def setUp(self):
|
|
489
|
+
"""Set up integration test environment."""
|
|
490
|
+
BackgroundProcessNotifier._instance = None
|
|
491
|
+
self.notifier = BackgroundProcessNotifier()
|
|
492
|
+
|
|
493
|
+
def tearDown(self):
|
|
494
|
+
"""Clean up integration tests."""
|
|
495
|
+
try:
|
|
496
|
+
self.notifier.stop()
|
|
497
|
+
except:
|
|
498
|
+
pass
|
|
499
|
+
BackgroundProcessNotifier._instance = None
|
|
500
|
+
|
|
501
|
+
@patch('autocoder.common.shell_commands.background_process_notifier.get_background_processes')
|
|
502
|
+
@patch('autocoder.common.shell_commands.background_process_notifier.get_background_process_info')
|
|
503
|
+
def test_scan_background_processes_completion(self, mock_get_info, mock_get_processes):
|
|
504
|
+
"""Test scanning for process completion."""
|
|
505
|
+
# Setup mocks
|
|
506
|
+
start_time = time.time()
|
|
507
|
+
end_time = start_time + 5.0
|
|
508
|
+
|
|
509
|
+
mock_get_processes.return_value = {
|
|
510
|
+
1234: {
|
|
511
|
+
"pid": 1234,
|
|
512
|
+
"process_uniq_id": "test_proc_123",
|
|
513
|
+
"status": "completed",
|
|
514
|
+
"exit_code": 0,
|
|
515
|
+
"start_time": start_time,
|
|
516
|
+
"end_time": end_time,
|
|
517
|
+
"cwd": "/tmp"
|
|
518
|
+
}
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
mock_get_info.return_value = {
|
|
522
|
+
"start_time": start_time
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
# Register a process
|
|
526
|
+
task_id = self.notifier.register_process(
|
|
527
|
+
conversation_id="conv_123",
|
|
528
|
+
pid=1234,
|
|
529
|
+
tool_name="execute_command",
|
|
530
|
+
command="echo test",
|
|
531
|
+
cwd="/tmp"
|
|
532
|
+
)
|
|
533
|
+
|
|
534
|
+
# Manually trigger scan
|
|
535
|
+
self.notifier._scan_background_processes()
|
|
536
|
+
|
|
537
|
+
# Check for completion message
|
|
538
|
+
messages = self.notifier.poll_messages("conv_123")
|
|
539
|
+
|
|
540
|
+
self.assertEqual(len(messages), 1)
|
|
541
|
+
message = messages[0]
|
|
542
|
+
self.assertEqual(message.task_id, task_id)
|
|
543
|
+
self.assertEqual(message.status, "completed")
|
|
544
|
+
self.assertEqual(message.exit_code, 0)
|
|
545
|
+
self.assertEqual(message.pid, 1234)
|
|
546
|
+
|
|
547
|
+
@patch('autocoder.common.shell_commands.background_process_notifier.get_background_processes')
|
|
548
|
+
@patch('autocoder.common.shell_commands.background_process_notifier.get_background_process_info')
|
|
549
|
+
def test_scan_background_processes_failure(self, mock_get_info, mock_get_processes):
|
|
550
|
+
"""Test scanning for process failure."""
|
|
551
|
+
# Setup mocks for failed process
|
|
552
|
+
start_time = time.time()
|
|
553
|
+
end_time = start_time + 2.0
|
|
554
|
+
|
|
555
|
+
mock_get_processes.return_value = {
|
|
556
|
+
1234: {
|
|
557
|
+
"pid": 1234,
|
|
558
|
+
"process_uniq_id": "test_proc_123",
|
|
559
|
+
"status": "completed",
|
|
560
|
+
"exit_code": 1, # Failed
|
|
561
|
+
"start_time": start_time,
|
|
562
|
+
"end_time": end_time,
|
|
563
|
+
"cwd": "/tmp"
|
|
564
|
+
}
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
mock_get_info.return_value = {
|
|
568
|
+
"start_time": start_time
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
# Register a process
|
|
572
|
+
task_id = self.notifier.register_process(
|
|
573
|
+
conversation_id="conv_123",
|
|
574
|
+
pid=1234,
|
|
575
|
+
tool_name="execute_command",
|
|
576
|
+
command="false", # Command that fails
|
|
577
|
+
cwd="/tmp"
|
|
578
|
+
)
|
|
579
|
+
|
|
580
|
+
# Manually trigger scan
|
|
581
|
+
self.notifier._scan_background_processes()
|
|
582
|
+
|
|
583
|
+
# Check for failure message
|
|
584
|
+
messages = self.notifier.poll_messages("conv_123")
|
|
585
|
+
|
|
586
|
+
self.assertEqual(len(messages), 1)
|
|
587
|
+
message = messages[0]
|
|
588
|
+
self.assertEqual(message.task_id, task_id)
|
|
589
|
+
self.assertEqual(message.status, "failed")
|
|
590
|
+
self.assertEqual(message.exit_code, 1)
|
|
591
|
+
|
|
592
|
+
def test_get_process_start_time_with_info(self):
|
|
593
|
+
"""Test getting process start time when info is available."""
|
|
594
|
+
with patch('autocoder.common.shell_commands.background_process_notifier.get_background_process_info') as mock_get_info:
|
|
595
|
+
mock_get_info.return_value = {"start_time": 123456.789}
|
|
596
|
+
|
|
597
|
+
start_time = self.notifier._get_process_start_time(1234)
|
|
598
|
+
self.assertEqual(start_time, 123456.789)
|
|
599
|
+
|
|
600
|
+
def test_get_process_start_time_without_info(self):
|
|
601
|
+
"""Test getting process start time when info is not available."""
|
|
602
|
+
with patch('autocoder.common.shell_commands.background_process_notifier.get_background_process_info') as mock_get_info:
|
|
603
|
+
mock_get_info.return_value = None
|
|
604
|
+
|
|
605
|
+
before_time = time.time()
|
|
606
|
+
start_time = self.notifier._get_process_start_time(1234)
|
|
607
|
+
after_time = time.time()
|
|
608
|
+
|
|
609
|
+
# Should return current time
|
|
610
|
+
self.assertGreaterEqual(start_time, before_time)
|
|
611
|
+
self.assertLessEqual(start_time, after_time)
|
|
612
|
+
|
|
613
|
+
|
|
614
|
+
class TestGetBackgroundProcessNotifier(unittest.TestCase):
|
|
615
|
+
"""Test the get_background_process_notifier convenience function."""
|
|
616
|
+
|
|
617
|
+
def setUp(self):
|
|
618
|
+
"""Reset singleton for testing."""
|
|
619
|
+
BackgroundProcessNotifier._instance = None
|
|
620
|
+
|
|
621
|
+
def tearDown(self):
|
|
622
|
+
"""Clean up singleton."""
|
|
623
|
+
if BackgroundProcessNotifier._instance:
|
|
624
|
+
try:
|
|
625
|
+
BackgroundProcessNotifier._instance.stop()
|
|
626
|
+
except:
|
|
627
|
+
pass
|
|
628
|
+
BackgroundProcessNotifier._instance = None
|
|
629
|
+
|
|
630
|
+
def test_get_background_process_notifier(self):
|
|
631
|
+
"""Test that function returns singleton instance."""
|
|
632
|
+
notifier1 = get_background_process_notifier()
|
|
633
|
+
notifier2 = get_background_process_notifier()
|
|
634
|
+
|
|
635
|
+
self.assertIs(notifier1, notifier2)
|
|
636
|
+
self.assertIsInstance(notifier1, BackgroundProcessNotifier)
|
|
637
|
+
|
|
638
|
+
|
|
639
|
+
def test_background_process_notifier_basic():
|
|
640
|
+
"""
|
|
641
|
+
Start a short-lived background command, register it to BackgroundProcessNotifier,
|
|
642
|
+
poll for completion, print a summary (similar to AgenticEdit) and assert results.
|
|
643
|
+
"""
|
|
644
|
+
# Unique conversation id to scope messages
|
|
645
|
+
conv_id = f"test-conv-{uuid.uuid4()}"
|
|
646
|
+
|
|
647
|
+
# Short-lived command that prints output and exits with code 0
|
|
648
|
+
# Use Python to ensure cross-platform behavior
|
|
649
|
+
command = 'python -c "import time,sys; print(\\"start\\"); sys.stdout.flush(); time.sleep(0.6); print(\\"end\\")"'
|
|
650
|
+
|
|
651
|
+
# Start background process
|
|
652
|
+
info = execute_command_background(command=command, verbose=True)
|
|
653
|
+
|
|
654
|
+
# Register with notifier
|
|
655
|
+
notifier = get_background_process_notifier()
|
|
656
|
+
notifier.set_options(poll_interval_sec=0.1, max_output_bytes=4096, max_output_lines=50)
|
|
657
|
+
notifier.register_process(
|
|
658
|
+
conversation_id=conv_id,
|
|
659
|
+
pid=info["pid"],
|
|
660
|
+
tool_name="ExecuteCommandTool",
|
|
661
|
+
command=info["command"],
|
|
662
|
+
cwd=info.get("working_directory"),
|
|
663
|
+
)
|
|
664
|
+
|
|
665
|
+
# Poll for completion with timeout
|
|
666
|
+
deadline = time.time() + 10.0
|
|
667
|
+
while time.time() < deadline and not notifier.has_messages(conv_id):
|
|
668
|
+
time.sleep(0.1)
|
|
669
|
+
|
|
670
|
+
msgs = notifier.poll_messages(conv_id, max_items=16)
|
|
671
|
+
|
|
672
|
+
# Print summary similar to AgenticEdit injection
|
|
673
|
+
print(f"后台任务完成通知({len(msgs)} 项):")
|
|
674
|
+
for m in msgs:
|
|
675
|
+
meta = []
|
|
676
|
+
if m.exit_code is not None:
|
|
677
|
+
meta.append(f"exit_code={m.exit_code}")
|
|
678
|
+
if m.duration_sec is not None:
|
|
679
|
+
meta.append(f"duration={m.duration_sec:.2f}s")
|
|
680
|
+
meta_str = ", ".join(meta)
|
|
681
|
+
print(f"- [{m.tool_name}] pid={m.pid} status={m.status} {('('+meta_str+')') if meta_str else ''}")
|
|
682
|
+
if m.agent_name or m.task:
|
|
683
|
+
detail = []
|
|
684
|
+
if m.agent_name:
|
|
685
|
+
detail.append(f"agent={m.agent_name}")
|
|
686
|
+
if m.task:
|
|
687
|
+
detail.append(f"task={m.task}")
|
|
688
|
+
print(" " + ", ".join(detail))
|
|
689
|
+
if m.output_tail:
|
|
690
|
+
print(" output_tail:\n" + _indent_tail(m.output_tail))
|
|
691
|
+
if m.error_tail:
|
|
692
|
+
print(" error_tail:\n" + _indent_tail(m.error_tail))
|
|
693
|
+
|
|
694
|
+
# Basic assertions
|
|
695
|
+
assert len(msgs) >= 1, "expected at least one completion message"
|
|
696
|
+
m0 = msgs[0]
|
|
697
|
+
assert m0.conversation_id == conv_id
|
|
698
|
+
assert m0.pid == info["pid"]
|
|
699
|
+
assert m0.status in ("completed", "failed")
|
|
700
|
+
|
|
701
|
+
|
|
702
|
+
if __name__ == "__main__":
|
|
703
|
+
unittest.main()
|