myagent-ai 1.32.8 → 1.32.10
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
|
@@ -3515,7 +3515,7 @@ window.addEventListener('beforeunload', function() {{
|
|
|
3515
3515
|
"""GET /api/agents - 返回扁平 agent 列表(系统 agent 排在最前)"""
|
|
3516
3516
|
await self._ensure_agents_initialized()
|
|
3517
3517
|
agents = self._scan_agents_flat()
|
|
3518
|
-
# 统计会话数
|
|
3518
|
+
# 统计会话数 + 附加数字 agent_id
|
|
3519
3519
|
if self.core.memory:
|
|
3520
3520
|
rows = self.core.memory._get_conn().execute(
|
|
3521
3521
|
"SELECT session_id, COUNT(*) as cnt FROM memories "
|
|
@@ -3527,6 +3527,7 @@ window.addEventListener('beforeunload', function() {{
|
|
|
3527
3527
|
session_counts[ap] = session_counts.get(ap, 0) + r["cnt"]
|
|
3528
3528
|
for a in agents:
|
|
3529
3529
|
a["session_count"] = session_counts.get(a["path"], 0)
|
|
3530
|
+
a["aid"] = self.core.memory.get_agent_id(a["path"])
|
|
3530
3531
|
# 系统 agent 排在最前
|
|
3531
3532
|
agents.sort(key=lambda a: (0 if a.get("system") else 1, a.get("path", "")))
|
|
3532
3533
|
return web.json_response(agents)
|
|
@@ -405,12 +405,19 @@ async function deleteAgentKB(path,filename){
|
|
|
405
405
|
|
|
406
406
|
async function loadAgentSessions(){
|
|
407
407
|
const path=window._currentEditAgentPath;if(!path)return;
|
|
408
|
+
// 查找该 agent 的数字 ID(aid)
|
|
409
|
+
var _editAgentAid = 1;
|
|
410
|
+
if (typeof allAgentsCache !== 'undefined' && Array.isArray(allAgentsCache)) {
|
|
411
|
+
for (var i = 0; i < allAgentsCache.length; i++) {
|
|
412
|
+
if (allAgentsCache[i].path === path) { _editAgentAid = allAgentsCache[i].aid || 1; break; }
|
|
413
|
+
}
|
|
414
|
+
}
|
|
408
415
|
const data=await api(`/api/agents/${encodeURIComponent(path)}/sessions`);
|
|
409
416
|
const sessions=Array.isArray(data)?data:(data?.sessions||[]);
|
|
410
417
|
let html=`<div class="flex justify-between items-center mb-16"><h4 style="font-size:14px;color:var(--text2)">会话 (${sessions.length})</h4></div>`;
|
|
411
418
|
if(sessions.length===0){html+='<div class="empty">暂无会话</div>';}
|
|
412
419
|
else{html+='<div class="table-wrap"><table><tr><th>会话</th><th>消息数</th><th>最后活动</th><th></th></tr>';
|
|
413
|
-
for(const s of sessions){const dn=s.display_name||s.id;html+=`<tr><td style="max-width:200px;overflow:hidden;text-overflow:ellipsis" title="${escHtml(s.id)}">${escHtml(dn.length>25?dn.slice(0,25)+'...':dn)}</td><td>${s.messages||0}</td><td>${fmtTimeAgo(s.last)}</td><td><button class="btn btn-sm" style="background:var(--success);color:#fff" onclick="enterSession('${escHtml(s.id)}'
|
|
420
|
+
for(const s of sessions){const dn=s.display_name||s.id;html+=`<tr><td style="max-width:200px;overflow:hidden;text-overflow:ellipsis" title="${escHtml(s.id)}">${escHtml(dn.length>25?dn.slice(0,25)+'...':dn)}</td><td>${s.messages||0}</td><td>${fmtTimeAgo(s.last)}</td><td><button class="btn btn-sm" style="background:var(--success);color:#fff" onclick="enterSession('${escHtml(s.id)}',${_editAgentAid})">切入</button> <button class="btn btn-sm btn-ghost" onclick="viewSessionMsgs('${escHtml(s.id)}')">查看</button></td></tr>`}
|
|
414
421
|
html+='</table></div>';}
|
|
415
422
|
$('sessionsContent').innerHTML=html;
|
|
416
423
|
}
|
|
@@ -11,18 +11,8 @@ async function renderSessions(){
|
|
|
11
11
|
}
|
|
12
12
|
let html='<div class="table-wrap"><table><tr><th>会话</th><th>Agent</th><th>消息数</th><th>最后活动</th><th>操作</th></tr>';
|
|
13
13
|
for(const s of (ss||[])){
|
|
14
|
-
//
|
|
14
|
+
// 使用后端返回的 agent_id(agent 名称)
|
|
15
15
|
let agentName=s.agent_id||'';
|
|
16
|
-
if(!agentName){
|
|
17
|
-
// 回退:从 session_id 前缀提取(兼容旧格式)
|
|
18
|
-
if((s.id||'').indexOf('_web_')>=0){
|
|
19
|
-
agentName=(s.id||'').split('_web_')[0]||'default';
|
|
20
|
-
}else if((s.id||'').indexOf('_cli_')>=0){
|
|
21
|
-
agentName=(s.id||'').split('_cli_')[0]||'default';
|
|
22
|
-
}else{
|
|
23
|
-
agentName='default';
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
16
|
const displayName=s.display_name||s.id;
|
|
27
17
|
const dbId=s.agent_db_id||1;
|
|
28
18
|
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>
|
package/web/ui/chat/chat_main.js
CHANGED
|
@@ -2815,13 +2815,9 @@ function newChat() {
|
|
|
2815
2815
|
state.activeSessionId = '__new__';
|
|
2816
2816
|
state._selectedSessionLabel = null; // [v1.18.9] 清除选中的会话名称
|
|
2817
2817
|
// ── 更新 URL(新对话移除 session 参数) ──
|
|
2818
|
-
// [v1.27.2] 使用 aid 代替 a
|
|
2819
2818
|
try {
|
|
2820
2819
|
const url = new URL(window.location.href);
|
|
2821
2820
|
url.searchParams.delete('s');
|
|
2822
|
-
url.searchParams.delete('session');
|
|
2823
|
-
url.searchParams.delete('a');
|
|
2824
|
-
url.searchParams.delete('agent');
|
|
2825
2821
|
var _ncAgentObj = findAgentByPath(state.activeAgent);
|
|
2826
2822
|
if (_ncAgentObj && _ncAgentObj.aid) {
|
|
2827
2823
|
url.searchParams.set('aid', _ncAgentObj.aid);
|
|
@@ -3530,6 +3526,8 @@ function groupHistoryMessages(messages) {
|
|
|
3530
3526
|
// ── [v1.23.20] 统一消息渲染函数:历史消息和流式消息共享 ──
|
|
3531
3527
|
// 生成单条消息的完整 HTML(message-row 外壳 + 所有内部内容)
|
|
3532
3528
|
// 流式结束后也用此函数重建 DOM,确保历史/流式样式完全一致
|
|
3529
|
+
// [v1.32.9] 流式结束后 _normalizeMessageAfterStreaming() 将 parts 转为 groupHistoryMessages() 格式,
|
|
3530
|
+
// 所以无论数据来源如何,此函数都走同一渲染分支
|
|
3533
3531
|
window.buildMessageHtml = function(msg, idx, agent) {
|
|
3534
3532
|
const isUser = msg.role === 'user';
|
|
3535
3533
|
if (msg.role === 'tool') return '';
|
|
@@ -1373,11 +1373,13 @@ function showToolResultModal(msgIndex, eventId) {
|
|
|
1373
1373
|
if (msg.exec_events) {
|
|
1374
1374
|
evt = msg.exec_events.find(e => String(e.id) === String(eventId));
|
|
1375
1375
|
}
|
|
1376
|
-
// 再从 parts 查找(V2
|
|
1376
|
+
// 再从 parts 查找(V2 格式或规范化后的 exec 格式)
|
|
1377
1377
|
if (!evt && msg.parts) {
|
|
1378
1378
|
for (const part of msg.parts) {
|
|
1379
|
-
|
|
1380
|
-
|
|
1379
|
+
// [v1.32.9] 同时支持 v2_tool 和规范化后的 exec 格式
|
|
1380
|
+
const inner = (part.type === 'v2_tool') ? part.data : (part.type === 'exec') ? part.data : null;
|
|
1381
|
+
if (inner && String(inner.id) === String(eventId)) {
|
|
1382
|
+
evt = inner;
|
|
1381
1383
|
break;
|
|
1382
1384
|
}
|
|
1383
1385
|
}
|
|
@@ -1532,6 +1534,112 @@ function _assembleV2Content(msg, msgParts) {
|
|
|
1532
1534
|
return '(无回复)';
|
|
1533
1535
|
}
|
|
1534
1536
|
|
|
1537
|
+
// ══════════════════════════════════════════════════════
|
|
1538
|
+
// ── [v1.32.9] Post-Streaming Normalization ──
|
|
1539
|
+
// ══════════════════════════════════════════════════════
|
|
1540
|
+
|
|
1541
|
+
/**
|
|
1542
|
+
* 流式结束后,将 msg.parts 规范化为与 groupHistoryMessages() 一致的格式。
|
|
1543
|
+
* 这确保 buildMessageHtml() 在流式和历史两条路径上走同一个渲染分支,
|
|
1544
|
+
* 避免视觉差异。
|
|
1545
|
+
*
|
|
1546
|
+
* 主要处理:
|
|
1547
|
+
* 1. v2_tool (tool_start + tool_result) → exec (合并卡片,与历史格式对齐)
|
|
1548
|
+
* 2. 仅含1个文本部分且无工具调用时 → 删除 parts(走简单气泡路径,与历史对齐)
|
|
1549
|
+
* 3. v2_ask → 保留原样
|
|
1550
|
+
*/
|
|
1551
|
+
function _normalizeMessageAfterStreaming(msg) {
|
|
1552
|
+
if (!msg || !Array.isArray(msg.parts) || msg.parts.length === 0) return;
|
|
1553
|
+
|
|
1554
|
+
// ── Step 1: 转换 v2_tool 为 exec 格式,并合并 tool_start + tool_result ──
|
|
1555
|
+
var normalizedParts = [];
|
|
1556
|
+
var evtIdCounter = 0;
|
|
1557
|
+
|
|
1558
|
+
for (var i = 0; i < msg.parts.length; i++) {
|
|
1559
|
+
var part = msg.parts[i];
|
|
1560
|
+
|
|
1561
|
+
if (part.type === 'v2_tool') {
|
|
1562
|
+
var inner = part.data || {};
|
|
1563
|
+
// tool_start: 创建 exec 卡片
|
|
1564
|
+
if (inner.type === 'tool_start' || inner.status === 'running') {
|
|
1565
|
+
var execPart = {
|
|
1566
|
+
type: 'exec',
|
|
1567
|
+
data: {
|
|
1568
|
+
id: evtIdCounter,
|
|
1569
|
+
type: 'tool_call',
|
|
1570
|
+
title: inner.title || inner.tool_name || '工具调用',
|
|
1571
|
+
tool_name: inner.tool_name || '',
|
|
1572
|
+
params: inner.params,
|
|
1573
|
+
status: 'done',
|
|
1574
|
+
has_result: false,
|
|
1575
|
+
result_data: null,
|
|
1576
|
+
}
|
|
1577
|
+
};
|
|
1578
|
+
// 向后搜索同一 tool_name 的 tool_result 进行合并
|
|
1579
|
+
for (var ri = i + 1; ri < msg.parts.length; ri++) {
|
|
1580
|
+
var rp = msg.parts[ri];
|
|
1581
|
+
if (rp.type === 'v2_tool' && rp.data && rp.data.type === 'tool_result') {
|
|
1582
|
+
var rInner = rp.data;
|
|
1583
|
+
if (rInner.tool_name === inner.tool_name || rInner.id === inner.id) {
|
|
1584
|
+
execPart.data.has_result = true;
|
|
1585
|
+
execPart.data.success = rInner.success;
|
|
1586
|
+
execPart.data.summary = rInner.summary ? rInner.summary.substring(0, 500).trim() : undefined;
|
|
1587
|
+
execPart.data.result = rInner.result || { output: rInner.summary || '' };
|
|
1588
|
+
if (rInner.title) execPart.data.title = rInner.title;
|
|
1589
|
+
// 标记已消费
|
|
1590
|
+
rp._consumed = true;
|
|
1591
|
+
break;
|
|
1592
|
+
}
|
|
1593
|
+
}
|
|
1594
|
+
// 遇到下一个 tool_start 就停止搜索
|
|
1595
|
+
if (rp.type === 'v2_tool' && rp.data && (rp.data.type === 'tool_start' || rp.data.status === 'running')) break;
|
|
1596
|
+
}
|
|
1597
|
+
evtIdCounter++;
|
|
1598
|
+
normalizedParts.push(execPart);
|
|
1599
|
+
} else if (inner.type === 'tool_result' && !part._consumed) {
|
|
1600
|
+
// 孤立的 tool_result(没有匹配的 tool_start)
|
|
1601
|
+
evtIdCounter++;
|
|
1602
|
+
normalizedParts.push({
|
|
1603
|
+
type: 'exec',
|
|
1604
|
+
data: {
|
|
1605
|
+
id: evtIdCounter,
|
|
1606
|
+
type: 'tool_result',
|
|
1607
|
+
title: inner.title || inner.tool_name || '工具结果',
|
|
1608
|
+
tool_name: inner.tool_name || '',
|
|
1609
|
+
success: inner.success,
|
|
1610
|
+
summary: inner.summary ? inner.summary.substring(0, 500).trim() : undefined,
|
|
1611
|
+
result: inner.result || { output: inner.summary || '' },
|
|
1612
|
+
has_result: true,
|
|
1613
|
+
}
|
|
1614
|
+
});
|
|
1615
|
+
}
|
|
1616
|
+
// 其他 v2_tool 类型(如已完成但非 start/result 的),也转为 exec
|
|
1617
|
+
else if (inner.type !== 'tool_start' && inner.type !== 'tool_result') {
|
|
1618
|
+
evtIdCounter++;
|
|
1619
|
+
normalizedParts.push({
|
|
1620
|
+
type: 'exec',
|
|
1621
|
+
data: Object.assign({}, inner, { id: evtIdCounter })
|
|
1622
|
+
});
|
|
1623
|
+
}
|
|
1624
|
+
} else {
|
|
1625
|
+
// text / exec / v2_ask 等类型保持不变
|
|
1626
|
+
normalizedParts.push(part);
|
|
1627
|
+
}
|
|
1628
|
+
}
|
|
1629
|
+
|
|
1630
|
+
// ── Step 2: 与 groupHistoryMessages() 对齐——决定是否保留 parts ──
|
|
1631
|
+
// groupHistoryMessages 逻辑: 仅当 hasExecParts || textParts.length > 1 时设置 parts
|
|
1632
|
+
var textParts = normalizedParts.filter(function(p) { return p.type === 'text'; });
|
|
1633
|
+
var hasExecParts = normalizedParts.some(function(p) { return p.type === 'exec'; });
|
|
1634
|
+
|
|
1635
|
+
if (hasExecParts || textParts.length > 1) {
|
|
1636
|
+
msg.parts = normalizedParts;
|
|
1637
|
+
} else {
|
|
1638
|
+
// 仅1段文本且无工具调用 → 删除 parts,走简单气泡渲染路径
|
|
1639
|
+
delete msg.parts;
|
|
1640
|
+
}
|
|
1641
|
+
}
|
|
1642
|
+
|
|
1535
1643
|
// ══════════════════════════════════════════════════════
|
|
1536
1644
|
// ── Voice Input: User Bubble Replacement ──
|
|
1537
1645
|
// ══════════════════════════════════════════════════════
|
|
@@ -1648,10 +1756,6 @@ async function sendMessage(opts) {
|
|
|
1648
1756
|
try {
|
|
1649
1757
|
const url = new URL(window.location.href);
|
|
1650
1758
|
url.searchParams.set('s', UrlCodec.encode(sessionId));
|
|
1651
|
-
// [v1.27.2] 使用 aid 代替 a
|
|
1652
|
-
url.searchParams.delete('a');
|
|
1653
|
-
url.searchParams.delete('agent');
|
|
1654
|
-
url.searchParams.delete('session');
|
|
1655
1759
|
var _feAgentObj = (typeof findAgentByPath === 'function') ? findAgentByPath(state.activeAgent) : null;
|
|
1656
1760
|
if (_feAgentObj && _feAgentObj.aid) url.searchParams.set('aid', _feAgentObj.aid);
|
|
1657
1761
|
window.history.replaceState({}, '', url.toString());
|
|
@@ -1903,6 +2007,8 @@ async function sendMessage(opts) {
|
|
|
1903
2007
|
state.messages[msgIdx].content = _assembleV2Content(state.messages[msgIdx], msgParts);
|
|
1904
2008
|
state.messages[msgIdx]._streamingText = '';
|
|
1905
2009
|
if (allExecEvents.length > 0) state.messages[msgIdx].exec_events = [...allExecEvents];
|
|
2010
|
+
// [v1.32.9] 规范化 parts 为 groupHistoryMessages() 格式
|
|
2011
|
+
_normalizeMessageAfterStreaming(state.messages[msgIdx]);
|
|
1906
2012
|
}
|
|
1907
2013
|
// Start new message
|
|
1908
2014
|
state.messages.push({ role: 'user', content: evt.message, time: new Date().toISOString() });
|
|
@@ -2312,6 +2418,8 @@ async function sendMessage(opts) {
|
|
|
2312
2418
|
state.messages[msgIdx].exec_events = allExecEvents;
|
|
2313
2419
|
// Assemble final content: prefer V2 reasoning/ask text over raw XML
|
|
2314
2420
|
state.messages[msgIdx].content = _assembleV2Content(state.messages[msgIdx], msgParts);
|
|
2421
|
+
// [v1.32.9] 规范化 parts 为 groupHistoryMessages() 格式,确保 buildMessageHtml() 渲染一致
|
|
2422
|
+
_normalizeMessageAfterStreaming(state.messages[msgIdx]);
|
|
2315
2423
|
}
|
|
2316
2424
|
|
|
2317
2425
|
// ── 流式传输完成,清除恢复数据 ──
|
|
@@ -2420,7 +2528,13 @@ async function sendMessage(opts) {
|
|
|
2420
2528
|
state.isGenerating = false;
|
|
2421
2529
|
state.abortController = null;
|
|
2422
2530
|
// 重置所有消息的流式标志
|
|
2423
|
-
state.messages.forEach(m => {
|
|
2531
|
+
state.messages.forEach(m => {
|
|
2532
|
+
if(m.streaming) {
|
|
2533
|
+
m.streaming = false;
|
|
2534
|
+
// [v1.32.9] 规范化 parts,确保异常结束时也和历史格式一致
|
|
2535
|
+
_normalizeMessageAfterStreaming(m);
|
|
2536
|
+
}
|
|
2537
|
+
});
|
|
2424
2538
|
hideTypingIndicator();
|
|
2425
2539
|
stopExecTimerPolling();
|
|
2426
2540
|
document.getElementById('sendBtn').style.display = '';
|
|
@@ -2879,10 +2993,6 @@ const StreamingRecovery = {
|
|
|
2879
2993
|
try {
|
|
2880
2994
|
const url = new URL(window.location.href);
|
|
2881
2995
|
url.searchParams.set('s', UrlCodec.encode(sessionId));
|
|
2882
|
-
// [v1.27.2] 使用 aid 代替 a
|
|
2883
|
-
url.searchParams.delete('a');
|
|
2884
|
-
url.searchParams.delete('agent');
|
|
2885
|
-
url.searchParams.delete('session');
|
|
2886
2996
|
var _rvAgentObj = (typeof findAgentByPath === 'function') ? findAgentByPath(state.activeAgent) : null;
|
|
2887
2997
|
if (_rvAgentObj && _rvAgentObj.aid) url.searchParams.set('aid', _rvAgentObj.aid);
|
|
2888
2998
|
window.history.replaceState({}, '', url.toString());
|