myagent-ai 1.23.51 → 1.23.54

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.
@@ -298,6 +298,7 @@ class MainAgent(BaseAgent):
298
298
  agent_name=_injected_name or self.name,
299
299
  agent_description=_injected_desc or self.description,
300
300
  agent_override_prompt=_override_prompt,
301
+ agent_path=getattr(self, '_agent_override_path', None),
301
302
  )
302
303
  finally:
303
304
  # 移除活跃上下文
@@ -598,6 +599,7 @@ class MainAgent(BaseAgent):
598
599
  stream_callback: Optional[Callable] = None,
599
600
  stream_response=None,
600
601
  text_delta_callback=None,
602
+ agent_path: Optional[str] = None,
601
603
  ) -> AgentContext:
602
604
  """
603
605
  V2 主处理循环 — 使用结构化输出格式。
@@ -619,6 +621,7 @@ class MainAgent(BaseAgent):
619
621
  stream_callback: 可选的 SSE 事件回调 (callable 或 async callable)
620
622
  stream_response: 可选的流式响应对象(用于 LLM 流式输出)
621
623
  text_delta_callback: 可选的文本增量回调
624
+ agent_path: Agent 路径(用于独立工作目录)
622
625
  """
623
626
  task_id = context.task_id or generate_id("task")
624
627
  context.task_id = task_id
@@ -631,7 +634,8 @@ class MainAgent(BaseAgent):
631
634
  try:
632
635
  return await self._process_v2_inner(
633
636
  context, task_id, agent_name, agent_description,
634
- agent_override_prompt, stream_callback, stream_response, text_delta_callback
637
+ agent_override_prompt, stream_callback, stream_response, text_delta_callback,
638
+ agent_path=agent_path,
635
639
  )
636
640
  except Exception as e:
637
641
  logger.error(f"[{task_id}] V2 执行循环异常: {e}", exc_info=True)
@@ -649,6 +653,7 @@ class MainAgent(BaseAgent):
649
653
  stream_callback: Optional[Callable] = None,
650
654
  stream_response=None,
651
655
  text_delta_callback=None,
656
+ agent_path: Optional[str] = None,
652
657
  ) -> AgentContext:
653
658
  """V2 内部循环逻辑 — 结构化输出 + 工具调度 + SSE 事件推送"""
654
659
 
@@ -778,6 +783,7 @@ class MainAgent(BaseAgent):
778
783
  get_knowledge=get_knowledge_content,
779
784
  recall=recall_content,
780
785
  memory_context_prompt=_memory_ctx_prompt,
786
+ agent_path=agent_path,
781
787
  )
782
788
 
783
789
  await self._emit_v2_event(
@@ -82,7 +82,7 @@ class ChatBotManager:
82
82
  asyncio.ensure_future(self._safe_stop(key, bot))
83
83
  else:
84
84
  loop.run_until_complete(bot.stop())
85
- except RuntimeError:
85
+ except Exception:
86
86
  pass
87
87
  if task and not task.done():
88
88
  task.cancel()
@@ -126,6 +126,7 @@ class ContextBuilder:
126
126
  get_knowledge: str = "",
127
127
  recall: str = "",
128
128
  memory_context_prompt: str = "",
129
+ agent_path: Optional[str] = None,
129
130
  ) -> str:
130
131
  """
131
132
  构建完整的 <context> XML 字符串。
@@ -143,6 +144,7 @@ class ContextBuilder:
143
144
  用于 RAG 搜索知识库。为空时使用用户消息作为查询。
144
145
  recall: 上一轮 LLM 输出的 <recall> 内容,
145
146
  用于定向检索长期记忆。为空时仅用用户消息搜索。
147
+ agent_path: Agent 路径(如 "default"),用于定位独立工作目录。
146
148
 
147
149
  Returns:
148
150
  完整的 <context>...</context> XML 字符串
@@ -162,7 +164,7 @@ class ContextBuilder:
162
164
 
163
165
  sections: List[str] = [
164
166
  self._build_datetime(),
165
- self._build_whomi(agent_name, agent_description, agent_override_prompt),
167
+ self._build_whomi(agent_name, agent_description, agent_override_prompt, agent_path=agent_path),
166
168
  self._build_memory(query, session_id, recall, memory_context_prompt),
167
169
  self._build_knowledge(kb_query),
168
170
  # 轻量近期对话兜底:最近 3 轮对话摘要,补充 automemory 搜索的盲区
@@ -216,6 +218,7 @@ class ContextBuilder:
216
218
  agent_name: str,
217
219
  agent_description: str,
218
220
  agent_override_prompt: Optional[str] = None,
221
+ agent_path: Optional[str] = None,
219
222
  ) -> str:
220
223
  """
221
224
  构建 <whomi> 段落 —— Agent 身份信息。
@@ -226,6 +229,7 @@ class ContextBuilder:
226
229
  agent_name: Agent 名称
227
230
  agent_description: Agent 描述
228
231
  agent_override_prompt: 可选的覆盖提示词
232
+ agent_path: Agent 路径(用于定位独立工作目录)
229
233
 
230
234
  Returns:
231
235
  <whomi> XML 段落字符串
@@ -239,8 +243,8 @@ class ContextBuilder:
239
243
  f"描述: {safe_desc}",
240
244
  ]
241
245
 
242
- # [v1.20.8] 注入工作目录信息,让 Agent 知道文件应保存到哪里
243
- work_dir = self._get_workspace_dir()
246
+ # [v1.20.8] [v1.23.52] 注入工作目录信息,每个 Agent 使用独立工作目录
247
+ work_dir = self._get_workspace_dir(agent_path=agent_path)
244
248
  if work_dir:
245
249
  parts.append(f"工作目录: {_xml_escape(str(work_dir))}")
246
250
  parts.append(f"文件保存说明: 通过 file_write 或代码生成的文件请保存到工作目录下的 userfiles 子目录。发送文件给用户请使用 file_send 工具。")
@@ -251,14 +255,31 @@ class ContextBuilder:
251
255
  parts.append("</whomi>")
252
256
  return "\n".join(parts)
253
257
 
254
- def _get_workspace_dir(self) -> Optional[str]:
255
- """[v1.20.8] 获取工作目录路径(~/.myagent/data/workspace)"""
258
+ def _get_workspace_dir(self, agent_path: Optional[str] = None) -> Optional[str]:
259
+ """[v1.20.8] [v1.23.52] 获取工作目录路径
260
+
261
+ 如果指定了 agent_path 且非默认 agent,则返回该 Agent 独立的工作目录:
262
+ ~/.myagent/data/agents/{agent_path}/workspace/
263
+ 否则返回全局工作目录:
264
+ ~/.myagent/data/workspace/
265
+ 独立目录下自动创建 userfiles 子目录。
266
+ """
256
267
  try:
257
268
  from config import ConfigManager
269
+ import os
258
270
  cm = ConfigManager()
259
- wd = cm.data_dir / "workspace"
260
- wd.mkdir(parents=True, exist_ok=True)
261
- return str(wd)
271
+ if agent_path and agent_path != "default":
272
+ # 使用 Agent 独立工作目录
273
+ wd = cm.data_dir / "agents" / agent_path / "workspace"
274
+ wd.mkdir(parents=True, exist_ok=True)
275
+ userfiles = wd / "userfiles"
276
+ userfiles.mkdir(parents=True, exist_ok=True)
277
+ return str(wd)
278
+ else:
279
+ # 默认 agent 或无 agent_path 时使用全局工作目录
280
+ wd = cm.data_dir / "workspace"
281
+ wd.mkdir(parents=True, exist_ok=True)
282
+ return str(wd)
262
283
  except Exception:
263
284
  return None
264
285
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "myagent-ai",
3
- "version": "1.23.51",
3
+ "version": "1.23.54",
4
4
  "description": "本地桌面端执行型AI助手 - Open Interpreter 风格 | Local Desktop Execution-Oriented AI Assistant",
5
5
  "main": "main.py",
6
6
  "bin": {
@@ -43,4 +43,4 @@
43
43
  "python": ">=3.10",
44
44
  "node": ">=18"
45
45
  }
46
- }
46
+ }
package/start.js CHANGED
@@ -793,6 +793,8 @@ function main() {
793
793
  "memory",
794
794
  // 媒体播放
795
795
  "playaudio", "playvideo",
796
+ // Agent 通信
797
+ "chat",
796
798
  // 帮助
797
799
  "help", "-h", "--help",
798
800
  ];
package/web/api_server.py CHANGED
@@ -4083,8 +4083,11 @@ window.addEventListener('beforeunload', function() {{
4083
4083
  data = await request.json()
4084
4084
  cp.enabled = data.get("enabled", not cp.enabled)
4085
4085
  self.core.config_mgr.save()
4086
- # 热更新聊天平台
4087
- await self._hot_reload_chat_platforms()
4086
+ # [v1.23.53] 热更新聊天平台(加异常保护,防止热更新失败导致配置无法保存)
4087
+ try:
4088
+ await self._hot_reload_chat_platforms()
4089
+ except Exception as e:
4090
+ logger.error(f"热更新聊天平台失败: {e}", exc_info=True)
4088
4091
  logger.info(f"聊天平台 {cp.display_name} 已{'启用' if cp.enabled else '禁用'}")
4089
4092
  return web.json_response({"ok": True, "enabled": cp.enabled, "id": cp.id, "hot_reload": True})
4090
4093
 
@@ -5532,6 +5535,16 @@ window.addEventListener('beforeunload', function() {{
5532
5535
  if agent_system_prompt and self.core.main_agent:
5533
5536
  self.core.main_agent._agent_override_prompt = agent_system_prompt
5534
5537
  self.core.main_agent._agent_override_path = agent_path
5538
+ # [v1.23.52] 为非默认 Agent 设置独立工作目录(executor 层面)
5539
+ _original_exec_work_dir = None
5540
+ if agent_path and agent_path != "default" and self.core.main_agent and self.core.main_agent.executor:
5541
+ from config import ConfigManager
5542
+ _cm = ConfigManager()
5543
+ _agent_wd = _cm.data_dir / "agents" / agent_path / "workspace"
5544
+ _agent_wd.mkdir(parents=True, exist_ok=True)
5545
+ (_agent_wd / "userfiles").mkdir(parents=True, exist_ok=True)
5546
+ _original_exec_work_dir = self.core.main_agent.executor.work_dir
5547
+ self.core.main_agent.executor.work_dir = str(_agent_wd)
5535
5548
  # [v1.23.35] 注入 Agent 名称和描述(群聊路径需要传递给 process_v2)
5536
5549
  # 确保 process() → process_v2() 拿到正确的 agent_name/agent_description
5537
5550
  if agent_path and self.core.main_agent:
@@ -5563,6 +5576,9 @@ window.addEventListener('beforeunload', function() {{
5563
5576
  self.core.main_agent._agent_override_name = None
5564
5577
  self.core.main_agent._agent_override_description = None
5565
5578
  self.core.main_agent._chat_mode = ""
5579
+ # [v1.23.52] 恢复执行引擎原始工作目录
5580
+ if _original_exec_work_dir is not None and self.core.main_agent.executor:
5581
+ self.core.main_agent.executor.work_dir = _original_exec_work_dir
5566
5582
  if self.core.main_agent.context_builder:
5567
5583
  self.core.main_agent.context_builder.agent_knowledge_dir = None
5568
5584
 
@@ -5840,14 +5856,25 @@ window.addEventListener('beforeunload', function() {{
5840
5856
  context.metadata["user_file_paths"] = _file_paths
5841
5857
  logger.info(f"[{session_id}] 用户发送了 {len(user_files)} 个文件,提取文本 {len(file_context) if _file_texts else 0} 字符")
5842
5858
 
5843
- # ── 根据 Agent 配置设置执行引擎参数(execution_mode 等)──
5859
+ # ── 根据 Agent 配置设置执行引擎参数(execution_mode、work_dir 等)──
5844
5860
  agent_cfg_for_exec = self._read_agent_config(agent_path)
5845
5861
  _original_exec_mode = None
5862
+ _original_work_dir = None
5846
5863
  if agent_cfg_for_exec and agent.executor:
5847
5864
  _exec_mode = agent_cfg_for_exec.get("execution_mode")
5848
5865
  if _exec_mode:
5849
5866
  _original_exec_mode = agent.executor.execution_mode
5850
5867
  agent.executor.set_execution_mode(_exec_mode)
5868
+ # [v1.23.52] 为非默认 Agent 设置独立工作目录
5869
+ if agent_path and agent_path != "default" and agent.executor:
5870
+ from config import ConfigManager
5871
+ cm = ConfigManager()
5872
+ agent_work_dir = cm.data_dir / "agents" / agent_path / "workspace"
5873
+ agent_work_dir.mkdir(parents=True, exist_ok=True)
5874
+ (agent_work_dir / "userfiles").mkdir(parents=True, exist_ok=True)
5875
+ _original_work_dir = agent.executor.work_dir
5876
+ agent.executor.work_dir = str(agent_work_dir)
5877
+ logger.info(f"[{session_id}] Agent [{agent_path}] 使用独立工作目录: {agent_work_dir}")
5851
5878
 
5852
5879
  # ── 设置 Agent 专属知识库目录(优先于组织知识库)──
5853
5880
  if agent_path and agent.context_builder:
@@ -5940,6 +5967,7 @@ window.addEventListener('beforeunload', function() {{
5940
5967
  agent_override_prompt=agent_system_prompt,
5941
5968
  stream_callback=_v2_collecting_callback,
5942
5969
  stream_response=stream_response,
5970
+ agent_path=agent_path,
5943
5971
  )
5944
5972
  finally:
5945
5973
  # 无论成功或异常,都清理 active_contexts
@@ -5947,6 +5975,9 @@ window.addEventListener('beforeunload', function() {{
5947
5975
  # 恢复执行引擎原始模式(防止影响后续 Agent 请求)
5948
5976
  if _original_exec_mode is not None and agent.executor:
5949
5977
  agent.executor.set_execution_mode(_original_exec_mode)
5978
+ # [v1.23.52] 恢复执行引擎原始工作目录
5979
+ if _original_work_dir is not None and agent.executor:
5980
+ agent.executor.work_dir = _original_work_dir
5950
5981
  # 清理 Agent 专属知识库目录设置(防止影响其他 Agent 请求)
5951
5982
  if agent.context_builder:
5952
5983
  agent.context_builder.agent_knowledge_dir = None
@@ -27,10 +27,12 @@ async function renderPlatforms(){
27
27
  html+='</div>';$('content').innerHTML=html;
28
28
  }
29
29
  async function togglePlatform(id,enable){
30
- const r=await api(`/api/platforms/${id}`,{method:'PUT',body:JSON.stringify({enabled})});
31
- if(r.error){showToast('操作失败: '+r.error,'danger');return}
32
- showToast(enable?'平台已启用':'平台已停用',enable?'success':'info');
33
- renderPlatforms();
30
+ try{
31
+ var r=await api('/api/platforms/'+encodeURIComponent(id)+'/toggle',{method:'POST',body:JSON.stringify({enabled})});
32
+ if(r.error){showToast('操作失败: '+r.error,'danger');return}
33
+ showToast(enable?'平台已启用':'平台已停用',enable?'success':'info');
34
+ renderPlatforms();
35
+ }catch(e){showToast('请求异常: '+e.message,'danger')}
34
36
  }
35
37
  function showAddPlatformModal(){
36
38
  $('modalContainer').innerHTML=`<div class="modal-overlay" onclick="closeModal()"><div class="modal" onclick="event.stopPropagation()" style="max-width:520px">
@@ -1218,6 +1218,15 @@ input,textarea,select{font:inherit}
1218
1218
  font-size:12px;color:#fff;flex-shrink:0;
1219
1219
  }
1220
1220
 
1221
+ /* [v1.23.51] Department group chat button */
1222
+ .rp-dept-group-btn{
1223
+ margin-left:auto;flex-shrink:0;padding:2px 6px;border-radius:10px;
1224
+ font-size:11px;line-height:1;border:1px solid var(--border);
1225
+ background:var(--bg2);cursor:pointer;color:var(--text2);
1226
+ transition:var(--transition);
1227
+ }
1228
+ .rp-dept-group-btn:hover{background:var(--accent);color:#fff;border-color:var(--accent)}
1229
+
1221
1230
  /* Agent list */
1222
1231
  .agent-list{flex:1;overflow-y:auto;padding:0}
1223
1232
 
@@ -1424,10 +1424,12 @@ function renderRpDeptNode(dept, depth) {
1424
1424
  }
1425
1425
  var indent = depth * 20;
1426
1426
  var agentCount = dept.agent_count || 0;
1427
+ var chatGroupId = dept.chat_group_id || '';
1428
+ var hasGroupChat = !!chatGroupId;
1427
1429
 
1428
1430
  // Department node itself — clicking anywhere on the item expands/collapses to show agents
1429
1431
  var deptOnclick = hasChildren ? ' onclick="toggleRpDept(\'' + escapeHtml(dept.path) + '\')"' : '';
1430
- html += '<div class="rp-dept-item" style="padding-left:' + (12 + indent) + 'px"' + deptOnclick + '>';
1432
+ html += '<div class="rp-dept-item" style="padding-left:' + (12 + indent) + 'px;display:flex;align-items:center;gap:4px"' + deptOnclick + '>';
1431
1433
  if (hasChildren) {
1432
1434
  html += '<span class="rp-section-toggle ' + (isExpanded ? 'expanded' : '') + '" onclick="event.stopPropagation();toggleRpDept(\'' + escapeHtml(dept.path) + '\')">▶</span>';
1433
1435
  } else {
@@ -1437,6 +1439,10 @@ function renderRpDeptNode(dept, depth) {
1437
1439
  html += '<span style="font-weight:500">' + escapeHtml(dept.name) + '</span>';
1438
1440
  html += ' <span style="font-size:10px;color:var(--text3)">(' + agentCount + '成员)</span>';
1439
1441
  if (dept.head) html += ' <span style="font-size:10px;color:var(--accent)">👑</span>';
1442
+ // [v1.23.51] 部门节点直接显示群聊入口按钮
1443
+ if (hasGroupChat) {
1444
+ html += ' <button class="rp-dept-group-btn" onclick="event.stopPropagation();openDeptGroupChat(\'' + escapeHtml(chatGroupId) + '\')" title="进入部门群聊">💬</button>';
1445
+ }
1440
1446
  html += '</div>';
1441
1447
 
1442
1448
  // Children (sub-departments + agents) - if expanded
@@ -1555,6 +1561,11 @@ async function selectAgent(agentPath) {
1555
1561
  if (dot) dot.style.display = '';
1556
1562
  document.getElementById('userInput').placeholder = '输入消息... (Enter 发送, Shift+Enter 换行)';
1557
1563
  }
1564
+ // [v1.23.51] 恢复侧边栏 UI(从群聊切回个人聊天)
1565
+ var _newChatBtn = document.querySelector('.new-chat-btn');
1566
+ if (_newChatBtn) _newChatBtn.style.display = '';
1567
+ var _searchInput = document.getElementById('searchInput');
1568
+ if (_searchInput) { _searchInput.placeholder = '搜索对话...'; _searchInput.value = ''; }
1558
1569
  // 移动端:立即关闭右侧栏,不要等异步操作完成
1559
1570
  if (isMobile()) closeMobileAgentPanel();
1560
1571
  // Always reload sessions even if clicking the same agent
@@ -2089,6 +2100,30 @@ function formatSessionName(id) {
2089
2100
 
2090
2101
  function renderSessions(filter = '') {
2091
2102
  const list = document.getElementById('sessionList');
2103
+ // [v1.23.51] 群聊模式下,左侧显示群聊列表而非个人会话
2104
+ if (currentView === 'group' && typeof groups !== 'undefined' && groups.length > 0) {
2105
+ var html = '';
2106
+ for (var i = 0; i < groups.length; i++) {
2107
+ var g = groups[i];
2108
+ var isActive = g.id === currentGroupId;
2109
+ var memberCount = (g.members || []).length;
2110
+ html += '<div class="session-item ' + (isActive ? 'active' : '') + '" onclick="selectGroup(\'' + escapeHtml(g.id) + '\')" title="' + escapeHtml(g.name) + '">'
2111
+ + '<div class="session-icon" style="font-size:16px">' + escapeHtml(g.avatar_emoji || g.emoji || '👥') + '</div>'
2112
+ + '<div class="session-info">'
2113
+ + '<div class="session-name">' + escapeHtml(g.name) + '</div>'
2114
+ + '<div class="session-preview">' + escapeHtml(g.description || (memberCount + ' 位成员')) + '</div>'
2115
+ + '</div>'
2116
+ + '</div>';
2117
+ }
2118
+ // 在列表顶部添加"返回个人聊天"按钮
2119
+ html = '<div class="session-item" onclick="exitGroupChat()" title="返回个人聊天" style="border-bottom:1px solid var(--border);margin-bottom:4px">'
2120
+ + '<div class="session-icon" style="font-size:16px">🔙</div>'
2121
+ + '<div class="session-info"><div class="session-name">返回个人聊天</div><div class="session-preview">退出群聊视图</div></div>'
2122
+ + '</div>' + html;
2123
+ list.innerHTML = html;
2124
+ return;
2125
+ }
2126
+
2092
2127
  const fl = filter.toLowerCase();
2093
2128
  const filtered = state.sessions.filter(s =>
2094
2129
  s.id !== '__new__' && (!fl || s.name.toLowerCase().includes(fl) || s.id.toLowerCase().includes(fl))
@@ -173,6 +173,12 @@ async function selectGroup(gid) {
173
173
  StatePersistence.save('currentGroupId', gid);
174
174
  }
175
175
 
176
+ // [v1.23.51] 群聊模式:隐藏"新对话"按钮,搜索框改为搜索群聊
177
+ var newChatBtn = document.querySelector('.new-chat-btn');
178
+ if (newChatBtn) newChatBtn.style.display = 'none';
179
+ var searchInput = document.getElementById('searchInput');
180
+ if (searchInput) { searchInput.placeholder = '搜索群聊...'; searchInput.value = ''; }
181
+
176
182
  // Update sidebar
177
183
  renderSessions();
178
184
  renderGroups();
@@ -237,6 +243,12 @@ function exitGroupChat() {
237
243
  state.messages = [];
238
244
  state.activeSessionId = null;
239
245
 
246
+ // [v1.23.51] 恢复侧边栏 UI
247
+ var newChatBtn = document.querySelector('.new-chat-btn');
248
+ if (newChatBtn) newChatBtn.style.display = '';
249
+ var searchInput = document.getElementById('searchInput');
250
+ if (searchInput) { searchInput.placeholder = '搜索对话...'; searchInput.value = ''; }
251
+
240
252
  // Restore header
241
253
  document.getElementById('headerTitle').textContent = '新对话';
242
254
  document.getElementById('activeAgentBadge').style.display = '';