myagent-ai 1.23.36 → 1.23.37

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/groups/manager.py CHANGED
@@ -275,6 +275,27 @@ class GroupManager:
275
275
  CREATE INDEX IF NOT EXISTS idx_group_messages_group_id
276
276
  ON group_messages(group_id, timestamp)
277
277
  """)
278
+ # [v1.23.37] Agent间私聊记录表(独立于群聊消息,便于查询和管理)
279
+ self._db_conn.execute("""
280
+ CREATE TABLE IF NOT EXISTS agent_chat (
281
+ id TEXT PRIMARY KEY,
282
+ group_id TEXT NOT NULL DEFAULT '',
283
+ from_agent TEXT NOT NULL,
284
+ from_name TEXT DEFAULT '',
285
+ to_agent TEXT NOT NULL,
286
+ to_name TEXT DEFAULT '',
287
+ content TEXT NOT NULL,
288
+ timestamp REAL NOT NULL
289
+ )
290
+ """)
291
+ self._db_conn.execute("""
292
+ CREATE INDEX IF NOT EXISTS idx_agent_chat_group
293
+ ON agent_chat(group_id, timestamp)
294
+ """)
295
+ self._db_conn.execute("""
296
+ CREATE INDEX IF NOT EXISTS idx_agent_chat_pair
297
+ ON agent_chat(from_agent, to_agent, timestamp)
298
+ """)
278
299
  self._db_conn.commit()
279
300
 
280
301
  def close(self):
@@ -697,6 +718,75 @@ class GroupManager:
697
718
  self._db_conn.commit()
698
719
  return True
699
720
 
721
+ # ==================================================================
722
+ # [v1.23.37] Agent间私聊记录
723
+ # ==================================================================
724
+
725
+ def add_agent_chat(self, group_id: str, from_agent: str, from_name: str,
726
+ to_agent: str, to_name: str, content: str) -> str:
727
+ """添加一条Agent间私聊记录,返回消息ID"""
728
+ if not self._db_conn:
729
+ return ""
730
+ msg_id = uuid.uuid4().hex[:16]
731
+ self._db_conn.execute(
732
+ "INSERT INTO agent_chat (id, group_id, from_agent, from_name, to_agent, to_name, content, timestamp) "
733
+ "VALUES (?, ?, ?, ?, ?, ?, ?, ?)",
734
+ (msg_id, group_id, from_agent, from_name, to_agent, to_name, content, time.time()),
735
+ )
736
+ self._db_conn.commit()
737
+ return msg_id
738
+
739
+ def get_agent_chats(self, group_id: str = "", from_agent: str = "",
740
+ to_agent: str = "", limit: int = 200) -> list:
741
+ """查询Agent间私聊记录,支持按群、发送方、接收方筛选"""
742
+ if not self._db_conn:
743
+ return []
744
+ conditions = []
745
+ params = []
746
+ if group_id:
747
+ conditions.append("group_id = ?")
748
+ params.append(group_id)
749
+ if from_agent:
750
+ conditions.append("(from_agent = ? OR to_agent = ?)")
751
+ params.extend([from_agent, from_agent])
752
+ if to_agent:
753
+ conditions.append("(from_agent = ? OR to_agent = ?)")
754
+ params.extend([to_agent, to_agent])
755
+ where = (" WHERE " + " AND ".join(conditions)) if conditions else ""
756
+ rows = self._db_conn.execute(
757
+ f"SELECT * FROM agent_chat{where} ORDER BY timestamp DESC LIMIT ?",
758
+ params + [limit],
759
+ ).fetchall()
760
+ return [dict(r) for r in rows]
761
+
762
+ def get_agent_chat_pairs(self, group_id: str = "") -> list:
763
+ """获取所有有私聊记录的Agent对(去重),返回 [{from_agent, from_name, to_agent, to_name, count, last_time}]"""
764
+ if not self._db_conn:
765
+ return []
766
+ where = " WHERE group_id = ?" if group_id else ""
767
+ params = [group_id] if group_id else []
768
+ rows = self._db_conn.execute(
769
+ f"""SELECT from_agent, from_name, to_agent, to_name,
770
+ COUNT(*) as cnt, MAX(timestamp) as last_ts
771
+ FROM agent_chat{where}
772
+ GROUP BY CASE WHEN from_agent < to_agent THEN from_agent ELSE to_agent END,
773
+ CASE WHEN from_agent < to_agent THEN to_agent ELSE from_agent END
774
+ ORDER BY last_ts DESC""",
775
+ params,
776
+ ).fetchall()
777
+ return [dict(r) for r in rows]
778
+
779
+ def clear_agent_chats(self, group_id: str = "") -> int:
780
+ """清空私聊记录,返回删除数量"""
781
+ if not self._db_conn:
782
+ return 0
783
+ if group_id:
784
+ cur = self._db_conn.execute("DELETE FROM agent_chat WHERE group_id = ?", (group_id,))
785
+ else:
786
+ cur = self._db_conn.execute("DELETE FROM agent_chat")
787
+ self._db_conn.commit()
788
+ return cur.rowcount
789
+
700
790
  # ==================================================================
701
791
  # 群消息统计
702
792
  # ==================================================================
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "myagent-ai",
3
- "version": "1.23.36",
3
+ "version": "1.23.37",
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
@@ -464,6 +464,10 @@ class ApiServer:
464
464
  r.add_get("/api/groups/{gid}/messages", self.handle_get_group_messages)
465
465
  r.add_post("/api/groups/{gid}/messages", self.handle_send_group_message)
466
466
  r.add_delete("/api/groups/{gid}/messages", self.handle_clear_group_messages)
467
+ # [v1.23.37] Agent间私聊记录查询
468
+ r.add_get("/api/agent-chat/pairs", self.handle_get_agent_chat_pairs)
469
+ r.add_get("/api/agent-chat/messages", self.handle_get_agent_chat_messages)
470
+ r.add_delete("/api/agent-chat/messages", self.handle_clear_agent_chat_messages)
467
471
  # ── 部门管理 ──
468
472
  r.add_get("/api/departments", self.handle_dept_tree)
469
473
  r.add_post("/api/departments", self.handle_create_dept)
@@ -7779,11 +7783,15 @@ window.addEventListener('beforeunload', function() {{
7779
7783
  - @某个Agent: 只有被@的Agent需要回复
7780
7784
  - @所有人 / @all: 所有成员都需要回复
7781
7785
  - 不@任何人(广播): 你自行判断是否需要回复
7782
- 2. **跨Agent沟通**: 你可以使用 `myagent-ai chat --agent <路径> --message "消息"` 命令向群内其他Agent发送消息。消息会出现在群聊中,对方下次被@时会看到。
7783
- 3. **回复方式**:
7784
- - 直接回复用户的消息
7785
- - 需要其他Agent协助时,使用 chat 命令沟通,不要假装代替其他Agent回答
7786
- - 如需协调多个Agent,建议用户使用 @所有人
7786
+ 2. **跨Agent私下沟通**: 你可以使用 `myagent-ai chat --agent <路径> --message "消息"` 命令向群内其他Agent发送私下消息。对方会在自己下次处理消息时收到。
7787
+ - 私下沟通的内容不会直接显示给用户,适合讨论细节、交换数据、协调方案
7788
+ - 当任务需要多个Agent协作时,应该先在群里讨论分工,然后私下沟通具体细节
7789
+ 3. **协作分工模式**(复杂任务):
7790
+ - 部长/管理员应主动分析任务,在群里提出分工方案(谁负责什么)
7791
+ - 其他成员应积极响应,认领自己擅长的部分
7792
+ - 分工确定后,各自私下沟通需要协调的细节
7793
+ - 部长/管理员负责在群里汇总进展,向用户汇报阶段性成果和最终结果
7794
+ - 私下沟通的详细过程不需要在群里展示,只汇报关键进展和最终结论
7787
7795
  {at_info}
7788
7796
  ### 近期群聊记录(最近10条)
7789
7797
  {chat_history if chat_history else '(暂无历史消息)'}
@@ -7792,7 +7800,7 @@ window.addEventListener('beforeunload', function() {{
7792
7800
  - 你只代表你自己发言,使用第一人称
7793
7801
  - 不要假装是其他Agent或代替其他Agent回答
7794
7802
  - 如果问题超出你的能力范围,建议用户@相关专家Agent
7795
- - 如果需要其他Agent的信息,使用 `myagent-ai chat` 命令沟通
7803
+ - 如果需要其他Agent的信息,使用 `myagent-ai chat` 命令私下沟通
7796
7804
 
7797
7805
  ### 能力提醒(关键)
7798
7806
  - 你拥有完整的工具调用能力(搜索、文件操作、代码执行、图片生成等)
@@ -7872,6 +7880,7 @@ window.addEventListener('beforeunload', function() {{
7872
7880
  )
7873
7881
 
7874
7882
  # [v1.23.29] 从 Agent 响应中提取 __CHAT_AGENT__ 标记,清理并保存跨Agent通信消息
7883
+ # [v1.23.37] 同时写入独立的 agent_chat 私聊记录表,便于管理后台查看
7875
7884
  _chat_msg_pattern = _re.compile(r'__CHAT_AGENT__(.+?)\|(.+?)\|(.+?)__END__')
7876
7885
  for resp in final_responses:
7877
7886
  resp_text = resp.get("response", "")
@@ -7879,17 +7888,31 @@ window.addEventListener('beforeunload', function() {{
7879
7888
  if chat_matches:
7880
7889
  # 清理标记文本
7881
7890
  resp["response"] = _chat_msg_pattern.sub('', resp_text).strip()
7882
- # 保存跨Agent通信消息到群聊
7891
+ # 保存跨Agent通信消息
7883
7892
  for c_path, c_name, c_msg in chat_matches:
7893
+ c_path_s, c_name_s, c_msg_s = c_path.strip(), c_name.strip(), c_msg.strip()
7894
+ # 群聊中显示简要提示
7884
7895
  chat_sys_msg = GroupMessage(
7885
7896
  group_id=gid,
7886
7897
  sender=resp.get("agent_path", ""),
7887
7898
  sender_name=resp.get("name", ""),
7888
7899
  sender_avatar=resp.get("avatar", "🤖"),
7889
- content=f"💬 {c_name.strip()} 说: {c_msg.strip()}",
7900
+ content=f"💬 私下与 {c_name_s} 沟通中...",
7890
7901
  msg_type="text",
7891
7902
  )
7892
7903
  mgr.add_message(chat_sys_msg)
7904
+ # [v1.23.37] 写入独立的私聊记录表
7905
+ try:
7906
+ mgr.add_agent_chat(
7907
+ group_id=gid,
7908
+ from_agent=resp.get("agent_path", ""),
7909
+ from_name=resp.get("name", ""),
7910
+ to_agent=c_path_s,
7911
+ to_name=c_name_s,
7912
+ content=c_msg_s,
7913
+ )
7914
+ except Exception as ce:
7915
+ logger.debug(f"保存私聊记录失败: {ce}")
7893
7916
 
7894
7917
  # Save agent messages sequentially in sorted order
7895
7918
  for resp in final_responses:
@@ -7926,6 +7949,34 @@ window.addEventListener('beforeunload', function() {{
7926
7949
  ok = mgr.clear_messages(gid)
7927
7950
  return web.json_response({"ok": ok})
7928
7951
 
7952
+ # ── [v1.23.37] Agent间私聊记录 API ──
7953
+
7954
+ async def handle_get_agent_chat_pairs(self, request):
7955
+ """GET /api/agent-chat/pairs - 获取所有私聊Agent对"""
7956
+ gid = request.query.get("group_id", "")
7957
+ mgr = self._get_group_manager()
7958
+ pairs = mgr.get_agent_chat_pairs(group_id=gid)
7959
+ return web.json_response(pairs)
7960
+
7961
+ async def handle_get_agent_chat_messages(self, request):
7962
+ """GET /api/agent-chat/messages - 查询私聊记录详情"""
7963
+ gid = request.query.get("group_id", "")
7964
+ from_agent = request.query.get("from_agent", "")
7965
+ to_agent = request.query.get("to_agent", "")
7966
+ limit = min(int(request.query.get("limit", "200")), 500)
7967
+ mgr = self._get_group_manager()
7968
+ messages = mgr.get_agent_chats(
7969
+ group_id=gid, from_agent=from_agent, to_agent=to_agent, limit=limit
7970
+ )
7971
+ return web.json_response(messages)
7972
+
7973
+ async def handle_clear_agent_chat_messages(self, request):
7974
+ """DELETE /api/agent-chat/messages - 清空私聊记录"""
7975
+ gid = request.query.get("group_id", "")
7976
+ mgr = self._get_group_manager()
7977
+ deleted = mgr.clear_agent_chats(group_id=gid)
7978
+ return web.json_response({"ok": True, "deleted": deleted})
7979
+
7929
7980
  async def start(self, port: int = 8767, host: str = "127.0.0.1"):
7930
7981
  # 加载禁用技能列表
7931
7982
  self._load_disabled_skills()
@@ -0,0 +1,175 @@
1
+ /**
2
+ * [v1.23.37] Agent间私聊记录查看模块
3
+ * 路径: /api/agent-chat/pairs, /api/agent-chat/messages
4
+ */
5
+ async function renderAgentChat() {
6
+ const content = $('content');
7
+ if (!content) return;
8
+
9
+ let currentFilter = { group_id: '', from_agent: '', to_agent: '' };
10
+
11
+ function esc(s) { return (s || '').replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;').replace(/"/g,'&quot;'); }
12
+
13
+ async function loadGroups() {
14
+ try {
15
+ const data = await api('/api/groups');
16
+ return Array.isArray(data) ? data : (data.groups || data.data || []);
17
+ } catch { return []; }
18
+ }
19
+
20
+ async function loadPairs() {
21
+ const q = new URLSearchParams();
22
+ if (currentFilter.group_id) q.set('group_id', currentFilter.group_id);
23
+ const data = await api('/api/agent-chat/pairs?' + q.toString());
24
+ return Array.isArray(data) ? data : [];
25
+ }
26
+
27
+ async function loadMessages(fromAgent, toAgent) {
28
+ const q = new URLSearchParams();
29
+ if (currentFilter.group_id) q.set('group_id', currentFilter.group_id);
30
+ if (fromAgent) q.set('from_agent', fromAgent);
31
+ if (toAgent) q.set('to_agent', toAgent);
32
+ q.set('limit', '500');
33
+ const data = await api('/api/agent-chat/messages?' + q.toString());
34
+ return Array.isArray(data) ? data : [];
35
+ }
36
+
37
+ function formatTime(ts) {
38
+ if (!ts) return '';
39
+ const d = new Date(ts * 1000);
40
+ const pad = n => String(n).padStart(2, '0');
41
+ return `${d.getFullYear()}-${pad(d.getMonth()+1)}-${pad(d.getDate())} ${pad(d.getHours())}:${pad(d.getMinutes())}:${pad(d.getSeconds())}`;
42
+ }
43
+
44
+ function truncate(s, len) {
45
+ if (!s) return '';
46
+ return s.length > len ? s.slice(0, len) + '...' : s;
47
+ }
48
+
49
+ // ── 主界面 ──
50
+ content.innerHTML = `
51
+ <div style="display:flex;align-items:center;justify-content:space-between;margin-bottom:20px">
52
+ <div>
53
+ <h2 style="font-size:20px;font-weight:600;color:var(--text)">Agent 私聊记录</h2>
54
+ <p style="font-size:13px;color:var(--text3);margin-top:4px">查看群聊中 Agent 之间的私下沟通记录</p>
55
+ </div>
56
+ <div style="display:flex;gap:8px">
57
+ <button id="acRefreshBtn" onclick="window._acRefresh()" style="padding:6px 14px;background:var(--bg3);color:var(--text);border:1px solid var(--border);border-radius:var(--radius);cursor:pointer;font-size:13px">
58
+ 刷新
59
+ </button>
60
+ <button id="acClearBtn" onclick="window._acClearAll()" style="padding:6px 14px;background:var(--danger);color:#fff;border:none;border-radius:var(--radius);cursor:pointer;font-size:13px">
61
+ 清空全部
62
+ </button>
63
+ </div>
64
+ </div>
65
+ <div style="display:flex;gap:12px;margin-bottom:16px;flex-wrap:wrap">
66
+ <select id="acGroupFilter" onchange="window._acFilterChange()" style="padding:6px 10px;background:var(--bg3);color:var(--text);border:1px solid var(--border);border-radius:var(--radius);font-size:13px;min-width:160px">
67
+ <option value="">全部群聊</option>
68
+ </select>
69
+ </div>
70
+ <div style="display:grid;grid-template-columns:340px 1fr;gap:16px;min-height:500px" id="acLayout">
71
+ <div id="acPairsPanel" style="background:var(--bg2);border:1px solid var(--border);border-radius:var(--radius);overflow:hidden;display:flex;flex-direction:column">
72
+ <div style="padding:12px 16px;border-bottom:1px solid var(--border);font-weight:600;font-size:14px;color:var(--text2)">聊天对象</div>
73
+ <div id="acPairsList" style="flex:1;overflow-y:auto;padding:8px">
74
+ <div style="text-align:center;color:var(--text3);padding:40px;font-size:13px">加载中...</div>
75
+ </div>
76
+ </div>
77
+ <div id="acChatPanel" style="background:var(--bg2);border:1px solid var(--border);border-radius:var(--radius);overflow:hidden;display:flex;flex-direction:column">
78
+ <div id="acChatHeader" style="padding:12px 16px;border-bottom:1px solid var(--border);font-weight:600;font-size:14px;color:var(--text2)">选择左侧聊天对象查看详情</div>
79
+ <div id="acChatMessages" style="flex:1;overflow-y:auto;padding:16px">
80
+ <div style="text-align:center;color:var(--text3);padding:60px;font-size:13px">暂无聊天记录</div>
81
+ </div>
82
+ </div>
83
+ </div>
84
+ `;
85
+
86
+ // ── 加载群列表 ──
87
+ const groups = await loadGroups();
88
+ const sel = $('acGroupFilter');
89
+ groups.forEach(g => {
90
+ const opt = document.createElement('option');
91
+ opt.value = g.id || g.group_id || '';
92
+ opt.textContent = g.name || g.group_name || g.id || '未命名';
93
+ sel.appendChild(opt);
94
+ });
95
+
96
+ // ── 渲染聊天对列表 ──
97
+ async function renderPairs() {
98
+ const pairs = await loadPairs();
99
+ const list = $('acPairsList');
100
+ if (!pairs.length) {
101
+ list.innerHTML = '<div style="text-align:center;color:var(--text3);padding:40px;font-size:13px">暂无私聊记录</div>';
102
+ return;
103
+ }
104
+ list.innerHTML = pairs.map(p => {
105
+ const a = esc(p.from_name || p.from_agent);
106
+ const b = esc(p.to_name || p.to_agent);
107
+ const last = formatTime(p.last_ts);
108
+ const count = p.cnt || p.count || 0;
109
+ return `<div onclick="window._acSelectPair('${esc(p.from_agent)}','${esc(p.to_agent)}','${a}','${b}')"
110
+ style="padding:10px 12px;border-radius:6px;cursor:pointer;margin-bottom:4px;transition:background .15s"
111
+ onmouseover="this.style.background='var(--bg3)'" onmouseout="this.style.background='transparent'">
112
+ <div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:4px">
113
+ <span style="font-size:13px;font-weight:600;color:var(--text)">${a} ↔ ${b}</span>
114
+ <span style="font-size:11px;color:var(--text3)">${count}条</span>
115
+ </div>
116
+ <div style="font-size:11px;color:var(--text3)">最后沟通: ${last}</div>
117
+ </div>`;
118
+ }).join('');
119
+ }
120
+
121
+ // ── 渲染聊天记录 ──
122
+ async function renderMessages(fromAgent, toAgent, fromName, toName) {
123
+ const header = $('acChatHeader');
124
+ header.innerHTML = `<span style="color:var(--accent)">${esc(fromName)}</span> ↔ <span style="color:var(--accent)">${esc(toName)}</span> 的私聊记录`;
125
+
126
+ const msgs = await loadMessages(fromAgent, toAgent);
127
+ const container = $('acChatMessages');
128
+ if (!msgs.length) {
129
+ container.innerHTML = '<div style="text-align:center;color:var(--text3);padding:60px;font-size:13px">暂无记录</div>';
130
+ return;
131
+ }
132
+ // 按 time 正序显示
133
+ const sorted = [...msgs].sort((a, b) => (a.timestamp || 0) - (b.timestamp || 0));
134
+ container.innerHTML = sorted.map(m => {
135
+ const isFrom = m.from_agent === fromAgent;
136
+ const sender = isFrom ? esc(m.from_name || m.from_agent) : esc(m.to_name || m.to_agent);
137
+ const bubbleBg = isFrom ? 'var(--accent-light)' : 'var(--bg3)';
138
+ const align = isFrom ? 'flex-end' : 'flex-start';
139
+ const labelColor = isFrom ? 'var(--accent)' : 'var(--text3)';
140
+ const content = esc(m.content || '').replace(/\n/g, '<br>');
141
+ const time = formatTime(m.timestamp);
142
+ return `<div style="display:flex;flex-direction:column;align-items:${align};margin-bottom:12px">
143
+ <div style="font-size:11px;color:${labelColor};margin-bottom:3px;padding:0 4px">${sender} · ${time}</div>
144
+ <div style="max-width:75%;padding:10px 14px;background:${bubbleBg};border-radius:12px;font-size:13px;line-height:1.6;color:var(--text);word-break:break-word">${content}</div>
145
+ </div>`;
146
+ }).join('');
147
+ container.scrollTop = container.scrollHeight;
148
+ }
149
+
150
+ // ── 全局方法 ──
151
+ window._acRefresh = async () => { await renderPairs(); };
152
+ window._acFilterChange = () => {
153
+ currentFilter.group_id = $('acGroupFilter').value;
154
+ renderPairs();
155
+ };
156
+ window._acSelectPair = (fa, ta, fn, tn) => { renderMessages(fa, ta, fn, tn); };
157
+ window._acClearAll = async () => {
158
+ if (!confirm('确定要清空所有私聊记录吗?此操作不可恢复。')) return;
159
+ const q = new URLSearchParams();
160
+ if (currentFilter.group_id) q.set('group_id', currentFilter.group_id);
161
+ try {
162
+ const data = await api('/api/agent-chat/messages?' + q.toString(), { method: 'DELETE' });
163
+ toast('已清空 ' + (data.deleted || 0) + ' 条记录');
164
+ await renderPairs();
165
+ $('acChatMessages').innerHTML = '<div style="text-align:center;color:var(--text3);padding:60px;font-size:13px">暂无聊天记录</div>';
166
+ $('acChatHeader').textContent = '选择左侧聊天对象查看详情';
167
+ } catch (e) { toast('清空失败: ' + (e.message || e), 'error'); }
168
+ };
169
+
170
+ // 初始加载
171
+ await renderPairs();
172
+ }
173
+
174
+ if (typeof window._adminRenderers === 'undefined') window._adminRenderers = {};
175
+ window._adminRenderers['agentchat'] = renderAgentChat;
package/web/ui/index.html CHANGED
@@ -327,6 +327,7 @@ tr:hover{background:var(--surface2)}
327
327
  <div class="nav-item" data-tooltip="聊天平台" onclick="showPage('platforms')"><span class="icon">🌐</span><span class="icon-text">聊天平台</span></div>
328
328
  <div class="nav-item" data-tooltip="组织管理" onclick="showPage('organization')"><span class="icon">🏢</span><span class="icon-text">组织管理</span></div>
329
329
  <div class="nav-item" data-tooltip="部门管理" onclick="showPage('departments')"><span class="icon">🏛</span><span class="icon-text">部门管理</span></div>
330
+ <div class="nav-item" data-tooltip="Agent私聊" onclick="showPage('agentchat')"><span class="icon">🔒</span><span class="icon-text">Agent私聊</span></div>
330
331
  <div class="nav-item" data-tooltip="会话管理" onclick="showPage('sessions')"><span class="icon">💬</span><span class="icon-text">会话管理</span></div>
331
332
  <div class="nav-item" data-tooltip="记忆管理" onclick="showPage('memory')"><span class="icon">🧠</span><span class="icon-text">记忆管理</span></div>
332
333
  <div class="nav-item" data-tooltip="权限管理" onclick="showPage('permissions')"><span class="icon">🔑</span><span class="icon-text">权限管理</span></div>
@@ -368,6 +369,7 @@ tr:hover{background:var(--surface2)}
368
369
  <script src="admin/admin-logs.js"></script>
369
370
  <script src="admin/admin-tasks.js"></script>
370
371
  <script src="admin/admin-org.js"></script>
372
+ <script src="admin/admin-agentchat.js"></script>
371
373
  <script src="admin/admin-system.js"></script>
372
374
  </body>
373
375
  </html>