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.
@@ -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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "myagent-ai",
3
- "version": "1.20.0",
3
+ "version": "1.20.1",
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
@@ -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):
@@ -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 = (!isUser && msg.content) ? `
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="max-width:75%;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>`;
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="max-width:75%;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>`;
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,'&quot;')}">展开全部 (${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 style="margin-bottom:8px;font-size:12px;color:var(--text3)" id="rawTimeNav"></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)" style="color:var(--accent);margin-right:8px;text-decoration:none" onclick="document.querySelectorAll('.raw-item')[${minutes[t][0]}].scrollIntoView({behavior:'smooth',block:'center'})">${escHtml(t.slice(11))}</a>`;
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
  }