myagent-ai 1.32.6 → 1.32.8

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.
@@ -221,7 +221,7 @@ class MainAgent(BaseAgent):
221
221
  try:
222
222
  self.agent_id = self.memory.get_agent_id(self.name)
223
223
  except Exception:
224
- self.agent_id = 0
224
+ self.agent_id = 1
225
225
 
226
226
  def init_context_builder(self, memory_manager=None, skill_registry=None, knowledge_base_dir=None, context_window=None):
227
227
  """初始化 Context Builder(在系统启动后调用,注入依赖)"""
package/memory/manager.py CHANGED
@@ -35,8 +35,8 @@ logger = get_logger("myagent.memory")
35
35
  class MemoryEntry:
36
36
  """记忆条目"""
37
37
  id: str = field(default_factory=lambda: generate_id("mem"))
38
- session_id: str = "default"
39
- agent_id: int = 0 # 所属agent ID(数字ID,从1开始,0表示未分配)
38
+ session_id: str = ""
39
+ agent_id: int = 1 # 所属agent ID(数字ID,从1开始,1=全权Agent)
40
40
  category: str = "session" # session | global
41
41
  key: str = "" # 检索键/标签
42
42
  content: str = "" # 记忆内容
@@ -66,7 +66,7 @@ class MemoryEntry:
66
66
  return cls(
67
67
  id=row["id"],
68
68
  session_id=row["session_id"],
69
- agent_id=int(dict(row).get("agent_id", 0) or 0),
69
+ agent_id=int(dict(row).get("agent_id", 1) or 1),
70
70
  category=row["category"],
71
71
  key=row["key"],
72
72
  content=row["content"],
@@ -147,14 +147,14 @@ class MemoryManager:
147
147
  from datetime import datetime as _dt
148
148
  from core.utils import get_config_tz
149
149
  now = _dt.now(get_config_tz()).strftime("%Y-%m-%d %H:%M:%S")
150
- conn.execute("INSERT OR IGNORE INTO agents (id, name, created_at) VALUES (1, 'default', ?)", (now,))
150
+ conn.execute("INSERT OR IGNORE INTO agents (id, name, created_at) VALUES (1, '1', ?)", (now,))
151
151
 
152
152
  # 创建 memories 表
153
153
  conn.executescript("""
154
154
  CREATE TABLE IF NOT EXISTS memories (
155
155
  id TEXT PRIMARY KEY,
156
156
  session_id TEXT NOT NULL,
157
- agent_id INTEGER NOT NULL DEFAULT 0,
157
+ agent_id INTEGER NOT NULL DEFAULT 1,
158
158
  category TEXT NOT NULL,
159
159
  key TEXT DEFAULT '',
160
160
  content TEXT DEFAULT '',
@@ -207,7 +207,8 @@ class MemoryManager:
207
207
  return cursor.lastrowid
208
208
  except Exception as e:
209
209
  logger.error(f"获取 agent ID 失败: {e}")
210
- return 0
210
+ # 返回 1 作为兜底(全权Agent),避免 agent_id=0 导致数据归属不明
211
+ return 1
211
212
 
212
213
  def register_agent(self, agent_name: str) -> int:
213
214
  """注册新 agent,返回分配的 ID"""
@@ -333,7 +334,7 @@ class MemoryManager:
333
334
  # 会话记忆 (session) — 对话上下文 + 任务进度
334
335
  # ==========================================================================
335
336
 
336
- def add_session(self, session_id, agent_id=0, role="", content="", key="", importance=0.5, metadata=None) -> str:
337
+ def add_session(self, session_id, agent_id=1, role="", content="", key="", importance=0.5, metadata=None) -> str:
337
338
  """添加会话记忆。内容不包含时间前缀,时间仅存于 created_at 和 metadata。"""
338
339
  from datetime import datetime as _dt
339
340
  from core.utils import get_config_tz
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "myagent-ai",
3
- "version": "1.32.6",
3
+ "version": "1.32.8",
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
@@ -1896,8 +1896,7 @@ window.addEventListener('beforeunload', function() {{
1896
1896
  agent_path = data.get("agent_path", agent_name)
1897
1897
  # 获取数字 agent_id
1898
1898
  agent_id = self.core.memory.get_agent_id(agent_path)
1899
- # 统一使用 sid 作为会话标识(兼容旧字段 session_id
1900
- session_id = data.get("sid", "") or data.get("session_id", "") or "web_default"
1899
+ session_id = data.get("sid", "") or data.get("session_id", "")
1901
1900
  chat_mode = data.get("mode", "") # "exec" = 执行模式
1902
1901
  escalated = data.get("escalated", False) # 临时提权到 local
1903
1902
 
@@ -2056,8 +2055,7 @@ window.addEventListener('beforeunload', function() {{
2056
2055
  return web.Response(text="data: " + json.dumps({"error": "message is required"}) + "\n\n", content_type="text/event-stream")
2057
2056
 
2058
2057
  agent_path = data.get("agent_path", data.get("agent_name", "1")) or "1"
2059
- # 统一使用 sid 作为会话标识(兼容旧字段 session_id
2060
- session_id = data.get("sid", "") or data.get("session_id", "") or "web_default"
2058
+ session_id = data.get("sid", "") or data.get("session_id", "")
2061
2059
  chat_mode = data.get("mode", "")
2062
2060
  escalated = data.get("escalated", False)
2063
2061
  voice_text = data.get("voice_text", "").strip() # 语音转文字原始文本(用于 usersays_correct)
@@ -2191,7 +2189,7 @@ window.addEventListener('beforeunload', function() {{
2191
2189
  """后台任务:即使 SSE 断开也完整执行并保存结果"""
2192
2190
  logger.info(f"[{session_id}] _run_stream_task 开始执行")
2193
2191
  # [v1.32.2] 安全修复: 定义 agent_id,避免 except 块中引用时 NameError
2194
- _stream_agent_id = self.core.memory.get_agent_id(agent_path) if self.core.memory and agent_path else 0
2192
+ _stream_agent_id = self.core.memory.get_agent_id(agent_path) if self.core.memory and agent_path else 1
2195
2193
  try:
2196
2194
  agent_cfg = self._read_agent_config(agent_path)
2197
2195
  model_chain = self._build_model_chain(agent_cfg, agent_path)
@@ -2449,8 +2447,7 @@ window.addEventListener('beforeunload', function() {{
2449
2447
  return web.json_response({"error": "message is required"}, status=400)
2450
2448
 
2451
2449
  agent_path = data.get("agent_path", "1") or "1"
2452
- # 统一使用 sid 作为会话标识(兼容旧字段 session_id
2453
- session_id = data.get("sid", "") or data.get("session_id", "web_default")
2450
+ session_id = data.get("sid", "") or data.get("session_id", "")
2454
2451
  choice = data.get("choice", "queue") # "continue" (插入后继续) 或 "queue" (排队)
2455
2452
 
2456
2453
  # 检查会话是否正在运行
@@ -3834,12 +3831,12 @@ window.addEventListener('beforeunload', function() {{
3834
3831
  # 清理该 Agent 相关的会话和记忆数据
3835
3832
  if self.core.memory:
3836
3833
  try:
3837
- # 删除相关会话 (session_id 格式: agent_session_xxx 或 agent_web_xxx)
3834
+ # 删除该 agent 的所有会话(通过 agent_id 精确匹配)
3838
3835
  conn = self.core.memory._get_conn()
3839
- like_pattern = f"{path}%"
3836
+ target_aid = self.core.memory.get_agent_id(path)
3840
3837
  rows = conn.execute(
3841
- "SELECT DISTINCT session_id FROM memories WHERE session_id LIKE ?",
3842
- (like_pattern,),
3838
+ "SELECT DISTINCT session_id FROM memories WHERE agent_id = ?",
3839
+ (target_aid,),
3843
3840
  ).fetchall()
3844
3841
  for row in rows:
3845
3842
  sid = row["session_id"]
@@ -4329,34 +4326,22 @@ window.addEventListener('beforeunload', function() {{
4329
4326
  _hidden = '(' + ','.join(["'llm_output'", "'llm_input'", "'tool_result_raw'", "'conversation_insight'"]) + ')'
4330
4327
  # 只统计用户可见的消息数(排除 llm_output 和 conversation_insight)
4331
4328
  if agent:
4332
- # [v1.27.2] 先将 agent 路径转为数字 ID,用数字 ID 精确过滤
4333
- target_aid = self.core.memory.get_agent_id(agent) if agent else 0
4334
- # [v1.32.4] 兼容旧路径 "default" 和新路径 "1",两者都指全权Agent
4335
- if agent == "1" or agent == "default":
4336
- # default agent: agent_id=0(未分配)或 agent_id=1(default 的数字 ID)或 旧格式
4337
- rows = conn.execute(
4338
- f"""SELECT DISTINCT session_id, COUNT(*) as cnt, MAX(created_at) as last,
4339
- MAX(CASE WHEN agent_id > 1 THEN agent_id ELSE NULL END) as raw_agent_id FROM memories
4340
- WHERE category = 'session' AND role != ''
4341
- AND key NOT IN {_hidden}
4342
- AND (agent_id = 0 OR agent_id = ? OR session_id = 'web_default' OR session_id LIKE 'sess_%')
4343
- GROUP BY session_id ORDER BY last DESC LIMIT 100""",
4344
- (target_aid,)).fetchall()
4345
- else:
4346
- # 其他 agent: agent_id=数字ID 或 session_id 前缀匹配(旧数据)
4347
- rows = conn.execute(
4348
- f"""SELECT DISTINCT session_id, COUNT(*) as cnt, MAX(created_at) as last,
4349
- MAX(CASE WHEN agent_id != ? THEN agent_id ELSE NULL END) as raw_agent_id FROM memories
4350
- WHERE category = 'session' AND role != ''
4351
- AND key NOT IN {_hidden}
4352
- AND (agent_id = ? OR session_id LIKE ?)
4353
- GROUP BY session_id ORDER BY last DESC LIMIT 100""",
4354
- (target_aid, target_aid, f"{agent}_%")).fetchall()
4329
+ # agent 路径转为数字 ID,精确过滤
4330
+ target_aid = self.core.memory.get_agent_id(agent)
4331
+ # 统一使用 agent_id 精确匹配
4332
+ rows = conn.execute(
4333
+ f"""SELECT DISTINCT session_id, COUNT(*) as cnt, MAX(created_at) as last,
4334
+ agent_id as raw_agent_id FROM memories
4335
+ WHERE category = 'session' AND role != ''
4336
+ AND key NOT IN {_hidden}
4337
+ AND agent_id = ?
4338
+ GROUP BY session_id ORDER BY last DESC LIMIT 100""",
4339
+ (target_aid,)).fetchall()
4355
4340
  else:
4356
4341
  # [v1.27.2] 无 agent 过滤时也返回 agent_id,供后台管理页面使用
4357
4342
  rows = conn.execute(
4358
4343
  f"SELECT DISTINCT session_id, COUNT(*) as cnt, MAX(created_at) as last, "
4359
- f"MAX(CASE WHEN agent_id > 0 THEN agent_id ELSE NULL END) as raw_agent_id FROM memories "
4344
+ f"agent_id as raw_agent_id FROM memories "
4360
4345
  f"WHERE category = 'session' AND role != '' "
4361
4346
  f"AND key NOT IN {_hidden} "
4362
4347
  f"GROUP BY session_id ORDER BY last DESC LIMIT 100").fetchall()
@@ -4369,7 +4354,7 @@ window.addEventListener('beforeunload', function() {{
4369
4354
  agent_name = _agent_id_map.get(int(raw_aid), "")
4370
4355
  sessions.append({"id": r["session_id"], "messages": r["cnt"], "last": r["last"],
4371
4356
  "agent_id": agent_name, # 返回 agent 名称(非数字 ID)
4372
- "agent_db_id": (int(raw_aid) if raw_aid is not None else 0), # 数字 ID 供 URL 使用
4357
+ "agent_db_id": int(raw_aid), # 数字 ID 供 URL 使用
4373
4358
  "display_name": "", "preview": ""})
4374
4359
  # 批量获取自定义会话名称
4375
4360
  sids = [s["id"] for s in sessions]
@@ -4399,25 +4384,13 @@ window.addEventListener('beforeunload', function() {{
4399
4384
  name = request.match_info["name"]
4400
4385
  if not self.core.memory:
4401
4386
  return web.json_response({"agent": name, "sessions": []})
4402
- # [v1.27.2] 使用数字 agent_id 精确过滤,同时兼容旧数据
4387
+ # 使用数字 agent_id 精确过滤
4403
4388
  target_aid = self.core.memory.get_agent_id(name)
4404
- # [v1.32.4] 兼容旧路径 "default" 和新路径 "1"
4405
- if name == "1" or name == "default":
4406
- # default agent: agent_id=0 agent_id=1 或 旧格式
4407
- rows = self.core.memory._get_conn().execute(
4408
- """SELECT DISTINCT session_id, COUNT(*) as cnt, MAX(created_at) as last FROM memories
4409
- WHERE category = 'session'
4410
- AND (agent_id = 0 OR agent_id = ? OR session_id = 'web_default' OR session_id LIKE 'sess_%')
4411
- GROUP BY session_id ORDER BY last DESC LIMIT 100""",
4412
- (target_aid,)).fetchall()
4413
- else:
4414
- # 其他 agent: agent_id=数字ID 或 session_id 前缀匹配(旧数据)
4415
- rows = self.core.memory._get_conn().execute(
4416
- """SELECT DISTINCT session_id, COUNT(*) as cnt, MAX(created_at) as last FROM memories
4417
- WHERE category = 'session'
4418
- AND (agent_id = ? OR session_id LIKE ?)
4419
- GROUP BY session_id ORDER BY last DESC LIMIT 100""",
4420
- (target_aid, f"{name}_%")).fetchall()
4389
+ rows = self.core.memory._get_conn().execute(
4390
+ """SELECT DISTINCT session_id, COUNT(*) as cnt, MAX(created_at) as last FROM memories
4391
+ WHERE category = 'session' AND agent_id = ?
4392
+ GROUP BY session_id ORDER BY last DESC LIMIT 100""",
4393
+ (target_aid,)).fetchall()
4421
4394
  sessions = [{"id": r["session_id"], "messages": r["cnt"], "last": r["last"]} for r in rows]
4422
4395
  # 批量获取自定义会话名称
4423
4396
  sids = [s["id"] for s in sessions]
@@ -6140,7 +6113,7 @@ window.addEventListener('beforeunload', function() {{
6140
6113
  context.metadata["user_voice_text"] = voice_text # 语音输入原始文本(用于 usersays_correct)
6141
6114
 
6142
6115
  # [v1.28] 传入正确的 agent_db_id,确保 V2 路径保存会话时使用正确的 agent_id
6143
- agent_id = 0 # [v1.32.2] 安全修复: 定义 agent_id,避免后续引用时 NameError
6116
+ agent_id = 1 # 默认为全权Agent
6144
6117
  if agent_path and self.core.memory:
6145
6118
  agent_id = self.core.memory.get_agent_id(agent_path)
6146
6119
  context.metadata["agent_db_id"] = agent_id
@@ -6390,7 +6363,7 @@ window.addEventListener('beforeunload', function() {{
6390
6363
  recent = agent.memory.get_conversation(session_id, limit=3)
6391
6364
  _has_assistant = any(e.role == "assistant" for e in recent)
6392
6365
  if not _has_assistant and final_response and final_response.strip():
6393
- _fallback_agent_id = context.metadata.get("agent_db_id", 0)
6366
+ _fallback_agent_id = context.metadata.get("agent_db_id", 1)
6394
6367
  agent.memory.add_session(agent_id=_fallback_agent_id,
6395
6368
  session_id=session_id, role="assistant", content=final_response,
6396
6369
  )
@@ -556,10 +556,18 @@ function confirmDeleteAgent(path,name){
556
556
  });
557
557
  }
558
558
 
559
- // 直接以执行模式打开与指定 Agent 的对话
560
- // [v1.27.2] 使用 a 参数(UrlCodec 编码),不再明文暴露 agent 路径
559
+ // 直接以执行模式打开与指定 Agent 的对话(使用 aid 数字 ID)
561
560
  function chatWithAgent(path){
562
- window.location.href='/ui/chat/chat_container.html?mode=exec&a='+UrlCodec.encode(path);
561
+ // 查找该 agent 的数字 ID
562
+ var _aid = null;
563
+ if (typeof allAgentsCache !== 'undefined' && Array.isArray(allAgentsCache)) {
564
+ for (var i = 0; i < allAgentsCache.length; i++) {
565
+ if (allAgentsCache[i].path === path) { _aid = allAgentsCache[i].aid; break; }
566
+ }
567
+ }
568
+ var url = '/ui/chat/chat_container.html?mode=exec';
569
+ if (_aid) url += '&aid=' + _aid;
570
+ window.location.href = url;
563
571
  }
564
572
 
565
573
  if (typeof window._adminRenderers === 'undefined') window._adminRenderers = {};
@@ -158,26 +158,26 @@ document.getElementById('adminSidebar')?.addEventListener('click',function(e){
158
158
  loadVersion();
159
159
  setTimeout(()=>checkUpdate(false),30000);
160
160
 
161
- // [v1.23.81] 返回聊天:通过纯数字 session ID 恢复群聊(不再传递 group_id)
161
+ // 返回聊天:通过 aid(数字 agent ID)和 session 参数恢复
162
162
  (function(){
163
163
  function _buildReturnUrl(){
164
164
  var params=new URLSearchParams(window.location.search);
165
165
  var base='/ui/chat/chat_container.html';
166
- // 从 URL 参数中恢复聊天状态
167
- var agent=params.get('from_agent');
166
+ // 从 URL 参数中恢复聊天状态(统一使用 aid 数字 ID)
167
+ var aid=params.get('from_aid');
168
168
  var session=params.get('from_session');
169
- // [v1.23.81] 群聊纯数字 session ID(优先级最高,覆盖 ?s= 个人会话参数)
170
- var groupSession=params.get('from_group_session');
169
+ // 群聊纯数字 session ID(优先级最高,覆盖 ?s= 个人会话参数)
170
+ var groupSession=params.get('from_g');
171
171
  var mode=params.get('from_mode');
172
172
  var parts=[];
173
173
  // 如果有群聊 session,用 ?g= 参数(纯数字 ID)
174
174
  if(groupSession && /^[0-9]+$/.test(groupSession)){
175
175
  parts.push('g='+groupSession);
176
- if(agent)parts.push('a='+encodeURIComponent(agent));
176
+ if(aid)parts.push('aid='+aid);
177
177
  if(mode)parts.push('mode='+encodeURIComponent(mode));
178
178
  } else {
179
179
  // 个人聊天模式
180
- if(agent)parts.push('a='+encodeURIComponent(agent));
180
+ if(aid)parts.push('aid='+aid);
181
181
  if(mode)parts.push('mode='+encodeURIComponent(mode));
182
182
  if(session)parts.push('s='+encodeURIComponent(session));
183
183
  }
@@ -86,19 +86,20 @@ function closeMobileAgentPanel() {
86
86
  if (overlay) overlay.classList.remove('active');
87
87
  }
88
88
 
89
- // [v1.23.81] 跳转后台管理:携带当前聊天上下文(agent、session、群聊session ID),以便"返回聊天"正确恢复
89
+ // 跳转后台管理:携带当前聊天上下文(aid数字ID、session、群聊ID),以便"返回聊天"正确恢复
90
90
  function goToAdmin() {
91
91
  var params = new URLSearchParams();
92
92
  var page = 'dashboard';
93
- // 传递当前聊天状态,供"返回聊天"恢复
94
- if (state.activeAgent) params.set('from_agent', state.activeAgent);
93
+ // 传递当前聊天状态,供"返回聊天"恢复(统一使用 aid 数字 ID)
94
+ var _curAgentObj = findAgentByPath(state.activeAgent);
95
+ if (_curAgentObj && _curAgentObj.aid) params.set('from_aid', _curAgentObj.aid);
95
96
  if (state.chatMode) params.set('from_mode', state.chatMode);
96
97
  if (state.activeSessionId && currentView !== 'group') params.set('from_session', state.activeSessionId);
97
- // [v1.23.81] 传递群聊纯数字 session ID(从 URL 读取),而非 JS 变量
98
+ // 传递群聊纯数字 session ID(从 URL 读取)
98
99
  if (typeof currentView !== 'undefined' && currentView === 'group') {
99
100
  var urlP = new URLSearchParams(window.location.search);
100
101
  var gSession = urlP.get('g');
101
- if (gSession) params.set('from_group_session', gSession);
102
+ if (gSession) params.set('from_g', gSession);
102
103
  }
103
104
  // 保留原始 ?page= 参数
104
105
  var origParams = new URLSearchParams(window.location.search);
@@ -356,10 +357,7 @@ const StatePersistence = {
356
357
  if (savedSessionId) {
357
358
  state._pendingSessionRestore = savedSessionId;
358
359
  }
359
- // [v1.23.81] 不再从 localStorage 恢复群聊状态(统一由 URL ?g= 参数恢复)
360
- // 仅保留兼容处理:如果旧版遗留了 localStorage 群聊状态,清除它
361
- StatePersistence.remove('currentView');
362
- StatePersistence.remove('currentGroupId');
360
+ // 群聊状态统一由 URL ?g= 参数恢复,不从 localStorage 恢复
363
361
  },
364
362
  /** 标记 setup 已完成(避免每次刷新弹出向导) */
365
363
  markSetupDone() { StatePersistence.save('setupDone', true); },
@@ -376,19 +374,13 @@ async function initChat() {
376
374
  initAttachmentUI();
377
375
 
378
376
  // URL 参数处理: ?aid=数字ID&mode=exec&s=编码session&g=群聊ID&popout=1
379
- // [v1.27.2] 使用 aid(数字 agent ID)代替明文 agent 名字
380
377
  const urlParams = new URLSearchParams(window.location.search);
381
378
  const urlAid = parseInt(urlParams.get('aid') || '', 10) || 0;
382
- // 兼容旧格式 ?a= 编码 或 ?agent= 明文
383
- const urlAgentLegacy = UrlCodec.decode(urlParams.get('a') || '') || UrlCodec.decode(urlParams.get('agent') || '');
384
379
  const urlMode = urlParams.get('mode');
385
- const urlSession = UrlCodec.decode(urlParams.get('s') || '') || UrlCodec.decode(urlParams.get('session') || '');
380
+ const urlSession = UrlCodec.decode(urlParams.get('s') || '');
386
381
  const isPopout = urlParams.get('popout') === '1';
387
- // [v1.23.81] 群聊纯数字 session ID(?g= 参数,优先级最高)
382
+ // 群聊纯数字 session ID(?g= 参数,优先级最高)
388
383
  const urlGroupSession = urlParams.get('g') || '';
389
- // 兼容旧版 ?group= 参数(从后台管理返回时的旧格式)
390
- const urlGroupSessionLegacy = urlParams.get('from_group_session') || '';
391
- const _effectiveGroupSession = urlGroupSession || urlGroupSessionLegacy;
392
384
  if (urlMode === 'chat' || urlMode === 'exec') {
393
385
  state.chatMode = urlMode;
394
386
  }
@@ -473,26 +465,11 @@ async function initChat() {
473
465
  state.agentsFlat.forEach(function(a) {
474
466
  if (a.aid) _aidToPath[a.aid] = a.path;
475
467
  });
476
- // 从 URL 解析 agent: 优先 aid(数字),兼容 a/agent(编码/明文)
468
+ // 从 URL 解析 agent: 仅支持 aid(数字 agent ID)
477
469
  var urlAgent = '';
478
470
  if (urlAid && _aidToPath[urlAid]) {
479
471
  urlAgent = _aidToPath[urlAid];
480
- } else if (urlAgentLegacy) {
481
- urlAgent = urlAgentLegacy;
482
472
  }
483
- // 清理 URL 中的旧格式参数(agent/a/session),统一使用 aid/s
484
- try {
485
- var _cleanUrl = new URL(window.location.href);
486
- _cleanUrl.searchParams.delete('agent');
487
- _cleanUrl.searchParams.delete('a');
488
- _cleanUrl.searchParams.delete('session');
489
- if (urlAgent && !urlAid) {
490
- // 如果没有 aid 但有旧格式 agent,添加 aid
491
- var _agentObj = findAgentByPath(urlAgent);
492
- if (_agentObj && _agentObj.aid) _cleanUrl.searchParams.set('aid', _agentObj.aid);
493
- }
494
- window.history.replaceState({}, '', _cleanUrl.toString());
495
- } catch (_) {}
496
473
 
497
474
  if (isPopout) {
498
475
  const agentObj = findAgentByPath(urlAgent);
@@ -520,12 +497,12 @@ async function initChat() {
520
497
  }
521
498
 
522
499
  // 如果 URL 中有 ?g= 纯数字 session ID,优先恢复群聊
523
- if (_effectiveGroupSession && /^[0-9]+$/.test(_effectiveGroupSession)) {
500
+ if (urlGroupSession && /^[0-9]+$/.test(urlGroupSession)) {
524
501
  if (urlAgent && urlAgent !== state.activeAgent) {
525
502
  state.activeAgent = urlAgent;
526
503
  StatePersistence.save('activeAgent', urlAgent);
527
504
  }
528
- _restoreGroupBySession(_effectiveGroupSession);
505
+ _restoreGroupBySession(urlGroupSession);
529
506
  } else if (urlAgent) {
530
507
  var resolved = urlAgent;
531
508
  if (!findAgentByPath(resolved)) {
@@ -536,17 +513,8 @@ async function initChat() {
536
513
  selectAgent(resolved);
537
514
  }
538
515
  } else if (urlSession) {
539
- // 只有 session 没有 agent,尝试从 session ID 推断 agent(兼容旧格式 agent_web_xxx)
540
- var targetAgent = findMasterAgentPath();
541
- if (urlSession.indexOf('_web_') >= 0) {
542
- targetAgent = urlSession.split('_web_')[0] || findMasterAgentPath();
543
- }
544
- // 新格式 sess_xxx 无法推断 agent,回退 master
545
- var target = targetAgent;
546
- if (!findAgentByPath(target)) {
547
- console.warn('Session-inferred agent not found, fallback to master:', target);
548
- target = findMasterAgentPath();
549
- }
516
+ // 只有 session 没有 agent,回退 master agent
517
+ var target = findMasterAgentPath();
550
518
  if (target !== state.activeAgent) {
551
519
  selectAgent(target);
552
520
  }
@@ -1822,26 +1790,17 @@ function findAgentByPath(path) {
1822
1790
  return state.agentsFlat.find(function(a) { return a.path === path; });
1823
1791
  }
1824
1792
 
1825
- // [v1.32.4] 查找系统级 Agent 的路径(兼容旧路径 'default'/'配置助手' 和新路径 '1'/'2')
1793
+ // [v1.32.7] 查找系统级 Agent 的路径
1826
1794
  function findMasterAgentPath() {
1827
- // 优先按新路径 "1",兼容旧路径 "default",再按 system 标志+名称兜底
1828
- var a = state.agentsFlat.find(function(x) { return x.path === '1'; })
1829
- || state.agentsFlat.find(function(x) { return x.path === 'default'; })
1830
- || state.agentsFlat.find(function(x) { return x.system && x.name === '全权Agent'; })
1831
- || state.agentsFlat.find(function(x) { return x.system; });
1832
- return a ? a.path : 'default';
1795
+ var a = state.agentsFlat.find(function(x) { return x.path === '1'; });
1796
+ return a ? a.path : '1';
1833
1797
  }
1834
1798
  function findHelperAgentPath() {
1835
- var a = state.agentsFlat.find(function(x) { return x.path === '2'; })
1836
- || state.agentsFlat.find(function(x) { return x.path === '配置助手'; })
1837
- || state.agentsFlat.find(function(x) { return x.system && x.name === '配置助手'; })
1838
- || null;
1799
+ var a = state.agentsFlat.find(function(x) { return x.path === '2'; });
1839
1800
  return a ? a.path : null;
1840
1801
  }
1841
1802
  function isSystemAgentPath(path) {
1842
- var mp = findMasterAgentPath();
1843
- var hp = findHelperAgentPath();
1844
- return path === mp || (hp !== null && path === hp);
1803
+ return path === '1' || path === '2';
1845
1804
  }
1846
1805
 
1847
1806
  function getActiveAgentObj() {
@@ -1854,8 +1813,7 @@ async function loadAgents() {
1854
1813
  state.agentTree = treeData || [];
1855
1814
  state.agentsFlat = flattenTree(state.agentTree, 0, null);
1856
1815
 
1857
- // [v1.32.4] 兼容旧路径:如果后端没有 path='default' agent,不创建合成的 fake agent
1858
- // 而是依赖 findMasterAgentPath() 来正确解析 master agent 路径
1816
+ // 使用 findMasterAgentPath() 解析 master agent 路径
1859
1817
  var _masterPath = findMasterAgentPath();
1860
1818
 
1861
1819
  for (var i = 0; i < state.agentsFlat.length; i++) {
@@ -1929,7 +1887,7 @@ function renderRightPanelSections() {
1929
1887
  function renderMasterAgentCard() {
1930
1888
  var el = document.getElementById('rpMasterAgent');
1931
1889
  if (!el) return;
1932
- // [v1.32.4] 使用 findMasterAgentPath() 兼容旧路径 'default' 和新路径 '1'
1890
+ // 使用 findMasterAgentPath() 获取 master agent 路径
1933
1891
  var masterPath = findMasterAgentPath();
1934
1892
  var agent = findAgentByPath(masterPath);
1935
1893
  var isActive = state.activeAgent === masterPath;
@@ -1953,7 +1911,7 @@ function renderMasterAgentCard() {
1953
1911
  function renderHelperAgentCard() {
1954
1912
  var el = document.getElementById('rpHelperAgent');
1955
1913
  if (!el) return;
1956
- // [v1.32.4] 使用 findHelperAgentPath() 兼容旧路径 '配置助手' 和新路径 '2'
1914
+ // 使用 findHelperAgentPath() 获取 helper agent 路径
1957
1915
  var helperPath = findHelperAgentPath();
1958
1916
  if (!helperPath) { el.innerHTML = ''; return; } // 无配置助手则不渲染
1959
1917
  var agent = findAgentByPath(helperPath);
@@ -2678,40 +2636,18 @@ async function loadSessions() {
2678
2636
  // 优先级: URL session 参数 > localStorage 持久化的 session > 最新 session
2679
2637
  // [fix] 所有候选 session ID 必须属于当前 agent 的会话列表,防止错乱到其它 agent 的历史对话
2680
2638
  const urlParams = new URLSearchParams(window.location.search);
2681
- const urlSession = UrlCodec.decode(urlParams.get('s') || '') || UrlCodec.decode(urlParams.get('session') || '');
2639
+ const urlSession = UrlCodec.decode(urlParams.get('s') || '');
2682
2640
  var targetSessionId = null;
2683
2641
 
2684
2642
  if (urlSession && state.sessions.some(s => s.id === urlSession)) {
2685
2643
  // URL 指定了有效的 session ID,直接选中(刷新恢复)
2686
2644
  targetSessionId = urlSession;
2687
- } else if (urlSession) {
2688
- // [v1.23.24] 前缀匹配 fallback:前端生成的临时 ID 与后端 canonical ID 可能不同
2689
- // 例如前端 "default_web_20250101120000" 后端实际为 "default_web_20250101120000_a1b2c3"
2690
- var prefixMatch = state.sessions.find(function(s) {
2691
- return s.id.startsWith(urlSession) || urlSession.startsWith(s.id);
2692
- });
2693
- if (prefixMatch) {
2694
- targetSessionId = prefixMatch.id;
2695
- // 更新 URL 为 canonical ID,避免下次刷新再走 fallback
2696
- try {
2697
- var _fixUrl = new URL(window.location.href);
2698
- _fixUrl.searchParams.set('s', UrlCodec.encode(prefixMatch.id));
2699
- window.history.replaceState({}, '', _fixUrl.toString());
2700
- } catch (_) {}
2701
- }
2702
2645
  }
2703
2646
  if (!targetSessionId && state._pendingSessionRestore && state.sessions.some(s => s.id === state._pendingSessionRestore)) {
2704
2647
  // 从 localStorage 恢复的 session(beforeunload 触发的保存)
2705
2648
  targetSessionId = state._pendingSessionRestore;
2706
2649
  state._pendingSessionRestore = null; // 清除,防止重复恢复
2707
2650
  } else if (!targetSessionId && state._pendingSessionRestore) {
2708
- // [v1.23.24] localStorage 恢复的 ID 也不匹配,尝试前缀匹配
2709
- var lsPrefixMatch = state.sessions.find(function(s) {
2710
- return s.id.startsWith(state._pendingSessionRestore) || state._pendingSessionRestore.startsWith(s.id);
2711
- });
2712
- if (lsPrefixMatch) {
2713
- targetSessionId = lsPrefixMatch.id;
2714
- }
2715
2651
  state._pendingSessionRestore = null;
2716
2652
  }
2717
2653
  if (!targetSessionId && !state.activeSessionId && state.sessions.length > 0) {
@@ -3039,10 +2975,6 @@ async function selectSession(id) {
3039
2975
  try {
3040
2976
  const url = new URL(window.location.href);
3041
2977
  url.searchParams.set('s', UrlCodec.encode(id));
3042
- // 移除旧格式参数
3043
- url.searchParams.delete('a');
3044
- url.searchParams.delete('agent');
3045
- url.searchParams.delete('session');
3046
2978
  // 设置 aid(数字 agent ID)
3047
2979
  var _selAgentObj = findAgentByPath(state.activeAgent);
3048
2980
  if (_selAgentObj && _selAgentObj.aid) {
@@ -2502,7 +2502,7 @@ async function handleInjectChoice(choice) {
2502
2502
  clearDraft();
2503
2503
 
2504
2504
  try {
2505
- const rawSid = state.activeSessionId ? state.activeSessionId.replace(`${state.activeAgent}_`, '') : 'web_default';
2505
+ const rawSid = state.activeSessionId || 'web_default';
2506
2506
  const resp = await fetch('/api/chat/inject', {
2507
2507
  method: 'POST',
2508
2508
  headers: { 'Content-Type': 'application/json' },
@@ -255,8 +255,9 @@ async function selectGroup(gid) {
255
255
  var sessionId = sessionData.sid;
256
256
  // 用群聊专用 ?g= 参数更新 URL(不刷新页面),纯数字 session ID
257
257
  var newUrl = '/ui/chat/chat_container.html?g=' + sessionId;
258
- if (typeof state !== 'undefined' && state.activeAgent) {
259
- newUrl += '&a=' + encodeURIComponent(state.activeAgent);
258
+ var _grpAgentObj = (typeof findAgentByPath === 'function') ? findAgentByPath(state.activeAgent) : null;
259
+ if (_grpAgentObj && _grpAgentObj.aid) {
260
+ newUrl += '&aid=' + _grpAgentObj.aid;
260
261
  }
261
262
  window.history.replaceState({groupSession: sessionId, groupId: gid}, '', newUrl);
262
263
  }
@@ -344,13 +345,14 @@ function exitGroupChat() {
344
345
  currentView = 'chat';
345
346
  currentGroupId = null;
346
347
  groupMessages = [];
347
- // [v1.23.81] 清除群聊 ?g= session URL,恢复普通聊天 URL
348
+ // 清除群聊 ?g= session URL,恢复普通聊天 URL(统一使用 aid 数字 ID)
348
349
  var cleanUrl = '/ui/chat/chat_container.html';
349
- if (typeof state !== 'undefined' && state.activeAgent && state.activeAgent !== (typeof findMasterAgentPath === 'function' ? findMasterAgentPath() : '1')) {
350
- cleanUrl += '?a=' + encodeURIComponent(state.activeAgent);
350
+ var _exitAgentObj = (typeof findAgentByPath === 'function') ? findAgentByPath(state.activeAgent) : null;
351
+ if (_exitAgentObj && _exitAgentObj.aid) {
352
+ cleanUrl += '?aid=' + _exitAgentObj.aid;
351
353
  }
352
354
  if (typeof state !== 'undefined' && state.activeSessionId && state.activeSessionId !== '__new__') {
353
- cleanUrl += (cleanUrl.indexOf('?') >= 0 ? '&' : '?') + 's=' + encodeURIComponent(state.activeSessionId);
355
+ cleanUrl += (cleanUrl.indexOf('?') >= 0 ? '&' : '?') + 's=' + UrlCodec.encode(state.activeSessionId);
354
356
  }
355
357
  window.history.replaceState({}, '', cleanUrl);
356
358
  // 清除群聊 localStorage 持久化状态
@@ -456,8 +458,9 @@ function startPrivateChatFromGroup(agentPath) {
456
458
  StatePersistence.remove('currentGroupId');
457
459
  // 清除群聊 URL 中的 ?g= 参数
458
460
  var cleanUrl = '/ui/chat/chat_container.html';
459
- if (typeof state !== 'undefined' && agentPath && agentPath !== (typeof findMasterAgentPath === 'function' ? findMasterAgentPath() : '1')) {
460
- cleanUrl += '?a=' + encodeURIComponent(agentPath);
461
+ var _memAgentObj = (typeof findAgentByPath === 'function') ? findAgentByPath(agentPath) : null;
462
+ if (_memAgentObj && _memAgentObj.aid) {
463
+ cleanUrl += '?aid=' + _memAgentObj.aid;
461
464
  }
462
465
  window.history.replaceState({}, '', cleanUrl);
463
466
  // 让 selectAgent 处理完整的私聊切换