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.

Files changed (574) hide show
  1. auto_coder-2.0.0.dist-info/LICENSE +158 -0
  2. auto_coder-2.0.0.dist-info/METADATA +558 -0
  3. auto_coder-2.0.0.dist-info/RECORD +795 -0
  4. {auto_coder-1.0.0.dist-info → auto_coder-2.0.0.dist-info}/WHEEL +1 -1
  5. {auto_coder-1.0.0.dist-info → auto_coder-2.0.0.dist-info}/entry_points.txt +3 -3
  6. autocoder/__init__.py +31 -0
  7. autocoder/agent/auto_filegroup.py +32 -13
  8. autocoder/agent/auto_learn_from_commit.py +9 -1
  9. autocoder/agent/base_agentic/__init__.py +3 -0
  10. autocoder/agent/base_agentic/agent_hub.py +1 -1
  11. autocoder/agent/base_agentic/base_agent.py +235 -136
  12. autocoder/agent/base_agentic/default_tools.py +119 -118
  13. autocoder/agent/base_agentic/test_base_agent.py +1 -1
  14. autocoder/agent/base_agentic/tool_registry.py +32 -20
  15. autocoder/agent/base_agentic/tools/read_file_tool_resolver.py +24 -3
  16. autocoder/agent/base_agentic/tools/write_to_file_tool_resolver.py +24 -11
  17. autocoder/agent/base_agentic/types.py +42 -0
  18. autocoder/agent/entry_command_agent/chat.py +73 -59
  19. autocoder/auto_coder.py +31 -40
  20. autocoder/auto_coder_rag.py +11 -1084
  21. autocoder/auto_coder_runner.py +970 -2345
  22. autocoder/auto_coder_terminal.py +26 -0
  23. autocoder/auto_coder_terminal_v3.py +190 -0
  24. autocoder/chat/conf_command.py +224 -124
  25. autocoder/chat/models_command.py +361 -299
  26. autocoder/chat/rules_command.py +79 -31
  27. autocoder/chat_auto_coder.py +988 -398
  28. autocoder/chat_auto_coder_lang.py +23 -732
  29. autocoder/commands/auto_command.py +25 -8
  30. autocoder/commands/auto_web.py +1 -1
  31. autocoder/commands/tools.py +44 -44
  32. autocoder/common/__init__.py +150 -128
  33. autocoder/common/ac_style_command_parser/__init__.py +39 -2
  34. autocoder/common/ac_style_command_parser/config.py +422 -0
  35. autocoder/common/ac_style_command_parser/parser.py +292 -78
  36. autocoder/common/ac_style_command_parser/test_parser.py +241 -16
  37. autocoder/common/ac_style_command_parser/test_typed_parser.py +342 -0
  38. autocoder/common/ac_style_command_parser/typed_parser.py +653 -0
  39. autocoder/common/action_yml_file_manager.py +25 -13
  40. autocoder/common/agent_events/__init__.py +52 -0
  41. autocoder/common/agent_events/agent_event_emitter.py +193 -0
  42. autocoder/common/agent_events/event_factory.py +177 -0
  43. autocoder/common/agent_events/examples.py +307 -0
  44. autocoder/common/agent_events/types.py +113 -0
  45. autocoder/common/agent_events/utils.py +68 -0
  46. autocoder/common/agent_hooks/__init__.py +44 -0
  47. autocoder/common/agent_hooks/examples.py +582 -0
  48. autocoder/common/agent_hooks/hook_executor.py +217 -0
  49. autocoder/common/agent_hooks/hook_manager.py +288 -0
  50. autocoder/common/agent_hooks/types.py +133 -0
  51. autocoder/common/agent_hooks/utils.py +99 -0
  52. autocoder/common/agent_query_queue/queue_executor.py +324 -0
  53. autocoder/common/agent_query_queue/queue_manager.py +325 -0
  54. autocoder/common/agents/__init__.py +11 -0
  55. autocoder/common/agents/agent_manager.py +323 -0
  56. autocoder/common/agents/agent_parser.py +189 -0
  57. autocoder/common/agents/example_usage.py +344 -0
  58. autocoder/common/agents/integration_example.py +330 -0
  59. autocoder/common/agents/test_agent_parser.py +545 -0
  60. autocoder/common/async_utils.py +101 -0
  61. autocoder/common/auto_coder_lang.py +23 -972
  62. autocoder/common/autocoderargs_parser/__init__.py +14 -0
  63. autocoder/common/autocoderargs_parser/parser.py +184 -0
  64. autocoder/common/autocoderargs_parser/tests/__init__.py +1 -0
  65. autocoder/common/autocoderargs_parser/tests/test_args_parser.py +235 -0
  66. autocoder/common/autocoderargs_parser/tests/test_token_parser.py +195 -0
  67. autocoder/common/autocoderargs_parser/token_parser.py +290 -0
  68. autocoder/common/buildin_tokenizer.py +2 -4
  69. autocoder/common/code_auto_generate.py +149 -74
  70. autocoder/common/code_auto_generate_diff.py +163 -70
  71. autocoder/common/code_auto_generate_editblock.py +179 -89
  72. autocoder/common/code_auto_generate_strict_diff.py +167 -72
  73. autocoder/common/code_auto_merge_editblock.py +13 -6
  74. autocoder/common/code_modification_ranker.py +1 -1
  75. autocoder/common/command_completer.py +3 -3
  76. autocoder/common/command_file_manager/manager.py +183 -47
  77. autocoder/common/command_file_manager/test_command_file_manager.py +507 -0
  78. autocoder/common/command_templates.py +1 -1
  79. autocoder/common/conf_utils.py +2 -4
  80. autocoder/common/conversations/config.py +11 -3
  81. autocoder/common/conversations/get_conversation_manager.py +100 -2
  82. autocoder/common/conversations/llm_stats_models.py +264 -0
  83. autocoder/common/conversations/manager.py +112 -28
  84. autocoder/common/conversations/models.py +16 -2
  85. autocoder/common/conversations/storage/index_manager.py +134 -10
  86. autocoder/common/core_config/__init__.py +63 -0
  87. autocoder/common/core_config/agentic_mode_manager.py +109 -0
  88. autocoder/common/core_config/base_manager.py +123 -0
  89. autocoder/common/core_config/compatibility.py +151 -0
  90. autocoder/common/core_config/config_manager.py +156 -0
  91. autocoder/common/core_config/conversation_manager.py +31 -0
  92. autocoder/common/core_config/exclude_manager.py +72 -0
  93. autocoder/common/core_config/file_manager.py +177 -0
  94. autocoder/common/core_config/human_as_model_manager.py +129 -0
  95. autocoder/common/core_config/lib_manager.py +54 -0
  96. autocoder/common/core_config/main_manager.py +81 -0
  97. autocoder/common/core_config/mode_manager.py +126 -0
  98. autocoder/common/core_config/models.py +70 -0
  99. autocoder/common/core_config/test_memory_manager.py +1056 -0
  100. autocoder/common/env_manager.py +282 -0
  101. autocoder/common/env_manager_usage_example.py +211 -0
  102. autocoder/common/file_checkpoint/conversation_checkpoint.py +19 -19
  103. autocoder/common/file_checkpoint/manager.py +264 -48
  104. autocoder/common/file_checkpoint/test_backup.py +1 -18
  105. autocoder/common/file_checkpoint/test_manager.py +270 -1
  106. autocoder/common/file_checkpoint/test_store.py +1 -17
  107. autocoder/common/file_handler/__init__.py +23 -0
  108. autocoder/common/file_handler/active_context_handler.py +159 -0
  109. autocoder/common/file_handler/add_files_handler.py +409 -0
  110. autocoder/common/file_handler/chat_handler.py +180 -0
  111. autocoder/common/file_handler/coding_handler.py +401 -0
  112. autocoder/common/file_handler/commit_handler.py +200 -0
  113. autocoder/common/file_handler/lib_handler.py +156 -0
  114. autocoder/common/file_handler/list_files_handler.py +111 -0
  115. autocoder/common/file_handler/mcp_handler.py +268 -0
  116. autocoder/common/file_handler/models_handler.py +493 -0
  117. autocoder/common/file_handler/remove_files_handler.py +172 -0
  118. autocoder/common/git_utils.py +44 -8
  119. autocoder/common/global_cancel.py +15 -6
  120. autocoder/common/ignorefiles/test_ignore_file_utils.py +1 -1
  121. autocoder/common/international/__init__.py +31 -0
  122. autocoder/common/international/demo_international.py +92 -0
  123. autocoder/common/international/message_manager.py +157 -0
  124. autocoder/common/international/messages/__init__.py +56 -0
  125. autocoder/common/international/messages/async_command_messages.py +507 -0
  126. autocoder/common/international/messages/auto_coder_messages.py +2208 -0
  127. autocoder/common/international/messages/chat_auto_coder_messages.py +1547 -0
  128. autocoder/common/international/messages/command_help_messages.py +986 -0
  129. autocoder/common/international/messages/conversation_command_messages.py +191 -0
  130. autocoder/common/international/messages/git_helper_plugin_messages.py +159 -0
  131. autocoder/common/international/messages/queue_command_messages.py +751 -0
  132. autocoder/common/international/messages/rules_command_messages.py +77 -0
  133. autocoder/common/international/messages/sdk_messages.py +1707 -0
  134. autocoder/common/international/messages/token_helper_plugin_messages.py +361 -0
  135. autocoder/common/international/messages/tool_display_messages.py +1212 -0
  136. autocoder/common/international/messages/workflow_exception_messages.py +473 -0
  137. autocoder/common/international/test_international.py +612 -0
  138. autocoder/common/linter_core/__init__.py +28 -0
  139. autocoder/common/linter_core/base_linter.py +61 -0
  140. autocoder/common/linter_core/config_loader.py +271 -0
  141. autocoder/common/linter_core/formatters/__init__.py +0 -0
  142. autocoder/common/linter_core/formatters/base_formatter.py +38 -0
  143. autocoder/common/linter_core/formatters/raw_formatter.py +17 -0
  144. autocoder/common/linter_core/linter.py +166 -0
  145. autocoder/common/linter_core/linter_factory.py +216 -0
  146. autocoder/common/linter_core/linter_manager.py +333 -0
  147. autocoder/common/linter_core/linters/__init__.py +9 -0
  148. autocoder/common/linter_core/linters/java_linter.py +342 -0
  149. autocoder/common/linter_core/linters/python_linter.py +115 -0
  150. autocoder/common/linter_core/linters/typescript_linter.py +119 -0
  151. autocoder/common/linter_core/models/__init__.py +7 -0
  152. autocoder/common/linter_core/models/lint_result.py +91 -0
  153. autocoder/common/linter_core/models.py +33 -0
  154. autocoder/common/linter_core/tests/__init__.py +3 -0
  155. autocoder/common/linter_core/tests/test_config_loader.py +323 -0
  156. autocoder/common/linter_core/tests/test_config_loading.py +308 -0
  157. autocoder/common/linter_core/tests/test_factory_manager.py +234 -0
  158. autocoder/common/linter_core/tests/test_formatters.py +147 -0
  159. autocoder/common/linter_core/tests/test_integration.py +317 -0
  160. autocoder/common/linter_core/tests/test_java_linter.py +496 -0
  161. autocoder/common/linter_core/tests/test_linters.py +265 -0
  162. autocoder/common/linter_core/tests/test_models.py +81 -0
  163. autocoder/common/linter_core/tests/verify_config_loading.py +296 -0
  164. autocoder/common/linter_core/tests/verify_fixes.py +183 -0
  165. autocoder/common/llm_friendly_package/__init__.py +31 -0
  166. autocoder/common/llm_friendly_package/base_manager.py +102 -0
  167. autocoder/common/llm_friendly_package/docs_manager.py +121 -0
  168. autocoder/common/llm_friendly_package/library_manager.py +171 -0
  169. autocoder/common/{llm_friendly_package.py → llm_friendly_package/main_manager.py} +204 -231
  170. autocoder/common/llm_friendly_package/models.py +40 -0
  171. autocoder/common/llm_friendly_package/test_llm_friendly_package.py +536 -0
  172. autocoder/common/llms/__init__.py +15 -0
  173. autocoder/common/llms/demo_error_handling.py +85 -0
  174. autocoder/common/llms/factory.py +142 -0
  175. autocoder/common/llms/manager.py +264 -0
  176. autocoder/common/llms/pricing.py +121 -0
  177. autocoder/common/llms/registry.py +288 -0
  178. autocoder/common/llms/schema.py +77 -0
  179. autocoder/common/llms/simple_demo.py +45 -0
  180. autocoder/common/llms/test_quick_model.py +116 -0
  181. autocoder/common/llms/test_remove_functionality.py +182 -0
  182. autocoder/common/llms/tests/__init__.py +1 -0
  183. autocoder/common/llms/tests/test_manager.py +330 -0
  184. autocoder/common/llms/tests/test_registry.py +364 -0
  185. autocoder/common/mcp_tools/__init__.py +62 -0
  186. autocoder/common/{mcp_tools.py → mcp_tools/executor.py} +49 -40
  187. autocoder/common/{mcp_hub.py → mcp_tools/hub.py} +42 -68
  188. autocoder/common/{mcp_server_install.py → mcp_tools/installer.py} +16 -28
  189. autocoder/common/{mcp_server.py → mcp_tools/server.py} +176 -48
  190. autocoder/common/mcp_tools/test_keyboard_interrupt.py +93 -0
  191. autocoder/common/mcp_tools/test_mcp_tools.py +391 -0
  192. autocoder/common/{mcp_server_types.py → mcp_tools/types.py} +121 -48
  193. autocoder/common/mcp_tools/verify_functionality.py +202 -0
  194. autocoder/common/model_speed_tester.py +32 -26
  195. autocoder/common/priority_directory_finder/__init__.py +142 -0
  196. autocoder/common/priority_directory_finder/examples.py +230 -0
  197. autocoder/common/priority_directory_finder/finder.py +283 -0
  198. autocoder/common/priority_directory_finder/models.py +236 -0
  199. autocoder/common/priority_directory_finder/test_priority_directory_finder.py +431 -0
  200. autocoder/common/project_scanner/__init__.py +18 -0
  201. autocoder/common/project_scanner/compat.py +77 -0
  202. autocoder/common/project_scanner/scanner.py +436 -0
  203. autocoder/common/project_tracker/__init__.py +27 -0
  204. autocoder/common/project_tracker/api.py +228 -0
  205. autocoder/common/project_tracker/demo.py +272 -0
  206. autocoder/common/project_tracker/tracker.py +487 -0
  207. autocoder/common/project_tracker/types.py +53 -0
  208. autocoder/common/pruner/__init__.py +67 -0
  209. autocoder/common/pruner/agentic_conversation_pruner.py +651 -102
  210. autocoder/common/pruner/conversation_message_ids_api.py +386 -0
  211. autocoder/common/pruner/conversation_message_ids_manager.py +347 -0
  212. autocoder/common/pruner/conversation_message_ids_pruner.py +473 -0
  213. autocoder/common/pruner/conversation_normalizer.py +347 -0
  214. autocoder/common/pruner/conversation_pruner.py +26 -6
  215. autocoder/common/pruner/test_agentic_conversation_pruner.py +554 -112
  216. autocoder/common/pruner/test_conversation_normalizer.py +502 -0
  217. autocoder/common/pruner/test_tool_content_detector.py +324 -0
  218. autocoder/common/pruner/tool_content_detector.py +227 -0
  219. autocoder/common/pruner/tools/__init__.py +18 -0
  220. autocoder/common/pruner/tools/query_message_ids.py +264 -0
  221. autocoder/common/pruner/tools/test_agentic_pruning_logic.py +432 -0
  222. autocoder/common/pruner/tools/test_message_ids_pruning_only.py +192 -0
  223. autocoder/common/pull_requests/__init__.py +9 -1
  224. autocoder/common/pull_requests/utils.py +122 -1
  225. autocoder/common/rag_manager/rag_manager.py +36 -40
  226. autocoder/common/rulefiles/__init__.py +53 -1
  227. autocoder/common/rulefiles/api.py +250 -0
  228. autocoder/common/rulefiles/core/__init__.py +14 -0
  229. autocoder/common/rulefiles/core/manager.py +241 -0
  230. autocoder/common/rulefiles/core/selector.py +805 -0
  231. autocoder/common/rulefiles/models/__init__.py +20 -0
  232. autocoder/common/rulefiles/models/index.py +16 -0
  233. autocoder/common/rulefiles/models/init_rule.py +18 -0
  234. autocoder/common/rulefiles/models/rule_file.py +18 -0
  235. autocoder/common/rulefiles/models/rule_relevance.py +14 -0
  236. autocoder/common/rulefiles/models/summary.py +16 -0
  237. autocoder/common/rulefiles/test_rulefiles.py +776 -0
  238. autocoder/common/rulefiles/utils/__init__.py +34 -0
  239. autocoder/common/rulefiles/utils/monitor.py +86 -0
  240. autocoder/common/rulefiles/utils/parser.py +230 -0
  241. autocoder/common/save_formatted_log.py +67 -10
  242. autocoder/common/search_replace.py +8 -1
  243. autocoder/common/search_replace_patch/__init__.py +24 -0
  244. autocoder/common/search_replace_patch/base.py +115 -0
  245. autocoder/common/search_replace_patch/manager.py +248 -0
  246. autocoder/common/search_replace_patch/patch_replacer.py +304 -0
  247. autocoder/common/search_replace_patch/similarity_replacer.py +306 -0
  248. autocoder/common/search_replace_patch/string_replacer.py +181 -0
  249. autocoder/common/search_replace_patch/tests/__init__.py +3 -0
  250. autocoder/common/search_replace_patch/tests/run_tests.py +126 -0
  251. autocoder/common/search_replace_patch/tests/test_base.py +188 -0
  252. autocoder/common/search_replace_patch/tests/test_empty_line_insert.py +233 -0
  253. autocoder/common/search_replace_patch/tests/test_integration.py +389 -0
  254. autocoder/common/search_replace_patch/tests/test_manager.py +351 -0
  255. autocoder/common/search_replace_patch/tests/test_patch_replacer.py +316 -0
  256. autocoder/common/search_replace_patch/tests/test_regex_replacer.py +306 -0
  257. autocoder/common/search_replace_patch/tests/test_similarity_replacer.py +384 -0
  258. autocoder/common/shell_commands/__init__.py +197 -0
  259. autocoder/common/shell_commands/background_process_notifier.py +346 -0
  260. autocoder/common/shell_commands/command_executor.py +1127 -0
  261. autocoder/common/shell_commands/error_recovery.py +541 -0
  262. autocoder/common/shell_commands/exceptions.py +120 -0
  263. autocoder/common/shell_commands/interactive_executor.py +476 -0
  264. autocoder/common/shell_commands/interactive_pexpect_process.py +623 -0
  265. autocoder/common/shell_commands/interactive_process.py +744 -0
  266. autocoder/common/shell_commands/interactive_session_manager.py +1014 -0
  267. autocoder/common/shell_commands/monitoring.py +529 -0
  268. autocoder/common/shell_commands/process_cleanup.py +386 -0
  269. autocoder/common/shell_commands/process_manager.py +606 -0
  270. autocoder/common/shell_commands/test_interactive_pexpect_process.py +281 -0
  271. autocoder/common/shell_commands/tests/__init__.py +6 -0
  272. autocoder/common/shell_commands/tests/conftest.py +118 -0
  273. autocoder/common/shell_commands/tests/test_background_process_notifier.py +703 -0
  274. autocoder/common/shell_commands/tests/test_command_executor.py +448 -0
  275. autocoder/common/shell_commands/tests/test_error_recovery.py +305 -0
  276. autocoder/common/shell_commands/tests/test_exceptions.py +299 -0
  277. autocoder/common/shell_commands/tests/test_execute_batch.py +588 -0
  278. autocoder/common/shell_commands/tests/test_indented_batch_commands.py +244 -0
  279. autocoder/common/shell_commands/tests/test_integration.py +664 -0
  280. autocoder/common/shell_commands/tests/test_monitoring.py +546 -0
  281. autocoder/common/shell_commands/tests/test_performance.py +632 -0
  282. autocoder/common/shell_commands/tests/test_process_cleanup.py +397 -0
  283. autocoder/common/shell_commands/tests/test_process_manager.py +606 -0
  284. autocoder/common/shell_commands/tests/test_timeout_config.py +343 -0
  285. autocoder/common/shell_commands/tests/test_timeout_manager.py +520 -0
  286. autocoder/common/shell_commands/timeout_config.py +315 -0
  287. autocoder/common/shell_commands/timeout_manager.py +352 -0
  288. autocoder/common/terminal_paste/__init__.py +14 -0
  289. autocoder/common/terminal_paste/demo.py +145 -0
  290. autocoder/common/terminal_paste/demo_paste_functionality.py +95 -0
  291. autocoder/common/terminal_paste/paste_handler.py +200 -0
  292. autocoder/common/terminal_paste/paste_manager.py +118 -0
  293. autocoder/common/terminal_paste/tests/__init__.py +1 -0
  294. autocoder/common/terminal_paste/tests/test_paste_handler.py +182 -0
  295. autocoder/common/terminal_paste/tests/test_paste_manager.py +126 -0
  296. autocoder/common/terminal_paste/utils.py +163 -0
  297. autocoder/common/test_autocoder_args.py +232 -0
  298. autocoder/common/test_env_manager.py +173 -0
  299. autocoder/common/test_env_manager_integration.py +159 -0
  300. autocoder/common/text_similarity/__init__.py +9 -0
  301. autocoder/common/text_similarity/demo.py +216 -0
  302. autocoder/common/text_similarity/examples.py +266 -0
  303. autocoder/common/text_similarity/test_text_similarity.py +306 -0
  304. autocoder/common/text_similarity/text_similarity.py +194 -0
  305. autocoder/common/text_similarity/utils.py +125 -0
  306. autocoder/common/todos/__init__.py +61 -0
  307. autocoder/common/todos/cache/__init__.py +16 -0
  308. autocoder/common/todos/cache/base_cache.py +89 -0
  309. autocoder/common/todos/cache/cache_manager.py +228 -0
  310. autocoder/common/todos/cache/memory_cache.py +225 -0
  311. autocoder/common/todos/config.py +155 -0
  312. autocoder/common/todos/exceptions.py +35 -0
  313. autocoder/common/todos/get_todo_manager.py +161 -0
  314. autocoder/common/todos/manager.py +537 -0
  315. autocoder/common/todos/models.py +239 -0
  316. autocoder/common/todos/storage/__init__.py +14 -0
  317. autocoder/common/todos/storage/base_storage.py +76 -0
  318. autocoder/common/todos/storage/file_storage.py +278 -0
  319. autocoder/common/tokens/counter.py +24 -2
  320. autocoder/common/tools_manager/__init__.py +17 -0
  321. autocoder/common/tools_manager/examples.py +162 -0
  322. autocoder/common/tools_manager/manager.py +385 -0
  323. autocoder/common/tools_manager/models.py +39 -0
  324. autocoder/common/tools_manager/test_tools_manager.py +303 -0
  325. autocoder/common/tools_manager/utils.py +191 -0
  326. autocoder/common/v2/agent/agentic_callbacks.py +270 -0
  327. autocoder/common/v2/agent/agentic_edit.py +2699 -1856
  328. autocoder/common/v2/agent/agentic_edit_change_manager.py +474 -0
  329. autocoder/common/v2/agent/agentic_edit_tools/__init__.py +35 -1
  330. autocoder/common/v2/agent/agentic_edit_tools/ac_mod_list_tool_resolver.py +279 -0
  331. autocoder/common/v2/agent/agentic_edit_tools/ac_mod_write_tool_resolver.py +10 -1
  332. autocoder/common/v2/agent/agentic_edit_tools/background_task_tool_resolver.py +1167 -0
  333. autocoder/common/v2/agent/agentic_edit_tools/base_tool_resolver.py +2 -2
  334. autocoder/common/v2/agent/agentic_edit_tools/conversation_message_ids_read_tool_resolver.py +214 -0
  335. autocoder/common/v2/agent/agentic_edit_tools/conversation_message_ids_write_tool_resolver.py +299 -0
  336. autocoder/common/v2/agent/agentic_edit_tools/count_tokens_tool_resolver.py +290 -0
  337. autocoder/common/v2/agent/agentic_edit_tools/execute_command_tool_resolver.py +564 -29
  338. autocoder/common/v2/agent/agentic_edit_tools/execute_workflow_tool_resolver.py +485 -0
  339. autocoder/common/v2/agent/agentic_edit_tools/extract_to_text_tool_resolver.py +225 -0
  340. autocoder/common/v2/agent/agentic_edit_tools/lint_report.py +79 -0
  341. autocoder/common/v2/agent/agentic_edit_tools/linter_config_models.py +343 -0
  342. autocoder/common/v2/agent/agentic_edit_tools/linter_enabled_tool_resolver.py +189 -0
  343. autocoder/common/v2/agent/agentic_edit_tools/list_files_tool_resolver.py +169 -101
  344. autocoder/common/v2/agent/agentic_edit_tools/load_extra_document_tool_resolver.py +349 -0
  345. autocoder/common/v2/agent/agentic_edit_tools/read_file_tool_resolver.py +243 -50
  346. autocoder/common/v2/agent/agentic_edit_tools/replace_in_file_tool_resolver.py +667 -147
  347. autocoder/common/v2/agent/agentic_edit_tools/run_named_subagents_tool_resolver.py +691 -0
  348. autocoder/common/v2/agent/agentic_edit_tools/search_files_tool_resolver.py +410 -86
  349. autocoder/common/v2/agent/agentic_edit_tools/session_interactive_tool_resolver.py +115 -0
  350. autocoder/common/v2/agent/agentic_edit_tools/session_start_tool_resolver.py +190 -0
  351. autocoder/common/v2/agent/agentic_edit_tools/session_stop_tool_resolver.py +76 -0
  352. autocoder/common/v2/agent/agentic_edit_tools/test_write_to_file_tool_resolver.py +207 -192
  353. autocoder/common/v2/agent/agentic_edit_tools/todo_read_tool_resolver.py +80 -63
  354. autocoder/common/v2/agent/agentic_edit_tools/todo_write_tool_resolver.py +237 -233
  355. autocoder/common/v2/agent/agentic_edit_tools/use_mcp_tool_resolver.py +2 -2
  356. autocoder/common/v2/agent/agentic_edit_tools/web_crawl_tool_resolver.py +557 -0
  357. autocoder/common/v2/agent/agentic_edit_tools/web_search_tool_resolver.py +600 -0
  358. autocoder/common/v2/agent/agentic_edit_tools/write_to_file_tool_resolver.py +56 -121
  359. autocoder/common/v2/agent/agentic_edit_types.py +343 -9
  360. autocoder/common/v2/agent/runner/__init__.py +3 -3
  361. autocoder/common/v2/agent/runner/base_runner.py +12 -26
  362. autocoder/common/v2/agent/runner/{event_runner.py → file_based_event_runner.py} +3 -2
  363. autocoder/common/v2/agent/runner/sdk_runner.py +150 -8
  364. autocoder/common/v2/agent/runner/terminal_runner.py +170 -57
  365. autocoder/common/v2/agent/runner/tool_display.py +557 -159
  366. autocoder/common/v2/agent/test_agentic_callbacks.py +265 -0
  367. autocoder/common/v2/agent/test_agentic_edit.py +194 -0
  368. autocoder/common/v2/agent/tool_caller/__init__.py +24 -0
  369. autocoder/common/v2/agent/tool_caller/default_tool_resolver_map.py +135 -0
  370. autocoder/common/v2/agent/tool_caller/integration_test.py +172 -0
  371. autocoder/common/v2/agent/tool_caller/plugins/__init__.py +14 -0
  372. autocoder/common/v2/agent/tool_caller/plugins/base_plugin.py +126 -0
  373. autocoder/common/v2/agent/tool_caller/plugins/examples/__init__.py +13 -0
  374. autocoder/common/v2/agent/tool_caller/plugins/examples/logging_plugin.py +164 -0
  375. autocoder/common/v2/agent/tool_caller/plugins/examples/security_filter_plugin.py +198 -0
  376. autocoder/common/v2/agent/tool_caller/plugins/plugin_interface.py +141 -0
  377. autocoder/common/v2/agent/tool_caller/test_tool_caller.py +278 -0
  378. autocoder/common/v2/agent/tool_caller/tool_call_plugin_manager.py +331 -0
  379. autocoder/common/v2/agent/tool_caller/tool_caller.py +337 -0
  380. autocoder/common/v2/agent/tool_caller/usage_example.py +193 -0
  381. autocoder/common/v2/code_agentic_editblock_manager.py +4 -4
  382. autocoder/common/v2/code_auto_generate.py +136 -78
  383. autocoder/common/v2/code_auto_generate_diff.py +135 -79
  384. autocoder/common/v2/code_auto_generate_editblock.py +174 -99
  385. autocoder/common/v2/code_auto_generate_strict_diff.py +151 -71
  386. autocoder/common/v2/code_auto_merge.py +1 -1
  387. autocoder/common/v2/code_auto_merge_editblock.py +13 -1
  388. autocoder/common/v2/code_diff_manager.py +3 -3
  389. autocoder/common/v2/code_editblock_manager.py +4 -14
  390. autocoder/common/v2/code_manager.py +1 -1
  391. autocoder/common/v2/code_strict_diff_manager.py +2 -2
  392. autocoder/common/wrap_llm_hint/__init__.py +10 -0
  393. autocoder/common/wrap_llm_hint/test_wrap_llm_hint.py +1067 -0
  394. autocoder/common/wrap_llm_hint/utils.py +432 -0
  395. autocoder/common/wrap_llm_hint/wrap_llm_hint.py +323 -0
  396. autocoder/completer/__init__.py +8 -0
  397. autocoder/completer/command_completer_v2.py +1051 -0
  398. autocoder/default_project/__init__.py +501 -0
  399. autocoder/dispacher/__init__.py +4 -12
  400. autocoder/dispacher/actions/action.py +165 -7
  401. autocoder/dispacher/actions/plugins/action_regex_project.py +2 -2
  402. autocoder/index/entry.py +116 -124
  403. autocoder/{agent → index/filter}/agentic_filter.py +322 -333
  404. autocoder/index/filter/normal_filter.py +5 -11
  405. autocoder/index/filter/quick_filter.py +1 -1
  406. autocoder/index/index.py +36 -9
  407. autocoder/index/tests/__init__.py +1 -0
  408. autocoder/index/tests/run_tests.py +195 -0
  409. autocoder/index/tests/test_entry.py +303 -0
  410. autocoder/index/tests/test_index_manager.py +314 -0
  411. autocoder/index/tests/test_module_integration.py +300 -0
  412. autocoder/index/tests/test_symbols_utils.py +183 -0
  413. autocoder/inner/__init__.py +4 -0
  414. autocoder/inner/agentic.py +932 -0
  415. autocoder/inner/async_command_handler.py +992 -0
  416. autocoder/inner/conversation_command_handlers.py +623 -0
  417. autocoder/inner/merge_command_handler.py +213 -0
  418. autocoder/inner/queue_command_handler.py +684 -0
  419. autocoder/models.py +95 -266
  420. autocoder/plugins/git_helper_plugin.py +31 -29
  421. autocoder/plugins/token_helper_plugin.py +65 -46
  422. autocoder/pyproject/__init__.py +32 -29
  423. autocoder/rag/agentic_rag.py +215 -75
  424. autocoder/rag/cache/simple_cache.py +1 -2
  425. autocoder/rag/loaders/image_loader.py +1 -1
  426. autocoder/rag/long_context_rag.py +42 -26
  427. autocoder/rag/qa_conversation_strategy.py +1 -1
  428. autocoder/rag/terminal/__init__.py +17 -0
  429. autocoder/rag/terminal/args.py +581 -0
  430. autocoder/rag/terminal/bootstrap.py +61 -0
  431. autocoder/rag/terminal/command_handlers.py +653 -0
  432. autocoder/rag/terminal/formatters/__init__.py +20 -0
  433. autocoder/rag/terminal/formatters/base.py +70 -0
  434. autocoder/rag/terminal/formatters/json_format.py +66 -0
  435. autocoder/rag/terminal/formatters/stream_json.py +95 -0
  436. autocoder/rag/terminal/formatters/text.py +28 -0
  437. autocoder/rag/terminal/init.py +120 -0
  438. autocoder/rag/terminal/utils.py +106 -0
  439. autocoder/rag/test_agentic_rag.py +389 -0
  440. autocoder/rag/test_doc_filter.py +3 -3
  441. autocoder/rag/test_long_context_rag.py +1 -1
  442. autocoder/rag/test_token_limiter.py +517 -10
  443. autocoder/rag/token_counter.py +3 -0
  444. autocoder/rag/token_limiter.py +19 -15
  445. autocoder/rag/tools/__init__.py +26 -2
  446. autocoder/rag/tools/bochaai_example.py +343 -0
  447. autocoder/rag/tools/bochaai_sdk.py +541 -0
  448. autocoder/rag/tools/metaso_example.py +268 -0
  449. autocoder/rag/tools/metaso_sdk.py +417 -0
  450. autocoder/rag/tools/recall_tool.py +28 -7
  451. autocoder/rag/tools/run_integration_tests.py +204 -0
  452. autocoder/rag/tools/test_all_providers.py +318 -0
  453. autocoder/rag/tools/test_bochaai_integration.py +482 -0
  454. autocoder/rag/tools/test_final_integration.py +215 -0
  455. autocoder/rag/tools/test_metaso_integration.py +424 -0
  456. autocoder/rag/tools/test_metaso_real.py +171 -0
  457. autocoder/rag/tools/test_web_crawl_tool.py +639 -0
  458. autocoder/rag/tools/test_web_search_tool.py +509 -0
  459. autocoder/rag/tools/todo_read_tool.py +202 -0
  460. autocoder/rag/tools/todo_write_tool.py +412 -0
  461. autocoder/rag/tools/web_crawl_tool.py +634 -0
  462. autocoder/rag/tools/web_search_tool.py +558 -0
  463. autocoder/rag/tools/web_tools_example.py +119 -0
  464. autocoder/rag/types.py +16 -0
  465. autocoder/rag/variable_holder.py +4 -2
  466. autocoder/rags.py +86 -79
  467. autocoder/regexproject/__init__.py +23 -21
  468. autocoder/sdk/__init__.py +46 -190
  469. autocoder/sdk/api.py +370 -0
  470. autocoder/sdk/async_runner/__init__.py +26 -0
  471. autocoder/sdk/async_runner/async_executor.py +650 -0
  472. autocoder/sdk/async_runner/async_handler.py +356 -0
  473. autocoder/sdk/async_runner/markdown_processor.py +595 -0
  474. autocoder/sdk/async_runner/task_metadata.py +284 -0
  475. autocoder/sdk/async_runner/worktree_manager.py +438 -0
  476. autocoder/sdk/cli/__init__.py +2 -5
  477. autocoder/sdk/cli/formatters.py +28 -204
  478. autocoder/sdk/cli/handlers.py +77 -44
  479. autocoder/sdk/cli/main.py +154 -171
  480. autocoder/sdk/cli/options.py +95 -22
  481. autocoder/sdk/constants.py +139 -51
  482. autocoder/sdk/core/auto_coder_core.py +484 -109
  483. autocoder/sdk/core/bridge.py +297 -115
  484. autocoder/sdk/exceptions.py +18 -12
  485. autocoder/sdk/formatters/__init__.py +19 -0
  486. autocoder/sdk/formatters/input.py +64 -0
  487. autocoder/sdk/formatters/output.py +247 -0
  488. autocoder/sdk/formatters/stream.py +54 -0
  489. autocoder/sdk/models/__init__.py +6 -5
  490. autocoder/sdk/models/options.py +55 -18
  491. autocoder/sdk/utils/formatters.py +27 -195
  492. autocoder/suffixproject/__init__.py +28 -25
  493. autocoder/terminal/__init__.py +14 -0
  494. autocoder/terminal/app.py +454 -0
  495. autocoder/terminal/args.py +32 -0
  496. autocoder/terminal/bootstrap.py +178 -0
  497. autocoder/terminal/command_processor.py +521 -0
  498. autocoder/terminal/command_registry.py +57 -0
  499. autocoder/terminal/help.py +97 -0
  500. autocoder/terminal/tasks/__init__.py +5 -0
  501. autocoder/terminal/tasks/background.py +77 -0
  502. autocoder/terminal/tasks/task_event.py +70 -0
  503. autocoder/terminal/ui/__init__.py +13 -0
  504. autocoder/terminal/ui/completer.py +268 -0
  505. autocoder/terminal/ui/keybindings.py +75 -0
  506. autocoder/terminal/ui/session.py +41 -0
  507. autocoder/terminal/ui/toolbar.py +64 -0
  508. autocoder/terminal/utils/__init__.py +13 -0
  509. autocoder/terminal/utils/errors.py +18 -0
  510. autocoder/terminal/utils/paths.py +19 -0
  511. autocoder/terminal/utils/shell.py +43 -0
  512. autocoder/terminal_v3/__init__.py +10 -0
  513. autocoder/terminal_v3/app.py +201 -0
  514. autocoder/terminal_v3/handlers/__init__.py +5 -0
  515. autocoder/terminal_v3/handlers/command_handler.py +131 -0
  516. autocoder/terminal_v3/models/__init__.py +6 -0
  517. autocoder/terminal_v3/models/conversation_buffer.py +214 -0
  518. autocoder/terminal_v3/models/message.py +50 -0
  519. autocoder/terminal_v3/models/tool_display.py +247 -0
  520. autocoder/terminal_v3/ui/__init__.py +7 -0
  521. autocoder/terminal_v3/ui/keybindings.py +56 -0
  522. autocoder/terminal_v3/ui/layout.py +141 -0
  523. autocoder/terminal_v3/ui/styles.py +43 -0
  524. autocoder/tsproject/__init__.py +23 -23
  525. autocoder/utils/auto_coder_utils/chat_stream_out.py +1 -1
  526. autocoder/utils/llms.py +88 -80
  527. autocoder/utils/math_utils.py +101 -0
  528. autocoder/utils/model_provider_selector.py +16 -4
  529. autocoder/utils/operate_config_api.py +33 -5
  530. autocoder/utils/thread_utils.py +2 -2
  531. autocoder/version.py +4 -2
  532. autocoder/workflow_agents/__init__.py +84 -0
  533. autocoder/workflow_agents/agent.py +143 -0
  534. autocoder/workflow_agents/exceptions.py +573 -0
  535. autocoder/workflow_agents/executor.py +489 -0
  536. autocoder/workflow_agents/loader.py +737 -0
  537. autocoder/workflow_agents/runner.py +267 -0
  538. autocoder/workflow_agents/types.py +172 -0
  539. autocoder/workflow_agents/utils.py +434 -0
  540. autocoder/workflow_agents/workflow_manager.py +211 -0
  541. auto_coder-1.0.0.dist-info/METADATA +0 -396
  542. auto_coder-1.0.0.dist-info/RECORD +0 -442
  543. auto_coder-1.0.0.dist-info/licenses/LICENSE +0 -201
  544. autocoder/auto_coder_server.py +0 -672
  545. autocoder/benchmark.py +0 -138
  546. autocoder/common/ac_style_command_parser/example.py +0 -7
  547. autocoder/common/cleaner.py +0 -31
  548. autocoder/common/command_completer_v2.py +0 -615
  549. autocoder/common/context_pruner.py +0 -477
  550. autocoder/common/conversation_pruner.py +0 -132
  551. autocoder/common/directory_cache/__init__.py +0 -1
  552. autocoder/common/directory_cache/cache.py +0 -192
  553. autocoder/common/directory_cache/test_cache.py +0 -190
  554. autocoder/common/file_checkpoint/examples.py +0 -217
  555. autocoder/common/llm_friendly_package_example.py +0 -138
  556. autocoder/common/llm_friendly_package_test.py +0 -63
  557. autocoder/common/pull_requests/test_module.py +0 -1
  558. autocoder/common/rulefiles/autocoderrules_utils.py +0 -484
  559. autocoder/common/text.py +0 -30
  560. autocoder/common/v2/agent/agentic_edit_tools/list_package_info_tool_resolver.py +0 -42
  561. autocoder/common/v2/agent/agentic_edit_tools/test_execute_command_tool_resolver.py +0 -70
  562. autocoder/common/v2/agent/agentic_edit_tools/test_search_files_tool_resolver.py +0 -163
  563. autocoder/common/v2/agent/agentic_tool_display.py +0 -183
  564. autocoder/plugins/dynamic_completion_example.py +0 -148
  565. autocoder/plugins/sample_plugin.py +0 -160
  566. autocoder/sdk/cli/__main__.py +0 -26
  567. autocoder/sdk/cli/completion_wrapper.py +0 -38
  568. autocoder/sdk/cli/install_completion.py +0 -301
  569. autocoder/sdk/models/messages.py +0 -209
  570. autocoder/sdk/session/__init__.py +0 -32
  571. autocoder/sdk/session/session.py +0 -106
  572. autocoder/sdk/session/session_manager.py +0 -56
  573. {auto_coder-1.0.0.dist-info → auto_coder-2.0.0.dist-info}/top_level.txt +0 -0
  574. /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()