myagent-ai 1.15.50 → 1.15.52

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.
@@ -6,6 +6,7 @@ agents/main_agent.py - 主 Agent
6
6
  from __future__ import annotations
7
7
 
8
8
  import asyncio
9
+ import json
9
10
  import re
10
11
  from typing import Any, Callable, Dict, List, Optional
11
12
 
@@ -576,6 +577,21 @@ class MainAgent(BaseAgent):
576
577
  content=context.user_message or "请处理上述上下文。"
577
578
  ))
578
579
 
580
+ # [v1.15.52] 保存完整的 LLM 输入消息(system prompt + user message + 工具结果回调)
581
+ # 用于 Raw 查看器完整回溯 LLM 交互过程
582
+ if self.memory:
583
+ _input_parts = []
584
+ for _msg in messages:
585
+ _input_parts.append(f"=== {_msg.role.upper()} ===\n{_msg.content}")
586
+ _llm_input_text = "\n\n".join(_input_parts)
587
+ self.memory.add_session(
588
+ session_id=context.session_id,
589
+ role="system",
590
+ content=_llm_input_text,
591
+ key="llm_input",
592
+ importance=0.3,
593
+ )
594
+
579
595
  if stream_response and self.llm:
580
596
  response = await self._call_llm_stream(
581
597
  messages, text_delta_callback=text_delta_callback,
@@ -1193,6 +1209,8 @@ class MainAgent(BaseAgent):
1193
1209
  key="tool_call",
1194
1210
  importance=0.4,
1195
1211
  )
1212
+ # [v1.15.52] 保存完整的工具执行原始结果(不截断,原汁原味)
1213
+ _raw_result_str = json.dumps(tool_result, ensure_ascii=False, indent=2) if tool_result else str(tool_result)
1196
1214
  self.memory.add_session(
1197
1215
  session_id=context.session_id,
1198
1216
  role="tool",
@@ -1200,6 +1218,14 @@ class MainAgent(BaseAgent):
1200
1218
  key="tool_result",
1201
1219
  importance=0.4,
1202
1220
  )
1221
+ # 额外保存原始完整 JSON(用于 Raw 查看器完整回溯)
1222
+ self.memory.add_session(
1223
+ session_id=context.session_id,
1224
+ role="tool",
1225
+ content=f"[{tool_name}] 原始结果 ({_status})\n{truncate_str(_raw_result_str, 45000)}",
1226
+ key="tool_result_raw",
1227
+ importance=0.2,
1228
+ )
1203
1229
 
1204
1230
  # 任一工具超时 → 立即停止执行剩余工具
1205
1231
  if _has_timeout:
package/memory/manager.py CHANGED
@@ -299,7 +299,7 @@ class MemoryManager:
299
299
  # 只排除纯内部审计条目,保留 tool_call/tool_result 供前端展示
300
300
  sql = """SELECT * FROM memories
301
301
  WHERE session_id = ? AND category = 'session' AND role != ''
302
- AND key NOT IN ('llm_output', 'conversation_insight')
302
+ AND key NOT IN ('llm_output', 'llm_input', 'tool_result_raw', 'conversation_insight')
303
303
  ORDER BY created_at ASC LIMIT ?"""
304
304
  rows = conn.execute(sql, (session_id, limit)).fetchall()
305
305
  entries = [MemoryEntry.from_row(row) for row in rows]
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "myagent-ai",
3
- "version": "1.15.50",
3
+ "version": "1.15.52",
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
@@ -2499,14 +2499,14 @@ class ApiServer:
2499
2499
  rows = self.core.memory._get_conn().execute(
2500
2500
  "SELECT DISTINCT session_id, COUNT(*) as cnt, MAX(created_at) as last FROM memories "
2501
2501
  "WHERE category = 'session' AND role != '' "
2502
- "AND key NOT IN ('llm_output', 'conversation_insight') "
2502
+ "AND key NOT IN ('llm_output', 'llm_input', 'tool_result_raw', 'conversation_insight') "
2503
2503
  "AND session_id LIKE ? GROUP BY session_id ORDER BY last DESC LIMIT 100",
2504
2504
  (prefix + "%",)).fetchall()
2505
2505
  else:
2506
2506
  rows = self.core.memory._get_conn().execute(
2507
2507
  "SELECT DISTINCT session_id, COUNT(*) as cnt, MAX(created_at) as last FROM memories "
2508
2508
  "WHERE category = 'session' AND role != '' "
2509
- "AND key NOT IN ('llm_output', 'conversation_insight') "
2509
+ "AND key NOT IN ('llm_output', 'llm_input', 'tool_result_raw', 'conversation_insight') "
2510
2510
  "GROUP BY session_id ORDER BY last DESC LIMIT 100").fetchall()
2511
2511
  sessions = [{"id": r["session_id"], "messages": r["cnt"], "last": r["last"]} for r in rows]
2512
2512
  # 批量获取自定义会话名称
@@ -2559,7 +2559,7 @@ class ApiServer:
2559
2559
  return web.json_response({**agent_info, "sessions": sessions})
2560
2560
 
2561
2561
  # Internal keys that should not appear in chat history UI
2562
- _HIDDEN_KEYS = {"llm_output"}
2562
+ _HIDDEN_KEYS = {"llm_output", "llm_input", "tool_result_raw"}
2563
2563
 
2564
2564
  async def handle_get_messages(self, request):
2565
2565
  sid = request.match_info["sid"]
package/web/ui/index.html CHANGED
@@ -354,7 +354,7 @@ function showConfirm(title,msg,onOk){
354
354
  <button class="btn btn-ghost" onclick="closeModal()">取消</button></div></div></div>`;
355
355
  }
356
356
 
357
- function showPage(page){
357
+ function showPage(page, addHistory){
358
358
  closeMobileSidebar();
359
359
  currentPage=page;
360
360
  document.querySelectorAll('.nav-item').forEach((n,i)=>n.classList.toggle('active',Object.keys(pages)[i]===page));
@@ -363,8 +363,69 @@ function showPage(page){
363
363
  if(renderers[page]){
364
364
  try{renderers[page]();}catch(e){console.error('Page render error:',e);$('content').innerHTML='<div class="empty" style="color:var(--danger)">页面加载失败: '+escHtml(e.message)+'</div>';}
365
365
  }
366
+ // 记录到浏览器历史
367
+ if(addHistory!==false){
368
+ var _sub=window._navSubState||null;
369
+ var hash=page+(_sub?'~'+_sub:'');
370
+ history.pushState({page:page,sub:_sub},'','#'+hash);
371
+ }
372
+ }
373
+
374
+ // 导航历史系统:记录子页面跳转(如 viewSession、viewSessionRaw)
375
+ // _navHistory: [{page,sub}] 最多6步
376
+ var _navHistory=[];
377
+ var _navSubState=null;
378
+
379
+ function navigateTo(page, sub, renderFn){
380
+ // 保存当前状态到历史
381
+ _navHistory.push({page:currentPage,sub:window._navSubState});
382
+ if(_navHistory.length>6)_navHistory.shift();
383
+ currentPage=page;
384
+ _navSubState=sub;
385
+ // 更新 URL hash
386
+ var hash=page+(sub?'~'+sub:'');
387
+ history.pushState({page:page,sub:sub},'','#'+hash);
388
+ // 更新导航高亮
389
+ document.querySelectorAll('.nav-item').forEach((n,i)=>n.classList.toggle('active',Object.keys(pages)[i]===page));
390
+ // 渲染页面
391
+ if(renderFn){
392
+ try{renderFn();}catch(e){console.error('Navigate render error:',e);}
393
+ }
366
394
  }
367
395
 
396
+ function goBack(){
397
+ if(_navHistory.length>0){
398
+ var prev=_navHistory.pop();
399
+ currentPage=prev.page;
400
+ _navSubState=prev.sub;
401
+ var hash=prev.page+(prev.sub?'~'+prev.sub:'');
402
+ history.pushState({page:prev.page,sub:prev.sub},'','#'+hash);
403
+ // 找到对应的渲染函数
404
+ var 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};
405
+ if(renderers[prev.page]){
406
+ try{renderers[prev.page]();}catch(e){}
407
+ }
408
+ document.querySelectorAll('.nav-item').forEach((n,i)=>n.classList.toggle('active',Object.keys(pages)[i]===prev.page));
409
+ }else{
410
+ // 没有内部历史,尝试浏览器后退
411
+ history.back();
412
+ }
413
+ }
414
+
415
+ // 浏览器前进/后退按钮支持
416
+ window.addEventListener('popstate',function(e){
417
+ var s=e.state;
418
+ if(s&&s.page){
419
+ currentPage=s.page;
420
+ _navSubState=s.sub||null;
421
+ document.querySelectorAll('.nav-item').forEach((n,i)=>n.classList.toggle('active',Object.keys(pages)[i]===s.page));
422
+ var 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};
423
+ if(renderers[s.page]){
424
+ try{renderers[s.page]();}catch(e){}
425
+ }
426
+ }
427
+ });
428
+
368
429
  // ========== Dashboard ==========
369
430
  async function renderDashboard(){
370
431
  const s=await api('/api/status');const m=await api('/api/memory/stats');
@@ -933,7 +994,8 @@ async function renderSessions(){
933
994
  }
934
995
  async function viewSession(sid){
935
996
  window._viewSessionSid=sid;window._viewSessionOffset=0;
936
- await _loadSessionMessages();
997
+ _navSubState='view:'+sid;
998
+ navigateTo('sessions','view:'+sid,_loadSessionMessages);
937
999
  }
938
1000
  async function _loadSessionMessages(){
939
1001
  const sid=window._viewSessionSid;if(!sid)return;
@@ -977,17 +1039,25 @@ async function _loadSessionMessages(){
977
1039
  }
978
1040
  html+='</div>';
979
1041
  if(hasMore)html+=`<button class="btn btn-ghost mt-8" onclick="window._viewSessionOffset=${offset+100};_loadSessionMessages()">加载更多...</button>`;
980
- html+='<div class="flex gap-8 mt-8"><button class="btn btn-ghost" onclick="renderSessions()">返回</button>';
1042
+ html+='<div class="flex gap-8 mt-8"><button class="btn btn-ghost" onclick="goBack()">返回</button>';
981
1043
  html+=`<button class="btn" style="background:var(--accent);color:#fff" onclick="viewSessionRaw('${escHtml(sid)}')">Raw 原始消息</button>`;
982
1044
  html+=`<button class="btn btn-primary" onclick="enterSession('${escHtml(sid)}','${escHtml(sid.split('_web_')[0]||'default')}')">在聊天中查看完整记录</button></div>`;
983
1045
  $('content').innerHTML=html;
984
1046
  }
985
1047
  // ========== Raw 原始消息查看 ==========
986
1048
  async function viewSessionRaw(sid){
1049
+ window._viewSessionSid=sid;
1050
+ _navSubState='raw:'+sid;
1051
+ // 不用 navigateTo 因为需要先 fetch 数据再渲染,直接记录历史后继续
1052
+ _navHistory.push({page:currentPage,sub:window._navSubState});
1053
+ if(_navHistory.length>6)_navHistory.shift();
1054
+ var hash='sessions'+'~raw:'+sid;
1055
+ history.pushState({page:'sessions',sub:'raw:'+sid},'','#'+hash);
1056
+
987
1057
  const msgs=await api(`/api/session/raw?sid=${encodeURIComponent(sid)}&limit=5000`);
988
1058
  if(!Array.isArray(msgs)){showToast('加载失败','danger');return}
989
1059
  // 按时间分组(同秒内合并)
990
- const keyLabelMap={'llm_output':'LLM 输出','tool_call':'工具调用','tool_result':'工具结果','reasoning':'推理过程','conversation_insight':'会话洞察','':'对话','llm_request':'LLM 请求'};
1060
+ const keyLabelMap={'llm_output':'LLM 输出','llm_input':'LLM 输入','tool_call':'工具调用','tool_result':'工具结果','tool_result_raw':'工具原始数据','reasoning':'推理过程','conversation_insight':'会话洞察','':'对话','llm_request':'LLM 请求'};
991
1061
  let html=`<h3 style="margin-bottom:12px">Raw: ${escHtml(sid)} <span class="badge badge-blue">${msgs.length} 条</span></h3>`;
992
1062
  // 筛选按钮
993
1063
  html+=`<div style="margin-bottom:10px;display:flex;gap:6px;flex-wrap:wrap" id="rawFilterBar">`;
@@ -1014,9 +1084,11 @@ async function viewSessionRaw(sid){
1014
1084
  // 颜色标识
1015
1085
  let borderColor='var(--border)';
1016
1086
  let bgColor='var(--surface)';
1017
- if(key==='llm_output'){borderColor='#e6a817';bgColor='#1a1700'}
1087
+ if(key==='llm_input'){borderColor='#06b6d4';bgColor='#001a1f'}
1088
+ else if(key==='llm_output'){borderColor='#e6a817';bgColor='#1a1700'}
1018
1089
  else if(key==='tool_call'){borderColor='#3b82f6';bgColor='#001029'}
1019
1090
  else if(key==='tool_result'){borderColor='#22c55e';bgColor='#001a0d'}
1091
+ else if(key==='tool_result_raw'){borderColor='#10b981';bgColor='#001510'}
1020
1092
  else if(key==='reasoning'){borderColor='#a855f7';bgColor='#0d0020'}
1021
1093
  else if(role==='user'){borderColor='var(--primary)';bgColor='var(--surface)'}
1022
1094
  else if(role==='assistant'){borderColor='#6b7280';bgColor='var(--surface2)'}
@@ -1034,8 +1106,8 @@ async function viewSessionRaw(sid){
1034
1106
  html+=`</div></div>`;
1035
1107
  }
1036
1108
  html+=`</div>`;
1037
- html+=`<div class="flex gap-8 mt-8"><button class="btn btn-ghost" onclick="viewSession('${escHtml(sid)}')">返回查看</button>`;
1038
- html+=`<button class="btn btn-ghost" onclick="renderSessions()">返回会话列表</button></div>`;
1109
+ html+=`<div class="flex gap-8 mt-8"><button class="btn btn-ghost" onclick="goBack()">返回查看</button>`;
1110
+ html+=`<button class="btn btn-ghost" onclick="goBack()">返回会话列表</button></div>`;
1039
1111
  $('content').innerHTML=html;
1040
1112
  // 生成时间索引
1041
1113
  _buildTimeNav(msgs);
@@ -1141,7 +1213,7 @@ async function searchMemory(){
1141
1213
  }else{
1142
1214
  html+='<div class="empty">未找到匹配的记忆</div>';
1143
1215
  }
1144
- html+='<button class="btn btn-ghost mt-8" onclick="renderMemory()">返回</button>';$('content').innerHTML=html;
1216
+ html+='<button class="btn btn-ghost mt-8" onclick="goBack()">返回</button>';$('content').innerHTML=html;
1145
1217
  }
1146
1218
  async function deleteMemory(id){await api(`/api/memory/${id}`,{method:'DELETE'});renderMemory()}
1147
1219
  async function cleanupMemory(){const r=await api('/api/memory/cleanup',{method:'POST'});showToast('清理了 '+(r.cleaned||0)+' 条','success');renderMemory()}
@@ -2066,9 +2138,22 @@ async function sysLoadPreview(){
2066
2138
 
2067
2139
  // Init
2068
2140
  (function(){
2069
- var params=new URLSearchParams(window.location.search);
2070
- var page=params.get('page')||'dashboard';
2071
- if(pages[page])showPage(page);else showPage('dashboard');
2141
+ var hash=window.location.hash.slice(1)||'';
2142
+ var page=hash?hash.split('~')[0]:'';
2143
+ var sub=hash?hash.slice(page.length+1):'';
2144
+ // 兼容旧 URL query param ?page=xxx
2145
+ if(!page){
2146
+ var params=new URLSearchParams(window.location.search);
2147
+ page=params.get('page')||'dashboard';
2148
+ }
2149
+ if(sub)_navSubState=sub;
2150
+ if(pages[page]){
2151
+ history.replaceState({page:page,sub:sub||null},'','#'+page+(sub?'~'+sub:''));
2152
+ showPage(page,false);
2153
+ }else{
2154
+ history.replaceState({page:'dashboard',sub:null},'','#dashboard');
2155
+ showPage('dashboard',false);
2156
+ }
2072
2157
  })();
2073
2158
  setInterval(()=>{api('/api/status').catch(()=>{})},30000);
2074
2159