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,1167 @@
1
+ import os
2
+ import time
3
+ import signal
4
+ import psutil
5
+ from typing import Optional, Dict, List, Any, Tuple
6
+ from autocoder.common.v2.agent.agentic_edit_tools.base_tool_resolver import BaseToolResolver
7
+ from autocoder.common.v2.agent.agentic_edit_types import BackgroundTaskTool, ToolResult
8
+ from autocoder.common import AutoCoderArgs
9
+ from autocoder.common.shell_commands import get_background_process_notifier, get_background_processes, get_background_process_info
10
+ from loguru import logger
11
+ import typing
12
+
13
+ if typing.TYPE_CHECKING:
14
+ from autocoder.common.v2.agent.agentic_edit import AgenticEdit
15
+
16
+
17
+ class BackgroundTaskToolResolver(BaseToolResolver):
18
+ """统一的后台任务管理工具解析器"""
19
+
20
+ def __init__(self, agent: Optional['AgenticEdit'], tool: BackgroundTaskTool, args: AutoCoderArgs):
21
+ super().__init__(agent, tool, args)
22
+ self.tool: BackgroundTaskTool = tool
23
+
24
+ def resolve(self) -> ToolResult:
25
+ """
26
+ 根据 action 参数执行相应的后台任务操作
27
+
28
+ Returns:
29
+ ToolResult: 操作结果
30
+ """
31
+ try:
32
+ # 获取当前会话ID
33
+ conversation_id = self.agent.conversation_config.conversation_id if self.agent else None
34
+ if not conversation_id:
35
+ return ToolResult(
36
+ success=False,
37
+ message="无法获取当前会话ID",
38
+ content={"action": self.tool.action, "error": "missing_agent_or_config"}
39
+ )
40
+
41
+ # 根据 action 参数调用相应的方法
42
+ if self.tool.action == "list":
43
+ return self._handle_list_action(conversation_id)
44
+ elif self.tool.action == "monitor":
45
+ return self._handle_monitor_action(conversation_id)
46
+ elif self.tool.action == "cleanup":
47
+ return self._handle_cleanup_action(conversation_id)
48
+ elif self.tool.action == "kill":
49
+ return self._handle_kill_action(conversation_id)
50
+ else:
51
+ return ToolResult(
52
+ success=False,
53
+ message=f"不支持的操作类型: {self.tool.action}。支持的操作: list, monitor, cleanup, kill",
54
+ content={"action": self.tool.action, "error": "unsupported_action"}
55
+ )
56
+
57
+ except Exception as e:
58
+ logger.error(f"BackgroundTaskTool execution failed: {str(e)}")
59
+ return ToolResult(
60
+ success=False,
61
+ message=f"后台任务操作失败: {str(e)}",
62
+ content={"action": self.tool.action, "error": str(e)}
63
+ )
64
+
65
+ def _handle_list_action(self, conversation_id: str) -> ToolResult:
66
+ """处理列出任务的操作"""
67
+ try:
68
+ # 获取后台进程通知器
69
+ notifier = get_background_process_notifier()
70
+
71
+ # 获取全局后台进程列表
72
+ bg_processes = get_background_processes()
73
+ if not bg_processes:
74
+ bg_processes = {}
75
+
76
+ # 收集任务信息
77
+ running_tasks = []
78
+ completed_tasks = []
79
+
80
+ # 从通知器获取注册的任务信息
81
+ with notifier._lock:
82
+ registrations = dict(notifier._registrations)
83
+ reported = set(notifier._reported)
84
+ pending_messages = dict(notifier._pending_messages)
85
+
86
+ # 处理注册的任务
87
+ for key, reg in registrations.items():
88
+ if reg.conversation_id != conversation_id:
89
+ continue
90
+
91
+ pid, start_time = key
92
+
93
+ # 应用任务类型过滤(兼容不同的工具命名)
94
+ if self.tool.task_type:
95
+ category = self._classify_tool_name(reg.tool_name)
96
+ if category != self.tool.task_type:
97
+ continue
98
+
99
+ # 获取进程信息
100
+ process_info = bg_processes.get(pid, {})
101
+ status = process_info.get("status", "unknown")
102
+
103
+ task_info = {
104
+ "task_id": reg.task_id,
105
+ "pid": pid,
106
+ "process_uniq_id": process_info.get("process_uniq_id") if process_info else None,
107
+ "tool_name": reg.tool_name,
108
+ "status": status,
109
+ "command": reg.command,
110
+ "cwd": reg.cwd,
111
+ "agent_name": reg.agent_name,
112
+ "task": reg.task,
113
+ "start_time": reg.start_time,
114
+ "running_time": time.time() - reg.start_time
115
+ }
116
+
117
+ if status == "completed":
118
+ task_info["exit_code"] = process_info.get("exit_code")
119
+ task_info["end_time"] = process_info.get("end_time")
120
+ completed_tasks.append(task_info)
121
+ else:
122
+ running_tasks.append(task_info)
123
+
124
+ # 从待处理消息中获取已完成的任务
125
+ if conversation_id in pending_messages:
126
+ for msg in pending_messages[conversation_id]:
127
+ # 应用任务类型过滤(暂时不考虑)
128
+ # if self.tool.task_type:
129
+ # if self.tool.task_type == "command" and msg.tool_name != "ExecuteCommandTool":
130
+ # continue
131
+ # elif self.tool.task_type == "subagent" and msg.tool_name != "RunNamedSubagentsTool":
132
+ # continue
133
+
134
+ task_info = {
135
+ "task_id": msg.task_id,
136
+ "pid": msg.pid,
137
+ "tool_name": msg.tool_name,
138
+ "status": msg.status,
139
+ "command": msg.command,
140
+ "cwd": msg.cwd,
141
+ "agent_name": msg.agent_name,
142
+ "task": msg.task,
143
+ "exit_code": msg.exit_code,
144
+ "running_time": msg.duration_sec,
145
+ "completed_at": msg.completed_at
146
+ }
147
+ completed_tasks.append(task_info)
148
+
149
+ # 格式化输出
150
+ output_lines = []
151
+
152
+ if running_tasks:
153
+ output_lines.append(f"正在运行的后台任务 ({len(running_tasks)}个):")
154
+ output_lines.append("")
155
+
156
+ for i, task in enumerate(running_tasks, 1):
157
+ id_info = f"ID: {task['process_uniq_id']}" if task.get('process_uniq_id') else f"PID: {task['pid']}"
158
+ output_lines.append(f"{i}. [{task['tool_name']}] {id_info} | 状态: {task['status']} | 运行时间: {self._format_duration(task['running_time'])}")
159
+
160
+ if self._classify_tool_name(task['tool_name']) == 'command':
161
+ output_lines.append(f" 命令: {task['command']}")
162
+ if task['cwd']:
163
+ output_lines.append(f" 工作目录: {task['cwd']}")
164
+ elif self._classify_tool_name(task['tool_name']) == 'subagent':
165
+ if task['agent_name']:
166
+ output_lines.append(f" 代理: {task['agent_name']}")
167
+ if task['task']:
168
+ # 限制任务描述长度
169
+ task_desc = task['task'][:100] + "..." if len(task['task']) > 100 else task['task']
170
+ output_lines.append(f" 任务: {task_desc}")
171
+
172
+ output_lines.append("")
173
+ else:
174
+ output_lines.append("当前没有正在运行的后台任务")
175
+ output_lines.append("")
176
+
177
+ if self.tool.show_completed and completed_tasks:
178
+ output_lines.append(f"已完成的任务 ({len(completed_tasks)}个):")
179
+ output_lines.append("")
180
+
181
+ for i, task in enumerate(completed_tasks, 1):
182
+ status_icon = "✅" if task['status'] == 'completed' else "❌"
183
+ duration = task.get('running_time', 0)
184
+ exit_code = task.get('exit_code', 'N/A')
185
+
186
+ id_info = f"ID: {task.get('process_uniq_id')}" if task.get('process_uniq_id') else f"PID: {task['pid']}"
187
+ output_lines.append(f"{i}. [{task['tool_name']}] {id_info} | 状态: {task['status']} {status_icon} | 退出码: {exit_code} | 运行时间: {self._format_duration(duration)}")
188
+
189
+ if self._classify_tool_name(task['tool_name']) == 'command':
190
+ output_lines.append(f" 命令: {task['command']}")
191
+ elif self._classify_tool_name(task['tool_name']) == 'subagent':
192
+ if task['agent_name']:
193
+ output_lines.append(f" 代理: {task['agent_name']}")
194
+ if task['task']:
195
+ task_desc = task['task'][:100] + "..." if len(task['task']) > 100 else task['task']
196
+ output_lines.append(f" 任务: {task_desc}")
197
+
198
+ output_lines.append("")
199
+
200
+ # 构建结果
201
+ result_content = {
202
+ "action": "list",
203
+ "running_tasks": running_tasks,
204
+ "completed_tasks": completed_tasks if self.tool.show_completed else [],
205
+ "summary": {
206
+ "running_count": len(running_tasks),
207
+ "completed_count": len(completed_tasks),
208
+ "total_count": len(running_tasks) + len(completed_tasks)
209
+ }
210
+ }
211
+
212
+ message = "\n".join(output_lines).strip()
213
+ if not message:
214
+ message = "当前没有后台任务"
215
+
216
+ return ToolResult(
217
+ success=True,
218
+ message=message,
219
+ content=result_content
220
+ )
221
+
222
+ except Exception as e:
223
+ logger.error(f"List action failed: {str(e)}")
224
+ return ToolResult(
225
+ success=False,
226
+ message=f"获取后台任务列表失败: {str(e)}",
227
+ content={"action": "list", "error": str(e)}
228
+ )
229
+
230
+ def _handle_monitor_action(self, conversation_id: str) -> ToolResult:
231
+ """处理监控任务输出的操作"""
232
+ try:
233
+ # 验证参数
234
+ if not self.tool.task_id and not self.tool.pid and not self.tool.process_uniq_id:
235
+ return ToolResult(
236
+ success=False,
237
+ message="monitor 操作必须提供 task_id、pid 或 process_uniq_id 参数",
238
+ content={
239
+ "action": "monitor",
240
+ "tasks": [],
241
+ "output_type": self.tool.output_type,
242
+ "lines_requested": self.tool.lines,
243
+ "task_count": 0,
244
+ "error": "missing_required_params"
245
+ }
246
+ )
247
+
248
+ # 获取后台进程通知器和进程信息
249
+ notifier = get_background_process_notifier()
250
+ bg_processes = get_background_processes()
251
+ if not bg_processes:
252
+ bg_processes = {}
253
+
254
+ # 查找目标任务
255
+ target_tasks = []
256
+
257
+ if self.tool.task_id:
258
+ # 通过 task_id 查找
259
+ target_tasks = self._find_tasks_by_task_id(notifier, conversation_id, self.tool.task_id)
260
+ elif self.tool.pid:
261
+ # 通过 pid 查找
262
+ target_tasks = self._find_tasks_by_pid(notifier, conversation_id, self.tool.pid)
263
+ elif self.tool.process_uniq_id:
264
+ # 通过 process_uniq_id 查找
265
+ target_tasks = self._find_tasks_by_process_uniq_id(notifier, conversation_id, self.tool.process_uniq_id)
266
+
267
+ if not target_tasks:
268
+ if self.tool.task_id:
269
+ identifier = f"task_id={self.tool.task_id}"
270
+ elif self.tool.pid:
271
+ identifier = f"pid={self.tool.pid}"
272
+ else:
273
+ identifier = f"process_uniq_id={self.tool.process_uniq_id}"
274
+ return ToolResult(
275
+ success=False,
276
+ message=f"未找到指定的任务: {identifier}",
277
+ content={
278
+ "action": "monitor",
279
+ "tasks": [],
280
+ "output_type": self.tool.output_type,
281
+ "lines_requested": self.tool.lines,
282
+ "task_count": 0,
283
+ "error": "task_not_found"
284
+ }
285
+ )
286
+
287
+ # 收集所有任务的输出
288
+ output_sections = []
289
+
290
+ for task_info in target_tasks:
291
+ pid = task_info['pid']
292
+ task_id = task_info['task_id']
293
+
294
+ # 获取进程信息
295
+ process_info = bg_processes.get(pid)
296
+ if not process_info:
297
+ output_sections.append(f"任务 {task_id} (PID: {pid}) - 进程信息不可用")
298
+ continue
299
+
300
+ # 构建任务头部信息
301
+ process_uniq_id = process_info.get('process_uniq_id') if process_info else task_info.get('process_uniq_id')
302
+ id_display = f"ID: {process_uniq_id}" if process_uniq_id else f"PID: {pid}"
303
+ header_lines = [
304
+ f"任务 {task_id} ({id_display}) 输出:",
305
+ f"工具: {task_info['tool_name']}",
306
+ f"状态: {process_info.get('status', 'unknown')}",
307
+ ]
308
+
309
+ if task_info['tool_name'] == 'execute_command':
310
+ header_lines.append(f"命令: {task_info['command']}")
311
+ elif task_info['tool_name'] == 'run_named_subagents':
312
+ if task_info.get('agent_name'):
313
+ header_lines.append(f"代理: {task_info['agent_name']}")
314
+ if task_info.get('task'):
315
+ task_desc = task_info['task'][:100] + "..." if len(task_info['task']) > 100 else task_info['task']
316
+ header_lines.append(f"任务: {task_desc}")
317
+
318
+ if task_info.get('cwd'):
319
+ header_lines.append(f"工作目录: {task_info['cwd']}")
320
+
321
+ header_lines.append("")
322
+
323
+ # 获取输出内容
324
+ stdout_content, stderr_content = self._get_process_output(process_info, pid)
325
+
326
+ # 根据 output_type 参数决定显示哪些输出
327
+ if self.tool.output_type in ["stdout", "both"] and stdout_content:
328
+ stdout_lines = self._format_output_lines(stdout_content, self.tool.lines, "STDOUT")
329
+ header_lines.extend(stdout_lines)
330
+ header_lines.append("")
331
+
332
+ if self.tool.output_type in ["stderr", "both"] and stderr_content:
333
+ stderr_lines = self._format_output_lines(stderr_content, self.tool.lines, "STDERR")
334
+ header_lines.extend(stderr_lines)
335
+ header_lines.append("")
336
+
337
+ # 如果没有输出内容
338
+ if not stdout_content and not stderr_content:
339
+ header_lines.append("暂无输出内容")
340
+ header_lines.append("")
341
+
342
+ output_sections.append("\n".join(header_lines))
343
+
344
+ # 构建最终输出
345
+ if len(target_tasks) == 1:
346
+ message = output_sections[0].strip()
347
+ else:
348
+ message = "\n".join([
349
+ f"找到 {len(target_tasks)} 个匹配的任务:",
350
+ "=" * 80,
351
+ "\n".join(output_sections)
352
+ ]).strip()
353
+
354
+ # 构建结果内容
355
+ result_content = {
356
+ "action": "monitor",
357
+ "tasks": target_tasks,
358
+ "output_type": self.tool.output_type,
359
+ "lines_requested": self.tool.lines,
360
+ "task_count": len(target_tasks)
361
+ }
362
+
363
+ return ToolResult(
364
+ success=True,
365
+ message=message,
366
+ content=result_content
367
+ )
368
+
369
+ except Exception as e:
370
+ logger.error(f"Monitor action failed: {str(e)}")
371
+ return ToolResult(
372
+ success=False,
373
+ message=f"监控后台任务失败: {str(e)}",
374
+ content={"action": "monitor", "error": str(e), "task_count": 0}
375
+ )
376
+
377
+ def _handle_cleanup_action(self, conversation_id: str) -> ToolResult:
378
+ """处理清理任务的操作"""
379
+ try:
380
+ # 获取后台进程通知器
381
+ notifier = get_background_process_notifier()
382
+ bg_processes = get_background_processes()
383
+ if not bg_processes:
384
+ bg_processes = {}
385
+
386
+ # 收集要清理的任务
387
+ tasks_to_cleanup = []
388
+
389
+ with notifier._lock:
390
+ registrations = dict(notifier._registrations)
391
+ reported = set(notifier._reported)
392
+ pending_messages = dict(notifier._pending_messages)
393
+
394
+ current_time = time.time()
395
+
396
+ # 检查已报告的任务(已完成的注册任务)
397
+ for key in list(reported):
398
+ # 这些任务已经完成并报告,可以安全清理
399
+ if key in registrations:
400
+ reg = registrations[key]
401
+ if reg.conversation_id == conversation_id:
402
+ # 应用过滤条件
403
+ if self._should_cleanup_task(reg, None, current_time):
404
+ tasks_to_cleanup.append({
405
+ "type": "reported_registration",
406
+ "key": key,
407
+ "task_id": reg.task_id,
408
+ "pid": key[0],
409
+ "tool_name": reg.tool_name,
410
+ "status": "completed"
411
+ })
412
+
413
+ # 检查待处理消息中的已完成任务
414
+ if conversation_id in pending_messages:
415
+ messages_to_remove = []
416
+ for i, msg in enumerate(pending_messages[conversation_id]):
417
+ # 应用过滤条件
418
+ if self._should_cleanup_message(msg, current_time):
419
+ tasks_to_cleanup.append({
420
+ "type": "pending_message",
421
+ "index": i,
422
+ "task_id": msg.task_id,
423
+ "pid": msg.pid,
424
+ "tool_name": msg.tool_name,
425
+ "status": msg.status
426
+ })
427
+ messages_to_remove.append(i)
428
+
429
+ # 如果指定了特定的 task_ids,只清理这些任务
430
+ if self.tool.task_ids:
431
+ tasks_to_cleanup = [
432
+ task for task in tasks_to_cleanup
433
+ if task["task_id"] in self.tool.task_ids
434
+ ]
435
+
436
+ if not tasks_to_cleanup:
437
+ return ToolResult(
438
+ success=True,
439
+ message="没有找到符合条件的任务需要清理",
440
+ content={
441
+ "action": "cleanup",
442
+ "cleaned_count": 0,
443
+ "cleaned_tasks": [],
444
+ "remaining_active": self._count_active_tasks(notifier, conversation_id)
445
+ }
446
+ )
447
+
448
+ # 执行清理操作
449
+ cleaned_tasks = []
450
+ memory_freed = 0
451
+
452
+ with notifier._lock:
453
+ # 清理已报告的注册任务
454
+ for task in tasks_to_cleanup:
455
+ if task["type"] == "reported_registration":
456
+ key = task["key"]
457
+ if key in notifier._reported:
458
+ notifier._reported.remove(key)
459
+ if key in notifier._registrations:
460
+ del notifier._registrations[key]
461
+ # 更新 pid_to_key 映射
462
+ pid = key[0]
463
+ if pid in notifier._pid_to_key and notifier._pid_to_key[pid] == key:
464
+ del notifier._pid_to_key[pid]
465
+
466
+ cleaned_tasks.append(task)
467
+ memory_freed += self._estimate_task_memory_usage(task)
468
+
469
+ # 清理待处理消息
470
+ if conversation_id in notifier._pending_messages:
471
+ messages_to_remove = [
472
+ task["index"] for task in tasks_to_cleanup
473
+ if task["type"] == "pending_message"
474
+ ]
475
+ # 按索引降序排列,避免删除时索引变化
476
+ messages_to_remove.sort(reverse=True)
477
+
478
+ for index in messages_to_remove:
479
+ if index < len(notifier._pending_messages[conversation_id]):
480
+ removed_msg = notifier._pending_messages[conversation_id].pop(index)
481
+ memory_freed += self._estimate_message_memory_usage(removed_msg)
482
+
483
+ # 找到对应的任务并添加到清理列表
484
+ for task in tasks_to_cleanup:
485
+ if (task["type"] == "pending_message" and
486
+ task["index"] == index):
487
+ cleaned_tasks.append(task)
488
+ break
489
+
490
+ # 如果队列为空,删除整个条目
491
+ if not notifier._pending_messages[conversation_id]:
492
+ del notifier._pending_messages[conversation_id]
493
+
494
+ # 统计清理结果
495
+ completed_count = len([t for t in cleaned_tasks if t["status"] == "completed"])
496
+ failed_count = len([t for t in cleaned_tasks if t["status"] == "failed"])
497
+
498
+ # 构建输出消息
499
+ output_lines = [
500
+ "清理后台任务完成:",
501
+ f"- 已清理 {completed_count} 个已完成任务",
502
+ f"- 已清理 {failed_count} 个失败任务",
503
+ f"- 释放内存约 {self._format_memory_size(memory_freed)}",
504
+ "",
505
+ f"剩余活跃任务: {self._count_active_tasks(notifier, conversation_id)} 个正在运行"
506
+ ]
507
+
508
+ # 构建结果内容
509
+ result_content = {
510
+ "action": "cleanup",
511
+ "cleaned_count": len(cleaned_tasks),
512
+ "cleaned_tasks": cleaned_tasks,
513
+ "completed_count": completed_count,
514
+ "failed_count": failed_count,
515
+ "memory_freed_bytes": memory_freed,
516
+ "remaining_active": self._count_active_tasks(notifier, conversation_id)
517
+ }
518
+
519
+ return ToolResult(
520
+ success=True,
521
+ message="\n".join(output_lines),
522
+ content=result_content
523
+ )
524
+
525
+ except Exception as e:
526
+ logger.error(f"Cleanup action failed: {str(e)}")
527
+ return ToolResult(
528
+ success=False,
529
+ message=f"清理后台任务失败: {str(e)}",
530
+ content={"action": "cleanup", "error": str(e), "cleaned_count": 0}
531
+ )
532
+
533
+ def _handle_kill_action(self, conversation_id: str) -> ToolResult:
534
+ """处理终止任务的操作"""
535
+ try:
536
+ # 验证参数
537
+ if not self.tool.task_id and not self.tool.pid and not self.tool.pids and not self.tool.process_uniq_id:
538
+ return ToolResult(
539
+ success=False,
540
+ message="kill 操作必须提供 task_id、pid、pids 或 process_uniq_id 参数",
541
+ content={
542
+ "action": "kill",
543
+ "target_pids": [],
544
+ "verified_pids": [],
545
+ "kill_results": [],
546
+ "success_count": 0,
547
+ "failed_count": 0,
548
+ "force_mode": self.tool.force,
549
+ "kill_children": self.tool.kill_children,
550
+ "error": "missing_required_params"
551
+ }
552
+ )
553
+
554
+ # 收集要终止的进程ID
555
+ target_pids = []
556
+
557
+ if self.tool.pids:
558
+ # 批量操作模式
559
+ target_pids = list(self.tool.pids)
560
+ elif self.tool.pid:
561
+ # 单个PID模式
562
+ target_pids = [self.tool.pid]
563
+ elif self.tool.task_id:
564
+ # 通过task_id查找PID
565
+ found_pids = self._find_pids_by_task_id(conversation_id, self.tool.task_id)
566
+ if not found_pids:
567
+ return ToolResult(
568
+ success=False,
569
+ message=f"未找到 task_id={self.tool.task_id} 对应的进程",
570
+ content={
571
+ "action": "kill",
572
+ "target_pids": [],
573
+ "verified_pids": [],
574
+ "kill_results": [],
575
+ "success_count": 0,
576
+ "failed_count": 0,
577
+ "force_mode": self.tool.force,
578
+ "kill_children": self.tool.kill_children,
579
+ "error": "task_not_found"
580
+ }
581
+ )
582
+ target_pids = found_pids
583
+ elif self.tool.process_uniq_id:
584
+ # 通过 process_uniq_id 查找 PID
585
+ found_pids = self._find_pids_by_process_uniq_id(conversation_id, self.tool.process_uniq_id)
586
+ if not found_pids:
587
+ return ToolResult(
588
+ success=False,
589
+ message=f"未找到 process_uniq_id={self.tool.process_uniq_id} 对应的进程",
590
+ content={
591
+ "action": "kill",
592
+ "target_pids": [],
593
+ "verified_pids": [],
594
+ "kill_results": [],
595
+ "success_count": 0,
596
+ "failed_count": 0,
597
+ "force_mode": self.tool.force,
598
+ "kill_children": self.tool.kill_children,
599
+ "error": "process_not_found"
600
+ }
601
+ )
602
+ target_pids = found_pids
603
+
604
+ # 验证进程是否属于当前会话
605
+ verified_pids = []
606
+ for pid in target_pids:
607
+ if self._verify_process_ownership(conversation_id, pid):
608
+ verified_pids.append(pid)
609
+ else:
610
+ logger.warning(f"PID {pid} 不属于当前会话 {conversation_id},跳过")
611
+
612
+ if not verified_pids:
613
+ return ToolResult(
614
+ success=False,
615
+ message="没有找到属于当前会话的有效进程",
616
+ content={
617
+ "action": "kill",
618
+ "target_pids": target_pids,
619
+ "verified_pids": [],
620
+ "kill_results": [],
621
+ "success_count": 0,
622
+ "failed_count": 0,
623
+ "force_mode": self.tool.force,
624
+ "kill_children": self.tool.kill_children,
625
+ "error": "no_valid_processes"
626
+ }
627
+ )
628
+
629
+ # 执行终止操作
630
+ kill_results = []
631
+ success_count = 0
632
+ failed_count = 0
633
+
634
+ for pid in verified_pids:
635
+ result = self._kill_process(pid, self.tool.force, self.tool.kill_children)
636
+ kill_results.append(result)
637
+
638
+ if result["success"]:
639
+ success_count += 1
640
+ else:
641
+ failed_count += 1
642
+
643
+ # 构建输出消息
644
+ output_lines = ["终止后台任务:"]
645
+
646
+ for result in kill_results:
647
+ status_icon = "✅" if result["success"] else "❌"
648
+ task_info = f"任务 {result.get('task_id', 'N/A')} (PID: {result['pid']})"
649
+
650
+ if result["success"]:
651
+ output_lines.append(f"{status_icon} {task_info} 已成功终止")
652
+ else:
653
+ output_lines.append(f"{status_icon} {task_info} 终止失败: {result['error']}")
654
+
655
+ output_lines.append("")
656
+ output_lines.append(f"总计: {success_count} 个任务成功终止, {failed_count} 个任务失败")
657
+
658
+ # 构建结果内容
659
+ result_content = {
660
+ "action": "kill",
661
+ "target_pids": target_pids,
662
+ "verified_pids": verified_pids,
663
+ "kill_results": kill_results,
664
+ "success_count": success_count,
665
+ "failed_count": failed_count,
666
+ "force_mode": self.tool.force,
667
+ "kill_children": self.tool.kill_children
668
+ }
669
+
670
+ overall_success = success_count > 0
671
+
672
+ return ToolResult(
673
+ success=overall_success,
674
+ message="\n".join(output_lines),
675
+ content=result_content
676
+ )
677
+
678
+ except Exception as e:
679
+ logger.error(f"Kill action failed: {str(e)}")
680
+ return ToolResult(
681
+ success=False,
682
+ message=f"终止后台任务失败: {str(e)}"
683
+ )
684
+
685
+ # 以下是辅助方法,从原来的各个解析器中整合而来
686
+
687
+ def _format_duration(self, seconds: float) -> str:
688
+ """格式化持续时间为人类可读格式"""
689
+ if seconds < 60:
690
+ return f"{int(seconds)}s"
691
+ elif seconds < 3600:
692
+ minutes = int(seconds // 60)
693
+ secs = int(seconds % 60)
694
+ return f"{minutes}m {secs}s"
695
+ else:
696
+ hours = int(seconds // 3600)
697
+ minutes = int((seconds % 3600) // 60)
698
+ return f"{hours}h {minutes}m"
699
+
700
+ def _find_tasks_by_task_id(self, notifier, conversation_id: str, task_id: str) -> List[Dict[str, Any]]:
701
+ """通过 task_id 查找任务"""
702
+ tasks = []
703
+
704
+ with notifier._lock:
705
+ registrations = dict(notifier._registrations)
706
+ pending_messages = dict(notifier._pending_messages)
707
+
708
+ # 在注册的任务中查找
709
+ for key, reg in registrations.items():
710
+ if reg.conversation_id == conversation_id and reg.task_id == task_id:
711
+ pid, start_time = key
712
+ tasks.append({
713
+ "task_id": reg.task_id,
714
+ "pid": pid,
715
+ "tool_name": reg.tool_name,
716
+ "command": reg.command,
717
+ "cwd": reg.cwd,
718
+ "agent_name": reg.agent_name,
719
+ "task": reg.task,
720
+ "start_time": reg.start_time
721
+ })
722
+
723
+ # 在待处理消息中查找
724
+ if conversation_id in pending_messages:
725
+ for msg in pending_messages[conversation_id]:
726
+ if msg.task_id == task_id:
727
+ tasks.append({
728
+ "task_id": msg.task_id,
729
+ "pid": msg.pid,
730
+ "tool_name": msg.tool_name,
731
+ "command": msg.command,
732
+ "cwd": msg.cwd,
733
+ "agent_name": msg.agent_name,
734
+ "task": msg.task,
735
+ "start_time": msg.completed_at - (msg.duration_sec or 0),
736
+ "completed": True
737
+ })
738
+
739
+ return tasks
740
+
741
+ def _find_tasks_by_process_uniq_id(self, notifier, conversation_id: str, process_uniq_id: str) -> List[Dict[str, Any]]:
742
+ """通过 process_uniq_id 查找任务"""
743
+ tasks = []
744
+ bg_processes = get_background_processes()
745
+ if not bg_processes:
746
+ bg_processes = {}
747
+
748
+ # 在后台进程中查找匹配的 process_uniq_id
749
+ for pid, process_info in bg_processes.items():
750
+ if process_info.get("process_uniq_id") == process_uniq_id:
751
+ # 找到匹配的进程,现在查找对应的任务信息
752
+ with notifier._lock:
753
+ registrations = dict(notifier._registrations)
754
+ pending_messages = dict(notifier._pending_messages)
755
+
756
+ # 在注册的任务中查找
757
+ for key, reg in registrations.items():
758
+ reg_pid, start_time = key
759
+ if reg.conversation_id == conversation_id and reg_pid == pid:
760
+ tasks.append({
761
+ "task_id": reg.task_id,
762
+ "pid": reg_pid,
763
+ "process_uniq_id": process_uniq_id,
764
+ "tool_name": reg.tool_name,
765
+ "command": reg.command,
766
+ "cwd": reg.cwd,
767
+ "agent_name": reg.agent_name,
768
+ "task": reg.task,
769
+ "start_time": reg.start_time
770
+ })
771
+
772
+ # 在待处理消息中查找
773
+ if conversation_id in pending_messages:
774
+ for msg in pending_messages[conversation_id]:
775
+ if msg.pid == pid:
776
+ tasks.append({
777
+ "task_id": msg.task_id,
778
+ "pid": msg.pid,
779
+ "process_uniq_id": process_uniq_id,
780
+ "tool_name": msg.tool_name,
781
+ "command": msg.command,
782
+ "cwd": msg.cwd,
783
+ "agent_name": msg.agent_name,
784
+ "task": msg.task,
785
+ "start_time": msg.completed_at - (msg.duration_sec or 0),
786
+ "completed": True
787
+ })
788
+
789
+ # 如果没有找到注册任务,创建一个默认任务
790
+ if not tasks:
791
+ tasks.append({
792
+ "task_id": f"bg_{pid}", # 生成一个默认的 task_id
793
+ "pid": pid,
794
+ "process_uniq_id": process_uniq_id,
795
+ "tool_name": "background_command",
796
+ "command": process_info.get("command", "Unknown"),
797
+ "cwd": process_info.get("cwd"),
798
+ "agent_name": "system",
799
+ "task": process_info.get("command", "Unknown"),
800
+ "start_time": process_info.get("start_time")
801
+ })
802
+
803
+ break
804
+
805
+ return tasks
806
+
807
+ def _find_pids_by_process_uniq_id(self, conversation_id: str, process_uniq_id: str) -> List[int]:
808
+ """通过 process_uniq_id 查找对应的 PID"""
809
+ pids = []
810
+ bg_processes = get_background_processes()
811
+ if not bg_processes:
812
+ return pids
813
+
814
+ # 在后台进程中查找匹配的 process_uniq_id
815
+ for pid, process_info in bg_processes.items():
816
+ if process_info.get("process_uniq_id") == process_uniq_id:
817
+ # 验证该进程属于当前会话
818
+ if self._verify_process_ownership(conversation_id, pid):
819
+ pids.append(pid)
820
+ break
821
+
822
+ return pids
823
+
824
+ def _find_tasks_by_pid(self, notifier, conversation_id: str, pid: int) -> List[Dict[str, Any]]:
825
+ """通过 pid 查找任务"""
826
+ tasks = []
827
+
828
+ with notifier._lock:
829
+ registrations = dict(notifier._registrations)
830
+ pending_messages = dict(notifier._pending_messages)
831
+
832
+ # 在注册的任务中查找
833
+ for key, reg in registrations.items():
834
+ reg_pid, start_time = key
835
+ if reg.conversation_id == conversation_id and reg_pid == pid:
836
+ tasks.append({
837
+ "task_id": reg.task_id,
838
+ "pid": reg_pid,
839
+ "tool_name": reg.tool_name,
840
+ "command": reg.command,
841
+ "cwd": reg.cwd,
842
+ "agent_name": reg.agent_name,
843
+ "task": reg.task,
844
+ "start_time": reg.start_time
845
+ })
846
+
847
+ # 在待处理消息中查找
848
+ if conversation_id in pending_messages:
849
+ for msg in pending_messages[conversation_id]:
850
+ if msg.pid == pid:
851
+ tasks.append({
852
+ "task_id": msg.task_id,
853
+ "pid": msg.pid,
854
+ "tool_name": msg.tool_name,
855
+ "command": msg.command,
856
+ "cwd": msg.cwd,
857
+ "agent_name": msg.agent_name,
858
+ "task": msg.task,
859
+ "start_time": msg.completed_at - (msg.duration_sec or 0),
860
+ "completed": True
861
+ })
862
+
863
+ # 如果在注册中没有找到,尝试从 bg_processes 中查找
864
+ if not tasks:
865
+ bg_processes = get_background_processes()
866
+ if pid in bg_processes:
867
+ process_info = bg_processes[pid]
868
+ tasks.append({
869
+ "task_id": f"bg_{pid}", # 生成一个默认的 task_id
870
+ "pid": pid,
871
+ "tool_name": "background_command",
872
+ "command": process_info.get("command", "Unknown"),
873
+ "cwd": process_info.get("cwd"),
874
+ "agent_name": "system",
875
+ "task": process_info.get("command", "Unknown"),
876
+ "start_time": process_info.get("start_time"),
877
+ "process_uniq_id": process_info.get("process_uniq_id")
878
+ })
879
+
880
+ return tasks
881
+
882
+ def _get_process_output(self, process_info: Dict[str, Any], pid: int) -> Tuple[Optional[str], Optional[str]]:
883
+ """从文件系统中获取进程的 stdout 和 stderr 输出,使用通知器的实现"""
884
+ try:
885
+ # 获取后台进程通知器实例
886
+ notifier = get_background_process_notifier()
887
+
888
+ # 使用通知器的方法来读取输出
889
+ return notifier._read_process_output_tails(process_info)
890
+
891
+ except Exception as e:
892
+ logger.error(f"Error getting process output for PID {pid}: {str(e)}")
893
+ return None, None
894
+
895
+ def _format_output_lines(self, content: str, max_lines: int, output_type: str) -> List[str]:
896
+ """格式化输出内容为指定行数"""
897
+ if not content:
898
+ return []
899
+
900
+ lines = content.splitlines()
901
+
902
+ # 取最后 max_lines 行
903
+ if len(lines) > max_lines:
904
+ lines = lines[-max_lines:]
905
+ truncated = True
906
+ else:
907
+ truncated = False
908
+
909
+ result = [f"=== {output_type} (最后 {len(lines)} 行) ==="]
910
+
911
+ if truncated:
912
+ result.append(f"... (已截断,仅显示最后 {max_lines} 行)")
913
+
914
+ result.extend(lines)
915
+
916
+ return result
917
+
918
+ def _should_cleanup_task(self, registration, process_info: Optional[Dict], current_time: float) -> bool:
919
+ """判断注册任务是否应该被清理"""
920
+ # 状态过滤
921
+ if self.tool.status_filter:
922
+ # 对于已报告的任务,假设它们已完成
923
+ if self.tool.status_filter not in ["completed"]:
924
+ return False
925
+
926
+ # 时间过滤
927
+ if self.tool.older_than_minutes:
928
+ task_age_minutes = (current_time - registration.start_time) / 60
929
+ if task_age_minutes < self.tool.older_than_minutes:
930
+ return False
931
+
932
+ return True
933
+
934
+ def _should_cleanup_message(self, message, current_time: float) -> bool:
935
+ """判断待处理消息是否应该被清理"""
936
+ # 状态过滤
937
+ if self.tool.status_filter:
938
+ if message.status != self.tool.status_filter:
939
+ return False
940
+
941
+ # 时间过滤
942
+ if self.tool.older_than_minutes:
943
+ message_age_minutes = (current_time - message.completed_at) / 60
944
+ if message_age_minutes < self.tool.older_than_minutes:
945
+ return False
946
+
947
+ return True
948
+
949
+ def _count_active_tasks(self, notifier, conversation_id: str) -> int:
950
+ """统计当前活跃的任务数量"""
951
+ count = 0
952
+ with notifier._lock:
953
+ for reg in notifier._registrations.values():
954
+ if reg.conversation_id == conversation_id:
955
+ count += 1
956
+ return count
957
+
958
+ def _estimate_task_memory_usage(self, task: Dict[str, Any]) -> int:
959
+ """估算任务占用的内存大小(字节)"""
960
+ # 简单估算:基于任务信息的字符串长度
961
+ base_size = 1024 # 基础对象开销
962
+
963
+ # 字符串字段的估算
964
+ string_fields = ["task_id", "tool_name", "status"]
965
+ for field in string_fields:
966
+ if field in task and task[field]:
967
+ base_size += len(str(task[field])) * 2 # Unicode 字符
968
+
969
+ return base_size
970
+
971
+ def _estimate_message_memory_usage(self, message) -> int:
972
+ """估算消息对象占用的内存大小(字节)"""
973
+ base_size = 2048 # 基础对象开销
974
+
975
+ # 估算字符串字段
976
+ string_fields = ["task_id", "tool_name", "command", "cwd", "agent_name", "task", "output_tail", "error_tail"]
977
+ for field in string_fields:
978
+ value = getattr(message, field, None)
979
+ if value:
980
+ base_size += len(str(value)) * 2
981
+
982
+ return base_size
983
+
984
+ def _format_memory_size(self, bytes_size: int) -> str:
985
+ """格式化内存大小为人类可读格式"""
986
+ if bytes_size < 1024:
987
+ return f"{bytes_size}B"
988
+ elif bytes_size < 1024 * 1024:
989
+ return f"{bytes_size / 1024:.1f}KB"
990
+ else:
991
+ return f"{bytes_size / (1024 * 1024):.1f}MB"
992
+
993
+ def _find_pids_by_task_id(self, conversation_id: str, task_id: str) -> List[int]:
994
+ """通过 task_id 查找对应的 PID"""
995
+ pids = []
996
+ notifier = get_background_process_notifier()
997
+
998
+ with notifier._lock:
999
+ registrations = dict(notifier._registrations)
1000
+ pending_messages = dict(notifier._pending_messages)
1001
+
1002
+ # 在注册的任务中查找
1003
+ for key, reg in registrations.items():
1004
+ if reg.conversation_id == conversation_id and reg.task_id == task_id:
1005
+ pid, start_time = key
1006
+ pids.append(pid)
1007
+
1008
+ # 在待处理消息中查找(已完成的任务)
1009
+ if conversation_id in pending_messages:
1010
+ for msg in pending_messages[conversation_id]:
1011
+ if msg.task_id == task_id:
1012
+ pids.append(msg.pid)
1013
+
1014
+ return pids
1015
+
1016
+ def _verify_process_ownership(self, conversation_id: str, pid: int) -> bool:
1017
+ """验证进程是否属于当前会话"""
1018
+ notifier = get_background_process_notifier()
1019
+
1020
+ with notifier._lock:
1021
+ registrations = dict(notifier._registrations)
1022
+ pending_messages = dict(notifier._pending_messages)
1023
+
1024
+ # 检查注册的任务
1025
+ for key, reg in registrations.items():
1026
+ reg_pid, start_time = key
1027
+ if reg.conversation_id == conversation_id and reg_pid == pid:
1028
+ return True
1029
+
1030
+ # 检查待处理消息
1031
+ if conversation_id in pending_messages:
1032
+ for msg in pending_messages[conversation_id]:
1033
+ if msg.pid == pid:
1034
+ return True
1035
+
1036
+ return False
1037
+
1038
+ def _kill_process(self, pid: int, force: bool, kill_children: bool) -> Dict[str, Any]:
1039
+ """终止指定的进程"""
1040
+ result = {
1041
+ "pid": pid,
1042
+ "success": False,
1043
+ "error": None,
1044
+ "task_id": None,
1045
+ "children_killed": []
1046
+ }
1047
+
1048
+ try:
1049
+ # 获取任务信息
1050
+ task_id = self._get_task_id_by_pid(pid)
1051
+ result["task_id"] = task_id
1052
+
1053
+ # 检查进程是否存在
1054
+ if not psutil.pid_exists(pid):
1055
+ result["error"] = "进程不存在"
1056
+ return result
1057
+
1058
+ try:
1059
+ process = psutil.Process(pid)
1060
+ except psutil.NoSuchProcess:
1061
+ result["error"] = "进程不存在"
1062
+ return result
1063
+
1064
+ # 获取子进程(如果需要)
1065
+ children = []
1066
+ if kill_children:
1067
+ try:
1068
+ children = process.children(recursive=True)
1069
+ except (psutil.NoSuchProcess, psutil.AccessDenied):
1070
+ pass
1071
+
1072
+ # 选择终止信号
1073
+ if force:
1074
+ sig = signal.SIGKILL # 强制终止
1075
+ else:
1076
+ sig = signal.SIGTERM # 优雅终止
1077
+
1078
+ # 终止子进程
1079
+ children_killed = []
1080
+ for child in children:
1081
+ try:
1082
+ child.send_signal(sig)
1083
+ children_killed.append(child.pid)
1084
+ logger.info(f"Sent signal {sig} to child process {child.pid}")
1085
+ except (psutil.NoSuchProcess, psutil.AccessDenied) as e:
1086
+ logger.warning(f"Failed to kill child process {child.pid}: {str(e)}")
1087
+
1088
+ result["children_killed"] = children_killed
1089
+
1090
+ # 终止主进程
1091
+ try:
1092
+ process.send_signal(sig)
1093
+ logger.info(f"Sent signal {sig} to process {pid}")
1094
+
1095
+ # 等待进程终止(最多等待5秒)
1096
+ if not force:
1097
+ try:
1098
+ process.wait(timeout=5)
1099
+ except psutil.TimeoutExpired:
1100
+ # 如果优雅终止超时,使用强制终止
1101
+ logger.warning(f"Process {pid} did not terminate gracefully, forcing kill")
1102
+ process.send_signal(signal.SIGKILL)
1103
+ process.wait(timeout=2)
1104
+
1105
+ result["success"] = True
1106
+
1107
+ except psutil.NoSuchProcess:
1108
+ # 进程已经不存在,认为成功
1109
+ result["success"] = True
1110
+ except psutil.AccessDenied:
1111
+ result["error"] = "权限不足,无法终止进程"
1112
+ except Exception as e:
1113
+ result["error"] = f"终止进程时发生错误: {str(e)}"
1114
+
1115
+ except Exception as e:
1116
+ result["error"] = f"处理进程时发生错误: {str(e)}"
1117
+ logger.error(f"Error killing process {pid}: {str(e)}")
1118
+
1119
+ return result
1120
+
1121
+ def _get_task_id_by_pid(self, pid: int) -> Optional[str]:
1122
+ """通过 PID 获取 task_id"""
1123
+ notifier = get_background_process_notifier()
1124
+
1125
+ with notifier._lock:
1126
+ registrations = dict(notifier._registrations)
1127
+ pending_messages = dict(notifier._pending_messages)
1128
+
1129
+ # 在注册的任务中查找
1130
+ for key, reg in registrations.items():
1131
+ reg_pid, start_time = key
1132
+ if reg_pid == pid:
1133
+ return reg.task_id
1134
+
1135
+ # 在待处理消息中查找
1136
+ for conv_messages in pending_messages.values():
1137
+ for msg in conv_messages:
1138
+ if msg.pid == pid:
1139
+ return msg.task_id
1140
+
1141
+ return None
1142
+
1143
+ def _classify_tool_name(self, tool_name: Optional[str]) -> str:
1144
+ """将不同风格的工具名称归一化为类别: 'command' 或 'subagent'
1145
+
1146
+ 兼容的名称示例:
1147
+ - 命令类: 'ExecuteCommandTool'
1148
+ - 子代理类:'RunNamedSubagentsTool'
1149
+ """
1150
+ if not tool_name:
1151
+ return 'command'
1152
+ name = str(tool_name).strip().lower()
1153
+ # 子代理优先匹配,避免 "subagent" 与 "command" 同时出现时误判
1154
+ if 'subagent' in name or name in {'run_named_subagents', 'runnamedsubagentstool'}:
1155
+ return 'subagent'
1156
+ if (
1157
+ name in {'execute_command', 'background_command', 'command'}
1158
+ or 'executecommand' in name
1159
+ or ('command' in name and 'subagent' not in name)
1160
+ ):
1161
+ return 'command'
1162
+ # 未知的名称默认按命令类处理
1163
+ return 'command'
1164
+
1165
+
1166
+
1167
+