myagent-ai 1.23.81 → 1.23.82

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.
Files changed (139) hide show
  1. package/core/agent_storage.py +0 -0
  2. package/data/novnc/lib/base64.js +0 -0
  3. package/data/novnc/lib/decoders/copyrect.js +0 -0
  4. package/data/novnc/lib/decoders/hextile.js +0 -0
  5. package/data/novnc/lib/decoders/jpeg.js +0 -0
  6. package/data/novnc/lib/decoders/raw.js +0 -0
  7. package/data/novnc/lib/decoders/rre.js +0 -0
  8. package/data/novnc/lib/decoders/tight.js +0 -0
  9. package/data/novnc/lib/decoders/tightpng.js +0 -0
  10. package/data/novnc/lib/decoders/zrle.js +0 -0
  11. package/data/novnc/lib/deflator.js +0 -0
  12. package/data/novnc/lib/des.js +0 -0
  13. package/data/novnc/lib/display.js +0 -0
  14. package/data/novnc/lib/encodings.js +0 -0
  15. package/data/novnc/lib/inflator.js +0 -0
  16. package/data/novnc/lib/input/domkeytable.js +0 -0
  17. package/data/novnc/lib/input/fixedkeys.js +0 -0
  18. package/data/novnc/lib/input/gesturehandler.js +0 -0
  19. package/data/novnc/lib/input/keyboard.js +0 -0
  20. package/data/novnc/lib/input/keysym.js +0 -0
  21. package/data/novnc/lib/input/keysymdef.js +0 -0
  22. package/data/novnc/lib/input/util.js +0 -0
  23. package/data/novnc/lib/input/vkeys.js +0 -0
  24. package/data/novnc/lib/input/xtscancodes.js +0 -0
  25. package/data/novnc/lib/ra2.js +0 -0
  26. package/data/novnc/lib/rfb.js +0 -0
  27. package/data/novnc/lib/util/browser.js +0 -0
  28. package/data/novnc/lib/util/cursor.js +0 -0
  29. package/data/novnc/lib/util/element.js +0 -0
  30. package/data/novnc/lib/util/events.js +0 -0
  31. package/data/novnc/lib/util/eventtarget.js +0 -0
  32. package/data/novnc/lib/util/int.js +0 -0
  33. package/data/novnc/lib/util/logging.js +0 -0
  34. package/data/novnc/lib/util/md5.js +0 -0
  35. package/data/novnc/lib/util/strings.js +0 -0
  36. package/data/novnc/lib/vendor/pako/lib/utils/common.js +0 -0
  37. package/data/novnc/lib/vendor/pako/lib/zlib/adler32.js +0 -0
  38. package/data/novnc/lib/vendor/pako/lib/zlib/constants.js +0 -0
  39. package/data/novnc/lib/vendor/pako/lib/zlib/crc32.js +0 -0
  40. package/data/novnc/lib/vendor/pako/lib/zlib/deflate.js +0 -0
  41. package/data/novnc/lib/vendor/pako/lib/zlib/gzheader.js +0 -0
  42. package/data/novnc/lib/vendor/pako/lib/zlib/inffast.js +0 -0
  43. package/data/novnc/lib/vendor/pako/lib/zlib/inflate.js +0 -0
  44. package/data/novnc/lib/vendor/pako/lib/zlib/inftrees.js +0 -0
  45. package/data/novnc/lib/vendor/pako/lib/zlib/messages.js +0 -0
  46. package/data/novnc/lib/vendor/pako/lib/zlib/trees.js +0 -0
  47. package/data/novnc/lib/vendor/pako/lib/zlib/zstream.js +0 -0
  48. package/data/novnc/lib/websock.js +0 -0
  49. package/myagent/core/agent_storage.py +0 -0
  50. package/myagent/data/novnc/lib/base64.js +0 -0
  51. package/myagent/data/novnc/lib/decoders/copyrect.js +0 -0
  52. package/myagent/data/novnc/lib/decoders/hextile.js +0 -0
  53. package/myagent/data/novnc/lib/decoders/jpeg.js +0 -0
  54. package/myagent/data/novnc/lib/decoders/raw.js +0 -0
  55. package/myagent/data/novnc/lib/decoders/rre.js +0 -0
  56. package/myagent/data/novnc/lib/decoders/tight.js +0 -0
  57. package/myagent/data/novnc/lib/decoders/tightpng.js +0 -0
  58. package/myagent/data/novnc/lib/decoders/zrle.js +0 -0
  59. package/myagent/data/novnc/lib/deflator.js +0 -0
  60. package/myagent/data/novnc/lib/des.js +0 -0
  61. package/myagent/data/novnc/lib/display.js +0 -0
  62. package/myagent/data/novnc/lib/encodings.js +0 -0
  63. package/myagent/data/novnc/lib/inflator.js +0 -0
  64. package/myagent/data/novnc/lib/input/domkeytable.js +0 -0
  65. package/myagent/data/novnc/lib/input/fixedkeys.js +0 -0
  66. package/myagent/data/novnc/lib/input/gesturehandler.js +0 -0
  67. package/myagent/data/novnc/lib/input/keyboard.js +0 -0
  68. package/myagent/data/novnc/lib/input/keysym.js +0 -0
  69. package/myagent/data/novnc/lib/input/keysymdef.js +0 -0
  70. package/myagent/data/novnc/lib/input/util.js +0 -0
  71. package/myagent/data/novnc/lib/input/vkeys.js +0 -0
  72. package/myagent/data/novnc/lib/input/xtscancodes.js +0 -0
  73. package/myagent/data/novnc/lib/ra2.js +0 -0
  74. package/myagent/data/novnc/lib/rfb.js +0 -0
  75. package/myagent/data/novnc/lib/util/browser.js +0 -0
  76. package/myagent/data/novnc/lib/util/cursor.js +0 -0
  77. package/myagent/data/novnc/lib/util/element.js +0 -0
  78. package/myagent/data/novnc/lib/util/events.js +0 -0
  79. package/myagent/data/novnc/lib/util/eventtarget.js +0 -0
  80. package/myagent/data/novnc/lib/util/int.js +0 -0
  81. package/myagent/data/novnc/lib/util/logging.js +0 -0
  82. package/myagent/data/novnc/lib/util/md5.js +0 -0
  83. package/myagent/data/novnc/lib/util/strings.js +0 -0
  84. package/myagent/data/novnc/lib/vendor/pako/lib/utils/common.js +0 -0
  85. package/myagent/data/novnc/lib/vendor/pako/lib/zlib/adler32.js +0 -0
  86. package/myagent/data/novnc/lib/vendor/pako/lib/zlib/constants.js +0 -0
  87. package/myagent/data/novnc/lib/vendor/pako/lib/zlib/crc32.js +0 -0
  88. package/myagent/data/novnc/lib/vendor/pako/lib/zlib/deflate.js +0 -0
  89. package/myagent/data/novnc/lib/vendor/pako/lib/zlib/gzheader.js +0 -0
  90. package/myagent/data/novnc/lib/vendor/pako/lib/zlib/inffast.js +0 -0
  91. package/myagent/data/novnc/lib/vendor/pako/lib/zlib/inflate.js +0 -0
  92. package/myagent/data/novnc/lib/vendor/pako/lib/zlib/inftrees.js +0 -0
  93. package/myagent/data/novnc/lib/vendor/pako/lib/zlib/messages.js +0 -0
  94. package/myagent/data/novnc/lib/vendor/pako/lib/zlib/trees.js +0 -0
  95. package/myagent/data/novnc/lib/vendor/pako/lib/zlib/zstream.js +0 -0
  96. package/myagent/data/novnc/lib/websock.js +0 -0
  97. package/myagent/package.json +1 -1
  98. package/myagent/web/api_server.py +336 -10
  99. package/myagent/web/ui/admin/admin-agentchat.js +0 -0
  100. package/myagent/web/ui/admin/admin-agents.js +0 -0
  101. package/myagent/web/ui/admin/admin-core.js +0 -0
  102. package/myagent/web/ui/admin/admin-dashboard.js +0 -0
  103. package/myagent/web/ui/admin/admin-executor.js +0 -0
  104. package/myagent/web/ui/admin/admin-files.js +0 -0
  105. package/myagent/web/ui/admin/admin-llm.js +0 -0
  106. package/myagent/web/ui/admin/admin-logs.js +0 -0
  107. package/myagent/web/ui/admin/admin-memory.js +0 -0
  108. package/myagent/web/ui/admin/admin-org.js +0 -0
  109. package/myagent/web/ui/admin/admin-permissions.js +0 -0
  110. package/myagent/web/ui/admin/admin-platforms.js +0 -0
  111. package/myagent/web/ui/admin/admin-sessions.js +0 -0
  112. package/myagent/web/ui/admin/admin-skills.js +0 -0
  113. package/myagent/web/ui/admin/admin-system.js +0 -0
  114. package/myagent/web/ui/admin/admin-tasks.js +0 -0
  115. package/myagent/web/ui/chat/chat.css +2 -1
  116. package/myagent/web/ui/chat/groupchat.js +96 -14
  117. package/package.json +1 -1
  118. package/skills/fullstack-dev/SKILL.md +120 -952
  119. package/skills/pdf/SKILL.md +1 -7
  120. package/web/api_server.py +308 -0
  121. package/web/ui/admin/admin-agentchat.js +0 -0
  122. package/web/ui/admin/admin-agents.js +0 -0
  123. package/web/ui/admin/admin-core.js +0 -0
  124. package/web/ui/admin/admin-dashboard.js +0 -0
  125. package/web/ui/admin/admin-executor.js +0 -0
  126. package/web/ui/admin/admin-files.js +0 -0
  127. package/web/ui/admin/admin-llm.js +0 -0
  128. package/web/ui/admin/admin-logs.js +0 -0
  129. package/web/ui/admin/admin-memory.js +0 -0
  130. package/web/ui/admin/admin-org.js +0 -0
  131. package/web/ui/admin/admin-permissions.js +0 -0
  132. package/web/ui/admin/admin-platforms.js +0 -0
  133. package/web/ui/admin/admin-sessions.js +0 -0
  134. package/web/ui/admin/admin-skills.js +0 -0
  135. package/web/ui/admin/admin-system.js +0 -0
  136. package/web/ui/admin/admin-tasks.js +0 -0
  137. package/web/ui/chat/chat.css +2 -1
  138. package/web/ui/chat/groupchat.js +96 -14
  139. package/worklog.md +24 -0
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "myagent-ai",
3
- "version": "1.23.81",
3
+ "version": "1.23.82",
4
4
  "description": "本地桌面端执行型AI助手 - Open Interpreter 风格 | Local Desktop Execution-Oriented AI Assistant",
5
5
  "main": "main.py",
6
6
  "bin": {
@@ -473,7 +473,11 @@ class ApiServer:
473
473
  # ── 群消息 ──
474
474
  r.add_get("/api/groups/{gid}/messages", self.handle_get_group_messages)
475
475
  r.add_post("/api/groups/{gid}/messages", self.handle_send_group_message)
476
+ r.add_post("/api/groups/{gid}/messages/stream", self.handle_send_group_message_stream)
476
477
  r.add_delete("/api/groups/{gid}/messages", self.handle_clear_group_messages)
478
+ # [v1.23.81] 群聊 session 管理(纯数字 ID,用于 URL 导航恢复)
479
+ r.add_get("/api/groups/{gid}/session", self.handle_get_group_session)
480
+ r.add_get("/api/group-session/{sid}", self.handle_resolve_group_session)
477
481
  # [v1.23.37] Agent间私聊记录查询
478
482
  r.add_get("/api/agent-chat/pairs", self.handle_get_agent_chat_pairs)
479
483
  r.add_get("/api/agent-chat/messages", self.handle_get_agent_chat_messages)
@@ -4169,9 +4173,7 @@ window.addEventListener('beforeunload', function() {{
4169
4173
  return web.json_response({"error": str(e)}, status=500)
4170
4174
 
4171
4175
  async def handle_list_sessions(self, request):
4172
- if not self.core.memory:
4173
- logger.warning("handle_list_sessions: self.core.memory is None, 无法查询会话")
4174
- return web.json_response([])
4176
+ if not self.core.memory: return web.json_response([])
4175
4177
  agent = request.query.get("agent", "")
4176
4178
  # 只统计用户可见的消息数(排除 llm_output 和 conversation_insight)
4177
4179
  if agent:
@@ -4189,13 +4191,6 @@ window.addEventListener('beforeunload', function() {{
4189
4191
  "AND key NOT IN ('llm_output', 'llm_input', 'tool_result_raw', 'conversation_insight') "
4190
4192
  "GROUP BY session_id ORDER BY last DESC LIMIT 100").fetchall()
4191
4193
  sessions = [{"id": r["session_id"], "messages": r["cnt"], "last": r["last"]} for r in rows]
4192
- if not sessions:
4193
- try:
4194
- total = self.core.memory._get_conn().execute("SELECT COUNT(*) as cnt FROM memories WHERE category = 'session'").fetchone()
4195
- total_all = self.core.memory._get_conn().execute("SELECT COUNT(*) as cnt FROM memories").fetchone()
4196
- logger.debug(f"handle_list_sessions: 无会话. session记录: {total['cnt']}, 总: {total_all['cnt']}, agent: {agent or '(无)'}")
4197
- except Exception:
4198
- pass
4199
4194
  # 批量获取自定义会话名称
4200
4195
  sids = [s["id"] for s in sessions]
4201
4196
  name_map = self.core.memory.list_session_names(sids) if sids else {}
@@ -7981,6 +7976,313 @@ window.addEventListener('beforeunload', function() {{
7981
7976
  "task_id": task_id,
7982
7977
  })
7983
7978
 
7979
+ async def handle_send_group_message_stream(self, request):
7980
+ """POST /api/groups/{gid}/messages/stream - SSE 流式群聊
7981
+
7982
+ SSE 事件格式:
7983
+ data: {"type":"agent_start","agent_path":"...","name":"...","avatar":"...","agent_color":"..."}
7984
+ data: {"type":"text_delta","content":"...","agent_path":"..."}
7985
+ data: {"type":"agent_done","agent_path":"..."}
7986
+ data: {"type":"system_message","content":"..."}
7987
+ data: {"type":"done","task_id":"..."}
7988
+ data: {"type":"error","content":"..."}
7989
+ """
7990
+ import re as _re
7991
+ import time as _time
7992
+ import asyncio
7993
+
7994
+ try:
7995
+ data = await request.json()
7996
+ except Exception:
7997
+ return web.Response(
7998
+ text='data: ' + json.dumps({"type": "error", "content": "invalid JSON"}) + '\n\n',
7999
+ content_type="text/event-stream",
8000
+ )
8001
+
8002
+ gid = request.match_info["gid"]
8003
+ content = data.get("message", "").strip()
8004
+ if not content:
8005
+ return web.Response(
8006
+ text='data: ' + json.dumps({"type": "error", "content": "消息不能为空"}) + '\n\n',
8007
+ content_type="text/event-stream",
8008
+ )
8009
+
8010
+ mgr = self._get_group_manager()
8011
+ group = mgr.get_group(gid)
8012
+ if not group:
8013
+ return web.Response(
8014
+ text='data: ' + json.dumps({"type": "error", "content": "群不存在"}) + '\n\n',
8015
+ content_type="text/event-stream",
8016
+ )
8017
+
8018
+ # 创建 SSE 流式响应
8019
+ response = web.StreamResponse(
8020
+ status=200,
8021
+ headers={
8022
+ "Content-Type": "text/event-stream",
8023
+ "Cache-Control": "no-cache",
8024
+ "Connection": "keep-alive",
8025
+ "X-Accel-Buffering": "no",
8026
+ }
8027
+ )
8028
+ await response.prepare(request)
8029
+
8030
+ client_connected = {"value": True}
8031
+ _write_lock = asyncio.Lock()
8032
+
8033
+ async def safe_write(obj):
8034
+ if not client_connected["value"]:
8035
+ return False
8036
+ try:
8037
+ async with _write_lock:
8038
+ await response.write(("data: " + json.dumps(obj, ensure_ascii=False) + "\n\n").encode())
8039
+ return True
8040
+ except Exception:
8041
+ client_connected["value"] = False
8042
+ return False
8043
+
8044
+ # 1. 保存用户消息
8045
+ from groups.manager import GroupMessage
8046
+ user_msg = GroupMessage(
8047
+ group_id=gid, sender="user", sender_name="用户",
8048
+ sender_avatar="👤", content=content,
8049
+ )
8050
+ mgr.add_message(user_msg)
8051
+
8052
+ # 2. 保存任务记录
8053
+ tp = self._get_task_persistence()
8054
+ from core.utils import generate_id
8055
+ task_id = generate_id("task")
8056
+ tp.save_task(
8057
+ task_id=task_id, description=content[:500],
8058
+ session_id=f"group_{gid}", group_id=gid,
8059
+ agent_path=", ".join(m.agent_path for m in group.members if not m.muted),
8060
+ status="running", metadata={"source": "group_chat", "group_name": group.name},
8061
+ )
8062
+
8063
+ # 3. 解析 @提及
8064
+ at_pattern = _re.compile(r'@(\S+)')
8065
+ at_targets = at_pattern.findall(content)
8066
+ mentioned_agents = set()
8067
+ mentioned_all = False
8068
+ for target in at_targets:
8069
+ target_lower = target.lower()
8070
+ if target_lower in ('all', '所有人', '大家'):
8071
+ mentioned_all = True
8072
+ break
8073
+ for m in group.members:
8074
+ agent_cfg = self._read_agent_config(m.agent_path)
8075
+ agent_name = agent_cfg.get("name", "") if agent_cfg else ""
8076
+ if (target_lower == m.agent_path.lower()
8077
+ or target_lower == agent_name.lower()
8078
+ or (m.nickname and target_lower == m.nickname.lower())):
8079
+ mentioned_agents.add(m.agent_path)
8080
+
8081
+ active_members = [m for m in group.members if not m.muted]
8082
+ if mentioned_all:
8083
+ target_members = active_members
8084
+ elif mentioned_agents:
8085
+ target_members = [m for m in active_members if m.agent_path in mentioned_agents]
8086
+ else:
8087
+ target_members = active_members
8088
+
8089
+ # 4. 串行处理每个 agent,流式输出
8090
+ _chat_msg_pattern = _re.compile(r'__CHAT_AGENT__(.+?)\|(.+?)\|(.+?)__END__')
8091
+ final_responses = []
8092
+
8093
+ for member in target_members:
8094
+ agent_path = member.agent_path
8095
+ agent_cfg = self._read_agent_config(agent_path)
8096
+
8097
+ # 获取显示信息
8098
+ avatar = "🤖"
8099
+ display_name = agent_path
8100
+ agent_color = "var(--accent)"
8101
+ if agent_cfg:
8102
+ avatar = agent_cfg.get("avatar_emoji", "🤖") or "🤖"
8103
+ display_name = agent_cfg.get("name", agent_path)
8104
+ agent_color = agent_cfg.get("avatar_color", "var(--accent)")
8105
+
8106
+ # 通知前端:新 agent 开始回复
8107
+ await safe_write({
8108
+ "type": "agent_start",
8109
+ "agent_path": agent_path,
8110
+ "name": display_name,
8111
+ "avatar": avatar,
8112
+ "agent_color": agent_color,
8113
+ })
8114
+
8115
+ try:
8116
+ model_chain = self._build_model_chain(agent_cfg, agent_path)
8117
+ session_id = f"group_{gid}_{agent_path}"
8118
+
8119
+ _, agent_system_prompt = self._build_agent_chat_context(agent_path, agent_cfg, content)
8120
+
8121
+ # 构建群聊上下文(与 handle_send_group_message 完全一致)
8122
+ member_lines = []
8123
+ for m in group.members:
8124
+ mc = self._read_agent_config(m.agent_path)
8125
+ m_name = mc.get("name", m.agent_path) if mc else m.agent_path
8126
+ m_id = mc.get("id", "") if mc else ""
8127
+ m_desc = mc.get("description", "") if mc else ""
8128
+ role_label = {"owner": "群主", "admin": "管理员"}.get(m.role, "成员")
8129
+ nick = f"(昵称: {m.nickname})" if m.nickname else ""
8130
+ line = f" - {m_name} [ID: {m_id}] ({role_label})"
8131
+ if m_desc: line += f" — {m_desc}"
8132
+ line += nick
8133
+ if m.muted: line += "(已禁言)"
8134
+ member_lines.append(line)
8135
+
8136
+ my_name = agent_cfg.get("name", agent_path) if agent_cfg else agent_path
8137
+ my_role = {"owner": "群主", "admin": "管理员"}.get(member.role, "成员")
8138
+
8139
+ recent_msgs = mgr.get_messages(gid, limit=10)
8140
+ history_lines = []
8141
+ for rm in recent_msgs:
8142
+ rm_name = rm.sender_name or ("用户" if rm.sender == "user" else rm.agent_path)
8143
+ rm_time_str = _time.strftime("%H:%M", _time.localtime(rm.timestamp)) if rm.timestamp else ""
8144
+ history_lines.append(f"[{rm_time_str}] {rm_name}: {rm.content[:200]}")
8145
+ chat_history = "\n".join(reversed(history_lines))
8146
+
8147
+ is_mentioned = mentioned_all or member.agent_path in mentioned_agents
8148
+ at_info = ""
8149
+ if mentioned_all:
8150
+ at_info = "- 本次消息使用了 @所有人,请务必回复。\n"
8151
+ elif mentioned_agents:
8152
+ if is_mentioned:
8153
+ at_info = f"- 本次消息使用了 @{my_name},请务必回复。\n"
8154
+ else:
8155
+ at_info = "- 本次消息使用了 @其他成员,与你无关,请不要回复。\n"
8156
+ else:
8157
+ at_info = "- 本次消息没有 @任何人(广播消息),你可以根据内容决定是否回复。\n"
8158
+
8159
+ group_context = f"""## 群聊上下文
8160
+
8161
+ 你正在参与一个群聊,以下是群的详细信息:
8162
+
8163
+ ### 群信息
8164
+ - 群名称: {group.name}
8165
+ - 群描述: {group.description or '无'}
8166
+ - 当前发言者: 用户(群聊中的真人用户)
8167
+
8168
+ ### 你的身份
8169
+ - 名称: {my_name}
8170
+ - 路径: {agent_path}
8171
+ - 角色: {my_role}{"(已禁言,但仍可接收消息)" if member.muted else ""}
8172
+
8173
+ ### 群成员列表(共{len(group.members)}人)
8174
+ {chr(10).join(member_lines)}
8175
+
8176
+ ### 沟通规则(重要)
8177
+ 1. **@提及机制**: 用户发送消息时可以使用 @名称 来指定回复者
8178
+ - @某个Agent: 只有被@的Agent需要回复
8179
+ - @所有人 / @all: 所有成员都需要回复
8180
+ - 不@任何人(广播): 你自行判断是否需要回复
8181
+ 2. **跨Agent私下沟通**: 你可以使用 `myagent-ai chat --agent <ID> --message "消息"` 命令向群内其他Agent发送私下消息。
8182
+ - **必须使用Agent的ID**,不是名字。从上方「群成员列表」中找到目标Agent的 `[ID: xxx]` 就是它的唯一ID
8183
+ - 正确示例: `myagent-ai chat --agent a1b2c3d4e5f6 --message "你好"`
8184
+ - 私下沟通的内容不会直接显示给用户,适合讨论细节、交换数据、协调方案
8185
+ - 当任务需要多个Agent协作时,应该先在群里讨论分工,然后私下沟通具体细节
8186
+ 3. **协作分工模式**(复杂任务):
8187
+ - 部长/管理员应主动分析任务,在群里提出分工方案(谁负责什么)
8188
+ - 其他成员应积极响应,认领自己擅长的部分
8189
+ - 分工确定后,各自私下沟通需要协调的细节
8190
+ - 部长/管理员负责在群里汇总进展,向用户汇报阶段性成果和最终结果
8191
+ - 私下沟通的详细过程不需要在群里展示,只汇报关键进展和最终结论
8192
+ {at_info}
8193
+ ### 近期群聊记录(最近10条)
8194
+ {chat_history if chat_history else '(暂无历史消息)'}
8195
+
8196
+ ### 重要提醒
8197
+ - 你只代表你自己发言,使用第一人称
8198
+ - 不要假装是其他Agent或代替其他Agent回答
8199
+ - 如果问题超出你的能力范围,建议用户@相关专家Agent
8200
+ - 如果需要其他Agent的信息,使用 `myagent-ai chat --agent <ID>` 命令私下沟通(使用Agent的ID,从群成员列表的[ID: xxx]中获取)
8201
+
8202
+ ### 能力提醒(关键)
8203
+ - 你拥有完整的工具调用能力(搜索、文件操作、代码执行、图片生成等)
8204
+ - 在群聊中,你应该像在1对1对话中一样主动使用工具来完成用户的请求
8205
+ - 不要因为身处群聊就只做简单文字回复,该用工具时一定要用"""
8206
+
8207
+ if agent_system_prompt:
8208
+ agent_system_prompt += "\n\n" + group_context
8209
+ else:
8210
+ agent_system_prompt = group_context
8211
+
8212
+ dept_context = self._build_dept_context(gid, agent_path)
8213
+ if dept_context:
8214
+ agent_system_prompt += "\n\n" + dept_context
8215
+
8216
+ agent_content = content
8217
+
8218
+ # 调用 LLM 获取回复
8219
+ if model_chain and self.core.llm:
8220
+ response_text = await self._try_model_chain_inner(
8221
+ model_chain, agent_content, session_id,
8222
+ agent_path=agent_path, agent_system_prompt=agent_system_prompt,
8223
+ )
8224
+ else:
8225
+ response_text = await self.core.process_message(agent_content, session_id)
8226
+
8227
+ # 流式输出该 agent 的回复文本(逐块发送)
8228
+ await self._stream_text_chunked(response_text, safe_write, chunk_size=6, delay=0.01)
8229
+
8230
+ # 处理跨 Agent 私聊标记
8231
+ chat_matches = _chat_msg_pattern.findall(response_text)
8232
+ if chat_matches:
8233
+ response_text = _chat_msg_pattern.sub('', response_text).strip()
8234
+ for c_path, c_name, c_msg in chat_matches:
8235
+ c_path_s, c_name_s, c_msg_s = c_path.strip(), c_name.strip(), c_msg.strip()
8236
+ chat_sys_msg = GroupMessage(
8237
+ group_id=gid, sender=agent_path,
8238
+ sender_name=display_name, sender_avatar=avatar,
8239
+ content=f"💬 私下与 {c_name_s} 沟通中...",
8240
+ )
8241
+ mgr.add_message(chat_sys_msg)
8242
+ try:
8243
+ mgr.add_agent_chat(
8244
+ group_id=gid, from_agent=agent_path,
8245
+ from_name=display_name, to_agent=c_path_s,
8246
+ to_name=c_name_s, content=c_msg_s,
8247
+ )
8248
+ except Exception as ce:
8249
+ logger.debug(f"保存私聊记录失败: {ce}")
8250
+
8251
+ # 保存到数据库
8252
+ agent_msg = GroupMessage(
8253
+ group_id=gid, sender="agent",
8254
+ sender_name=display_name, sender_avatar=avatar,
8255
+ content=response_text, agent_path=agent_path,
8256
+ )
8257
+ mgr.add_message(agent_msg)
8258
+
8259
+ final_responses.append({
8260
+ "ok": True, "agent_path": agent_path,
8261
+ "name": display_name, "avatar": avatar,
8262
+ "agent_color": agent_color,
8263
+ "response": response_text,
8264
+ })
8265
+
8266
+ except Exception as e:
8267
+ logger.error(f"群消息处理失败 ({agent_path}): {e}")
8268
+ await safe_write({"type": "text_delta", "content": f"处理失败: {str(e)}", "agent_path": agent_path})
8269
+ final_responses.append({
8270
+ "ok": False, "agent_path": agent_path,
8271
+ "name": agent_path, "avatar": "❌",
8272
+ "response": f"处理失败: {str(e)}",
8273
+ })
8274
+
8275
+ # 通知前端:该 agent 回复完成
8276
+ await safe_write({"type": "agent_done", "agent_path": agent_path})
8277
+
8278
+ # 更新任务状态
8279
+ has_failure = any(not r.get("ok") for r in final_responses)
8280
+ tp.update_task_status(task_id, "failed" if has_failure else "completed", last_message=content[:500])
8281
+
8282
+ # 通知前端:全部完成
8283
+ await safe_write({"type": "done", "task_id": task_id, "group_id": gid})
8284
+ return response
8285
+
7984
8286
  async def handle_clear_group_messages(self, request):
7985
8287
  """DELETE /api/groups/{gid}/messages - 清空群消息"""
7986
8288
  gid = request.match_info["gid"]
@@ -7988,6 +8290,30 @@ window.addEventListener('beforeunload', function() {{
7988
8290
  ok = mgr.clear_messages(gid)
7989
8291
  return web.json_response({"ok": ok})
7990
8292
 
8293
+ # ── [v1.23.81] 群聊 Session API ──
8294
+
8295
+ async def handle_get_group_session(self, request):
8296
+ """GET /api/groups/{gid}/session - 获取或创建群聊的 session ID(纯数字)"""
8297
+ gid = request.match_info["gid"]
8298
+ mgr = self._get_group_manager()
8299
+ group = mgr.get_group(gid)
8300
+ if not group:
8301
+ return web.json_response({"error": "群不存在"}, status=404)
8302
+ session_id = mgr.get_or_create_group_session(gid)
8303
+ return web.json_response({"session_id": session_id, "group_id": gid})
8304
+
8305
+ async def handle_resolve_group_session(self, request):
8306
+ """GET /api/group-session/{sid} - 通过 session ID 查找 group_id(用于 URL 恢复)"""
8307
+ sid = request.match_info["sid"]
8308
+ mgr = self._get_group_manager()
8309
+ group_id = mgr.get_group_id_by_session(sid)
8310
+ if not group_id:
8311
+ return web.json_response({"error": "session 不存在"}, status=404)
8312
+ group = mgr.get_group(group_id)
8313
+ if not group:
8314
+ return web.json_response({"error": "群已不存在"}, status=404)
8315
+ return web.json_response({"session_id": sid, "group_id": group_id, "group": self._enrich_group_dict(group.to_dict())})
8316
+
7991
8317
  # ── [v1.23.37] Agent间私聊记录 API ──
7992
8318
 
7993
8319
  async def handle_get_agent_chat_pairs(self, request):
File without changes