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
@@ -0,0 +1,869 @@
1
+ # -*- coding: utf-8 -*-
2
+ """任务列表模块。
3
+
4
+ 该模块提供任务列表管理功能,支持多任务动态管理、上下文分层共享、Agent权限隔离。
5
+ """
6
+
7
+ import json
8
+ import os
9
+ import time
10
+ from collections import OrderedDict
11
+ from dataclasses import asdict
12
+ from dataclasses import dataclass
13
+ from dataclasses import field
14
+ from enum import Enum
15
+ from threading import Lock
16
+ from typing import Dict
17
+ from typing import List
18
+ from typing import Optional
19
+ from typing import Set
20
+ from typing import Tuple
21
+
22
+ from jarvis.jarvis_utils.output import PrettyOutput
23
+
24
+
25
+ class TaskStatus(Enum):
26
+ """任务状态枚举。"""
27
+
28
+ PENDING = "pending"
29
+ RUNNING = "running"
30
+ COMPLETED = "completed"
31
+ FAILED = "failed"
32
+ ABANDONED = "abandoned"
33
+
34
+
35
+ class AgentType(Enum):
36
+ """Agent类型枚举。"""
37
+
38
+ MAIN = "main"
39
+ SUB = "sub" # 子Agent,自动识别代码/通用任务类型
40
+
41
+
42
+ @dataclass
43
+ class Task:
44
+ """任务实体。
45
+
46
+ 任务实体为最小数据单元,采用结构化字典存储。
47
+ """
48
+
49
+ task_id: str
50
+ task_name: str
51
+ task_desc: str
52
+ priority: int
53
+ status: TaskStatus
54
+ expected_output: str
55
+ agent_type: AgentType
56
+ create_time: int
57
+ update_time: int
58
+ dependencies: List[str] = field(default_factory=list)
59
+ actual_output: Optional[str] = None
60
+
61
+ def __post_init__(self):
62
+ """验证字段约束。"""
63
+ # 验证 task_id 格式(支持数字ID格式:task-数字)
64
+ if not self.task_id.startswith("task-"):
65
+ raise ValueError(f"task_id 格式错误: {self.task_id}")
66
+
67
+ # 验证数字部分是否为有效数字
68
+ try:
69
+ int(self.task_id[5:]) # 提取task-后面的数字部分
70
+ except ValueError:
71
+ raise ValueError(f"task_id 格式错误: {self.task_id}")
72
+
73
+ # 验证 priority
74
+ if not (1 <= self.priority <= 5):
75
+ raise ValueError(f"priority 必须在 1-5 之间: {self.priority}")
76
+
77
+ def to_dict(self) -> Dict:
78
+ """转换为字典。"""
79
+ result = asdict(self)
80
+ result["status"] = self.status.value
81
+ result["agent_type"] = self.agent_type.value
82
+ return result
83
+
84
+ @classmethod
85
+ def from_dict(cls, data: Dict) -> "Task":
86
+ """从字典创建任务。"""
87
+ data = data.copy()
88
+ data["status"] = TaskStatus(data["status"])
89
+ data["agent_type"] = AgentType(data["agent_type"])
90
+ return cls(**data)
91
+
92
+ def update_status(
93
+ self, new_status: TaskStatus, actual_output: Optional[str] = None
94
+ ) -> bool:
95
+ """更新任务状态。"""
96
+ # 移除状态转换验证,允许任意状态转换
97
+ self.status = new_status
98
+ self.update_time = int(time.time() * 1000)
99
+ if actual_output is not None:
100
+ self.actual_output = actual_output
101
+ return True
102
+
103
+
104
+ class TaskList:
105
+ """任务列表容器。
106
+
107
+ 用于管理多个任务实体,采用有序字典存储。
108
+ """
109
+
110
+ def __init__(
111
+ self,
112
+ main_goal: str,
113
+ max_active_tasks: int = 10,
114
+ version: int = 1,
115
+ ):
116
+ """初始化任务列表。
117
+
118
+ 参数:
119
+ main_goal: 用户核心需求
120
+ max_active_tasks: 最大活跃任务数
121
+ version: 版本号
122
+ """
123
+ self.main_goal = main_goal
124
+ self.tasks: Dict[str, Task] = OrderedDict()
125
+ self.max_active_tasks = max_active_tasks
126
+ self.version = version
127
+ self._lock = Lock()
128
+
129
+ def _update_active_and_completed_lists(self):
130
+ """更新活跃任务和已完成任务列表。"""
131
+ # 这个方法主要用于内部维护,实际使用时通过属性访问
132
+ pass
133
+
134
+ @property
135
+ def active_task_ids(self) -> List[str]:
136
+ """获取活跃任务 ID 列表。"""
137
+ return [
138
+ task_id
139
+ for task_id, task in self.tasks.items()
140
+ if task.status in (TaskStatus.PENDING, TaskStatus.RUNNING)
141
+ ]
142
+
143
+ @property
144
+ def completed_task_ids(self) -> List[str]:
145
+ """获取已完成任务 ID 列表。"""
146
+ return [
147
+ task_id
148
+ for task_id, task in self.tasks.items()
149
+ if task.status == TaskStatus.COMPLETED
150
+ ]
151
+
152
+ def add_task(self, task: Task) -> bool:
153
+ """添加任务。"""
154
+ with self._lock:
155
+ if task.task_id in self.tasks:
156
+ return False
157
+ # 验证依赖关系
158
+ for dep_id in task.dependencies:
159
+ if dep_id not in self.tasks:
160
+ return False
161
+ self.tasks[task.task_id] = task
162
+ self.version += 1
163
+ return True
164
+
165
+ def get_task(self, task_id: str) -> Optional[Task]:
166
+ """获取任务。"""
167
+ return self.tasks.get(task_id)
168
+
169
+ def update_task(self, task_id: str, **kwargs) -> bool:
170
+ """更新任务。"""
171
+ with self._lock:
172
+ if task_id not in self.tasks:
173
+ return False
174
+ task = self.tasks[task_id]
175
+ for key, value in kwargs.items():
176
+ if hasattr(task, key):
177
+ setattr(task, key, value)
178
+ task.update_time = int(time.time() * 1000)
179
+ self.version += 1
180
+ return True
181
+
182
+ def to_dict(self) -> Dict:
183
+ """转换为字典。"""
184
+ return {
185
+ "main_goal": self.main_goal,
186
+ "tasks": {task_id: task.to_dict() for task_id, task in self.tasks.items()},
187
+ "max_active_tasks": self.max_active_tasks,
188
+ "version": self.version,
189
+ }
190
+
191
+ @classmethod
192
+ def from_dict(cls, data: Dict) -> "TaskList":
193
+ """从字典创建任务列表。"""
194
+ task_list = cls(
195
+ main_goal=data["main_goal"],
196
+ max_active_tasks=data.get("max_active_tasks", 10),
197
+ version=data.get("version", 1),
198
+ )
199
+ for task_id, task_data in data.get("tasks", {}).items():
200
+ task = Task.from_dict(task_data)
201
+ task_list.tasks[task_id] = task
202
+ return task_list
203
+
204
+
205
+ class TaskListManager:
206
+ """任务列表管理器。
207
+
208
+ 实现三层架构:数据层、核心逻辑层、接口层。
209
+ """
210
+
211
+ # 全局任务计数器,用于生成连续的数字任务ID
212
+ _global_task_counter = 0
213
+ _global_tasklist_counter = 0
214
+ _counter_lock = Lock()
215
+
216
+ def __init__(self, root_dir: str, persist_dir: Optional[str] = None):
217
+ """初始化任务列表管理器。
218
+
219
+ 参数:
220
+ root_dir: 项目根目录
221
+ persist_dir: 持久化目录,默认为 .jarvis/task_lists
222
+ """
223
+ self.root_dir = root_dir
224
+ self.persist_dir = persist_dir or os.path.join(
225
+ root_dir, ".jarvis", "task_lists"
226
+ )
227
+ os.makedirs(self.persist_dir, exist_ok=True)
228
+
229
+ # 内存存储:task_list_id -> TaskList
230
+ self.task_lists: Dict[str, TaskList] = {}
231
+
232
+ # 权限隔离:agent_id -> Set[task_id]
233
+ self.agent_task_mapping: Dict[str, Set[str]] = {}
234
+
235
+ # 版本快照:task_list_id -> List[Dict] (按版本号排序)
236
+ self.version_snapshots: Dict[str, List[Dict]] = {}
237
+
238
+ self._lock = Lock()
239
+
240
+ # 加载持久化数据
241
+ self._load_persisted_data()
242
+
243
+ @classmethod
244
+ def _get_next_task_id(cls) -> str:
245
+ """获取下一个连续的数字任务ID。
246
+
247
+ 返回:
248
+ str: 格式为 "task-数字" 的任务ID
249
+ """
250
+ with cls._counter_lock:
251
+ cls._global_task_counter += 1
252
+ return f"task-{cls._global_task_counter}"
253
+
254
+ @classmethod
255
+ def _get_next_tasklist_id(cls) -> str:
256
+ """获取下一个连续的数字任务列表ID。
257
+
258
+ 返回:
259
+ str: 格式为 "tasklist-数字" 的任务列表ID
260
+ """
261
+ with cls._counter_lock:
262
+ cls._global_tasklist_counter += 1
263
+ return f"tasklist-{cls._global_tasklist_counter}"
264
+
265
+ def _load_persisted_data(self):
266
+ """从磁盘加载持久化数据。"""
267
+ snapshot_file = os.path.join(self.persist_dir, "snapshots.json")
268
+ if os.path.exists(snapshot_file):
269
+ try:
270
+ with open(snapshot_file, "r", encoding="utf-8") as f:
271
+ data = json.load(f)
272
+ self.version_snapshots = data.get("snapshots", {})
273
+ except Exception as e:
274
+ PrettyOutput.auto_print(f"⚠️ 加载快照数据失败: {e}")
275
+
276
+ def _save_snapshot(self, task_list_id: str, task_list: TaskList):
277
+ """保存版本快照。"""
278
+ snapshot_file = os.path.join(self.persist_dir, "snapshots.json")
279
+ try:
280
+ snapshot_data = task_list.to_dict()
281
+ if task_list_id not in self.version_snapshots:
282
+ self.version_snapshots[task_list_id] = []
283
+ self.version_snapshots[task_list_id].append(snapshot_data)
284
+ # 只保留最近 10 个版本
285
+ if len(self.version_snapshots[task_list_id]) > 10:
286
+ self.version_snapshots[task_list_id] = self.version_snapshots[
287
+ task_list_id
288
+ ][-10:]
289
+
290
+ # 保存到磁盘
291
+ with open(snapshot_file, "w", encoding="utf-8") as f:
292
+ json.dump(
293
+ {"snapshots": self.version_snapshots},
294
+ f,
295
+ ensure_ascii=False,
296
+ indent=2,
297
+ )
298
+ except Exception as e:
299
+ PrettyOutput.auto_print(f"⚠️ 保存快照失败: {e}")
300
+
301
+ def _check_agent_permission(
302
+ self, agent_id: str, task_id: str, is_main_agent: bool
303
+ ) -> bool:
304
+ """检查 Agent 权限。
305
+
306
+ 参数:
307
+ agent_id: Agent ID
308
+ task_id: 任务 ID
309
+ is_main_agent: 是否为主 Agent
310
+
311
+ 返回:
312
+ bool: 是否有权限
313
+ """
314
+ if is_main_agent:
315
+ return True
316
+ # 子 Agent 只能访问关联的任务
317
+ return task_id in self.agent_task_mapping.get(agent_id, set())
318
+
319
+ # ========== 接口层:主 Agent 专属接口 ==========
320
+
321
+ def create_task_list(
322
+ self, main_goal: str, agent_id: str
323
+ ) -> Tuple[Optional[str], bool, Optional[str]]:
324
+ """创建任务列表容器。
325
+
326
+ 参数:
327
+ main_goal: 用户核心需求
328
+ agent_id: 主 Agent ID
329
+
330
+ 返回:
331
+ Tuple[task_list_id, status, error_msg]
332
+ """
333
+ try:
334
+ task_list_id = self._get_next_tasklist_id()
335
+ task_list = TaskList(main_goal=main_goal)
336
+
337
+ with self._lock:
338
+ self.task_lists[task_list_id] = task_list
339
+ # 主 Agent 拥有所有权限
340
+ self.agent_task_mapping[agent_id] = set()
341
+
342
+ # 保存快照
343
+ self._save_snapshot(task_list_id, task_list)
344
+
345
+ return task_list_id, True, None
346
+ except Exception as e:
347
+ return None, False, str(e)
348
+
349
+ def add_task(
350
+ self, task_list_id: str, task_info: Dict, agent_id: str
351
+ ) -> Tuple[Optional[str], bool, Optional[str]]:
352
+ """添加任务至任务列表。
353
+
354
+ 参数:
355
+ task_list_id: 任务列表 ID
356
+ task_info: 任务信息字典(含 Task 必选字段)
357
+ agent_id: 主 Agent ID
358
+
359
+ 返回:
360
+ Tuple[task_id, status, error_msg]
361
+ """
362
+ try:
363
+ with self._lock:
364
+ if task_list_id not in self.task_lists:
365
+ return None, False, "任务列表不存在"
366
+
367
+ task_list = self.task_lists[task_list_id]
368
+
369
+ # 验证必选字段
370
+ required_fields = [
371
+ "task_name",
372
+ "task_desc",
373
+ "priority",
374
+ "expected_output",
375
+ "agent_type",
376
+ ]
377
+ missing_fields = [
378
+ field for field in required_fields if field not in task_info
379
+ ]
380
+ if missing_fields:
381
+ return None, False, f"缺少必选字段: {', '.join(missing_fields)}"
382
+
383
+ # 创建任务(使用连续数字ID替代UUID)
384
+ task_id = self._get_next_task_id()
385
+ current_time = int(time.time() * 1000)
386
+
387
+ task = Task(
388
+ task_id=task_id,
389
+ task_name=task_info["task_name"],
390
+ task_desc=task_info["task_desc"],
391
+ priority=task_info["priority"],
392
+ status=TaskStatus.PENDING,
393
+ expected_output=task_info["expected_output"],
394
+ agent_type=AgentType(task_info["agent_type"]),
395
+ create_time=current_time,
396
+ update_time=current_time,
397
+ dependencies=task_info.get("dependencies", []),
398
+ )
399
+
400
+ # 验证依赖关系(检查循环依赖)
401
+ if not self._validate_dependencies(task_list, task):
402
+ return None, False, "依赖关系验证失败:存在循环依赖或无效依赖"
403
+
404
+ if not task_list.add_task(task):
405
+ return None, False, "添加任务失败:任务ID已存在或依赖无效"
406
+
407
+ # 保存快照
408
+ self._save_snapshot(task_list_id, task_list)
409
+
410
+ return task_id, True, None
411
+ except ValueError as e:
412
+ return None, False, f"字段格式错误: {str(e)}"
413
+ except Exception as e:
414
+ return None, False, str(e)
415
+
416
+ def add_tasks(
417
+ self, task_list_id: str, tasks_info: List[Dict], agent_id: str
418
+ ) -> Tuple[List[str], bool, Optional[str]]:
419
+ """批量添加任务至任务列表。
420
+
421
+ 参数:
422
+ task_list_id: 任务列表 ID
423
+ tasks_info: 任务信息字典列表(每个字典含 Task 必选字段)
424
+ agent_id: 主 Agent ID
425
+
426
+ 返回:
427
+ Tuple[task_ids, status, error_msg]
428
+ """
429
+ try:
430
+ with self._lock:
431
+ if task_list_id not in self.task_lists:
432
+ return [], False, "任务列表不存在"
433
+
434
+ task_list = self.task_lists[task_list_id]
435
+
436
+ if not tasks_info:
437
+ return [], False, "任务列表为空"
438
+
439
+ # 验证必选字段
440
+ required_fields = [
441
+ "task_name",
442
+ "task_desc",
443
+ "priority",
444
+ "expected_output",
445
+ "agent_type",
446
+ ]
447
+
448
+ # 先验证所有任务的基本字段
449
+ for idx, task_info in enumerate(tasks_info):
450
+ missing_fields = [
451
+ field for field in required_fields if field not in task_info
452
+ ]
453
+ if missing_fields:
454
+ return (
455
+ [],
456
+ False,
457
+ f"第 {idx + 1} 个任务缺少必选字段: {', '.join(missing_fields)}",
458
+ )
459
+
460
+ # 创建所有任务对象(先不添加到列表,用于验证依赖关系)
461
+ current_time = int(time.time() * 1000)
462
+ tasks_to_add = []
463
+ task_ids = []
464
+ # 创建任务名称到任务ID的映射(用于依赖关系匹配)
465
+ name_to_id_map = {}
466
+
467
+ # 第一遍:创建所有任务对象,建立名称到ID的映射
468
+ for task_info in tasks_info:
469
+ task_id = self._get_next_task_id()
470
+ task_ids.append(task_id)
471
+ task_name = task_info["task_name"]
472
+ name_to_id_map[task_name] = task_id
473
+
474
+ task = Task(
475
+ task_id=task_id,
476
+ task_name=task_name,
477
+ task_desc=task_info["task_desc"],
478
+ priority=task_info["priority"],
479
+ status=TaskStatus.PENDING,
480
+ expected_output=task_info["expected_output"],
481
+ agent_type=AgentType(task_info["agent_type"]),
482
+ create_time=current_time,
483
+ update_time=current_time,
484
+ dependencies=[], # 先不设置依赖,后续处理
485
+ )
486
+ tasks_to_add.append(task)
487
+
488
+ # 第二遍:处理依赖关系,将任务名称转换为任务ID
489
+ for idx, task_info in enumerate(tasks_info):
490
+ dependencies = task_info.get("dependencies", [])
491
+ if dependencies:
492
+ processed_deps = []
493
+ for dep in dependencies:
494
+ # 如果是任务名称,转换为任务ID
495
+ if dep in name_to_id_map:
496
+ processed_deps.append(name_to_id_map[dep])
497
+ else:
498
+ # 可能是任务ID,直接使用(也可能是已存在的任务名称)
499
+ # 检查是否是已存在的任务名称
500
+ found = False
501
+ for existing_task in task_list.tasks.values():
502
+ if existing_task.task_name == dep:
503
+ processed_deps.append(existing_task.task_id)
504
+ found = True
505
+ break
506
+ if not found:
507
+ # 假设是任务ID,直接使用
508
+ processed_deps.append(dep)
509
+ tasks_to_add[idx].dependencies = processed_deps
510
+
511
+ # 验证所有任务的依赖关系(检查循环依赖和无效依赖)
512
+ # 先检查依赖的任务是否都在本次批量添加的任务中,或者已经在任务列表中
513
+ task_id_map = {t.task_id: t for t in tasks_to_add}
514
+ for task in tasks_to_add:
515
+ for dep_id in task.dependencies:
516
+ # 检查依赖是否在本次要添加的任务中
517
+ dep_in_batch = dep_id in task_id_map
518
+ # 检查依赖是否已经在任务列表中
519
+ dep_in_list = task_list.get_task(dep_id) is not None
520
+ if not (dep_in_batch or dep_in_list):
521
+ return (
522
+ [],
523
+ False,
524
+ f"任务 {task.task_name} 的依赖 {dep_id} 不存在",
525
+ )
526
+
527
+ # 临时添加所有任务到任务列表(用于循环依赖检查)
528
+ temp_tasks = {}
529
+ for task in tasks_to_add:
530
+ temp_tasks[task.task_id] = task
531
+ task_list.tasks[task.task_id] = task
532
+
533
+ # 验证循环依赖
534
+ try:
535
+ for task in tasks_to_add:
536
+ if not self._validate_dependencies_batch(
537
+ task_list, task, task_id_map
538
+ ):
539
+ return (
540
+ [],
541
+ False,
542
+ f"任务 {task.task_name} 的依赖关系验证失败:存在循环依赖",
543
+ )
544
+ finally:
545
+ # 移除临时添加的任务
546
+ for task_id in temp_tasks:
547
+ if task_id in task_list.tasks:
548
+ del task_list.tasks[task_id]
549
+
550
+ # 所有验证通过,批量添加任务
551
+ added_task_ids = []
552
+ for task in tasks_to_add:
553
+ if task_list.add_task(task):
554
+ added_task_ids.append(task.task_id)
555
+ else:
556
+ # 如果某个任务添加失败,回滚已添加的任务
557
+ for added_id in added_task_ids:
558
+ if added_id in task_list.tasks:
559
+ del task_list.tasks[added_id]
560
+ return (
561
+ [],
562
+ False,
563
+ f"添加任务 {task.task_id} 失败:任务ID已存在或依赖无效",
564
+ )
565
+
566
+ # 保存快照
567
+ self._save_snapshot(task_list_id, task_list)
568
+
569
+ return added_task_ids, True, None
570
+ except ValueError as e:
571
+ return [], False, f"字段格式错误: {str(e)}"
572
+ except Exception as e:
573
+ return [], False, str(e)
574
+
575
+ def _validate_dependencies(self, task_list: TaskList, task: Task) -> bool:
576
+ """验证依赖关系,检查循环依赖。"""
577
+ visited = set()
578
+
579
+ def has_cycle(current_id: str) -> bool:
580
+ if current_id in visited:
581
+ return True
582
+ visited.add(current_id)
583
+ current_task = task_list.get_task(current_id)
584
+ if current_task:
585
+ for dep_id in current_task.dependencies:
586
+ if has_cycle(dep_id):
587
+ return True
588
+ visited.remove(current_id)
589
+ return False
590
+
591
+ # 检查新任务的依赖是否会导致循环
592
+ visited.add(task.task_id)
593
+ for dep_id in task.dependencies:
594
+ dep_task = task_list.get_task(dep_id)
595
+ if not dep_task:
596
+ # 依赖的任务不存在(可能在批量添加时,依赖的任务在本次批次中)
597
+ # 这种情况下,在批量添加时会单独检查,这里先返回 True
598
+ continue
599
+ if has_cycle(dep_id):
600
+ visited.remove(task.task_id)
601
+ return False # 存在循环依赖
602
+ visited.remove(task.task_id)
603
+ return True
604
+
605
+ def _validate_dependencies_batch(
606
+ self, task_list: TaskList, task: Task, batch_task_map: Dict[str, Task]
607
+ ) -> bool:
608
+ """验证批量添加时的依赖关系,检查循环依赖。"""
609
+ visited = set()
610
+
611
+ def has_cycle(current_id: str) -> bool:
612
+ if current_id in visited:
613
+ return True
614
+ visited.add(current_id)
615
+ # 先从批次中查找,再从任务列表中查找
616
+ current_task = batch_task_map.get(current_id) or task_list.get_task(
617
+ current_id
618
+ )
619
+ if current_task:
620
+ for dep_id in current_task.dependencies:
621
+ if has_cycle(dep_id):
622
+ return True
623
+ visited.remove(current_id)
624
+ return False
625
+
626
+ # 检查新任务的依赖是否会导致循环
627
+ visited.add(task.task_id)
628
+ for dep_id in task.dependencies:
629
+ if has_cycle(dep_id):
630
+ visited.remove(task.task_id)
631
+ return False # 存在循环依赖
632
+ visited.remove(task.task_id)
633
+ return True
634
+
635
+ def get_next_task(
636
+ self, task_list_id: str, agent_id: str
637
+ ) -> Tuple[Optional[Task], Optional[str]]:
638
+ """获取优先级最高的待执行任务。
639
+
640
+ 参数:
641
+ task_list_id: 任务列表 ID
642
+ agent_id: 主 Agent ID
643
+
644
+ 返回:
645
+ Tuple[task, msg]
646
+ """
647
+ with self._lock:
648
+ if task_list_id not in self.task_lists:
649
+ return None, "任务列表不存在"
650
+
651
+ task_list = self.task_lists[task_list_id]
652
+
653
+ # 获取所有待执行任务(pending 状态)
654
+ pending_tasks = [
655
+ task
656
+ for task in task_list.tasks.values()
657
+ if task.status == TaskStatus.PENDING
658
+ ]
659
+
660
+ if not pending_tasks:
661
+ return None, "暂无待执行任务"
662
+
663
+ # 检查活跃任务数限制
664
+ active_count = len(task_list.active_task_ids)
665
+ if active_count >= task_list.max_active_tasks:
666
+ return None, f"活跃任务数已达上限 ({task_list.max_active_tasks})"
667
+
668
+ # 过滤出依赖已满足的任务
669
+ ready_tasks = []
670
+ completed_ids = set(task_list.completed_task_ids)
671
+
672
+ for task in pending_tasks:
673
+ if all(dep_id in completed_ids for dep_id in task.dependencies):
674
+ ready_tasks.append(task)
675
+
676
+ if not ready_tasks:
677
+ return None, "暂无满足依赖条件的待执行任务"
678
+
679
+ # 按优先级排序(优先级高的在前),相同优先级按创建时间排序
680
+ ready_tasks.sort(key=lambda t: (-t.priority, t.create_time))
681
+
682
+ return ready_tasks[0], None
683
+
684
+ def rollback_task_list(
685
+ self, task_list_id: str, version: int, agent_id: str
686
+ ) -> Tuple[bool, Optional[str]]:
687
+ """回滚任务列表至指定版本。
688
+
689
+ 参数:
690
+ task_list_id: 任务列表 ID
691
+ version: 目标版本号
692
+ agent_id: 主 Agent ID
693
+
694
+ 返回:
695
+ Tuple[status, msg]
696
+ """
697
+ with self._lock:
698
+ if task_list_id not in self.version_snapshots:
699
+ return False, "任务列表不存在"
700
+
701
+ snapshots = self.version_snapshots[task_list_id]
702
+ target_snapshot = None
703
+ for snapshot in snapshots:
704
+ if snapshot.get("version") == version:
705
+ target_snapshot = snapshot
706
+ break
707
+
708
+ if not target_snapshot:
709
+ return False, "版本无效"
710
+
711
+ try:
712
+ task_list = TaskList.from_dict(target_snapshot)
713
+ self.task_lists[task_list_id] = task_list
714
+ return True, None
715
+ except Exception as e:
716
+ return False, f"回滚失败: {str(e)}"
717
+
718
+ # ========== 接口层:主 Agent / 子 Agent 共享接口 ==========
719
+
720
+ def update_task_status(
721
+ self,
722
+ task_list_id: str,
723
+ task_id: str,
724
+ status: str,
725
+ agent_id: str,
726
+ is_main_agent: bool,
727
+ actual_output: Optional[str] = None,
728
+ ) -> Tuple[bool, Optional[str]]:
729
+ """更新任务状态与执行结果。
730
+
731
+ 参数:
732
+ task_list_id: 任务列表 ID
733
+ task_id: 任务 ID
734
+ status: 新状态
735
+ agent_id: Agent ID
736
+ is_main_agent: 是否为主 Agent
737
+ actual_output: 实际输出(可选)
738
+
739
+ 返回:
740
+ Tuple[status, msg]
741
+ """
742
+ try:
743
+ new_status = TaskStatus(status)
744
+ except ValueError:
745
+ return False, f"无效的状态值: {status}"
746
+
747
+ try:
748
+ with self._lock:
749
+ if task_list_id not in self.task_lists:
750
+ return False, "任务列表不存在"
751
+
752
+ # 权限检查
753
+ if not self._check_agent_permission(agent_id, task_id, is_main_agent):
754
+ return False, "权限不足:无法访问该任务"
755
+
756
+ task_list = self.task_lists[task_list_id]
757
+ task = task_list.get_task(task_id)
758
+ if not task:
759
+ return False, "任务不存在"
760
+
761
+ # 状态转换
762
+ if not task.update_status(new_status, actual_output):
763
+ return False, f"无效的状态转换: {task.status.value} -> {status}"
764
+
765
+ task_list.version += 1
766
+
767
+ # 保存快照
768
+ self._save_snapshot(task_list_id, task_list)
769
+
770
+ return True, None
771
+ except Exception as e:
772
+ return False, str(e)
773
+
774
+ def get_task_detail(
775
+ self, task_list_id: str, task_id: str, agent_id: str, is_main_agent: bool
776
+ ) -> Tuple[Optional[Task], bool, Optional[str]]:
777
+ """获取任务详细信息。
778
+
779
+ 参数:
780
+ task_list_id: 任务列表 ID
781
+ task_id: 任务 ID
782
+ agent_id: Agent ID
783
+ is_main_agent: 是否为主 Agent
784
+
785
+ 返回:
786
+ Tuple[task, status, error_msg]
787
+ """
788
+ with self._lock:
789
+ if task_list_id not in self.task_lists:
790
+ return None, False, "任务列表不存在"
791
+
792
+ # 权限检查
793
+ if not self._check_agent_permission(agent_id, task_id, is_main_agent):
794
+ return None, False, "权限不足:无法访问该任务"
795
+
796
+ task_list = self.task_lists[task_list_id]
797
+ task = task_list.get_task(task_id)
798
+ if not task:
799
+ return None, False, "任务不存在"
800
+
801
+ return task, True, None
802
+
803
+ def register_sub_agent(
804
+ self, agent_id: str, task_ids: List[str], main_agent_id: str
805
+ ) -> bool:
806
+ """注册子 Agent 与任务的关联关系。
807
+
808
+ 参数:
809
+ agent_id: 子 Agent ID
810
+ task_ids: 关联的任务 ID 列表
811
+ main_agent_id: 主 Agent ID(用于权限验证)
812
+
813
+ 返回:
814
+ bool: 是否成功
815
+ """
816
+ with self._lock:
817
+ # 验证主 Agent 权限(简化实现,实际可以更严格)
818
+ if main_agent_id not in self.agent_task_mapping:
819
+ return False
820
+
821
+ self.agent_task_mapping[agent_id] = set(task_ids)
822
+ return True
823
+
824
+ def get_task_list(self, task_list_id: str) -> Optional[TaskList]:
825
+ """获取任务列表(内部方法)。"""
826
+ return self.task_lists.get(task_list_id)
827
+
828
+ def get_task_list_summary(self, task_list_id: str) -> Optional[Dict]:
829
+ """获取任务列表摘要信息。
830
+
831
+ 返回:
832
+ Dict: 包含任务统计信息的字典
833
+ """
834
+ with self._lock:
835
+ if task_list_id not in self.task_lists:
836
+ return None
837
+
838
+ task_list = self.task_lists[task_list_id]
839
+ tasks = list(task_list.tasks.values())
840
+
841
+ summary = {
842
+ "task_list_id": task_list_id,
843
+ "main_goal": task_list.main_goal,
844
+ "version": task_list.version,
845
+ "total_tasks": len(tasks),
846
+ "pending": len([t for t in tasks if t.status == TaskStatus.PENDING]),
847
+ "running": len([t for t in tasks if t.status == TaskStatus.RUNNING]),
848
+ "completed": len(
849
+ [t for t in tasks if t.status == TaskStatus.COMPLETED]
850
+ ),
851
+ "failed": len([t for t in tasks if t.status == TaskStatus.FAILED]),
852
+ "abandoned": len(
853
+ [t for t in tasks if t.status == TaskStatus.ABANDONED]
854
+ ),
855
+ "tasks": [
856
+ {
857
+ "task_id": t.task_id,
858
+ "task_name": t.task_name,
859
+ "task_desc": t.task_desc,
860
+ "status": t.status.value,
861
+ "priority": t.priority,
862
+ "agent_type": t.agent_type.value,
863
+ "dependencies": t.dependencies,
864
+ "actual_output": t.actual_output,
865
+ }
866
+ for t in tasks
867
+ ],
868
+ }
869
+ return summary