jarvis-ai-assistant 0.7.8__py3-none-any.whl → 1.0.2__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.
Files changed (279) hide show
  1. jarvis/__init__.py +1 -1
  2. jarvis/jarvis_agent/__init__.py +567 -222
  3. jarvis/jarvis_agent/agent_manager.py +19 -12
  4. jarvis/jarvis_agent/builtin_input_handler.py +79 -11
  5. jarvis/jarvis_agent/config_editor.py +7 -2
  6. jarvis/jarvis_agent/event_bus.py +24 -13
  7. jarvis/jarvis_agent/events.py +19 -1
  8. jarvis/jarvis_agent/file_context_handler.py +67 -64
  9. jarvis/jarvis_agent/file_methodology_manager.py +38 -24
  10. jarvis/jarvis_agent/jarvis.py +186 -114
  11. jarvis/jarvis_agent/language_extractors/__init__.py +8 -1
  12. jarvis/jarvis_agent/language_extractors/c_extractor.py +7 -4
  13. jarvis/jarvis_agent/language_extractors/cpp_extractor.py +9 -4
  14. jarvis/jarvis_agent/language_extractors/go_extractor.py +7 -4
  15. jarvis/jarvis_agent/language_extractors/java_extractor.py +27 -20
  16. jarvis/jarvis_agent/language_extractors/javascript_extractor.py +22 -17
  17. jarvis/jarvis_agent/language_extractors/python_extractor.py +7 -4
  18. jarvis/jarvis_agent/language_extractors/rust_extractor.py +7 -4
  19. jarvis/jarvis_agent/language_extractors/typescript_extractor.py +22 -17
  20. jarvis/jarvis_agent/language_support_info.py +250 -219
  21. jarvis/jarvis_agent/main.py +19 -23
  22. jarvis/jarvis_agent/memory_manager.py +9 -6
  23. jarvis/jarvis_agent/methodology_share_manager.py +21 -15
  24. jarvis/jarvis_agent/output_handler.py +4 -2
  25. jarvis/jarvis_agent/prompt_builder.py +7 -6
  26. jarvis/jarvis_agent/prompt_manager.py +113 -8
  27. jarvis/jarvis_agent/prompts.py +317 -85
  28. jarvis/jarvis_agent/protocols.py +5 -2
  29. jarvis/jarvis_agent/run_loop.py +192 -32
  30. jarvis/jarvis_agent/session_manager.py +7 -3
  31. jarvis/jarvis_agent/share_manager.py +23 -13
  32. jarvis/jarvis_agent/shell_input_handler.py +12 -8
  33. jarvis/jarvis_agent/stdio_redirect.py +25 -26
  34. jarvis/jarvis_agent/task_analyzer.py +29 -23
  35. jarvis/jarvis_agent/task_list.py +869 -0
  36. jarvis/jarvis_agent/task_manager.py +26 -23
  37. jarvis/jarvis_agent/tool_executor.py +6 -5
  38. jarvis/jarvis_agent/tool_share_manager.py +24 -14
  39. jarvis/jarvis_agent/user_interaction.py +3 -3
  40. jarvis/jarvis_agent/utils.py +9 -1
  41. jarvis/jarvis_agent/web_bridge.py +37 -17
  42. jarvis/jarvis_agent/web_output_sink.py +5 -2
  43. jarvis/jarvis_agent/web_server.py +165 -36
  44. jarvis/jarvis_c2rust/__init__.py +1 -1
  45. jarvis/jarvis_c2rust/cli.py +260 -141
  46. jarvis/jarvis_c2rust/collector.py +37 -18
  47. jarvis/jarvis_c2rust/constants.py +60 -0
  48. jarvis/jarvis_c2rust/library_replacer.py +242 -1010
  49. jarvis/jarvis_c2rust/library_replacer_checkpoint.py +133 -0
  50. jarvis/jarvis_c2rust/library_replacer_llm.py +287 -0
  51. jarvis/jarvis_c2rust/library_replacer_loader.py +191 -0
  52. jarvis/jarvis_c2rust/library_replacer_output.py +134 -0
  53. jarvis/jarvis_c2rust/library_replacer_prompts.py +124 -0
  54. jarvis/jarvis_c2rust/library_replacer_utils.py +188 -0
  55. jarvis/jarvis_c2rust/llm_module_agent.py +98 -1044
  56. jarvis/jarvis_c2rust/llm_module_agent_apply.py +170 -0
  57. jarvis/jarvis_c2rust/llm_module_agent_executor.py +288 -0
  58. jarvis/jarvis_c2rust/llm_module_agent_loader.py +170 -0
  59. jarvis/jarvis_c2rust/llm_module_agent_prompts.py +268 -0
  60. jarvis/jarvis_c2rust/llm_module_agent_types.py +57 -0
  61. jarvis/jarvis_c2rust/llm_module_agent_utils.py +150 -0
  62. jarvis/jarvis_c2rust/llm_module_agent_validator.py +119 -0
  63. jarvis/jarvis_c2rust/loaders.py +28 -10
  64. jarvis/jarvis_c2rust/models.py +5 -2
  65. jarvis/jarvis_c2rust/optimizer.py +192 -1974
  66. jarvis/jarvis_c2rust/optimizer_build_fix.py +286 -0
  67. jarvis/jarvis_c2rust/optimizer_clippy.py +766 -0
  68. jarvis/jarvis_c2rust/optimizer_config.py +49 -0
  69. jarvis/jarvis_c2rust/optimizer_docs.py +183 -0
  70. jarvis/jarvis_c2rust/optimizer_options.py +48 -0
  71. jarvis/jarvis_c2rust/optimizer_progress.py +469 -0
  72. jarvis/jarvis_c2rust/optimizer_report.py +52 -0
  73. jarvis/jarvis_c2rust/optimizer_unsafe.py +309 -0
  74. jarvis/jarvis_c2rust/optimizer_utils.py +469 -0
  75. jarvis/jarvis_c2rust/optimizer_visibility.py +185 -0
  76. jarvis/jarvis_c2rust/scanner.py +229 -166
  77. jarvis/jarvis_c2rust/transpiler.py +531 -2732
  78. jarvis/jarvis_c2rust/transpiler_agents.py +503 -0
  79. jarvis/jarvis_c2rust/transpiler_build.py +1294 -0
  80. jarvis/jarvis_c2rust/transpiler_codegen.py +204 -0
  81. jarvis/jarvis_c2rust/transpiler_compile.py +146 -0
  82. jarvis/jarvis_c2rust/transpiler_config.py +178 -0
  83. jarvis/jarvis_c2rust/transpiler_context.py +122 -0
  84. jarvis/jarvis_c2rust/transpiler_executor.py +516 -0
  85. jarvis/jarvis_c2rust/transpiler_generation.py +278 -0
  86. jarvis/jarvis_c2rust/transpiler_git.py +163 -0
  87. jarvis/jarvis_c2rust/transpiler_mod_utils.py +225 -0
  88. jarvis/jarvis_c2rust/transpiler_modules.py +336 -0
  89. jarvis/jarvis_c2rust/transpiler_planning.py +394 -0
  90. jarvis/jarvis_c2rust/transpiler_review.py +1196 -0
  91. jarvis/jarvis_c2rust/transpiler_symbols.py +176 -0
  92. jarvis/jarvis_c2rust/utils.py +269 -79
  93. jarvis/jarvis_code_agent/after_change.py +233 -0
  94. jarvis/jarvis_code_agent/build_validation_config.py +37 -30
  95. jarvis/jarvis_code_agent/builtin_rules.py +68 -0
  96. jarvis/jarvis_code_agent/code_agent.py +976 -1517
  97. jarvis/jarvis_code_agent/code_agent_build.py +227 -0
  98. jarvis/jarvis_code_agent/code_agent_diff.py +246 -0
  99. jarvis/jarvis_code_agent/code_agent_git.py +525 -0
  100. jarvis/jarvis_code_agent/code_agent_impact.py +177 -0
  101. jarvis/jarvis_code_agent/code_agent_lint.py +283 -0
  102. jarvis/jarvis_code_agent/code_agent_llm.py +159 -0
  103. jarvis/jarvis_code_agent/code_agent_postprocess.py +105 -0
  104. jarvis/jarvis_code_agent/code_agent_prompts.py +46 -0
  105. jarvis/jarvis_code_agent/code_agent_rules.py +305 -0
  106. jarvis/jarvis_code_agent/code_analyzer/__init__.py +52 -48
  107. jarvis/jarvis_code_agent/code_analyzer/base_language.py +12 -10
  108. jarvis/jarvis_code_agent/code_analyzer/build_validator/__init__.py +12 -11
  109. jarvis/jarvis_code_agent/code_analyzer/build_validator/base.py +16 -12
  110. jarvis/jarvis_code_agent/code_analyzer/build_validator/cmake.py +26 -17
  111. jarvis/jarvis_code_agent/code_analyzer/build_validator/detector.py +558 -104
  112. jarvis/jarvis_code_agent/code_analyzer/build_validator/fallback.py +27 -16
  113. jarvis/jarvis_code_agent/code_analyzer/build_validator/go.py +22 -18
  114. jarvis/jarvis_code_agent/code_analyzer/build_validator/java_gradle.py +21 -16
  115. jarvis/jarvis_code_agent/code_analyzer/build_validator/java_maven.py +20 -16
  116. jarvis/jarvis_code_agent/code_analyzer/build_validator/makefile.py +27 -16
  117. jarvis/jarvis_code_agent/code_analyzer/build_validator/nodejs.py +47 -23
  118. jarvis/jarvis_code_agent/code_analyzer/build_validator/python.py +71 -37
  119. jarvis/jarvis_code_agent/code_analyzer/build_validator/rust.py +162 -35
  120. jarvis/jarvis_code_agent/code_analyzer/build_validator/validator.py +111 -57
  121. jarvis/jarvis_code_agent/code_analyzer/build_validator.py +18 -12
  122. jarvis/jarvis_code_agent/code_analyzer/context_manager.py +185 -183
  123. jarvis/jarvis_code_agent/code_analyzer/context_recommender.py +2 -1
  124. jarvis/jarvis_code_agent/code_analyzer/dependency_analyzer.py +24 -15
  125. jarvis/jarvis_code_agent/code_analyzer/file_ignore.py +227 -141
  126. jarvis/jarvis_code_agent/code_analyzer/impact_analyzer.py +321 -247
  127. jarvis/jarvis_code_agent/code_analyzer/language_registry.py +37 -29
  128. jarvis/jarvis_code_agent/code_analyzer/language_support.py +21 -13
  129. jarvis/jarvis_code_agent/code_analyzer/languages/__init__.py +15 -9
  130. jarvis/jarvis_code_agent/code_analyzer/languages/c_cpp_language.py +75 -45
  131. jarvis/jarvis_code_agent/code_analyzer/languages/go_language.py +87 -52
  132. jarvis/jarvis_code_agent/code_analyzer/languages/java_language.py +84 -51
  133. jarvis/jarvis_code_agent/code_analyzer/languages/javascript_language.py +94 -64
  134. jarvis/jarvis_code_agent/code_analyzer/languages/python_language.py +109 -71
  135. jarvis/jarvis_code_agent/code_analyzer/languages/rust_language.py +97 -63
  136. jarvis/jarvis_code_agent/code_analyzer/languages/typescript_language.py +103 -69
  137. jarvis/jarvis_code_agent/code_analyzer/llm_context_recommender.py +271 -268
  138. jarvis/jarvis_code_agent/code_analyzer/symbol_extractor.py +76 -64
  139. jarvis/jarvis_code_agent/code_analyzer/tree_sitter_extractor.py +92 -19
  140. jarvis/jarvis_code_agent/diff_visualizer.py +998 -0
  141. jarvis/jarvis_code_agent/lint.py +223 -524
  142. jarvis/jarvis_code_agent/rule_share_manager.py +158 -0
  143. jarvis/jarvis_code_agent/rules/clean_code.md +144 -0
  144. jarvis/jarvis_code_agent/rules/code_review.md +115 -0
  145. jarvis/jarvis_code_agent/rules/documentation.md +165 -0
  146. jarvis/jarvis_code_agent/rules/generate_rules.md +52 -0
  147. jarvis/jarvis_code_agent/rules/performance.md +158 -0
  148. jarvis/jarvis_code_agent/rules/refactoring.md +139 -0
  149. jarvis/jarvis_code_agent/rules/security.md +160 -0
  150. jarvis/jarvis_code_agent/rules/tdd.md +78 -0
  151. jarvis/jarvis_code_agent/test_rules/cpp_test.md +118 -0
  152. jarvis/jarvis_code_agent/test_rules/go_test.md +98 -0
  153. jarvis/jarvis_code_agent/test_rules/java_test.md +99 -0
  154. jarvis/jarvis_code_agent/test_rules/javascript_test.md +113 -0
  155. jarvis/jarvis_code_agent/test_rules/php_test.md +117 -0
  156. jarvis/jarvis_code_agent/test_rules/python_test.md +91 -0
  157. jarvis/jarvis_code_agent/test_rules/ruby_test.md +102 -0
  158. jarvis/jarvis_code_agent/test_rules/rust_test.md +86 -0
  159. jarvis/jarvis_code_agent/utils.py +36 -26
  160. jarvis/jarvis_code_analysis/checklists/loader.py +21 -21
  161. jarvis/jarvis_code_analysis/code_review.py +64 -33
  162. jarvis/jarvis_data/config_schema.json +285 -192
  163. jarvis/jarvis_git_squash/main.py +8 -6
  164. jarvis/jarvis_git_utils/git_commiter.py +53 -76
  165. jarvis/jarvis_mcp/__init__.py +5 -2
  166. jarvis/jarvis_mcp/sse_mcp_client.py +40 -30
  167. jarvis/jarvis_mcp/stdio_mcp_client.py +27 -19
  168. jarvis/jarvis_mcp/streamable_mcp_client.py +35 -26
  169. jarvis/jarvis_memory_organizer/memory_organizer.py +78 -55
  170. jarvis/jarvis_methodology/main.py +48 -39
  171. jarvis/jarvis_multi_agent/__init__.py +56 -23
  172. jarvis/jarvis_multi_agent/main.py +15 -18
  173. jarvis/jarvis_platform/base.py +179 -111
  174. jarvis/jarvis_platform/human.py +27 -16
  175. jarvis/jarvis_platform/kimi.py +52 -45
  176. jarvis/jarvis_platform/openai.py +101 -40
  177. jarvis/jarvis_platform/registry.py +51 -33
  178. jarvis/jarvis_platform/tongyi.py +68 -38
  179. jarvis/jarvis_platform/yuanbao.py +59 -43
  180. jarvis/jarvis_platform_manager/main.py +68 -76
  181. jarvis/jarvis_platform_manager/service.py +24 -14
  182. jarvis/jarvis_rag/README_CONFIG.md +314 -0
  183. jarvis/jarvis_rag/README_DYNAMIC_LOADING.md +311 -0
  184. jarvis/jarvis_rag/README_ONLINE_MODELS.md +230 -0
  185. jarvis/jarvis_rag/__init__.py +57 -4
  186. jarvis/jarvis_rag/cache.py +3 -1
  187. jarvis/jarvis_rag/cli.py +48 -68
  188. jarvis/jarvis_rag/embedding_interface.py +39 -0
  189. jarvis/jarvis_rag/embedding_manager.py +7 -230
  190. jarvis/jarvis_rag/embeddings/__init__.py +41 -0
  191. jarvis/jarvis_rag/embeddings/base.py +114 -0
  192. jarvis/jarvis_rag/embeddings/cohere.py +66 -0
  193. jarvis/jarvis_rag/embeddings/edgefn.py +117 -0
  194. jarvis/jarvis_rag/embeddings/local.py +260 -0
  195. jarvis/jarvis_rag/embeddings/openai.py +62 -0
  196. jarvis/jarvis_rag/embeddings/registry.py +293 -0
  197. jarvis/jarvis_rag/llm_interface.py +8 -6
  198. jarvis/jarvis_rag/query_rewriter.py +8 -9
  199. jarvis/jarvis_rag/rag_pipeline.py +61 -52
  200. jarvis/jarvis_rag/reranker.py +7 -75
  201. jarvis/jarvis_rag/reranker_interface.py +32 -0
  202. jarvis/jarvis_rag/rerankers/__init__.py +41 -0
  203. jarvis/jarvis_rag/rerankers/base.py +109 -0
  204. jarvis/jarvis_rag/rerankers/cohere.py +67 -0
  205. jarvis/jarvis_rag/rerankers/edgefn.py +140 -0
  206. jarvis/jarvis_rag/rerankers/jina.py +79 -0
  207. jarvis/jarvis_rag/rerankers/local.py +89 -0
  208. jarvis/jarvis_rag/rerankers/registry.py +293 -0
  209. jarvis/jarvis_rag/retriever.py +58 -43
  210. jarvis/jarvis_sec/__init__.py +66 -141
  211. jarvis/jarvis_sec/agents.py +21 -17
  212. jarvis/jarvis_sec/analysis.py +80 -33
  213. jarvis/jarvis_sec/checkers/__init__.py +7 -13
  214. jarvis/jarvis_sec/checkers/c_checker.py +356 -164
  215. jarvis/jarvis_sec/checkers/rust_checker.py +47 -29
  216. jarvis/jarvis_sec/cli.py +43 -21
  217. jarvis/jarvis_sec/clustering.py +430 -272
  218. jarvis/jarvis_sec/file_manager.py +99 -55
  219. jarvis/jarvis_sec/parsers.py +9 -6
  220. jarvis/jarvis_sec/prompts.py +4 -3
  221. jarvis/jarvis_sec/report.py +44 -22
  222. jarvis/jarvis_sec/review.py +180 -107
  223. jarvis/jarvis_sec/status.py +50 -41
  224. jarvis/jarvis_sec/types.py +3 -0
  225. jarvis/jarvis_sec/utils.py +160 -83
  226. jarvis/jarvis_sec/verification.py +411 -181
  227. jarvis/jarvis_sec/workflow.py +132 -21
  228. jarvis/jarvis_smart_shell/main.py +28 -41
  229. jarvis/jarvis_stats/cli.py +14 -12
  230. jarvis/jarvis_stats/stats.py +28 -19
  231. jarvis/jarvis_stats/storage.py +14 -8
  232. jarvis/jarvis_stats/visualizer.py +12 -7
  233. jarvis/jarvis_tools/base.py +5 -2
  234. jarvis/jarvis_tools/clear_memory.py +13 -9
  235. jarvis/jarvis_tools/cli/main.py +23 -18
  236. jarvis/jarvis_tools/edit_file.py +572 -873
  237. jarvis/jarvis_tools/execute_script.py +10 -7
  238. jarvis/jarvis_tools/file_analyzer.py +7 -8
  239. jarvis/jarvis_tools/meta_agent.py +287 -0
  240. jarvis/jarvis_tools/methodology.py +5 -3
  241. jarvis/jarvis_tools/read_code.py +305 -1438
  242. jarvis/jarvis_tools/read_symbols.py +50 -17
  243. jarvis/jarvis_tools/read_webpage.py +19 -18
  244. jarvis/jarvis_tools/registry.py +435 -156
  245. jarvis/jarvis_tools/retrieve_memory.py +16 -11
  246. jarvis/jarvis_tools/save_memory.py +8 -6
  247. jarvis/jarvis_tools/search_web.py +31 -31
  248. jarvis/jarvis_tools/sub_agent.py +32 -28
  249. jarvis/jarvis_tools/sub_code_agent.py +44 -60
  250. jarvis/jarvis_tools/task_list_manager.py +1811 -0
  251. jarvis/jarvis_tools/virtual_tty.py +29 -19
  252. jarvis/jarvis_utils/__init__.py +4 -0
  253. jarvis/jarvis_utils/builtin_replace_map.py +2 -1
  254. jarvis/jarvis_utils/clipboard.py +9 -8
  255. jarvis/jarvis_utils/collections.py +331 -0
  256. jarvis/jarvis_utils/config.py +699 -194
  257. jarvis/jarvis_utils/dialogue_recorder.py +294 -0
  258. jarvis/jarvis_utils/embedding.py +6 -3
  259. jarvis/jarvis_utils/file_processors.py +7 -1
  260. jarvis/jarvis_utils/fzf.py +9 -3
  261. jarvis/jarvis_utils/git_utils.py +71 -42
  262. jarvis/jarvis_utils/globals.py +116 -32
  263. jarvis/jarvis_utils/http.py +6 -2
  264. jarvis/jarvis_utils/input.py +318 -83
  265. jarvis/jarvis_utils/jsonnet_compat.py +119 -104
  266. jarvis/jarvis_utils/methodology.py +37 -28
  267. jarvis/jarvis_utils/output.py +201 -44
  268. jarvis/jarvis_utils/utils.py +986 -628
  269. {jarvis_ai_assistant-0.7.8.dist-info → jarvis_ai_assistant-1.0.2.dist-info}/METADATA +49 -33
  270. jarvis_ai_assistant-1.0.2.dist-info/RECORD +304 -0
  271. jarvis/jarvis_code_agent/code_analyzer/structured_code.py +0 -556
  272. jarvis/jarvis_tools/generate_new_tool.py +0 -205
  273. jarvis/jarvis_tools/lsp_client.py +0 -1552
  274. jarvis/jarvis_tools/rewrite_file.py +0 -105
  275. jarvis_ai_assistant-0.7.8.dist-info/RECORD +0 -218
  276. {jarvis_ai_assistant-0.7.8.dist-info → jarvis_ai_assistant-1.0.2.dist-info}/WHEEL +0 -0
  277. {jarvis_ai_assistant-0.7.8.dist-info → jarvis_ai_assistant-1.0.2.dist-info}/entry_points.txt +0 -0
  278. {jarvis_ai_assistant-0.7.8.dist-info → jarvis_ai_assistant-1.0.2.dist-info}/licenses/LICENSE +0 -0
  279. {jarvis_ai_assistant-0.7.8.dist-info → jarvis_ai_assistant-1.0.2.dist-info}/top_level.txt +0 -0
@@ -1,23 +1,29 @@
1
1
  # -*- coding: utf-8 -*-
2
2
  """验证相关模块"""
3
3
 
4
- from typing import Dict, List, Optional
5
- import typer
4
+ from typing import Dict
5
+ from typing import List
6
+ from typing import Optional
6
7
 
7
8
  from jarvis.jarvis_agent import Agent
8
- from jarvis.jarvis_tools.registry import ToolRegistry
9
- from jarvis.jarvis_sec.prompts import build_verification_summary_prompt
9
+ from jarvis.jarvis_utils.output import PrettyOutput
10
+ from jarvis.jarvis_utils.tag import ot
11
+ from jarvis.jarvis_sec.agents import create_analysis_agent
12
+ from jarvis.jarvis_sec.agents import subscribe_summary_event
13
+ from jarvis.jarvis_sec.analysis import build_analysis_task_context
14
+ from jarvis.jarvis_sec.analysis import expand_and_filter_analysis_results
15
+ from jarvis.jarvis_sec.analysis import run_analysis_agent_with_retry
10
16
  from jarvis.jarvis_sec.parsers import try_parse_summary_report
11
- from jarvis.jarvis_sec.agents import create_analysis_agent, subscribe_summary_event
12
- from jarvis.jarvis_sec.utils import git_restore_if_dirty, sig_of, count_issues_from_file
13
- from jarvis.jarvis_sec.analysis import (
14
- build_analysis_task_context,
15
- run_analysis_agent_with_retry,
16
- expand_and_filter_analysis_results,
17
- )
17
+ from jarvis.jarvis_sec.prompts import build_verification_summary_prompt
18
+ from jarvis.jarvis_sec.utils import count_issues_from_file
19
+ from jarvis.jarvis_sec.utils import git_restore_if_dirty
20
+ from jarvis.jarvis_sec.utils import sig_of
21
+ from jarvis.jarvis_tools.registry import ToolRegistry
18
22
 
19
23
 
20
- def build_gid_to_verification_mapping(verification_results: List[Dict]) -> Dict[int, Dict]:
24
+ def build_gid_to_verification_mapping(
25
+ verification_results: List[Dict],
26
+ ) -> Dict[int, Dict]:
21
27
  """构建gid到验证结果的映射"""
22
28
  gid_to_verification: Dict[int, Dict] = {}
23
29
  for vr in verification_results:
@@ -32,7 +38,9 @@ def build_gid_to_verification_mapping(verification_results: List[Dict]) -> Dict[
32
38
  gids_to_process.append(gid_int)
33
39
  except Exception as e:
34
40
  try:
35
- typer.secho(f"[jarvis-sec] 警告:验证结果中 gids 数组元素格式错误: {gid_val}, 错误: {e}", fg=typer.colors.YELLOW)
41
+ PrettyOutput.auto_print(
42
+ f"[jarvis-sec] 警告:验证结果中 gids 数组元素格式错误: {gid_val}, 错误: {e}"
43
+ )
36
44
  except Exception:
37
45
  pass
38
46
  elif "gid" in vr:
@@ -43,26 +51,32 @@ def build_gid_to_verification_mapping(verification_results: List[Dict]) -> Dict[
43
51
  gids_to_process.append(gid_int)
44
52
  else:
45
53
  try:
46
- typer.secho(f"[jarvis-sec] 警告:验证结果中 gid 值无效: {gid_val} (必须 >= 1)", fg=typer.colors.YELLOW)
54
+ PrettyOutput.auto_print(
55
+ f"[jarvis-sec] 警告:验证结果中 gid 值无效: {gid_val} (必须 >= 1)"
56
+ )
47
57
  except Exception:
48
58
  pass
49
59
  except Exception as e:
50
60
  try:
51
- typer.secho(f"[jarvis-sec] 警告:验证结果中 gid 格式错误: {vr.get('gid')}, 错误: {e}", fg=typer.colors.YELLOW)
61
+ PrettyOutput.auto_print(
62
+ f"[jarvis-sec] 警告:验证结果中 gid 格式错误: {vr.get('gid')}, 错误: {e}"
63
+ )
52
64
  except Exception:
53
65
  pass
54
66
  else:
55
67
  try:
56
- typer.secho(f"[jarvis-sec] 警告:验证结果项缺少 gid 或 gids 字段: {vr}", fg=typer.colors.YELLOW)
68
+ PrettyOutput.auto_print(
69
+ f"[jarvis-sec] 警告:验证结果项缺少 gid 或 gids 字段: {vr}"
70
+ )
57
71
  except Exception:
58
72
  pass
59
-
73
+
60
74
  is_valid = vr.get("is_valid")
61
75
  verification_notes = str(vr.get("verification_notes", "")).strip()
62
76
  for gid in gids_to_process:
63
77
  gid_to_verification[gid] = {
64
78
  "is_valid": is_valid,
65
- "verification_notes": verification_notes
79
+ "verification_notes": verification_notes,
66
80
  }
67
81
  return gid_to_verification
68
82
 
@@ -81,7 +95,7 @@ def merge_verified_items(
81
95
  gid_to_candidate[c_gid] = c
82
96
  except Exception:
83
97
  pass
84
-
98
+
85
99
  verified_items: List[Dict] = []
86
100
  for item in items_with_risk:
87
101
  item_gid = int(item.get("gid", 0))
@@ -92,17 +106,23 @@ def merge_verified_items(
92
106
  merged_item = {
93
107
  **candidate, # 原始候选信息
94
108
  **item, # 分析结果
95
- "verification_notes": str(verification.get("verification_notes", "")).strip(),
109
+ "verification_notes": str(
110
+ verification.get("verification_notes", "")
111
+ ).strip(),
96
112
  }
97
113
  verified_items.append(merged_item)
98
114
  elif verification and verification.get("is_valid") is False:
99
115
  try:
100
- typer.secho(f"[jarvis-sec] 验证 Agent 判定 gid={item_gid} 为误报: {verification.get('verification_notes', '')}", fg=typer.colors.BLUE)
116
+ PrettyOutput.auto_print(
117
+ f"[jarvis-sec] 验证 Agent 判定 gid={item_gid} 为误报: {verification.get('verification_notes', '')}"
118
+ )
101
119
  except Exception:
102
120
  pass
103
121
  else:
104
122
  try:
105
- typer.secho(f"[jarvis-sec] 警告:验证结果中未找到 gid={item_gid},视为验证不通过", fg=typer.colors.YELLOW)
123
+ PrettyOutput.auto_print(
124
+ f"[jarvis-sec] 警告:验证结果中未找到 gid={item_gid},视为验证不通过"
125
+ )
106
126
  except Exception:
107
127
  pass
108
128
  return verified_items
@@ -121,7 +141,7 @@ def merge_verified_items_without_verification(
121
141
  gid_to_candidate[c_gid] = c
122
142
  except Exception:
123
143
  pass
124
-
144
+
125
145
  verified_items: List[Dict] = []
126
146
  for item in items_with_risk:
127
147
  item_gid = int(item.get("gid", 0))
@@ -188,68 +208,84 @@ def run_verification_agent_with_retry(
188
208
  use_direct_model_verify = False
189
209
  prev_parse_error_verify: Optional[str] = None
190
210
  verify_attempt = 0
191
-
211
+
192
212
  while True:
193
213
  verify_attempt += 1
194
214
  verification_summary_container["text"] = ""
195
-
215
+
196
216
  if use_direct_model_verify:
197
217
  verification_summary_prompt_text = build_verification_summary_prompt()
198
218
  error_guidance = ""
199
219
  if prev_parse_error_verify:
200
220
  error_guidance = f"\n\n**格式错误详情(请根据以下错误修复输出格式):**\n- JSON解析失败: {prev_parse_error_verify}\n\n请确保输出的JSON格式正确,包括正确的引号、逗号、大括号等。仅输出一个 <REPORT> 块,块内直接包含 JSON 数组(不需要额外的标签)。支持jsonnet语法(如尾随逗号、注释、||| 或 ``` 分隔符多行字符串等)。"
201
-
221
+
202
222
  full_verify_prompt = f"{verification_task}{error_guidance}\n\n{verification_summary_prompt_text}"
203
223
  try:
204
- verify_response = verification_agent.model.chat_until_success(full_verify_prompt) # type: ignore
224
+ verify_response = verification_agent.model.chat_until_success(
225
+ full_verify_prompt
226
+ )
205
227
  verification_summary_container["text"] = verify_response
206
228
  except Exception as e:
207
229
  try:
208
- typer.secho(f"[jarvis-sec] 验证阶段直接模型调用失败: {e},回退到 run()", fg=typer.colors.YELLOW)
230
+ PrettyOutput.auto_print(
231
+ f"[jarvis-sec] 验证阶段直接模型调用失败: {e},回退到 run()"
232
+ )
209
233
  except Exception:
210
234
  pass
211
235
  verification_agent.run(verification_task)
212
236
  else:
213
237
  verification_agent.run(verification_task)
214
-
238
+
215
239
  # 工作区保护
216
240
  try:
217
241
  _changed_verify = git_restore_if_dirty(entry_path)
218
242
  if _changed_verify:
219
243
  try:
220
- typer.secho(f"[jarvis-sec] 验证 Agent 工作区已恢复 ({_changed_verify} 个文件)", fg=typer.colors.BLUE)
244
+ PrettyOutput.auto_print(
245
+ f"[jarvis-sec] 验证 Agent 工作区已恢复 ({_changed_verify} 个文件)"
246
+ )
221
247
  except Exception:
222
248
  pass
223
249
  except Exception:
224
250
  pass
225
-
251
+
226
252
  # 解析验证结果
227
253
  verification_summary_text = verification_summary_container.get("text", "")
228
254
  parse_error_verify = None
229
255
  if verification_summary_text:
230
- verification_parsed, parse_error_verify = try_parse_summary_report(verification_summary_text)
256
+ verification_parsed, parse_error_verify = try_parse_summary_report(
257
+ verification_summary_text
258
+ )
231
259
  if parse_error_verify:
232
260
  prev_parse_error_verify = parse_error_verify
233
261
  try:
234
- typer.secho(f"[jarvis-sec] 验证结果JSON解析失败: {parse_error_verify}", fg=typer.colors.YELLOW)
262
+ PrettyOutput.auto_print(
263
+ f"[jarvis-sec] 验证结果JSON解析失败: {parse_error_verify}"
264
+ )
235
265
  except Exception:
236
266
  pass
237
267
  else:
238
268
  prev_parse_error_verify = None
239
269
  if isinstance(verification_parsed, list):
240
- if verification_parsed and all(is_valid_verification_item(item) for item in verification_parsed):
270
+ if verification_parsed and all(
271
+ is_valid_verification_item(item) for item in verification_parsed
272
+ ):
241
273
  return verification_parsed, None
242
-
274
+
243
275
  # 格式校验失败,后续重试使用直接模型调用
244
276
  use_direct_model_verify = True
245
277
  if parse_error_verify:
246
278
  try:
247
- typer.secho(f"[jarvis-sec] 验证结果JSON解析失败 -> 重试第 {verify_attempt} 次 (批次={bidx},使用直接模型调用,将反馈解析错误)", fg=typer.colors.YELLOW)
279
+ PrettyOutput.auto_print(
280
+ f"[jarvis-sec] 验证结果JSON解析失败 -> 重试第 {verify_attempt} 次 (批次={bidx},使用直接模型调用,将反馈解析错误)"
281
+ )
248
282
  except Exception:
249
283
  pass
250
284
  else:
251
285
  try:
252
- typer.secho(f"[jarvis-sec] 验证结果格式无效 -> 重试第 {verify_attempt} 次 (批次={bidx},使用直接模型调用)", fg=typer.colors.YELLOW)
286
+ PrettyOutput.auto_print(
287
+ f"[jarvis-sec] 验证结果格式无效 -> 重试第 {verify_attempt} 次 (批次={bidx},使用直接模型调用)"
288
+ )
253
289
  except Exception:
254
290
  pass
255
291
 
@@ -272,7 +308,7 @@ def process_verification_batch(
272
308
  ) -> None:
273
309
  """
274
310
  处理单个验证批次。
275
-
311
+
276
312
  参数:
277
313
  - batch: 当前批次的候选列表
278
314
  - bidx: 批次索引
@@ -281,7 +317,7 @@ def process_verification_batch(
281
317
  """
282
318
  task_id = f"JARVIS-SEC-Batch-{bidx}"
283
319
  batch_file = batch[0].get("file") if batch else None
284
-
320
+
285
321
  # 进度:批次开始
286
322
  _progress_append(
287
323
  {
@@ -300,7 +336,7 @@ def process_verification_batch(
300
336
  total_batches=total_batches,
301
337
  batch_id=task_id,
302
338
  file_name=batch_file,
303
- message=f"正在验证批次 {bidx}/{total_batches}"
339
+ message=f"正在验证批次 {bidx}/{total_batches}",
304
340
  )
305
341
 
306
342
  # 显示进度(提取批次中的所有 gid,用于后续打印)
@@ -317,42 +353,56 @@ def process_verification_batch(
317
353
  batch_gids_all_sorted = sorted(batch_gids_all)
318
354
  except Exception:
319
355
  batch_gids_all_sorted = []
320
-
356
+
321
357
  # 显示进度
322
358
  try:
323
359
  if batch_gids_all_sorted:
324
360
  gids_str = str(batch_gids_all_sorted)
325
- typer.secho(f"\n[jarvis-sec] 分析批次 {bidx}/{total_batches}: 大小={len(batch)} 文件='{batch_file}' gids={gids_str}", fg=typer.colors.CYAN)
361
+ PrettyOutput.auto_print(
362
+ f"\n[jarvis-sec] 分析批次 {bidx}/{total_batches}: 大小={len(batch)} 文件='{batch_file}' gids={gids_str}"
363
+ )
326
364
  else:
327
- typer.secho(f"\n[jarvis-sec] 分析批次 {bidx}/{total_batches}: 大小={len(batch)} 文件='{batch_file}' (无有效gid)", fg=typer.colors.CYAN)
365
+ PrettyOutput.auto_print(
366
+ f"\n[jarvis-sec] 分析批次 {bidx}/{total_batches}: 大小={len(batch)} 文件='{batch_file}' (无有效gid)"
367
+ )
328
368
  except Exception:
329
369
  pass
330
370
 
331
371
  # 创建分析Agent
332
- agent = create_analysis_agent(task_id, llm_group, force_save_memory=force_save_memory)
333
-
372
+ agent = create_analysis_agent(
373
+ task_id, llm_group, force_save_memory=force_save_memory
374
+ )
375
+
334
376
  # 构建任务上下文
335
377
  per_task = build_analysis_task_context(batch, entry_path, langs)
336
-
378
+
337
379
  # 订阅摘要事件
338
380
  summary_container = subscribe_summary_event(agent)
339
-
381
+
340
382
  # 运行分析Agent并重试
341
383
  summary_items, workspace_restore_info = run_analysis_agent_with_retry(
342
384
  agent, per_task, summary_container, entry_path, task_id, bidx, meta_records
343
385
  )
344
-
386
+
345
387
  # 处理分析结果
346
388
  parse_fail = summary_items is None
347
389
  verified_items: List[Dict] = []
348
-
390
+
349
391
  # 处理空数组情况:分析 Agent 返回 [] 表示所有候选都被判定为无风险
350
392
  if summary_items is not None and len(summary_items) == 0:
351
393
  # 空数组表示所有候选都被判定为无风险,需要保存到 analysis.jsonl
352
394
  try:
353
- batch_gids = sorted([int(item.get("gid", 0)) for item in batch if int(item.get("gid", 0)) >= 1])
354
- typer.secho(f"[jarvis-sec] 批次 {bidx}/{total_batches} 分析 Agent 返回空数组,判定所有候选为无风险: 有风险gid=[], 无风险gid={batch_gids}", fg=typer.colors.BLUE)
355
-
395
+ batch_gids = sorted(
396
+ [
397
+ int(item.get("gid", 0))
398
+ for item in batch
399
+ if int(item.get("gid", 0)) >= 1
400
+ ]
401
+ )
402
+ PrettyOutput.auto_print(
403
+ f"[jarvis-sec] 批次 {bidx}/{total_batches} 分析 Agent 返回空数组,判定所有候选为无风险: 有风险gid=[], 无风险gid={batch_gids}"
404
+ )
405
+
356
406
  # 构建无风险项(将批次中的所有候选标记为无风险)
357
407
  no_risk_items = []
358
408
  for item in batch:
@@ -367,76 +417,141 @@ def process_verification_batch(
367
417
  no_risk_items.append(no_risk_item)
368
418
  except Exception:
369
419
  pass
370
-
420
+
371
421
  # 保存到 analysis.jsonl
372
422
  if no_risk_items:
373
- merged_no_risk_items = merge_verified_items_without_verification(no_risk_items, batch)
423
+ merged_no_risk_items = merge_verified_items_without_verification(
424
+ no_risk_items, batch
425
+ )
374
426
  if merged_no_risk_items:
375
- _append_report(merged_no_risk_items, "analysis_only", task_id, {"batch": True, "candidates": batch})
376
- typer.secho(f"[jarvis-sec] 批次 {bidx}/{total_batches} 已将所有无风险候选保存到 analysis.jsonl: gids={batch_gids}", fg=typer.colors.GREEN)
427
+ _append_report(
428
+ merged_no_risk_items,
429
+ "analysis_only",
430
+ task_id,
431
+ {"batch": True, "candidates": batch},
432
+ )
433
+ PrettyOutput.auto_print(
434
+ f"[jarvis-sec] 批次 {bidx}/{total_batches} 已将所有无风险候选保存到 analysis.jsonl: gids={batch_gids}"
435
+ )
377
436
  except Exception as e:
378
437
  try:
379
- typer.secho(f"[jarvis-sec] 警告:处理空数组结果失败: {e}", fg=typer.colors.YELLOW)
438
+ PrettyOutput.auto_print(f"[jarvis-sec] 警告:处理空数组结果失败: {e}")
380
439
  except Exception:
381
440
  pass
382
-
441
+
383
442
  elif summary_items:
384
443
  # 展开并过滤分析结果
385
- items_with_risk, items_without_risk = expand_and_filter_analysis_results(summary_items)
386
-
444
+ items_with_risk, items_without_risk = expand_and_filter_analysis_results(
445
+ summary_items
446
+ )
447
+
387
448
  # 记录分析结论(分别显示有风险和无风险的gid)
388
- risk_gids = sorted([int(item.get("gid", 0)) for item in items_with_risk if int(item.get("gid", 0)) >= 1]) if items_with_risk else []
389
- no_risk_gids = sorted([int(item.get("gid", 0)) for item in items_without_risk if int(item.get("gid", 0)) >= 1]) if items_without_risk else []
390
-
449
+ risk_gids = (
450
+ sorted(
451
+ [
452
+ int(item.get("gid", 0))
453
+ for item in items_with_risk
454
+ if int(item.get("gid", 0)) >= 1
455
+ ]
456
+ )
457
+ if items_with_risk
458
+ else []
459
+ )
460
+ no_risk_gids = (
461
+ sorted(
462
+ [
463
+ int(item.get("gid", 0))
464
+ for item in items_without_risk
465
+ if int(item.get("gid", 0)) >= 1
466
+ ]
467
+ )
468
+ if items_without_risk
469
+ else []
470
+ )
471
+
391
472
  if items_with_risk or items_without_risk:
392
473
  try:
393
- typer.secho(f"[jarvis-sec] 批次 {bidx}/{total_batches} 分析 Agent 判定结果: 有风险gid={risk_gids}, 无风险gid={no_risk_gids}", fg=typer.colors.YELLOW if items_with_risk else typer.colors.BLUE)
474
+ PrettyOutput.auto_print(
475
+ f"[jarvis-sec] 批次 {bidx}/{total_batches} 分析 Agent 判定结果: 有风险gid={risk_gids}, 无风险gid={no_risk_gids}"
476
+ )
394
477
  except Exception:
395
478
  pass
396
-
479
+
397
480
  # 如果所有 gid 都被判定为无风险,也需要保存到 analysis.jsonl
398
481
  if not items_with_risk and items_without_risk:
399
482
  try:
400
483
  # 将所有无风险的 gid 保存到 analysis.jsonl,确保它们被标记为已分析
401
- no_risk_items = merge_verified_items_without_verification(items_without_risk, batch)
484
+ no_risk_items = merge_verified_items_without_verification(
485
+ items_without_risk, batch
486
+ )
402
487
  if no_risk_items:
403
- _append_report(no_risk_items, "analysis_only", task_id, {"batch": True, "candidates": batch})
488
+ _append_report(
489
+ no_risk_items,
490
+ "analysis_only",
491
+ task_id,
492
+ {"batch": True, "candidates": batch},
493
+ )
404
494
  try:
405
- typer.secho(f"[jarvis-sec] 批次 {bidx}/{total_batches} 所有候选均为无风险,已保存到 analysis.jsonl", fg=typer.colors.BLUE)
495
+ PrettyOutput.auto_print(
496
+ f"[jarvis-sec] 批次 {bidx}/{total_batches} 所有候选均为无风险,已保存到 analysis.jsonl"
497
+ )
406
498
  except Exception:
407
499
  pass
408
500
  except Exception as e:
409
501
  try:
410
- typer.secho(f"[jarvis-sec] 警告:保存无风险 gid 失败: {e}", fg=typer.colors.YELLOW)
502
+ PrettyOutput.auto_print(
503
+ f"[jarvis-sec] 警告:保存无风险 gid 失败: {e}"
504
+ )
411
505
  except Exception:
412
506
  pass
413
-
507
+
414
508
  # 运行验证Agent(仅当分析Agent发现有风险的问题时,且启用二次验证)
415
509
  if items_with_risk:
416
510
  if not enable_verification:
417
511
  # 如果关闭二次验证,直接将分析Agent确认的问题作为已验证的问题
418
- verified_items = merge_verified_items_without_verification(items_with_risk, batch)
512
+ verified_items = merge_verified_items_without_verification(
513
+ items_with_risk, batch
514
+ )
419
515
  if verified_items:
420
- verified_gids = sorted([int(item.get("gid", 0)) for item in verified_items if int(item.get("gid", 0)) >= 1])
516
+ verified_gids = sorted(
517
+ [
518
+ int(item.get("gid", 0))
519
+ for item in verified_items
520
+ if int(item.get("gid", 0)) >= 1
521
+ ]
522
+ )
421
523
  for item in verified_items:
422
524
  gid = int(item.get("gid", 0))
423
525
  if gid >= 1:
424
526
  gid_counts[gid] = gid_counts.get(gid, 0) + 1
425
527
  # 计算无风险的gid(批次中不在verified_gids中的gid)
426
- no_risk_gids_in_batch = sorted([gid for gid in batch_gids_all_sorted if gid not in verified_gids])
427
- typer.secho(f"[jarvis-sec] 批次 {bidx}/{total_batches} 跳过验证,直接写入: 数量={len(verified_items)} 有风险gid={verified_gids}, 无风险gid={no_risk_gids_in_batch}", fg=typer.colors.GREEN)
428
- _append_report(verified_items, "analysis_only", task_id, {"batch": True, "candidates": batch})
528
+ no_risk_gids_in_batch = sorted(
529
+ [
530
+ gid
531
+ for gid in batch_gids_all_sorted
532
+ if gid not in verified_gids
533
+ ]
534
+ )
535
+ PrettyOutput.auto_print(
536
+ f"[jarvis-sec] 批次 {bidx}/{total_batches} 跳过验证,直接写入: 数量={len(verified_items)} 有风险gid={verified_gids}, 无风险gid={no_risk_gids_in_batch}"
537
+ )
538
+ _append_report(
539
+ verified_items,
540
+ "analysis_only",
541
+ task_id,
542
+ {"batch": True, "candidates": batch},
543
+ )
429
544
  current_count = count_issues_from_file(sec_dir)
430
545
  status_mgr.update_verification(
431
546
  current_batch=bidx,
432
547
  total_batches=total_batches,
433
548
  issues_found=current_count,
434
- message=f"已处理 {bidx}/{total_batches} 批次,发现 {current_count} 个问题(未验证)"
549
+ message=f"已处理 {bidx}/{total_batches} 批次,发现 {current_count} 个问题(未验证)",
435
550
  )
436
551
  else:
437
552
  # 启用二次验证,运行验证Agent
438
553
  # 创建验证 Agent 来验证分析 Agent 的结论
439
- verification_system_prompt = """
554
+ verification_system_prompt = f"""
440
555
  # 验证 Agent 约束
441
556
  - 你的核心任务是验证分析 Agent 给出的安全结论是否正确。
442
557
  - 你需要仔细检查分析 Agent 给出的前置条件、触发路径、后果和建议是否合理、准确。
@@ -448,9 +563,9 @@ def process_verification_batch(
448
563
  - 在验证过程中,充分利用 retrieve_memory 工具检索已有的记忆,特别是分析 Agent 保存的与当前验证函数相关的记忆。
449
564
  - 这些记忆可能包含函数的分析要点、指针判空情况、输入校验情况、调用路径分析结果等,可以帮助你更准确地验证分析结论。
450
565
  - 如果发现分析 Agent 的结论与记忆中的信息不一致,需要仔细核实。
451
- - 完成验证后,主输出仅打印结束符 <!!!COMPLETE!!!> ,不需要汇总结果。
566
+ - 完成验证后,主输出仅打印结束符 {ot("!!!COMPLETE!!!")},不要输出其他任何内容。任务总结将会在后面的交互中被询问。
452
567
  """.strip()
453
-
568
+
454
569
  verification_task_id = f"JARVIS-SEC-Verify-Batch-{bidx}"
455
570
  verification_agent_kwargs: Dict = dict(
456
571
  system_prompt=verification_system_prompt,
@@ -468,9 +583,10 @@ def process_verification_batch(
468
583
  if llm_group:
469
584
  verification_agent_kwargs["model_group"] = llm_group
470
585
  verification_agent = Agent(**verification_agent_kwargs)
471
-
586
+
472
587
  # 构造验证任务上下文
473
588
  import json as _json3
589
+
474
590
  verification_task = f"""
475
591
  # 验证分析结论任务
476
592
  上下文参数:
@@ -488,11 +604,16 @@ def process_verification_batch(
488
604
 
489
605
  对于每个 gid,请判断分析结论是否正确(is_valid: true/false),并给出验证说明。
490
606
  """.strip()
491
-
607
+
492
608
  # 订阅验证 Agent 的摘要
493
- verification_summary_container = subscribe_summary_event(verification_agent)
494
-
495
- verification_results, verification_parse_error = run_verification_agent_with_retry(
609
+ verification_summary_container = subscribe_summary_event(
610
+ verification_agent
611
+ )
612
+
613
+ (
614
+ verification_results,
615
+ verification_parse_error,
616
+ ) = run_verification_agent_with_retry(
496
617
  verification_agent,
497
618
  verification_task,
498
619
  build_verification_summary_prompt(),
@@ -500,117 +621,195 @@ def process_verification_batch(
500
621
  verification_summary_container,
501
622
  bidx,
502
623
  )
503
-
624
+
504
625
  # 调试日志:显示验证结果
505
626
  if verification_results is None:
506
627
  try:
507
- typer.secho("[jarvis-sec] 警告:验证 Agent 返回 None,可能解析失败", fg=typer.colors.YELLOW)
628
+ PrettyOutput.auto_print(
629
+ "[jarvis-sec] 警告:验证 Agent 返回 None,可能解析失败"
630
+ )
508
631
  except Exception:
509
632
  pass
510
633
  elif not isinstance(verification_results, list):
511
634
  try:
512
- typer.secho(f"[jarvis-sec] 警告:验证 Agent 返回类型错误,期望 list,实际: {type(verification_results)}", fg=typer.colors.YELLOW)
635
+ PrettyOutput.auto_print(
636
+ f"[jarvis-sec] 警告:验证 Agent 返回类型错误,期望 list,实际: {type(verification_results)}"
637
+ )
513
638
  except Exception:
514
639
  pass
515
640
  elif len(verification_results) == 0:
516
641
  try:
517
- typer.secho("[jarvis-sec] 警告:验证 Agent 返回空列表", fg=typer.colors.YELLOW)
642
+ PrettyOutput.auto_print(
643
+ "[jarvis-sec] 警告:验证 Agent 返回空列表"
644
+ )
518
645
  except Exception:
519
646
  pass
520
647
  else:
521
648
  try:
522
- typer.secho(f"[jarvis-sec] 验证 Agent 返回 {len(verification_results)} 个结果项", fg=typer.colors.BLUE)
649
+ PrettyOutput.auto_print(
650
+ f"[jarvis-sec] 验证 Agent 返回 {len(verification_results)} 个结果项"
651
+ )
523
652
  except Exception:
524
653
  pass
525
-
654
+
526
655
  # 根据验证结果筛选:只保留验证通过(is_valid: true)的告警
527
656
  if verification_results:
528
- gid_to_verification = build_gid_to_verification_mapping(verification_results)
529
-
657
+ gid_to_verification = build_gid_to_verification_mapping(
658
+ verification_results
659
+ )
660
+
530
661
  # 调试日志:显示提取到的验证结果(包含gid列表)
531
662
  if gid_to_verification:
532
663
  try:
533
664
  # 分类显示验证结果:通过和不通过的gid
534
- valid_gids = sorted([gid for gid, v in gid_to_verification.items() if v.get("is_valid") is True])
535
- invalid_gids = sorted([gid for gid, v in gid_to_verification.items() if v.get("is_valid") is False])
665
+ valid_gids = sorted(
666
+ [
667
+ gid
668
+ for gid, v in gid_to_verification.items()
669
+ if v.get("is_valid") is True
670
+ ]
671
+ )
672
+ invalid_gids = sorted(
673
+ [
674
+ gid
675
+ for gid, v in gid_to_verification.items()
676
+ if v.get("is_valid") is False
677
+ ]
678
+ )
536
679
  all_verified_gids = sorted(gid_to_verification.keys())
537
-
680
+
538
681
  # 计算未验证的gid(批次中不在验证结果中的gid,视为无风险)
539
- unverified_gids = sorted([gid for gid in batch_gids_all_sorted if gid not in all_verified_gids])
682
+ unverified_gids = sorted(
683
+ [
684
+ gid
685
+ for gid in batch_gids_all_sorted
686
+ if gid not in all_verified_gids
687
+ ]
688
+ )
540
689
  # 合并所有无风险的gid(验证不通过的 + 未验证的)
541
- all_no_risk_gids = sorted(list(set(invalid_gids + unverified_gids)))
542
- typer.secho(f"[jarvis-sec] 验证 Agent 返回 {len(gid_to_verification)} 个验证结果: 有风险gid={valid_gids}, 无风险gid={all_no_risk_gids}", fg=typer.colors.BLUE)
690
+ all_no_risk_gids = sorted(
691
+ list(set(invalid_gids + unverified_gids))
692
+ )
693
+ PrettyOutput.auto_print(
694
+ f"[jarvis-sec] 验证 Agent 返回 {len(gid_to_verification)} 个验证结果: 有风险gid={valid_gids}, 无风险gid={all_no_risk_gids}"
695
+ )
543
696
  if valid_gids:
544
- typer.secho(f"[jarvis-sec] 验证 Agent 判定 {len(valid_gids)} 个候选验证通过(is_valid: true): 有风险gid={valid_gids}", fg=typer.colors.GREEN)
697
+ PrettyOutput.auto_print(
698
+ f"[jarvis-sec] 验证 Agent 判定 {len(valid_gids)} 个候选验证通过(is_valid: true): 有风险gid={valid_gids}"
699
+ )
545
700
  if invalid_gids:
546
- typer.secho(f"[jarvis-sec] 验证 Agent 判定 {len(invalid_gids)} 个候选验证不通过(is_valid: false): 无风险gid={invalid_gids}", fg=typer.colors.RED)
701
+ PrettyOutput.auto_print(
702
+ f"[jarvis-sec] 验证 Agent 判定 {len(invalid_gids)} 个候选验证不通过(is_valid: false): 无风险gid={invalid_gids}"
703
+ )
547
704
  if unverified_gids:
548
- typer.secho(f"[jarvis-sec] 验证 Agent 未验证的候选(不在验证结果中,视为无风险): 无风险gid={unverified_gids}", fg=typer.colors.BLUE)
705
+ PrettyOutput.auto_print(
706
+ f"[jarvis-sec] 验证 Agent 未验证的候选(不在验证结果中,视为无风险): 无风险gid={unverified_gids}"
707
+ )
549
708
  except Exception:
550
709
  pass
551
710
  else:
552
711
  try:
553
- typer.secho(f"[jarvis-sec] 警告:验证结果解析成功,但未提取到任何有效的 gid。验证结果: {verification_results}", fg=typer.colors.YELLOW)
712
+ PrettyOutput.auto_print(
713
+ f"[jarvis-sec] 警告:验证结果解析成功,但未提取到任何有效的 gid。验证结果: {verification_results}"
714
+ )
554
715
  except Exception:
555
716
  pass
556
-
717
+
557
718
  # 合并验证通过的告警
558
- verified_items = merge_verified_items(items_with_risk, batch, gid_to_verification)
719
+ verified_items = merge_verified_items(
720
+ items_with_risk, batch, gid_to_verification
721
+ )
559
722
  else:
560
- typer.secho("[jarvis-sec] 警告:验证 Agent 结果解析失败,不保留任何告警(保守策略)", fg=typer.colors.YELLOW)
561
-
723
+ PrettyOutput.auto_print(
724
+ "[jarvis-sec] 警告:验证 Agent 结果解析失败,不保留任何告警(保守策略)"
725
+ )
726
+
562
727
  # 只有验证通过的告警才写入文件
563
728
  if verified_items:
564
- verified_gids = sorted([int(item.get("gid", 0)) for item in verified_items if int(item.get("gid", 0)) >= 1])
729
+ verified_gids = sorted(
730
+ [
731
+ int(item.get("gid", 0))
732
+ for item in verified_items
733
+ if int(item.get("gid", 0)) >= 1
734
+ ]
735
+ )
565
736
  for item in verified_items:
566
737
  gid = int(item.get("gid", 0))
567
738
  if gid >= 1:
568
739
  gid_counts[gid] = gid_counts.get(gid, 0) + 1
569
740
  # 计算无风险的gid(批次中不在verified_gids中的gid)
570
- no_risk_gids_in_batch = sorted([gid for gid in batch_gids_all_sorted if gid not in verified_gids])
571
- typer.secho(f"[jarvis-sec] 批次 {bidx}/{total_batches} 验证通过: 数量={len(verified_items)}/{len(items_with_risk)} -> 写入文件 有风险gid={verified_gids}, 无风险gid={no_risk_gids_in_batch}", fg=typer.colors.GREEN)
572
- _append_report(verified_items, "verified", task_id, {"batch": True, "candidates": batch})
741
+ no_risk_gids_in_batch = sorted(
742
+ [
743
+ gid
744
+ for gid in batch_gids_all_sorted
745
+ if gid not in verified_gids
746
+ ]
747
+ )
748
+ PrettyOutput.auto_print(
749
+ f"[jarvis-sec] 批次 {bidx}/{total_batches} 验证通过: 数量={len(verified_items)}/{len(items_with_risk)} -> 写入文件 有风险gid={verified_gids}, 无风险gid={no_risk_gids_in_batch}"
750
+ )
751
+ _append_report(
752
+ verified_items,
753
+ "verified",
754
+ task_id,
755
+ {"batch": True, "candidates": batch},
756
+ )
573
757
  # 从文件读取当前总数(用于状态显示)
574
758
  current_count = count_issues_from_file(sec_dir)
575
759
  status_mgr.update_verification(
576
760
  current_batch=bidx,
577
761
  total_batches=total_batches,
578
762
  issues_found=current_count,
579
- message=f"已验证 {bidx}/{total_batches} 批次,发现 {current_count} 个问题(验证通过)"
763
+ message=f"已验证 {bidx}/{total_batches} 批次,发现 {current_count} 个问题(验证通过)",
580
764
  )
581
765
  else:
582
766
  # 验证后无有效告警时也要打印gid列表(所有都视为无风险)
583
767
  try:
584
- risk_gids = sorted([int(item.get("gid", 0)) for item in items_with_risk if int(item.get("gid", 0)) >= 1])
585
- # 验证后全部不通过,所以所有gid都是无风险
586
- typer.secho(f"[jarvis-sec] 批次 {bidx}/{total_batches} 验证后无有效告警: 分析 Agent 发现 {len(items_with_risk)} 个有风险的问题,验证后全部不通过 有风险gid=[], 无风险gid={batch_gids_all_sorted}", fg=typer.colors.BLUE)
768
+ risk_gids = sorted(
769
+ [
770
+ int(item.get("gid", 0))
771
+ for item in items_with_risk
772
+ if int(item.get("gid", 0)) >= 1
773
+ ]
774
+ )
775
+ # 验证后全部不通过,所以所有gid都是无风险 PrettyOutput.auto_print(f"[jarvis-sec] 批次 {bidx}/{total_batches} 验证后无有效告警: 分析 Agent 发现 {len(items_with_risk)} 个有风险的问题,验证后全部不通过 有风险gid=[], 无风险gid={batch_gids_all_sorted}")
587
776
  except Exception:
588
- typer.secho(f"[jarvis-sec] 批次 {bidx}/{total_batches} 验证后无有效告警: 分析 Agent 发现 {len(items_with_risk)} 个有风险的问题,验证后全部不通过 有风险gid=[], 无风险gid={batch_gids_all_sorted}", fg=typer.colors.BLUE)
777
+ PrettyOutput.auto_print(
778
+ f"[jarvis-sec] 批次 {bidx}/{total_batches} 验证后无有效告警: 分析 Agent 发现 {len(items_with_risk)} 个有风险的问题,验证后全部不通过 有风险gid=[], 无风险gid={batch_gids_all_sorted}"
779
+ )
589
780
  current_count = count_issues_from_file(sec_dir)
590
781
  status_mgr.update_verification(
591
782
  current_batch=bidx,
592
783
  total_batches=total_batches,
593
784
  issues_found=current_count,
594
- message=f"已验证 {bidx}/{total_batches} 批次,验证后无有效告警"
785
+ message=f"已验证 {bidx}/{total_batches} 批次,验证后无有效告警",
595
786
  )
596
787
  elif parse_fail:
597
788
  # 解析失败时也要打印gid列表(无法判断风险,但显示所有gid)
598
789
  try:
599
- typer.secho(f"[jarvis-sec] 批次 {bidx}/{total_batches} 解析失败 (摘要中无 <REPORT> 或字段无效): 有风险gid=?, 无风险gid=? (无法判断,gids={batch_gids_all_sorted})", fg=typer.colors.YELLOW)
790
+ PrettyOutput.auto_print(
791
+ f"[jarvis-sec] 批次 {bidx}/{total_batches} 解析失败 (摘要中无 <REPORT> 或字段无效): 有风险gid=?, 无风险gid=? (无法判断,gids={batch_gids_all_sorted})"
792
+ )
600
793
  except Exception:
601
- typer.secho(f"[jarvis-sec] 批次 {bidx}/{total_batches} 解析失败 (摘要中无 <REPORT> 或字段无效)", fg=typer.colors.YELLOW)
794
+ PrettyOutput.auto_print(
795
+ f"[jarvis-sec] 批次 {bidx}/{total_batches} 解析失败 (摘要中无 <REPORT> 或字段无效)"
796
+ )
602
797
  else:
603
798
  # 未发现问题时也要打印gid列表(所有都视为无风险)
604
799
  try:
605
- typer.secho(f"[jarvis-sec] 批次 {bidx}/{total_batches} 未发现问题: 有风险gid=[], 无风险gid={batch_gids_all_sorted}", fg=typer.colors.BLUE)
800
+ PrettyOutput.auto_print(
801
+ f"[jarvis-sec] 批次 {bidx}/{total_batches} 未发现问题: 有风险gid=[], 无风险gid={batch_gids_all_sorted}"
802
+ )
606
803
  except Exception:
607
- typer.secho(f"[jarvis-sec] 批次 {bidx}/{total_batches} 未发现问题", fg=typer.colors.BLUE)
804
+ PrettyOutput.auto_print(
805
+ f"[jarvis-sec] 批次 {bidx}/{total_batches} 未发现问题"
806
+ )
608
807
  current_count = count_issues_from_file(sec_dir)
609
808
  status_mgr.update_verification(
610
809
  current_batch=bidx,
611
810
  total_batches=total_batches,
612
811
  issues_found=current_count,
613
- message=f"已验证 {bidx}/{total_batches} 批次"
812
+ message=f"已验证 {bidx}/{total_batches} 批次",
614
813
  )
615
814
 
616
815
  # 为本批次所有候选写入 done 记录
@@ -621,28 +820,32 @@ def process_verification_batch(
621
820
  except Exception:
622
821
  c_gid = 0
623
822
  cnt = gid_counts.get(c_gid, 0)
624
- _progress_append({
625
- "event": "task_status",
626
- "status": "done",
627
- "task_id": task_id,
628
- "candidate_signature": sig,
629
- "candidate": c,
630
- "issues_count": int(cnt),
631
- "parse_fail": parse_fail,
632
- "workspace_restore": workspace_restore_info,
633
- "batch_index": bidx,
634
- })
823
+ _progress_append(
824
+ {
825
+ "event": "task_status",
826
+ "status": "done",
827
+ "task_id": task_id,
828
+ "candidate_signature": sig,
829
+ "candidate": c,
830
+ "issues_count": int(cnt),
831
+ "parse_fail": parse_fail,
832
+ "workspace_restore": workspace_restore_info,
833
+ "batch_index": bidx,
834
+ }
835
+ )
635
836
 
636
837
  # 批次结束记录
637
- _progress_append({
638
- "event": "batch_status",
639
- "status": "done",
640
- "batch_id": task_id,
641
- "batch_index": bidx,
642
- "total_batches": total_batches,
643
- "issues_count": len(verified_items),
644
- "parse_fail": parse_fail,
645
- })
838
+ _progress_append(
839
+ {
840
+ "event": "batch_status",
841
+ "status": "done",
842
+ "batch_id": task_id,
843
+ "batch_index": bidx,
844
+ "total_batches": total_batches,
845
+ "issues_count": len(verified_items),
846
+ "parse_fail": parse_fail,
847
+ }
848
+ )
646
849
 
647
850
 
648
851
  def process_verification_phase(
@@ -658,61 +861,73 @@ def process_verification_phase(
658
861
  force_save_memory: bool = False,
659
862
  ) -> List[Dict]:
660
863
  """处理验证阶段,返回所有已保存的告警"""
661
- from jarvis.jarvis_sec.file_manager import load_analysis_results, get_all_analyzed_gids
662
-
864
+ from jarvis.jarvis_sec.file_manager import get_all_analyzed_gids
865
+ from jarvis.jarvis_sec.file_manager import load_analysis_results
866
+
663
867
  batches: List[List[Dict]] = cluster_batches
664
868
  total_batches = len(batches)
665
-
869
+
666
870
  # 从 analysis.jsonl 中读取已分析的结果
667
871
  analysis_results = load_analysis_results(sec_dir)
668
872
  analyzed_gids = get_all_analyzed_gids(sec_dir)
669
-
873
+
670
874
  # 构建已完成的批次集合(通过 cluster_id 匹配)
671
875
  completed_cluster_ids = set()
672
876
  for result in analysis_results:
673
877
  cluster_id = result.get("cluster_id", "")
674
878
  if cluster_id:
675
879
  completed_cluster_ids.add(cluster_id)
676
-
880
+
677
881
  if completed_cluster_ids:
678
882
  try:
679
- typer.secho(f"[jarvis-sec] 断点恢复:从 analysis.jsonl 读取到 {len(completed_cluster_ids)} 个已完成的聚类", fg=typer.colors.BLUE)
883
+ PrettyOutput.auto_print(
884
+ f"[jarvis-sec] 断点恢复:从 analysis.jsonl 读取到 {len(completed_cluster_ids)} 个已完成的聚类"
885
+ )
680
886
  except Exception:
681
887
  pass
682
-
888
+
683
889
  if analyzed_gids:
684
890
  try:
685
- typer.secho(f"[jarvis-sec] 断点恢复:从 analysis.jsonl 读取到 {len(analyzed_gids)} 个已分析的 gids", fg=typer.colors.BLUE)
891
+ PrettyOutput.auto_print(
892
+ f"[jarvis-sec] 断点恢复:从 analysis.jsonl 读取到 {len(analyzed_gids)} 个已分析的 gids"
893
+ )
686
894
  except Exception:
687
895
  pass
688
-
896
+
689
897
  meta_records: List[Dict] = []
690
898
  gid_counts: Dict[int, int] = {}
691
-
899
+
692
900
  # 加载 clusters.jsonl 以匹配批次和聚类
693
901
  from jarvis.jarvis_sec.file_manager import load_clusters
902
+
694
903
  clusters = load_clusters(sec_dir)
695
-
904
+
696
905
  # 计算实际需要处理的批次数量(排除已完成的批次)
697
906
  pending_batches = []
698
907
  skipped_count = 0
699
-
908
+
700
909
  # 调试:显示已分析的 gid 信息
701
910
  if analyzed_gids:
702
911
  try:
703
912
  analyzed_gids_sorted = sorted(list(analyzed_gids))
704
- sample_gids = analyzed_gids_sorted[:10] if len(analyzed_gids_sorted) > 10 else analyzed_gids_sorted
705
- typer.secho(f"[jarvis-sec] 断点恢复:已分析的 gid 示例: {sample_gids}{'...' if len(analyzed_gids_sorted) > 10 else ''} (共 {len(analyzed_gids)} 个)", fg=typer.colors.BLUE)
913
+ sample_gids = (
914
+ analyzed_gids_sorted[:10]
915
+ if len(analyzed_gids_sorted) > 10
916
+ else analyzed_gids_sorted
917
+ )
918
+ PrettyOutput.auto_print(
919
+ f"[jarvis-sec] 断点恢复:已分析的 gid 示例: {sample_gids}{'...' if len(analyzed_gids_sorted) > 10 else ''} (共 {len(analyzed_gids)} 个)"
920
+ )
706
921
  except Exception:
707
922
  pass
708
-
923
+
709
924
  for bidx, batch in enumerate(batches, start=1):
710
925
  batch_file = batch[0].get("file") if batch else None
711
-
926
+
712
927
  # 检查批次是否已完成
713
928
  is_batch_completed = False
714
929
  completion_reason = ""
715
-
930
+
716
931
  # 从批次中提取 gids(确保类型为整数)
717
932
  batch_gids = set()
718
933
  for item in batch:
@@ -723,12 +938,12 @@ def process_verification_phase(
723
938
  batch_gids.add(_gid)
724
939
  except Exception:
725
940
  pass
726
-
941
+
727
942
  if not batch_gids:
728
943
  # 批次中没有有效的 gid,跳过
729
944
  skipped_count += 1
730
945
  continue
731
-
946
+
732
947
  # 方法1:通过 cluster_id 检查是否已完成
733
948
  # 查找匹配的聚类(精确匹配:文件相同且 gid 集合完全相同)
734
949
  for cluster in clusters:
@@ -741,25 +956,28 @@ def process_verification_phase(
741
956
  cluster_gids.add(int(gid_val))
742
957
  except Exception:
743
958
  pass
744
-
959
+
745
960
  # 文件路径匹配:使用标准化路径进行比较(去除尾部斜杠等)
746
961
  def normalize_path(p: str) -> str:
747
962
  if not p:
748
963
  return ""
749
964
  # 统一使用正斜杠,去除尾部斜杠
750
965
  return p.replace("\\", "/").rstrip("/")
751
-
966
+
752
967
  batch_file_normalized = normalize_path(batch_file or "")
753
968
  cluster_file_normalized = normalize_path(cluster_file)
754
-
969
+
755
970
  # 匹配条件:文件路径相同(标准化后)且 gid 集合完全相同
756
- if cluster_file_normalized == batch_file_normalized and cluster_gids == batch_gids:
971
+ if (
972
+ cluster_file_normalized == batch_file_normalized
973
+ and cluster_gids == batch_gids
974
+ ):
757
975
  cluster_id = cluster.get("cluster_id", "")
758
976
  if cluster_id and cluster_id in completed_cluster_ids:
759
977
  is_batch_completed = True
760
978
  completion_reason = f"通过 cluster_id 匹配: {cluster_id}"
761
979
  break
762
-
980
+
763
981
  # 方法2:如果所有 gid 都已分析,则认为该批次已完成
764
982
  if not is_batch_completed and batch_gids and analyzed_gids:
765
983
  # batch_gids已经是整数集合,analyzed_gids也应该是整数集合
@@ -770,51 +988,61 @@ def process_verification_phase(
770
988
  completion_reason = f"所有 gid 已分析 (批次 gids: {sorted(list(batch_gids))[:5]}{'...' if len(batch_gids) > 5 else ''})"
771
989
  elif bidx <= 3: # 调试:显示前3个批次的匹配情况
772
990
  try:
773
- typer.secho(f"[jarvis-sec] 批次 {bidx} 部分 gid 未分析: 缺失={sorted(list(missing_gids))[:5]}{'...' if len(missing_gids) > 5 else ''}, 已分析={sorted(list(batch_gids & analyzed_gids))[:5]}{'...' if len(batch_gids & analyzed_gids) > 5 else ''}", fg=typer.colors.YELLOW)
991
+ PrettyOutput.auto_print(
992
+ f"[jarvis-sec] 批次 {bidx} 部分 gid 未分析: 缺失={sorted(list(missing_gids))[:5]}{'...' if len(missing_gids) > 5 else ''}, 已分析={sorted(list(batch_gids & analyzed_gids))[:5]}{'...' if len(batch_gids & analyzed_gids) > 5 else ''}"
993
+ )
774
994
  except Exception:
775
995
  pass
776
-
996
+
777
997
  if is_batch_completed:
778
998
  skipped_count += 1
779
999
  # 调试日志:显示跳过的批次信息
780
1000
  try:
781
- typer.secho(f"[jarvis-sec] 跳过批次 {bidx}/{total_batches} (文件={batch_file}, gids={sorted(list(batch_gids))[:5]}{'...' if len(batch_gids) > 5 else ''}): {completion_reason}", fg=typer.colors.GREEN)
1001
+ PrettyOutput.auto_print(
1002
+ f"[jarvis-sec] 跳过批次 {bidx}/{total_batches} (文件={batch_file}, gids={sorted(list(batch_gids))[:5]}{'...' if len(batch_gids) > 5 else ''}): {completion_reason}"
1003
+ )
782
1004
  except Exception:
783
1005
  pass
784
1006
  else:
785
1007
  # 调试日志:显示待处理的批次信息
786
1008
  if bidx <= 3: # 只显示前3个待处理批次
787
1009
  try:
788
- missing_gids = batch_gids - analyzed_gids if analyzed_gids else batch_gids
789
- typer.secho(f"[jarvis-sec] 待处理批次 {bidx}/{total_batches} (文件={batch_file}, gids={sorted(list(batch_gids))[:5]}{'...' if len(batch_gids) > 5 else ''}, 未分析={sorted(list(missing_gids))[:5]}{'...' if len(missing_gids) > 5 else ''})", fg=typer.colors.YELLOW)
1010
+ missing_gids = (
1011
+ batch_gids - analyzed_gids if analyzed_gids else batch_gids
1012
+ )
1013
+ PrettyOutput.auto_print(
1014
+ f"[jarvis-sec] 待处理批次 {bidx}/{total_batches} (文件={batch_file}, gids={sorted(list(batch_gids))[:5]}{'...' if len(batch_gids) > 5 else ''}, 未分析={sorted(list(missing_gids))[:5]}{'...' if len(missing_gids) > 5 else ''})"
1015
+ )
790
1016
  except Exception:
791
1017
  pass
792
1018
  pending_batches.append((bidx, batch))
793
-
1019
+
794
1020
  # 实际需要处理的批次数量
795
1021
  actual_total_batches = len(pending_batches)
796
1022
  processed_count = 0
797
-
1023
+
798
1024
  # 显示跳过批次的信息
799
1025
  if skipped_count > 0:
800
1026
  try:
801
- typer.secho(f"[jarvis-sec] 断点恢复:跳过 {skipped_count} 个已完成的批次,剩余 {actual_total_batches} 个批次待处理", fg=typer.colors.GREEN)
1027
+ PrettyOutput.auto_print(
1028
+ f"[jarvis-sec] 断点恢复:跳过 {skipped_count} 个已完成的批次,剩余 {actual_total_batches} 个批次待处理"
1029
+ )
802
1030
  except Exception:
803
1031
  pass
804
-
1032
+
805
1033
  # 更新验证阶段状态(使用实际需要处理的总批次数)
806
1034
  if actual_total_batches > 0:
807
1035
  status_mgr.update_verification(
808
1036
  current_batch=0,
809
1037
  total_batches=actual_total_batches,
810
- message=f"开始安全验证...(共 {actual_total_batches} 个批次待处理)"
1038
+ message=f"开始安全验证...(共 {actual_total_batches} 个批次待处理)",
811
1039
  )
812
-
1040
+
813
1041
  # 处理待处理的批次
814
1042
  for bidx, batch in pending_batches:
815
1043
  processed_count += 1
816
1044
  batch_file = batch[0].get("file") if batch else None
817
-
1045
+
818
1046
  # 处理验证批次(使用实际已处理的批次编号)
819
1047
  process_verification_batch(
820
1048
  batch,
@@ -832,17 +1060,19 @@ def process_verification_phase(
832
1060
  enable_verification=enable_verification,
833
1061
  force_save_memory=force_save_memory,
834
1062
  )
835
-
1063
+
836
1064
  # 从 analysis.jsonl 读取所有已验证的问题
837
- from jarvis.jarvis_sec.file_manager import get_verified_issue_gids, load_candidates
1065
+ from jarvis.jarvis_sec.file_manager import get_verified_issue_gids
1066
+ from jarvis.jarvis_sec.file_manager import load_candidates
1067
+
838
1068
  get_verified_issue_gids(sec_dir)
839
1069
  load_candidates(sec_dir)
840
-
1070
+
841
1071
  # 构建问题列表(从 analysis.jsonl 的 issues 字段)
842
1072
  all_issues = []
843
1073
  for result in analysis_results:
844
1074
  issues = result.get("issues", [])
845
1075
  if isinstance(issues, list):
846
1076
  all_issues.extend(issues)
847
-
1077
+
848
1078
  return all_issues