evolclaw 2.8.0 → 2.8.2

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.
@@ -35,16 +35,45 @@ export class AUNChannel {
35
35
  recallHandler;
36
36
  connected = false;
37
37
  traceStream = null;
38
- traceDate = ''; // 当前 trace 文件对应的日期 (YYYYMMDD)
38
+ traceHourTag = ''; // 当前 trace 文件对应的小时标识 (YYYYMMDD-HH)
39
39
  trace(dir, event, data) {
40
40
  if (!this.config.aunTrace)
41
41
  return;
42
42
  this.rotateTraceIfNeeded();
43
43
  if (!this.traceStream)
44
44
  return;
45
- const line = JSON.stringify({ ts: localTimestamp(), dir, event, data });
45
+ // 自动从 data 推断顶层字段(self_aid / peer_aid / group_id / task_id / chatmode),
46
+ // 便于 jq 过滤:`jq 'select(.task_id == "task-xxx")'`
47
+ const d = (data && typeof data === 'object') ? data : {};
48
+ const payload = d.payload ?? {};
49
+ const topContext = {
50
+ self_aid: this._aid ?? this.config.aid,
51
+ };
52
+ // peer / group 识别
53
+ const peerAid = d.to ?? d.from ?? d.sender_aid ?? payload.to;
54
+ if (peerAid)
55
+ topContext.peer_aid = peerAid;
56
+ const groupId = d.group_id ?? payload.group_id;
57
+ if (groupId)
58
+ topContext.group_id = groupId;
59
+ // task_id / chatmode(message.send / thought.put / status 都可能有)
60
+ const taskId = payload.task_id ?? d.context?.id ?? d.data?.task_id;
61
+ if (taskId)
62
+ topContext.task_id = taskId;
63
+ const chatmode = payload.chatmode;
64
+ if (chatmode)
65
+ topContext.chatmode = chatmode;
66
+ const line = JSON.stringify({ ts: localTimestamp(), dir, event, ...topContext, data });
46
67
  this.traceStream.write(line + '\n');
47
68
  }
69
+ /** 日志前缀(含 self aid 简称,多实例可识别) */
70
+ logPrefix() {
71
+ const aid = this._aid ?? this.config.aid;
72
+ if (!aid)
73
+ return '[AUN]';
74
+ const short = aid.split('.')[0] || aid;
75
+ return `[AUN ${short}]`;
76
+ }
48
77
  /**
49
78
  * 统一的 RPC 调用包装:自动记录 OUT 发送、.ok 结果、.error 错误(含 trace + evolclaw.log 失败日志)。
50
79
  * 所有 client.call() 都应通过此方法调用,保证 aun-trace 里每个 OUT 调用都有"发+收/错"成对记录。
@@ -68,22 +97,43 @@ export class AUNChannel {
68
97
  code: e?.code,
69
98
  name: e?.name,
70
99
  });
71
- logger.warn(`[AUN] rpc ${method} failed: ${e?.name ?? ''}(${e?.code ?? ''}) ${e?.message ?? e}`);
100
+ logger.warn(`${this.logPrefix()} rpc ${method} failed: ${e?.name ?? ''}(${e?.code ?? ''}) ${e?.message ?? e}`);
72
101
  throw e;
73
102
  }
74
103
  }
104
+ static AUN_TRACE_RE = /^aun-\d{8}-\d{2}\.log$/;
105
+ static AUN_RETAIN_HOURS = 12;
75
106
  rotateTraceIfNeeded() {
76
107
  const d = new Date();
77
- const today = `${d.getFullYear()}${String(d.getMonth() + 1).padStart(2, '0')}${String(d.getDate()).padStart(2, '0')}`;
78
- if (this.traceDate === today && this.traceStream)
108
+ const pad = (n) => String(n).padStart(2, '0');
109
+ const hourTag = `${d.getFullYear()}${pad(d.getMonth() + 1)}${pad(d.getDate())}-${pad(d.getHours())}`;
110
+ if (this.traceHourTag === hourTag && this.traceStream)
79
111
  return;
80
112
  if (this.traceStream) {
81
113
  this.traceStream.end();
82
114
  this.traceStream = null;
83
115
  }
84
- this.traceDate = today;
85
- const logPath = path.join(resolvePaths().logs, `aun-${today}.log`);
116
+ this.traceHourTag = hourTag;
117
+ const logPath = path.join(resolvePaths().logs, `aun-${hourTag}.log`);
86
118
  this.traceStream = fs.createWriteStream(logPath, { flags: 'a' });
119
+ this.cleanupOldTraceLogs();
120
+ }
121
+ cleanupOldTraceLogs() {
122
+ const logsDir = resolvePaths().logs;
123
+ const cutoff = Date.now() - AUNChannel.AUN_RETAIN_HOURS * 60 * 60 * 1000;
124
+ try {
125
+ for (const name of fs.readdirSync(logsDir)) {
126
+ if (!AUNChannel.AUN_TRACE_RE.test(name))
127
+ continue;
128
+ try {
129
+ const full = path.join(logsDir, name);
130
+ if (fs.statSync(full).mtimeMs < cutoff)
131
+ fs.unlinkSync(full);
132
+ }
133
+ catch { }
134
+ }
135
+ }
136
+ catch { }
87
137
  }
88
138
  /** 判断 channelId 是否为群组 ID
89
139
  * - 新格式:group.{issuer}/{group_no|group_name}
@@ -104,6 +154,18 @@ export class AUNChannel {
104
154
  return undefined;
105
155
  return trimmed.split('.')[0] || trimmed;
106
156
  }
157
+ /** 同步获取对端显示名(仅从缓存,不触发网络请求)。用于日志中补充对端标识。
158
+ * 返回 `shortAid(displayName)` 或 `shortAid`,若 aid 为空返回 '?'
159
+ */
160
+ peerLabel(aid) {
161
+ if (!aid)
162
+ return '?';
163
+ const bareAid = aid.includes(':') ? aid.substring(0, aid.indexOf(':')) : aid;
164
+ const short = this.getShortAid(bareAid) ?? bareAid;
165
+ const cached = this.peerInfoCache.get(bareAid);
166
+ const name = cached?.name;
167
+ return name && name !== short ? `${short}(${name})` : short;
168
+ }
107
169
  extractTextPayload(payload) {
108
170
  if (typeof payload === 'string')
109
171
  return payload;
@@ -150,6 +212,20 @@ export class AUNChannel {
150
212
  }
151
213
  return false;
152
214
  }
215
+ /** 从正文提取 @aid(AID 格式:至少三段域名),用于出站填充 payload.mentions */
216
+ static MENTION_AID_RE = /@([a-zA-Z0-9](?:[a-zA-Z0-9-]*[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]*[a-zA-Z0-9])?){2,})/g;
217
+ extractMentionAidsFromText(text) {
218
+ const out = [];
219
+ const seen = new Set();
220
+ for (const m of text.matchAll(AUNChannel.MENTION_AID_RE)) {
221
+ const aid = m[1];
222
+ if (!seen.has(aid)) {
223
+ seen.add(aid);
224
+ out.push(aid);
225
+ }
226
+ }
227
+ return out;
228
+ }
153
229
  buildGroupReplyContext(taskId, senderAid, encrypted) {
154
230
  const replyContext = { metadata: { encrypted } };
155
231
  if (taskId)
@@ -160,7 +236,7 @@ export class AUNChannel {
160
236
  acknowledgeImmediately(messageId, seq) {
161
237
  if (seq != null && this.client) {
162
238
  this.client.call('message.ack', { seq }).catch(e => {
163
- logger.debug(`[AUN] Immediate ack failed: ${e}`);
239
+ logger.debug(`${this.logPrefix()} Immediate ack failed: ${e}`);
164
240
  });
165
241
  }
166
242
  if (messageId)
@@ -207,7 +283,7 @@ export class AUNChannel {
207
283
  this.config = config;
208
284
  if (config.aunTrace) {
209
285
  this.rotateTraceIfNeeded();
210
- logger.info(`[AUN] Trace logging enabled (daily rotation): ${resolvePaths().logs}/aun-YYYYMMDD.log`);
286
+ logger.info(`${this.logPrefix()} Trace logging enabled (hourly rotation, 12h retention): ${resolvePaths().logs}/aun-YYYYMMDD-HH.log`);
211
287
  }
212
288
  }
213
289
  async connect() {
@@ -240,17 +316,17 @@ export class AUNChannel {
240
316
  try {
241
317
  const discovery = new GatewayDiscovery({});
242
318
  gateway = await discovery.discover(wellKnownUrl);
243
- logger.info(`[AUN] Gateway discovered: ${gateway}`);
319
+ logger.info(`${this.logPrefix()} Gateway discovered: ${gateway}`);
244
320
  }
245
321
  catch (e) {
246
- logger.warn(`[AUN] Well-known discovery failed (${e}), no fallback available`);
322
+ logger.warn(`${this.logPrefix()} Well-known discovery failed (${e}), no fallback available`);
247
323
  }
248
324
  }
249
325
  if (!gateway) {
250
- logger.error('[AUN] Cannot resolve gateway URL from AID');
326
+ logger.error(`${this.logPrefix()} Cannot resolve gateway URL from AID`);
251
327
  throw new Error('Cannot resolve gateway URL from AID');
252
328
  }
253
- logger.info(`[AUN] Initializing: aid=${aidName}, gateway=${gateway}, aun_path=${aunPath}`);
329
+ logger.info(`${this.logPrefix()} Initializing: aid=${aidName}, gateway=${gateway}, aun_path=${aunPath}`);
254
330
  // Create client with FileSecretStore (AES-256-GCM)
255
331
  // 不传 encryption_seed 时,SDK 自动从 {aun_path}/.seed 文件派生密钥(与 aun_cli.py 对齐)
256
332
  const rootCaPath = path.join(aunPath, 'CA', 'root', 'root.crt');
@@ -266,14 +342,14 @@ export class AUNChannel {
266
342
  this.trace('IN', 'message.received', data);
267
343
  const kind = (data && typeof data === 'object') ? data.kind ?? '' : '';
268
344
  const keys = (data && typeof data === 'object') ? Object.keys(data).join(',') : typeof data;
269
- logger.debug(`[AUN][DIAG] message.received: kind=${kind} keys=${keys}`);
345
+ logger.debug(`${this.logPrefix()}[DIAG] message.received: kind=${kind} keys=${keys}`);
270
346
  this.handleIncomingPrivateMessage(data);
271
347
  });
272
348
  this.client.on('group.message_created', (data) => {
273
349
  this.trace('IN', 'group.message_created', data);
274
350
  const gid = (data && typeof data === 'object') ? data.group_id ?? '' : '';
275
351
  const sender = (data && typeof data === 'object') ? data.sender_aid ?? '' : '';
276
- logger.debug(`[AUN][DIAG] group.message_created: group_id=${gid} sender=${sender}`);
352
+ logger.debug(`${this.logPrefix()}[DIAG] group.message_created: group_id=${gid} sender=${sender}`);
277
353
  this.handleIncomingGroupMessage(data);
278
354
  });
279
355
  this.client.on('connection.state', (data) => {
@@ -287,7 +363,7 @@ export class AUNChannel {
287
363
  if (Array.isArray(ids)) {
288
364
  for (const id of ids) {
289
365
  if (typeof id === 'string') {
290
- logger.info(`[AUN] Message recalled: ${id}`);
366
+ logger.info(`${this.logPrefix()} Message recalled: ${id}`);
291
367
  this.recallHandler?.(id);
292
368
  }
293
369
  }
@@ -297,12 +373,12 @@ export class AUNChannel {
297
373
  this.client.on('message.undecryptable', (data) => {
298
374
  this.trace('IN', 'message.undecryptable', data);
299
375
  const d = data;
300
- logger.warn(`[AUN] Message undecryptable: from=${d.from} mid=${d.message_id} err=${d._decrypt_error}`);
376
+ logger.warn(`${this.logPrefix()} Message undecryptable: from=${d.from} mid=${d.message_id} err=${d._decrypt_error}`);
301
377
  });
302
378
  this.client.on('group.message_undecryptable', (data) => {
303
379
  this.trace('IN', 'group.message_undecryptable', data);
304
380
  const d = data;
305
- logger.warn(`[AUN] Group message undecryptable: group=${d.group_id} from=${d.from} mid=${d.message_id} err=${d._decrypt_error}`);
381
+ logger.warn(`${this.logPrefix()} Group message undecryptable: group=${d.group_id} from=${d.from} mid=${d.message_id} err=${d._decrypt_error}`);
306
382
  });
307
383
  // Authenticate
308
384
  // Workaround: SDK 0.3.x _loadIdentityOrRaise doesn't set identity.aid from requested aid,
@@ -319,7 +395,7 @@ export class AUNChannel {
319
395
  }
320
396
  let accessToken;
321
397
  try {
322
- logger.info(`[AUN] Authenticating as ${aidName}...`);
398
+ logger.info(`${this.logPrefix()} Authenticating as ${aidName}...`);
323
399
  this.trace('OUT', 'auth.authenticate', { aid: aidName });
324
400
  const auth = await this.client.auth.authenticate(aidName ? { aid: aidName } : undefined);
325
401
  this.trace('OUT', 'auth.authenticate.ok', { aid: auth.aid, gateway: auth.gateway, hasToken: !!auth.access_token });
@@ -327,23 +403,23 @@ export class AUNChannel {
327
403
  accessToken = auth.access_token;
328
404
  const resolvedGateway = auth.gateway || gateway;
329
405
  this.client._gatewayUrl = resolvedGateway;
330
- logger.info(`[AUN] Authenticated as ${auth.aid ?? '?'}, gateway=${resolvedGateway}`);
406
+ logger.info(`${this.logPrefix()} Authenticated as ${auth.aid ?? '?'}, gateway=${resolvedGateway}`);
331
407
  }
332
408
  catch (e) {
333
409
  const errMsg = e.message || String(e);
334
410
  const errName = e.constructor?.name || 'Error';
335
411
  this.trace('OUT', 'auth.authenticate.error', { error: errMsg, name: errName });
336
- logger.error(`[AUN] Authentication failed (${errName}): ${errMsg}`);
412
+ logger.error(`${this.logPrefix()} Authentication failed (${errName}): ${errMsg}`);
337
413
  if (e.stack)
338
- logger.debug(`[AUN] Auth stack: ${e.stack}`);
414
+ logger.debug(`${this.logPrefix()} Auth stack: ${e.stack}`);
339
415
  // Fallback: try direct token from env/config (legacy)
340
416
  accessToken = this.config.accessToken || process.env.AUN_ACCESS_TOKEN || '';
341
417
  if (!accessToken) {
342
- logger.error(`[AUN] No accessToken fallback available, scheduling retry`);
418
+ logger.error(`${this.logPrefix()} No accessToken fallback available, scheduling retry`);
343
419
  this.scheduleReconnect();
344
420
  throw new Error('Authentication failed and no accessToken fallback available');
345
421
  }
346
- logger.warn(`[AUN] Using accessToken fallback`);
422
+ logger.warn(`${this.logPrefix()} Using accessToken fallback`);
347
423
  }
348
424
  // Connect (SDK auto_reconnect handles transient failures)
349
425
  try {
@@ -364,16 +440,16 @@ export class AUNChannel {
364
440
  const cert = clientAny._keystore?.loadCert?.(aidName);
365
441
  if (cert) {
366
442
  clientAny._identity.cert = cert;
367
- logger.info('[AUN] Backfilled identity.cert from keystore for e2ee fingerprint');
443
+ logger.info(`${this.logPrefix()} Backfilled identity.cert from keystore for e2ee fingerprint`);
368
444
  }
369
445
  }
370
- logger.info(`[AUN] Connected as ${this._aid}`);
446
+ logger.info(`${this.logPrefix()} Connected as ${this._aid}`);
371
447
  // Send welcome message to owner after first connection
372
448
  await this.sendWelcomeMessage();
373
449
  }
374
450
  catch (e) {
375
451
  this.trace('OUT', 'client.connect.error', { error: String(e) });
376
- logger.error(`[AUN] Connection failed: ${e}`);
452
+ logger.error(`${this.logPrefix()} Connection failed: ${e}`);
377
453
  this.scheduleReconnect();
378
454
  throw e;
379
455
  }
@@ -382,7 +458,7 @@ export class AUNChannel {
382
458
  try {
383
459
  const owner = this.config.owner;
384
460
  if (!owner) {
385
- logger.info('[AUN] No owner configured, skipping welcome message');
461
+ logger.info(`${this.logPrefix()} No owner configured, skipping welcome message`);
386
462
  return;
387
463
  }
388
464
  // Check agent.md initialized field
@@ -390,25 +466,25 @@ export class AUNChannel {
390
466
  const aidName = aid.startsWith('@') ? aid.slice(1) : aid;
391
467
  const agentMdPath = path.join(os.homedir(), '.aun', 'AIDs', aidName, 'agent.md');
392
468
  if (!fs.existsSync(agentMdPath)) {
393
- logger.warn('[AUN] agent.md not found, skipping welcome message');
469
+ logger.warn(`${this.logPrefix()} agent.md not found, skipping welcome message`);
394
470
  return;
395
471
  }
396
472
  const agentMdContent = fs.readFileSync(agentMdPath, 'utf-8');
397
473
  const match = agentMdContent.match(/^---\n([\s\S]*?)\n---/);
398
474
  if (!match) {
399
- logger.warn('[AUN] agent.md frontmatter not found');
475
+ logger.warn(`${this.logPrefix()} agent.md frontmatter not found`);
400
476
  return;
401
477
  }
402
478
  const frontmatter = match[1];
403
479
  const initializedMatch = frontmatter.match(/^initialized:\s*(true|false)/m);
404
480
  if (!initializedMatch || initializedMatch[1] === 'true') {
405
- logger.info('[AUN] Agent already initialized, skipping welcome message');
481
+ logger.info(`${this.logPrefix()} Agent already initialized, skipping welcome message`);
406
482
  return;
407
483
  }
408
484
  // Fetch owner's agent.md to derive name and validate type
409
485
  const ownerInfo = await this.fetchPeerInfo(owner);
410
486
  if (ownerInfo.type !== null && ownerInfo.type !== 'human') {
411
- logger.warn(`[AUN] Owner ${owner} type is "${ownerInfo.type}" (not human). Consider using a human AID as owner.`);
487
+ logger.warn(`${this.logPrefix()} Owner ${owner} type is "${ownerInfo.type}" (not human). Consider using a human AID as owner.`);
412
488
  }
413
489
  // Name: prefer existing agent.md name if user has customized it,
414
490
  // otherwise generate "{ownerName}的Evol助手 ({aidLabel})" for disambiguation
@@ -452,14 +528,14 @@ EvolClaw AI Agent 网关,支持多项目会话管理和多 AI 后端切换。
452
528
  `;
453
529
  // Write locally
454
530
  fs.writeFileSync(agentMdPath, newAgentMd, 'utf-8');
455
- logger.info('[AUN] Updated agent.md with initialized=true');
531
+ logger.info(`${this.logPrefix()} Updated agent.md with initialized=true`);
456
532
  // Publish to AUN network via auth.uploadAgentMd
457
533
  try {
458
534
  await this.client.auth.uploadAgentMd(newAgentMd);
459
- logger.info('[AUN] Published agent.md to AUN network');
535
+ logger.info(`${this.logPrefix()} Published agent.md to AUN network`);
460
536
  }
461
537
  catch (e) {
462
- logger.warn(`[AUN] Failed to publish agent.md: ${e}`);
538
+ logger.warn(`${this.logPrefix()} Failed to publish agent.md: ${e}`);
463
539
  }
464
540
  // Send welcome message
465
541
  const welcomeText = `🎉 欢迎使用 EvolClaw!
@@ -487,7 +563,7 @@ EvolClaw AI Agent 网关,支持多项目会话管理和多 AI 后端切换。
487
563
  // pull if the initial E2EE push still arrives before the cert resolves.
488
564
  await new Promise(resolve => setTimeout(resolve, 3000));
489
565
  if (!this.client) {
490
- logger.warn('[AUN] Client disconnected before welcome message could be sent');
566
+ logger.warn(`${this.logPrefix()} Client disconnected before welcome message could be sent`);
491
567
  return;
492
568
  }
493
569
  await this.callAndTrace('message.send', {
@@ -496,10 +572,10 @@ EvolClaw AI Agent 网关,支持多项目会话管理和多 AI 后端切换。
496
572
  encrypt: true,
497
573
  persist_required: true,
498
574
  });
499
- logger.info(`[AUN] Welcome message sent to owner: ${owner}`);
575
+ logger.info(`${this.logPrefix()} Welcome message sent to owner: ${owner}`);
500
576
  }
501
577
  catch (e) {
502
- logger.warn(`[AUN] Failed to send welcome message: ${e}`);
578
+ logger.warn(`${this.logPrefix()} Failed to send welcome message: ${e}`);
503
579
  }
504
580
  }
505
581
  // ── Event handlers ──────────────────────────────────────────
@@ -508,7 +584,7 @@ EvolClaw AI Agent 网关,支持多项目会话管理和多 AI 后端切换。
508
584
  const objectKey = att.object_key;
509
585
  const filename = att.filename || objectKey.split('/').pop() || 'unknown';
510
586
  if (!objectKey) {
511
- logger.warn('[AUN] Attachment missing object_key, skipping');
587
+ logger.warn(`${this.logPrefix()} Attachment missing object_key, skipping`);
512
588
  return null;
513
589
  }
514
590
  let downloadUrl;
@@ -519,32 +595,32 @@ EvolClaw AI Agent 网关,支持多项目会话管理和多 AI 后端切换。
519
595
  });
520
596
  downloadUrl = ticket.download_url || '';
521
597
  if (!downloadUrl) {
522
- logger.warn(`[AUN] No download_url for attachment: ${filename}`);
598
+ logger.warn(`${this.logPrefix()} No download_url for attachment: ${filename}`);
523
599
  return null;
524
600
  }
525
601
  }
526
602
  catch (e) {
527
- logger.warn(`[AUN] create_download_ticket failed for ${filename}: ${e}`);
603
+ logger.warn(`${this.logPrefix()} create_download_ticket failed for ${filename}: ${e}`);
528
604
  return null;
529
605
  }
530
606
  let buffer;
531
607
  try {
532
608
  const res = await fetch(downloadUrl);
533
609
  if (!res.ok) {
534
- logger.warn(`[AUN] Download failed for ${filename}: HTTP ${res.status}`);
610
+ logger.warn(`${this.logPrefix()} Download failed for ${filename}: HTTP ${res.status}`);
535
611
  return null;
536
612
  }
537
613
  buffer = Buffer.from(await res.arrayBuffer());
538
614
  }
539
615
  catch (e) {
540
- logger.warn(`[AUN] Download error for ${filename}: ${e}`);
616
+ logger.warn(`${this.logPrefix()} Download error for ${filename}: ${e}`);
541
617
  return null;
542
618
  }
543
619
  if (att.sha256) {
544
620
  const { createHash } = await import('node:crypto');
545
621
  const actual = createHash('sha256').update(buffer).digest('hex');
546
622
  if (actual !== att.sha256) {
547
- logger.warn(`[AUN] SHA256 mismatch for ${filename}: expected ${att.sha256.slice(0, 8)}… got ${actual.slice(0, 8)}…`);
623
+ logger.warn(`${this.logPrefix()} SHA256 mismatch for ${filename}: expected ${att.sha256.slice(0, 8)}… got ${actual.slice(0, 8)}…`);
548
624
  return null;
549
625
  }
550
626
  }
@@ -553,11 +629,11 @@ EvolClaw AI Agent 网关,支持多项目会话管理和多 AI 后端切换。
553
629
  : process.cwd();
554
630
  try {
555
631
  const result = saveToUploads(buffer, filename, projectPath);
556
- logger.info(`[AUN] Saved attachment: ${result.filePath} (${result.size} bytes)`);
632
+ logger.info(`${this.logPrefix()} Saved attachment: ${result.filePath} (${result.size} bytes)`);
557
633
  return result.filePath;
558
634
  }
559
635
  catch (e) {
560
- logger.warn(`[AUN] saveToUploads failed for ${filename}: ${e}`);
636
+ logger.warn(`${this.logPrefix()} saveToUploads failed for ${filename}: ${e}`);
561
637
  return null;
562
638
  }
563
639
  }
@@ -576,7 +652,7 @@ EvolClaw AI Agent 网关,支持多项目会话管理和多 AI 后端切换。
576
652
  const msgChatId = typeof payload === 'object' && payload !== null && payload.chat_id;
577
653
  if (this._aid && fromAid === this._aid && (!msgChatId || !this._chatId || msgChatId !== this._chatId)) {
578
654
  this.acknowledgeImmediately(messageId, seq);
579
- logger.debug(`[AUN] P2P dropped: echo from self (from=${fromAid} mid=${messageId})`);
655
+ logger.debug(`${this.logPrefix()} P2P dropped: echo from self (from=${fromAid} mid=${messageId})`);
580
656
  return;
581
657
  }
582
658
  // 记录入站消息加密状态,透传到出站 ReplyContext
@@ -618,7 +694,10 @@ EvolClaw AI Agent 网关,支持多项目会话管理和多 AI 后端切换。
618
694
  const peerInfo = await this.fetchPeerInfo(fromAid);
619
695
  const shortAid = this.getShortAid(fromAid);
620
696
  const displayName = peerInfo.name || shortAid;
621
- logger.info(`[AUN] P2P dispatched: from=${shortAid}(${displayName}) mid=${messageId} encrypt=${msgEncrypted} text=${finalText.slice(0, 60)}`);
697
+ // 详细 dispatch 决策日志:记录消息为何被路由到 agent
698
+ const p2pPayloadType = (payload && typeof payload === 'object') ? payload.type ?? '' : '';
699
+ logger.info(`${this.logPrefix()} P2P dispatch decision: mid=${messageId} from=${shortAid}(${displayName}) peerType=${peerInfo.type || 'unknown'} payloadType=${p2pPayloadType} chatId=${chatId} encrypt=${msgEncrypted} textPreview=${JSON.stringify(text.slice(0, 80))}`);
700
+ logger.info(`${this.logPrefix()} P2P dispatched: from=${shortAid}(${displayName}) mid=${messageId} encrypt=${msgEncrypted} text=${finalText.slice(0, 60)}`);
622
701
  const replyContext = { metadata: { encrypted: msgEncrypted } };
623
702
  if (taskId)
624
703
  replyContext.threadId = taskId;
@@ -651,15 +730,15 @@ EvolClaw AI Agent 网关,支持多项目会话管理和多 AI 后端切换。
651
730
  const payloadMentions = Array.isArray(payload?.mentions)
652
731
  ? payload.mentions.filter((m) => typeof m === 'string')
653
732
  : [];
654
- logger.debug(`[AUN][DIAG-GRP] full_msg=${JSON.stringify(msg).substring(0, 500)}`);
733
+ logger.debug(`${this.logPrefix()}[DIAG-GRP] full_msg=${JSON.stringify(msg).substring(0, 500)}`);
655
734
  if (!groupId || !senderAid) {
656
735
  this.acknowledgeImmediately(messageId, seq);
657
- logger.debug(`[AUN] Group dropped: missing groupId or senderAid (mid=${messageId})`);
736
+ logger.debug(`${this.logPrefix()} Group dropped: missing groupId or senderAid (mid=${messageId})`);
658
737
  return;
659
738
  }
660
739
  if (this._aid && senderAid === this._aid) {
661
740
  this.acknowledgeImmediately(messageId, seq);
662
- logger.debug(`[AUN] Group dropped: own message (group=${groupId} mid=${messageId})`);
741
+ logger.debug(`${this.logPrefix()} Group dropped: own message (group=${groupId} mid=${messageId})`);
663
742
  return;
664
743
  }
665
744
  // ── proactive 模式入站白名单 ──
@@ -670,17 +749,18 @@ EvolClaw AI Agent 网关,支持多项目会话管理和多 AI 后端切换。
670
749
  const payloadType = payloadObj?.type ?? '';
671
750
  if (!AUNChannel.PROACTIVE_ALLOW_TYPES.has(payloadType)) {
672
751
  this.acknowledgeImmediately(messageId, seq);
673
- logger.debug(`[AUN] Group dropped (proactive deny): type=${payloadType} group=${groupId} sender=${senderAid} mid=${messageId}`);
752
+ logger.info(`${this.logPrefix()} Group dropped (proactive deny): type=${payloadType} group=${groupId} sender=${senderAid} mid=${messageId}`);
674
753
  return;
675
754
  }
676
755
  const rawText = typeof payloadObj?.text === 'string' ? payloadObj.text : '';
677
756
  const rawMentions = Array.isArray(payloadObj?.mentions) ? payloadObj.mentions : [];
678
757
  const mentionAids = this.extractMentionAids(rawMentions);
679
758
  const mentionsSelf = !!this._aid && (this.hasExplicitMention(rawText, this._aid) || mentionAids.includes(this._aid));
680
- const mentionsAll = this.hasExplicitMention(rawText, 'all') || this.hasMentionAll(rawMentions);
759
+ // @all 仅认结构化 mentions(payload.mentions),不扫描正文 — 避免引述性 "@all" 误判
760
+ const mentionsAll = this.hasMentionAll(rawMentions);
681
761
  if (!mentionsSelf && !mentionsAll) {
682
762
  this.acknowledgeImmediately(messageId, seq);
683
- logger.debug(`[AUN] Group dropped (proactive whitelist): type=${payloadType} group=${groupId} sender=${senderAid} mid=${messageId}`);
763
+ logger.info(`${this.logPrefix()} Group dropped (proactive whitelist): type=${payloadType} group=${groupId} sender=${senderAid} mid=${messageId} textPreview=${JSON.stringify(rawText.slice(0, 80))}`);
684
764
  return;
685
765
  }
686
766
  }
@@ -694,11 +774,12 @@ EvolClaw AI Agent 网关,支持多项目会话管理和多 AI 后端切换。
694
774
  const mentionedSelf = this._aid
695
775
  ? (this.hasExplicitMention(text, this._aid) || payloadMentions.includes(this._aid))
696
776
  : false;
697
- const mentionedAll = this.hasExplicitMention(text, 'all') || payloadMentions.includes('all');
777
+ // @all 仅认结构化 mentions(payload.mentions),不扫描正文 避免引述性 "@all" 误判
778
+ const mentionedAll = payloadMentions.includes('all');
698
779
  // In mention mode, only respond when explicitly mentioned; in broadcast mode, respond to all
699
780
  if (dispatchMode === 'mention' && !mentionedSelf && !mentionedAll) {
700
781
  this.acknowledgeImmediately(messageId, seq);
701
- logger.debug(`[AUN] Group missed: unmentioned in mention-mode (group=${groupId} sender=${senderAid} mid=${messageId})`);
782
+ logger.info(`${this.logPrefix()} Group dropped: unmentioned in mention-mode (group=${groupId} sender=${senderAid} mid=${messageId} textPreview=${JSON.stringify(text.slice(0, 80))})`);
702
783
  return;
703
784
  }
704
785
  const strippedText = this.stripSelfMentionIfOnly(text, this._aid);
@@ -710,7 +791,7 @@ EvolClaw AI Agent 网关,支持多项目会话管理和多 AI 后端切换。
710
791
  // Allow through if there's text OR attachments; both-empty messages are silently dropped
711
792
  if (!strippedText && !hasAttachments) {
712
793
  this.acknowledgeImmediately(messageId, seq);
713
- logger.debug(`[AUN] Group dropped: empty text and no attachments (group=${groupId} sender=${senderAid} mid=${messageId})`);
794
+ logger.debug(`${this.logPrefix()} Group dropped: empty text and no attachments (group=${groupId} sender=${senderAid} mid=${messageId})`);
714
795
  return;
715
796
  }
716
797
  const mentions = mentionedAll
@@ -739,7 +820,19 @@ EvolClaw AI Agent 网关,支持多项目会话管理和多 AI 后端切换。
739
820
  const peerInfo = await this.fetchPeerInfo(senderAid);
740
821
  const shortAid = this.getShortAid(senderAid);
741
822
  const displayName = peerInfo.name || shortAid;
742
- logger.info(`[AUN] Group dispatched: group=${groupId} sender=${shortAid}(${displayName}) mode=${dispatchMode} mid=${messageId} text=${finalText.slice(0, 60)}`);
823
+ // 详细 dispatch 决策日志:记录消息为何被路由到 agent
824
+ const payloadType = (payload && typeof payload === 'object') ? payload.type ?? '' : '';
825
+ const textMentionSelf = this._aid ? this.hasExplicitMention(text, this._aid) : false;
826
+ const textMentionAll = this.hasExplicitMention(text, 'all');
827
+ const structMentionSelf = this._aid ? payloadMentions.includes(this._aid) : false;
828
+ const structMentionAll = payloadMentions.includes('all');
829
+ const reason = mentionedAll
830
+ ? 'mention.all(struct)'
831
+ : mentionedSelf
832
+ ? (structMentionSelf ? 'mention.self(struct)' : 'mention.self(text)')
833
+ : `${dispatchMode}.no-mention`;
834
+ logger.info(`${this.logPrefix()} Group dispatch decision: mid=${messageId} group=${groupId} sender=${shortAid}(${displayName}) peerType=${peerInfo.type || 'unknown'} payloadType=${payloadType} dispatchMode=${dispatchMode} reason=${reason} structMentions=${JSON.stringify(payloadMentions)} textMentionSelf=${textMentionSelf} textMentionAll=${textMentionAll} structMentionSelf=${structMentionSelf} structMentionAll=${structMentionAll} encrypt=${msgEncrypted} textPreview=${JSON.stringify(text.slice(0, 80))}`);
835
+ logger.info(`${this.logPrefix()} Group dispatched: group=${groupId} sender=${shortAid}(${displayName}) mode=${dispatchMode} mid=${messageId} text=${finalText.slice(0, 60)}`);
743
836
  this.dispatchMessage({
744
837
  channelId: groupId,
745
838
  userId: senderAid,
@@ -787,7 +880,7 @@ EvolClaw AI Agent 网关,支持多项目会话管理和多 AI 后端切换。
787
880
  mentions: mentionObjects,
788
881
  replyContext,
789
882
  }).catch(err => {
790
- logger.error('[AUN] Message handler error:', err);
883
+ logger.error(`${this.logPrefix()} Message handler error:`, err);
791
884
  });
792
885
  }
793
886
  handleConnectionState(data) {
@@ -800,14 +893,15 @@ EvolClaw AI Agent 网关,支持多项目会话管理和多 AI 后端切换。
800
893
  this.lastReconnectLogTime = 0;
801
894
  this.lastReconnectLogAttempt = 0;
802
895
  this.trace('IN', 'connection.state', data);
803
- logger.info('[AUN] Connected');
896
+ logger.info(`${this.logPrefix()} Connected`);
804
897
  }
805
898
  else if (state === 'disconnected') {
806
899
  this.connected = false;
807
900
  this.trace('IN', 'connection.state', data);
808
- logger.warn(`[AUN] Disconnected: ${data.error ?? 'unknown'}`);
901
+ logger.warn(`${this.logPrefix()} Disconnected: ${data.error ?? 'unknown'}`);
809
902
  }
810
903
  else if (state === 'reconnecting') {
904
+ this.connected = false;
811
905
  const attempt = data.attempt ?? 0;
812
906
  const now = Date.now();
813
907
  // Throttled logging: first attempt, every N attempts, or every M seconds
@@ -817,14 +911,14 @@ EvolClaw AI Agent 网关,支持多项目会话管理和多 AI 后端切换。
817
911
  if (isFirst || isStep || isInterval) {
818
912
  const suppressed = attempt - this.lastReconnectLogAttempt - 1;
819
913
  const suffix = suppressed > 0 ? `, ${suppressed} suppressed since last log` : '';
820
- logger.info(`[AUN] SDK reconnecting (attempt ${attempt}${suffix})`);
914
+ logger.info(`${this.logPrefix()} SDK reconnecting (attempt ${attempt}${suffix})`);
821
915
  this.lastReconnectLogTime = now;
822
916
  this.lastReconnectLogAttempt = attempt;
823
917
  this.trace('IN', 'connection.state', data);
824
918
  }
825
919
  // Detect runaway SDK reconnect loop: force disconnect and use TS-layer backoff
826
920
  if (attempt >= AUNChannel.SDK_RECONNECT_GIVEUP && !this.intentionalDisconnect) {
827
- logger.warn(`[AUN] SDK reconnect stuck at attempt ${attempt}, forcing TS-layer reconnect with backoff`);
921
+ logger.warn(`${this.logPrefix()} SDK reconnect stuck at attempt ${attempt}, forcing TS-layer reconnect with backoff`);
828
922
  this.connected = false;
829
923
  if (this.client) {
830
924
  this.trace('OUT', 'client.close', { reason: 'sdk_reconnect_stuck' });
@@ -838,7 +932,7 @@ EvolClaw AI Agent 网关,支持多项目会话管理和多 AI 后端切换。
838
932
  this.connected = false;
839
933
  this.trace('IN', 'connection.state', data);
840
934
  const reason = data.reason ?? '';
841
- logger.error(`[AUN] Terminal failure: ${data.error ?? 'unknown'}${reason ? ` (${reason})` : ''}`);
935
+ logger.error(`${this.logPrefix()} Terminal failure: ${data.error ?? 'unknown'}${reason ? ` (${reason})` : ''}`);
842
936
  if (!this.intentionalDisconnect) {
843
937
  this.scheduleReconnect();
844
938
  }
@@ -859,11 +953,11 @@ EvolClaw AI Agent 网关,支持多项目会话管理和多 AI 后端切换。
859
953
  }
860
954
  async sendMessage(channelId, text, context) {
861
955
  if (!this.connected || !this.client) {
862
- logger.warn('[AUN] Cannot send: not connected');
956
+ logger.warn(`${this.logPrefix()} Cannot send: not connected`);
863
957
  return;
864
958
  }
865
959
  if (!text?.trim()) {
866
- logger.warn('[AUN] Attempted to send empty message, skipping');
960
+ logger.warn(`${this.logPrefix()} Attempted to send empty message, skipping`);
867
961
  return;
868
962
  }
869
963
  let finalText = text;
@@ -879,6 +973,12 @@ EvolClaw AI Agent 网关,支持多项目会话管理和多 AI 后端切换。
879
973
  }
880
974
  }
881
975
  const payload = { type: 'text', text: finalText };
976
+ // 出站群消息:从正文提取 @aid 填入结构化 mentions(与 aun CLI 对齐,方便对端按 mentions 过滤)
977
+ if (this.isGroupId(channelId)) {
978
+ const extracted = this.extractMentionAidsFromText(finalText);
979
+ if (extracted.length > 0)
980
+ payload.mentions = extracted;
981
+ }
882
982
  if (context?.threadId)
883
983
  payload.thread_id = context.threadId;
884
984
  if (context?.metadata?.taskId)
@@ -904,31 +1004,31 @@ EvolClaw AI Agent 网关,支持多项目会话管理和多 AI 后端切换。
904
1004
  if (!result || !result.message_id) {
905
1005
  const dispatchStatus = result?.message_dispatch?.status;
906
1006
  if (dispatchStatus === 'debounced' || dispatchStatus === 'dispatched') {
907
- logger.info(`[AUN] group.send ok (${dispatchStatus}): group=${channelId} encrypt=${encrypt} text=${finalText.slice(0, 60)}`);
1007
+ logger.info(`${this.logPrefix()} group.send ok (${dispatchStatus}): group=${channelId} encrypt=${encrypt} text=${finalText.slice(0, 60)}`);
908
1008
  }
909
1009
  else {
910
- logger.warn(`[AUN] group.send returned no message_id: ${JSON.stringify(result)}`);
1010
+ logger.warn(`${this.logPrefix()} group.send returned no message_id: ${JSON.stringify(result)}`);
911
1011
  }
912
1012
  }
913
1013
  else {
914
- logger.info(`[AUN] group.send ok: group=${channelId} mid=${result.message_id} encrypt=${encrypt} text=${finalText.slice(0, 60)}`);
1014
+ logger.info(`${this.logPrefix()} group.send ok: group=${channelId} mid=${result.message_id} encrypt=${encrypt} text=${finalText.slice(0, 60)}`);
915
1015
  }
916
1016
  }
917
1017
  else {
918
1018
  params.to = targetAid;
919
1019
  const result = await this.callAndTrace('message.send', params);
920
1020
  if (!result || !result.message_id) {
921
- logger.warn(`[AUN] message.send returned no message_id: ${JSON.stringify(result)}`);
1021
+ logger.warn(`${this.logPrefix()} message.send returned no message_id: ${JSON.stringify(result)}`);
922
1022
  }
923
1023
  else {
924
- logger.info(`[AUN] message.send ok: to=${targetAid} mid=${result.message_id} encrypt=${encrypt} text=${finalText.slice(0, 60)}`);
1024
+ logger.info(`${this.logPrefix()} message.send ok: to=${this.peerLabel(targetAid)} mid=${result.message_id} encrypt=${encrypt} text=${finalText.slice(0, 60)}`);
925
1025
  }
926
1026
  }
927
1027
  }
928
1028
  catch (e) {
929
1029
  if (encrypt && e instanceof E2EEError) {
930
1030
  this.peerE2ee.set(encryptTarget, { ok: false, ts: Date.now() });
931
- logger.warn(`[AUN] E2EE send failed to ${channelId}, retrying plaintext: ${e}`);
1031
+ logger.warn(`${this.logPrefix()} E2EE send failed to ${channelId}, retrying plaintext: ${e}`);
932
1032
  params.encrypt = false;
933
1033
  try {
934
1034
  if (isGroup) {
@@ -936,7 +1036,7 @@ EvolClaw AI Agent 网关,支持多项目会话管理和多 AI 后端切换。
936
1036
  const result = await this.client.call('group.send', params);
937
1037
  this.trace('OUT', 'group.send.fallback.ok', { message_id: result?.message_id });
938
1038
  if (!result || !result.message_id) {
939
- logger.warn(`[AUN] group.send fallback returned no message_id: ${JSON.stringify(result)}`);
1039
+ logger.warn(`${this.logPrefix()} group.send fallback returned no message_id: ${JSON.stringify(result)}`);
940
1040
  }
941
1041
  }
942
1042
  else {
@@ -944,18 +1044,18 @@ EvolClaw AI Agent 网关,支持多项目会话管理和多 AI 后端切换。
944
1044
  const result = await this.client.call('message.send', params);
945
1045
  this.trace('OUT', 'message.send.fallback.ok', { message_id: result?.message_id });
946
1046
  if (!result || !result.message_id) {
947
- logger.warn(`[AUN] message.send fallback returned no message_id: ${JSON.stringify(result)}`);
1047
+ logger.warn(`${this.logPrefix()} message.send fallback returned no message_id: ${JSON.stringify(result)}`);
948
1048
  }
949
1049
  }
950
1050
  }
951
1051
  catch (e2) {
952
1052
  this.trace('OUT', 'send.fallback.error', { channelId, error: String(e2) });
953
- logger.error(`[AUN] Plaintext fallback also failed to ${channelId}: ${e2}`);
1053
+ logger.error(`${this.logPrefix()} Plaintext fallback also failed to ${channelId}: ${e2}`);
954
1054
  }
955
1055
  }
956
1056
  else {
957
1057
  this.trace('OUT', 'send.error', { channelId, error: String(e) });
958
- logger.error(`[AUN] Send failed to ${channelId}: ${e}`);
1058
+ logger.error(`${this.logPrefix()} Send failed to ${channelId}: ${e}`);
959
1059
  }
960
1060
  }
961
1061
  }
@@ -988,36 +1088,36 @@ EvolClaw AI Agent 网关,支持多项目会话管理和多 AI 后端切换。
988
1088
  if (this.isGroupId(channelId)) {
989
1089
  params.group_id = targetId;
990
1090
  await this.callAndTrace('group.thought.put', params);
991
- logger.info(`[AUN] thought.put ok group=${targetId} task=${taskId} stage=${stage} encrypt=${encrypt}`);
1091
+ logger.info(`${this.logPrefix()} thought.put ok group=${targetId} task=${taskId} stage=${stage} encrypt=${encrypt}`);
992
1092
  }
993
1093
  else {
994
1094
  params.to = targetId;
995
1095
  await this.callAndTrace('message.thought.put', params);
996
- logger.info(`[AUN] thought.put ok p2p=${targetId} task=${taskId} stage=${stage} encrypt=${encrypt}`);
1096
+ logger.info(`${this.logPrefix()} thought.put ok p2p=${this.peerLabel(targetId)} task=${taskId} stage=${stage} encrypt=${encrypt}`);
997
1097
  }
998
1098
  }
999
1099
  catch (e) {
1000
1100
  const err = e;
1001
- logger.debug(`[AUN] thought.put failed to ${channelId}: ${err?.name}(${err?.code})=${err?.message}`);
1101
+ logger.debug(`${this.logPrefix()} thought.put failed to ${channelId}: ${err?.name}(${err?.code})=${err?.message}`);
1002
1102
  }
1003
1103
  }
1004
1104
  async sendFile(channelId, filePath, context) {
1005
1105
  if (!this.connected || !this.client) {
1006
- logger.warn('[AUN] Cannot sendFile: not connected');
1106
+ logger.warn(`${this.logPrefix()} Cannot sendFile: not connected`);
1007
1107
  return;
1008
1108
  }
1009
1109
  const absPath = path.resolve(filePath);
1010
1110
  if (!fs.existsSync(absPath)) {
1011
- logger.warn(`[AUN] sendFile: file not found: ${absPath}`);
1111
+ logger.warn(`${this.logPrefix()} sendFile: file not found: ${absPath}`);
1012
1112
  return;
1013
1113
  }
1014
1114
  const stat = fs.statSync(absPath);
1015
1115
  if (stat.size === 0) {
1016
- logger.warn('[AUN] sendFile: file is empty');
1116
+ logger.warn(`${this.logPrefix()} sendFile: file is empty`);
1017
1117
  return;
1018
1118
  }
1019
1119
  if (stat.size > 10 * 1024 * 1024) {
1020
- logger.warn(`[AUN] sendFile: file too large (${formatSize(stat.size)}, max 10 MB)`);
1120
+ logger.warn(`${this.logPrefix()} sendFile: file too large (${formatSize(stat.size)}, max 10 MB)`);
1021
1121
  return;
1022
1122
  }
1023
1123
  const filename = path.basename(absPath);
@@ -1099,7 +1199,7 @@ EvolClaw AI Agent 网关,支持多项目会话管理和多 AI 后端切换。
1099
1199
  const result = await this.client.call('group.send', params);
1100
1200
  this.trace('OUT', 'group.send.file.ok', { message_id: result?.message_id });
1101
1201
  if (!result || !result.message_id) {
1102
- logger.warn(`[AUN] group.send.file returned no message_id: ${JSON.stringify(result)}`);
1202
+ logger.warn(`${this.logPrefix()} group.send.file returned no message_id: ${JSON.stringify(result)}`);
1103
1203
  }
1104
1204
  }
1105
1205
  else {
@@ -1108,7 +1208,7 @@ EvolClaw AI Agent 网关,支持多项目会话管理和多 AI 后端切换。
1108
1208
  const result = await this.client.call('message.send', params);
1109
1209
  this.trace('OUT', 'message.send.file.ok', { message_id: result?.message_id });
1110
1210
  if (!result || !result.message_id) {
1111
- logger.warn(`[AUN] message.send.file returned no message_id: ${JSON.stringify(result)}`);
1211
+ logger.warn(`${this.logPrefix()} message.send.file returned no message_id: ${JSON.stringify(result)}`);
1112
1212
  }
1113
1213
  }
1114
1214
  }
@@ -1119,14 +1219,14 @@ EvolClaw AI Agent 网关,支持多项目会话管理和多 AI 后端切换。
1119
1219
  });
1120
1220
  if (encrypt && sendErr instanceof E2EEError) {
1121
1221
  this.peerE2ee.set(encryptTarget, { ok: false, ts: Date.now() });
1122
- logger.warn(`[AUN] E2EE sendFile failed to ${channelId}, retrying plaintext: ${sendErr}`);
1222
+ logger.warn(`${this.logPrefix()} E2EE sendFile failed to ${channelId}, retrying plaintext: ${sendErr}`);
1123
1223
  params.encrypt = false;
1124
1224
  if (isGroup) {
1125
1225
  this.trace('OUT', 'group.send.file.fallback', params);
1126
1226
  const result = await this.client.call('group.send', params);
1127
1227
  this.trace('OUT', 'group.send.file.fallback.ok', { message_id: result?.message_id });
1128
1228
  if (!result || !result.message_id) {
1129
- logger.warn(`[AUN] group.send.file fallback returned no message_id: ${JSON.stringify(result)}`);
1229
+ logger.warn(`${this.logPrefix()} group.send.file fallback returned no message_id: ${JSON.stringify(result)}`);
1130
1230
  }
1131
1231
  }
1132
1232
  else {
@@ -1134,7 +1234,7 @@ EvolClaw AI Agent 网关,支持多项目会话管理和多 AI 后端切换。
1134
1234
  const result = await this.client.call('message.send', params);
1135
1235
  this.trace('OUT', 'message.send.file.fallback.ok', { message_id: result?.message_id });
1136
1236
  if (!result || !result.message_id) {
1137
- logger.warn(`[AUN] message.send.file fallback returned no message_id: ${JSON.stringify(result)}`);
1237
+ logger.warn(`${this.logPrefix()} message.send.file fallback returned no message_id: ${JSON.stringify(result)}`);
1138
1238
  }
1139
1239
  }
1140
1240
  }
@@ -1142,11 +1242,11 @@ EvolClaw AI Agent 网关,支持多项目会话管理和多 AI 后端切换。
1142
1242
  throw sendErr;
1143
1243
  }
1144
1244
  }
1145
- logger.info(`[AUN] File sent: ${filename} (${formatSize(stat.size)}) → ${channelId}`);
1245
+ logger.info(`${this.logPrefix()} File sent: ${filename} (${formatSize(stat.size)}) → ${channelId}`);
1146
1246
  }
1147
1247
  catch (e) {
1148
1248
  this.trace('OUT', 'sendFile.error', { channelId, filePath, error: String(e) });
1149
- logger.error(`[AUN] sendFile failed for ${channelId}: ${e}`);
1249
+ logger.error(`${this.logPrefix()} sendFile failed for ${channelId}: ${e}`);
1150
1250
  }
1151
1251
  }
1152
1252
  acknowledge(messageId) {
@@ -1190,14 +1290,14 @@ EvolClaw AI Agent 网关,支持多项目会话管理和多 AI 后端切换。
1190
1290
  this.client.call(method, params).catch(e => {
1191
1291
  if (encrypt && e instanceof E2EEError) {
1192
1292
  this.peerE2ee.set(encryptTarget, { ok: false, ts: Date.now() });
1193
- logger.warn(`[AUN] E2EE status send failed to ${channelId}, retrying plaintext`);
1293
+ logger.warn(`${this.logPrefix()} E2EE status send failed to ${channelId}, retrying plaintext`);
1194
1294
  params.encrypt = false;
1195
1295
  this.client.call(method, params).catch(e2 => {
1196
- logger.debug(`[AUN] Processing status fallback failed: ${e2}`);
1296
+ logger.debug(`${this.logPrefix()} Processing status fallback failed: ${e2}`);
1197
1297
  });
1198
1298
  }
1199
1299
  else {
1200
- logger.debug(`[AUN] Processing status failed: ${e}`);
1300
+ logger.debug(`${this.logPrefix()} Processing status failed: ${e}`);
1201
1301
  }
1202
1302
  });
1203
1303
  };
@@ -1211,7 +1311,10 @@ EvolClaw AI Agent 网关,支持多项目会话管理和多 AI 后端切换。
1211
1311
  this.trace('OUT', 'message.send.status', params);
1212
1312
  sendWithFallback('message.send');
1213
1313
  }
1214
- logger.info(`[AUN] task.${status} task=${taskId} session=${sessionId} encrypt=${encrypt} target=${channelId}`);
1314
+ // 群聊显示 group id 简称,P2P 显示 peer label;从 context.metadata 读取 chatmode
1315
+ const targetLabel = this.isGroupId(channelId) ? channelId : this.peerLabel(channelId);
1316
+ const chatmode = context?.metadata?.chatmode ?? '?';
1317
+ logger.info(`${this.logPrefix()} task.${status} task=${taskId} session=${sessionId} chatmode=${chatmode} encrypt=${encrypt} target=${targetLabel}`);
1215
1318
  }
1216
1319
  sendCustomPayload(channelId, payload) {
1217
1320
  if (!this.client || !this.connected)
@@ -1241,7 +1344,7 @@ EvolClaw AI Agent 网关,支持多项目会话管理和多 AI 后端切换。
1241
1344
  this.trace('OUT', 'message.send.custom.ok', { message_id: result?.message_id });
1242
1345
  }).catch(e => {
1243
1346
  this.trace('OUT', 'message.send.custom.error', { error: String(e) });
1244
- logger.warn(`[AUN] Custom payload failed: ${e}`);
1347
+ logger.warn(`${this.logPrefix()} Custom payload failed: ${e}`);
1245
1348
  });
1246
1349
  }
1247
1350
  async disconnect() {
@@ -1262,12 +1365,12 @@ EvolClaw AI Agent 网关,支持多项目会话管理和多 AI 后端切换。
1262
1365
  this.client = null;
1263
1366
  }
1264
1367
  this.connected = false;
1265
- logger.info('[AUN] Disconnected');
1368
+ logger.info(`${this.logPrefix()} Disconnected`);
1266
1369
  if (this.traceStream) {
1267
1370
  this.traceStream.end();
1268
1371
  this.traceStream = null;
1269
1372
  }
1270
- logger.info('[AUN] Disconnected');
1373
+ logger.info(`${this.logPrefix()} Disconnected`);
1271
1374
  }
1272
1375
  // ── TS-layer reconnect (fallback when SDK auto_reconnect exhausted) ──
1273
1376
  scheduleReconnect() {
@@ -1277,25 +1380,25 @@ EvolClaw AI Agent 网关,支持多项目会话管理和多 AI 后端切换。
1277
1380
  return;
1278
1381
  const delays = AUNChannel.RECONNECT_DELAYS;
1279
1382
  if (this.reconnectAttempt >= delays.length) {
1280
- logger.error(`[AUN] All ${delays.length} reconnect attempts exhausted, giving up`);
1383
+ logger.error(`${this.logPrefix()} All ${delays.length} reconnect attempts exhausted, giving up`);
1281
1384
  this.onChannelDown?.();
1282
1385
  return;
1283
1386
  }
1284
1387
  const delay = delays[this.reconnectAttempt];
1285
1388
  this.reconnectAttempt++;
1286
- logger.info(`[AUN] Scheduling reconnect #${this.reconnectAttempt}/${delays.length} in ${delay}s`);
1389
+ logger.info(`${this.logPrefix()} Scheduling reconnect #${this.reconnectAttempt}/${delays.length} in ${delay}s`);
1287
1390
  this.reconnectTimer = setTimeout(async () => {
1288
1391
  this.reconnectTimer = null;
1289
1392
  try {
1290
- logger.info(`[AUN] Reconnect #${this.reconnectAttempt} starting...`);
1393
+ logger.info(`${this.logPrefix()} Reconnect #${this.reconnectAttempt} starting...`);
1291
1394
  this.trace('OUT', 'reconnect.start', { attempt: this.reconnectAttempt });
1292
1395
  await this.initClient();
1293
1396
  this.trace('OUT', 'reconnect.ok', { attempt: this.reconnectAttempt });
1294
- logger.info(`[AUN] Reconnect #${this.reconnectAttempt} succeeded`);
1397
+ logger.info(`${this.logPrefix()} Reconnect #${this.reconnectAttempt} succeeded`);
1295
1398
  }
1296
1399
  catch (err) {
1297
1400
  this.trace('OUT', 'reconnect.error', { attempt: this.reconnectAttempt, error: String(err) });
1298
- logger.error(`[AUN] Reconnect #${this.reconnectAttempt} failed:`, err);
1401
+ logger.error(`${this.logPrefix()} Reconnect #${this.reconnectAttempt} failed:`, err);
1299
1402
  this.scheduleReconnect();
1300
1403
  }
1301
1404
  }, delay * 1000);
@@ -1371,7 +1474,7 @@ EvolClaw AI Agent 网关,支持多项目会话管理和多 AI 后端切换。
1371
1474
  return info;
1372
1475
  }
1373
1476
  catch (e) {
1374
- logger.debug(`[AUN] fetchPeerInfo failed for ${aid}: ${e}`);
1477
+ logger.debug(`${this.logPrefix()} fetchPeerInfo failed for ${aid}: ${e}`);
1375
1478
  return { type: null }; // no agent.md → unknown
1376
1479
  }
1377
1480
  }