myagent-ai 1.27.3 → 1.27.5

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.
@@ -23,7 +23,7 @@ from pathlib import Path
23
23
  from typing import Any, Dict, List, Optional
24
24
 
25
25
  from core.logger import get_logger
26
- from core.utils import generate_id
26
+ from core.utils import next_agent_id, generate_id
27
27
 
28
28
  logger = get_logger("myagent.agent_storage")
29
29
 
@@ -243,7 +243,7 @@ class AgentStorage:
243
243
  conn = self._get_conn()
244
244
  now = _now_iso()
245
245
  if not cfg.id:
246
- cfg.id = generate_id()
246
+ cfg.id = next_agent_id()
247
247
  if not cfg.created_at:
248
248
  cfg.created_at = now
249
249
  if not cfg.updated_at:
@@ -433,8 +433,8 @@ class AgentStorage:
433
433
  rel_path = cfg_path.parent.relative_to(agents_dir).as_posix()
434
434
 
435
435
  # 补全缺失字段
436
- if "id" not in raw or not raw["id"] or not str(raw["id"]).isdigit():
437
- raw["id"] = generate_id()
436
+ if "id" not in raw or not raw["id"]:
437
+ raw["id"] = next_agent_id()
438
438
  if "created_at" not in raw or not raw["created_at"]:
439
439
  raw["created_at"] = now
440
440
  if "updated_at" not in raw or not raw["updated_at"]:
package/core/utils.py CHANGED
@@ -10,6 +10,7 @@ import time
10
10
  import re
11
11
  from datetime import datetime, timezone
12
12
  from zoneinfo import ZoneInfo
13
+ from pathlib import Path
13
14
  from typing import Any, Dict, Optional, TypeVar
14
15
 
15
16
  T = TypeVar("T")
@@ -42,12 +43,25 @@ def timestamp_ms() -> int:
42
43
  return int(time.time() * 1000)
43
44
 
44
45
 
46
+ _AGENT_ID_SEQ = Path.home() / ".myagent" / "data" / ".agent_id_seq"
47
+
48
+
49
+ def next_agent_id() -> int:
50
+ """自增数字 Agent ID (1, 2, 3, ...)"""
51
+ _AGENT_ID_SEQ.parent.mkdir(parents=True, exist_ok=True)
52
+ try:
53
+ cur = int(_AGENT_ID_SEQ.read_text().strip())
54
+ except (FileNotFoundError, ValueError):
55
+ cur = 0
56
+ cur += 1
57
+ _AGENT_ID_SEQ.write_text(str(cur))
58
+ return cur
59
+
60
+
45
61
  def generate_id(prefix: str = "") -> str:
46
- """生成唯一数字 ID (10位: 时间戳后5位 + 随机5位)"""
62
+ """生成唯一 ID"""
47
63
  import random
48
- ts = str(int(time.time() * 1000) % 100000).zfill(5)
49
- rand = str(random.randint(10000, 99999))
50
- uid = f"{ts}{rand}"
64
+ uid = uuid.uuid4().hex[:12]
51
65
  return f"{prefix}_{uid}" if prefix else uid
52
66
 
53
67
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "myagent-ai",
3
- "version": "1.27.3",
3
+ "version": "1.27.5",
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
@@ -14,6 +14,7 @@ from core.llm import Message
14
14
  from config import ModelEntry, ChatPlatformConfig
15
15
  import datetime
16
16
  import uuid
17
+ from core.utils import next_agent_id
17
18
  from web.tts_handler import synthesize, preprocess_for_tts, AVAILABLE_VOICES
18
19
 
19
20
  logger = get_logger("myagent.api")
@@ -1818,8 +1819,8 @@ window.addEventListener('beforeunload', function() {{
1818
1819
  if not message:
1819
1820
  return web.json_response({"error": "message is required"}, status=400)
1820
1821
 
1821
- agent_name = data.get("agent_name", "default") or "default"
1822
- # 支持 path 格式 (如 "coder/python-expert")
1822
+ agent_name = data.get("agent_name", "1") or "1"
1823
+ # 支持 path 格式 (如 "3")
1823
1824
  agent_path = data.get("agent_path", agent_name)
1824
1825
  # 获取数字 agent_id
1825
1826
  agent_id = self.core.memory.get_agent_id(agent_path)
@@ -1983,7 +1984,7 @@ window.addEventListener('beforeunload', function() {{
1983
1984
  if not message and not user_images and not user_files:
1984
1985
  return web.Response(text="data: " + json.dumps({"error": "message is required"}) + "\n\n", content_type="text/event-stream")
1985
1986
 
1986
- agent_path = data.get("agent_path", data.get("agent_name", "default")) or "default"
1987
+ agent_path = data.get("agent_path", data.get("agent_name", "1")) or "1"
1987
1988
  # [v1.23.35] 直接使用前端传来的 session_id,不再拼接 agent_path 前缀
1988
1989
  session_id = data.get("session_id", "") or "web_default"
1989
1990
  chat_mode = data.get("mode", "")
@@ -2374,7 +2375,7 @@ window.addEventListener('beforeunload', function() {{
2374
2375
  if not message:
2375
2376
  return web.json_response({"error": "message is required"}, status=400)
2376
2377
 
2377
- agent_path = data.get("agent_path", "default")
2378
+ agent_path = data.get("agent_path", "1") or "1"
2378
2379
  # [v1.23.35] 直接使用前端传来的 session_id,不再拼接 agent_path 前缀
2379
2380
  session_id = data.get("session_id", "web_default")
2380
2381
  choice = data.get("choice", "queue") # "continue" (插入后继续) 或 "queue" (排队)
@@ -2422,7 +2423,7 @@ window.addEventListener('beforeunload', function() {{
2422
2423
  if not raw_text:
2423
2424
  return web.json_response({"error": "text is required"}, status=400)
2424
2425
 
2425
- agent_path = data.get("agent_path", "default") or "default"
2426
+ agent_path = data.get("agent_path", "1") or "1"
2426
2427
  session_id = data.get("session_id", "")
2427
2428
  chat_mode = data.get("mode", "")
2428
2429
 
@@ -3070,7 +3071,7 @@ window.addEventListener('beforeunload', function() {{
3070
3071
  优先使用 session_id 查找(与聊天流存储键一致),
3071
3072
  回退到 agent_path 查找(手动创建的任务)。
3072
3073
  """
3073
- agent_path = request.query.get("agent", "default")
3074
+ agent_path = request.query.get("agent", "1")
3074
3075
  session_id = request.query.get("session", "")
3075
3076
  # 优先按 session_id 查找(聊天流生成的任务列表存储在此键下)
3076
3077
  if session_id:
@@ -3084,7 +3085,7 @@ window.addEventListener('beforeunload', function() {{
3084
3085
  async def handle_update_task_plan(self, request):
3085
3086
  """PUT /api/task-plan - Overwrite entire task list."""
3086
3087
  data = await request.json()
3087
- agent_path = data.get("agent", "default")
3088
+ agent_path = data.get("agent", "1")
3088
3089
  session_id = data.get("session", "")
3089
3090
  tasks = data.get("tasks", [])
3090
3091
  # 优先按 session_id 存储(与聊天流一致),回退到 agent_path
@@ -3095,7 +3096,7 @@ window.addEventListener('beforeunload', function() {{
3095
3096
  async def handle_add_task_item(self, request):
3096
3097
  """POST /api/task-plan - Add a new task item."""
3097
3098
  data = await request.json()
3098
- agent_path = data.get("agent", "default")
3099
+ agent_path = data.get("agent", "1")
3099
3100
  session_id = data.get("session", "")
3100
3101
  text = data.get("text", "").strip()
3101
3102
  if not text:
@@ -3110,7 +3111,7 @@ window.addEventListener('beforeunload', function() {{
3110
3111
  async def handle_delete_task_item(self, request):
3111
3112
  """DELETE /api/task-plan/{idx} - Delete task by index."""
3112
3113
  idx = int(request.match_info["idx"])
3113
- agent_path = request.query.get("agent", "default")
3114
+ agent_path = request.query.get("agent", "1")
3114
3115
  session_id = request.query.get("session", "")
3115
3116
  # 优先按 session_id 查找,回退到 agent_path
3116
3117
  store_key = session_id or agent_path
@@ -3156,32 +3157,29 @@ window.addEventListener('beforeunload', function() {{
3156
3157
  return web.json_response({"ok": True})
3157
3158
 
3158
3159
  # --- Agents (层级体系) ---
3159
- # 目录结构: agents/default/{config.json, soul.md, ...}
3160
- # agents/coder/{config.json, soul.md, ...}
3161
- # agents/coder/python-expert/{config.json, soul.md, ...}
3162
- # agent path = 相对于 agents/ 的路径, 如 "default", "coder", "coder/python-expert"
3160
+ # 目录结构: agents/1/{config.json, soul.md, ...}
3161
+ # agents/2/{config.json, soul.md, ...}
3162
+ # agent path = agent ID, 如 "1", "2", "3"
3163
3163
 
3164
3164
  def _agents_dir(self):
3165
3165
  d = self.core.config_mgr.data_dir / "agents"
3166
3166
  d.mkdir(parents=True, exist_ok=True)
3167
3167
  return d
3168
3168
 
3169
- def _agent_dir(self, path: str) -> Path:
3170
- """根据 agent path 返回目录 (path 如 'coder/python-expert')"""
3171
- return self._agents_dir() / path
3169
+ def _agent_dir(self, aid: str) -> Path:
3170
+ """根据 agent ID 返回目录 (aid 如 '1', '2')"""
3171
+ return self._agents_dir() / aid
3172
3172
 
3173
3173
  def _ensure_default_agent(self):
3174
- """确保默认 agent 存在(名为「全权Agent」,目录名保持 default)"""
3175
- ad = self._agent_dir("default")
3176
- # 先尝试迁移(会自动修复损坏的 config.json)
3177
- if (ad / "config.json").exists():
3178
- self._migrate_agent_config("default", "全权Agent")
3179
- # 迁移后再次检查(损坏文件可能已被删除)
3174
+ """确保默认 agent 存在(ID=1,名为「全权Agent」,目录名=1)"""
3175
+ aid = "1"
3176
+ ad = self._agent_dir(aid)
3180
3177
  if not (ad / "config.json").exists():
3181
3178
  ad.mkdir(parents=True, exist_ok=True)
3182
3179
  now = _now_iso()
3183
3180
  cfg = {
3184
- "id": generate_id(),
3181
+ "id": 1,
3182
+ "path": aid,
3185
3183
  "name": "全权Agent",
3186
3184
  "description": "全权Agent - 拥有完整权限的本地助手",
3187
3185
  "avatar_color": _agent_color("全权Agent"),
@@ -3202,49 +3200,26 @@ window.addEventListener('beforeunload', function() {{
3202
3200
  if not (ad / fn).exists():
3203
3201
  (ad / fn).write_text(default, encoding="utf-8")
3204
3202
  logger.info("已创建默认 Agent (全权Agent)")
3205
- else:
3206
- # 即使已存在也同步其基本配置(如系统提示词可能更新)
3207
- self._migrate_agent_config("default")
3208
3203
 
3209
3204
  self._ensure_config_helper()
3210
3205
 
3211
- def _migrate_agent_config(self, path: str, intended_name: str = None):
3212
- """为旧 agent config 添加缺失的 id, created_at, updated_at 字段;JSON 损坏则自动重建"""
3213
- cfg_file = self._agent_dir(path) / "config.json"
3206
+ def _migrate_agent_config(self, aid: str):
3207
+ """为旧 agent config 补全缺失字段"""
3208
+ cfg_file = self._agent_dir(aid) / "config.json"
3214
3209
  if not cfg_file.exists():
3215
3210
  return
3216
3211
  try:
3217
3212
  cfg = json.loads(cfg_file.read_text(encoding="utf-8"))
3218
3213
  except (json.JSONDecodeError, ValueError):
3219
- logger.warning(f"Agent config JSON 解析失败,自动删除重建: {path}")
3220
- try:
3221
- cfg_file.unlink()
3222
- except OSError:
3223
- pass
3224
- # 如果是 default 或配置助手,由 _ensure_default_agent / _ensure_config_helper 重建
3225
- if path in ("default", "配置助手", "p"):
3226
- return
3227
- # 其他 agent:创建一个最小 config
3228
- ad = self._agent_dir(path)
3229
- if ad.exists():
3230
- now = _now_iso()
3231
- cfg = {
3232
- "id": generate_id(),
3233
- "name": path,
3234
- "description": "",
3235
- "avatar_emoji": "🤖",
3236
- "execution_mode": "sandbox",
3237
- "enabled": True,
3238
- "created_at": now,
3239
- "updated_at": now,
3240
- }
3241
- cfg_file.write_text(json.dumps(cfg, indent=2, ensure_ascii=False), encoding="utf-8")
3242
- logger.info(f"已重建 Agent 配置: {path}")
3214
+ logger.warning(f"Agent config JSON 解析失败: {aid}")
3243
3215
  return
3244
3216
  changed = False
3245
3217
  now = _now_iso()
3246
- if "id" not in cfg or not cfg["id"].isdigit():
3247
- cfg["id"] = generate_id()
3218
+ if "id" not in cfg:
3219
+ cfg["id"] = int(aid) if aid.isdigit() else next_agent_id()
3220
+ changed = True
3221
+ if "path" not in cfg:
3222
+ cfg["path"] = aid
3248
3223
  changed = True
3249
3224
  if "created_at" not in cfg:
3250
3225
  cfg["created_at"] = now
@@ -3252,25 +3227,19 @@ window.addEventListener('beforeunload', function() {{
3252
3227
  if "updated_at" not in cfg:
3253
3228
  cfg["updated_at"] = now
3254
3229
  changed = True
3255
- # 修正旧 default agent 的名字
3256
- if intended_name and cfg.get("name") == "default":
3257
- cfg["name"] = intended_name
3258
- changed = True
3259
3230
  if changed:
3260
3231
  cfg_file.write_text(json.dumps(cfg, indent=2, ensure_ascii=False), encoding="utf-8")
3261
3232
 
3262
3233
  def _ensure_config_helper(self):
3263
- """确保系统级「配置助手」agent 存在(不可删除、不可改名、核心字段不可修改)"""
3264
- ad = self._agent_dir("配置助手")
3265
- # 先尝试迁移(会自动修复损坏的 config.json)
3266
- if (ad / "config.json").exists():
3267
- self._migrate_agent_config("配置助手")
3268
- # 迁移后再次检查(损坏文件可能已被删除)
3234
+ """确保系统级「配置助手」agent 存在(ID=2,目录名=2)"""
3235
+ aid = "2"
3236
+ ad = self._agent_dir(aid)
3269
3237
  if not (ad / "config.json").exists():
3270
3238
  ad.mkdir(parents=True, exist_ok=True)
3271
3239
  now = _now_iso()
3272
3240
  cfg = {
3273
- "id": generate_id(),
3241
+ "id": 2,
3242
+ "path": aid,
3274
3243
  "name": "配置助手",
3275
3244
  "description": "MyAgent 智能配置助手 - 内置系统Agent,帮助用户完成初始配置和日常配置管理",
3276
3245
  "avatar_color": "#4f46e5",
@@ -3303,21 +3272,12 @@ window.addEventListener('beforeunload', function() {{
3303
3272
  except Exception as e:
3304
3273
  logger.warning(f"同步配置助手提示词失败: {e}")
3305
3274
 
3306
- # 创建快捷方式 p -> 配置助手(符号链接)
3307
- p_dir = self._agent_dir("p")
3308
- if not p_dir.exists() and ad.exists():
3309
- try:
3310
- # 在 agents 目录下创建 p -> 配置助手 的符号链接
3311
- p_dir.symlink_to(ad)
3312
- logger.info("已创建配置助手快捷方式: p -> 配置助手")
3313
- except OSError as e:
3314
- logger.debug(f"创建快捷方式 p 失败(非关键): {e}")
3315
3275
  # 自动绑定知识库:将 配置使用说明.md 复制到配置助手的知识库目录
3316
3276
  self._bind_config_helper_kb()
3317
3277
 
3318
3278
  def _bind_config_helper_kb(self):
3319
3279
  """将 docs/配置使用说明.md 绑定到配置助手的知识库目录"""
3320
- kb_dir = self._get_agent_knowledge_dir("配置助手")
3280
+ kb_dir = self._get_agent_knowledge_dir("2")
3321
3281
  kb_dir.mkdir(parents=True, exist_ok=True)
3322
3282
  target = kb_dir / "配置使用说明.md"
3323
3283
 
@@ -3350,8 +3310,8 @@ window.addEventListener('beforeunload', function() {{
3350
3310
  target.write_text(content, encoding="utf-8")
3351
3311
  logger.info(f"配置助手知识库已绑定: {source} -> {target}")
3352
3312
  # 刷新 RAG 索引
3353
- if hasattr(self, '_agent_rags') and "配置助手" in self._agent_rags:
3354
- self._agent_rags["配置助手"].build_index()
3313
+ if hasattr(self, '_agent_rags') and "2" in self._agent_rags:
3314
+ self._agent_rags["2"].build_index()
3355
3315
  else:
3356
3316
  logger.warning(f"未找到 配置使用说明.md 源文件,已搜索: {[str(c) for c in source_candidates]}")
3357
3317
 
@@ -3415,56 +3375,43 @@ window.addEventListener('beforeunload', function() {{
3415
3375
 
3416
3376
  return user_message, agent_system_prompt
3417
3377
 
3418
- def _scan_agents_flat(self, base_dir: Path = None, prefix: str = "") -> list[dict]:
3419
- """递归扫描所有 agent,返回扁平列表(含迁移补全字段)"""
3420
- if base_dir is None:
3421
- base_dir = self._agents_dir()
3378
+ def _scan_agents_flat(self) -> list[dict]:
3379
+ """扫描所有 agent 目录,返回扁平列表(目录名即为 agent ID)"""
3380
+ base_dir = self._agents_dir()
3422
3381
  agents = []
3423
3382
  if not base_dir.exists():
3424
3383
  return agents
3425
3384
  for d in sorted(base_dir.iterdir()):
3426
- if not d.is_dir():
3385
+ if not d.is_dir() or d.is_symlink():
3427
3386
  continue
3428
3387
  cfg_file = d / "config.json"
3429
3388
  if not cfg_file.exists():
3430
3389
  continue
3431
- agent_path = f"{prefix}{d.name}" if not prefix else f"{prefix}/{d.name}"
3432
- # 跳过符号链接目录(如 p -> 配置助手),避免重复处理
3433
- if d.is_symlink():
3390
+ aid = d.name
3391
+ self._migrate_agent_config(aid)
3392
+ if not cfg_file.exists():
3434
3393
  continue
3435
- # 迁移: 为缺少 id/created_at/updated_at 的旧 agent 补全字段
3436
- else:
3437
- self._migrate_agent_config(agent_path)
3438
- # 迁移可能删除了损坏的文件
3439
- if not cfg_file.exists():
3440
- continue
3441
- try:
3442
- cfg = json.loads(cfg_file.read_text(encoding="utf-8"))
3443
- except (json.JSONDecodeError, ValueError):
3444
- logger.warning(f"Agent config JSON 解析失败,跳过: {agent_path}")
3445
- continue
3446
- agent = {"path": agent_path, "name": d.name, **cfg}
3447
- agent["avatar_color"] = cfg.get("avatar_color") or _agent_color(d.name)
3448
- agent["depth"] = agent_path.count("/")
3449
- # [v1.20.13] 自动检测 avatar.png 文件,补全 avatar_image 字段
3394
+ try:
3395
+ cfg = json.loads(cfg_file.read_text(encoding="utf-8"))
3396
+ except (json.JSONDecodeError, ValueError):
3397
+ continue
3398
+ agent = {"path": aid, "id": int(aid) if aid.isdigit() else cfg.get("id", aid), "name": cfg.get("name", aid), **cfg}
3399
+ agent["avatar_color"] = cfg.get("avatar_color") or _agent_color(cfg.get("name", aid))
3400
+ # [v1.20.13] 自动检测 avatar.png
3450
3401
  if not agent.get("avatar_image") and (d / "avatar.png").exists():
3451
- agent["avatar_image"] = f"/api/agents/{agent_path}/avatar.png"
3402
+ agent["avatar_image"] = f"/api/agents/{aid}/avatar.png"
3452
3403
  agents.append(agent)
3453
- # 递归子目录
3454
- agents.extend(self._scan_agents_flat(d, agent_path))
3455
3404
  return agents
3456
3405
 
3457
3406
  def _build_agent_tree(self, agents_flat: list[dict]) -> list[dict]:
3458
- """将扁平 agent 列表构建为树结构"""
3459
- # path 索引
3460
- by_path = {a["path"]: {**a, "children": []} for a in agents_flat}
3407
+ """将扁平 agent 列表构建为树结构(通过 parent 字段关联)"""
3408
+ by_id = {a["path"]: {**a, "children": []} for a in agents_flat}
3461
3409
  roots = []
3462
3410
  for a in agents_flat:
3463
- path = a["path"]
3464
- parent_path = "/".join(path.split("/")[:-1]) if "/" in path else None
3465
- node = by_path[path]
3466
- if parent_path and parent_path in by_path:
3467
- by_path[parent_path]["children"].append(node)
3411
+ parent = a.get("parent", "")
3412
+ node = by_id[a["path"]]
3413
+ if parent and parent in by_id:
3414
+ by_id[parent]["children"].append(node)
3468
3415
  else:
3469
3416
  roots.append(node)
3470
3417
  return roots
@@ -3524,13 +3471,15 @@ window.addEventListener('beforeunload', function() {{
3524
3471
  if "/" in name or "\\" in name or name == "default":
3525
3472
  return web.json_response({"error": "invalid name (no slashes, cannot be 'default')"}, status=400)
3526
3473
 
3527
- ad = self._agent_dir(name)
3474
+ aid = str(next_agent_id())
3475
+ ad = self._agent_dir(aid)
3528
3476
  if (ad / "config.json").exists():
3529
3477
  return web.json_response({"error": f"Agent '{name}' already exists"}, status=409)
3530
3478
 
3531
3479
  now = _now_iso()
3532
3480
  cfg = {
3533
- "id": generate_id(),
3481
+ "id": int(aid),
3482
+ "path": aid,
3534
3483
  "name": name,
3535
3484
  "description": data.get("description", ""),
3536
3485
  "avatar_color": data.get("avatar_color") or _agent_color(name),
@@ -3575,13 +3524,13 @@ window.addEventListener('beforeunload', function() {{
3575
3524
  if data.get("department"):
3576
3525
  try:
3577
3526
  dm = self._get_dept_manager()
3578
- dm.assign_agent(data["department"], agents=[name], action="add")
3527
+ dm.assign_agent(data["department"], agents=[aid], action="add")
3579
3528
  logger.info(f"Agent {name} 已自动分配到部门: {data['department']}")
3580
3529
  except Exception as e:
3581
3530
  logger.warning(f"自动分配 Agent {name} 到部门失败: {e}")
3582
3531
 
3583
- logger.info(f"创建 Agent: {name} (sandbox模式)")
3584
- return web.json_response({"ok": True, "path": name, "name": name, "avatar_color": cfg["avatar_color"]})
3532
+ logger.info(f"创建 Agent: {name} (ID={aid})")
3533
+ return web.json_response({"ok": True, "path": aid, "id": int(aid), "name": name, "avatar_color": cfg["avatar_color"]})
3585
3534
 
3586
3535
  async def handle_create_child(self, request):
3587
3536
  """POST /api/agents/{parent}/children - 创建子 agent"""
@@ -3600,11 +3549,14 @@ window.addEventListener('beforeunload', function() {{
3600
3549
  return web.json_response({"error": "invalid name (no slashes)"}, status=400)
3601
3550
 
3602
3551
  child_path = f"{parent_path}/{name}"
3603
- ad = self._agent_dir(child_path)
3552
+ child_aid = str(next_agent_id())
3553
+ ad = self._agent_dir(child_aid)
3604
3554
  if (ad / "config.json").exists():
3605
- return web.json_response({"error": f"Agent '{child_path}' already exists"}, status=409)
3555
+ return web.json_response({"error": f"Agent '{name}' already exists"}, status=409)
3606
3556
 
3607
3557
  cfg = {
3558
+ "id": int(child_aid),
3559
+ "path": child_aid,
3608
3560
  "name": name,
3609
3561
  "parent": parent_path,
3610
3562
  "description": data.get("description", ""),
@@ -3640,30 +3592,18 @@ window.addEventListener('beforeunload', function() {{
3640
3592
  if not (ad / fn).exists():
3641
3593
  (ad / fn).write_text(default, encoding="utf-8")
3642
3594
 
3643
- logger.info(f"创建子 Agent: {child_path} (sandbox模式)")
3644
- return web.json_response({"ok": True, "path": child_path, "name": name, "parent": parent_path, "avatar_color": cfg["avatar_color"]})
3595
+ logger.info(f"创建子 Agent: {name} (ID={child_aid}, parent={parent_path})")
3596
+ return web.json_response({"ok": True, "path": child_aid, "id": int(child_aid), "name": name, "parent": parent_path, "avatar_color": cfg["avatar_color"]})
3645
3597
 
3646
3598
  async def handle_list_children(self, request):
3647
- """GET /api/agents/{parent}/children - 列出子 agent"""
3599
+ """GET /api/agents/{parent}/children - 列出子 agent(通过 parent 字段)"""
3648
3600
  parent_path = request.match_info["name"]
3649
- parent_dir = self._agent_dir(parent_path)
3650
- if not (parent_dir / "config.json").exists():
3601
+ parent_cfg = self._read_agent_config(parent_path)
3602
+ if not parent_cfg:
3651
3603
  return web.json_response({"error": f"Agent '{parent_path}' not found"}, status=404)
3652
3604
 
3653
- children = []
3654
- if parent_dir.exists():
3655
- for d in sorted(parent_dir.iterdir()):
3656
- if d.is_dir() and (d / "config.json").exists():
3657
- try:
3658
- cfg = json.loads((d / "config.json").read_text(encoding="utf-8"))
3659
- except (json.JSONDecodeError, ValueError):
3660
- logger.warning(f"Agent config JSON 解析失败,跳过: {parent_path}/{d.name}")
3661
- continue
3662
- child_path = f"{parent_path}/{d.name}"
3663
- child = {"path": child_path, "name": d.name, "parent": parent_path, **cfg}
3664
- child["avatar_color"] = cfg.get("avatar_color") or _agent_color(d.name)
3665
- child["depth"] = child_path.count("/")
3666
- children.append(child)
3605
+ all_agents = self._scan_agents_flat()
3606
+ children = [a for a in all_agents if a.get("parent") == parent_path]
3667
3607
  return web.json_response(children)
3668
3608
 
3669
3609
  async def handle_get_agent(self, request):
@@ -3718,8 +3658,7 @@ window.addEventListener('beforeunload', function() {{
3718
3658
  return web.json_response({"error": "config.json 解析失败"}, status=500)
3719
3659
 
3720
3660
  # 系统 Agent(内置 Agent)保护:核心字段不可修改
3721
- # "p" "配置助手" 的快捷方式,同样受保护
3722
- is_system = cfg.get("system") or path in ("p",)
3661
+ is_system = cfg.get("system") or path in ("1", "2")
3723
3662
  if is_system:
3724
3663
  blocked = [k for k in data if k in self._SYSTEM_AGENT_PROTECTED_FIELDS]
3725
3664
  if blocked:
@@ -3771,39 +3710,16 @@ window.addEventListener('beforeunload', function() {{
3771
3710
  if "soul" in data and not is_system: (ad / "soul.md").write_text(data["soul"], encoding="utf-8")
3772
3711
  if "identity" in data and not is_system: (ad / "identity.md").write_text(data["identity"], encoding="utf-8")
3773
3712
  if "user" in data: (ad / "user.md").write_text(data["user"], encoding="utf-8")
3774
- # 如果 name 改变,需要重命名目录
3775
- new_name = data.get("name")
3776
- renamed_to = None
3777
- if new_name and new_name != path and not is_system:
3778
- agents_dir = self._agents_dir()
3779
- new_ad = agents_dir / new_name
3780
- if not new_ad.exists():
3781
- ad.rename(new_ad)
3782
- logger.info(f"Agent 目录已重命名: {path} -> {new_name}")
3783
- renamed_to = new_name
3784
- # [v1.20.2] 重命名后更新 avatar_image URL 中的路径
3785
- old_avatar = cfg.get("avatar_image", "")
3786
- if old_avatar and f"/api/agents/{path}/" in old_avatar:
3787
- cfg["avatar_image"] = old_avatar.replace(f"/api/agents/{path}/", f"/api/agents/{new_name}/")
3788
- (new_ad / "config.json").write_text(
3789
- json.dumps(cfg, indent=2, ensure_ascii=False), encoding="utf-8"
3790
- )
3791
- else:
3792
- logger.warning(f"目标目录已存在,无法重命名: {new_name}")
3713
+ # 名字改变不再需要重命名目录(目录名是 agent ID,与名字无关)
3793
3714
  logger.info(f"更新 Agent: {path}")
3794
- result = {"ok": True, "hot_reload": True}
3795
- if renamed_to:
3796
- result["renamed_to"] = renamed_to
3797
- return web.json_response(result)
3715
+ return web.json_response({"ok": True, "hot_reload": True})
3798
3716
 
3799
3717
  async def handle_delete_agent(self, request):
3800
3718
  """DELETE /api/agents/{path} - 删除 agent 及其所有子 agent"""
3801
3719
  path = request.match_info["name"]
3802
- if path == "default":
3803
- return web.json_response({"error": "cannot delete default agent"}, status=403)
3804
- if path in ("配置助手", "p"):
3720
+ if path in ("1", "2"):
3805
3721
  return web.json_response({
3806
- "error": f"系统 Agent '{path}' 是内置 Agent,不可删除也不可重命名",
3722
+ "error": "系统 Agent 不可删除",
3807
3723
  "agent_path": path,
3808
3724
  }, status=403)
3809
3725
  ad = self._agent_dir(path)
@@ -4317,7 +4233,7 @@ window.addEventListener('beforeunload', function() {{
4317
4233
  if agent:
4318
4234
  # [v1.27.2] 先将 agent 路径转为数字 ID,用数字 ID 精确过滤
4319
4235
  target_aid = self.core.memory.get_agent_id(agent) if agent else 0
4320
- if agent == "default":
4236
+ if agent == "1":
4321
4237
  # default agent: agent_id=0(未分配)或 agent_id=1(default 的数字 ID)或 旧格式
4322
4238
  rows = conn.execute(
4323
4239
  f"""SELECT DISTINCT session_id, COUNT(*) as cnt, MAX(created_at) as last,
@@ -4386,7 +4302,7 @@ window.addEventListener('beforeunload', function() {{
4386
4302
  return web.json_response({"agent": name, "sessions": []})
4387
4303
  # [v1.27.2] 使用数字 agent_id 精确过滤,同时兼容旧数据
4388
4304
  target_aid = self.core.memory.get_agent_id(name)
4389
- if name == "default":
4305
+ if name == "1":
4390
4306
  # default agent: agent_id=0 或 agent_id=1 或 旧格式
4391
4307
  rows = self.core.memory._get_conn().execute(
4392
4308
  """SELECT DISTINCT session_id, COUNT(*) as cnt, MAX(created_at) as last FROM memories
@@ -5471,7 +5387,7 @@ window.addEventListener('beforeunload', function() {{
5471
5387
  else:
5472
5388
  # 兜底到全局默认 LLM 配置
5473
5389
  chain.append({
5474
- "id": "default",
5390
+ "id": "1",
5475
5391
  "name": llm_defaults.model,
5476
5392
  "provider": llm_defaults.provider,
5477
5393
  "api_type": llm_defaults.api_type,
@@ -5663,7 +5579,7 @@ window.addEventListener('beforeunload', function() {{
5663
5579
  self.core.main_agent._agent_override_path = agent_path
5664
5580
  # [v1.23.52] 为非默认 Agent 设置独立工作目录(executor 层面)
5665
5581
  _original_exec_work_dir = None
5666
- if agent_path and agent_path != "default" and self.core.main_agent and self.core.main_agent.executor:
5582
+ if agent_path and agent_path != "1" and self.core.main_agent and self.core.main_agent.executor:
5667
5583
  from config import ConfigManager
5668
5584
  _cm = ConfigManager()
5669
5585
  _agent_wd = _cm.data_dir / "agents" / agent_path / "workspace"
@@ -5992,7 +5908,7 @@ window.addEventListener('beforeunload', function() {{
5992
5908
  _original_exec_mode = agent.executor.execution_mode
5993
5909
  agent.executor.set_execution_mode(_exec_mode)
5994
5910
  # [v1.23.52] 为非默认 Agent 设置独立工作目录
5995
- if agent_path and agent_path != "default" and agent.executor:
5911
+ if agent_path and agent_path != "1" and agent.executor:
5996
5912
  from config import ConfigManager
5997
5913
  cm = ConfigManager()
5998
5914
  agent_work_dir = cm.data_dir / "agents" / agent_path / "workspace"
@@ -7141,14 +7057,14 @@ window.addEventListener('beforeunload', function() {{
7141
7057
  needs_setup = not config.llm.api_key
7142
7058
  # 确保配置助手存在
7143
7059
  self._ensure_config_helper()
7144
- helper_exists = (self._agent_dir("配置助手") / "config.json").exists()
7060
+ helper_exists = (self._agent_dir("2") / "config.json").exists()
7145
7061
  return web.json_response({
7146
7062
  "needs_setup": needs_setup,
7147
7063
  "helper_exists": helper_exists,
7148
7064
  "has_api_key": bool(config.llm.api_key),
7149
7065
  "provider": config.llm.provider,
7150
7066
  "model": config.llm.model,
7151
- "default_agent": "配置助手",
7067
+ "default_agent": "1",
7152
7068
  })
7153
7069
 
7154
7070
  async def handle_setup_complete(self, request):
@@ -7297,7 +7213,7 @@ window.addEventListener('beforeunload', function() {{
7297
7213
  def _check_org_admin(self, agent_path: str = "") -> bool:
7298
7214
  """检查 agent 是否是组织知识库管理员"""
7299
7215
  org_cfg = self.core.config.organization
7300
- admin = org_cfg.knowledge_admin or "default"
7216
+ admin = org_cfg.knowledge_admin or "1"
7301
7217
  return agent_path == admin
7302
7218
 
7303
7219
  async def handle_get_organization(self, request):
@@ -7357,7 +7273,7 @@ window.addEventListener('beforeunload', function() {{
7357
7273
  async def handle_upload_org_knowledge(self, request):
7358
7274
  """POST /api/organization/knowledge/upload - 上传文件到组织知识库(支持文件和文件夹上传)"""
7359
7275
  # 权限检查
7360
- agent = request.query.get("agent", "default")
7276
+ agent = request.query.get("agent", "1")
7361
7277
  if not self._check_org_admin(agent):
7362
7278
  return web.json_response(
7363
7279
  {"ok": False, "error": "权限不足:只有知识库管理员才能上传"},
@@ -7385,7 +7301,7 @@ window.addEventListener('beforeunload', function() {{
7385
7301
  async def handle_delete_org_knowledge(self, request):
7386
7302
  """DELETE /api/organization/knowledge?path=xxx - 删除组织知识库文件"""
7387
7303
  # 权限检查
7388
- agent = request.query.get("agent", "default")
7304
+ agent = request.query.get("agent", "1")
7389
7305
  if not self._check_org_admin(agent):
7390
7306
  return web.json_response(
7391
7307
  {"ok": False, "error": "权限不足:只有知识库管理员才能删除"},
@@ -7727,7 +7643,7 @@ window.addEventListener('beforeunload', function() {{
7727
7643
  name = data.get("name", "").strip()
7728
7644
  if not name:
7729
7645
  return web.json_response({"error": "群名不能为空"}, status=400)
7730
- owner = data.get("owner", "default")
7646
+ owner = data.get("owner", "1")
7731
7647
  description = data.get("description", "")
7732
7648
  avatar_emoji = data.get("avatar_emoji", "👥")
7733
7649
  avatar_color = data.get("avatar_color", "")
@@ -9041,7 +8957,7 @@ window.addEventListener('beforeunload', function() {{
9041
8957
  return web.json_response({"error": "invalid JSON"}, status=400)
9042
8958
 
9043
8959
  text = data.get("text", "")
9044
- agent_path = data.get("agent_path", "default")
8960
+ agent_path = data.get("agent_path", "1") or "1"
9045
8961
  filename = data.get("filename", "").strip()
9046
8962
 
9047
8963
  if not text: