jarvis-ai-assistant 0.7.0__py3-none-any.whl → 0.7.6__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 (159) hide show
  1. jarvis/__init__.py +1 -1
  2. jarvis/jarvis_agent/__init__.py +243 -139
  3. jarvis/jarvis_agent/agent_manager.py +5 -10
  4. jarvis/jarvis_agent/builtin_input_handler.py +2 -6
  5. jarvis/jarvis_agent/config_editor.py +2 -7
  6. jarvis/jarvis_agent/event_bus.py +82 -12
  7. jarvis/jarvis_agent/file_context_handler.py +265 -15
  8. jarvis/jarvis_agent/file_methodology_manager.py +3 -4
  9. jarvis/jarvis_agent/jarvis.py +113 -98
  10. jarvis/jarvis_agent/language_extractors/__init__.py +57 -0
  11. jarvis/jarvis_agent/language_extractors/c_extractor.py +21 -0
  12. jarvis/jarvis_agent/language_extractors/cpp_extractor.py +21 -0
  13. jarvis/jarvis_agent/language_extractors/go_extractor.py +21 -0
  14. jarvis/jarvis_agent/language_extractors/java_extractor.py +84 -0
  15. jarvis/jarvis_agent/language_extractors/javascript_extractor.py +79 -0
  16. jarvis/jarvis_agent/language_extractors/python_extractor.py +21 -0
  17. jarvis/jarvis_agent/language_extractors/rust_extractor.py +21 -0
  18. jarvis/jarvis_agent/language_extractors/typescript_extractor.py +84 -0
  19. jarvis/jarvis_agent/language_support_info.py +486 -0
  20. jarvis/jarvis_agent/main.py +6 -12
  21. jarvis/jarvis_agent/memory_manager.py +7 -16
  22. jarvis/jarvis_agent/methodology_share_manager.py +10 -16
  23. jarvis/jarvis_agent/prompt_manager.py +1 -1
  24. jarvis/jarvis_agent/prompts.py +193 -171
  25. jarvis/jarvis_agent/protocols.py +8 -12
  26. jarvis/jarvis_agent/run_loop.py +77 -14
  27. jarvis/jarvis_agent/session_manager.py +2 -3
  28. jarvis/jarvis_agent/share_manager.py +12 -21
  29. jarvis/jarvis_agent/shell_input_handler.py +1 -2
  30. jarvis/jarvis_agent/task_analyzer.py +26 -4
  31. jarvis/jarvis_agent/task_manager.py +11 -27
  32. jarvis/jarvis_agent/tool_executor.py +2 -3
  33. jarvis/jarvis_agent/tool_share_manager.py +12 -24
  34. jarvis/jarvis_agent/web_server.py +55 -20
  35. jarvis/jarvis_c2rust/__init__.py +5 -5
  36. jarvis/jarvis_c2rust/cli.py +461 -499
  37. jarvis/jarvis_c2rust/collector.py +45 -53
  38. jarvis/jarvis_c2rust/constants.py +26 -0
  39. jarvis/jarvis_c2rust/library_replacer.py +264 -132
  40. jarvis/jarvis_c2rust/llm_module_agent.py +162 -190
  41. jarvis/jarvis_c2rust/loaders.py +207 -0
  42. jarvis/jarvis_c2rust/models.py +28 -0
  43. jarvis/jarvis_c2rust/optimizer.py +1592 -395
  44. jarvis/jarvis_c2rust/transpiler.py +1722 -1064
  45. jarvis/jarvis_c2rust/utils.py +385 -0
  46. jarvis/jarvis_code_agent/build_validation_config.py +2 -3
  47. jarvis/jarvis_code_agent/code_agent.py +394 -320
  48. jarvis/jarvis_code_agent/code_analyzer/__init__.py +3 -0
  49. jarvis/jarvis_code_agent/code_analyzer/build_validator/base.py +4 -0
  50. jarvis/jarvis_code_agent/code_analyzer/build_validator/cmake.py +17 -2
  51. jarvis/jarvis_code_agent/code_analyzer/build_validator/fallback.py +3 -0
  52. jarvis/jarvis_code_agent/code_analyzer/build_validator/go.py +36 -4
  53. jarvis/jarvis_code_agent/code_analyzer/build_validator/java_gradle.py +9 -0
  54. jarvis/jarvis_code_agent/code_analyzer/build_validator/java_maven.py +9 -0
  55. jarvis/jarvis_code_agent/code_analyzer/build_validator/makefile.py +12 -1
  56. jarvis/jarvis_code_agent/code_analyzer/build_validator/nodejs.py +22 -5
  57. jarvis/jarvis_code_agent/code_analyzer/build_validator/python.py +57 -32
  58. jarvis/jarvis_code_agent/code_analyzer/build_validator/rust.py +62 -6
  59. jarvis/jarvis_code_agent/code_analyzer/build_validator/validator.py +8 -9
  60. jarvis/jarvis_code_agent/code_analyzer/context_manager.py +290 -5
  61. jarvis/jarvis_code_agent/code_analyzer/language_support.py +21 -0
  62. jarvis/jarvis_code_agent/code_analyzer/languages/__init__.py +21 -3
  63. jarvis/jarvis_code_agent/code_analyzer/languages/c_cpp_language.py +72 -4
  64. jarvis/jarvis_code_agent/code_analyzer/languages/go_language.py +35 -3
  65. jarvis/jarvis_code_agent/code_analyzer/languages/java_language.py +212 -0
  66. jarvis/jarvis_code_agent/code_analyzer/languages/javascript_language.py +254 -0
  67. jarvis/jarvis_code_agent/code_analyzer/languages/python_language.py +52 -2
  68. jarvis/jarvis_code_agent/code_analyzer/languages/rust_language.py +73 -1
  69. jarvis/jarvis_code_agent/code_analyzer/languages/typescript_language.py +280 -0
  70. jarvis/jarvis_code_agent/code_analyzer/llm_context_recommender.py +306 -152
  71. jarvis/jarvis_code_agent/code_analyzer/structured_code.py +556 -0
  72. jarvis/jarvis_code_agent/code_analyzer/symbol_extractor.py +193 -18
  73. jarvis/jarvis_code_agent/code_analyzer/tree_sitter_extractor.py +18 -8
  74. jarvis/jarvis_code_agent/lint.py +258 -27
  75. jarvis/jarvis_code_agent/utils.py +0 -1
  76. jarvis/jarvis_code_analysis/code_review.py +19 -24
  77. jarvis/jarvis_data/config_schema.json +53 -26
  78. jarvis/jarvis_git_squash/main.py +4 -5
  79. jarvis/jarvis_git_utils/git_commiter.py +44 -49
  80. jarvis/jarvis_mcp/sse_mcp_client.py +20 -27
  81. jarvis/jarvis_mcp/stdio_mcp_client.py +11 -12
  82. jarvis/jarvis_mcp/streamable_mcp_client.py +15 -14
  83. jarvis/jarvis_memory_organizer/memory_organizer.py +55 -74
  84. jarvis/jarvis_methodology/main.py +32 -48
  85. jarvis/jarvis_multi_agent/__init__.py +79 -61
  86. jarvis/jarvis_multi_agent/main.py +3 -7
  87. jarvis/jarvis_platform/base.py +469 -199
  88. jarvis/jarvis_platform/human.py +7 -8
  89. jarvis/jarvis_platform/kimi.py +30 -36
  90. jarvis/jarvis_platform/openai.py +65 -27
  91. jarvis/jarvis_platform/registry.py +26 -10
  92. jarvis/jarvis_platform/tongyi.py +24 -25
  93. jarvis/jarvis_platform/yuanbao.py +31 -42
  94. jarvis/jarvis_platform_manager/main.py +66 -77
  95. jarvis/jarvis_platform_manager/service.py +8 -13
  96. jarvis/jarvis_rag/cli.py +49 -51
  97. jarvis/jarvis_rag/embedding_manager.py +13 -18
  98. jarvis/jarvis_rag/llm_interface.py +8 -9
  99. jarvis/jarvis_rag/query_rewriter.py +10 -21
  100. jarvis/jarvis_rag/rag_pipeline.py +24 -27
  101. jarvis/jarvis_rag/reranker.py +4 -5
  102. jarvis/jarvis_rag/retriever.py +28 -30
  103. jarvis/jarvis_sec/__init__.py +220 -3520
  104. jarvis/jarvis_sec/agents.py +143 -0
  105. jarvis/jarvis_sec/analysis.py +276 -0
  106. jarvis/jarvis_sec/cli.py +29 -6
  107. jarvis/jarvis_sec/clustering.py +1439 -0
  108. jarvis/jarvis_sec/file_manager.py +427 -0
  109. jarvis/jarvis_sec/parsers.py +73 -0
  110. jarvis/jarvis_sec/prompts.py +268 -0
  111. jarvis/jarvis_sec/report.py +83 -4
  112. jarvis/jarvis_sec/review.py +453 -0
  113. jarvis/jarvis_sec/utils.py +499 -0
  114. jarvis/jarvis_sec/verification.py +848 -0
  115. jarvis/jarvis_sec/workflow.py +7 -0
  116. jarvis/jarvis_smart_shell/main.py +38 -87
  117. jarvis/jarvis_stats/cli.py +1 -1
  118. jarvis/jarvis_stats/stats.py +7 -7
  119. jarvis/jarvis_stats/storage.py +15 -21
  120. jarvis/jarvis_tools/clear_memory.py +3 -20
  121. jarvis/jarvis_tools/cli/main.py +20 -23
  122. jarvis/jarvis_tools/edit_file.py +1066 -0
  123. jarvis/jarvis_tools/execute_script.py +42 -21
  124. jarvis/jarvis_tools/file_analyzer.py +6 -9
  125. jarvis/jarvis_tools/generate_new_tool.py +11 -20
  126. jarvis/jarvis_tools/lsp_client.py +1552 -0
  127. jarvis/jarvis_tools/methodology.py +2 -3
  128. jarvis/jarvis_tools/read_code.py +1525 -87
  129. jarvis/jarvis_tools/read_symbols.py +2 -3
  130. jarvis/jarvis_tools/read_webpage.py +7 -10
  131. jarvis/jarvis_tools/registry.py +370 -181
  132. jarvis/jarvis_tools/retrieve_memory.py +20 -19
  133. jarvis/jarvis_tools/rewrite_file.py +105 -0
  134. jarvis/jarvis_tools/save_memory.py +3 -15
  135. jarvis/jarvis_tools/search_web.py +3 -7
  136. jarvis/jarvis_tools/sub_agent.py +17 -6
  137. jarvis/jarvis_tools/sub_code_agent.py +14 -16
  138. jarvis/jarvis_tools/virtual_tty.py +54 -32
  139. jarvis/jarvis_utils/clipboard.py +7 -10
  140. jarvis/jarvis_utils/config.py +98 -63
  141. jarvis/jarvis_utils/embedding.py +5 -5
  142. jarvis/jarvis_utils/fzf.py +8 -8
  143. jarvis/jarvis_utils/git_utils.py +81 -67
  144. jarvis/jarvis_utils/input.py +24 -49
  145. jarvis/jarvis_utils/jsonnet_compat.py +465 -0
  146. jarvis/jarvis_utils/methodology.py +33 -35
  147. jarvis/jarvis_utils/utils.py +245 -202
  148. {jarvis_ai_assistant-0.7.0.dist-info → jarvis_ai_assistant-0.7.6.dist-info}/METADATA +205 -70
  149. jarvis_ai_assistant-0.7.6.dist-info/RECORD +218 -0
  150. jarvis/jarvis_agent/edit_file_handler.py +0 -584
  151. jarvis/jarvis_agent/rewrite_file_handler.py +0 -141
  152. jarvis/jarvis_agent/task_planner.py +0 -496
  153. jarvis/jarvis_platform/ai8.py +0 -332
  154. jarvis/jarvis_tools/ask_user.py +0 -54
  155. jarvis_ai_assistant-0.7.0.dist-info/RECORD +0 -192
  156. {jarvis_ai_assistant-0.7.0.dist-info → jarvis_ai_assistant-0.7.6.dist-info}/WHEEL +0 -0
  157. {jarvis_ai_assistant-0.7.0.dist-info → jarvis_ai_assistant-0.7.6.dist-info}/entry_points.txt +0 -0
  158. {jarvis_ai_assistant-0.7.0.dist-info → jarvis_ai_assistant-0.7.6.dist-info}/licenses/LICENSE +0 -0
  159. {jarvis_ai_assistant-0.7.0.dist-info → jarvis_ai_assistant-0.7.6.dist-info}/top_level.txt +0 -0
@@ -4,7 +4,6 @@ from pathlib import Path
4
4
  from typing import Any, Dict, List, Optional
5
5
 
6
6
  from jarvis.jarvis_utils.config import get_data_dir, get_max_input_token_count
7
- from jarvis.jarvis_utils.output import OutputType, PrettyOutput
8
7
  from jarvis.jarvis_utils.globals import get_short_term_memories
9
8
  from jarvis.jarvis_utils.embedding import get_context_token_count
10
9
 
@@ -13,17 +12,7 @@ class RetrieveMemoryTool:
13
12
  """检索记忆工具,用于从长短期记忆系统中检索信息"""
14
13
 
15
14
  name = "retrieve_memory"
16
- description = """从长短期记忆系统中检索信息。
17
-
18
- 支持的记忆类型:
19
- - project_long_term: 项目长期记忆(与当前项目相关的信息)
20
- - global_long_term: 全局长期记忆(通用的信息、用户喜好、知识、方法等)
21
- - short_term: 短期记忆(当前任务相关的信息)
22
- - all: 从所有类型中检索
23
-
24
- 可以通过标签过滤检索结果,支持多个标签(满足任一标签即可)
25
- 注意:标签数量建议不要超过10个,以保证检索效率
26
- """
15
+ description = "从长短期记忆系统中检索信息。支持按类型(project_long_term/global_long_term/short_term/all)和标签过滤,标签建议不超过10个。"
27
16
 
28
17
  parameters = {
29
18
  "type": "object",
@@ -99,9 +88,7 @@ class RetrieveMemoryTool:
99
88
 
100
89
  memories.append(memory_data)
101
90
  except Exception as e:
102
- PrettyOutput.print(
103
- f"读取记忆文件 {memory_file} 失败: {str(e)}", OutputType.WARNING
104
- )
91
+ print(f"⚠️ 读取记忆文件 {memory_file} 失败: {str(e)}")
105
92
 
106
93
  return memories
107
94
 
@@ -131,9 +118,23 @@ class RetrieveMemoryTool:
131
118
  # 按创建时间排序(最新的在前)
132
119
  all_memories.sort(key=lambda x: x.get("created_at", ""), reverse=True)
133
120
 
134
- # 获取最大输入token数的2/3作为记忆的token限制
135
- max_input_tokens = get_max_input_token_count()
136
- memory_token_limit = int(max_input_tokens * 2 / 3)
121
+ # 优先使用剩余token数量,回退到输入窗口限制
122
+ memory_token_limit = None
123
+ agent = args.get("agent")
124
+ if agent and hasattr(agent, "model"):
125
+ try:
126
+ remaining_tokens = agent.model.get_remaining_token_count()
127
+ # 使用剩余token的2/3作为限制,保留1/3作为安全余量
128
+ memory_token_limit = int(remaining_tokens * 2 / 3)
129
+ if memory_token_limit <= 0:
130
+ memory_token_limit = None
131
+ except Exception:
132
+ pass
133
+
134
+ # 回退方案:使用输入窗口的2/3
135
+ if memory_token_limit is None:
136
+ max_input_tokens = get_max_input_token_count()
137
+ memory_token_limit = int(max_input_tokens * 2 / 3)
137
138
 
138
139
  # 基于token限制和条数限制筛选记忆
139
140
  filtered_memories: List[Dict[str, Any]] = []
@@ -223,5 +224,5 @@ class RetrieveMemoryTool:
223
224
 
224
225
  except Exception as e:
225
226
  error_msg = f"检索记忆失败: {str(e)}"
226
- PrettyOutput.print(error_msg, OutputType.ERROR)
227
+ print(f"❌ {error_msg}")
227
228
  return {"success": False, "stdout": "", "stderr": error_msg}
@@ -0,0 +1,105 @@
1
+ # -*- coding: utf-8 -*-
2
+ import os
3
+ from typing import Any, Dict
4
+
5
+
6
+
7
+ class RewriteFileTool:
8
+ """文件重写工具,用于完全重写文件内容"""
9
+
10
+ name = "rewrite_file"
11
+ description = "完全重写文件内容,适用于新增文件或大范围改写。具备失败回滚能力。局部修改请使用 edit_file。\n\n ⚠️ 重要提示:\n - 不要一次重写太多内容,建议分多次进行,避免超过LLM的上下文窗口大小\n - 如果文件内容较长(超过2048字符),建议采用以下策略:\n 1. 第一次调用 rewrite_file 写入部分内容(如文件的前半部分或关键部分)\n 2. 然后多次调用 edit_file 工具,使用 insert_after 操作补充后续内容\n - 这样可以避免单次操作内容过长导致上下文溢出"
12
+
13
+ parameters = {
14
+ "type": "object",
15
+ "properties": {
16
+ "file_path": {
17
+ "type": "string",
18
+ "description": "要重写的文件路径(支持绝对路径和相对路径)",
19
+ },
20
+ "content": {
21
+ "type": "string",
22
+ "description": "新的文件完整内容",
23
+ },
24
+ },
25
+ "required": ["file_path", "content"],
26
+ }
27
+
28
+ def __init__(self):
29
+ """初始化文件重写工具"""
30
+ pass
31
+
32
+ def execute(self, args: Dict[str, Any]) -> Dict[str, Any]:
33
+ """执行文件重写操作"""
34
+ try:
35
+ file_path = args.get("file_path")
36
+ content = args.get("content")
37
+
38
+ if not file_path:
39
+ return {
40
+ "success": False,
41
+ "stdout": "",
42
+ "stderr": "缺少必需参数:file_path",
43
+ }
44
+
45
+ if content is None:
46
+ return {
47
+ "success": False,
48
+ "stdout": "",
49
+ "stderr": "缺少必需参数:content",
50
+ }
51
+
52
+ abs_path = os.path.abspath(file_path)
53
+ original_content = None
54
+ processed = False
55
+
56
+ try:
57
+ file_exists = os.path.exists(abs_path)
58
+ if file_exists:
59
+ with open(abs_path, "r", encoding="utf-8") as rf:
60
+ original_content = rf.read()
61
+
62
+ os.makedirs(os.path.dirname(abs_path), exist_ok=True)
63
+ with open(abs_path, "w", encoding="utf-8") as wf:
64
+ wf.write(content)
65
+ processed = True
66
+
67
+ # 记录 REWRITE 操作调用统计
68
+ try:
69
+ from jarvis.jarvis_stats.stats import StatsManager
70
+
71
+ StatsManager.increment("rewrite_file", group="tool")
72
+ except Exception:
73
+ pass
74
+
75
+ return {
76
+ "success": True,
77
+ "stdout": f"文件 {abs_path} 重写成功",
78
+ "stderr": "",
79
+ }
80
+
81
+ except Exception as e:
82
+ # 回滚已修改内容
83
+ try:
84
+ if processed:
85
+ if original_content is None:
86
+ if os.path.exists(abs_path):
87
+ os.remove(abs_path)
88
+ else:
89
+ with open(abs_path, "w", encoding="utf-8") as wf:
90
+ wf.write(original_content)
91
+ except Exception:
92
+ pass
93
+ error_msg = f"文件重写失败: {str(e)}"
94
+ print(f"❌ {error_msg}")
95
+ return {
96
+ "success": False,
97
+ "stdout": "",
98
+ "stderr": error_msg,
99
+ }
100
+
101
+ except Exception as e:
102
+ error_msg = f"文件重写失败: {str(e)}"
103
+ print(f"❌ {error_msg}")
104
+ return {"success": False, "stdout": "", "stderr": error_msg}
105
+
@@ -6,7 +6,6 @@ from pathlib import Path
6
6
  from typing import Any, Dict
7
7
 
8
8
  from jarvis.jarvis_utils.config import get_data_dir
9
- from jarvis.jarvis_utils.output import OutputType, PrettyOutput
10
9
  from jarvis.jarvis_utils.globals import add_short_term_memory
11
10
 
12
11
 
@@ -14,18 +13,7 @@ class SaveMemoryTool:
14
13
  """保存记忆工具,用于将信息保存到长短期记忆系统"""
15
14
 
16
15
  name = "save_memory"
17
- description = """保存信息到长短期记忆系统。
18
-
19
- 支持批量保存多条记忆,可以同时保存不同类型的记忆。
20
-
21
- 支持的记忆类型:
22
- - project_long_term: 项目长期记忆(与当前项目相关的信息)
23
- - global_long_term: 全局长期记忆(通用的信息、用户喜好、知识、方法等)
24
- - short_term: 短期记忆(当前任务相关的信息)
25
-
26
- 项目长期记忆存储在当前目录的 .jarvis/memory 下
27
- 全局长期记忆和短期记忆存储在数据目录的 memory 子目录下
28
- """
16
+ description = "保存信息到长短期记忆系统。支持批量保存,记忆类型:project_long_term(项目长期)、global_long_term(全局长期)、short_term(短期)。"
29
17
 
30
18
  parameters = {
31
19
  "type": "object",
@@ -163,7 +151,7 @@ class SaveMemoryTool:
163
151
  except Exception as e:
164
152
  failed_count += 1
165
153
  error_msg = f"保存第 {i+1} 条记忆失败: {str(e)}"
166
- PrettyOutput.print(error_msg, OutputType.ERROR)
154
+ print(f"❌ {error_msg}")
167
155
  results.append(
168
156
  {
169
157
  "error": error_msg,
@@ -190,5 +178,5 @@ class SaveMemoryTool:
190
178
 
191
179
  except Exception as e:
192
180
  error_msg = f"保存记忆失败: {str(e)}"
193
- PrettyOutput.print(error_msg, OutputType.ERROR)
181
+ print(f"❌ {error_msg}")
194
182
  return {"success": False, "stdout": "", "stderr": error_msg}
@@ -17,7 +17,6 @@ from jarvis.jarvis_utils.config import (
17
17
  get_web_search_model_name,
18
18
  )
19
19
  from jarvis.jarvis_utils.http import get as http_get
20
- from jarvis.jarvis_utils.output import OutputType, PrettyOutput
21
20
 
22
21
 
23
22
  class SearchWebTool:
@@ -65,12 +64,9 @@ class SearchWebTool:
65
64
  visited_urls.append(url)
66
65
  visited_count += 1
67
66
  except requests.exceptions.HTTPError as e:
68
- PrettyOutput.print(
69
- f"⚠️ HTTP错误 {e.response.status_code} 访问 {url}",
70
- OutputType.WARNING,
71
- )
67
+ print(f"⚠️ HTTP错误 {e.response.status_code} 访问 {url}")
72
68
  except requests.exceptions.RequestException as e:
73
- PrettyOutput.print(f"⚠️ 请求错误: {e}", OutputType.WARNING)
69
+ print(f"⚠️ 请求错误: {e}")
74
70
 
75
71
  if not full_content.strip():
76
72
  return {
@@ -108,7 +104,7 @@ class SearchWebTool:
108
104
  return {"stdout": summary, "stderr": "", "success": True}
109
105
 
110
106
  except Exception as e:
111
- PrettyOutput.print(f"❌ 网页搜索过程中发生错误: {e}", OutputType.ERROR)
107
+ print(f"❌ 网页搜索过程中发生错误: {e}")
112
108
  return {
113
109
  "stdout": "",
114
110
  "stderr": f"网页搜索过程中发生错误: {e}",
@@ -15,7 +15,6 @@ import json
15
15
 
16
16
  from jarvis.jarvis_agent import Agent
17
17
  from jarvis.jarvis_utils.globals import delete_agent
18
- from jarvis.jarvis_utils.output import OutputType, PrettyOutput
19
18
 
20
19
 
21
20
  class SubAgentTool:
@@ -28,7 +27,7 @@ class SubAgentTool:
28
27
 
29
28
  # 必须与文件名一致,供 ToolRegistry 自动注册
30
29
  name = "sub_agent"
31
- description = "将子任务交给通用 Agent 执行,并返回执行结果(继承父Agent部分配置:model_group、input_handler、execute_tool_confirm、multiline_inputer、non_interactive、use_methodology、use_analysis;其他参数需显式提供,自动完成并生成总结)。"
30
+ description = "将子任务交给通用 Agent 执行并返回结果(继承父Agent部分配置,自动完成并生成总结)。"
32
31
  parameters = {
33
32
  "type": "object",
34
33
  "properties": {
@@ -158,6 +157,21 @@ class SubAgentTool:
158
157
  non_interactive=parent_non_interactive,
159
158
  )
160
159
 
160
+ # 禁用 sub_agent 和 sub_code_agent,避免无限递归
161
+ try:
162
+ # 获取当前启用的工具列表
163
+ tool_registry = agent.get_tool_registry()
164
+ if tool_registry:
165
+ current_tools = [t.get("name") for t in tool_registry.get_all_tools() if isinstance(t, dict) and t.get("name")]
166
+ # 过滤掉禁止的工具
167
+ forbidden_tools = {"sub_agent", "sub_code_agent"}
168
+ filtered_tools = [t for t in current_tools if t not in forbidden_tools]
169
+ if filtered_tools:
170
+ agent.set_use_tools(filtered_tools)
171
+ except Exception:
172
+ # 如果禁用工具失败,不影响主流程
173
+ pass
174
+
161
175
  # 校验子Agent所用模型是否有效,必要时回退到平台可用模型
162
176
  try:
163
177
  platform = getattr(agent, "model", None)
@@ -167,10 +181,7 @@ class SubAgentTool:
167
181
  available_names = [m for m, _ in available_models]
168
182
  current_model_name = platform.name()
169
183
  if current_model_name not in available_names:
170
- PrettyOutput.print(
171
- f"检测到子Agent模型 {current_model_name} 不存在于平台 {platform.platform_name()} 的可用模型列表,将回退到 {available_names[0]}",
172
- OutputType.WARNING,
173
- )
184
+ print(f"⚠️ 检测到子Agent模型 {current_model_name} 不存在于平台 {platform.platform_name()} 的可用模型列表,将回退到 {available_names[0]}")
174
185
  platform.set_model_name(available_names[0])
175
186
  except Exception:
176
187
  # 获取模型列表或设置模型失败时,保持原设置并继续,交由底层报错处理
@@ -14,7 +14,6 @@ from typing import Any, Dict, List
14
14
  from jarvis.jarvis_code_agent.code_agent import CodeAgent
15
15
  from jarvis.jarvis_utils.globals import delete_agent
16
16
  from jarvis.jarvis_utils.config import set_config, get_git_check_mode
17
- from jarvis.jarvis_utils.output import OutputType, PrettyOutput
18
17
 
19
18
 
20
19
  class SubCodeAgentTool:
@@ -27,7 +26,7 @@ class SubCodeAgentTool:
27
26
 
28
27
  # 必须与文件名一致,供 ToolRegistry 自动注册
29
28
  name = "sub_code_agent"
30
- description = "将子任务交给 CodeAgent 执行,并返回执行结果(使用系统默认配置,自动完成并生成总结)。"
29
+ description = "将子任务交给 CodeAgent 执行并返回结果(自动完成并生成总结)。"
31
30
  parameters = {
32
31
  "type": "object",
33
32
  "properties": {
@@ -109,21 +108,20 @@ class SubCodeAgentTool:
109
108
  pass
110
109
 
111
110
  # 依据父Agent已启用工具集,推导 append_tools(作为在 CodeAgent 基础工具上的增量)
111
+ # 禁用 sub_agent 和 sub_code_agent,避免无限递归
112
+ forbidden_tools = {"sub_agent", "sub_code_agent"}
112
113
  append_tools = None
113
114
  try:
114
115
  base_tools = [
115
116
  "execute_script",
116
- "search_web",
117
- "ask_user",
118
117
  "read_code",
119
-
120
- "save_memory",
121
- "retrieve_memory",
122
- "clear_memory",
123
- "sub_code_agent",
118
+ "edit_file",
119
+ "rewrite_file",
120
+ "lsp_client",
124
121
  ]
125
122
  if use_tools:
126
- extras = [t for t in use_tools if t not in base_tools]
123
+ # 过滤掉基础工具和禁止的工具
124
+ extras = [t for t in use_tools if t not in base_tools and t not in forbidden_tools]
127
125
  append_tools = ",".join(extras) if extras else None
128
126
  except Exception:
129
127
  append_tools = None
@@ -158,9 +156,12 @@ class SubCodeAgentTool:
158
156
  # 子Agent需要自动完成
159
157
  try:
160
158
  code_agent.auto_complete = True
161
- # 同步父Agent工具使用集(如可用)
159
+ # 同步父Agent工具使用集(如可用),但禁用 sub_agent 和 sub_code_agent 避免无限递归
162
160
  if use_tools:
163
- code_agent.set_use_tools(use_tools)
161
+ forbidden_tools = {"sub_agent", "sub_code_agent"}
162
+ filtered_tools = [t for t in use_tools if t not in forbidden_tools]
163
+ if filtered_tools:
164
+ code_agent.set_use_tools(filtered_tools)
164
165
  # 同步父Agent的模型名称(如可用),以尽量保持平台与模型一致
165
166
  if (
166
167
  parent_agent is not None
@@ -181,10 +182,7 @@ class SubCodeAgentTool:
181
182
  available_names = [m for m, _ in available_models]
182
183
  current_model_name = model_obj.name()
183
184
  if current_model_name not in available_names:
184
- PrettyOutput.print(
185
- f"检测到子CodeAgent模型 {current_model_name} 不存在于平台 {model_obj.platform_name()} 的可用模型列表,将回退到 {available_names[0]}",
186
- OutputType.WARNING,
187
- )
185
+ print(f"⚠️ 检测到子CodeAgent模型 {current_model_name} 不存在于平台 {model_obj.platform_name()} 的可用模型列表,将回退到 {available_names[0]}")
188
186
  model_obj.set_model_name(available_names[0])
189
187
  except Exception:
190
188
  # 获取模型列表或设置模型失败时,保持原设置并继续,交由底层报错处理
@@ -3,7 +3,6 @@ import os
3
3
  import sys
4
4
  import time
5
5
  from typing import Any, Dict, TYPE_CHECKING
6
- from jarvis.jarvis_utils.output import OutputType, PrettyOutput
7
6
 
8
7
  # 为了类型检查,总是导入这些模块
9
8
  if TYPE_CHECKING:
@@ -19,12 +18,7 @@ else:
19
18
 
20
19
  class VirtualTTYTool:
21
20
  name = "virtual_tty"
22
- description = (
23
- "控制虚拟终端执行各种操作,如启动终端、输入命令、获取输出等。"
24
- + "与execute_script不同,此工具会创建一个持久的虚拟终端会话,可以连续执行多个命令,并保持终端状态。"
25
- + "适用于需要交互式操作的场景,如运行需要用户输入的交互式程序(如:ssh连接、sftp传输、gdb/dlv调试等)。"
26
- + "注意:Windows平台功能有限,某些Unix特有功能可能不可用。"
27
- )
21
+ description = "控制虚拟终端执行交互式操作(如ssh、sftp、gdb等)。与execute_script不同,此工具创建持久会话,保持终端状态。Windows平台功能有限。"
28
22
  parameters = {
29
23
  "type": "object",
30
24
  "properties": {
@@ -42,19 +36,19 @@ class VirtualTTYTool:
42
36
  },
43
37
  "keys": {
44
38
  "type": "string",
45
- "description": "要发送的按键序列(仅支持单行输入,当action为send_keys时有效)",
39
+ "description": "要发送的按键序列(仅当action为send_keys时有效)",
46
40
  },
47
41
  "add_enter": {
48
42
  "type": "boolean",
49
- "description": "是否在单行命令末尾自动添加回车符(仅当action为send_keys时有效,默认为true)",
43
+ "description": "是否在命令末尾自动添加回车符(仅当action为send_keys时有效,默认true)",
50
44
  },
51
45
  "timeout": {
52
46
  "type": "number",
53
- "description": "等待输出的超时时间(秒,仅当action为send_keys或output时有效,默认为5.0)",
47
+ "description": "等待输出的超时时间(秒,仅当action为send_keys或output时有效,默认5.0)",
54
48
  },
55
49
  "tty_id": {
56
50
  "type": "string",
57
- "description": "虚拟终端的唯一标识符(默认为'default')",
51
+ "description": "虚拟终端的唯一标识符(默认'default')",
58
52
  },
59
53
  },
60
54
  "required": ["action"],
@@ -83,6 +77,8 @@ class VirtualTTYTool:
83
77
  # 确保agent有tty_sessions字典
84
78
  if not hasattr(agent, "tty_sessions"):
85
79
  agent.tty_sessions = {}
80
+ elif agent.tty_sessions is None:
81
+ agent.tty_sessions = {}
86
82
 
87
83
  # 如果指定的tty_id不存在,为其创建一个新的tty_data
88
84
  if tty_id not in agent.tty_sessions:
@@ -116,9 +112,7 @@ class VirtualTTYTool:
116
112
  try:
117
113
  if action == "launch":
118
114
  if args.get("keys", "") != "":
119
- PrettyOutput.print(
120
- "启动虚拟终端时,不能同时指定 keys 参数", OutputType.ERROR
121
- )
115
+ print("❌ 启动虚拟终端时,不能同时指定 keys 参数")
122
116
  return {
123
117
  "success": False,
124
118
  "stdout": "",
@@ -127,9 +121,7 @@ class VirtualTTYTool:
127
121
 
128
122
  result = self._launch_tty(agent, tty_id)
129
123
  if not result["success"]:
130
- PrettyOutput.print(
131
- f"启动虚拟终端 [{tty_id}] 失败", OutputType.ERROR
132
- )
124
+ print(f"❌ 启动虚拟终端 [{tty_id}] 失败")
133
125
  return result
134
126
  elif action == "send_keys":
135
127
  keys = args.get("keys", "").strip()
@@ -138,40 +130,32 @@ class VirtualTTYTool:
138
130
 
139
131
  result = self._input_command(agent, tty_id, keys, timeout, add_enter)
140
132
  if not result["success"]:
141
- PrettyOutput.print(
142
- f"发送按键序列到终端 [{tty_id}] 失败", OutputType.ERROR
143
- )
133
+ print(f"❌ 发送按键序列到终端 [{tty_id}] 失败")
144
134
  return result
145
135
  elif action == "output":
146
136
  timeout = args.get("timeout", 5.0) # 默认5秒超时
147
137
 
148
138
  result = self._get_output(agent, tty_id, timeout)
149
139
  if not result["success"]:
150
- PrettyOutput.print(
151
- f"获取终端 [{tty_id}] 输出失败", OutputType.ERROR
152
- )
140
+ print(f"❌ 获取终端 [{tty_id}] 输出失败")
153
141
  return result
154
142
  elif action == "close":
155
143
 
156
144
  result = self._close_tty(agent, tty_id)
157
145
  if not result["success"]:
158
- PrettyOutput.print(
159
- f"关闭虚拟终端 [{tty_id}] 失败", OutputType.ERROR
160
- )
146
+ print(f"❌ 关闭虚拟终端 [{tty_id}] 失败")
161
147
  return result
162
148
  elif action == "get_screen":
163
149
 
164
150
  result = self._get_screen(agent, tty_id)
165
151
  if not result["success"]:
166
- PrettyOutput.print(
167
- f"获取终端 [{tty_id}] 屏幕内容失败", OutputType.ERROR
168
- )
152
+ print(f"❌ 获取终端 [{tty_id}] 屏幕内容失败")
169
153
  return result
170
154
  elif action == "list":
171
155
 
172
156
  result = self._list_ttys(agent)
173
157
  if not result["success"]:
174
- PrettyOutput.print("获取虚拟终端列表失败", OutputType.ERROR)
158
+ print("获取虚拟终端列表失败")
175
159
  return result
176
160
  return {"success": False, "stdout": "", "stderr": "不支持的操作"}
177
161
 
@@ -231,6 +215,8 @@ class VirtualTTYTool:
231
215
  except BlockingIOError:
232
216
  continue
233
217
 
218
+ if output:
219
+ print(f"📥 启动终端时的初始输出 [{tty_id}]:\n{output}")
234
220
  return {"success": True, "stdout": output, "stderr": ""}
235
221
 
236
222
  except Exception as e:
@@ -296,6 +282,8 @@ class VirtualTTYTool:
296
282
  except _queue.Empty:
297
283
  continue
298
284
 
285
+ if output:
286
+ print(f"📥 启动终端时的初始输出 [{tty_id}]:\n{output}")
299
287
  return {"success": True, "stdout": output, "stderr": ""}
300
288
 
301
289
  except Exception as e:
@@ -372,6 +360,8 @@ class VirtualTTYTool:
372
360
  output += data.decode()
373
361
  except BlockingIOError:
374
362
  continue
363
+ if output:
364
+ print(f"📥 命令执行后的输出内容 [{tty_id}]:\n{output}")
375
365
  return {"success": True, "stdout": output, "stderr": ""}
376
366
 
377
367
  except Exception as e:
@@ -420,6 +410,8 @@ class VirtualTTYTool:
420
410
  except Exception: # queue.Empty
421
411
  continue
422
412
 
413
+ if output:
414
+ print(f"📥 命令执行后的输出内容 [{tty_id}]:\n{output}")
423
415
  return {"success": True, "stdout": output, "stderr": ""}
424
416
 
425
417
  except Exception as e:
@@ -472,6 +464,8 @@ class VirtualTTYTool:
472
464
  break
473
465
  except BlockingIOError:
474
466
  break
467
+ if output:
468
+ print(f"📥 获取到的输出内容 [{tty_id}]:\n{output}")
475
469
  return {"success": True, "stdout": output, "stderr": ""}
476
470
 
477
471
  except Exception as e:
@@ -503,6 +497,8 @@ class VirtualTTYTool:
503
497
  except Exception: # queue.Empty
504
498
  continue
505
499
 
500
+ if output:
501
+ print(f"📥 获取到的输出内容 [{tty_id}]:\n{output}")
506
502
  return {"success": True, "stdout": output, "stderr": ""}
507
503
 
508
504
  except Exception as e:
@@ -568,9 +564,35 @@ class VirtualTTYTool:
568
564
  }
569
565
 
570
566
  try:
567
+ process = agent.tty_sessions[tty_id]["process"]
571
568
  # 终止进程
572
- agent.tty_sessions[tty_id]["process"].terminate()
573
- agent.tty_sessions[tty_id]["process"].wait()
569
+ try:
570
+ import subprocess as _subprocess # pylint: disable=import-outside-toplevel
571
+ process.terminate()
572
+ process.wait(timeout=2)
573
+ except _subprocess.TimeoutExpired:
574
+ try:
575
+ process.kill()
576
+ process.wait()
577
+ except Exception:
578
+ pass
579
+ except Exception:
580
+ try:
581
+ process.kill()
582
+ process.wait()
583
+ except Exception:
584
+ pass
585
+ finally:
586
+ # 确保所有文件描述符被关闭
587
+ try:
588
+ if process.stdin:
589
+ process.stdin.close()
590
+ if process.stdout:
591
+ process.stdout.close()
592
+ if process.stderr:
593
+ process.stderr.close()
594
+ except Exception:
595
+ pass
574
596
 
575
597
  # 重置终端数据
576
598
  import queue as _queue # pylint: disable=import-outside-toplevel
@@ -2,7 +2,6 @@
2
2
  import platform
3
3
  import subprocess
4
4
 
5
- from jarvis.jarvis_utils.output import OutputType, PrettyOutput
6
5
 
7
6
 
8
7
  def copy_to_clipboard(text: str) -> None:
@@ -11,9 +10,9 @@ def copy_to_clipboard(text: str) -> None:
11
10
  参数:
12
11
  text: 要复制的文本
13
12
  """
14
- PrettyOutput.print("--- 剪贴板内容开始 ---", OutputType.INFO)
13
+ print("ℹ️ --- 剪贴板内容开始 ---")
15
14
  print(text)
16
- PrettyOutput.print("--- 剪贴板内容结束 ---", OutputType.INFO)
15
+ print("ℹ️ --- 剪贴板内容结束 ---")
17
16
 
18
17
  system = platform.system()
19
18
 
@@ -33,7 +32,7 @@ def copy_to_clipboard(text: str) -> None:
33
32
  process.stdin.close()
34
33
  return
35
34
  except Exception as e:
36
- PrettyOutput.print(f"使用Windows clip命令时出错: {e}", OutputType.WARNING)
35
+ print(f"⚠️ 使用Windows clip命令时出错: {e}")
37
36
 
38
37
  # macOS系统
39
38
  elif system == "Darwin":
@@ -49,7 +48,7 @@ def copy_to_clipboard(text: str) -> None:
49
48
  process.stdin.close()
50
49
  return
51
50
  except Exception as e:
52
- PrettyOutput.print(f"使用macOS pbcopy命令时出错: {e}", OutputType.WARNING)
51
+ print(f"⚠️ 使用macOS pbcopy命令时出错: {e}")
53
52
 
54
53
  # Linux系统
55
54
  else:
@@ -68,7 +67,7 @@ def copy_to_clipboard(text: str) -> None:
68
67
  except FileNotFoundError:
69
68
  pass # xsel 未安装,继续尝试下一个
70
69
  except Exception as e:
71
- PrettyOutput.print(f"使用xsel时出错: {e}", OutputType.WARNING)
70
+ print(f"⚠️ 使用xsel时出错: {e}")
72
71
 
73
72
  # 尝试使用 xclip
74
73
  try:
@@ -83,8 +82,6 @@ def copy_to_clipboard(text: str) -> None:
83
82
  process.stdin.close()
84
83
  return
85
84
  except FileNotFoundError:
86
- PrettyOutput.print(
87
- "xsel 和 xclip 均未安装, 无法复制到剪贴板", OutputType.WARNING
88
- )
85
+ print("⚠️ xsel 和 xclip 均未安装, 无法复制到剪贴板")
89
86
  except Exception as e:
90
- PrettyOutput.print(f"使用xclip时出错: {e}", OutputType.WARNING)
87
+ print(f"⚠️ 使用xclip时出错: {e}")