acp-ts 1.2.5 → 1.2.6

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/dist/agentcp.d.ts CHANGED
@@ -177,6 +177,19 @@ declare class AgentCP implements IAgentCP {
177
177
  * @param limit 每次拉取数量上限,0 表示使用服务端默认值
178
178
  */
179
179
  pullAndStoreGroupMessages(groupId: string, afterMsgId?: number, limit?: number): Promise<GroupMessage[]>;
180
+ /**
181
+ * 将群组加入在线列表(不发送网络请求)。
182
+ */
183
+ addOnlineGroup(groupId: string): void;
184
+ /**
185
+ * 确保群组心跳定时器已启动(公开方法)。
186
+ */
187
+ ensureGroupHeartbeat(): void;
188
+ /**
189
+ * 向 group.ap 发送一次 register_online,告知当前客户端在线。
190
+ * 不带群组 ID,只需在启动或重连时调用一次。
191
+ */
192
+ groupRegisterOnline(): Promise<void>;
180
193
  /**
181
194
  * 加入群组会话(完整生命周期):
182
195
  * 1. register_online → 告知 group.ap 在线
package/dist/agentcp.js CHANGED
@@ -701,6 +701,29 @@ class AgentCP {
701
701
  // ============================================================
702
702
  // Group Session Lifecycle (register_online / pull / heartbeat / unregister)
703
703
  // ============================================================
704
+ /**
705
+ * 将群组加入在线列表(不发送网络请求)。
706
+ */
707
+ addOnlineGroup(groupId) {
708
+ this._onlineGroups.add(groupId);
709
+ }
710
+ /**
711
+ * 确保群组心跳定时器已启动(公开方法)。
712
+ */
713
+ ensureGroupHeartbeat() {
714
+ this._ensureHeartbeat();
715
+ }
716
+ /**
717
+ * 向 group.ap 发送一次 register_online,告知当前客户端在线。
718
+ * 不带群组 ID,只需在启动或重连时调用一次。
719
+ */
720
+ async groupRegisterOnline() {
721
+ if (!this.groupOps || !this._groupTargetAid) {
722
+ throw new Error('群组客户端未初始化,请先调用 initGroupClient');
723
+ }
724
+ await this.groupOps.registerOnline(this._groupTargetAid);
725
+ utils_1.logger.log(`[Group] registerOnline: 已通知 group.ap 在线`);
726
+ }
704
727
  /**
705
728
  * 加入群组会话(完整生命周期):
706
729
  * 1. register_online → 告知 group.ap 在线
@@ -711,11 +734,13 @@ class AgentCP {
711
734
  if (!this.groupOps || !this._groupTargetAid) {
712
735
  throw new Error('群组客户端未初始化,请先调用 initGroupClient');
713
736
  }
714
- // Step 1: register_online(仅通知 group.ap 在线,不再返回游标)
715
- await this.groupOps.registerOnline(this._groupTargetAid);
737
+ // 如果还没有在线群组,说明是首次加入,需要先 registerOnline
738
+ if (this._onlineGroups.size === 0) {
739
+ await this.groupOps.registerOnline(this._groupTargetAid);
740
+ }
716
741
  this._onlineGroups.add(groupId);
717
742
  utils_1.logger.log(`[Group] joinGroupSession: group=${groupId}`);
718
- // Step 2: 冷启动同步 — 拉取历史消息对齐,再进入批推送接收
743
+ // 冷启动同步 — 拉取历史消息对齐,再进入批推送接收
719
744
  try {
720
745
  const lastMsgId = this.getGroupLastMsgId(groupId);
721
746
  await this.pullAndStoreGroupMessages(groupId, lastMsgId, 50);
@@ -723,7 +748,7 @@ class AgentCP {
723
748
  catch (e) {
724
749
  utils_1.logger.warn(`[Group] cold-start sync failed: group=${groupId}`, e.message || e);
725
750
  }
726
- // Step 3: 启动心跳定时器(首次加入群组时启动)
751
+ // 启动心跳定时器(首次加入群组时启动)
727
752
  this._ensureHeartbeat();
728
753
  }
729
754
  /**
package/dist/agentmd.d.ts CHANGED
@@ -17,7 +17,7 @@ export interface AgentMdOptions {
17
17
  }
18
18
  /**
19
19
  * 从 AID 中提取显示名称
20
- * 例如: "alice.aid.show" -> "alice"
20
+ * 例如: "alice.agentcp.io" -> "alice"
21
21
  */
22
22
  export declare function extractDisplayName(aid: string): string;
23
23
  /**
package/dist/agentmd.js CHANGED
@@ -7,10 +7,10 @@ exports.extractDisplayName = extractDisplayName;
7
7
  exports.generateAgentMd = generateAgentMd;
8
8
  /**
9
9
  * 从 AID 中提取显示名称
10
- * 例如: "alice.aid.show" -> "alice"
10
+ * 例如: "alice.agentcp.io" -> "alice"
11
11
  */
12
12
  function extractDisplayName(aid) {
13
- const suffixes = ['.agentcp.io', '.aid.show', '.agentid.pub'];
13
+ const suffixes = ['.agentcp.io', '.agentid.pub'];
14
14
  for (const suffix of suffixes) {
15
15
  if (aid.endsWith(suffix)) {
16
16
  return aid.slice(0, -suffix.length);
package/dist/server.js CHANGED
@@ -151,25 +151,27 @@ async function doEnsureOnline(aid) {
151
151
  instance.agentWS.acceptInviteFromHeartbeat(invite.sessionId, invite.inviterAgentId, invite.inviteCode);
152
152
  }
153
153
  });
154
- // 心跳重连成功后,自动触发 WebSocket 重连 + 群组重新注册
154
+ // 心跳重连成功后,自动触发 WebSocket 重连 + 重新注册上线
155
155
  hb.onReconnect(() => {
156
156
  if (instance.agentWS) {
157
157
  utils_1.logger.log('[Server] 心跳重连成功,触发 WebSocket 重连...');
158
158
  instance.agentWS.reconnect().then(async () => {
159
- // WebSocket 重连成功后,重新注册所有在线群组
160
- // 断线期间 group.ap 会将在线状态过期,必须重新 register_online 才能收到推送
161
159
  const onlineGroups = instance.agentCP.getOnlineGroups();
162
160
  if (onlineGroups.length > 0) {
163
- utils_1.logger.log(`[Server] WebSocket 重连成功,重新注册 ${onlineGroups.length} 个在线群组...`);
164
- for (const groupId of onlineGroups) {
161
+ await instance.agentCP.groupRegisterOnline();
162
+ utils_1.logger.log(`[Server] WebSocket 重连成功,已重新注册上线,在线群组: ${onlineGroups.length}`);
163
+ // 后台异步拉取断线期间可能漏掉的消息
164
+ Promise.all(onlineGroups.map(async (groupId) => {
165
165
  try {
166
- await instance.agentCP.joinGroupSession(groupId);
167
- utils_1.logger.log(`[Server] 群组重新注册成功: ${groupId}`);
166
+ const lastMsgId = instance.agentCP.getGroupLastMsgId(groupId);
167
+ await instance.agentCP.pullAndStoreGroupMessages(groupId, lastMsgId, 50);
168
168
  }
169
169
  catch (e) {
170
- utils_1.logger.warn(`[Server] 群组重新注册失败: ${groupId}`, e.message || e);
170
+ utils_1.logger.warn(`[Server] reconnect sync failed: ${groupId}`, e.message || e);
171
171
  }
172
- }
172
+ })).then(() => {
173
+ utils_1.logger.log(`[Server] 重连后台消息同步完成`);
174
+ });
173
175
  }
174
176
  }).catch((err) => {
175
177
  utils_1.logger.error('[Server] WebSocket 重连失败:', err);
@@ -184,19 +186,22 @@ async function doEnsureOnline(aid) {
184
186
  instance.connectionConfig = newConnConfig;
185
187
  utils_1.logger.log('[Server] 重新鉴权成功,使用新 signature 重连 WebSocket...');
186
188
  await instance.agentWS.reconnect(newConnConfig.messageServer, newConnConfig.messageSignature);
187
- // 重连成功后重新注册所有在线群组
188
189
  const onlineGroups = instance.agentCP.getOnlineGroups();
189
190
  if (onlineGroups.length > 0) {
190
- utils_1.logger.log(`[Server] 重新鉴权重连成功,重新注册 ${onlineGroups.length} 个在线群组...`);
191
- for (const groupId of onlineGroups) {
191
+ await instance.agentCP.groupRegisterOnline();
192
+ utils_1.logger.log(`[Server] 重新鉴权重连成功,已重新注册上线,在线群组: ${onlineGroups.length}`);
193
+ // 后台异步拉取断线期间可能漏掉的消息
194
+ Promise.all(onlineGroups.map(async (groupId) => {
192
195
  try {
193
- await instance.agentCP.joinGroupSession(groupId);
194
- utils_1.logger.log(`[Server] 群组重新注册成功: ${groupId}`);
196
+ const lastMsgId = instance.agentCP.getGroupLastMsgId(groupId);
197
+ await instance.agentCP.pullAndStoreGroupMessages(groupId, lastMsgId, 50);
195
198
  }
196
199
  catch (e) {
197
- utils_1.logger.warn(`[Server] 群组重新注册失败: ${groupId}`, e.message || e);
200
+ utils_1.logger.warn(`[Server] reconnect sync failed: ${groupId}`, e.message || e);
198
201
  }
199
- }
202
+ })).then(() => {
203
+ utils_1.logger.log(`[Server] 重连后台消息同步完成`);
204
+ });
200
205
  }
201
206
  }
202
207
  catch (err) {
@@ -284,12 +289,33 @@ async function doEnsureOnline(aid) {
284
289
  return instance;
285
290
  }
286
291
  // 确保群组客户端已初始化
292
+ // 群组客户端初始化锁,防止并发请求重复初始化
293
+ const groupInitPromises = new Map();
287
294
  async function ensureGroupClient(instance) {
288
295
  if (instance.groupInitialized && instance.agentCP.groupClient)
289
296
  return;
290
297
  if (!instance.agentWS)
291
298
  throw new Error('WebSocket 未连接');
292
299
  const aid = instance.aid;
300
+ // 如果已有初始化在进行中,等待它完成
301
+ const existing = groupInitPromises.get(aid);
302
+ if (existing) {
303
+ await existing;
304
+ return;
305
+ }
306
+ const initPromise = doInitGroupClient(instance);
307
+ groupInitPromises.set(aid, initPromise);
308
+ try {
309
+ await initPromise;
310
+ }
311
+ finally {
312
+ groupInitPromises.delete(aid);
313
+ }
314
+ }
315
+ async function doInitGroupClient(instance) {
316
+ if (!instance.agentWS)
317
+ throw new Error('WebSocket 未连接');
318
+ const aid = instance.aid;
293
319
  // 计算 group target AID: group.{issuer}
294
320
  const parts = aid.split('.', 1);
295
321
  const issuer = aid.substring(parts[0].length + 1) || aid;
@@ -432,15 +458,27 @@ async function ensureGroupClient(instance) {
432
458
  utils_1.logger.warn('[Group] syncGroupList error:', e.message);
433
459
  }
434
460
  }
435
- // 为所有已加入群组注册上线(register_online + 拉取未读 + 启动心跳)
461
+ // 注册上线 + 将群组加入在线列表(不阻塞)
436
462
  const groups = instance.agentCP.getLocalGroupList();
437
- for (const group of groups) {
438
- try {
439
- await instance.agentCP.joinGroupSession(group.group_id);
440
- }
441
- catch (e) {
442
- utils_1.logger.warn(`[Group] joinGroupSession failed: ${group.group_id}`, e.message);
443
- }
463
+ if (groups.length > 0) {
464
+ await instance.agentCP.groupRegisterOnline();
465
+ for (const group of groups) {
466
+ instance.agentCP.addOnlineGroup(group.group_id);
467
+ }
468
+ instance.agentCP.ensureGroupHeartbeat();
469
+ utils_1.logger.log(`[Group] 已注册上线,在线群组: ${groups.length}`);
470
+ // 后台异步拉取历史消息,不阻塞启动流程
471
+ Promise.all(groups.map(async (group) => {
472
+ try {
473
+ const lastMsgId = instance.agentCP.getGroupLastMsgId(group.group_id);
474
+ await instance.agentCP.pullAndStoreGroupMessages(group.group_id, lastMsgId, 50);
475
+ }
476
+ catch (e) {
477
+ utils_1.logger.warn(`[Group] background sync failed: ${group.group_id}`, e.message);
478
+ }
479
+ })).then(() => {
480
+ utils_1.logger.log(`[Group] 后台消息同步完成,共 ${groups.length} 个群组`);
481
+ });
444
482
  }
445
483
  instance.groupInitialized = true;
446
484
  instance.groupSessionId = groupSessionId;
@@ -769,7 +807,7 @@ const indexHtml = `<!DOCTYPE html>
769
807
  function updateApSelect() {
770
808
  var sel = document.getElementById('apSelect');
771
809
  if (sel && sel.options.length === 0) {
772
- const options = ['agentcp.io', 'aid.show', 'agentid.pub'];
810
+ const options = ['agentcp.io', 'agentid.pub'];
773
811
  options.forEach(function(op) {
774
812
  var opt = document.createElement('option');
775
813
  opt.value = op;
@@ -870,6 +908,7 @@ const indexHtml = `<!DOCTYPE html>
870
908
  var data = await res.json();
871
909
  if (data.success) {
872
910
  showStatus(aid + ' 已上线,正在进入聊天...', 'success');
911
+ sessionStorage.setItem('chatEntry','1');
873
912
  window.location.href = '/chat';
874
913
  } else {
875
914
  showStatus(data.error || '上线失败', 'error');
@@ -881,7 +920,7 @@ const indexHtml = `<!DOCTYPE html>
881
920
  }
882
921
  }
883
922
 
884
- function enterChat(aid) { window.location.href = '/chat'; }
923
+ function enterChat(aid) { sessionStorage.setItem('chatEntry','1'); window.location.href = '/chat'; }
885
924
 
886
925
  async function goOffline(aid) {
887
926
  try {
@@ -1194,6 +1233,7 @@ const chatHtml = `<!DOCTYPE html>
1194
1233
  <div class="group-info-bar" id="groupInfoBar" style="display:none;">
1195
1234
  <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2"></path><circle cx="9" cy="7" r="4"></circle><path d="M23 21v-2a4 4 0 0 0-3-3.87"></path><path d="M16 3.13a4 4 0 0 1 0 7.75"></path></svg>
1196
1235
  <span id="groupInfoText">群组</span>
1236
+ <span id="groupMemberStats" style="font-size:11px;color:var(--t2);margin-left:4px;"></span>
1197
1237
  <span class="copy-link" id="groupInviteBtn" onclick="generateInviteLink()" title="生成邀请链接" style="display:none;">生成邀请链接</span>
1198
1238
  <span class="copy-link" id="groupCopyLinkBtn" onclick="copyGroupLink()" title="复制群链接" style="display:none;">复制群链接</span>
1199
1239
  <span class="copy-link" onclick="showGroupMembers()" title="查看成员">成员</span>
@@ -1384,14 +1424,39 @@ const chatHtml = `<!DOCTYPE html>
1384
1424
  if (type === 'human') return '/assets/human.png';
1385
1425
  return '/assets/agent.png';
1386
1426
  }
1387
- async function fetchAgentInfo(aid) {
1388
- if (agentInfoCache[aid]) return agentInfoCache[aid];
1389
- try {
1390
- var r = await fetch('/api/agent-info?aid=' + encodeURIComponent(aid));
1391
- var d = await r.json();
1392
- if (d.type || d.name) { agentInfoCache[aid] = d; }
1393
- return d;
1394
- } catch(e) { return { type:'', name:'', description:'', tags:[] }; }
1427
+ // 批量 agent info 请求:攒批 50ms 后合并发送一次
1428
+ var _agentInfoBatchQueue=[];
1429
+ var _agentInfoBatchTimer=null;
1430
+ function _flushAgentInfoBatch(){
1431
+ _agentInfoBatchTimer=null;
1432
+ var queue=_agentInfoBatchQueue;
1433
+ _agentInfoBatchQueue=[];
1434
+ var aids=queue.map(function(q){ return q.aid; });
1435
+ fetch('/api/agent-info-batch',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({aids:aids})})
1436
+ .then(function(r){ return r.json(); })
1437
+ .then(function(d){
1438
+ if(d.success&&d.data){
1439
+ queue.forEach(function(q){
1440
+ var info=d.data[q.aid]||{type:'',name:'',description:'',tags:[]};
1441
+ if(info.type||info.name) agentInfoCache[q.aid]=info;
1442
+ q.resolve(info);
1443
+ });
1444
+ } else {
1445
+ var empty={type:'',name:'',description:'',tags:[]};
1446
+ queue.forEach(function(q){ q.resolve(empty); });
1447
+ }
1448
+ })
1449
+ .catch(function(){
1450
+ var empty={type:'',name:'',description:'',tags:[]};
1451
+ queue.forEach(function(q){ q.resolve(empty); });
1452
+ });
1453
+ }
1454
+ function fetchAgentInfo(aid){
1455
+ if(agentInfoCache[aid]) return Promise.resolve(agentInfoCache[aid]);
1456
+ return new Promise(function(resolve){
1457
+ _agentInfoBatchQueue.push({aid:aid,resolve:resolve});
1458
+ if(!_agentInfoBatchTimer) _agentInfoBatchTimer=setTimeout(_flushAgentInfoBatch,50);
1459
+ });
1395
1460
  }
1396
1461
  async function deleteSession(e, sessionId){
1397
1462
  e.stopPropagation();
@@ -1429,6 +1494,9 @@ const chatHtml = `<!DOCTYPE html>
1429
1494
  function hideNewMsgTip(){ if(D.newMsgTip) D.newMsgTip.style.display='none'; }
1430
1495
 
1431
1496
  async function init(){
1497
+ // 刷新页面时强制回到身份管理页面
1498
+ if(!sessionStorage.getItem('chatEntry')){ window.location.href='/'; return; }
1499
+ sessionStorage.removeItem('chatEntry');
1432
1500
  initDom();
1433
1501
  // 配置 marked:支持换行、GFM
1434
1502
  if(typeof marked!=='undefined'&&marked.setOptions){
@@ -1994,6 +2062,7 @@ const chatHtml = `<!DOCTYPE html>
1994
2062
  D.title.textContent=name;
1995
2063
  D.groupInfoBar.style.display='flex';
1996
2064
  D.groupInfoText.textContent=name;
2065
+ $('groupMemberStats').textContent='';
1997
2066
  D.input.disabled=false;
1998
2067
  D.input.placeholder='输入群消息...';
1999
2068
  D.input.focus();
@@ -2009,7 +2078,7 @@ const chatHtml = `<!DOCTYPE html>
2009
2078
  gmsg.textContent='选择群组...';
2010
2079
  await fetch('/api/group/select',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({groupId:groupId,aid:S.aid})});
2011
2080
  } catch(e){}
2012
- // 获取群信息判断是否为创建者
2081
+ // 获取群信息判断是否为创建者,同时获取成员统计
2013
2082
  try {
2014
2083
  gmsg.textContent='获取群信息...';
2015
2084
  var r=await fetch('/api/group/info?groupId='+encodeURIComponent(groupId)+'&aid='+encodeURIComponent(S.aid));
@@ -2022,6 +2091,14 @@ const chatHtml = `<!DOCTYPE html>
2022
2091
  } else {
2023
2092
  $('groupCopyLinkBtn').style.display='';
2024
2093
  }
2094
+ // 展示成员统计
2095
+ if(d.member_stats){
2096
+ var ms=d.member_stats;
2097
+ var parts=[ms.total+'人'];
2098
+ if(ms.human) parts.push(ms.human+'人类');
2099
+ if(ms.agent) parts.push(ms.agent+'Agent');
2100
+ $('groupMemberStats').textContent='('+parts.join(' / ')+')';
2101
+ }
2025
2102
  } catch(e){
2026
2103
  // 获取失败时默认显示复制群链接
2027
2104
  $('groupCopyLinkBtn').style.display='';
@@ -2234,16 +2311,33 @@ const chatHtml = `<!DOCTYPE html>
2234
2311
  } else {
2235
2312
  D.msgs.scrollTop=prevScrollTop;
2236
2313
  }
2237
- // 异步加载未缓存的 agent info,加载完成后重新渲染以更新头像
2314
+ // 异步加载未缓存的 agent info,加载完成后局部更新头像和名字
2238
2315
  var unique=needFetch.filter(function(v,i,a){ return a.indexOf(v)===i; });
2239
- unique.forEach(function(aid){
2240
- fetchAgentInfo(aid).then(function(){
2241
- if(S.tab!=='group') return;
2242
- _lastGroupMsgSig='';
2243
- _lastGroupMsgs._forceRender=true;
2244
- renderGroupMsgs(_lastGroupMsgs);
2316
+ if(unique.length){
2317
+ fetchAgentInfo(unique[0]); // 触发批量请求(攒批机制会合并)
2318
+ unique.forEach(function(aid){
2319
+ fetchAgentInfo(aid).then(function(info){
2320
+ if(!info||S.tab!=='group') return;
2321
+ // 局部更新 DOM:找到该 sender 的所有消息,更新头像和名字
2322
+ var avatars=D.msgs.querySelectorAll('.msg-avatar[title="'+escH(aid)+'"]');
2323
+ var newSrc=getAvatarSrc(info.type);
2324
+ var displayName=(info.name)||aid;
2325
+ avatars.forEach(function(img){
2326
+ img.src=newSrc;
2327
+ img.title=displayName;
2328
+ // 更新同一消息里的名字
2329
+ var msgEl=img.closest('.message');
2330
+ if(msgEl){
2331
+ var meta=msgEl.querySelector('.msg-meta');
2332
+ if(meta&&!msgEl.classList.contains('sent')){
2333
+ var timeStr=meta.textContent.split(' · ')[1]||'';
2334
+ meta.textContent=displayName+' · '+timeStr;
2335
+ }
2336
+ }
2337
+ });
2338
+ });
2245
2339
  });
2246
- });
2340
+ }
2247
2341
  }
2248
2342
 
2249
2343
  // Group modals
@@ -2738,6 +2832,31 @@ async function handleRequest(req, res) {
2738
2832
  sendJson(res, info);
2739
2833
  return;
2740
2834
  }
2835
+ // 批量获取 agent info,优先读本地缓存,未缓存的后台预热
2836
+ if (pathname === '/api/agent-info-batch' && method === 'POST') {
2837
+ try {
2838
+ const body = await parseBody(req);
2839
+ const aids = body.aids || [];
2840
+ const result = {};
2841
+ const empty = { type: '', name: '', description: '', tags: [] };
2842
+ for (const aid of aids) {
2843
+ const cached = agentInfoCache.get(aid);
2844
+ if (cached && Date.now() - cached.cachedAt < AGENT_INFO_CACHE_TTL) {
2845
+ result[aid] = { type: cached.type, name: cached.name, description: cached.description, tags: cached.tags || [] };
2846
+ }
2847
+ else {
2848
+ result[aid] = cached ? { type: cached.type, name: cached.name, description: cached.description, tags: cached.tags || [] } : empty;
2849
+ // 后台异步拉取,下次请求就有了
2850
+ getAgentInfo(aid).catch(() => { });
2851
+ }
2852
+ }
2853
+ sendJson(res, { success: true, data: result });
2854
+ }
2855
+ catch (e) {
2856
+ sendJson(res, { success: false, error: e.message });
2857
+ }
2858
+ return;
2859
+ }
2741
2860
  // 获取远程 agent.md 原始内容
2742
2861
  if (pathname === '/api/agent-md-raw' && method === 'GET') {
2743
2862
  const aid = parsedUrl.query.aid;
@@ -3250,8 +3369,33 @@ async function handleRequest(req, res) {
3250
3369
  }
3251
3370
  const instance = await ensureOnline(aid);
3252
3371
  await ensureGroupClient(instance);
3253
- const info = await instance.agentCP.groupOps.getGroupInfo(instance.groupTargetAid, groupId);
3254
- sendJson(res, Object.assign({ success: true }, info));
3372
+ const [info, membersResult] = await Promise.all([
3373
+ instance.agentCP.groupOps.getGroupInfo(instance.groupTargetAid, groupId),
3374
+ instance.agentCP.groupOps.getMembers(instance.groupTargetAid, groupId).catch(() => ({ members: [] })),
3375
+ ]);
3376
+ // 只用本地缓存统计成员类型,不发远程请求,不阻塞响应
3377
+ let humanCount = 0, agentCount = 0;
3378
+ const members = membersResult.members || [];
3379
+ for (const m of members) {
3380
+ const memberAid = m.agent_id || '';
3381
+ if (!memberAid)
3382
+ continue;
3383
+ const cached = agentInfoCache.get(memberAid);
3384
+ if (cached) {
3385
+ if (cached.type === 'human')
3386
+ humanCount++;
3387
+ else if (cached.type === 'agent')
3388
+ agentCount++;
3389
+ }
3390
+ }
3391
+ sendJson(res, Object.assign(Object.assign({ success: true }, info), { member_stats: { total: members.length, human: humanCount, agent: agentCount } }));
3392
+ // 后台异步预热未缓存的 agent info,下次请求就有了
3393
+ for (const m of members) {
3394
+ const memberAid = m.agent_id || '';
3395
+ if (memberAid && !agentInfoCache.has(memberAid)) {
3396
+ getAgentInfo(memberAid).catch(() => { });
3397
+ }
3398
+ }
3255
3399
  }
3256
3400
  catch (e) {
3257
3401
  sendJson(res, { success: false, error: e.message });
@@ -3284,6 +3428,7 @@ async function handleRequest(req, res) {
3284
3428
  sendJson(res, Object.assign({ success: true }, result));
3285
3429
  }
3286
3430
  catch (e) {
3431
+ utils_1.logger.error(`[API] /api/group/send FAILED: error=${e.message}`);
3287
3432
  sendJson(res, { success: false, error: e.message });
3288
3433
  }
3289
3434
  return;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "acp-ts",
3
- "version": "1.2.5",
3
+ "version": "1.2.6",
4
4
  "description": "基于 ACP智能体通信协议 的智能体通信库,提供智能体身份管理和实时通信功能",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",