jarvis-ai-assistant 0.3.26__py3-none-any.whl → 0.3.27__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 (33) hide show
  1. jarvis/__init__.py +1 -1
  2. jarvis/jarvis_agent/__init__.py +290 -173
  3. jarvis/jarvis_agent/config.py +92 -0
  4. jarvis/jarvis_agent/event_bus.py +48 -0
  5. jarvis/jarvis_agent/jarvis.py +69 -35
  6. jarvis/jarvis_agent/memory_manager.py +70 -2
  7. jarvis/jarvis_agent/prompt_manager.py +82 -0
  8. jarvis/jarvis_agent/run_loop.py +130 -0
  9. jarvis/jarvis_agent/task_analyzer.py +88 -9
  10. jarvis/jarvis_agent/task_manager.py +26 -0
  11. jarvis/jarvis_agent/user_interaction.py +42 -0
  12. jarvis/jarvis_code_agent/code_agent.py +18 -3
  13. jarvis/jarvis_code_agent/lint.py +5 -5
  14. jarvis/jarvis_data/config_schema.json +7 -6
  15. jarvis/jarvis_git_squash/main.py +6 -1
  16. jarvis/jarvis_git_utils/git_commiter.py +38 -12
  17. jarvis/jarvis_platform/base.py +4 -5
  18. jarvis/jarvis_platform_manager/main.py +28 -11
  19. jarvis/jarvis_stats/cli.py +13 -32
  20. jarvis/jarvis_stats/stats.py +179 -51
  21. jarvis/jarvis_tools/registry.py +15 -0
  22. jarvis/jarvis_tools/sub_agent.py +94 -84
  23. jarvis/jarvis_tools/sub_code_agent.py +12 -6
  24. jarvis/jarvis_utils/config.py +14 -0
  25. jarvis/jarvis_utils/fzf.py +56 -0
  26. jarvis/jarvis_utils/input.py +0 -3
  27. jarvis/jarvis_utils/utils.py +56 -8
  28. {jarvis_ai_assistant-0.3.26.dist-info → jarvis_ai_assistant-0.3.27.dist-info}/METADATA +2 -3
  29. {jarvis_ai_assistant-0.3.26.dist-info → jarvis_ai_assistant-0.3.27.dist-info}/RECORD +33 -27
  30. {jarvis_ai_assistant-0.3.26.dist-info → jarvis_ai_assistant-0.3.27.dist-info}/WHEEL +0 -0
  31. {jarvis_ai_assistant-0.3.26.dist-info → jarvis_ai_assistant-0.3.27.dist-info}/entry_points.txt +0 -0
  32. {jarvis_ai_assistant-0.3.26.dist-info → jarvis_ai_assistant-0.3.27.dist-info}/licenses/LICENSE +0 -0
  33. {jarvis_ai_assistant-0.3.26.dist-info → jarvis_ai_assistant-0.3.27.dist-info}/top_level.txt +0 -0
@@ -166,32 +166,58 @@ class StatsManager:
166
166
  >>> StatsManager.show("response_time", last_days=7, format="chart") # 图表显示
167
167
  """
168
168
  # 处理时间范围
169
- if end_time is None:
170
- end_time = datetime.now()
171
-
172
- if start_time is None:
173
- if last_hours:
174
- start_time = end_time - timedelta(hours=last_hours)
175
- elif last_days:
176
- start_time = end_time - timedelta(days=last_days)
177
- else:
178
- start_time = end_time - timedelta(days=7) # 默认7天
169
+ # 当未提供时间过滤参数时,默认显示全历史数据
170
+ all_time = False
171
+ if last_hours or last_days:
172
+ if end_time is None:
173
+ end_time = datetime.now()
174
+ if start_time is None:
175
+ if last_hours:
176
+ start_time = end_time - timedelta(hours=last_hours)
177
+ elif last_days:
178
+ start_time = end_time - timedelta(days=last_days)
179
+ else:
180
+ all_time = True
179
181
 
180
182
  if metric_name is None:
181
183
  # 显示所有指标摘要
182
184
  StatsManager._show_metrics_summary(start_time, end_time, tags)
183
185
  else:
186
+ # 基于元数据的 created_at / last_updated 推断全历史时间范围
187
+ storage = StatsManager._get_storage()
188
+ if start_time is None or end_time is None:
189
+ info = storage.get_metric_info(metric_name)
190
+ if info:
191
+ try:
192
+ created_at = info.get("created_at")
193
+ last_updated = info.get("last_updated")
194
+ start_dt = start_time or (
195
+ datetime.fromisoformat(created_at) if created_at else datetime.now()
196
+ )
197
+ end_dt = end_time or (
198
+ datetime.fromisoformat(last_updated) if last_updated else datetime.now()
199
+ )
200
+ except Exception:
201
+ start_dt = start_time or (datetime.now() - timedelta(days=7))
202
+ end_dt = end_time or datetime.now()
203
+ else:
204
+ start_dt = start_time or (datetime.now() - timedelta(days=7))
205
+ end_dt = end_time or datetime.now()
206
+ else:
207
+ start_dt = start_time
208
+ end_dt = end_time
209
+
184
210
  # 根据格式显示数据
185
211
  if format == "chart":
186
212
  StatsManager._show_chart(
187
- metric_name, start_time, end_time, aggregation, tags
213
+ metric_name, start_dt, end_dt, aggregation, tags
188
214
  )
189
215
  elif format == "summary":
190
216
  StatsManager._show_summary(
191
- metric_name, start_time, end_time, aggregation, tags
217
+ metric_name, start_dt, end_dt, aggregation, tags
192
218
  )
193
219
  else:
194
- StatsManager._show_table(metric_name, start_time, end_time, tags)
220
+ StatsManager._show_table(metric_name, start_dt, end_dt, tags)
195
221
 
196
222
  @staticmethod
197
223
  def plot(
@@ -224,21 +250,44 @@ class StatsManager:
224
250
  >>> StatsManager.plot(tags={"service": "api"}, last_days=7)
225
251
  """
226
252
  # 处理时间范围
227
- if end_time is None:
228
- end_time = datetime.now()
229
-
230
- if start_time is None:
231
- if last_hours:
232
- start_time = end_time - timedelta(hours=last_hours)
233
- elif last_days:
234
- start_time = end_time - timedelta(days=last_days)
235
- else:
236
- start_time = end_time - timedelta(days=7)
253
+ # 当未提供时间过滤参数时,默认使用全历史数据
254
+ if last_hours or last_days:
255
+ if end_time is None:
256
+ end_time = datetime.now()
257
+ if start_time is None:
258
+ if last_hours:
259
+ start_time = end_time - timedelta(hours=last_hours)
260
+ elif last_days:
261
+ start_time = end_time - timedelta(days=last_days)
237
262
 
238
263
  # 如果指定了metric_name,显示单个图表
239
264
  if metric_name:
265
+ # 基于元数据的 created_at / last_updated 推断全历史时间范围
266
+ storage = StatsManager._get_storage()
267
+ if start_time is None or end_time is None:
268
+ info = storage.get_metric_info(metric_name)
269
+ if info:
270
+ try:
271
+ created_at = info.get("created_at")
272
+ last_updated = info.get("last_updated")
273
+ start_dt = start_time or (
274
+ datetime.fromisoformat(created_at) if created_at else datetime.now()
275
+ )
276
+ end_dt = end_time or (
277
+ datetime.fromisoformat(last_updated) if last_updated else datetime.now()
278
+ )
279
+ except Exception:
280
+ start_dt = start_time or (datetime.now() - timedelta(days=7))
281
+ end_dt = end_time or datetime.now()
282
+ else:
283
+ start_dt = start_time or (datetime.now() - timedelta(days=7))
284
+ end_dt = end_time or datetime.now()
285
+ else:
286
+ start_dt = start_time
287
+ end_dt = end_time
288
+
240
289
  StatsManager._show_chart(
241
- metric_name, start_time, end_time, aggregation, tags, width, height
290
+ metric_name, start_dt, end_dt, aggregation, tags, width, height
242
291
  )
243
292
  else:
244
293
  # 如果没有指定metric_name,根据标签过滤获取所有匹配的指标
@@ -272,32 +321,53 @@ class StatsManager:
272
321
  统计数据字典
273
322
  """
274
323
  # 处理时间范围
275
- if end_time is None:
276
- end_time = datetime.now()
277
-
278
- if start_time is None:
279
- if last_hours:
280
- start_time = end_time - timedelta(hours=last_hours)
281
- elif last_days:
282
- start_time = end_time - timedelta(days=last_days)
283
- else:
284
- start_time = end_time - timedelta(days=7)
324
+ # 当未提供时间过滤参数时,返回全历史数据
325
+ if last_hours or last_days or start_time is not None:
326
+ if end_time is None:
327
+ end_time = datetime.now()
328
+ if start_time is None:
329
+ if last_hours:
330
+ start_time = end_time - timedelta(hours=last_hours)
331
+ elif last_days:
332
+ start_time = end_time - timedelta(days=last_days)
285
333
 
286
334
  storage = StatsManager._get_storage()
335
+
336
+ # 基于元数据推断全历史范围(当未提供时间过滤参数时)
337
+ start_dt = start_time
338
+ end_dt = end_time
339
+ if start_dt is None or end_dt is None:
340
+ info = storage.get_metric_info(metric_name)
341
+ if info:
342
+ try:
343
+ created_at = info.get("created_at")
344
+ last_updated = info.get("last_updated")
345
+ if start_dt is None and created_at:
346
+ start_dt = datetime.fromisoformat(created_at)
347
+ if end_dt is None and last_updated:
348
+ end_dt = datetime.fromisoformat(last_updated)
349
+ except Exception:
350
+ pass
351
+ if start_dt is None:
352
+ start_dt = datetime.now() - timedelta(days=7)
353
+ if end_dt is None:
354
+ end_dt = datetime.now()
355
+
287
356
  if aggregation:
288
- # 返回聚合数据
357
+ # 返回聚合数据(按推断的全历史时间范围)
289
358
  return storage.aggregate_metrics(
290
- metric_name, start_time, end_time, aggregation, tags
359
+ metric_name, start_dt, end_dt, aggregation, tags
291
360
  )
292
361
  else:
293
- # 返回原始数据
294
- records = storage.get_metrics(metric_name, start_time, end_time, tags)
362
+ # 返回原始数据(全历史或指定范围)
363
+ records = storage.get_metrics(metric_name, start_dt, end_dt, tags)
364
+
295
365
  return {
296
366
  "metric": metric_name,
297
367
  "records": records,
298
368
  "count": len(records),
299
- "start_time": start_time.isoformat(),
300
- "end_time": end_time.isoformat(),
369
+ "start_time": start_dt.isoformat(),
370
+ "end_time": end_dt.isoformat(),
301
371
  }
302
372
 
303
373
  @staticmethod
@@ -343,24 +413,56 @@ class StatsManager:
343
413
  console.print("[yellow]没有找到任何统计指标[/yellow]")
344
414
  return
345
415
 
346
- # 如果没有指定时间范围,使用默认值
347
- if end_time is None:
348
- end_time = datetime.now()
349
- if start_time is None:
350
- start_time = end_time - timedelta(days=7)
416
+ # 如果未指定时间范围且两者皆为 None,则表示使用全历史数据
417
+ all_time = False
418
+ if start_time is None and end_time is None:
419
+ all_time = True
420
+ else:
421
+ if end_time is None:
422
+ end_time = datetime.now()
423
+ if start_time is None:
424
+ start_time = end_time - timedelta(days=7)
425
+
426
+
427
+
428
+ # 计算时间范围列标题
429
+ if start_time is None or end_time is None:
430
+ period_label = "值"
431
+ else:
432
+ delta = end_time - start_time
433
+ if delta.days >= 1:
434
+ period_label = f"{delta.days}天数据点"
435
+ else:
436
+ hours = max(1, int(delta.total_seconds() // 3600))
437
+ period_label = f"{hours}小时数据点"
351
438
 
352
439
  # 创建表格
353
440
  table = Table(title="统计指标摘要")
354
441
  table.add_column("指标名称", style="cyan")
355
442
  table.add_column("单位", style="green")
356
443
  table.add_column("最后更新", style="yellow")
357
- table.add_column("7天数据点", style="magenta")
444
+ table.add_column(period_label, style="magenta")
358
445
 
359
446
  # 过滤满足标签条件的指标
360
447
  displayed_count = 0
361
448
  for metric in metrics:
362
- # 获取该指标的记录
363
- records = storage.get_metrics(metric, start_time, end_time, tags)
449
+ # 获取该指标的记录(全历史或指定范围)
450
+ if 'all_time' in locals() and all_time:
451
+ _start = None
452
+ _end = None
453
+ try:
454
+ info = storage.get_metric_info(metric)
455
+ if info:
456
+ ca = info.get("created_at")
457
+ lu = info.get("last_updated")
458
+ _start = datetime.fromisoformat(ca) if ca else None
459
+ _end = datetime.fromisoformat(lu) if lu else None
460
+ except Exception:
461
+ _start = None
462
+ _end = None
463
+ records = storage.get_metrics(metric, _start, _end, tags)
464
+ else:
465
+ records = storage.get_metrics(metric, start_time, end_time, tags)
364
466
 
365
467
  # 如果指定了标签过滤,但没有匹配的记录,跳过该指标
366
468
  if tags and len(records) == 0:
@@ -382,8 +484,14 @@ class StatsManager:
382
484
  except:
383
485
  pass
384
486
 
385
- count = len(records)
386
- table.add_row(metric, unit, last_updated, str(count))
487
+ total_value = sum(r.get("value", 0) for r in records)
488
+ # 智能格式化,如果是整数则不显示小数点
489
+ if total_value == int(total_value):
490
+ value_to_display = str(int(total_value))
491
+ else:
492
+ value_to_display = f"{total_value:.2f}"
493
+
494
+ table.add_row(metric, unit, last_updated, value_to_display)
387
495
  displayed_count += 1
388
496
 
389
497
  if displayed_count == 0:
@@ -532,8 +640,28 @@ class StatsManager:
532
640
  if i > 0:
533
641
  console.print("\n" + "=" * 80 + "\n") # 分隔符
534
642
 
643
+ # 计算该指标的有效时间范围(基于元数据推断全历史)
644
+ _start = start_time
645
+ _end = end_time
646
+ if _start is None or _end is None:
647
+ info = storage.get_metric_info(metric)
648
+ if info:
649
+ try:
650
+ ca = info.get("created_at")
651
+ lu = info.get("last_updated")
652
+ if _start is None and ca:
653
+ _start = datetime.fromisoformat(ca)
654
+ if _end is None and lu:
655
+ _end = datetime.fromisoformat(lu)
656
+ except Exception:
657
+ pass
658
+ if _start is None:
659
+ _start = datetime.now() - timedelta(days=7)
660
+ if _end is None:
661
+ _end = datetime.now()
662
+
535
663
  StatsManager._show_chart(
536
- metric, start_time, end_time, aggregation, tags, width, height
664
+ metric, _start, _end, aggregation, tags, width, height
537
665
  )
538
666
 
539
667
  @staticmethod
@@ -839,6 +839,21 @@ class ToolRegistry(OutputHandlerProtocol):
839
839
  # 执行工具调用(根据工具实现的协议版本,由系统在内部决定agent的传递方式)
840
840
  result = self.execute_tool(name, args, agent)
841
841
 
842
+ # 记录本轮实际执行的工具,供上层逻辑(如记忆保存判定)使用
843
+ try:
844
+ from jarvis.jarvis_agent import Agent # 延迟导入避免循环依赖
845
+ agent_instance_for_record: Agent = agent_instance
846
+ # 记录最后一次执行的工具
847
+ agent_instance_for_record.set_user_data("__last_executed_tool__", name) # type: ignore
848
+ # 记录本轮累计执行的工具列表
849
+ executed_list = agent_instance_for_record.get_user_data("__executed_tools__") # type: ignore
850
+ if not isinstance(executed_list, list):
851
+ executed_list = []
852
+ executed_list.append(name)
853
+ agent_instance_for_record.set_user_data("__executed_tools__", executed_list) # type: ignore
854
+ except Exception:
855
+ pass
856
+
842
857
  # 格式化输出
843
858
  output = self._format_tool_output(
844
859
  result["stdout"], result.get("stderr", "")
@@ -4,15 +4,16 @@ sub_agent 工具
4
4
  将子任务交给通用 Agent 执行,并返回执行结果。
5
5
 
6
6
  约定:
7
- - 仅接收一个参数:task
8
- - 不依赖父 Agent,所有配置使用系统默认与全局变量
7
+ - 必填参数:task, name, background, system_prompt, summary_prompt, use_tools
8
+ - 继承父 Agent 的部分配置:model_group、input_handler、execute_tool_confirm、multiline_inputer;其他参数需显式提供
9
9
  - 子Agent必须自动完成(auto_complete=True)且需要summary(need_summary=True)
10
10
  """
11
11
  from typing import Any, Dict, Optional
12
12
  import json
13
13
 
14
- from jarvis.jarvis_agent import Agent, origin_agent_system_prompt
14
+ from jarvis.jarvis_agent import Agent
15
15
  from jarvis.jarvis_utils.globals import delete_agent
16
+ from jarvis.jarvis_tools.registry import ToolRegistry
16
17
 
17
18
 
18
19
  class SubAgentTool:
@@ -25,7 +26,7 @@ class SubAgentTool:
25
26
 
26
27
  # 必须与文件名一致,供 ToolRegistry 自动注册
27
28
  name = "sub_agent"
28
- description = "将子任务交给通用 Agent 执行,并返回执行结果(使用系统默认配置,自动完成并生成总结)。"
29
+ description = "将子任务交给通用 Agent 执行,并返回执行结果(继承父Agent部分配置:model_group、input_handler、execute_tool_confirm、multiline_inputer;其他参数需显式提供,自动完成并生成总结)。"
29
30
  parameters = {
30
31
  "type": "object",
31
32
  "properties": {
@@ -33,12 +34,42 @@ class SubAgentTool:
33
34
  "type": "string",
34
35
  "description": "要执行的子任务内容(必填)",
35
36
  },
37
+ "name": {
38
+ "type": "string",
39
+ "description": "子Agent名称(必填)",
40
+ },
36
41
  "background": {
37
42
  "type": "string",
38
- "description": "任务背景与已知信息(可选,将与任务一并提供给子Agent)",
43
+ "description": "任务背景与已知信息(必填,将与任务一并提供给子Agent)",
44
+ },
45
+ "system_prompt": {
46
+ "type": "string",
47
+ "description": "覆盖子Agent的系统提示词(必填)",
48
+ },
49
+ "summary_prompt": {
50
+ "type": "string",
51
+ "description": "覆盖子Agent的总结提示词(必填)",
52
+ },
53
+ "use_tools": {
54
+ "type": "array",
55
+ "items": {"type": "string"},
56
+ "description": "限制子Agent可用的工具名称列表(必填)。兼容以逗号分隔的字符串输入。可用的工具列表:"
57
+ + "\n".join(
58
+ [
59
+ t["name"] + ": " + t["description"]
60
+ for t in ToolRegistry().get_all_tools()
61
+ ]
62
+ ),
39
63
  },
40
64
  },
41
- "required": ["task"],
65
+ "required": [
66
+ "task",
67
+ "name",
68
+ "background",
69
+ "system_prompt",
70
+ "summary_prompt",
71
+ "use_tools",
72
+ ],
42
73
  }
43
74
 
44
75
  def execute(self, args: Dict[str, Any]) -> Dict[str, Any]:
@@ -64,104 +95,83 @@ class SubAgentTool:
64
95
  f"背景信息:\n{background}\n\n任务:\n{task}" if background else task
65
96
  )
66
97
 
67
- # 继承父Agent的运行参数(用于覆盖默认值);若无父Agent则使用默认/全局配置
68
- parent_agent = args.get("agent")
69
- # 如未注入父Agent,尝试从全局获取当前或任一已注册Agent
70
- if parent_agent is None:
71
- try:
72
- from jarvis.jarvis_utils import globals as G # 延迟导入避免循环
73
-
74
- curr = getattr(G, "current_agent_name", "")
75
- if curr:
76
- parent_agent = getattr(G, "global_agents", {}).get(curr)
77
- if parent_agent is None and getattr(G, "global_agents", {}):
78
- try:
79
- parent_agent = next(iter(G.global_agents.values()))
80
- except Exception:
81
- parent_agent = None
82
- except Exception:
83
- parent_agent = None
84
- # 默认/全局
85
- system_prompt = origin_agent_system_prompt
98
+ # 不继承父Agent,所有关键参数必须由调用方显式提供
86
99
  need_summary = True
87
100
  auto_complete = True
88
101
 
89
- # 可继承参数
90
- model_group = None
91
- summary_prompt = None
92
- execute_tool_confirm = None
93
- use_methodology = None
94
- use_analysis = None
95
- force_save_memory = None
102
+ # 读取并校验必填参数
103
+ system_prompt = str(args.get("system_prompt", "")).strip()
104
+ summary_prompt = str(args.get("summary_prompt", "")).strip()
105
+ agent_name = str(args.get("name", "")).strip()
106
+
107
+ # 解析可用工具列表(支持数组或以逗号分隔的字符串)
108
+ _use_tools = args.get("use_tools", None)
96
109
  use_tools: list[str] = []
110
+ if isinstance(_use_tools, list):
111
+ use_tools = [str(x).strip() for x in _use_tools if str(x).strip()]
112
+ elif isinstance(_use_tools, str):
113
+ use_tools = [s.strip() for s in _use_tools.split(",") if s.strip()]
114
+ else:
115
+ use_tools = []
116
+
117
+ errors = []
118
+ if not system_prompt:
119
+ errors.append("system_prompt 不能为空")
120
+ if not summary_prompt:
121
+ errors.append("summary_prompt 不能为空")
122
+ if not agent_name:
123
+ errors.append("name 不能为空")
124
+ if not use_tools:
125
+ errors.append("use_tools 不能为空")
126
+ if not background:
127
+ errors.append("background 不能为空")
128
+
129
+ if errors:
130
+ return {
131
+ "success": False,
132
+ "stdout": "",
133
+ "stderr": "; ".join(errors),
134
+ }
97
135
 
136
+ # 基于父Agent(如有)继承部分配置后创建子Agent
137
+ parent_agent = args.get("agent", None)
138
+ parent_model_group = None
139
+ parent_input_handler = None
140
+ parent_execute_tool_confirm = None
141
+ parent_multiline_inputer = None
98
142
  try:
99
143
  if parent_agent is not None:
100
- # 继承模型组
101
144
  if getattr(parent_agent, "model", None):
102
- model_group = getattr(parent_agent.model, "model_group", None)
103
- # 继承开关类参数
104
- summary_prompt = getattr(parent_agent, "summary_prompt", None)
105
- execute_tool_confirm = getattr(
106
- parent_agent, "execute_tool_confirm", None
107
- )
108
- use_methodology = getattr(parent_agent, "use_methodology", None)
109
- use_analysis = getattr(parent_agent, "use_analysis", None)
110
- force_save_memory = getattr(parent_agent, "force_save_memory", None)
111
- # 继承工具使用集(名称列表)
112
- parent_registry = parent_agent.get_tool_registry()
113
- if parent_registry:
114
- for t in parent_registry.get_all_tools():
115
- if isinstance(t, dict) and t.get("name"):
116
- use_tools.append(str(t["name"]))
145
+ parent_model_group = getattr(parent_agent.model, "model_group", None)
146
+ parent_input_handler = getattr(parent_agent, "input_handler", None)
147
+ parent_execute_tool_confirm = getattr(parent_agent, "execute_tool_confirm", None)
148
+ parent_multiline_inputer = getattr(parent_agent, "multiline_inputer", None)
117
149
  except Exception:
118
- # 忽略继承失败,退回默认配置
150
+ # 安全兜底:无法从父Agent获取配置则保持为None,使用系统默认
119
151
  pass
120
152
 
121
- # 为避免交互阻塞:提供自动确认与空输入处理器
122
- def _auto_confirm(tip: str, default: bool = True) -> bool:
123
- return default
124
-
125
- # 创建子Agent(其余配置使用默认/全局)
126
153
  agent = Agent(
127
154
  system_prompt=system_prompt,
128
- name="SubAgent",
155
+ name=agent_name,
129
156
  description="Temporary sub agent for executing a subtask",
130
-
131
- model_group=model_group, # 继承父Agent模型组(如可用)
132
- summary_prompt=summary_prompt, # 继承父Agent总结提示词(如可用)
157
+ model_group=parent_model_group,
158
+ summary_prompt=summary_prompt,
133
159
  auto_complete=auto_complete,
134
- output_handler=None, # 默认 ToolRegistry
135
- use_tools=None, # 初始不限定,稍后同步父Agent工具集
136
- input_handler=None, # 允许使用系统默认输入链
137
- execute_tool_confirm=execute_tool_confirm, # 继承父Agent(如可用)
160
+ output_handler=None,
161
+ use_tools=None,
162
+ input_handler=parent_input_handler,
163
+ execute_tool_confirm=parent_execute_tool_confirm,
138
164
  need_summary=need_summary,
139
- multiline_inputer=None, # 使用系统默认输入器(允许用户输入)
140
- use_methodology=use_methodology, # 继承父Agent(如可用)
141
- use_analysis=use_analysis, # 继承父Agent(如可用)
142
- force_save_memory=force_save_memory, # 继承父Agent(如可用)
165
+ multiline_inputer=parent_multiline_inputer,
166
+ use_methodology=None,
167
+ use_analysis=None,
168
+ force_save_memory=None,
143
169
  files=None,
144
- confirm_callback=_auto_confirm, # 自动确认
145
170
  )
146
171
 
147
- # 同步父Agent的模型名称与工具使用集(若可用)
172
+ # 设置可用工具列表
148
173
  try:
149
- if (
150
- parent_agent is not None
151
- and getattr(parent_agent, "model", None)
152
- and getattr(agent, "model", None)
153
- ):
154
- try:
155
- model_name = parent_agent.model.name() # type: ignore[attr-defined]
156
- if model_name:
157
- from typing import Any
158
- model_obj: Any = getattr(agent, "model", None)
159
- if model_obj is not None:
160
- model_obj.set_model_name(model_name)
161
- except Exception:
162
- pass
163
- if use_tools:
164
- agent.set_use_tools(use_tools)
174
+ agent.set_use_tools(use_tools)
165
175
  except Exception:
166
176
  pass
167
177
 
@@ -13,7 +13,7 @@ import json
13
13
 
14
14
  from jarvis.jarvis_code_agent.code_agent import CodeAgent
15
15
  from jarvis.jarvis_utils.globals import delete_agent
16
- from jarvis.jarvis_code_agent import code_agent as code_agent_module
16
+ from jarvis.jarvis_utils.config import set_config, get_git_check_mode
17
17
 
18
18
 
19
19
  class SubCodeAgentTool:
@@ -59,11 +59,6 @@ class SubCodeAgentTool:
59
59
  "stderr": "task 不能为空",
60
60
  }
61
61
 
62
- # 在模块级别打补丁,仅自动确认交互(允许用户输入)
63
- def _auto_confirm(tip: str, default: bool = True) -> bool:
64
- return default
65
-
66
- code_agent_module.user_confirm = _auto_confirm
67
62
 
68
63
  # 读取背景信息并组合任务
69
64
  background: str = str(args.get("background", "")).strip()
@@ -131,7 +126,12 @@ class SubCodeAgentTool:
131
126
  except Exception:
132
127
  append_tools = None
133
128
 
129
+ # 在子Agent中放宽 Git 配置校验,避免因严格校验导致进程退出
130
+ # 使用配置项将校验模式临时切换为 warn,构造完成后恢复原值
131
+ old_mode = None
134
132
  try:
133
+ old_mode = get_git_check_mode()
134
+ set_config("JARVIS_GIT_CHECK_MODE", "warn")
135
135
  code_agent = CodeAgent(
136
136
  model_group=model_group,
137
137
  need_summary=True,
@@ -145,6 +145,12 @@ class SubCodeAgentTool:
145
145
  "stdout": "",
146
146
  "stderr": f"初始化 CodeAgent 失败(可能未配置 git 或当前非 git 仓库): {se}",
147
147
  }
148
+ finally:
149
+ if old_mode is not None:
150
+ try:
151
+ set_config("JARVIS_GIT_CHECK_MODE", old_mode)
152
+ except Exception:
153
+ pass
148
154
 
149
155
  # 子Agent需要自动完成
150
156
  try:
@@ -423,6 +423,20 @@ def is_enable_static_analysis() -> bool:
423
423
  return GLOBAL_CONFIG_DATA.get("JARVIS_ENABLE_STATIC_ANALYSIS", True) is True
424
424
 
425
425
 
426
+ def get_git_check_mode() -> str:
427
+ """
428
+ 获取Git校验模式。
429
+
430
+ 返回:
431
+ str: "strict" 或 "warn",默认为 "strict"
432
+ """
433
+ mode = GLOBAL_CONFIG_DATA.get("JARVIS_GIT_CHECK_MODE", "strict")
434
+ try:
435
+ return str(mode)
436
+ except Exception:
437
+ return "strict"
438
+
439
+
426
440
  def get_mcp_config() -> List[Dict[str, Any]]:
427
441
  """
428
442
  获取MCP配置列表。