myagent-ai 1.20.0 → 1.20.1
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/agents/main_agent.py +2 -2
- package/package.json +1 -1
- package/web/api_server.py +17 -0
- package/web/ui/chat/chat_main.js +40 -4
- package/web/ui/chat/flow_engine.js +9 -0
- package/web/ui/index.html +32 -5
package/agents/main_agent.py
CHANGED
|
@@ -60,8 +60,8 @@ class MainAgent(BaseAgent):
|
|
|
60
60
|
<next_step>当 finish=false 时必填,描述下一步计划做什么(简洁明了,1-2句话)。finish=true 时为空。</next_step>
|
|
61
61
|
</output>
|
|
62
62
|
|
|
63
|
-
|
|
64
|
-
1. toolstocal标签:
|
|
63
|
+
注意事项:
|
|
64
|
+
1. toolstocal标签: 尽量一次性列出所有需执行工具调用的,多个"tool""工具调用只要按顺序重复堆叠tool标签即可,解析器会按顺序执行工具调用,最终全部执行完后,会连同所有结果,回调大语言模型。如果某个工具执行超时了,也会回调回调大模型,让大模型分析为什么超时,改用其他工具。如非必要,不要一次仅调用一个工具。
|
|
65
65
|
2. 上下文中的记忆系统说明
|
|
66
66
|
- <automemory>: 系统自动根据你通过 <remember> 保存的记忆和当前用户输入,搜索出的 top10 相关记忆。这些是你过去主动记住的内容(包含时间信息),可供参考。
|
|
67
67
|
- <recall_memory>: 你在上一轮通过 <recall> 指定的记忆搜索结果。系统根据你提供的关键字和时间点搜索了 top5 相关记忆。
|
package/package.json
CHANGED
package/web/api_server.py
CHANGED
|
@@ -2021,6 +2021,23 @@ window.toggleFullscreen = function() {{
|
|
|
2021
2021
|
"status": "done",
|
|
2022
2022
|
})
|
|
2023
2023
|
|
|
2024
|
+
# ── 硬上限:任务列表不允许超过 MAX_TASK_ITEMS 条 ──
|
|
2025
|
+
# 系统提示词虽然要求 LLM 精简到 8 条,但 LLM 经常忽略,
|
|
2026
|
+
# 加上 merge 保留 done 项,会导致列表无限增长。
|
|
2027
|
+
# 策略:优先保留未完成项 + 最近完成的项,移除最早的已完成项
|
|
2028
|
+
MAX_TASK_ITEMS = 10
|
|
2029
|
+
if len(merged) > MAX_TASK_ITEMS:
|
|
2030
|
+
# 分离未完成和已完成
|
|
2031
|
+
unfinished = [t for t in merged if t.get("status") != "done"]
|
|
2032
|
+
finished = [t for t in merged if t.get("status") == "done"]
|
|
2033
|
+
# 保留所有未完成项 + 尽可能多的已完成项(保留最新的)
|
|
2034
|
+
remaining = MAX_TASK_ITEMS - len(unfinished)
|
|
2035
|
+
if remaining > 0:
|
|
2036
|
+
finished = finished[-remaining:] # 保留最近完成的
|
|
2037
|
+
else:
|
|
2038
|
+
finished = []
|
|
2039
|
+
merged = unfinished + finished
|
|
2040
|
+
|
|
2024
2041
|
return merged
|
|
2025
2042
|
|
|
2026
2043
|
async def handle_get_task_plan(self, request):
|
package/web/ui/chat/chat_main.js
CHANGED
|
@@ -1998,9 +1998,45 @@ function newChat() {
|
|
|
1998
1998
|
var _sessionPollTimers = {};
|
|
1999
1999
|
function _pollSessionCompletion(sessionId) {
|
|
2000
2000
|
if (_sessionPollTimers[sessionId]) clearInterval(_sessionPollTimers[sessionId]);
|
|
2001
|
+
var _lastMsgCount = state.messages.length;
|
|
2001
2002
|
_sessionPollTimers[sessionId] = setInterval(async function() {
|
|
2002
2003
|
try {
|
|
2003
2004
|
const data = await api('/api/session/status?sid=' + encodeURIComponent(sessionId));
|
|
2005
|
+
// 每次轮询都重新加载消息,实时显示执行过程中产生的中间输出
|
|
2006
|
+
try {
|
|
2007
|
+
const msgs = await api('/api/session/messages?sid=' + encodeURIComponent(sessionId) + '&limit=500');
|
|
2008
|
+
if (Array.isArray(msgs) && msgs.length > 0) {
|
|
2009
|
+
var loaded = msgs.filter(function(m) {
|
|
2010
|
+
return m && (m.role === 'user' || m.role === 'assistant' || m.role === 'tool');
|
|
2011
|
+
}).map(function(m) {
|
|
2012
|
+
var content = (m.content != null) ? String(m.content) : '';
|
|
2013
|
+
var mkey = (m.key || '').toLowerCase();
|
|
2014
|
+
if (m.role === 'assistant' && mkey !== 'tool_call' && mkey !== 'reasoning' && content && content.trim().startsWith('<')) {
|
|
2015
|
+
content = (typeof _stripXmlTags === 'function') ? _stripXmlTags(content) : content;
|
|
2016
|
+
}
|
|
2017
|
+
var mapped = {
|
|
2018
|
+
role: m.role || 'assistant',
|
|
2019
|
+
content: content,
|
|
2020
|
+
time: m.time || m.created_at || '',
|
|
2021
|
+
key: m.key || '',
|
|
2022
|
+
};
|
|
2023
|
+
if (m.images && m.images.length > 0) mapped.images = m.images;
|
|
2024
|
+
if (m.files && m.files.length > 0) mapped.files = m.files;
|
|
2025
|
+
return mapped;
|
|
2026
|
+
});
|
|
2027
|
+
var newMsgs = groupHistoryMessages(loaded);
|
|
2028
|
+
if (newMsgs.length !== _lastMsgCount) {
|
|
2029
|
+
// 只在有新消息时才重新渲染,避免闪烁
|
|
2030
|
+
state.messages = newMsgs;
|
|
2031
|
+
_lastMsgCount = newMsgs.length;
|
|
2032
|
+
renderMessages();
|
|
2033
|
+
// 自动滚动到底部,显示最新输出
|
|
2034
|
+
var mi = document.getElementById('messagesInner');
|
|
2035
|
+
if (mi) mi.scrollTop = mi.scrollHeight;
|
|
2036
|
+
}
|
|
2037
|
+
}
|
|
2038
|
+
} catch (_) {}
|
|
2039
|
+
|
|
2004
2040
|
if (!data.running || data.done) {
|
|
2005
2041
|
clearInterval(_sessionPollTimers[sessionId]);
|
|
2006
2042
|
delete _sessionPollTimers[sessionId];
|
|
@@ -2009,7 +2045,7 @@ function _pollSessionCompletion(sessionId) {
|
|
|
2009
2045
|
document.getElementById('sendBtn').style.display = '';
|
|
2010
2046
|
document.getElementById('sendBtn').disabled = !document.getElementById('userInput').value.trim();
|
|
2011
2047
|
document.getElementById('stopBtn').style.display = 'none';
|
|
2012
|
-
//
|
|
2048
|
+
// 最终重新加载消息以获取完整内容
|
|
2013
2049
|
await selectSession(sessionId);
|
|
2014
2050
|
if (data.error) {
|
|
2015
2051
|
toast('⚠️ 后台任务完成但出错: ' + data.error, 'error');
|
|
@@ -2681,17 +2717,17 @@ function _renderMessagesInner() {
|
|
|
2681
2717
|
</details>`;
|
|
2682
2718
|
})() : '';
|
|
2683
2719
|
const _isSpeakingThis = ttsManager && ttsManager.isPlaying && ttsManager.currentMsgIndex === i;
|
|
2684
|
-
const actionBtns =
|
|
2720
|
+
const actionBtns = msg.content ? `
|
|
2685
2721
|
<div class="msg-actions">
|
|
2686
2722
|
<button class="msg-action-btn" onclick="copyMessage(${i})" title="复制">
|
|
2687
2723
|
<svg viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" stroke-width="2"><rect x="9" y="9" width="13" height="13" rx="2" ry="2"/><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"/></svg>
|
|
2688
|
-
</button
|
|
2724
|
+
</button>${!isUser ? `
|
|
2689
2725
|
<button class="msg-action-btn${_isSpeakingThis ? ' tts-speaking' : ''}" onclick="speakMessage(${i})" title="${_isSpeakingThis ? '停止朗读' : '朗读'}">
|
|
2690
2726
|
${_isSpeakingThis
|
|
2691
2727
|
? '<svg viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" stroke-width="2"><polygon points="11 5 6 9 2 9 2 15 6 15 11 19 11 5"/><line x1="23" y1="9" x2="17" y2="15"/><line x1="17" y1="9" x2="23" y2="15"/></svg>'
|
|
2692
2728
|
: '<svg viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" stroke-width="2"><polygon points="11 5 6 9 2 9 2 15 6 15 11 19 11 5"/><path d="M19.07 4.93a10 10 0 0 1 0 14.14"/><path d="M15.54 8.46a5 5 0 0 1 0 7.07"/></svg>'
|
|
2693
2729
|
}
|
|
2694
|
-
</button
|
|
2730
|
+
</button>` : ''}
|
|
2695
2731
|
</div>` : '';
|
|
2696
2732
|
const ttsIndicator = _isSpeakingThis ?
|
|
2697
2733
|
' <span class="tts-playing-icon"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polygon points="11 5 6 9 2 9 2 15 6 15 11 19 11 5"/><path d="M15.54 8.46a5 5 0 0 1 0 7.07"/></svg></span>' : '';
|
|
@@ -1620,6 +1620,15 @@ async function sendMessage(opts) {
|
|
|
1620
1620
|
// 任务列表 JSON 直推更新(exec 模式)
|
|
1621
1621
|
// 如果已收到 finish 标签,忽略后续 task_list_update,防止覆盖已清空的任务列表
|
|
1622
1622
|
if (evt.tasks && Array.isArray(evt.tasks) && !_finishReceived) {
|
|
1623
|
+
// 前端安全上限:防止服务端限制失效时列表无限增长
|
|
1624
|
+
if (evt.tasks.length > 15) {
|
|
1625
|
+
var unfinished = evt.tasks.filter(function(t) { return t.status !== 'done'; });
|
|
1626
|
+
var finished = evt.tasks.filter(function(t) { return t.status === 'done'; });
|
|
1627
|
+
var remaining = 15 - unfinished.length;
|
|
1628
|
+
if (remaining > 0) finished = finished.slice(-remaining);
|
|
1629
|
+
else finished = [];
|
|
1630
|
+
evt.tasks = unfinished.concat(finished);
|
|
1631
|
+
}
|
|
1623
1632
|
state.taskItems = evt.tasks;
|
|
1624
1633
|
renderTaskList();
|
|
1625
1634
|
var panel = document.getElementById('taskPanel');
|
package/web/ui/index.html
CHANGED
|
@@ -274,6 +274,25 @@ tr:hover{background:var(--surface2)}
|
|
|
274
274
|
/* 任务表格优化 */
|
|
275
275
|
.table-wrap table{min-width:auto}
|
|
276
276
|
.table-wrap td[style*="max-width:300px"]{max-width:150px!important;white-space:normal!important;word-break:break-all}
|
|
277
|
+
/* ── 会话管理移动端:隐藏"最后活动"列,操作按钮堆叠 ── */
|
|
278
|
+
#content[data-page="sessions"] .table-wrap th:nth-child(4),
|
|
279
|
+
#content[data-page="sessions"] .table-wrap td:nth-child(4){display:none}
|
|
280
|
+
#content[data-page="sessions"] .table-wrap td:last-child{white-space:normal;min-width:auto}
|
|
281
|
+
#content[data-page="sessions"] .table-wrap td:last-child .btn{display:inline-block;margin:2px}
|
|
282
|
+
/* ── 任务记录移动端:隐藏"来源"列,操作按钮堆叠 ── */
|
|
283
|
+
#content[data-page="tasks"] .table-wrap th:nth-child(3),
|
|
284
|
+
#content[data-page="tasks"] .table-wrap td:nth-child(3){display:none}
|
|
285
|
+
#content[data-page="tasks"] .table-wrap td:last-child{white-space:normal;min-width:auto}
|
|
286
|
+
#content[data-page="tasks"] .table-wrap td:last-child .btn{display:inline-block;margin:2px}
|
|
287
|
+
/* ── 会话查看:消息气泡在窄屏撑满宽度 ── */
|
|
288
|
+
.msg-bubble-wrap{max-width:75%}
|
|
289
|
+
#content[data-page="sessions"] .msg-bubble-wrap{max-width:88%!important}
|
|
290
|
+
/* ── 任务计划卡片移动端堆叠 ── */
|
|
291
|
+
.card .flex.justify-between{flex-direction:column;align-items:flex-start;gap:6px}
|
|
292
|
+
/* ── 时间索引自适应换行 ── */
|
|
293
|
+
.raw-time-nav{display:flex;flex-wrap:wrap;align-items:center;gap:4px 8px;margin-bottom:8px}
|
|
294
|
+
.raw-time-link{color:var(--accent);text-decoration:none;font-size:12px;padding:2px 6px;border-radius:4px;background:var(--surface2);white-space:nowrap;transition:background .15s}
|
|
295
|
+
.raw-time-link:hover{background:var(--accent);color:#fff}
|
|
277
296
|
}
|
|
278
297
|
@media(max-width:480px){
|
|
279
298
|
.content{padding:12px}
|
|
@@ -287,6 +306,12 @@ tr:hover{background:var(--surface2)}
|
|
|
287
306
|
.form-group{margin-bottom:10px}
|
|
288
307
|
.form-group label{font-size:12px}
|
|
289
308
|
.raw-collapsed{max-height:0 !important;padding:0 10px !important;overflow:hidden}
|
|
309
|
+
/* ── 480px:任务表进一步精简,隐藏任务ID列 ── */
|
|
310
|
+
#content[data-page="tasks"] .table-wrap th:nth-child(1),
|
|
311
|
+
#content[data-page="tasks"] .table-wrap td:nth-child(1){display:none}
|
|
312
|
+
/* ── 480px:会话表隐藏 Agent 列 ── */
|
|
313
|
+
#content[data-page="sessions"] .table-wrap th:nth-child(2),
|
|
314
|
+
#content[data-page="sessions"] .table-wrap td:nth-child(2){display:none}
|
|
290
315
|
}
|
|
291
316
|
</style>
|
|
292
317
|
</head>
|
|
@@ -443,6 +468,7 @@ function showConfirm(title,msg,onOk){
|
|
|
443
468
|
function showPage(page, addHistory){
|
|
444
469
|
closeMobileSidebar();
|
|
445
470
|
currentPage=page;
|
|
471
|
+
$('content').setAttribute('data-page',page);
|
|
446
472
|
document.querySelectorAll('.nav-item').forEach((n,i)=>n.classList.toggle('active',Object.keys(pages)[i]===page));
|
|
447
473
|
$('pageTitle').textContent=pages[page]||page;
|
|
448
474
|
const renderers={dashboard:renderDashboard,agents:renderAgents,platforms:renderPlatforms,organization:renderOrganization,departments:renderDepartments,sessions:renderSessions,memory:renderMemory,permissions:renderPermissions,llm:renderLLM,system:renderSystem,executor:renderExecutor,skills:renderSkills,files:renderFiles,logs:renderLogs,tasks:renderTasks};
|
|
@@ -468,6 +494,7 @@ function navigateTo(page, sub, renderFn){
|
|
|
468
494
|
if(_navHistory.length>6)_navHistory.shift();
|
|
469
495
|
currentPage=page;
|
|
470
496
|
_navSubState=sub;
|
|
497
|
+
$('content').setAttribute('data-page',page);
|
|
471
498
|
// 更新 URL hash
|
|
472
499
|
var hash=page+(sub?'~'+sub:'');
|
|
473
500
|
history.pushState({page:page,sub:sub},'','#'+hash);
|
|
@@ -1073,7 +1100,7 @@ async function viewSessionMsgs(sid){
|
|
|
1073
1100
|
const displayContent=content.length>500?content.slice(0,500)+'...':content;
|
|
1074
1101
|
html+=`<div style="margin:8px 0;display:flex;gap:8px;${align}">`;
|
|
1075
1102
|
if(!isUser)html+=`<div style="flex-shrink:0;font-size:16px;margin-top:2px">${avatar}</div>`;
|
|
1076
|
-
html+=`<div style="
|
|
1103
|
+
html+=`<div class="msg-bubble-wrap" style="min-width:0"><div style="font-size:11px;color:var(--text3);margin-bottom:2px">${isUser?'用户':'助手'} <span style="margin-left:4px">${time}</span></div><div style="background:${bg};color:${fg};padding:10px 14px;border-radius:var(--radius);font-size:13px;line-height:1.6;white-space:pre-wrap;word-break:break-word">${escHtml(displayContent)}</div></div>`;
|
|
1077
1104
|
if(isUser)html+=`<div style="flex-shrink:0;font-size:16px;margin-top:2px">${avatar}</div>`;
|
|
1078
1105
|
html+=`</div>`;
|
|
1079
1106
|
}
|
|
@@ -1311,7 +1338,7 @@ async function _loadSessionMessages(){
|
|
|
1311
1338
|
const displayContent=long?content.slice(0,500)+'... (共'+content.length+'字符)':content;
|
|
1312
1339
|
html+=`<div style="margin:8px 0;display:flex;gap:8px;${isUser?'flex-direction:row-reverse':''}">`;
|
|
1313
1340
|
html+=`<div style="flex-shrink:0;font-size:16px;margin-top:2px">${avatar}</div>`;
|
|
1314
|
-
html+=`<div style="
|
|
1341
|
+
html+=`<div class="msg-bubble-wrap" style="min-width:0"><div style="font-size:11px;color:var(--text3);margin-bottom:2px;${isUser?'text-align:right':''}">${isUser?'用户':'助手'} <span style="margin-left:4px">${time}</span></div>`;
|
|
1315
1342
|
html+=`<div style="background:${bg};color:${fg};padding:10px 14px;border-radius:var(--radius);font-size:13px;line-height:1.6;white-space:pre-wrap;word-break:break-word">${escHtml(displayContent)}</div>`;
|
|
1316
1343
|
if(long)html+=`<button class="btn btn-sm btn-ghost" style="margin-top:4px;font-size:11px" onclick="this.previousElementSibling.textContent=this.dataset.full;this.remove()" data-full="${escHtml(content).replace(/"/g,'"')}">展开全部 (${content.length}字符)</button>`;
|
|
1317
1344
|
html+=`</div></div>`;
|
|
@@ -1351,7 +1378,7 @@ async function viewSessionRaw(sid){
|
|
|
1351
1378
|
}
|
|
1352
1379
|
html+=`</div>`;
|
|
1353
1380
|
// 时间索引导航
|
|
1354
|
-
html+=`<div
|
|
1381
|
+
html+=`<div class="raw-time-nav" id="rawTimeNav"></div>`;
|
|
1355
1382
|
// 消息列表
|
|
1356
1383
|
html+=`<div style="max-height:65vh;overflow-y:auto;font-family:monospace" id="rawMsgList">`;
|
|
1357
1384
|
for(let i=0;i<msgs.length;i++){
|
|
@@ -1419,9 +1446,9 @@ function _buildTimeNav(msgs){
|
|
|
1419
1446
|
}
|
|
1420
1447
|
const times=Object.keys(minutes);
|
|
1421
1448
|
if(times.length<=1)return;
|
|
1422
|
-
let navHtml='
|
|
1449
|
+
let navHtml='<span style="color:var(--text3);font-size:12px">时间索引:</span> ';
|
|
1423
1450
|
for(const t of times){
|
|
1424
|
-
navHtml+=`<a href="javascript:void(0)"
|
|
1451
|
+
navHtml+=`<a href="javascript:void(0)" class="raw-time-link" onclick="document.querySelectorAll('.raw-item')[${minutes[t][0]}].scrollIntoView({behavior:'smooth',block:'center'})">${escHtml(t.slice(11))}</a>`;
|
|
1425
1452
|
}
|
|
1426
1453
|
nav.innerHTML=navHtml;
|
|
1427
1454
|
}
|