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.
- jarvis/__init__.py +1 -1
- jarvis/jarvis_agent/__init__.py +243 -139
- jarvis/jarvis_agent/agent_manager.py +5 -10
- jarvis/jarvis_agent/builtin_input_handler.py +2 -6
- jarvis/jarvis_agent/config_editor.py +2 -7
- jarvis/jarvis_agent/event_bus.py +82 -12
- jarvis/jarvis_agent/file_context_handler.py +265 -15
- jarvis/jarvis_agent/file_methodology_manager.py +3 -4
- jarvis/jarvis_agent/jarvis.py +113 -98
- jarvis/jarvis_agent/language_extractors/__init__.py +57 -0
- jarvis/jarvis_agent/language_extractors/c_extractor.py +21 -0
- jarvis/jarvis_agent/language_extractors/cpp_extractor.py +21 -0
- jarvis/jarvis_agent/language_extractors/go_extractor.py +21 -0
- jarvis/jarvis_agent/language_extractors/java_extractor.py +84 -0
- jarvis/jarvis_agent/language_extractors/javascript_extractor.py +79 -0
- jarvis/jarvis_agent/language_extractors/python_extractor.py +21 -0
- jarvis/jarvis_agent/language_extractors/rust_extractor.py +21 -0
- jarvis/jarvis_agent/language_extractors/typescript_extractor.py +84 -0
- jarvis/jarvis_agent/language_support_info.py +486 -0
- jarvis/jarvis_agent/main.py +6 -12
- jarvis/jarvis_agent/memory_manager.py +7 -16
- jarvis/jarvis_agent/methodology_share_manager.py +10 -16
- jarvis/jarvis_agent/prompt_manager.py +1 -1
- jarvis/jarvis_agent/prompts.py +193 -171
- jarvis/jarvis_agent/protocols.py +8 -12
- jarvis/jarvis_agent/run_loop.py +77 -14
- jarvis/jarvis_agent/session_manager.py +2 -3
- jarvis/jarvis_agent/share_manager.py +12 -21
- jarvis/jarvis_agent/shell_input_handler.py +1 -2
- jarvis/jarvis_agent/task_analyzer.py +26 -4
- jarvis/jarvis_agent/task_manager.py +11 -27
- jarvis/jarvis_agent/tool_executor.py +2 -3
- jarvis/jarvis_agent/tool_share_manager.py +12 -24
- jarvis/jarvis_agent/web_server.py +55 -20
- jarvis/jarvis_c2rust/__init__.py +5 -5
- jarvis/jarvis_c2rust/cli.py +461 -499
- jarvis/jarvis_c2rust/collector.py +45 -53
- jarvis/jarvis_c2rust/constants.py +26 -0
- jarvis/jarvis_c2rust/library_replacer.py +264 -132
- jarvis/jarvis_c2rust/llm_module_agent.py +162 -190
- jarvis/jarvis_c2rust/loaders.py +207 -0
- jarvis/jarvis_c2rust/models.py +28 -0
- jarvis/jarvis_c2rust/optimizer.py +1592 -395
- jarvis/jarvis_c2rust/transpiler.py +1722 -1064
- jarvis/jarvis_c2rust/utils.py +385 -0
- jarvis/jarvis_code_agent/build_validation_config.py +2 -3
- jarvis/jarvis_code_agent/code_agent.py +394 -320
- jarvis/jarvis_code_agent/code_analyzer/__init__.py +3 -0
- jarvis/jarvis_code_agent/code_analyzer/build_validator/base.py +4 -0
- jarvis/jarvis_code_agent/code_analyzer/build_validator/cmake.py +17 -2
- jarvis/jarvis_code_agent/code_analyzer/build_validator/fallback.py +3 -0
- jarvis/jarvis_code_agent/code_analyzer/build_validator/go.py +36 -4
- jarvis/jarvis_code_agent/code_analyzer/build_validator/java_gradle.py +9 -0
- jarvis/jarvis_code_agent/code_analyzer/build_validator/java_maven.py +9 -0
- jarvis/jarvis_code_agent/code_analyzer/build_validator/makefile.py +12 -1
- jarvis/jarvis_code_agent/code_analyzer/build_validator/nodejs.py +22 -5
- jarvis/jarvis_code_agent/code_analyzer/build_validator/python.py +57 -32
- jarvis/jarvis_code_agent/code_analyzer/build_validator/rust.py +62 -6
- jarvis/jarvis_code_agent/code_analyzer/build_validator/validator.py +8 -9
- jarvis/jarvis_code_agent/code_analyzer/context_manager.py +290 -5
- jarvis/jarvis_code_agent/code_analyzer/language_support.py +21 -0
- jarvis/jarvis_code_agent/code_analyzer/languages/__init__.py +21 -3
- jarvis/jarvis_code_agent/code_analyzer/languages/c_cpp_language.py +72 -4
- jarvis/jarvis_code_agent/code_analyzer/languages/go_language.py +35 -3
- jarvis/jarvis_code_agent/code_analyzer/languages/java_language.py +212 -0
- jarvis/jarvis_code_agent/code_analyzer/languages/javascript_language.py +254 -0
- jarvis/jarvis_code_agent/code_analyzer/languages/python_language.py +52 -2
- jarvis/jarvis_code_agent/code_analyzer/languages/rust_language.py +73 -1
- jarvis/jarvis_code_agent/code_analyzer/languages/typescript_language.py +280 -0
- jarvis/jarvis_code_agent/code_analyzer/llm_context_recommender.py +306 -152
- jarvis/jarvis_code_agent/code_analyzer/structured_code.py +556 -0
- jarvis/jarvis_code_agent/code_analyzer/symbol_extractor.py +193 -18
- jarvis/jarvis_code_agent/code_analyzer/tree_sitter_extractor.py +18 -8
- jarvis/jarvis_code_agent/lint.py +258 -27
- jarvis/jarvis_code_agent/utils.py +0 -1
- jarvis/jarvis_code_analysis/code_review.py +19 -24
- jarvis/jarvis_data/config_schema.json +53 -26
- jarvis/jarvis_git_squash/main.py +4 -5
- jarvis/jarvis_git_utils/git_commiter.py +44 -49
- jarvis/jarvis_mcp/sse_mcp_client.py +20 -27
- jarvis/jarvis_mcp/stdio_mcp_client.py +11 -12
- jarvis/jarvis_mcp/streamable_mcp_client.py +15 -14
- jarvis/jarvis_memory_organizer/memory_organizer.py +55 -74
- jarvis/jarvis_methodology/main.py +32 -48
- jarvis/jarvis_multi_agent/__init__.py +79 -61
- jarvis/jarvis_multi_agent/main.py +3 -7
- jarvis/jarvis_platform/base.py +469 -199
- jarvis/jarvis_platform/human.py +7 -8
- jarvis/jarvis_platform/kimi.py +30 -36
- jarvis/jarvis_platform/openai.py +65 -27
- jarvis/jarvis_platform/registry.py +26 -10
- jarvis/jarvis_platform/tongyi.py +24 -25
- jarvis/jarvis_platform/yuanbao.py +31 -42
- jarvis/jarvis_platform_manager/main.py +66 -77
- jarvis/jarvis_platform_manager/service.py +8 -13
- jarvis/jarvis_rag/cli.py +49 -51
- jarvis/jarvis_rag/embedding_manager.py +13 -18
- jarvis/jarvis_rag/llm_interface.py +8 -9
- jarvis/jarvis_rag/query_rewriter.py +10 -21
- jarvis/jarvis_rag/rag_pipeline.py +24 -27
- jarvis/jarvis_rag/reranker.py +4 -5
- jarvis/jarvis_rag/retriever.py +28 -30
- jarvis/jarvis_sec/__init__.py +220 -3520
- jarvis/jarvis_sec/agents.py +143 -0
- jarvis/jarvis_sec/analysis.py +276 -0
- jarvis/jarvis_sec/cli.py +29 -6
- jarvis/jarvis_sec/clustering.py +1439 -0
- jarvis/jarvis_sec/file_manager.py +427 -0
- jarvis/jarvis_sec/parsers.py +73 -0
- jarvis/jarvis_sec/prompts.py +268 -0
- jarvis/jarvis_sec/report.py +83 -4
- jarvis/jarvis_sec/review.py +453 -0
- jarvis/jarvis_sec/utils.py +499 -0
- jarvis/jarvis_sec/verification.py +848 -0
- jarvis/jarvis_sec/workflow.py +7 -0
- jarvis/jarvis_smart_shell/main.py +38 -87
- jarvis/jarvis_stats/cli.py +1 -1
- jarvis/jarvis_stats/stats.py +7 -7
- jarvis/jarvis_stats/storage.py +15 -21
- jarvis/jarvis_tools/clear_memory.py +3 -20
- jarvis/jarvis_tools/cli/main.py +20 -23
- jarvis/jarvis_tools/edit_file.py +1066 -0
- jarvis/jarvis_tools/execute_script.py +42 -21
- jarvis/jarvis_tools/file_analyzer.py +6 -9
- jarvis/jarvis_tools/generate_new_tool.py +11 -20
- jarvis/jarvis_tools/lsp_client.py +1552 -0
- jarvis/jarvis_tools/methodology.py +2 -3
- jarvis/jarvis_tools/read_code.py +1525 -87
- jarvis/jarvis_tools/read_symbols.py +2 -3
- jarvis/jarvis_tools/read_webpage.py +7 -10
- jarvis/jarvis_tools/registry.py +370 -181
- jarvis/jarvis_tools/retrieve_memory.py +20 -19
- jarvis/jarvis_tools/rewrite_file.py +105 -0
- jarvis/jarvis_tools/save_memory.py +3 -15
- jarvis/jarvis_tools/search_web.py +3 -7
- jarvis/jarvis_tools/sub_agent.py +17 -6
- jarvis/jarvis_tools/sub_code_agent.py +14 -16
- jarvis/jarvis_tools/virtual_tty.py +54 -32
- jarvis/jarvis_utils/clipboard.py +7 -10
- jarvis/jarvis_utils/config.py +98 -63
- jarvis/jarvis_utils/embedding.py +5 -5
- jarvis/jarvis_utils/fzf.py +8 -8
- jarvis/jarvis_utils/git_utils.py +81 -67
- jarvis/jarvis_utils/input.py +24 -49
- jarvis/jarvis_utils/jsonnet_compat.py +465 -0
- jarvis/jarvis_utils/methodology.py +33 -35
- jarvis/jarvis_utils/utils.py +245 -202
- {jarvis_ai_assistant-0.7.0.dist-info → jarvis_ai_assistant-0.7.6.dist-info}/METADATA +205 -70
- jarvis_ai_assistant-0.7.6.dist-info/RECORD +218 -0
- jarvis/jarvis_agent/edit_file_handler.py +0 -584
- jarvis/jarvis_agent/rewrite_file_handler.py +0 -141
- jarvis/jarvis_agent/task_planner.py +0 -496
- jarvis/jarvis_platform/ai8.py +0 -332
- jarvis/jarvis_tools/ask_user.py +0 -54
- jarvis_ai_assistant-0.7.0.dist-info/RECORD +0 -192
- {jarvis_ai_assistant-0.7.0.dist-info → jarvis_ai_assistant-0.7.6.dist-info}/WHEEL +0 -0
- {jarvis_ai_assistant-0.7.0.dist-info → jarvis_ai_assistant-0.7.6.dist-info}/entry_points.txt +0 -0
- {jarvis_ai_assistant-0.7.0.dist-info → jarvis_ai_assistant-0.7.6.dist-info}/licenses/LICENSE +0 -0
- {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
|
-
|
|
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
|
-
#
|
|
135
|
-
|
|
136
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
107
|
+
print(f"❌ 网页搜索过程中发生错误: {e}")
|
|
112
108
|
return {
|
|
113
109
|
"stdout": "",
|
|
114
110
|
"stderr": f"网页搜索过程中发生错误: {e}",
|
jarvis/jarvis_tools/sub_agent.py
CHANGED
|
@@ -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
|
|
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
|
-
|
|
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
|
-
"
|
|
121
|
-
"
|
|
122
|
-
"clear_memory",
|
|
123
|
-
"sub_code_agent",
|
|
118
|
+
"edit_file",
|
|
119
|
+
"rewrite_file",
|
|
120
|
+
"lsp_client",
|
|
124
121
|
]
|
|
125
122
|
if use_tools:
|
|
126
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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": "
|
|
39
|
+
"description": "要发送的按键序列(仅当action为send_keys时有效)",
|
|
46
40
|
},
|
|
47
41
|
"add_enter": {
|
|
48
42
|
"type": "boolean",
|
|
49
|
-
"description": "
|
|
43
|
+
"description": "是否在命令末尾自动添加回车符(仅当action为send_keys时有效,默认true)",
|
|
50
44
|
},
|
|
51
45
|
"timeout": {
|
|
52
46
|
"type": "number",
|
|
53
|
-
"description": "等待输出的超时时间(秒,仅当action为send_keys或output
|
|
47
|
+
"description": "等待输出的超时时间(秒,仅当action为send_keys或output时有效,默认5.0)",
|
|
54
48
|
},
|
|
55
49
|
"tty_id": {
|
|
56
50
|
"type": "string",
|
|
57
|
-
"description": "
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
573
|
-
|
|
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
|
jarvis/jarvis_utils/clipboard.py
CHANGED
|
@@ -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
|
-
|
|
13
|
+
print("ℹ️ --- 剪贴板内容开始 ---")
|
|
15
14
|
print(text)
|
|
16
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
87
|
-
"xsel 和 xclip 均未安装, 无法复制到剪贴板", OutputType.WARNING
|
|
88
|
-
)
|
|
85
|
+
print("⚠️ xsel 和 xclip 均未安装, 无法复制到剪贴板")
|
|
89
86
|
except Exception as e:
|
|
90
|
-
|
|
87
|
+
print(f"⚠️ 使用xclip时出错: {e}")
|