jarvis-ai-assistant 0.3.25__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 (35) hide show
  1. jarvis/__init__.py +1 -1
  2. jarvis/jarvis_agent/__init__.py +290 -169
  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_rag/cli.py +5 -9
  20. jarvis/jarvis_stats/cli.py +13 -32
  21. jarvis/jarvis_stats/stats.py +179 -51
  22. jarvis/jarvis_tools/registry.py +15 -0
  23. jarvis/jarvis_tools/sub_agent.py +94 -84
  24. jarvis/jarvis_tools/sub_code_agent.py +12 -6
  25. jarvis/jarvis_utils/config.py +14 -0
  26. jarvis/jarvis_utils/fzf.py +56 -0
  27. jarvis/jarvis_utils/git_utils.py +3 -8
  28. jarvis/jarvis_utils/input.py +0 -3
  29. jarvis/jarvis_utils/utils.py +96 -16
  30. {jarvis_ai_assistant-0.3.25.dist-info → jarvis_ai_assistant-0.3.27.dist-info}/METADATA +2 -3
  31. {jarvis_ai_assistant-0.3.25.dist-info → jarvis_ai_assistant-0.3.27.dist-info}/RECORD +35 -29
  32. {jarvis_ai_assistant-0.3.25.dist-info → jarvis_ai_assistant-0.3.27.dist-info}/WHEEL +0 -0
  33. {jarvis_ai_assistant-0.3.25.dist-info → jarvis_ai_assistant-0.3.27.dist-info}/entry_points.txt +0 -0
  34. {jarvis_ai_assistant-0.3.25.dist-info → jarvis_ai_assistant-0.3.27.dist-info}/licenses/LICENSE +0 -0
  35. {jarvis_ai_assistant-0.3.25.dist-info → jarvis_ai_assistant-0.3.27.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,92 @@
1
+ # -*- coding: utf-8 -*-
2
+ """
3
+ AgentConfig: 聚合 Agent 的初始化配置并提供默认值解析。
4
+
5
+ 目标(阶段一,最小变更):
6
+ - 提供独立的配置承载类,封装 __init__ 中的配置项
7
+ - 支持从全局配置与上下文推导默认值
8
+ - 暂不强制替换 Agent 的现有参数与流程,后续逐步接入
9
+ """
10
+ from dataclasses import dataclass, field
11
+ from typing import List, Optional
12
+
13
+ from jarvis.jarvis_agent.prompts import DEFAULT_SUMMARY_PROMPT
14
+ from jarvis.jarvis_utils.config import (
15
+ get_max_token_count,
16
+ is_execute_tool_confirm,
17
+ is_force_save_memory,
18
+ is_use_analysis,
19
+ is_use_methodology,
20
+ )
21
+
22
+
23
+ @dataclass
24
+ class AgentConfig:
25
+ # 核心身份与系统参数
26
+ system_prompt: str
27
+ name: str = "Jarvis"
28
+ description: str = ""
29
+ model_group: Optional[str] = None
30
+
31
+ # 运行行为
32
+ auto_complete: bool = False
33
+ need_summary: bool = True
34
+
35
+ # 可选配置(None 表示使用默认策略解析)
36
+ summary_prompt: Optional[str] = None
37
+ execute_tool_confirm: Optional[bool] = None
38
+ use_methodology: Optional[bool] = None
39
+ use_analysis: Optional[bool] = None
40
+ force_save_memory: Optional[bool] = None
41
+ files: Optional[List[str]] = field(default_factory=list)
42
+ max_token_count: Optional[int] = None
43
+
44
+ def resolve_defaults(self) -> "AgentConfig":
45
+ """
46
+ 解析并填充默认值,返回新的 AgentConfig 实例,不修改原对象。
47
+ 策略与 Agent._init_config 中的逻辑保持一致,确保兼容。
48
+ """
49
+ # 复制当前实例的浅拷贝数据
50
+ cfg = AgentConfig(
51
+ system_prompt=self.system_prompt,
52
+ name=self.name,
53
+ description=self.description,
54
+ model_group=self.model_group,
55
+ auto_complete=self.auto_complete,
56
+ need_summary=self.need_summary,
57
+ summary_prompt=self.summary_prompt,
58
+ execute_tool_confirm=self.execute_tool_confirm,
59
+ use_methodology=self.use_methodology,
60
+ use_analysis=self.use_analysis,
61
+ force_save_memory=self.force_save_memory,
62
+ files=list(self.files or []),
63
+ max_token_count=self.max_token_count,
64
+ )
65
+
66
+ # use_methodology: 若存在上传文件则禁用;否则按照外部传入或全局默认
67
+ if cfg.files:
68
+ cfg.use_methodology = False
69
+ elif cfg.use_methodology is None:
70
+ cfg.use_methodology = is_use_methodology()
71
+
72
+ # use_analysis
73
+ if cfg.use_analysis is None:
74
+ cfg.use_analysis = is_use_analysis()
75
+
76
+ # execute_tool_confirm
77
+ if cfg.execute_tool_confirm is None:
78
+ cfg.execute_tool_confirm = is_execute_tool_confirm()
79
+
80
+ # summary_prompt
81
+ if cfg.summary_prompt is None:
82
+ cfg.summary_prompt = DEFAULT_SUMMARY_PROMPT
83
+
84
+ # max_token_count
85
+ if cfg.max_token_count is None:
86
+ cfg.max_token_count = get_max_token_count(cfg.model_group)
87
+
88
+ # force_save_memory
89
+ if cfg.force_save_memory is None:
90
+ cfg.force_save_memory = is_force_save_memory()
91
+
92
+ return cfg
@@ -0,0 +1,48 @@
1
+ # -*- coding: utf-8 -*-
2
+ """
3
+ 事件总线(EventBus)
4
+
5
+ 目标(阶段一,最小变更):
6
+ - 提供简单可靠的发布/订阅机制
7
+ - 回调异常隔离,避免影响主流程
8
+ - 不引入额外依赖,便于在 Agent 中渐进集成
9
+ """
10
+ from collections import defaultdict
11
+ from typing import Callable, DefaultDict, Dict, List
12
+
13
+
14
+
15
+ class EventBus:
16
+ """
17
+ 简单的同步事件总线。
18
+ - subscribe(event, callback): 订阅事件
19
+ - emit(event, **kwargs): 广播事件
20
+ - unsubscribe(event, callback): 取消订阅
21
+ """
22
+
23
+ def __init__(self) -> None:
24
+ self._listeners: DefaultDict[str, List[Callable[..., None]]] = defaultdict(list)
25
+
26
+ def subscribe(self, event: str, callback: Callable[..., None]) -> None:
27
+ if not callable(callback):
28
+ raise TypeError("callback must be callable")
29
+ self._listeners[event].append(callback)
30
+
31
+ def unsubscribe(self, event: str, callback: Callable[..., None]) -> None:
32
+ if event not in self._listeners:
33
+ return
34
+ try:
35
+ self._listeners[event].remove(callback)
36
+ except ValueError:
37
+ pass
38
+
39
+ def emit(self, event: str, **payload: Dict) -> None:
40
+ """
41
+ 广播事件。回调中的异常将被捕获并忽略,以保证主流程稳定。
42
+ """
43
+ for cb in list(self._listeners.get(event, [])):
44
+ try:
45
+ cb(**payload)
46
+ except Exception:
47
+ # 避免回调异常中断主流程
48
+ continue
@@ -19,6 +19,7 @@ from jarvis.jarvis_utils.config import (
19
19
  )
20
20
  import jarvis.jarvis_utils.utils as jutils
21
21
  from jarvis.jarvis_utils.input import user_confirm, get_single_line_input
22
+ from jarvis.jarvis_utils.fzf import fzf_select
22
23
  import os
23
24
  import sys
24
25
  import subprocess
@@ -403,47 +404,80 @@ def handle_builtin_config_selector(
403
404
 
404
405
  Console().print(table)
405
406
 
406
- choice = get_single_line_input(
407
- "选择要启动的配置编号,直接回车使用默认通用代理(jvs): ", default=""
407
+ # Try to use fzf for selection if available (include No. to support number-based filtering)
408
+ fzf_options = [
409
+ f"{idx:>3} | {opt['category']:<12} | {opt['name']:<30} | {opt.get('desc', '')}"
410
+ for idx, opt in enumerate(options, 1)
411
+ ]
412
+ selected_str = fzf_select(
413
+ fzf_options, prompt="选择要启动的配置编号 (ESC跳过) > "
408
414
  )
409
415
 
410
- if choice.strip():
416
+ choice_index = -1
417
+ if selected_str:
418
+ # Try to parse leading number before first '|'
411
419
  try:
412
- index = int(choice.strip())
413
- if 1 <= index <= len(options):
414
- sel = options[index - 1]
415
- args: list[str] = []
416
-
417
- if sel["category"] == "agent":
418
- # jarvis-agent 支持 -f/--config(全局配置)与 -c/--agent-definition
419
- args = [str(sel["cmd"]), "-c", str(sel["file"])]
420
- if model_group:
421
- args += ["-g", str(model_group)]
422
- if config_file:
423
- args += ["-f", str(config_file)]
424
- if task:
425
- args += ["--task", str(task)]
426
-
427
- elif sel["category"] == "multi_agent":
428
- # jarvis-multi-agent 需要 -c/--config,用户输入通过 -i/--input 传递
429
- args = [str(sel["cmd"]), "-c", str(sel["file"])]
430
- if task:
431
- args += ["-i", str(task)]
432
-
433
- elif sel["category"] == "roles":
434
- # jarvis-platform-manager role 子命令,支持 -c/-t/-g
435
- args = [str(sel["cmd"]), "role", "-c", str(sel["file"])]
436
- if model_group:
437
- args += ["-g", str(model_group)]
438
-
439
- if args:
440
- PrettyOutput.print(
441
- f"正在启动: {' '.join(args)}", OutputType.INFO
442
- )
443
- os.execvp(args[0], args)
420
+ num_part = selected_str.split("|", 1)[0].strip()
421
+ selected_index = int(num_part)
422
+ if 1 <= selected_index <= len(options):
423
+ choice_index = selected_index - 1
424
+ except Exception:
425
+ # Fallback to equality matching if parsing fails
426
+ for i, fzf_opt in enumerate(fzf_options):
427
+ if fzf_opt == selected_str:
428
+ choice_index = i
429
+ break
430
+ else:
431
+ # Fallback to manual input if fzf is not used or available
432
+ choice = get_single_line_input(
433
+ "选择要启动的配置编号,直接回车使用默认通用代理(jvs): ", default=""
434
+ )
435
+ if choice.strip():
436
+ try:
437
+ selected_index = int(choice.strip())
438
+ if 1 <= selected_index <= len(options):
439
+ choice_index = selected_index - 1
440
+ except ValueError:
441
+ pass # Invalid input
442
+
443
+ if choice_index != -1:
444
+ try:
445
+ sel = options[choice_index]
446
+ args: list[str] = []
447
+
448
+ if sel["category"] == "agent":
449
+ # jarvis-agent 支持 -f/--config(全局配置)与 -c/--agent-definition
450
+ args = [str(sel["cmd"]), "-c", str(sel["file"])]
451
+ if model_group:
452
+ args += ["-g", str(model_group)]
453
+ if config_file:
454
+ args += ["-f", str(config_file)]
455
+ if task:
456
+ args += ["--task", str(task)]
457
+
458
+ elif sel["category"] == "multi_agent":
459
+ # jarvis-multi-agent 需要 -c/--config,用户输入通过 -i/--input 传递
460
+ args = [str(sel["cmd"]), "-c", str(sel["file"])]
461
+ if task:
462
+ args += ["-i", str(task)]
463
+
464
+ elif sel["category"] == "roles":
465
+ # jarvis-platform-manager role 子命令,支持 -c/-t/-g
466
+ args = [str(sel["cmd"]), "role", "-c", str(sel["file"])]
467
+ if model_group:
468
+ args += ["-g", str(model_group)]
469
+
470
+ if args:
471
+ PrettyOutput.print(
472
+ f"正在启动: {' '.join(args)}", OutputType.INFO
473
+ )
474
+ os.execvp(args[0], args)
444
475
  except Exception:
445
476
  # 任何异常都不影响默认流程
446
477
  pass
478
+ else:
479
+ # User pressed Enter or provided invalid input
480
+ pass
447
481
  except Exception:
448
482
  # 静默忽略内置配置扫描错误,不影响主流程
449
483
  pass
@@ -20,6 +20,13 @@ class MemoryManager:
20
20
  agent: Agent实例
21
21
  """
22
22
  self.agent = agent
23
+ # 本轮任务是否已进行过记忆保存提示/处理的标记,用于事件去重
24
+ self._memory_prompted = False
25
+ # 订阅 Agent 事件(旁路集成,失败不影响主流程)
26
+ try:
27
+ self._subscribe_events()
28
+ except Exception:
29
+ pass
23
30
 
24
31
  def prepare_memory_tags_prompt(self) -> str:
25
32
  """准备记忆标签提示"""
@@ -88,13 +95,27 @@ class MemoryManager:
88
95
 
89
96
  # 处理记忆保存
90
97
  try:
98
+ # 清空本轮执行标记,便于准确判断是否调用了 save_memory
99
+ try:
100
+ self.agent.set_user_data("__last_executed_tool__", "")
101
+ self.agent.set_user_data("__executed_tools__", [])
102
+ except Exception:
103
+ pass
104
+
91
105
  response = self.agent.model.chat_until_success(prompt) # type: ignore
92
106
 
93
107
  # 执行工具调用(如果有)
94
108
  need_return, result = self.agent._call_tools(response)
95
109
 
96
- # 根据响应判断是否保存了记忆
97
- if "save_memory" in response:
110
+ # 根据实际执行的工具判断是否保存了记忆
111
+ saved = False
112
+ try:
113
+ last_tool = self.agent.get_user_data("__last_executed_tool__")
114
+ saved = last_tool == "save_memory"
115
+ except Exception:
116
+ saved = False
117
+
118
+ if saved:
98
119
  PrettyOutput.print(
99
120
  "已自动保存有价值的信息到记忆系统", OutputType.SUCCESS
100
121
  )
@@ -103,6 +124,13 @@ class MemoryManager:
103
124
 
104
125
  except Exception as e:
105
126
  PrettyOutput.print(f"记忆分析失败: {str(e)}", OutputType.ERROR)
127
+ finally:
128
+ # 设置记忆提示完成标记,避免事件触发造成重复处理
129
+ self._memory_prompted = True
130
+ try:
131
+ self.agent.set_user_data("__memory_save_prompted__", True)
132
+ except Exception:
133
+ pass
106
134
 
107
135
  def add_memory_prompts_to_addon(self, addon_prompt: str, tool_registry) -> str:
108
136
  """在附加提示中添加记忆相关提示"""
@@ -125,3 +153,43 @@ class MemoryManager:
125
153
  memory_prompts += "\n - 如果需要获取上下文或寻找解决方案,请调用retrieve_memory工具检索相关记忆"
126
154
 
127
155
  return memory_prompts
156
+
157
+ # -----------------------
158
+ # 事件订阅与处理(旁路)
159
+ # -----------------------
160
+ def _subscribe_events(self) -> None:
161
+ bus = self.agent.get_event_bus() # type: ignore[attr-defined]
162
+ # 任务开始时重置去重标记
163
+ bus.subscribe("task_started", self._on_task_started)
164
+ # 在清理历史前尝试保存记忆(若开启强制保存且尚未处理)
165
+ bus.subscribe("before_history_clear", self._ensure_memory_prompt)
166
+ # 任务完成时作为兜底再尝试一次
167
+ bus.subscribe("task_completed", self._ensure_memory_prompt)
168
+
169
+ def _on_task_started(self, **payload) -> None:
170
+ self._memory_prompted = False
171
+ try:
172
+ self.agent.set_user_data("__memory_save_prompted__", False)
173
+ except Exception:
174
+ pass
175
+
176
+ def _ensure_memory_prompt(self, **payload) -> None:
177
+ # 仅在开启强制保存记忆时启用
178
+ if not getattr(self.agent, "force_save_memory", False):
179
+ return
180
+ # 避免在同一任务内重复提示/处理
181
+ if self._memory_prompted:
182
+ return
183
+ try:
184
+ already = bool(self.agent.get_user_data("__memory_save_prompted__"))
185
+ if already:
186
+ self._memory_prompted = True
187
+ return
188
+ except Exception:
189
+ pass
190
+ # 静默执行保存逻辑,失败不影响主流程
191
+ try:
192
+ self.prompt_memory_save()
193
+ except Exception:
194
+ # 忽略异常,保持主流程稳定
195
+ self._memory_prompted = True
@@ -0,0 +1,82 @@
1
+ # -*- coding: utf-8 -*-
2
+ """
3
+ PromptManager: 统一管理 Agent 的系统提示词与附加提示词的构建逻辑。
4
+
5
+ 设计目标(阶段一,最小变更):
6
+ - 提供独立的提示构建类,不改变现有行为
7
+ - 先行落地构建逻辑,后续在 Agent 中逐步委派使用
8
+ - 保持与现有工具/记忆系统兼容
9
+ """
10
+ from typing import TYPE_CHECKING
11
+
12
+ from jarvis.jarvis_tools.registry import ToolRegistry
13
+ from jarvis.jarvis_utils.tag import ot
14
+
15
+ if TYPE_CHECKING:
16
+ # 避免运行时循环依赖,仅用于类型标注
17
+ from . import Agent # noqa: F401
18
+
19
+
20
+
21
+ class PromptManager:
22
+ """
23
+ 提示管理器:负责构建系统提示与默认附加提示。
24
+ 注意:该类不直接访问模型,只负责拼装字符串。
25
+ """
26
+
27
+ def __init__(self, agent: "Agent"):
28
+ self.agent = agent
29
+
30
+ # ----------------------------
31
+ # 系统提示词构建
32
+ # ----------------------------
33
+ def build_system_prompt(self) -> str:
34
+ """
35
+ 构建系统提示词,复用现有的工具使用提示生成逻辑,保持行为一致。
36
+ """
37
+ action_prompt = self.agent.get_tool_usage_prompt()
38
+ return f"""
39
+ {self.agent.system_prompt}
40
+
41
+ {action_prompt}
42
+ """
43
+
44
+ # ----------------------------
45
+ # 附加提示词构建
46
+ # ----------------------------
47
+ def build_default_addon_prompt(self, need_complete: bool) -> str:
48
+ """
49
+ 构建默认附加提示词(与 Agent.make_default_addon_prompt 行为保持一致)。
50
+ 仅进行字符串拼装,不操作会话状态。
51
+ """
52
+ # 结构化系统指令
53
+ action_handlers = ", ".join([handler.name() for handler in self.agent.output_handler])
54
+
55
+ # 任务完成提示
56
+ complete_prompt = (
57
+ f"- 输出{ot('!!!COMPLETE!!!')}"
58
+ if need_complete and self.agent.auto_complete
59
+ else ""
60
+ )
61
+
62
+ # 工具与记忆相关提示
63
+ tool_registry = self.agent.get_tool_registry()
64
+ memory_prompts = self.agent.memory_manager.add_memory_prompts_to_addon(
65
+ "", tool_registry if isinstance(tool_registry, ToolRegistry) else None
66
+ )
67
+
68
+ addon_prompt = f"""
69
+ <system_prompt>
70
+ 请判断是否已经完成任务,如果已经完成:
71
+ - 直接输出完成原因,不需要再有新的操作,不要输出{ot("TOOL_CALL")}标签
72
+ {complete_prompt}
73
+ 如果没有完成,请进行下一步操作:
74
+ - 仅包含一个操作
75
+ - 如果信息不明确,请请求用户补充
76
+ - 如果执行过程中连续失败5次,请使用ask_user询问用户操作
77
+ - 操作列表:{action_handlers}{memory_prompts}
78
+ </system_prompt>
79
+
80
+ 请继续。
81
+ """
82
+ return addon_prompt
@@ -0,0 +1,130 @@
1
+ # -*- coding: utf-8 -*-
2
+ """
3
+ AgentRunLoop: 承载 Agent 的主运行循环逻辑。
4
+
5
+ 阶段一目标(最小变更):
6
+ - 复制现有 _main_loop 逻辑到独立类,使用传入的 agent 实例进行委派调用
7
+ - 暂不变更外部调用入口,后续在 Agent._main_loop 中委派到该类
8
+ - 保持与现有异常处理、工具调用、用户交互完全一致
9
+ """
10
+ from enum import Enum
11
+ from typing import Any, TYPE_CHECKING
12
+
13
+ from jarvis.jarvis_utils.output import OutputType, PrettyOutput
14
+ from jarvis.jarvis_utils.tag import ot
15
+
16
+ if TYPE_CHECKING:
17
+ # 仅用于类型标注,避免运行时循环依赖
18
+ from . import Agent # noqa: F401
19
+
20
+
21
+ class AgentRunLoop:
22
+ def __init__(self, agent: "Agent") -> None:
23
+ self.agent = agent
24
+
25
+ def run(self) -> Any:
26
+ """主运行循环(委派到传入的 agent 实例的方法与属性)"""
27
+ run_input_handlers = True
28
+
29
+ while True:
30
+ try:
31
+ ag = self.agent
32
+
33
+ # 更新输入处理器标志
34
+ if ag.run_input_handlers_next_turn:
35
+ run_input_handlers = True
36
+ ag.run_input_handlers_next_turn = False
37
+
38
+ # 首次运行初始化
39
+ if ag.first:
40
+ ag._first_run()
41
+
42
+ # 调用模型获取响应
43
+ current_response = ag._call_model(
44
+ ag.session.prompt, True, run_input_handlers
45
+ )
46
+
47
+ ag.session.prompt = ""
48
+ run_input_handlers = False
49
+
50
+ # 处理中断
51
+ interrupt_result = ag._handle_run_interrupt(current_response)
52
+ if (
53
+ isinstance(interrupt_result, Enum)
54
+ and getattr(interrupt_result, "value", None) == "skip_turn"
55
+ ):
56
+ # 中断处理器请求跳过本轮剩余部分,直接开始下一次循环
57
+ continue
58
+ elif interrupt_result is not None and not isinstance(interrupt_result, Enum):
59
+ # 中断处理器返回了最终结果,任务结束
60
+ return interrupt_result
61
+
62
+ # 处理工具调用
63
+ # 广播工具调用前事件(不影响主流程)
64
+ try:
65
+ ag.event_bus.emit(
66
+ "before_tool_call",
67
+ agent=ag,
68
+ current_response=current_response,
69
+ )
70
+ except Exception:
71
+ pass
72
+ need_return, tool_prompt = ag._call_tools(current_response)
73
+
74
+ # 将上一个提示和工具提示安全地拼接起来
75
+ prompt_parts = []
76
+ if ag.session.prompt:
77
+ prompt_parts.append(ag.session.prompt)
78
+ if tool_prompt:
79
+ prompt_parts.append(tool_prompt)
80
+ ag.session.prompt = "\n\n".join(prompt_parts)
81
+
82
+ if need_return:
83
+ return ag.session.prompt
84
+
85
+ # 执行回调
86
+ if ag.after_tool_call_cb:
87
+ ag.after_tool_call_cb(ag)
88
+ # 广播工具调用后的事件(不影响主流程)
89
+ try:
90
+ ag.event_bus.emit(
91
+ "after_tool_call",
92
+ agent=ag,
93
+ current_response=current_response,
94
+ need_return=need_return,
95
+ tool_prompt=tool_prompt,
96
+ )
97
+ except Exception:
98
+ pass
99
+
100
+ # 检查是否需要继续
101
+ if ag.session.prompt or ag.session.addon_prompt:
102
+ continue
103
+
104
+ # 检查自动完成
105
+ if ag.auto_complete and ot("!!!COMPLETE!!!") in current_response:
106
+ return ag._complete_task(auto_completed=True)
107
+
108
+ # 获取下一步用户输入
109
+ next_action = ag._get_next_user_action()
110
+ if (
111
+ next_action == "continue"
112
+ or (
113
+ isinstance(next_action, Enum)
114
+ and getattr(next_action, "value", None) == "continue"
115
+ )
116
+ ):
117
+ run_input_handlers = True
118
+ continue
119
+ elif (
120
+ next_action == "complete"
121
+ or (
122
+ isinstance(next_action, Enum)
123
+ and getattr(next_action, "value", None) == "complete"
124
+ )
125
+ ):
126
+ return ag._complete_task(auto_completed=False)
127
+
128
+ except Exception as e:
129
+ PrettyOutput.print(f"任务失败: {str(e)}", OutputType.ERROR)
130
+ return f"Task failed: {str(e)}"