acp-ts 1.1.5 → 1.1.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
@@ -85,6 +85,11 @@ declare class AgentCP implements IAgentCP {
85
85
  * 设置群组事件处理器
86
86
  */
87
87
  setGroupEventHandler(handler: ACPGroupEventHandler): void;
88
+ /**
89
+ * 创建默认的群组事件处理器(防御性,仅打印日志)。
90
+ * 外部可通过 setGroupEventHandler 覆盖。
91
+ */
92
+ private _createDefaultGroupEventHandler;
88
93
  /**
89
94
  * 设置群组游标存储
90
95
  */
package/dist/agentcp.js CHANGED
@@ -333,6 +333,8 @@ class AgentCP {
333
333
  };
334
334
  this.groupClient = new group_1.ACPGroupClient(this.activeAid, sendFunc);
335
335
  this.groupOps = new group_1.GroupOperations(this.groupClient);
336
+ // 设置默认 event handler,防止通知被静默丢弃
337
+ this.groupClient.setEventHandler(this._createDefaultGroupEventHandler());
336
338
  }
337
339
  /**
338
340
  * 初始化跨AP群组客户端。
@@ -349,6 +351,8 @@ class AgentCP {
349
351
  };
350
352
  this.groupClient = new group_1.ACPGroupClient(this.activeAid, sendFunc);
351
353
  this.groupOps = new group_1.GroupOperations(this.groupClient);
354
+ // 设置默认 event handler,防止通知被静默丢弃
355
+ this.groupClient.setEventHandler(this._createDefaultGroupEventHandler());
352
356
  }
353
357
  /**
354
358
  * 处理群组协议消息路由。
@@ -393,6 +397,38 @@ class AgentCP {
393
397
  this.groupClient.setEventHandler(handler);
394
398
  }
395
399
  }
400
+ /**
401
+ * 创建默认的群组事件处理器(防御性,仅打印日志)。
402
+ * 外部可通过 setGroupEventHandler 覆盖。
403
+ */
404
+ _createDefaultGroupEventHandler() {
405
+ return {
406
+ onNewMessage(groupId, latestMsgId, sender, preview) {
407
+ console.log(`[Group][DefaultHandler] onNewMessage: group=${groupId} msgId=${latestMsgId} sender=${sender} preview=${preview}`);
408
+ },
409
+ onNewEvent(groupId, latestEventId, eventType, summary) {
410
+ console.log(`[Group][DefaultHandler] onNewEvent: group=${groupId} eventId=${latestEventId} type=${eventType}`);
411
+ },
412
+ onGroupInvite(groupId, groupAddress, invitedBy) {
413
+ console.log(`[Group][DefaultHandler] onGroupInvite: group=${groupId} address=${groupAddress} invitedBy=${invitedBy}`);
414
+ },
415
+ onJoinApproved(groupId, groupAddress) {
416
+ console.log(`[Group][DefaultHandler] onJoinApproved: group=${groupId} address=${groupAddress}`);
417
+ },
418
+ onJoinRejected(groupId, reason) {
419
+ console.log(`[Group][DefaultHandler] onJoinRejected: group=${groupId} reason=${reason}`);
420
+ },
421
+ onJoinRequestReceived(groupId, agentId, message) {
422
+ console.log(`[Group][DefaultHandler] onJoinRequestReceived: group=${groupId} agent=${agentId}`);
423
+ },
424
+ onGroupMessage(groupId, msg) {
425
+ console.log(`[Group][DefaultHandler] onGroupMessage: group=${groupId} msgId=${msg.msg_id} sender=${msg.sender}`);
426
+ },
427
+ onGroupEvent(groupId, evt) {
428
+ console.log(`[Group][DefaultHandler] onGroupEvent: group=${groupId} event=${evt.event_type}`);
429
+ },
430
+ };
431
+ }
396
432
  /**
397
433
  * 设置群组游标存储
398
434
  */
@@ -74,7 +74,7 @@ class ACPGroupClient {
74
74
  * Called by the message dispatch chain in AgentCP.
75
75
  */
76
76
  handleIncoming(payload) {
77
- var _a, _b;
77
+ var _a, _b, _c;
78
78
  // console.log(`[GroupClient] <<< handleIncoming raw payload (first 500 chars): ${payload.substring(0, 500)}`);
79
79
  let data;
80
80
  try {
@@ -95,6 +95,12 @@ class ACPGroupClient {
95
95
  clearTimeout(pending.timer);
96
96
  this._pendingReqs.delete(requestId);
97
97
  pending.resolve(resp);
98
+ // 如果响应同时携带 event 字段,也要 dispatch 通知
99
+ const event = (_b = data.event) !== null && _b !== void 0 ? _b : "";
100
+ if (event && this._handler != null) {
101
+ const notify = (0, types_1.parseGroupNotify)(data);
102
+ (0, events_1.dispatchAcpNotify)(this._handler, notify);
103
+ }
98
104
  return;
99
105
  }
100
106
  else {
@@ -102,12 +108,15 @@ class ACPGroupClient {
102
108
  }
103
109
  }
104
110
  // Try as notification (has event field)
105
- const event = (_b = data.event) !== null && _b !== void 0 ? _b : "";
111
+ const event = (_c = data.event) !== null && _c !== void 0 ? _c : "";
106
112
  if (event) {
107
113
  const notify = (0, types_1.parseGroupNotify)(data);
108
114
  if (this._handler != null) {
109
115
  (0, events_1.dispatchAcpNotify)(this._handler, notify);
110
116
  }
117
+ else {
118
+ console.warn(`[GroupClient] !!! notification event="${event}" dropped: no event handler registered. Call setEventHandler() first.`);
119
+ }
111
120
  return;
112
121
  }
113
122
  console.warn(`[GroupClient] !!! unhandled incoming message: no request_id and no event field`, JSON.stringify(data).substring(0, 300));
package/dist/server.js CHANGED
@@ -84,7 +84,8 @@ async function doEnsureOnline(aid) {
84
84
  const cp = new agentcp_1.AgentCP(globalApiUrl, '', globalDataDir || undefined, { persistMessages: true, persistGroupMessages: true });
85
85
  await cp.loadAid(aid);
86
86
  cp.setAutoGenerateAgentMd(true);
87
- cp.setAgentMdOptions({ type: 'human', tags: ['human', 'acp'] });
87
+ const customOpts = getAidMdOptionsForAid(aid);
88
+ cp.setAgentMdOptions(Object.assign({ type: 'human', tags: ['human', 'acp'] }, customOpts));
88
89
  const connConfig = await cp.online();
89
90
  console.log(`[Server] 自动上线 AID: ${aid}`);
90
91
  const hb = new heartbeat_1.HeartbeatClient(aid, connConfig.heartbeatServer, '');
@@ -223,6 +224,37 @@ async function ensureGroupClient(instance) {
223
224
  instance.agentWS.onRawMessage((message) => {
224
225
  return instance.agentCP.handleGroupMessage(message);
225
226
  });
227
+ // 注册群组事件处理器,确保 SDK 通知回调可靠触发
228
+ instance.agentCP.setGroupEventHandler({
229
+ onNewMessage(groupId, latestMsgId, sender, preview) {
230
+ console.log(`[Group] onNewMessage: group=${groupId} msgId=${latestMsgId} sender=${sender} preview=${preview}`);
231
+ // 收到新消息通知时,主动拉取并存储到本地
232
+ instance.agentCP.pullAndStoreGroupMessages(groupId, 20).catch(e => {
233
+ console.error(`[Group] auto-pull messages failed for group=${groupId}:`, e);
234
+ });
235
+ },
236
+ onNewEvent(groupId, latestEventId, eventType, summary) {
237
+ console.log(`[Group] onNewEvent: group=${groupId} eventId=${latestEventId} type=${eventType} summary=${summary}`);
238
+ },
239
+ onGroupInvite(groupId, groupAddress, invitedBy) {
240
+ console.log(`[Group] onGroupInvite: group=${groupId} address=${groupAddress} invitedBy=${invitedBy}`);
241
+ },
242
+ onJoinApproved(groupId, groupAddress) {
243
+ console.log(`[Group] onJoinApproved: group=${groupId} address=${groupAddress}`);
244
+ },
245
+ onJoinRejected(groupId, reason) {
246
+ console.log(`[Group] onJoinRejected: group=${groupId} reason=${reason}`);
247
+ },
248
+ onJoinRequestReceived(groupId, agentId, message) {
249
+ console.log(`[Group] onJoinRequestReceived: group=${groupId} agent=${agentId} msg=${message}`);
250
+ },
251
+ onGroupMessage(groupId, msg) {
252
+ console.log(`[Group] onGroupMessage: group=${groupId} msgId=${msg.msg_id} sender=${msg.sender}`);
253
+ },
254
+ onGroupEvent(groupId, evt) {
255
+ console.log(`[Group] onGroupEvent: group=${groupId} event=${evt.event_type}`);
256
+ },
257
+ });
226
258
  instance.groupInitialized = true;
227
259
  instance.groupSessionId = groupSessionId;
228
260
  instance.groupTargetAid = targetAid;
@@ -257,7 +289,7 @@ async function getAidStatusList() {
257
289
  }
258
290
  return result;
259
291
  }
260
- // agent.md 信息缓存 (aid -> { type, name, description, cachedAt })
292
+ // agent.md 信息缓存 (aid -> { type, name, description, tags, cachedAt })
261
293
  // 内存缓存 + 本地文件持久化,TTL 24 小时
262
294
  const agentInfoCache = new Map();
263
295
  const AGENT_INFO_CACHE_TTL = 24 * 60 * 60 * 1000; // 24 hours
@@ -274,7 +306,7 @@ function loadAgentInfoCacheFromDisk() {
274
306
  const now = Date.now();
275
307
  for (const [aid, info] of entries) {
276
308
  if (now - info.cachedAt < AGENT_INFO_CACHE_TTL) {
277
- agentInfoCache.set(aid, info);
309
+ agentInfoCache.set(aid, Object.assign(Object.assign({}, info), { tags: info.tags || [] }));
278
310
  }
279
311
  }
280
312
  console.log(`[Server] 已加载 agent info 缓存: ${agentInfoCache.size} 条`);
@@ -315,7 +347,7 @@ function fetchAgentMd(aid) {
315
347
  });
316
348
  }
317
349
  function parseAgentMdFrontmatter(content) {
318
- const result = { type: '', name: '', description: '' };
350
+ const result = { type: '', name: '', description: '', tags: [] };
319
351
  const match = content.match(/^---\s*\n([\s\S]*?)\n---/);
320
352
  if (!match)
321
353
  return result;
@@ -329,12 +361,20 @@ function parseAgentMdFrontmatter(content) {
329
361
  result.name = nameMatch[1].trim();
330
362
  if (descMatch)
331
363
  result.description = descMatch[1].trim();
364
+ // parse tags list
365
+ const tagsBlock = yaml.match(/^tags:\s*\n((?:\s+-\s+.*\n?)*)/m);
366
+ if (tagsBlock) {
367
+ const tagLines = tagsBlock[1].match(/^\s+-\s+(.+)$/gm);
368
+ if (tagLines) {
369
+ result.tags = tagLines.map(l => l.replace(/^\s+-\s+/, '').trim().replace(/^"(.*)"$/, '$1'));
370
+ }
371
+ }
332
372
  return result;
333
373
  }
334
374
  async function getAgentInfo(aid) {
335
375
  const cached = agentInfoCache.get(aid);
336
376
  if (cached && Date.now() - cached.cachedAt < AGENT_INFO_CACHE_TTL) {
337
- return { type: cached.type, name: cached.name, description: cached.description };
377
+ return { type: cached.type, name: cached.name, description: cached.description, tags: cached.tags || [] };
338
378
  }
339
379
  try {
340
380
  const md = await fetchAgentMd(aid);
@@ -346,10 +386,40 @@ async function getAgentInfo(aid) {
346
386
  catch (_a) {
347
387
  // 远程请求失败时,如果有过期缓存也先用着
348
388
  if (cached) {
349
- return { type: cached.type, name: cached.name, description: cached.description };
389
+ return { type: cached.type, name: cached.name, description: cached.description, tags: cached.tags || [] };
350
390
  }
351
- return { type: '', name: '', description: '' };
391
+ return { type: '', name: '', description: '', tags: [] };
392
+ }
393
+ }
394
+ // 每个 AID 的自定义 agent.md 选项 (昵称、描述)
395
+ function getAidMdOptionsPath() {
396
+ const dir = globalDataDir || process.cwd();
397
+ return path.join(dir, 'AIDs', '.aid-md-options.json');
398
+ }
399
+ function loadAidMdOptions() {
400
+ try {
401
+ const filePath = getAidMdOptionsPath();
402
+ if (fs.existsSync(filePath)) {
403
+ return JSON.parse(fs.readFileSync(filePath, 'utf-8'));
404
+ }
405
+ }
406
+ catch (_a) { }
407
+ return {};
408
+ }
409
+ function saveAidMdOptions(aid, opts) {
410
+ try {
411
+ const filePath = getAidMdOptionsPath();
412
+ const dir = path.dirname(filePath);
413
+ if (!fs.existsSync(dir))
414
+ fs.mkdirSync(dir, { recursive: true });
415
+ const all = loadAidMdOptions();
416
+ all[aid] = opts;
417
+ fs.writeFileSync(filePath, JSON.stringify(all, null, 2), 'utf-8');
352
418
  }
419
+ catch (_a) { }
420
+ }
421
+ function getAidMdOptionsForAid(aid) {
422
+ return loadAidMdOptions()[aid] || {};
353
423
  }
354
424
  // 消息与会话管理 — 每个 AID 独立 MessageStore
355
425
  let activeSessionId = null;
@@ -450,6 +520,10 @@ const indexHtml = `<!DOCTYPE html>
450
520
  <span class="dot-separator">.</span>
451
521
  <select id="apSelect"></select>
452
522
  </div>
523
+ <div style="display:flex;gap:8px;margin-top:8px;">
524
+ <input type="text" id="aidNickname" placeholder="昵称(选填)" style="flex:1;padding:8px 12px;border:1px solid #ddd;border-radius:6px;font-size:14px;">
525
+ <input type="text" id="aidDescription" placeholder="描述(选填)" style="flex:2;padding:8px 12px;border:1px solid #ddd;border-radius:6px;font-size:14px;">
526
+ </div>
453
527
  <button class="btn btn-primary" onclick="createAid()">注册 AID</button>
454
528
  </div>
455
529
 
@@ -558,10 +632,12 @@ const indexHtml = `<!DOCTYPE html>
558
632
  var ap = document.getElementById('apSelect').value;
559
633
  if (!ap) { showStatus('请选择 AP', 'error'); return; }
560
634
  var fullPrefix = prefix + '.' + ap;
635
+ var nickname = document.getElementById('aidNickname').value.trim();
636
+ var description = document.getElementById('aidDescription').value.trim();
561
637
  try {
562
- var res = await fetch('/api/aid/create', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ prefix: fullPrefix }) });
638
+ var res = await fetch('/api/aid/create', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ prefix: fullPrefix, nickname: nickname, description: description }) });
563
639
  var data = await res.json();
564
- if (data.success) { showStatus('AID 注册成功', 'success'); document.getElementById('newAid').value = ''; loadAidInfo(); }
640
+ if (data.success) { showStatus('AID 注册成功', 'success'); document.getElementById('newAid').value = ''; document.getElementById('aidNickname').value = ''; document.getElementById('aidDescription').value = ''; loadAidInfo(); }
565
641
  else { showStatus(data.error || '注册失败', 'error'); }
566
642
  } catch (e) { showStatus('注册失败: ' + e.message, 'error'); }
567
643
  }
@@ -659,12 +735,12 @@ const chatHtml = `<!DOCTYPE html>
659
735
 
660
736
  /* AID Group */
661
737
  .aid-group { border-bottom:1px solid var(--border); }
662
- .aid-group-header { padding:12px 14px; display:flex; align-items:center; cursor:pointer; background:linear-gradient(135deg,#f8fafc,#f1f5f9); user-select:none; border-left:3px solid var(--primary); }
663
- .aid-group-header:hover { background:linear-gradient(135deg,#eef2f7,#e8edf4); }
738
+ .aid-group-header { padding:12px 14px; display:flex; align-items:center; cursor:pointer; background:linear-gradient(135deg,#eef4ff,#e8f0fe); user-select:none; border-left:3px solid var(--primary); transition:all 0.2s; }
739
+ .aid-group-header:hover { background:linear-gradient(135deg,#dbeafe,#d0e4fd); }
664
740
  .aid-group-info { flex:1; min-width:0; margin-left:4px; }
665
- .aid-group-title { font-size:13px; font-weight:700; color:var(--t1); overflow:hidden; text-overflow:ellipsis; white-space:nowrap; display:block; }
666
- .aid-group-desc { font-size:10px; color:var(--t2); overflow:hidden; text-overflow:ellipsis; white-space:nowrap; margin-top:2px; display:block; }
667
- .aid-group-arrow { font-size:10px; color:var(--t2); transition:transform 0.2s; flex-shrink:0; }
741
+ .aid-group-title { font-size:13px; font-weight:700; color:#1e40af; background:linear-gradient(135deg,#dbeafe,#c7d7fe); padding:2px 8px; border-radius:6px; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; display:inline-block; max-width:100%; border:1px solid #bfdbfe; }
742
+ .aid-group-desc { font-size:10px; color:#6b7280; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; margin-top:3px; display:block; padding-left:2px; }
743
+ .aid-group-arrow { font-size:10px; color:var(--primary); transition:transform 0.2s; flex-shrink:0; }
668
744
  .aid-group-arrow.open { transform:rotate(90deg); }
669
745
  .aid-group-badge { font-size:10px; background:var(--primary); color:#fff; padding:1px 6px; border-radius:8px; margin-left:8px; flex-shrink:0; }
670
746
  .aid-group-add { background:none; border:1px solid var(--border); color:var(--t2); width:22px; height:22px; border-radius:4px; cursor:pointer; font-size:14px; line-height:20px; text-align:center; margin-left:6px; flex-shrink:0; }
@@ -672,24 +748,25 @@ const chatHtml = `<!DOCTYPE html>
672
748
  .aid-group-del { background:none; border:none; color:var(--t2); width:20px; height:20px; border-radius:4px; cursor:pointer; font-size:12px; line-height:20px; text-align:center; margin-left:4px; flex-shrink:0; display:none; }
673
749
  .aid-group-header:hover .aid-group-del { display:block; }
674
750
  .aid-group-del:hover { color:#dc3545; background:#ffebeb; }
675
- .session-del { position:absolute; right:8px; top:12px; background:none; border:none; color:var(--t2); font-size:12px; cursor:pointer; display:none; padding:2px; }
751
+ .session-del { position:absolute; right:8px; top:50%; transform:translateY(-50%); background:none; border:none; color:var(--t2); font-size:12px; cursor:pointer; display:none; padding:2px; }
676
752
  .session-item:hover .session-del { display:block; }
677
753
  .session-del:hover { color:#dc3545; }
678
- .aid-group-sessions { display:none; }
754
+ .aid-group-sessions { display:none; background:#fafbfc; }
679
755
  .aid-group-sessions.open { display:block; }
680
756
 
681
- .aid-group-avatar { width:34px; height:34px; border-radius:50%; object-fit:cover; flex-shrink:0; margin-right:8px; box-shadow:0 1px 3px rgba(0,0,0,0.12); }
757
+ .aid-group-avatar { width:36px; height:36px; border-radius:50%; object-fit:cover; flex-shrink:0; margin-right:8px; box-shadow:0 1px 4px rgba(37,99,235,0.18); border:2px solid #bfdbfe; }
682
758
 
683
- .session-item { padding:10px 14px 10px 32px; border-bottom:1px solid #f3f4f6; cursor:pointer; transition:background 0.15s; position:relative; }
684
- .session-item::before { content:''; position:absolute; left:18px; top:16px; width:6px; height:6px; border-radius:50%; background:var(--border); }
685
- .session-item:hover { background:#f5f7fa; }
759
+ .session-item { padding:10px 14px 10px 32px; border-bottom:1px solid #f0f1f3; cursor:pointer; transition:all 0.15s; position:relative; }
760
+ .session-item::before { content:''; position:absolute; left:18px; top:50%; transform:translateY(-50%); width:6px; height:6px; border-radius:50%; background:#d1d5db; }
761
+ .session-item:hover { background:#f0f5ff; }
686
762
  .session-item.active { background:#eff6ff; border-left:3px solid var(--primary); padding-left:29px; }
687
- .session-item.active::before { background:var(--primary); }
688
- .session-peer { font-weight:400; font-size:12px; color:var(--t1); overflow:hidden; text-overflow:ellipsis; white-space:nowrap; padding-left:10px; }
689
- .session-meta { font-size:10px; color:var(--t2); margin-top:2px; display:flex; align-items:center; gap:6px; padding-left:10px; }
690
- .tag { font-size:9px; padding:1px 5px; border-radius:3px; color:#fff; }
763
+ .session-item.active::before { background:var(--primary); box-shadow:0 0 0 2px rgba(37,99,235,0.2); }
764
+ .session-peer { font-weight:500; font-size:12px; color:var(--t1); overflow:hidden; text-overflow:ellipsis; white-space:nowrap; padding-left:10px; background:#f1f5f9; border-radius:4px; padding:3px 8px 3px 10px; border:1px solid #e8ecf1; }
765
+ .session-item.active .session-peer { background:#dbeafe; border-color:#bfdbfe; color:#1e40af; }
766
+ .session-meta { font-size:10px; color:var(--t2); margin-top:4px; display:flex; align-items:center; gap:6px; padding-left:10px; }
767
+ .tag { font-size:9px; padding:1px 5px; border-radius:3px; color:#fff; font-weight:600; letter-spacing:0.3px; }
691
768
  .tag.outgoing { background:var(--ok); }
692
- .tag.incoming { background:var(--t2); }
769
+ .tag.incoming { background:#8b5cf6; }
693
770
 
694
771
  /* Chat Area */
695
772
  .chat-area { flex:1; display:flex; flex-direction:column; background:var(--chat-bg); min-width:0; }
@@ -755,7 +832,8 @@ const chatHtml = `<!DOCTYPE html>
755
832
  .bubble ul, .bubble ol { padding-left:1.5em; margin-bottom:0.5em; }
756
833
  .bubble li { margin-bottom:0.2em; }
757
834
  .bubble blockquote { margin:0.5em 0; padding-left:1em; border-left:4px solid rgba(0,0,0,0.1); color:var(--t2); }
758
- .bubble a { color:var(--primary); text-decoration:none; } .bubble a:hover { text-decoration:underline; }
835
+ .bubble a { color:var(--primary); text-decoration:underline; } .bubble a:hover { opacity:0.85; }
836
+ .message.sent .bubble a { color:#fff; } .message.sent .bubble a:hover { opacity:0.85; }
759
837
  .bubble img { max-width:100%; border-radius:4px; }
760
838
  .bubble code { background:rgba(0,0,0,0.1); padding:2px 4px; border-radius:3px; font-family:monospace; font-size:0.9em; }
761
839
  .bubble pre { background:#2d2d2d; color:#fff; padding:12px; border-radius:6px; overflow-x:auto; margin:8px 0; }
@@ -908,9 +986,9 @@ const chatHtml = `<!DOCTYPE html>
908
986
  </div>
909
987
  </div>
910
988
  <div class="modal-overlay" id="membersModal">
911
- <div class="modal">
989
+ <div class="modal" style="max-width:520px;">
912
990
  <h3>群组成员</h3>
913
- <div id="membersList" style="max-height:300px;overflow-y:auto;margin-bottom:16px;font-size:13px;"></div>
991
+ <div id="membersList" style="max-height:400px;overflow-y:auto;margin-bottom:16px;font-size:13px;"></div>
914
992
  <div class="modal-btns">
915
993
  <button class="mbtn mbtn-cancel" onclick="hideMembersModal()">关闭</button>
916
994
  </div>
@@ -942,7 +1020,7 @@ const chatHtml = `<!DOCTYPE html>
942
1020
  var d = await r.json();
943
1021
  if (d.type || d.name) { agentInfoCache[aid] = d; }
944
1022
  return d;
945
- } catch(e) { return { type:'', name:'', description:'' }; }
1023
+ } catch(e) { return { type:'', name:'', description:'', tags:[] }; }
946
1024
  }
947
1025
  async function deleteSession(e, sessionId){
948
1026
  e.stopPropagation();
@@ -1040,12 +1118,17 @@ const chatHtml = `<!DOCTYPE html>
1040
1118
  var _pollCount=0;
1041
1119
  async function poll(){
1042
1120
  try {
1043
- var [sr,mr,wr] = await Promise.all([fetch('/api/sessions'),fetch('/api/messages'),fetch('/api/ws/status')]);
1044
- var sd=await sr.json(), md=await mr.json(), wd=await wr.json();
1045
- if(sd.sessions) updateSessions(sd.sessions, sd.activeSessionId);
1046
- S.closed=md.closed||false;
1047
- if(md.messages) renderMsgs(md.messages, S.closed);
1121
+ var wr=await fetch('/api/ws/status');
1122
+ var wd=await wr.json();
1048
1123
  updateDot(wd.status);
1124
+ // P2P会话和消息仅在P2P标签页时刷新
1125
+ if(S.tab==='p2p'){
1126
+ var [sr,mr] = await Promise.all([fetch('/api/sessions'),fetch('/api/messages')]);
1127
+ var sd=await sr.json(), md=await mr.json();
1128
+ if(sd.sessions) updateSessions(sd.sessions, sd.activeSessionId);
1129
+ S.closed=md.closed||false;
1130
+ if(md.messages) renderMsgs(md.messages, S.closed);
1131
+ }
1049
1132
  // 每5次轮询刷新一次AID在线状态
1050
1133
  if(++_pollCount%5===0){
1051
1134
  var ar=await fetch('/api/aid'); var ad=await ar.json();
@@ -1273,6 +1356,8 @@ const chatHtml = `<!DOCTYPE html>
1273
1356
  D.groupInfoBar.style.display=S.activeGroupId?'flex':'none';
1274
1357
  D.input.placeholder='输入群消息...';
1275
1358
  D.input.disabled=!S.activeGroupId;
1359
+ D.msgs.dataset.s='';
1360
+ _lastGroupMsgSig='';
1276
1361
  initGroupClient();
1277
1362
  pollGroupList();
1278
1363
  if(S.activeGroupId) pollGroupMessages();
@@ -1282,6 +1367,17 @@ const chatHtml = `<!DOCTYPE html>
1282
1367
  D.groupInfoBar.style.display='none';
1283
1368
  D.input.placeholder='输入消息...';
1284
1369
  D.input.disabled=false;
1370
+ _lastGroupMsgSig='';
1371
+ // 切回P2P时立即刷新消息
1372
+ D.msgs.dataset.s='';
1373
+ if(S.sid){
1374
+ fetch('/api/messages').then(function(r){ return r.json(); }).then(function(d){
1375
+ S.closed=d.closed||false;
1376
+ if(d.messages) renderMsgs(d.messages, S.closed);
1377
+ }).catch(function(){});
1378
+ } else {
1379
+ D.msgs.innerHTML='';
1380
+ }
1285
1381
  }
1286
1382
  }
1287
1383
 
@@ -1477,6 +1573,62 @@ const chatHtml = `<!DOCTYPE html>
1477
1573
  } catch(e){ alert('生成邀请码失败: '+e.message); }
1478
1574
  }
1479
1575
 
1576
+ function copyMemberAid(btn,aid){
1577
+ navigator.clipboard.writeText(aid).then(function(){
1578
+ btn.textContent='已复制';
1579
+ setTimeout(function(){ btn.textContent='复制'; },1200);
1580
+ });
1581
+ }
1582
+
1583
+ async function openAgentMdPage(aid){
1584
+ try {
1585
+ var r=await fetch('/api/agent-md-raw?aid='+encodeURIComponent(aid));
1586
+ var d=await r.json();
1587
+ if(!d.success||!d.content){ alert(d.error||'获取 agent.md 失败'); return; }
1588
+ var md=d.content;
1589
+ // 简单 markdown 渲染
1590
+ function renderMd(src){
1591
+ var h=escH(src);
1592
+ // headings
1593
+ h=h.replace(/^######\\s+(.+)$/gm,'<h6>$1</h6>');
1594
+ h=h.replace(/^#####\\s+(.+)$/gm,'<h5>$1</h5>');
1595
+ h=h.replace(/^####\\s+(.+)$/gm,'<h4>$1</h4>');
1596
+ h=h.replace(/^###\\s+(.+)$/gm,'<h3>$1</h3>');
1597
+ h=h.replace(/^##\\s+(.+)$/gm,'<h2>$1</h2>');
1598
+ h=h.replace(/^#\\s+(.+)$/gm,'<h1>$1</h1>');
1599
+ // bold & italic
1600
+ h=h.replace(/\\*\\*(.+?)\\*\\*/g,'<strong>$1</strong>');
1601
+ h=h.replace(/\\*(.+?)\\*/g,'<em>$1</em>');
1602
+ // blockquote
1603
+ h=h.replace(/^&gt;\\s?(.+)$/gm,'<blockquote style="border-left:3px solid #ddd;padding-left:12px;color:#666;margin:8px 0;">$1</blockquote>');
1604
+ // list items
1605
+ h=h.replace(/^-\\s+(.+)$/gm,'<li>$1</li>');
1606
+ // code inline
1607
+ var bt=String.fromCharCode(96);
1608
+ h=h.replace(new RegExp(bt+'([^'+bt+']+)'+bt,'g'),'<code style="background:#f5f5f5;padding:1px 4px;border-radius:3px;font-size:12px;">$1</code>');
1609
+ // frontmatter block: hide ---...---
1610
+ h=h.replace(/^---[\\s\\S]*?---\\s*/,'');
1611
+ // paragraphs
1612
+ h=h.replace(/\\n\\n/g,'</p><p>');
1613
+ h='<p>'+h+'</p>';
1614
+ return h;
1615
+ }
1616
+ var html='<!DOCTYPE html><html><head><meta charset="utf-8"><title>'+escH(aid)+' - Agent Profile</title>'
1617
+ +'<style>body{font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,sans-serif;max-width:720px;margin:40px auto;padding:0 20px;color:#333;line-height:1.6;}'
1618
+ +'h1{border-bottom:2px solid #eee;padding-bottom:8px;}h2{border-bottom:1px solid #eee;padding-bottom:6px;margin-top:24px;}'
1619
+ +'ul{padding-left:20px;}li{margin:4px 0;}blockquote{margin:12px 0;}'
1620
+ +'pre{background:#f5f5f5;padding:12px;border-radius:6px;overflow-x:auto;}'
1621
+ +'.aid-badge{display:inline-block;background:#e8f4fd;color:#0969da;padding:2px 8px;border-radius:10px;font-size:12px;font-family:monospace;margin-bottom:16px;}'
1622
+ +'</style></head><body>'
1623
+ +'<div class="aid-badge">'+escH(aid)+'</div>'
1624
+ +renderMd(md)
1625
+ +'</body></html>';
1626
+ var w=window.open('','_blank');
1627
+ if(w){ w.document.write(html); w.document.close(); }
1628
+ else { alert('弹窗被拦截,请允许弹窗后重试'); }
1629
+ } catch(e){ alert('获取 agent.md 失败: '+e.message); }
1630
+ }
1631
+
1480
1632
  async function showGroupMembers(){
1481
1633
  if(!S.activeGroupId) return;
1482
1634
  try {
@@ -1485,10 +1637,62 @@ const chatHtml = `<!DOCTYPE html>
1485
1637
  if(d.members){
1486
1638
  var html=d.members.map(function(m){
1487
1639
  var aid=m.agent_id||m;
1488
- var role=m.role?' ('+m.role+')':'';
1489
- return '<div style="padding:6px 0;border-bottom:1px solid #f3f4f6;font-family:monospace;font-size:12px;">'+escH(typeof aid==='string'?aid:JSON.stringify(aid))+role+'</div>';
1640
+ if(typeof aid!=='string') aid=JSON.stringify(aid);
1641
+ var role=m.role||'';
1642
+ var cachedInfo=agentInfoCache[aid];
1643
+ var avatarSrc=getAvatarSrc(cachedInfo?cachedInfo.type:'');
1644
+ var displayName=(cachedInfo&&cachedInfo.name)?cachedInfo.name:aid.split('.')[0];
1645
+ var typeTags='';
1646
+ if(cachedInfo&&cachedInfo.tags&&cachedInfo.tags.length){
1647
+ typeTags=cachedInfo.tags.map(function(t){ return '<span style="display:inline-block;background:#e8f4fd;color:#0969da;padding:1px 6px;border-radius:8px;font-size:10px;margin-right:4px;">'+escH(t)+'</span>'; }).join('');
1648
+ } else if(cachedInfo&&cachedInfo.type){
1649
+ typeTags='<span style="display:inline-block;background:#e8f4fd;color:#0969da;padding:1px 6px;border-radius:8px;font-size:10px;">'+escH(cachedInfo.type)+'</span>';
1650
+ }
1651
+ if(role){ typeTags+='<span style="display:inline-block;background:#fff3cd;color:#856404;padding:1px 6px;border-radius:8px;font-size:10px;margin-left:4px;">'+escH(role)+'</span>'; }
1652
+ var safeId='member-'+escH(aid).replace(/\\./g,'_');
1653
+ return '<div id="'+safeId+'" style="padding:10px 0;border-bottom:1px solid #f3f4f6;display:flex;align-items:center;gap:10px;">'
1654
+ +'<img src="'+avatarSrc+'" style="width:36px;height:36px;border-radius:50%;flex-shrink:0;" class="member-avatar" data-aid="'+escH(aid)+'">'
1655
+ +'<div style="flex:1;min-width:0;">'
1656
+ +'<div style="display:flex;align-items:center;gap:6px;flex-wrap:wrap;">'
1657
+ +'<span style="font-size:13px;font-weight:500;" class="member-name" data-aid="'+escH(aid)+'">'+escH(displayName)+'</span>'
1658
+ +'<span class="member-tags" data-aid="'+escH(aid)+'">'+typeTags+'</span>'
1659
+ +'</div>'
1660
+ +'<div style="font-size:11px;color:var(--t2);font-family:monospace;margin-top:2px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;">'+escH(aid)+'</div>'
1661
+ +'</div>'
1662
+ +'<div style="display:flex;gap:4px;flex-shrink:0;">'
1663
+ +'<button class="mbtn mbtn-ok" style="padding:4px 10px;font-size:11px;" onclick="copyMemberAid(this,\\''+escH(aid)+'\\')">复制</button>'
1664
+ +'<button class="mbtn mbtn-cancel" style="padding:4px 10px;font-size:11px;" onclick="openAgentMdPage(\\''+escH(aid)+'\\')">查看</button>'
1665
+ +'</div></div>';
1490
1666
  }).join('');
1491
1667
  $('membersList').innerHTML=html||'<div style="color:#999;">暂无成员</div>';
1668
+ // 异步加载未缓存的 agent info
1669
+ d.members.forEach(function(m){
1670
+ var aid=m.agent_id||m;
1671
+ if(typeof aid!=='string') aid=JSON.stringify(aid);
1672
+ if(!aid||agentInfoCache[aid]) return;
1673
+ fetchAgentInfo(aid).then(function(info){
1674
+ if(!info||(!info.name&&!info.type)) return;
1675
+ var safeId='member-'+aid.replace(/\\./g,'_');
1676
+ var el=document.getElementById(safeId);
1677
+ if(!el) return;
1678
+ var avatarEl=el.querySelector('.member-avatar[data-aid="'+aid+'"]');
1679
+ var nameEl=el.querySelector('.member-name[data-aid="'+aid+'"]');
1680
+ var tagsEl=el.querySelector('.member-tags[data-aid="'+aid+'"]');
1681
+ if(avatarEl) avatarEl.src=getAvatarSrc(info.type);
1682
+ if(nameEl) nameEl.textContent=info.name||aid.split('.')[0];
1683
+ if(tagsEl){
1684
+ var tags='';
1685
+ if(info.tags&&info.tags.length){
1686
+ tags=info.tags.map(function(t){ return '<span style="display:inline-block;background:#e8f4fd;color:#0969da;padding:1px 6px;border-radius:8px;font-size:10px;margin-right:4px;">'+escH(t)+'</span>'; }).join('');
1687
+ } else if(info.type){
1688
+ tags='<span style="display:inline-block;background:#e8f4fd;color:#0969da;padding:1px 6px;border-radius:8px;font-size:10px;">'+escH(info.type)+'</span>';
1689
+ }
1690
+ // 保留已有的 role tag
1691
+ var existingRole=tagsEl.querySelector('span[style*="fff3cd"]');
1692
+ tagsEl.innerHTML=tags+(existingRole?existingRole.outerHTML:'');
1693
+ }
1694
+ });
1695
+ });
1492
1696
  } else { $('membersList').innerHTML='<div style="color:#999;">获取失败</div>'; }
1493
1697
  $('membersModal').classList.add('show');
1494
1698
  } catch(e){ alert('获取成员失败: '+e.message); }
@@ -1676,6 +1880,22 @@ async function handleRequest(req, res) {
1676
1880
  sendJson(res, info);
1677
1881
  return;
1678
1882
  }
1883
+ // 获取远程 agent.md 原始内容
1884
+ if (pathname === '/api/agent-md-raw' && method === 'GET') {
1885
+ const aid = parsedUrl.query.aid;
1886
+ if (!aid) {
1887
+ sendJson(res, { success: false, error: '缺少 aid' });
1888
+ return;
1889
+ }
1890
+ try {
1891
+ const md = await fetchAgentMd(aid);
1892
+ sendJson(res, { success: true, content: md });
1893
+ }
1894
+ catch (e) {
1895
+ sendJson(res, { success: false, error: e.message || '获取失败' });
1896
+ }
1897
+ return;
1898
+ }
1679
1899
  if (pathname === '/api/aid' && method === 'GET') {
1680
1900
  try {
1681
1901
  const aidList = await datamanager_1.CertAndKeyStore.getAids();
@@ -1752,6 +1972,17 @@ async function handleRequest(req, res) {
1752
1972
  try {
1753
1973
  const created = await agentCP.createAid(aid);
1754
1974
  currentAid = created;
1975
+ // 保存自定义昵称和描述
1976
+ const nickname = (body.nickname || '').trim();
1977
+ const description = (body.description || '').trim();
1978
+ if (nickname || description) {
1979
+ const opts = {};
1980
+ if (nickname)
1981
+ opts.name = nickname;
1982
+ if (description)
1983
+ opts.description = description;
1984
+ saveAidMdOptions(created, opts);
1985
+ }
1755
1986
  sendJson(res, { success: true, aid: created });
1756
1987
  }
1757
1988
  catch (createErr) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "acp-ts",
3
- "version": "1.1.5",
3
+ "version": "1.1.6",
4
4
  "description": "基于 ACP智能体通信协议 的智能体通信库,提供智能体身份管理和实时通信功能",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",