myagent-ai 1.5.6 → 1.5.7

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.
@@ -115,6 +115,31 @@ class MainAgent(BaseAgent):
115
115
  self._org_context_mtime: float = 0 # 文件修改时间,用于检测变更
116
116
  # Token 上下文管理器 (滚动摘要 + 预算控制)
117
117
  self.context_manager = ContextManager(ContextConfig())
118
+ # 执行事件追踪(用于前端展示命令执行过程)
119
+ self._execution_events: List[Dict] = []
120
+ self._exec_event_counter: int = 0
121
+
122
+ def _add_exec_event(self, event_type: str, data: Dict):
123
+ """记录一个执行事件(供前端展示)"""
124
+ import time as _time
125
+ self._exec_event_counter += 1
126
+ event = {
127
+ "id": self._exec_event_counter,
128
+ "type": event_type,
129
+ "timestamp": _time.time(),
130
+ **data,
131
+ }
132
+ self._execution_events.append(event)
133
+ logger.debug(f"[exec-event] {event_type}: {data.get('title', '')[:80]}")
134
+
135
+ def get_execution_events(self) -> List[Dict]:
136
+ """获取本次处理中的所有执行事件"""
137
+ return self._execution_events
138
+
139
+ def clear_execution_events(self):
140
+ """清空执行事件"""
141
+ self._execution_events = []
142
+ self._exec_event_counter = 0
118
143
 
119
144
  async def process(self, context: AgentContext) -> AgentContext:
120
145
  """
@@ -133,6 +158,8 @@ class MainAgent(BaseAgent):
133
158
  context.task_id = task_id
134
159
  self._iteration_count = 0
135
160
  self._current_task_id = task_id
161
+ # 清空上一轮的执行事件
162
+ self.clear_execution_events()
136
163
 
137
164
  # 注册到配置广播器(用于热重载通知)
138
165
  if self.config_broadcaster:
@@ -217,10 +244,26 @@ class MainAgent(BaseAgent):
217
244
  content=response.content or "",
218
245
  tool_calls=response.tool_calls)
219
246
  )
247
+ # 记录工具调用事件
248
+ for tc in response.tool_calls:
249
+ self._add_exec_event("tool_call", {
250
+ "title": f"调用工具: {tc['name']}",
251
+ "tool_name": tc["name"],
252
+ "arguments": tc.get("arguments", {}),
253
+ })
220
254
  # 执行工具调用
221
255
  tool_results = await self._handle_tool_calls(
222
256
  response.tool_calls, context, task_id
223
257
  )
258
+ # 记录工具结果事件
259
+ for tc, result in tool_results:
260
+ self._add_exec_event("tool_result", {
261
+ "title": f"工具结果: {tc['name']}",
262
+ "tool_name": tc["name"],
263
+ "success": result.get("success", False),
264
+ "summary": truncate_str(result.get("output", result.get("error", "")), 500),
265
+ "result": result,
266
+ })
224
267
  # 再将工具结果加入消息历史
225
268
  for tc, result in tool_results:
226
269
  context.conversation_history.append(
@@ -510,13 +553,33 @@ class MainAgent(BaseAgent):
510
553
  "error": f"[权限] 当前 Agent 没有'{label}'权限,操作被拒绝",
511
554
  "metadata": {"permission_denied": skill_perm},
512
555
  })
556
+ self._add_exec_event("skill_call", {
557
+ "title": f"技能调用被拒: {skill_name}",
558
+ "skill_name": skill_name,
559
+ "success": False,
560
+ "error": f"权限不足: {label}",
561
+ })
513
562
  continue
514
563
 
564
+ # 记录技能调用事件
565
+ self._add_exec_event("skill_call", {
566
+ "title": f"调用技能: {skill_name}",
567
+ "skill_name": skill_name,
568
+ "params": action.get("params", {}),
569
+ })
515
570
  result = await self.skills.execute(
516
571
  action.get("name", ""),
517
572
  **action.get("params", {}),
518
573
  )
519
- results.append(result.to_dict())
574
+ result_dict = result.to_dict()
575
+ results.append(result_dict)
576
+ # 记录技能结果事件
577
+ self._add_exec_event("skill_result", {
578
+ "title": f"技能结果: {skill_name}",
579
+ "skill_name": skill_name,
580
+ "success": result_dict.get("success", False),
581
+ "summary": truncate_str(str(result_dict.get("output", result_dict.get("error", ""))), 500),
582
+ })
520
583
 
521
584
  elif action_type == "code" and self.executor:
522
585
  # 权限检查: execution
@@ -526,8 +589,27 @@ class MainAgent(BaseAgent):
526
589
  "error": "[权限] 当前 Agent 没有代码执行权限,操作被拒绝",
527
590
  "metadata": {"permission_denied": "execution"},
528
591
  })
592
+ self._add_exec_event("code_exec", {
593
+ "title": f"代码执行被拒",
594
+ "language": action.get("language", "unknown"),
595
+ "code_preview": truncate_str(action.get("code", ""), 200),
596
+ "success": False,
597
+ "error": "代码执行权限不足",
598
+ })
529
599
  continue
530
600
 
601
+ code_lang = action.get("language", "python")
602
+ code_text = action.get("code", "")
603
+ # 记录代码执行开始事件
604
+ self._add_exec_event("code_exec", {
605
+ "title": f"执行 {code_lang} 代码",
606
+ "language": code_lang,
607
+ "code": code_text,
608
+ "code_preview": truncate_str(code_text, 200),
609
+ "status": "running",
610
+ "timeout": timeout_seconds if 'timeout_seconds' in dir() else 120,
611
+ })
612
+
531
613
  # 注入权限检查器到 executor(用于更细粒度的检查)
532
614
  self.executor.set_permission_checker(
533
615
  self.check_permission, self.name
@@ -563,6 +645,21 @@ class MainAgent(BaseAgent):
563
645
 
564
646
  result_dict = exec_result.to_dict()
565
647
 
648
+ # 记录代码执行结果事件
649
+ self._add_exec_event("code_result", {
650
+ "title": f"{'超时' if exec_result.timed_out else '成功' if exec_result.success else '失败'}: {code_lang}",
651
+ "language": code_lang,
652
+ "code_preview": truncate_str(action.get("code", ""), 200),
653
+ "success": exec_result.success,
654
+ "timed_out": exec_result.timed_out,
655
+ "exit_code": exec_result.exit_code,
656
+ "execution_time": round(exec_result.execution_time, 3),
657
+ "stdout": truncate_str(exec_result.stdout, 5000),
658
+ "stderr": truncate_str(exec_result.stderr, 3000),
659
+ "error": truncate_str(exec_result.error, 2000),
660
+ "result": result_dict,
661
+ })
662
+
566
663
  # 超时后自动触发 LLM 诊断分析
567
664
  if exec_result.timed_out:
568
665
  logger.info(
@@ -9,15 +9,27 @@ param(
9
9
 
10
10
  $ErrorActionPreference = "Stop"
11
11
  $PKG_NAME = "myagent-ai"
12
- $PKG_VERSION = "1.5.3"
12
+ $PKG_VERSION = ""
13
+
14
+ # 动态获取 npm 最新版本号
15
+ function Get-NpmVersion {
16
+ try {
17
+ $ver = (npm view $PKG_NAME version 2>$null)
18
+ if ($ver) { $script:PKG_VERSION = $ver }
19
+ } catch {}
20
+ }
13
21
 
14
22
  # Allow running scripts for the current process
15
23
  if ($PSVersionTable.PSVersion.Major -ge 5) {
16
24
  Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope Process -Force
17
25
  }
18
26
 
27
+ # 尝试获取最新版本
28
+ Get-NpmVersion
29
+
30
+ $verDisplay = if ($PKG_VERSION) { " v$PKG_VERSION" } else { "" }
19
31
  Write-Host ""
20
- Write-Host " MyAgent Installer v$PKG_VERSION" -ForegroundColor Cyan
32
+ Write-Host " MyAgent Installer$verDisplay" -ForegroundColor Cyan
21
33
  Write-Host ""
22
34
 
23
35
  if ($PSVersionTable.PSVersion.Major -lt 5) {
@@ -21,14 +21,25 @@ NO_DEPS=false
21
21
  DRY_RUN=false
22
22
  SCRIPT_URL="https://raw.githubusercontent.com/ctz168/myagent/main/install/install.sh"
23
23
  PKG_NAME="myagent-ai"
24
- PKG_VERSION="1.5.3"
24
+ PKG_VERSION=""
25
+
26
+ # 动态获取 npm 最新版本号
27
+ fetch_version() {
28
+ PKG_VERSION=""
29
+ local npm_ver
30
+ npm_ver="$(npm view "$PKG_NAME" version 2>/dev/null)" || true
31
+ if [ -n "$npm_ver" ]; then
32
+ PKG_VERSION="$npm_ver"
33
+ fi
34
+ }
25
35
 
26
36
  for arg in "$@"; do
27
37
  case "$arg" in
28
38
  --no-deps) NO_DEPS=true ;;
29
39
  --dry-run) DRY_RUN=true ;;
30
40
  --help|-h)
31
- echo "MyAgent Installer v$PKG_VERSION"
41
+ fetch_version
42
+ echo "MyAgent Installer${PKG_VERSION:+ v$PKG_VERSION}"
32
43
  echo ""
33
44
  echo "Usage: curl -fsSL $SCRIPT_URL | bash [-s -- OPTIONS]"
34
45
  echo ""
@@ -60,9 +71,12 @@ detect_os() {
60
71
  esac
61
72
  }
62
73
 
74
+ # 尝试获取最新版本(获取失败则不显示版本号)
75
+ fetch_version
76
+
63
77
  OS="$(detect_os)"
64
78
  echo ""
65
- echo -e " ${BOLD}${ACCENT}MyAgent${NC} Installer v$PKG_VERSION"
79
+ echo -e " ${BOLD}${ACCENT}MyAgent${NC} Installer${PKG_VERSION:+ v$PKG_VERSION}"
66
80
  echo ""
67
81
  success "$OS detected"
68
82
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "myagent-ai",
3
- "version": "1.5.6",
3
+ "version": "1.5.7",
4
4
  "description": "本地桌面端执行型AI助手 - Open Interpreter 风格 | Local Desktop Execution-Oriented AI Assistant",
5
5
  "main": "main.py",
6
6
  "bin": {
package/web/api_server.py CHANGED
@@ -447,6 +447,11 @@ class ApiServer:
447
447
  # 记忆保存已由 MainAgent._process_inner() 完成,此处不再重复保存
448
448
  # (MainAgent 内部会保存 user + assistant 消息到短期记忆)
449
449
 
450
+ # ── 收集执行事件(供前端展示命令执行过程) ──
451
+ exec_events = []
452
+ if self.core.main_agent:
453
+ exec_events = self.core.main_agent.get_execution_events()
454
+
450
455
  # ── 执行模式: 从回复中提取并更新任务计划 ──
451
456
  if chat_mode == "exec" and self.core.llm:
452
457
  try:
@@ -454,7 +459,10 @@ class ApiServer:
454
459
  except Exception as tp_err:
455
460
  logger.warning(f"任务计划更新失败: {tp_err}")
456
461
 
457
- return web.json_response({"response": response, "session_id": session_id, "agent_name": agent_path, "agent_path": agent_path})
462
+ resp_data = {"response": response, "session_id": session_id, "agent_name": agent_path, "agent_path": agent_path}
463
+ if exec_events:
464
+ resp_data["exec_events"] = exec_events
465
+ return web.json_response(resp_data)
458
466
  except Exception as e:
459
467
  logger.error(f"Chat error: {e}", exc_info=True)
460
468
  return web.json_response({"error": str(e)}, status=500)
@@ -843,13 +851,10 @@ class ApiServer:
843
851
  if not cfg_file.exists():
844
852
  continue
845
853
  agent_path = f"{prefix}{d.name}" if not prefix else f"{prefix}/{d.name}"
846
- # 迁移: 为缺少 id/created_at/updated_at 的旧 agent 补全字段
847
854
  # 跳过符号链接目录(如 p -> 配置助手),避免重复处理
848
855
  if d.is_symlink():
849
- try:
850
- cfg = json.loads(cfg_file.read_text(encoding="utf-8"))
851
- except (json.JSONDecodeError, ValueError):
852
- continue
856
+ continue
857
+ # 迁移: 为缺少 id/created_at/updated_at 的旧 agent 补全字段
853
858
  else:
854
859
  self._migrate_agent_config(agent_path)
855
860
  # 迁移可能删除了损坏的文件
@@ -1178,6 +1183,26 @@ class ApiServer:
1178
1183
  "agent_path": path,
1179
1184
  }, status=403)
1180
1185
  if ad.exists():
1186
+ # 清理该 Agent 相关的会话和记忆数据
1187
+ if self.core.memory:
1188
+ try:
1189
+ # 删除相关会话 (session_id 格式: agent_session_xxx 或 agent_web_xxx)
1190
+ conn = self.core.memory._get_conn()
1191
+ like_pattern = f"{path}%"
1192
+ rows = conn.execute(
1193
+ "SELECT DISTINCT session_id FROM memories WHERE session_id LIKE ?",
1194
+ (like_pattern,),
1195
+ ).fetchall()
1196
+ for row in rows:
1197
+ sid = row["session_id"]
1198
+ # 删除该会话的所有记忆
1199
+ conn.execute("DELETE FROM memories WHERE session_id = ?", (sid,))
1200
+ conn.execute("DELETE FROM session_names WHERE session_id = ?", (sid,))
1201
+ logger.info(f" 清理会话记忆: {sid}")
1202
+ conn.commit()
1203
+ logger.info(f" 已清理 Agent '{path}' 的 {len(rows)} 个会话记忆")
1204
+ except Exception as e:
1205
+ logger.warning(f"清理会话记忆失败: {e}")
1181
1206
  shutil.rmtree(ad)
1182
1207
  logger.info(f"删除 Agent: {path}")
1183
1208
  return web.json_response({"ok": True})
@@ -1573,7 +1598,12 @@ class ApiServer:
1573
1598
  return web.json_response({"error": "权限管理器未初始化"}, status=500)
1574
1599
  data = await request.json()
1575
1600
  try:
1576
- pm.set_default_permission(data)
1601
+ # data 格式: {"execution": true, "file_read": false, ...}
1602
+ if isinstance(data, dict):
1603
+ for perm, value in data.items():
1604
+ if isinstance(value, bool) and perm in PermissionManager.ALL_PERMISSIONS:
1605
+ pm.set_default_permission(perm, value)
1606
+ pm.save()
1577
1607
  return web.json_response({"ok": True})
1578
1608
  except Exception as e:
1579
1609
  return web.json_response({"error": str(e)}, status=500)
@@ -1599,6 +1629,7 @@ class ApiServer:
1599
1629
  data = await request.json()
1600
1630
  try:
1601
1631
  pm.set_permissions(agent, data)
1632
+ pm.save()
1602
1633
  return web.json_response({"ok": True})
1603
1634
  except Exception as e:
1604
1635
  return web.json_response({"error": str(e)}, status=500)
@@ -1611,6 +1642,7 @@ class ApiServer:
1611
1642
  agent = request.match_info["agent"]
1612
1643
  try:
1613
1644
  pm.reset_agent_permissions(agent)
1645
+ pm.save()
1614
1646
  return web.json_response({"ok": True})
1615
1647
  except Exception as e:
1616
1648
  return web.json_response({"error": str(e)}, status=500)
package/web/ui/chat.html CHANGED
@@ -21,6 +21,33 @@
21
21
  --sidebar-w:280px;--header-h:60px;--agent-panel-w:300px;
22
22
  --transition:all .2s ease;
23
23
  }
24
+
25
+ /* ── Theme System ── */
26
+ [data-theme="claude"]{
27
+ --bg:#f5f0e8;--bg2:#ebe5d9;--bg3:#e2dace;--bg4:#d6cfc3;--bg5:#c8bfb0;
28
+ --text:#2c2c2c;--text2:#5a5a5a;--text3:#8a8a8a;
29
+ --accent:#c96442;--accent2:#d97a5a;--accent-light:#fceee8;--accent-dark:#a0502e;
30
+ --user-bubble:#c96442;--user-text:#ffffff;
31
+ --bot-bubble:#ffffff;--bot-text:#2c2c2c;
32
+ --ok:#4a9c6d;--warn:#c4862b;--danger:#c94444;--info:#4a7fc9;
33
+ --border:#d6cfc3;--border-light:#e2dace;
34
+ --shadow-sm:0 1px 2px rgba(0,0,0,.06);
35
+ --shadow:0 1px 3px rgba(0,0,0,.08),0 1px 2px rgba(0,0,0,.04);
36
+ --shadow-lg:0 10px 25px rgba(0,0,0,.1);
37
+ }
38
+ [data-theme="dark"]{
39
+ --bg:#0f1117;--bg2:#1a1d27;--bg3:#232733;--bg4:#2e3347;--bg5:#3a3f57;
40
+ --text:#e1e4ed;--text2:#8b92a5;--text3:#5c6378;
41
+ --accent:#6366f1;--accent2:#818cf8;--accent-light:#2d2f5e;--accent-dark:#9da3f8;
42
+ --user-bubble:#6366f1;--user-text:#ffffff;
43
+ --bot-bubble:#1a1d27;--bot-text:#e1e4ed;
44
+ --ok:#22c55e;--warn:#f59e0b;--danger:#ef4444;--info:#3b82f6;
45
+ --border:#2e3347;--border-light:#232733;
46
+ --shadow-sm:0 1px 2px rgba(0,0,0,.2);
47
+ --shadow:0 1px 3px rgba(0,0,0,.3),0 1px 2px rgba(0,0,0,.2);
48
+ --shadow-lg:0 10px 25px rgba(0,0,0,.4);
49
+ }
50
+
24
51
  html,body{height:100%;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',system-ui,'Noto Sans SC',sans-serif;background:var(--bg);color:var(--text);font-size:14px;line-height:1.6;overflow:hidden}
25
52
  a{color:var(--accent);text-decoration:none}
26
53
  button{font:inherit;cursor:pointer;border:none;background:none;color:inherit}
@@ -1281,6 +1308,111 @@ input,textarea,select{font:inherit}
1281
1308
  .tts-voice-item.active{background:var(--accent-light);color:var(--accent)}
1282
1309
  .tts-voice-item .voice-icon{font-size:14px}
1283
1310
  .tts-voice-item .voice-name{flex:1;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
1311
+
1312
+ /* ── Dark Theme Overrides ── */
1313
+ [data-theme="dark"] .message-bubble code{background:rgba(255,255,255,.1)}
1314
+ [data-theme="dark"] .message-row.user .message-bubble code{background:rgba(255,255,255,.15)}
1315
+ [data-theme="dark"] .message-bubble pre{background:#0a0c10;color:#cdd6f4}
1316
+ [data-theme="dark"] .group-msg-bubble code{background:rgba(255,255,255,.08)}
1317
+ [data-theme="dark"] .group-msg-bubble pre{background:#0a0c10;color:#cdd6f4}
1318
+ [data-theme="dark"] .toast-error{background:rgba(127,29,29,.8);color:#fca5a5;border-color:rgba(239,68,68,.4)}
1319
+ [data-theme="dark"] .toast-success{background:rgba(6,78,59,.8);color:#6ee7b7;border-color:rgba(16,185,129,.4)}
1320
+ [data-theme="dark"] .toast-info{background:rgba(30,64,175,.8);color:#93c5fd;border-color:rgba(59,130,246,.4)}
1321
+ [data-theme="dark"] .modal-overlay{background:rgba(0,0,0,.5)}
1322
+ [data-theme="dark"] .exec-badge.local{background:rgba(16,185,129,.15);color:#6ee7b7}
1323
+ [data-theme="dark"] .exec-badge.sandbox{background:rgba(245,158,11,.15);color:#fcd34d}
1324
+ [data-theme="dark"] .mode-btn.active-chat{background:var(--bg2);color:var(--accent);box-shadow:var(--shadow-sm)}
1325
+ [data-theme="dark"] .mode-btn.active-exec{background:var(--accent);color:#fff;box-shadow:var(--shadow-sm)}
1326
+ [data-theme="dark"] .exec-mode-btn.active-local{background:rgba(16,185,129,.15);color:#6ee7b7;border-color:rgba(16,185,129,.3)}
1327
+ [data-theme="dark"] .exec-mode-btn.active-local:hover{background:rgba(16,185,129,.25)}
1328
+ [data-theme="dark"] .lock-indicator{background:rgba(127,29,29,.3);border-color:rgba(239,68,68,.3);color:#fca5a5}
1329
+ [data-theme="dark"] .lock-indicator:hover{background:rgba(127,29,29,.5)}
1330
+ [data-theme="dark"] .code-block-wrapper pre{background:#0a0c10;color:#cdd6f4}
1331
+ [data-theme="dark"] .code-copy-btn{background:var(--bg4);color:var(--text2)}
1332
+ [data-theme="dark"] .code-copy-btn:hover{background:var(--bg5);color:var(--text)}
1333
+ [data-theme="dark"] .code-copy-btn.copied{background:rgba(16,185,129,.3);color:#a7f3d0}
1334
+ [data-theme="dark"] .group-member-role.owner{background:rgba(245,158,11,.15);color:#fcd34d}
1335
+ [data-theme="dark"] .group-member-role.admin{background:rgba(59,130,246,.15);color:#93c5fd}
1336
+ [data-theme="dark"] .setup-wizard-overlay{background:rgba(10,10,20,.9)}
1337
+ [data-theme="dark"] .largetext-option:hover,[data-theme="dark"] .largetext-option.selected{border-color:var(--accent);background:rgba(99,102,241,.1)}
1338
+ [data-theme="dark"] .platform-toggle{background:var(--bg4)}
1339
+ [data-theme="dark"] .quick-action{background:var(--bg2);border-color:var(--border)}
1340
+ [data-theme="dark"] .quick-action:hover{border-color:var(--accent);color:var(--accent);background:var(--accent-light)}
1341
+ [data-theme="dark"] .task-delete:hover{background:rgba(127,29,29,.3)}
1342
+ [data-theme="dark"] .platform-card-btn.delete:hover{background:rgba(127,29,29,.3)}
1343
+ [data-theme="dark"] ::-webkit-scrollbar-thumb{background:var(--bg4)}
1344
+ [data-theme="dark"] ::-webkit-scrollbar-thumb:hover{background:var(--bg5)}
1345
+ [data-theme="claude"] .sidebar-logo{background:linear-gradient(135deg,#c96442,#a0502e)}
1346
+ [data-theme="claude"] .new-chat-btn{background:var(--accent)}
1347
+ [data-theme="claude"] .new-chat-btn:hover{background:var(--accent2)}
1348
+
1349
+ /* ── Execution Events Panel ── */
1350
+ .exec-events-panel{margin-top:10px;border:1px solid var(--border);border-radius:var(--radius-sm);overflow:hidden;background:var(--bg)}
1351
+ .exec-events-header{display:flex;align-items:center;gap:6px;padding:8px 12px;background:var(--bg2);border-bottom:1px solid var(--border);cursor:pointer;font-size:12px;font-weight:600;color:var(--text2);user-select:none;transition:var(--transition)}
1352
+ .exec-events-header:hover{background:var(--bg3);color:var(--text)}
1353
+ .exec-events-header .events-chevron{transition:transform .2s ease;font-size:10px}
1354
+ .exec-events-header.expanded .events-chevron{transform:rotate(90deg)}
1355
+ .exec-events-header .events-count{background:var(--accent);color:#fff;font-size:10px;padding:0 6px;border-radius:10px;font-weight:700;min-width:18px;text-align:center}
1356
+ .exec-events-body{max-height:0;overflow:hidden;transition:max-height .3s ease}
1357
+ .exec-events-body.expanded{max-height:600px;overflow-y:auto}
1358
+ .exec-event-item{display:flex;align-items:flex-start;gap:8px;padding:8px 12px;border-bottom:1px solid var(--border-light);font-size:12px;transition:background .15s}
1359
+ .exec-event-item:last-child{border-bottom:none}
1360
+ .exec-event-item:hover{background:var(--bg2)}
1361
+ .exec-event-icon{width:20px;height:20px;border-radius:4px;display:grid;place-items:center;flex-shrink:0;font-size:11px;margin-top:1px}
1362
+ .exec-event-icon.tool{background:#eff6ff;color:#3b82f6}
1363
+ .exec-event-icon.code-run{background:#fef3c7;color:#d97706}
1364
+ .exec-event-icon.code-ok{background:#dcfce7;color:#16a34a}
1365
+ .exec-event-icon.code-fail{background:#fee2e2;color:#dc2626}
1366
+ .exec-event-icon.code-timeout{background:#fce7f3;color:#db2777}
1367
+ .exec-event-icon.skill{background:#f3e8ff;color:#7c3aed}
1368
+ .exec-event-body{flex:1;min-width:0}
1369
+ .exec-event-title{font-weight:600;color:var(--text);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
1370
+ .exec-event-meta{font-size:11px;color:var(--text3);margin-top:2px;display:flex;gap:8px;flex-wrap:wrap}
1371
+ .exec-event-meta .meta-tag{padding:0 4px;border-radius:3px;background:var(--bg3);white-space:nowrap}
1372
+ .exec-event-code{margin-top:4px;padding:4px 8px;background:var(--bg3);border-radius:4px;font-family:'SF Mono',Monaco,Consolas,'Courier New',monospace;font-size:11px;color:var(--text2);white-space:nowrap;overflow:hidden;text-overflow:ellipsis;cursor:pointer;transition:var(--transition)}
1373
+ .exec-event-code:hover{background:var(--bg4);color:var(--text)}
1374
+ .exec-event-summary{margin-top:3px;font-size:11px;color:var(--text3);white-space:nowrap;overflow:hidden;text-overflow:ellipsis;max-width:100%}
1375
+ .exec-event-result-btn{display:inline-flex;align-items:center;gap:4px;padding:2px 8px;margin-top:4px;border-radius:4px;background:var(--bg3);color:var(--accent);font-size:11px;cursor:pointer;border:none;transition:var(--transition)}
1376
+ .exec-event-result-btn:hover{background:var(--accent-light);color:var(--accent-dark)}
1377
+ .exec-event-result-btn svg{width:12px;height:12px}
1378
+
1379
+ /* ── Execution Result Modal ── */
1380
+ .exec-result-modal-overlay{position:fixed;inset:0;background:rgba(0,0,0,.5);z-index:1000;display:flex;align-items:center;justify-content:center;animation:fadeIn .15s ease}
1381
+ .exec-result-modal{background:var(--bg);border:1px solid var(--border);border-radius:12px;width:min(680px,90vw);max-height:80vh;display:flex;flex-direction:column;box-shadow:0 20px 60px rgba(0,0,0,.25);animation:slideUp .2s ease}
1382
+ .exec-result-modal-header{display:flex;align-items:center;justify-content:space-between;padding:14px 18px;border-bottom:1px solid var(--border);flex-shrink:0}
1383
+ .exec-result-modal-header h3{font-size:14px;font-weight:700;display:flex;align-items:center;gap:8px}
1384
+ .exec-result-modal-header h3 .status-icon{font-size:16px}
1385
+ .exec-result-modal-close{width:28px;height:28px;border-radius:6px;display:grid;place-items:center;cursor:pointer;color:var(--text3);transition:var(--transition);border:none;background:none}
1386
+ .exec-result-modal-close:hover{background:var(--bg3);color:var(--text)}
1387
+ .exec-result-modal-tabs{display:flex;border-bottom:1px solid var(--border);padding:0 18px;flex-shrink:0}
1388
+ .exec-result-tab{padding:8px 14px;font-size:12px;font-weight:600;color:var(--text3);cursor:pointer;border:none;background:none;border-bottom:2px solid transparent;transition:var(--transition)}
1389
+ .exec-result-tab:hover{color:var(--text2)}
1390
+ .exec-result-tab.active{color:var(--accent);border-bottom-color:var(--accent)}
1391
+ .exec-result-modal-body{flex:1;overflow-y:auto;padding:16px 18px;min-height:0}
1392
+ .exec-result-section{display:none}
1393
+ .exec-result-section.active{display:block}
1394
+ .exec-result-section pre{background:var(--bg3);border:1px solid var(--border-light);border-radius:8px;padding:12px 14px;font-family:'SF Mono',Monaco,Consolas,'Courier New',monospace;font-size:12px;line-height:1.6;white-space:pre-wrap;word-break:break-all;max-height:50vh;overflow-y:auto;color:var(--text)}
1395
+ .exec-result-info{display:grid;grid-template-columns:repeat(auto-fit,minmax(140px,1fr));gap:8px;margin-bottom:12px}
1396
+ .exec-result-info-item{padding:8px 12px;background:var(--bg2);border-radius:6px}
1397
+ .exec-result-info-item .label{font-size:11px;color:var(--text3);margin-bottom:2px}
1398
+ .exec-result-info-item .value{font-size:13px;font-weight:600;color:var(--text)}
1399
+ .exec-result-empty{text-align:center;padding:40px;color:var(--text3);font-size:13px}
1400
+
1401
+ @keyframes fadeIn{from{opacity:0}to{opacity:1}}
1402
+ @keyframes slideUp{from{opacity:0;transform:translateY(12px)}to{opacity:1;transform:translateY(0)}}
1403
+
1404
+ /* Dark theme overrides for exec events */
1405
+ [data-theme="dark"] .exec-event-icon.tool{background:rgba(59,130,246,.15);color:#93c5fd}
1406
+ [data-theme="dark"] .exec-event-icon.code-run{background:rgba(217,119,6,.15);color:#fcd34d}
1407
+ [data-theme="dark"] .exec-event-icon.code-ok{background:rgba(22,163,74,.15);color:#86efac}
1408
+ [data-theme="dark"] .exec-event-icon.code-fail{background:rgba(220,38,38,.15);color:#fca5a5}
1409
+ [data-theme="dark"] .exec-event-icon.code-timeout{background:rgba(219,39,119,.15);color:#f9a8d4}
1410
+ [data-theme="dark"] .exec-event-icon.skill{background:rgba(124,58,237,.15);color:#c4b5fd}
1411
+ [data-theme="dark"] .exec-events-panel{background:var(--bg2);border-color:var(--border)}
1412
+ [data-theme="dark"] .exec-result-modal{background:var(--bg2);border-color:var(--border)}
1413
+ [data-theme="dark"] .exec-result-modal-body pre{background:#0a0c10;color:#cdd6f4}
1414
+ [data-theme="dark"] .exec-result-info-item{background:var(--bg3)}
1415
+
1284
1416
  </style>
1285
1417
  </head>
1286
1418
  <body>
@@ -1378,6 +1510,9 @@ input,textarea,select{font:inherit}
1378
1510
  <button class="header-btn" id="clearChatBtn" onclick="clearCurrentChat()" title="清空当前对话">
1379
1511
  <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polyline points="3 6 5 6 21 6"/><path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"/></svg>
1380
1512
  </button>
1513
+ <button class="header-btn" id="themeToggle" title="切换主题">
1514
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="5"/><line x1="12" y1="1" x2="12" y2="3"/><line x1="12" y1="21" x2="12" y2="23"/><line x1="4.22" y1="4.22" x2="5.64" y2="5.64"/><line x1="18.36" y1="18.36" x2="19.78" y2="19.78"/><line x1="1" y1="12" x2="3" y2="12"/><line x1="21" y1="12" x2="23" y2="12"/><line x1="4.22" y1="19.78" x2="5.64" y2="18.36"/><line x1="18.36" y1="5.64" x2="19.78" y2="4.22"/></svg>
1515
+ </button>
1381
1516
  </div>
1382
1517
  </div>
1383
1518
 
@@ -1500,6 +1635,49 @@ input,textarea,select{font:inherit}
1500
1635
  <div id="groupModalContainer"></div>
1501
1636
 
1502
1637
  <script>
1638
+ // ── Theme Management ──
1639
+ function initTheme() {
1640
+ const saved = localStorage.getItem('myagent-theme') || 'claude';
1641
+ document.documentElement.setAttribute('data-theme', saved);
1642
+ updateThemeIcon(saved);
1643
+ }
1644
+ function toggleTheme() {
1645
+ const current = document.documentElement.getAttribute('data-theme') || 'claude';
1646
+ const next = current === 'claude' ? 'dark' : 'claude';
1647
+ document.documentElement.setAttribute('data-theme', next);
1648
+ localStorage.setItem('myagent-theme', next);
1649
+ updateThemeIcon(next);
1650
+ }
1651
+ function updateThemeIcon(theme) {
1652
+ const btn = document.getElementById('themeToggle');
1653
+ if (!btn) return;
1654
+ if (theme === 'dark') {
1655
+ btn.innerHTML = '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z"/></svg>';
1656
+ btn.title = '切换到 Claude 风格';
1657
+ } else {
1658
+ btn.innerHTML = '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="5"/><line x1="12" y1="1" x2="12" y2="3"/><line x1="12" y1="21" x2="12" y2="23"/><line x1="4.22" y1="4.22" x2="5.64" y2="5.64"/><line x1="18.36" y1="18.36" x2="19.78" y2="19.78"/><line x1="1" y1="12" x2="3" y2="12"/><line x1="21" y1="12" x2="23" y2="12"/><line x1="4.22" y1="19.78" x2="5.64" y2="18.36"/><line x1="18.36" y1="5.64" x2="19.78" y2="4.22"/></svg>';
1659
+ btn.title = '切换到夜间模式';
1660
+ }
1661
+ }
1662
+ // Initialize theme on load
1663
+ initTheme();
1664
+ // Bind theme toggle
1665
+ document.getElementById('themeToggle')?.addEventListener('click', toggleTheme);
1666
+
1667
+ // ── Sidebar Collapse ──
1668
+ function toggleSidebar() {
1669
+ const sidebar = document.querySelector('.sidebar');
1670
+ if (!sidebar) return;
1671
+ sidebar.classList.toggle('hidden');
1672
+ localStorage.setItem('myagent-sidebar-collapsed', sidebar.classList.contains('hidden'));
1673
+ }
1674
+ // Restore sidebar state
1675
+ (function() {
1676
+ if (localStorage.getItem('myagent-sidebar-collapsed') === 'true') {
1677
+ document.querySelector('.sidebar')?.classList.add('hidden');
1678
+ }
1679
+ })();
1680
+
1503
1681
  // ── State ──
1504
1682
  const state = {
1505
1683
  sessions: [], // [{id, name, messages: int, last: string}]
@@ -1616,6 +1794,8 @@ const StatePersistence = {
1616
1794
 
1617
1795
  // ── Init ──
1618
1796
  document.addEventListener('DOMContentLoaded', () => {
1797
+ // Initialize theme
1798
+ initTheme();
1619
1799
  // Restore persisted UI state
1620
1800
  StatePersistence.restoreUIState();
1621
1801
 
@@ -2717,7 +2897,8 @@ async function saveAgentModal(editPath, parentPath) {
2717
2897
 
2718
2898
  async function deleteAgent(agentPath) {
2719
2899
  if (agentPath === 'default') { toast('不能删除默认 Agent', 'error'); return; }
2720
- if (!confirm('确定删除 Agent "' + agentPath + '" 及其所有子 Agent')) return;
2900
+ var msg = '确定删除 Agent "' + agentPath + '" 吗?\n\n删除后将同时清理:\n 📁 工作目录及所有文件\n 💬 该 Agent 的所有会话历史\n 🧠 相关记忆数据\n 🔗 所有子 Agent\n\n⚠️ 此操作不可撤销!';
2901
+ if (!confirm(msg)) return;
2721
2902
  try {
2722
2903
  await api('/api/agents/' + encodeURIComponent(agentPath), { method: 'DELETE' });
2723
2904
  toast('Agent 已删除', 'success');
@@ -3014,11 +3195,14 @@ function renderMessages() {
3014
3195
  </div>` : '';
3015
3196
  const ttsIndicator = ttsManager && ttsManager.isPlaying && ttsManager.currentMsgIndex === i ?
3016
3197
  ' <span class="tts-playing-icon"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polygon points="11 5 6 9 2 9 2 15 6 15 11 19 11 5"/><path d="M15.54 8.46a5 5 0 0 1 0 7.07"/></svg></span>' : '';
3198
+ const execEventsHtml = (!isUser && msg.exec_events && msg.exec_events.length > 0)
3199
+ ? renderExecEvents(msg.exec_events, i) : '';
3017
3200
  html += `
3018
3201
  <div class="message-row ${msg.role}">
3019
3202
  <div class="message-avatar">${avatar}</div>
3020
- <div>
3203
+ <div style="flex:1;min-width:0">
3021
3204
  <div class="message-bubble">${content}${ttsIndicator}</div>
3205
+ ${execEventsHtml}
3022
3206
  ${msg.time ? `<div class="message-time">${formatTime(msg.time)}</div>` : ''}
3023
3207
  ${actionBtns}
3024
3208
  </div>
@@ -3032,6 +3216,242 @@ function renderMessages() {
3032
3216
  scrollToBottom();
3033
3217
  }
3034
3218
 
3219
+ // ── Execution Events Rendering ──
3220
+ function renderExecEvents(events, msgIndex) {
3221
+ // 过滤出有意义的事件(仅展示调用/结果对,跳过中间状态)
3222
+ const displayEvents = events.filter(e =>
3223
+ ['tool_call','tool_result','skill_call','skill_result','code_exec','code_result'].includes(e.type)
3224
+ );
3225
+ if (displayEvents.length === 0) return '';
3226
+
3227
+ // 统计执行步骤数
3228
+ const stepCount = displayEvents.filter(e => e.type === 'code_exec' || e.type === 'code_result' || e.type === 'tool_call' || e.type === 'skill_call').length;
3229
+ const successCount = displayEvents.filter(e => (e.type === 'code_result' || e.type === 'tool_result' || e.type === 'skill_result') && e.success !== false).length;
3230
+ const failCount = displayEvents.filter(e => (e.type === 'code_result' || e.type === 'tool_result' || e.type === 'skill_result') && e.success === false).length;
3231
+
3232
+ let itemsHtml = '';
3233
+ for (const evt of displayEvents) {
3234
+ const iconClass = getEventIconClass(evt);
3235
+ const iconEmoji = getEventIconEmoji(evt);
3236
+ const metaHtml = getEventMetaHtml(evt);
3237
+
3238
+ let bodyExtra = '';
3239
+ // 代码预览
3240
+ if (evt.code_preview && (evt.type === 'code_exec' || evt.type === 'code_result')) {
3241
+ bodyExtra += `<div class="exec-event-code" onclick="showExecResultModal(${msgIndex}, ${evt.id})" title="点击查看完整结果">${escapeHtml(evt.code_preview)}</div>`;
3242
+ }
3243
+ // 结果摘要
3244
+ if (evt.summary && (evt.type === 'tool_result' || evt.type === 'skill_result')) {
3245
+ bodyExtra += `<div class="exec-event-summary">${escapeHtml(evt.summary)}</div>`;
3246
+ }
3247
+ // 查看详情按钮(仅结果类型事件)
3248
+ if ((evt.type === 'code_result') && (evt.stdout || evt.stderr || evt.error)) {
3249
+ bodyExtra += `<button class="exec-event-result-btn" onclick="showExecResultModal(${msgIndex}, ${evt.id})"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z"/><circle cx="12" cy="12" r="3"/></svg> 查看详情</button>`;
3250
+ }
3251
+ if ((evt.type === 'tool_result' || evt.type === 'skill_result') && evt.result) {
3252
+ bodyExtra += `<button class="exec-event-result-btn" onclick="showToolResultModal(${msgIndex}, ${evt.id})"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z"/><circle cx="12" cy="12" r="3"/></svg> 查看详情</button>`;
3253
+ }
3254
+
3255
+ itemsHtml += `
3256
+ <div class="exec-event-item">
3257
+ <div class="exec-event-icon ${iconClass}">${iconEmoji}</div>
3258
+ <div class="exec-event-body">
3259
+ <div class="exec-event-title">${escapeHtml(evt.title || '')}</div>
3260
+ ${metaHtml ? `<div class="exec-event-meta">${metaHtml}</div>` : ''}
3261
+ ${bodyExtra}
3262
+ </div>
3263
+ </div>`;
3264
+ }
3265
+
3266
+ return `
3267
+ <div class="exec-events-panel">
3268
+ <div class="exec-events-header" onclick="toggleExecEventsPanel(this)">
3269
+ <span class="events-chevron">▶</span>
3270
+ <span>⚡ 执行过程</span>
3271
+ <span class="events-count">${stepCount}</span>
3272
+ ${successCount > 0 ? `<span style="color:var(--ok);font-weight:400;font-size:11px">${successCount} 成功</span>` : ''}
3273
+ ${failCount > 0 ? `<span style="color:var(--danger);font-weight:400;font-size:11px">${failCount} 失败</span>` : ''}
3274
+ </div>
3275
+ <div class="exec-events-body">
3276
+ ${itemsHtml}
3277
+ </div>
3278
+ </div>`;
3279
+ }
3280
+
3281
+ function getEventIconClass(evt) {
3282
+ if (evt.type === 'tool_call') return 'tool';
3283
+ if (evt.type === 'tool_result') return evt.success === false ? 'code-fail' : 'tool';
3284
+ if (evt.type === 'skill_call') return 'skill';
3285
+ if (evt.type === 'skill_result') return evt.success === false ? 'code-fail' : 'skill';
3286
+ if (evt.type === 'code_exec') return 'code-run';
3287
+ if (evt.type === 'code_result') {
3288
+ if (evt.timed_out) return 'code-timeout';
3289
+ return evt.success ? 'code-ok' : 'code-fail';
3290
+ }
3291
+ return 'tool';
3292
+ }
3293
+
3294
+ function getEventIconEmoji(evt) {
3295
+ if (evt.type === 'tool_call' || evt.type === 'tool_result') return '🔧';
3296
+ if (evt.type === 'skill_call' || evt.type === 'skill_result') return '🛠';
3297
+ if (evt.type === 'code_exec') return '▶';
3298
+ if (evt.type === 'code_result') {
3299
+ if (evt.timed_out) return '⏰';
3300
+ return evt.success ? '✅' : '❌';
3301
+ }
3302
+ return '📌';
3303
+ }
3304
+
3305
+ function getEventMetaHtml(evt) {
3306
+ const tags = [];
3307
+ if (evt.language) tags.push(`<span class="meta-tag">${escapeHtml(evt.language)}</span>`);
3308
+ if (evt.tool_name || evt.skill_name) tags.push(`<span class="meta-tag">${escapeHtml(evt.tool_name || evt.skill_name)}</span>`);
3309
+ if (evt.execution_time !== undefined) tags.push(`<span class="meta-tag">${evt.execution_time}s</span>`);
3310
+ if (evt.exit_code !== undefined) tags.push(`<span class="meta-tag">exit: ${evt.exit_code}</span>`);
3311
+ if (evt.timed_out) tags.push(`<span class="meta-tag" style="color:var(--danger)">超时</span>`);
3312
+ return tags.join('');
3313
+ }
3314
+
3315
+ function toggleExecEventsPanel(header) {
3316
+ header.classList.toggle('expanded');
3317
+ const body = header.nextElementSibling;
3318
+ body.classList.toggle('expanded');
3319
+ }
3320
+
3321
+ // ── Execution Result Modal ──
3322
+ function showExecResultModal(msgIndex, eventId) {
3323
+ const msg = state.messages[msgIndex];
3324
+ if (!msg || !msg.exec_events) return;
3325
+ const evt = msg.exec_events.find(e => e.id === eventId);
3326
+ if (!evt) return;
3327
+
3328
+ const lang = evt.language || 'unknown';
3329
+ const isSuccess = evt.success && !evt.timed_out;
3330
+ const statusIcon = evt.timed_out ? '⏰' : isSuccess ? '✅' : '❌';
3331
+ const statusText = evt.timed_out ? '执行超时' : isSuccess ? '执行成功' : '执行失败';
3332
+
3333
+ const hasStdout = evt.stdout && evt.stdout.trim();
3334
+ const hasStderr = evt.stderr && evt.stderr.trim();
3335
+ const hasError = evt.error && evt.error.trim();
3336
+ const hasCode = evt.code && evt.code.trim();
3337
+
3338
+ // Build info grid
3339
+ let infoHtml = `
3340
+ <div class="exec-result-info">
3341
+ <div class="exec-result-info-item"><div class="label">语言</div><div class="value">${escapeHtml(lang)}</div></div>
3342
+ <div class="exec-result-info-item"><div class="label">状态</div><div class="value">${statusIcon} ${statusText}</div></div>
3343
+ ${evt.execution_time !== undefined ? `<div class="exec-result-info-item"><div class="label">耗时</div><div class="value">${evt.execution_time}s</div></div>` : ''}
3344
+ ${evt.exit_code !== undefined ? `<div class="exec-result-info-item"><div class="label">退出码</div><div class="value">${evt.exit_code}</div></div>` : ''}
3345
+ </div>`;
3346
+
3347
+ // Build tabs
3348
+ const tabs = [];
3349
+ const sections = [];
3350
+ if (hasCode) {
3351
+ tabs.push({id:'code', label:'源代码'});
3352
+ sections.push({id:'code', content: escapeHtml(evt.code), active: !hasStdout});
3353
+ }
3354
+ if (hasStdout) {
3355
+ tabs.push({id:'stdout', label:'标准输出'});
3356
+ sections.push({id:'stdout', content: escapeHtml(evt.stdout), active: true});
3357
+ }
3358
+ if (hasStderr) {
3359
+ tabs.push({id:'stderr', label:'标准错误'});
3360
+ sections.push({id:'stderr', content: escapeHtml(evt.stderr)});
3361
+ }
3362
+ if (hasError) {
3363
+ tabs.push({id:'error', label:'错误信息'});
3364
+ sections.push({id:'error', content: escapeHtml(evt.error)});
3365
+ }
3366
+
3367
+ if (tabs.length === 0) {
3368
+ infoHtml += '<div class="exec-result-empty">无详细输出</div>';
3369
+ }
3370
+
3371
+ const tabsHtml = tabs.map((t, idx) =>
3372
+ `<button class="exec-result-tab ${t.active ? 'active' : ''}" onclick="switchExecResultTab(this, '${t.id}')">${t.label}</button>`
3373
+ ).join('');
3374
+
3375
+ const sectionsHtml = sections.map(s =>
3376
+ `<div class="exec-result-section ${s.active ? 'active' : ''}" id="exec-section-${s.id}"><pre>${s.content}</pre></div>`
3377
+ ).join('');
3378
+
3379
+ const modalHtml = `
3380
+ <div class="exec-result-modal-overlay" onclick="closeExecResultModal(event)">
3381
+ <div class="exec-result-modal" onclick="event.stopPropagation()">
3382
+ <div class="exec-result-modal-header">
3383
+ <h3><span class="status-icon">${statusIcon}</span> ${escapeHtml(evt.title || '执行结果')} <span style="font-weight:400;font-size:12px;color:var(--text3)">${escapeHtml(lang)}</span></h3>
3384
+ <button class="exec-result-modal-close" onclick="closeExecResultModal()">✕</button>
3385
+ </div>
3386
+ <div class="exec-result-modal-body">
3387
+ ${infoHtml}
3388
+ ${tabs.length > 0 ? `<div class="exec-result-modal-tabs">${tabsHtml}</div>` : ''}
3389
+ ${sectionsHtml}
3390
+ </div>
3391
+ </div>
3392
+ </div>`;
3393
+
3394
+ // Remove existing modal
3395
+ const existing = document.getElementById('execResultModalContainer');
3396
+ if (existing) existing.remove();
3397
+ // Add modal
3398
+ const container = document.createElement('div');
3399
+ container.id = 'execResultModalContainer';
3400
+ container.innerHTML = modalHtml;
3401
+ document.body.appendChild(container);
3402
+ }
3403
+
3404
+ function showToolResultModal(msgIndex, eventId) {
3405
+ const msg = state.messages[msgIndex];
3406
+ if (!msg || !msg.exec_events) return;
3407
+ const evt = msg.exec_events.find(e => e.id === eventId);
3408
+ if (!evt) return;
3409
+
3410
+ const name = evt.tool_name || evt.skill_name || 'unknown';
3411
+ const isSuccess = evt.success !== false;
3412
+ const statusIcon = isSuccess ? '✅' : '❌';
3413
+
3414
+ const resultText = evt.result
3415
+ ? escapeHtml(typeof evt.result === 'string' ? evt.result : JSON.stringify(evt.result, null, 2))
3416
+ : (evt.summary ? escapeHtml(evt.summary) : '无结果');
3417
+
3418
+ const modalHtml = `
3419
+ <div class="exec-result-modal-overlay" onclick="closeExecResultModal(event)">
3420
+ <div class="exec-result-modal" onclick="event.stopPropagation()">
3421
+ <div class="exec-result-modal-header">
3422
+ <h3><span class="status-icon">${statusIcon}</span> ${escapeHtml(name)}</h3>
3423
+ <button class="exec-result-modal-close" onclick="closeExecResultModal()">✕</button>
3424
+ </div>
3425
+ <div class="exec-result-modal-body">
3426
+ <pre>${resultText}</pre>
3427
+ </div>
3428
+ </div>
3429
+ </div>`;
3430
+
3431
+ const existing = document.getElementById('execResultModalContainer');
3432
+ if (existing) existing.remove();
3433
+ const container = document.createElement('div');
3434
+ container.id = 'execResultModalContainer';
3435
+ container.innerHTML = modalHtml;
3436
+ document.body.appendChild(container);
3437
+ }
3438
+
3439
+ function switchExecResultTab(btn, tabId) {
3440
+ // Deactivate all tabs
3441
+ btn.parentElement.querySelectorAll('.exec-result-tab').forEach(t => t.classList.remove('active'));
3442
+ btn.classList.add('active');
3443
+ // Show target section, hide others
3444
+ document.querySelectorAll('.exec-result-section').forEach(s => s.classList.remove('active'));
3445
+ const target = document.getElementById('exec-section-' + tabId);
3446
+ if (target) target.classList.add('active');
3447
+ }
3448
+
3449
+ function closeExecResultModal(e) {
3450
+ if (e && e.target !== e.currentTarget) return;
3451
+ const container = document.getElementById('execResultModalContainer');
3452
+ if (container) container.remove();
3453
+ }
3454
+
3035
3455
  function renderMarkdown(text) {
3036
3456
  if (!text) return '';
3037
3457
  // Code blocks with copy button
@@ -3154,6 +3574,7 @@ async function sendMessage() {
3154
3574
  role: 'assistant',
3155
3575
  content: data.response || '(无回复)',
3156
3576
  time: new Date().toISOString(),
3577
+ exec_events: data.exec_events || [],
3157
3578
  });
3158
3579
 
3159
3580
  // Update session in list
@@ -3173,8 +3594,8 @@ async function sendMessage() {
3173
3594
  state.agentSessions[state.activeAgent] = [...state.sessions];
3174
3595
  renderSessions();
3175
3596
 
3176
- // Auto-play TTS if enabled
3177
- if (ttsManager.enabled && data.response) {
3597
+ // Auto-play TTS if enabled (skip command execution results)
3598
+ if (ttsManager.enabled && data.response && !data.response.match(/^\s*[✅❌⏰]\s*\[执行结果\]/m)) {
3178
3599
  const idx = state.messages.length - 1;
3179
3600
  ttsManager.speak(idx);
3180
3601
  }
@@ -3371,11 +3792,6 @@ function autoResize(el) {
3371
3792
  el.style.height = Math.min(el.scrollHeight, 150) + 'px';
3372
3793
  }
3373
3794
 
3374
- // ── Sidebar Toggle ──
3375
- function toggleSidebar() {
3376
- document.getElementById('sidebar').classList.toggle('hidden');
3377
- }
3378
-
3379
3795
  // ── Toast ──
3380
3796
  function toast(message, type = 'info') {
3381
3797
  const container = document.getElementById('toastContainer');
@@ -4870,6 +5286,10 @@ const ttsManager = {
4870
5286
  const msg = state.messages[msgIndex];
4871
5287
  if (!msg || msg.role !== 'user' && !msg.content) return;
4872
5288
 
5289
+ // 跳过命令执行结果(以 [执行结果] 开头的消息)
5290
+ var rawText = msg.content.replace(/<[^>]+>/g, '');
5291
+ if (/^\s*[✅❌⏰]\s*\[执行结果\]/m.test(rawText)) return;
5292
+
4873
5293
  // 去除 HTML 标签(msg.content 是 HTML 格式,SVG 图标等会被朗读)
4874
5294
  let text = msg.content
4875
5295
  .replace(/<svg[^>]*>[\s\S]*?<\/svg>/gi, '') // 移除 SVG 图标
package/web/ui/index.html CHANGED
@@ -6,7 +6,34 @@
6
6
  <title>MyAgent 管理后台</title>
7
7
  <style>
8
8
  *{margin:0;padding:0;box-sizing:border-box}
9
- :root{--bg:#0f1117;--surface:#1a1d27;--surface2:#232733;--border:#2e3347;--text:#e1e4ed;--text2:#8b92a5;--primary:#6366f1;--primary-h:#818cf8;--success:#22c55e;--warn:#f59e0b;--danger:#ef4444;--info:#3b82f6;--radius:8px}
9
+ :root{
10
+ --bg:#0f1117;--bg2:#1a1d27;--bg3:#232733;--bg4:#2e3347;--bg5:#3a3f57;
11
+ --surface:#1a1d27;--surface2:#232733;
12
+ --text:#e1e4ed;--text2:#8b92a5;--text3:#5c6378;
13
+ --accent:#6366f1;--accent2:#818cf8;--accent-light:#2d2f5e;--accent-dark:#9da3f8;
14
+ --primary:#6366f1;--primary-h:#818cf8;
15
+ --ok:#22c55e;--success:#22c55e;--warn:#f59e0b;--danger:#ef4444;--info:#3b82f6;
16
+ --border:#2e3347;--border-light:#232733;
17
+ --radius:8px;
18
+ }
19
+ [data-theme="claude"]{
20
+ --bg:#f5f0e8;--bg2:#ebe5d9;--bg3:#e2dace;--bg4:#d6cfc3;--bg5:#c8bfb0;
21
+ --surface:#ebe5d9;--surface2:#e2dace;
22
+ --text:#2c2c2c;--text2:#5a5a5a;--text3:#8a8a8a;
23
+ --accent:#c96442;--accent2:#d97a5a;--accent-light:#fceee8;--accent-dark:#a0502e;
24
+ --primary:#c96442;--primary-h:#d97a5a;
25
+ --ok:#4a9c6d;--success:#4a9c6d;--warn:#c4862b;--danger:#c94444;--info:#4a7fc9;
26
+ --border:#d6cfc3;--border-light:#e2dace;
27
+ }
28
+ [data-theme="dark"]{
29
+ --bg:#0f1117;--bg2:#1a1d27;--bg3:#232733;--bg4:#2e3347;--bg5:#3a3f57;
30
+ --surface:#1a1d27;--surface2:#232733;
31
+ --text:#e1e4ed;--text2:#8b92a5;--text3:#5c6378;
32
+ --accent:#6366f1;--accent2:#818cf8;--accent-light:#2d2f5e;--accent-dark:#9da3f8;
33
+ --primary:#6366f1;--primary-h:#818cf8;
34
+ --ok:#22c55e;--success:#22c55e;--warn:#f59e0b;--danger:#ef4444;--info:#3b82f6;
35
+ --border:#2e3347;--border-light:#232733;
36
+ }
10
37
  body{font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,sans-serif;background:var(--bg);color:var(--text);height:100vh;display:flex}
11
38
  .sidebar{width:220px;background:var(--surface);border-right:1px solid var(--border);display:flex;flex-direction:column;flex-shrink:0}
12
39
  .sidebar .logo{padding:20px;font-size:18px;font-weight:700;border-bottom:1px solid var(--border);display:flex;align-items:center;gap:8px}
@@ -106,29 +133,48 @@ tr:hover{background:var(--surface2)}
106
133
  .status-msg.loading{color:var(--info)}
107
134
  .status-msg.success{color:var(--success);background:rgba(34,197,94,.1)}
108
135
  .status-msg.error{color:var(--danger);background:rgba(239,68,68,.1)}
136
+ /* ── Theme & Sidebar ── */
137
+ .sidebar{transition:width .3s ease;overflow:hidden;position:relative}
138
+ .sidebar.collapsed{width:52px}
139
+ .sidebar.collapsed .nav-item{justify-content:center;padding:10px 0}
140
+ .sidebar.collapsed .nav-item .icon-text{display:none}
141
+ .sidebar.collapsed .logo .logo-text{display:none}
142
+ .sidebar.collapsed .logo{justify-content:center;padding:20px 8px}
143
+ .sidebar.collapsed .sidebar-footer-text{display:none}
144
+ .sidebar-toggle{position:absolute;top:50%;right:-14px;transform:translateY(-50%);width:28px;height:28px;border-radius:50%;background:var(--surface);border:1px solid var(--border);display:grid;place-items:center;cursor:pointer;z-index:25;color:var(--text2);font-size:14px;transition:all .15s}
145
+ .sidebar-toggle:hover{background:var(--primary);color:#fff;border-color:var(--primary)}
146
+ .header-btn{width:36px;height:36px;border-radius:6px;display:grid;place-items:center;color:var(--text2);cursor:pointer;border:none;background:none;transition:all .15s}
147
+ .header-btn:hover{background:var(--surface2);color:var(--text)}
148
+ [data-theme="claude"] .log-viewer,[data-theme="claude"] .config-preview{background:#faf6ef;color:#5a5a5a}
149
+ [data-theme="claude"] .log-viewer .INFO,[data-theme="claude"] .config-preview .key{color:#4a7fc9}
150
+ [data-theme="claude"] .badge-green{background:#4a9c6d22}
151
+ [data-theme="claude"] .badge-red{background:#c9444422}
152
+ [data-theme="claude"] .badge-yellow{background:#c4862b22}
153
+ [data-theme="claude"] .badge-blue{background:#4a7fc922}
109
154
  </style>
110
155
  </head>
111
156
  <body>
112
- <div class="sidebar">
113
- <div class="logo"><span>🤖</span> MyAgent</div>
157
+ <div class="sidebar" id="adminSidebar">
158
+ <div class="sidebar-toggle" onclick="toggleSidebar()" id="sidebarToggle">◀</div>
159
+ <div class="logo"><span>🤖</span> <span class="logo-text">MyAgent</span></div>
114
160
  <div class="nav">
115
- <div class="nav-item active" onclick="showPage('dashboard')"><span class="icon">📊</span>仪表盘</div>
116
- <div class="nav-item" onclick="showPage('agents')"><span class="icon">🧠</span>Agent 管理</div>
117
- <div class="nav-item" onclick="showPage('platforms')"><span class="icon">💬</span>聊天平台</div>
118
- <div class="nav-item" onclick="showPage('organization')"><span class="icon">🏢</span>组织管理</div>
119
- <div class="nav-item" onclick="showPage('departments')"><span class="icon">🏛</span>部门管理</div>
120
- <div class="nav-item" onclick="showPage('sessions')"><span class="icon">📂</span>会话管理</div>
121
- <div class="nav-item" onclick="showPage('memory')"><span class="icon">🧠</span>记忆管理</div>
122
- <div class="nav-item" onclick="showPage('permissions')"><span class="icon">🔑</span>权限管理</div>
123
- <div class="nav-item" onclick="showPage('llm')"><span class="icon">⚙️</span>大模型设置</div>
124
- <div class="nav-item" onclick="showPage('system')"><span class="icon">📦</span>系统配置</div>
125
- <div class="nav-item" onclick="showPage('executor')"><span class="icon">🔧</span>执行引擎</div>
126
- <div class="nav-item" onclick="showPage('skills')"><span class="icon">🛠</span>技能管理</div>
127
- <div class="nav-item" onclick="showPage('files')"><span class="icon">📁</span>工作目录</div>
128
- <div class="nav-item" onclick="showPage('logs')"><span class="icon">📋</span>查看日志</div>
129
- <div class="nav-item" onclick="showPage('tasks')"><span class="icon">📌</span>任务记录</div>
161
+ <div class="nav-item active" onclick="showPage('dashboard')"><span class="icon">📊</span><span class="icon-text">仪表盘</span></div>
162
+ <div class="nav-item" onclick="showPage('agents')"><span class="icon">🧠</span><span class="icon-text">Agent 管理</span></div>
163
+ <div class="nav-item" onclick="showPage('platforms')"><span class="icon">💬</span><span class="icon-text">聊天平台</span></div>
164
+ <div class="nav-item" onclick="showPage('organization')"><span class="icon">🏢</span><span class="icon-text">组织管理</span></div>
165
+ <div class="nav-item" onclick="showPage('departments')"><span class="icon">🏛</span><span class="icon-text">部门管理</span></div>
166
+ <div class="nav-item" onclick="showPage('sessions')"><span class="icon">📂</span><span class="icon-text">会话管理</span></div>
167
+ <div class="nav-item" onclick="showPage('memory')"><span class="icon">🧠</span><span class="icon-text">记忆管理</span></div>
168
+ <div class="nav-item" onclick="showPage('permissions')"><span class="icon">🔑</span><span class="icon-text">权限管理</span></div>
169
+ <div class="nav-item" onclick="showPage('llm')"><span class="icon">⚙️</span><span class="icon-text">大模型设置</span></div>
170
+ <div class="nav-item" onclick="showPage('system')"><span class="icon">📦</span><span class="icon-text">系统配置</span></div>
171
+ <div class="nav-item" onclick="showPage('executor')"><span class="icon">🔧</span><span class="icon-text">执行引擎</span></div>
172
+ <div class="nav-item" onclick="showPage('skills')"><span class="icon">🛠</span><span class="icon-text">技能管理</span></div>
173
+ <div class="nav-item" onclick="showPage('files')"><span class="icon">📁</span><span class="icon-text">工作目录</span></div>
174
+ <div class="nav-item" onclick="showPage('logs')"><span class="icon">📋</span><span class="icon-text">查看日志</span></div>
175
+ <div class="nav-item" onclick="showPage('tasks')"><span class="icon">📌</span><span class="icon-text">任务记录</span></div>
130
176
  </div>
131
- <div style="padding:12px;border-top:1px solid var(--border);font-size:12px;color:var(--text2)">
177
+ <div style="padding:12px;border-top:1px solid var(--border);font-size:12px;color:var(--text2)" class="sidebar-footer-text">
132
178
  <span id="sidebarVersion">v...</span>
133
179
  <span id="updateBadge" style="display:none;margin-left:6px;color:var(--danger);font-weight:bold;cursor:pointer" onclick="doUpdate()" title="点击更新">[有新版本]</span>
134
180
  · <a href="#" onclick="api('/api/status').then(r=>showToast('Running: '+r.running,'success'))" style="color:var(--primary)">状态</a>
@@ -136,7 +182,7 @@ tr:hover{background:var(--surface2)}
136
182
  </div>
137
183
  </div>
138
184
  <div class="main">
139
- <div class="header"><h2 id="pageTitle">📊 仪表盘</h2><div><span class="status-dot"></span>运行中</div></div>
185
+ <div class="header"><div style="display:flex;align-items:center;gap:12px"><h2 id="pageTitle">📊 仪表盘</h2><button class="header-btn" id="themeToggle" title="切换主题"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" width="18" height="18"><circle cx="12" cy="12" r="5"/><line x1="12" y1="1" x2="12" y2="3"/><line x1="12" y1="21" x2="12" y2="23"/><line x1="4.22" y1="4.22" x2="5.64" y2="5.64"/><line x1="18.36" y1="18.36" x2="19.78" y2="19.78"/><line x1="1" y1="12" x2="3" y2="12"/><line x1="21" y1="12" x2="23" y2="12"/><line x1="4.22" y1="19.78" x2="5.64" y2="18.36"/><line x1="18.36" y1="5.64" x2="19.78" y2="4.22"/></svg></button></div><div style="display:flex;align-items:center;gap:8px"><span class="status-dot"></span>运行中</div></div>
140
186
  <div class="content" id="content"></div>
141
187
  </div>
142
188
  <div id="modalContainer"></div>
@@ -199,6 +245,17 @@ async function doUpdate(){
199
245
  }catch(e){showToast('更新失败','danger')}
200
246
  }
201
247
  // 页面加载时获取版本号,30秒后自动检查更新
248
+ // ── Theme Management ──
249
+ function initTheme(){const s=localStorage.getItem('myagent-theme')||'claude';document.documentElement.setAttribute('data-theme',s);updateThemeIcon(s)}
250
+ function toggleTheme(){const c=document.documentElement.getAttribute('data-theme')||'claude';const n=c==='claude'?'dark':'claude';document.documentElement.setAttribute('data-theme',n);localStorage.setItem('myagent-theme',n);updateThemeIcon(n)}
251
+ function updateThemeIcon(t){const b=document.getElementById('themeToggle');if(!b)return;if(t==='dark'){b.innerHTML='<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" width="18" height="18"><path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z"/></svg>';b.title='切换到 Claude 风格'}else{b.innerHTML='<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" width="18" height="18"><circle cx="12" cy="12" r="5"/><line x1="12" y1="1" x2="12" y2="3"/><line x1="12" y1="21" x2="12" y2="23"/><line x1="4.22" y1="4.22" x2="5.64" y2="5.64"/><line x1="18.36" y1="18.36" x2="19.78" y2="19.78"/><line x1="1" y1="12" x2="3" y2="12"/><line x1="21" y1="12" x2="23" y2="12"/><line x1="4.22" y1="19.78" x2="5.64" y2="18.36"/><line x1="18.36" y1="5.64" x2="19.78" y2="4.22"/></svg>';b.title='切换到夜间模式'}}
252
+ // ── Sidebar Collapse ──
253
+ function toggleSidebar(){const s=document.getElementById('adminSidebar');const t=document.getElementById('sidebarToggle');if(!s)return;s.classList.toggle('collapsed');const isCollapsed=s.classList.contains('collapsed');t.textContent=isCollapsed?'▶':'◀';localStorage.setItem('myagent-admin-sidebar-collapsed',isCollapsed)}
254
+ // Initialize
255
+ initTheme();
256
+ document.getElementById('themeToggle')?.addEventListener('click',toggleTheme);
257
+ if(localStorage.getItem('myagent-admin-sidebar-collapsed')==='true'){document.getElementById('adminSidebar')?.classList.add('collapsed');const t=document.getElementById('sidebarToggle');if(t)t.textContent='▶'}
258
+
202
259
  loadVersion();
203
260
  setTimeout(()=>checkUpdate(false),30000);
204
261
  function showConfirm(title,msg,onOk){
@@ -347,6 +404,7 @@ async function openEditAgentModal(path){
347
404
  <div class="tab" onclick="agentTabSwitch(this,'atKB')">知识库</div>
348
405
  <div class="tab" onclick="agentTabSwitch(this,'atSessions')">会话历史</div>
349
406
  <div class="tab" onclick="agentTabSwitch(this,'atSettings')">设置</div>
407
+ <div class="tab" onclick="agentTabSwitch(this,'atPerms')">🔑 权限</div>
350
408
  </div>
351
409
 
352
410
  <!-- 基本信息 -->
@@ -405,17 +463,23 @@ async function openEditAgentModal(path){
405
463
  <button class="btn btn-primary" onclick="doSaveAgentSettings('${escHtml(path)}')">保存设置</button>
406
464
  </div>
407
465
  </div>
466
+
467
+ <!-- 权限 -->
468
+ <div id="atPerms" class="hidden">
469
+ <div id="atPermsContent"><div class="empty">加载中...</div></div>
470
+ </div>
408
471
  </div></div>`;
409
472
  }
410
473
 
411
474
  function agentTabSwitch(el,tabId){
412
475
  el.parentElement.querySelectorAll('.tab').forEach(t=>t.classList.remove('active'));
413
476
  el.classList.add('active');
414
- const allTabs=['atBasic','atSoul','atKB','atSessions','atSettings'];
477
+ const allTabs=['atBasic','atSoul','atKB','atSessions','atSettings','atPerms'];
415
478
  allTabs.forEach(id=>$(id).classList.toggle('hidden',id!==tabId));
416
479
  // Lazy load
417
480
  if(tabId==='atKB')loadAgentKB();
418
481
  if(tabId==='atSessions')loadAgentSessions();
482
+ if(tabId==='atPerms')loadAgentPerms();
419
483
  }
420
484
 
421
485
  async function doSaveAgent(path){
@@ -506,8 +570,67 @@ async function viewSessionMsgs(sid){
506
570
  $('sessionsContent').innerHTML=html;
507
571
  }
508
572
 
573
+ async function loadAgentPerms(){
574
+ const path=window._currentEditAgentPath;if(!path)return;
575
+ const [agentPerms,globalPerms]=await Promise.all([
576
+ api('/api/permissions/'+encodeURIComponent(path)),
577
+ api('/api/permissions')
578
+ ]);
579
+ const perms=globalPerms.all_permissions||[];
580
+ const labels=globalPerms.labels||{};
581
+ const defaults=globalPerms.defaults||{};
582
+ const ap=agentPerms.permissions||{};
583
+ let html='<h4 style="font-size:14px;color:var(--text2);margin-bottom:12px">Agent 权限配置</h4>';
584
+ html+='<p style="color:var(--text2);font-size:12px;margin-bottom:16px">为该 Agent 单独配置权限。未设置的项目将使用全局默认值。</p>';
585
+ html+='<div class="grid" style="grid-template-columns:repeat(3,1fr);gap:12px">';
586
+ for(const p of perms){
587
+ const label=labels[p]||p;
588
+ const defVal=defaults[p]!==false;
589
+ const curVal=ap[p]!==undefined?ap[p]:defVal;
590
+ const checked=curVal?'checked':'';
591
+ const isDefault=ap[p]===undefined?'(默认)':'';
592
+ html+=`<div class="form-group" style="display:flex;align-items:center;gap:10px"><label style="flex:1;font-weight:500">${label} <span style="color:var(--text3);font-size:11px">${isDefault}</span></label><input type="checkbox" id="agent_perm_${p}" ${checked} style="width:18px;height:18px;cursor:pointer"></div>`;
593
+ }
594
+ html+='</div>';
595
+ html+='<div class="flex gap-8 mt-16"><button class="btn btn-primary" onclick="saveAgentPermsFromTab()">保存权限</button>';
596
+ html+='<button class="btn btn-ghost" onclick="resetAgentPermsFromTab()">重置为默认</button></div>';
597
+ $('atPermsContent').innerHTML=html;
598
+ }
599
+ async function saveAgentPermsFromTab(){
600
+ const path=window._currentEditAgentPath;if(!path)return;
601
+ const [agentPerms,globalPerms]=await Promise.all([
602
+ api('/api/permissions/'+encodeURIComponent(path)),
603
+ api('/api/permissions')
604
+ ]);
605
+ const perms=globalPerms.all_permissions||[];
606
+ const defaults=globalPerms.defaults||{};
607
+ const ap=agentPerms.permissions||{};
608
+ const data={};
609
+ for(const p of perms){
610
+ const el=document.getElementById('agent_perm_'+p);
611
+ if(!el)continue;
612
+ const defVal=defaults[p]!==false;
613
+ const curVal=el.checked;
614
+ // Only include if different from default
615
+ if(curVal!==defVal){
616
+ data[p]=curVal;
617
+ }
618
+ }
619
+ const r=await api('/api/permissions/'+encodeURIComponent(path),{method:'PUT',body:JSON.stringify(data)});
620
+ if(r.error){showToast(r.error,'danger');return}
621
+ showToast('Agent 权限已保存','success');
622
+ }
623
+ async function resetAgentPermsFromTab(){
624
+ const path=window._currentEditAgentPath;if(!path)return;
625
+ if(!confirm('确认将此 Agent 的权限重置为全局默认值?'))return;
626
+ const r=await api('/api/permissions/'+encodeURIComponent(path),{method:'DELETE'});
627
+ if(r.error){showToast(r.error,'danger');return}
628
+ showToast('已重置为默认权限','success');
629
+ loadAgentPerms();
630
+ }
631
+
509
632
  function confirmDeleteAgent(path,name){
510
- showConfirm('删除 Agent','确认删除 Agent "'+escHtml(name)+'" 吗?此操作不可恢复。',async()=>{
633
+ showConfirm('删除 Agent','确认删除 Agent "'+escHtml(name)+'" 吗?\n\n删除后将同时清理:\n 📁 工作目录及所有文件\n 💬 该 Agent 的所有会话历史\n 🧠 相关记忆数据\n 🔗 所有子 Agent\n\n⚠️ 此操作不可撤销!',async()=>{
511
634
  const r=await api(`/api/agents/${encodeURIComponent(path)}`,{method:'DELETE'});
512
635
  if(r.error){showToast(r.error,'danger');return}
513
636
  closeModal();showToast('已删除','success');renderAgents();