myagent-ai 1.27.1 → 1.27.2
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.
package/package.json
CHANGED
package/web/api_server.py
CHANGED
|
@@ -3505,6 +3505,9 @@ window.addEventListener('beforeunload', function() {{
|
|
|
3505
3505
|
session_counts[ap] = session_counts.get(ap, 0) + r["cnt"]
|
|
3506
3506
|
for a in agents_flat:
|
|
3507
3507
|
a["session_count"] = session_counts.get(a["path"], 0)
|
|
3508
|
+
# [v1.27.2] 附加数字 agent_id 到每个 agent(供 URL 参数使用)
|
|
3509
|
+
for a in agents_flat:
|
|
3510
|
+
a["aid"] = self.core.memory.get_agent_id(a["path"])
|
|
3508
3511
|
# 系统 agent 排在最前
|
|
3509
3512
|
agents_flat.sort(key=lambda a: (0 if a.get("system") else 1, a.get("path", "")))
|
|
3510
3513
|
tree = self._build_agent_tree(agents_flat)
|
|
@@ -4299,39 +4302,60 @@ window.addEventListener('beforeunload', function() {{
|
|
|
4299
4302
|
async def handle_list_sessions(self, request):
|
|
4300
4303
|
if not self.core.memory: return web.json_response([])
|
|
4301
4304
|
agent = request.query.get("agent", "")
|
|
4305
|
+
conn = self.core.memory._get_conn()
|
|
4306
|
+
|
|
4307
|
+
# [v1.27.2] 构建 agent_id → agent_name 的映射(数字 ID → 路径名)
|
|
4308
|
+
_agent_id_map = {} # {int_id: str_name}
|
|
4309
|
+
try:
|
|
4310
|
+
for ar in conn.execute("SELECT id, name FROM agents").fetchall():
|
|
4311
|
+
_agent_id_map[int(ar["id"])] = ar["name"]
|
|
4312
|
+
except Exception:
|
|
4313
|
+
pass
|
|
4314
|
+
|
|
4315
|
+
_hidden = '(' + ','.join(["'llm_output'", "'llm_input'", "'tool_result_raw'", "'conversation_insight'"]) + ')'
|
|
4302
4316
|
# 只统计用户可见的消息数(排除 llm_output 和 conversation_insight)
|
|
4303
4317
|
if agent:
|
|
4304
|
-
# [v1.
|
|
4318
|
+
# [v1.27.2] 先将 agent 路径转为数字 ID,用数字 ID 精确过滤
|
|
4319
|
+
target_aid = self.core.memory.get_agent_id(agent) if agent else 0
|
|
4305
4320
|
if agent == "default":
|
|
4306
|
-
# default agent: agent_id=
|
|
4307
|
-
rows =
|
|
4308
|
-
"""SELECT DISTINCT session_id, COUNT(*) as cnt, MAX(created_at) as last,
|
|
4309
|
-
MAX(CASE WHEN agent_id
|
|
4321
|
+
# default agent: agent_id=0(未分配)或 agent_id=1(default 的数字 ID)或 旧格式
|
|
4322
|
+
rows = conn.execute(
|
|
4323
|
+
f"""SELECT DISTINCT session_id, COUNT(*) as cnt, MAX(created_at) as last,
|
|
4324
|
+
MAX(CASE WHEN agent_id > 1 THEN agent_id ELSE NULL END) as raw_agent_id FROM memories
|
|
4310
4325
|
WHERE category = 'session' AND role != ''
|
|
4311
|
-
AND key NOT IN
|
|
4312
|
-
AND (agent_id =
|
|
4313
|
-
GROUP BY session_id ORDER BY last DESC LIMIT 100"""
|
|
4326
|
+
AND key NOT IN {_hidden}
|
|
4327
|
+
AND (agent_id = 0 OR agent_id = ? OR session_id = 'web_default' OR session_id LIKE 'sess_%')
|
|
4328
|
+
GROUP BY session_id ORDER BY last DESC LIMIT 100""",
|
|
4329
|
+
(target_aid,)).fetchall()
|
|
4314
4330
|
else:
|
|
4315
|
-
# 其他 agent: agent_id
|
|
4316
|
-
rows =
|
|
4317
|
-
"""SELECT DISTINCT session_id, COUNT(*) as cnt, MAX(created_at) as last,
|
|
4318
|
-
MAX(CASE WHEN agent_id !=
|
|
4331
|
+
# 其他 agent: agent_id=数字ID 或 session_id 前缀匹配(旧数据)
|
|
4332
|
+
rows = conn.execute(
|
|
4333
|
+
f"""SELECT DISTINCT session_id, COUNT(*) as cnt, MAX(created_at) as last,
|
|
4334
|
+
MAX(CASE WHEN agent_id != ? THEN agent_id ELSE NULL END) as raw_agent_id FROM memories
|
|
4319
4335
|
WHERE category = 'session' AND role != ''
|
|
4320
|
-
AND key NOT IN
|
|
4336
|
+
AND key NOT IN {_hidden}
|
|
4321
4337
|
AND (agent_id = ? OR session_id LIKE ?)
|
|
4322
4338
|
GROUP BY session_id ORDER BY last DESC LIMIT 100""",
|
|
4323
|
-
(
|
|
4339
|
+
(target_aid, target_aid, f"{agent}_%")).fetchall()
|
|
4324
4340
|
else:
|
|
4325
|
-
# [v1.
|
|
4326
|
-
rows =
|
|
4327
|
-
"SELECT DISTINCT session_id, COUNT(*) as cnt, MAX(created_at) as last, "
|
|
4328
|
-
"MAX(CASE WHEN agent_id
|
|
4329
|
-
"WHERE category = 'session' AND role != '' "
|
|
4330
|
-
"AND key NOT IN
|
|
4331
|
-
"GROUP BY session_id ORDER BY last DESC LIMIT 100").fetchall()
|
|
4332
|
-
|
|
4333
|
-
|
|
4334
|
-
|
|
4341
|
+
# [v1.27.2] 无 agent 过滤时也返回 agent_id,供后台管理页面使用
|
|
4342
|
+
rows = conn.execute(
|
|
4343
|
+
f"SELECT DISTINCT session_id, COUNT(*) as cnt, MAX(created_at) as last, "
|
|
4344
|
+
f"MAX(CASE WHEN agent_id > 0 THEN agent_id ELSE NULL END) as raw_agent_id FROM memories "
|
|
4345
|
+
f"WHERE category = 'session' AND role != '' "
|
|
4346
|
+
f"AND key NOT IN {_hidden} "
|
|
4347
|
+
f"GROUP BY session_id ORDER BY last DESC LIMIT 100").fetchall()
|
|
4348
|
+
# [v1.27.2] 将数字 agent_id 转为 agent 名称
|
|
4349
|
+
sessions = []
|
|
4350
|
+
for r in rows:
|
|
4351
|
+
raw_aid = r["raw_agent_id"]
|
|
4352
|
+
agent_name = ""
|
|
4353
|
+
if raw_aid is not None:
|
|
4354
|
+
agent_name = _agent_id_map.get(int(raw_aid), "")
|
|
4355
|
+
sessions.append({"id": r["session_id"], "messages": r["cnt"], "last": r["last"],
|
|
4356
|
+
"agent_id": agent_name, # 返回 agent 名称(非数字 ID)
|
|
4357
|
+
"agent_db_id": (int(raw_aid) if raw_aid is not None else 0), # 数字 ID 供 URL 使用
|
|
4358
|
+
"display_name": "", "preview": ""})
|
|
4335
4359
|
# 批量获取自定义会话名称
|
|
4336
4360
|
sids = [s["id"] for s in sessions]
|
|
4337
4361
|
name_map = self.core.memory.list_session_names(sids) if sids else {}
|
|
@@ -4360,22 +4384,24 @@ window.addEventListener('beforeunload', function() {{
|
|
|
4360
4384
|
name = request.match_info["name"]
|
|
4361
4385
|
if not self.core.memory:
|
|
4362
4386
|
return web.json_response({"agent": name, "sessions": []})
|
|
4363
|
-
# [v1.
|
|
4387
|
+
# [v1.27.2] 使用数字 agent_id 精确过滤,同时兼容旧数据
|
|
4388
|
+
target_aid = self.core.memory.get_agent_id(name)
|
|
4364
4389
|
if name == "default":
|
|
4365
|
-
# default agent: agent_id=
|
|
4390
|
+
# default agent: agent_id=0 或 agent_id=1 或 旧格式
|
|
4366
4391
|
rows = self.core.memory._get_conn().execute(
|
|
4367
4392
|
"""SELECT DISTINCT session_id, COUNT(*) as cnt, MAX(created_at) as last FROM memories
|
|
4368
4393
|
WHERE category = 'session'
|
|
4369
|
-
AND (agent_id =
|
|
4370
|
-
GROUP BY session_id ORDER BY last DESC LIMIT 100"""
|
|
4394
|
+
AND (agent_id = 0 OR agent_id = ? OR session_id = 'web_default' OR session_id LIKE 'sess_%')
|
|
4395
|
+
GROUP BY session_id ORDER BY last DESC LIMIT 100""",
|
|
4396
|
+
(target_aid,)).fetchall()
|
|
4371
4397
|
else:
|
|
4372
|
-
# 其他 agent: agent_id
|
|
4398
|
+
# 其他 agent: agent_id=数字ID 或 session_id 前缀匹配(旧数据)
|
|
4373
4399
|
rows = self.core.memory._get_conn().execute(
|
|
4374
4400
|
"""SELECT DISTINCT session_id, COUNT(*) as cnt, MAX(created_at) as last FROM memories
|
|
4375
4401
|
WHERE category = 'session'
|
|
4376
4402
|
AND (agent_id = ? OR session_id LIKE ?)
|
|
4377
4403
|
GROUP BY session_id ORDER BY last DESC LIMIT 100""",
|
|
4378
|
-
(
|
|
4404
|
+
(target_aid, f"{name}_%")).fetchall()
|
|
4379
4405
|
sessions = [{"id": r["session_id"], "messages": r["cnt"], "last": r["last"]} for r in rows]
|
|
4380
4406
|
# 批量获取自定义会话名称
|
|
4381
4407
|
sids = [s["id"] for s in sessions]
|
|
@@ -4398,30 +4424,34 @@ window.addEventListener('beforeunload', function() {{
|
|
|
4398
4424
|
async def handle_get_messages(self, request):
|
|
4399
4425
|
sid = request.match_info["sid"]
|
|
4400
4426
|
if not self.core.memory: return web.json_response([])
|
|
4401
|
-
|
|
4402
|
-
|
|
4403
|
-
|
|
4404
|
-
|
|
4405
|
-
|
|
4406
|
-
|
|
4407
|
-
|
|
4408
|
-
|
|
4409
|
-
|
|
4410
|
-
|
|
4411
|
-
|
|
4412
|
-
|
|
4413
|
-
|
|
4414
|
-
|
|
4415
|
-
|
|
4416
|
-
|
|
4417
|
-
|
|
4418
|
-
|
|
4419
|
-
|
|
4420
|
-
|
|
4421
|
-
|
|
4422
|
-
|
|
4427
|
+
try:
|
|
4428
|
+
limit = min(int(request.query.get("limit", 500)), 500)
|
|
4429
|
+
offset = int(request.query.get("offset", 0))
|
|
4430
|
+
entries = self.core.memory.get_conversation(sid, limit=limit + offset)
|
|
4431
|
+
entries = entries[offset:]
|
|
4432
|
+
# Filter out internal entries (LLM raw output only)
|
|
4433
|
+
entries = [e for e in entries if (e.key or "") not in self._HIDDEN_KEYS]
|
|
4434
|
+
result = []
|
|
4435
|
+
for e in entries:
|
|
4436
|
+
msg = {"role": e.role, "content": e.content, "time": e.created_at, "key": e.key or ""}
|
|
4437
|
+
# [v1.16.17] 附加附件元数据
|
|
4438
|
+
meta = e.metadata or {}
|
|
4439
|
+
if meta.get("images"):
|
|
4440
|
+
msg["images"] = meta["images"]
|
|
4441
|
+
# [v1.23.19] 分离 _files 和 _media(前端用 msg._files 和 msg._media)
|
|
4442
|
+
_raw_files = meta.get("files") or []
|
|
4443
|
+
if _raw_files:
|
|
4444
|
+
_file_items = [f for f in _raw_files if f.get("_type") != "media"]
|
|
4445
|
+
_media_items = [f for f in _raw_files if f.get("_type") == "media"]
|
|
4446
|
+
if _file_items:
|
|
4447
|
+
msg["_files"] = _file_items
|
|
4448
|
+
if _media_items:
|
|
4449
|
+
msg["_media"] = _media_items
|
|
4423
4450
|
result.append(msg)
|
|
4424
4451
|
return web.json_response(result)
|
|
4452
|
+
except Exception as ex:
|
|
4453
|
+
logger.error(f"加载会话消息失败 sid={sid}: {ex}")
|
|
4454
|
+
return web.json_response({"error": f"加载消息失败: {str(ex)[:200]}"}, status=500)
|
|
4425
4455
|
|
|
4426
4456
|
async def handle_get_raw_messages(self, request):
|
|
4427
4457
|
"""GET /api/sessions/{sid}/raw - 获取会话全部原始消息(含 llm_output 等)"""
|
|
@@ -4459,30 +4489,34 @@ window.addEventListener('beforeunload', function() {{
|
|
|
4459
4489
|
if not sid:
|
|
4460
4490
|
return web.json_response([])
|
|
4461
4491
|
if not self.core.memory: return web.json_response([])
|
|
4462
|
-
|
|
4463
|
-
|
|
4464
|
-
|
|
4465
|
-
|
|
4466
|
-
|
|
4467
|
-
|
|
4468
|
-
|
|
4469
|
-
|
|
4470
|
-
|
|
4471
|
-
|
|
4472
|
-
|
|
4473
|
-
|
|
4474
|
-
|
|
4475
|
-
|
|
4476
|
-
|
|
4477
|
-
|
|
4478
|
-
|
|
4479
|
-
|
|
4480
|
-
|
|
4481
|
-
|
|
4482
|
-
|
|
4483
|
-
|
|
4484
|
-
|
|
4485
|
-
|
|
4492
|
+
try:
|
|
4493
|
+
limit = min(int(request.query.get("limit", 500)), 500)
|
|
4494
|
+
offset = int(request.query.get("offset", 0))
|
|
4495
|
+
entries = self.core.memory.get_conversation(sid, limit=limit + offset)
|
|
4496
|
+
entries = entries[offset:]
|
|
4497
|
+
# Filter out internal entries (LLM raw output only)
|
|
4498
|
+
entries = [e for e in entries if (e.key or "") not in self._HIDDEN_KEYS]
|
|
4499
|
+
result = []
|
|
4500
|
+
for e in entries:
|
|
4501
|
+
msg = {"role": e.role, "content": e.content, "time": e.created_at, "key": e.key or ""}
|
|
4502
|
+
# [v1.16.17] 附加附件元数据
|
|
4503
|
+
meta = e.metadata or {}
|
|
4504
|
+
if meta.get("images"):
|
|
4505
|
+
msg["images"] = meta["images"]
|
|
4506
|
+
# [v1.23.19] 分离 _files 和 _media(前端用 msg._files 和 msg._media)
|
|
4507
|
+
_raw_files = meta.get("files") or []
|
|
4508
|
+
if _raw_files:
|
|
4509
|
+
_file_items = [f for f in _raw_files if f.get("_type") != "media"]
|
|
4510
|
+
_media_items = [f for f in _raw_files if f.get("_type") == "media"]
|
|
4511
|
+
if _file_items:
|
|
4512
|
+
msg["_files"] = _file_items
|
|
4513
|
+
if _media_items:
|
|
4514
|
+
msg["_media"] = _media_items
|
|
4515
|
+
result.append(msg)
|
|
4516
|
+
return web.json_response(result)
|
|
4517
|
+
except Exception as ex:
|
|
4518
|
+
logger.error(f"加载会话消息失败 sid={sid}: {ex}")
|
|
4519
|
+
return web.json_response({"error": f"加载消息失败: {str(ex)[:200]}"}, status=500)
|
|
4486
4520
|
|
|
4487
4521
|
def _cleanup_session_state(self, sid: str):
|
|
4488
4522
|
"""删除会话时清理所有关联的后端状态"""
|
|
@@ -557,8 +557,9 @@ function confirmDeleteAgent(path,name){
|
|
|
557
557
|
}
|
|
558
558
|
|
|
559
559
|
// 直接以执行模式打开与指定 Agent 的对话
|
|
560
|
+
// [v1.27.2] 使用 a 参数(UrlCodec 编码),不再明文暴露 agent 路径
|
|
560
561
|
function chatWithAgent(path){
|
|
561
|
-
window.location.href='/ui/chat/chat_container.html?
|
|
562
|
+
window.location.href='/ui/chat/chat_container.html?mode=exec&a='+UrlCodec.encode(path);
|
|
562
563
|
}
|
|
563
564
|
|
|
564
565
|
if (typeof window._adminRenderers === 'undefined') window._adminRenderers = {};
|
|
@@ -7,6 +7,9 @@ let allAgentsCache=[];
|
|
|
7
7
|
let allModelsCache=[];
|
|
8
8
|
let allDeptsCache=[];
|
|
9
9
|
|
|
10
|
+
// [v1.27.2] UrlCodec: 与 chat 页面共享的 URL 编解码(避免 agent/session 明文暴露)
|
|
11
|
+
var UrlCodec=(function(){var _p='x';function _encode(str){if(!str)return'';try{var bytes=new TextEncoder().encode(str);var bin='';for(var i=0;i<bytes.length;i++)bin+=String.fromCharCode(bytes[i]);return _p+btoa(bin).replace(/=/g,'').replace(/\+/g,'-').replace(/\//g,'_')}catch(e){return str}}function _decode(encoded){if(!encoded||encoded[0]!==_p)return encoded;try{var b64=encoded.substring(1).replace(/-/g,'+').replace(/_/g,'/');while(b64.length%4)b64+='=';var bin=atob(b64);var bytes=new Uint8Array(bin.length);for(var i=0;i<bin.length;i++)bytes[i]=bin.charCodeAt(i);return new TextDecoder().decode(bytes)}catch(e){return encoded}}return{encode:_encode,decode:_decode}})();
|
|
12
|
+
|
|
10
13
|
function esc(s){if(!s)return'';return String(s).replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>').replace(/"/g,'"');}
|
|
11
14
|
|
|
12
15
|
async function api(url,opts={}){
|
|
@@ -7,7 +7,7 @@ async function renderSessions(){
|
|
|
7
7
|
}
|
|
8
8
|
let html='<div class="table-wrap"><table><tr><th>会话</th><th>Agent</th><th>消息数</th><th>最后活动</th><th>操作</th></tr>';
|
|
9
9
|
for(const s of (ss||[])){
|
|
10
|
-
// [v1.
|
|
10
|
+
// [v1.27.2] 优先使用后端返回的 agent_id(现在是 agent 名称),回退从前缀猜测
|
|
11
11
|
let agentName=s.agent_id||'';
|
|
12
12
|
if(!agentName){
|
|
13
13
|
// 回退:从 session_id 前缀提取(兼容旧格式)
|
|
@@ -20,8 +20,9 @@ async function renderSessions(){
|
|
|
20
20
|
}
|
|
21
21
|
}
|
|
22
22
|
const displayName=s.display_name||s.id;
|
|
23
|
+
const dbId=s.agent_db_id||1;
|
|
23
24
|
html+=`<tr><td title="${escHtml(s.id)}">${escHtml(displayName.length>30?displayName.slice(0,30)+'...':displayName)}</td><td>${escHtml(agentName)}</td><td>${s.messages}</td><td>${s.last?.slice(0,19)||''}</td>
|
|
24
|
-
<td><button class="btn btn-sm" style="background:var(--success);color:#fff" onclick="enterSession('${escHtml(s.id)}'
|
|
25
|
+
<td><button class="btn btn-sm" style="background:var(--success);color:#fff" onclick="enterSession('${escHtml(s.id)}',${dbId})">切入</button>
|
|
25
26
|
<button class="btn btn-sm btn-ghost" onclick="viewSession('${escHtml(s.id)}')">查看</button>
|
|
26
27
|
<button class="btn btn-sm btn-danger" onclick="clearSession('${escHtml(s.id)}')">清除</button></td></tr>`;}
|
|
27
28
|
html+='</table></div>';
|
|
@@ -86,7 +87,7 @@ async function _loadSessionMessages(){
|
|
|
86
87
|
if(hasMore)html+=`<button class="btn btn-ghost mt-8" onclick="window._viewSessionOffset=${offset+100};_loadSessionMessages()">加载更多...</button>`;
|
|
87
88
|
html+='<div class="flex gap-8 mt-8"><button class="btn btn-ghost" onclick="goBack()">返回</button>';
|
|
88
89
|
html+=`<button class="btn" style="background:var(--accent);color:#fff" onclick="viewSessionRaw('${escHtml(sid)}')">Raw 原始消息</button>`;
|
|
89
|
-
html+=`<button class="btn btn-primary" onclick="enterSession('${escHtml(sid)}',
|
|
90
|
+
html+=`<button class="btn btn-primary" onclick="enterSession('${escHtml(sid)}',1)">在聊天中查看完整记录</button></div>`;
|
|
90
91
|
$('content').innerHTML=html;
|
|
91
92
|
}
|
|
92
93
|
// ========== Raw 原始消息查看 ==========
|
|
@@ -233,8 +234,9 @@ function _buildTimeNav(msgs){
|
|
|
233
234
|
}
|
|
234
235
|
async function clearSession(sid){await api(`/api/sessions/${encodeURIComponent(sid)}`,{method:'DELETE'});renderSessions()}
|
|
235
236
|
// 切入会话: 打开聊天页面并自动加载指定会话
|
|
236
|
-
|
|
237
|
-
|
|
237
|
+
// [v1.27.2] 使用 aid(数字 agent ID)+ s(编码 session ID),不再暴露明文 agent 名字
|
|
238
|
+
function enterSession(sid,agentDbId){
|
|
239
|
+
window.location.href='/ui/chat/chat_container.html?aid='+encodeURIComponent(agentDbId||1)+'&mode=exec&s='+UrlCodec.encode(sid);
|
|
238
240
|
}
|
|
239
241
|
|
|
240
242
|
if (typeof window._adminRenderers === 'undefined') window._adminRenderers = {};
|
package/web/ui/chat/chat_main.js
CHANGED
|
@@ -136,10 +136,12 @@ function popoutChat() {
|
|
|
136
136
|
if (isMobile()) return;
|
|
137
137
|
// Build URL with current agent + session + mode, plus popout flag
|
|
138
138
|
// Note: chat_container.html is always at /ui/chat/chat_container.html
|
|
139
|
+
// [v1.27.2] 使用 aid + s,不再暴露明文 agent 名字
|
|
139
140
|
const baseUrl = window.location.origin + '/ui/chat/chat_container.html';
|
|
140
141
|
const params = new URLSearchParams();
|
|
141
|
-
|
|
142
|
-
if (
|
|
142
|
+
var _popAgent = findAgentByPath(state.activeAgent);
|
|
143
|
+
if (_popAgent && _popAgent.aid) params.set('aid', _popAgent.aid);
|
|
144
|
+
if (state.activeSessionId && state.activeSessionId !== '__new__') params.set('s', UrlCodec.encode(state.activeSessionId));
|
|
143
145
|
if (state.chatMode) params.set('mode', state.chatMode);
|
|
144
146
|
params.set('popout', '1');
|
|
145
147
|
const popoutUrl = baseUrl + '?' + params.toString();
|
|
@@ -373,9 +375,12 @@ async function initChat() {
|
|
|
373
375
|
// [v1.16.12] 初始化附件上传 UI
|
|
374
376
|
initAttachmentUI();
|
|
375
377
|
|
|
376
|
-
// URL 参数处理: ?
|
|
378
|
+
// URL 参数处理: ?aid=数字ID&mode=exec&s=编码session&g=群聊ID&popout=1
|
|
379
|
+
// [v1.27.2] 使用 aid(数字 agent ID)代替明文 agent 名字
|
|
377
380
|
const urlParams = new URLSearchParams(window.location.search);
|
|
378
|
-
const
|
|
381
|
+
const urlAid = parseInt(urlParams.get('aid') || '', 10) || 0;
|
|
382
|
+
// 兼容旧格式 ?a= 编码 或 ?agent= 明文
|
|
383
|
+
const urlAgentLegacy = UrlCodec.decode(urlParams.get('a') || '') || UrlCodec.decode(urlParams.get('agent') || '');
|
|
379
384
|
const urlMode = urlParams.get('mode');
|
|
380
385
|
const urlSession = UrlCodec.decode(urlParams.get('s') || '') || UrlCodec.decode(urlParams.get('session') || '');
|
|
381
386
|
const isPopout = urlParams.get('popout') === '1';
|
|
@@ -463,25 +468,47 @@ async function initChat() {
|
|
|
463
468
|
await loadAgents();
|
|
464
469
|
|
|
465
470
|
// loadAgents 完成后,agent 列表和 sessions 已加载完毕
|
|
466
|
-
//
|
|
471
|
+
// [v1.27.2] 构建 aid → path 映射表
|
|
472
|
+
var _aidToPath = {};
|
|
473
|
+
state.agentsFlat.forEach(function(a) {
|
|
474
|
+
if (a.aid) _aidToPath[a.aid] = a.path;
|
|
475
|
+
});
|
|
476
|
+
// 从 URL 解析 agent: 优先 aid(数字),兼容 a/agent(编码/明文)
|
|
477
|
+
var urlAgent = '';
|
|
478
|
+
if (urlAid && _aidToPath[urlAid]) {
|
|
479
|
+
urlAgent = _aidToPath[urlAid];
|
|
480
|
+
} else if (urlAgentLegacy) {
|
|
481
|
+
urlAgent = urlAgentLegacy;
|
|
482
|
+
}
|
|
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
|
+
|
|
467
497
|
if (isPopout) {
|
|
468
498
|
const agentObj = findAgentByPath(urlAgent);
|
|
469
499
|
document.title = (agentObj ? agentObj.name : urlAgent || 'MyAgent') + ' - MyAgent';
|
|
470
500
|
}
|
|
471
501
|
|
|
472
502
|
// [v1.23.81] 群聊恢复逻辑:从 URL 的 ?g= 纯数字 session ID 恢复(优先级最高)
|
|
473
|
-
|
|
474
|
-
state._pendingGroupRestore = null; // 不再使用 localStorage 中的群聊状态
|
|
503
|
+
state._pendingGroupRestore = null;
|
|
475
504
|
|
|
476
505
|
async function _restoreGroupBySession(sessionId) {
|
|
477
|
-
// 通过 API 将纯数字 session ID 解析为 group_id
|
|
478
506
|
try {
|
|
479
507
|
var resolveData = await api('/api/group-session/' + encodeURIComponent(sessionId));
|
|
480
508
|
if (resolveData && resolveData.group_id) {
|
|
481
509
|
if (typeof groups !== 'undefined' && groups.length > 0) {
|
|
482
510
|
selectGroup(resolveData.group_id);
|
|
483
511
|
} else {
|
|
484
|
-
// groups 尚未加载,等待后重试
|
|
485
512
|
setTimeout(function() { _restoreGroupBySession(sessionId); }, 200);
|
|
486
513
|
}
|
|
487
514
|
} else {
|
|
@@ -494,14 +521,12 @@ async function initChat() {
|
|
|
494
521
|
|
|
495
522
|
// 如果 URL 中有 ?g= 纯数字 session ID,优先恢复群聊
|
|
496
523
|
if (_effectiveGroupSession && /^[0-9]+$/.test(_effectiveGroupSession)) {
|
|
497
|
-
// 群聊模式:如果有 from_agent 参数,更新 state.activeAgent 以便后续群聊私聊时使用
|
|
498
524
|
if (urlAgent && urlAgent !== state.activeAgent) {
|
|
499
525
|
state.activeAgent = urlAgent;
|
|
500
526
|
StatePersistence.save('activeAgent', urlAgent);
|
|
501
527
|
}
|
|
502
528
|
_restoreGroupBySession(_effectiveGroupSession);
|
|
503
529
|
} else if (urlAgent) {
|
|
504
|
-
// URL 指定了 agent,校验后切换
|
|
505
530
|
var resolved = urlAgent;
|
|
506
531
|
if (!findAgentByPath(resolved)) {
|
|
507
532
|
console.warn('URL agent not found, fallback to default:', resolved);
|
|
@@ -2798,11 +2823,17 @@ function newChat() {
|
|
|
2798
2823
|
state.activeSessionId = '__new__';
|
|
2799
2824
|
state._selectedSessionLabel = null; // [v1.18.9] 清除选中的会话名称
|
|
2800
2825
|
// ── 更新 URL(新对话移除 session 参数) ──
|
|
2826
|
+
// [v1.27.2] 使用 aid 代替 a
|
|
2801
2827
|
try {
|
|
2802
2828
|
const url = new URL(window.location.href);
|
|
2803
2829
|
url.searchParams.delete('s');
|
|
2804
2830
|
url.searchParams.delete('session');
|
|
2805
|
-
url.searchParams.
|
|
2831
|
+
url.searchParams.delete('a');
|
|
2832
|
+
url.searchParams.delete('agent');
|
|
2833
|
+
var _ncAgentObj = findAgentByPath(state.activeAgent);
|
|
2834
|
+
if (_ncAgentObj && _ncAgentObj.aid) {
|
|
2835
|
+
url.searchParams.set('aid', _ncAgentObj.aid);
|
|
2836
|
+
}
|
|
2806
2837
|
window.history.replaceState({}, '', url.toString());
|
|
2807
2838
|
} catch (_) {}
|
|
2808
2839
|
state.messages = [];
|
|
@@ -2948,10 +2979,19 @@ async function selectSession(id) {
|
|
|
2948
2979
|
state._msgLoadTotal = 0;
|
|
2949
2980
|
|
|
2950
2981
|
// ── 更新 URL 参数,保留会话 ID(刷新页面可恢复会话) ──
|
|
2982
|
+
// [v1.27.2] 使用 aid(数字)代替 a(编码),移除明文 agent/session 参数
|
|
2951
2983
|
try {
|
|
2952
2984
|
const url = new URL(window.location.href);
|
|
2953
2985
|
url.searchParams.set('s', UrlCodec.encode(id));
|
|
2954
|
-
|
|
2986
|
+
// 移除旧格式参数
|
|
2987
|
+
url.searchParams.delete('a');
|
|
2988
|
+
url.searchParams.delete('agent');
|
|
2989
|
+
url.searchParams.delete('session');
|
|
2990
|
+
// 设置 aid(数字 agent ID)
|
|
2991
|
+
var _selAgentObj = findAgentByPath(state.activeAgent);
|
|
2992
|
+
if (_selAgentObj && _selAgentObj.aid) {
|
|
2993
|
+
url.searchParams.set('aid', _selAgentObj.aid);
|
|
2994
|
+
}
|
|
2955
2995
|
if (state.chatMode) url.searchParams.set('mode', state.chatMode);
|
|
2956
2996
|
window.history.replaceState({}, '', url.toString());
|
|
2957
2997
|
} catch (_) {}
|
|
@@ -1648,7 +1648,12 @@ async function sendMessage(opts) {
|
|
|
1648
1648
|
try {
|
|
1649
1649
|
const url = new URL(window.location.href);
|
|
1650
1650
|
url.searchParams.set('s', UrlCodec.encode(sessionId));
|
|
1651
|
-
|
|
1651
|
+
// [v1.27.2] 使用 aid 代替 a
|
|
1652
|
+
url.searchParams.delete('a');
|
|
1653
|
+
url.searchParams.delete('agent');
|
|
1654
|
+
url.searchParams.delete('session');
|
|
1655
|
+
var _feAgentObj = (typeof findAgentByPath === 'function') ? findAgentByPath(state.activeAgent) : null;
|
|
1656
|
+
if (_feAgentObj && _feAgentObj.aid) url.searchParams.set('aid', _feAgentObj.aid);
|
|
1652
1657
|
window.history.replaceState({}, '', url.toString());
|
|
1653
1658
|
} catch (_) {}
|
|
1654
1659
|
}
|
|
@@ -2874,7 +2879,12 @@ const StreamingRecovery = {
|
|
|
2874
2879
|
try {
|
|
2875
2880
|
const url = new URL(window.location.href);
|
|
2876
2881
|
url.searchParams.set('s', UrlCodec.encode(sessionId));
|
|
2877
|
-
|
|
2882
|
+
// [v1.27.2] 使用 aid 代替 a
|
|
2883
|
+
url.searchParams.delete('a');
|
|
2884
|
+
url.searchParams.delete('agent');
|
|
2885
|
+
url.searchParams.delete('session');
|
|
2886
|
+
var _rvAgentObj = (typeof findAgentByPath === 'function') ? findAgentByPath(state.activeAgent) : null;
|
|
2887
|
+
if (_rvAgentObj && _rvAgentObj.aid) url.searchParams.set('aid', _rvAgentObj.aid);
|
|
2878
2888
|
window.history.replaceState({}, '', url.toString());
|
|
2879
2889
|
} catch (e) {
|
|
2880
2890
|
console.error('[Recovery] 更新URL失败:', e);
|