evolclaw 3.1.3 → 3.1.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (100) hide show
  1. package/CHANGELOG.md +27 -0
  2. package/assets/.env.template +4 -0
  3. package/assets/config.json.template +6 -0
  4. package/assets/wechat-group-qr.jpeg +0 -0
  5. package/dist/agents/claude-runner.js +348 -156
  6. package/dist/agents/kit-renderer.js +211 -42
  7. package/dist/aun/aid/agentmd.js +75 -139
  8. package/dist/aun/aid/client.js +1 -14
  9. package/dist/aun/aid/identity.js +381 -54
  10. package/dist/aun/aid/index.js +3 -2
  11. package/dist/aun/aid/store.js +74 -0
  12. package/dist/aun/msg/p2p.js +26 -2
  13. package/dist/aun/rpc/connection.js +23 -35
  14. package/dist/channels/aun.js +92 -144
  15. package/dist/channels/dingtalk.js +1 -0
  16. package/dist/channels/feishu.js +270 -190
  17. package/dist/channels/qqbot.js +1 -0
  18. package/dist/channels/wechat.js +1 -0
  19. package/dist/channels/wecom.js +1 -0
  20. package/dist/cli/agent.js +26 -27
  21. package/dist/cli/bench.js +45 -34
  22. package/dist/cli/help.js +23 -0
  23. package/dist/cli/index.js +538 -77
  24. package/dist/cli/init-channel.js +7 -4
  25. package/dist/cli/link-rules.js +2 -1
  26. package/dist/cli/model.js +324 -0
  27. package/dist/cli/net-check.js +138 -56
  28. package/dist/cli/watch-msg.js +7 -7
  29. package/dist/cli/watch-web/debug-log.js +18 -0
  30. package/dist/cli/watch-web/server.js +306 -0
  31. package/dist/cli/watch-web/sources/aid.js +63 -0
  32. package/dist/cli/watch-web/sources/msg.js +70 -0
  33. package/dist/cli/watch-web/sources/session.js +638 -0
  34. package/dist/cli/watch-web/sources/types.js +10 -0
  35. package/dist/cli/watch-web/static/app.js +546 -0
  36. package/dist/cli/watch-web/static/index.html +54 -0
  37. package/dist/cli/watch-web/static/style.css +247 -0
  38. package/dist/core/channel-loader.js +7 -4
  39. package/dist/core/command-handler.js +87 -93
  40. package/dist/core/evolagent-registry.js +1 -1
  41. package/dist/core/evolagent.js +4 -4
  42. package/dist/core/interaction-router.js +59 -0
  43. package/dist/core/message/message-bridge.js +6 -6
  44. package/dist/core/message/message-log.js +2 -2
  45. package/dist/core/message/message-processor.js +104 -118
  46. package/dist/core/message/stream-idle-monitor.js +21 -0
  47. package/dist/core/model/model-catalog.js +215 -0
  48. package/dist/core/model/model-scope.js +250 -0
  49. package/dist/core/relation/peer-identity.js +78 -44
  50. package/dist/core/relation/peer-key.js +16 -0
  51. package/dist/core/session/session-fs-store.js +34 -55
  52. package/dist/core/session/session-key.js +24 -0
  53. package/dist/core/session/session-manager.js +312 -251
  54. package/dist/core/session/session-mapper.js +9 -4
  55. package/dist/core/trigger/manager.js +37 -0
  56. package/dist/core/trigger/scheduler.js +2 -1
  57. package/dist/index.js +10 -3
  58. package/dist/ipc.js +22 -0
  59. package/dist/paths.js +87 -16
  60. package/dist/utils/npm-ops.js +18 -11
  61. package/kits/docs/GUIDE.md +2 -2
  62. package/kits/docs/INDEX.md +11 -7
  63. package/kits/docs/channels/aun.md +56 -17
  64. package/kits/docs/channels/feishu.md +41 -12
  65. package/kits/docs/context-assembly.md +181 -0
  66. package/kits/docs/evolclaw/agent.md +49 -0
  67. package/kits/docs/evolclaw/aid.md +49 -0
  68. package/kits/docs/evolclaw/ctl.md +46 -0
  69. package/kits/docs/evolclaw/group.md +82 -0
  70. package/kits/docs/evolclaw/msg.md +86 -0
  71. package/kits/docs/evolclaw/rpc.md +35 -0
  72. package/kits/docs/evolclaw/storage.md +49 -0
  73. package/kits/docs/venues/aun-group.md +10 -0
  74. package/kits/docs/venues/aun-private.md +10 -0
  75. package/kits/docs/venues/client-desktop.md +10 -0
  76. package/kits/docs/venues/client-mobile.md +10 -0
  77. package/kits/docs/venues/feishu-group.md +13 -0
  78. package/kits/docs/venues/feishu-private.md +9 -0
  79. package/kits/docs/venues/group.md +11 -0
  80. package/kits/docs/venues/private.md +10 -0
  81. package/kits/eck_manifest.json +75 -39
  82. package/kits/rules/01-overview.md +20 -10
  83. package/kits/rules/05-venue.md +2 -2
  84. package/kits/rules/06-channel.md +30 -27
  85. package/kits/templates/system-fragments/baseagent.md +7 -1
  86. package/kits/templates/system-fragments/channel.md +4 -1
  87. package/kits/templates/system-fragments/identity.md +4 -4
  88. package/kits/templates/system-fragments/relation.md +8 -5
  89. package/kits/templates/system-fragments/session.md +27 -0
  90. package/kits/templates/system-fragments/venue.md +13 -1
  91. package/package.json +13 -6
  92. package/dist/aun/aid/lifecycle-log.js +0 -33
  93. package/dist/net-check.js +0 -640
  94. package/dist/utils/aid-lifecycle-log.js +0 -33
  95. package/dist/watch-msg.js +0 -544
  96. package/kits/docs/evolclaw/AGENT_CMD.md +0 -31
  97. package/kits/docs/evolclaw/MSG_GROUP.md +0 -30
  98. package/kits/docs/evolclaw/MSG_PRIVATE.md +0 -72
  99. package/kits/docs/evolclaw/tools.md +0 -25
  100. package/kits/templates/system-fragments/eckruntime.md +0 -14
@@ -1,37 +1,25 @@
1
- import fs from 'fs';
2
- import path from 'path';
3
- import os from 'os';
1
+ import { getAidStore, loadClient, SLOT } from '../aid/store.js';
4
2
  export async function createShortConnection(aid, opts) {
5
- const aunPath = opts?.aunPath ?? path.join(os.homedir(), '.aun');
6
- const slotId = opts?.slotId ?? '';
7
- const caCertPath = path.join(aunPath, 'CA', 'root', 'root.crt');
8
- const { AUNClient } = await import('@agentunion/fastaun');
9
- const encryptionSeed = process.env.AUN_ENCRYPTION_SEED || undefined;
10
- const clientOpts = { aun_path: aunPath, debug: false };
11
- if (fs.existsSync(caCertPath))
12
- clientOpts.root_ca_path = caCertPath;
13
- if (encryptionSeed)
14
- clientOpts.encryption_seed = encryptionSeed;
15
- const client = new AUNClient(clientOpts);
16
- await client.auth.createAid({ aid });
17
- const authResult = await client.auth.authenticate({ aid });
18
- const accessToken = authResult?.access_token ?? client._access_token;
19
- const gateway = client._gatewayUrl ?? authResult?.gateway;
20
- await client.connect({
21
- access_token: accessToken,
22
- gateway,
23
- slot_id: slotId,
24
- connection_kind: 'short',
25
- }, { auto_reconnect: false });
26
- return {
27
- async call(method, params) {
28
- return client.call(method, params);
29
- },
30
- async close() {
31
- try {
32
- await client.close();
33
- }
34
- catch { /* ignore */ }
35
- },
36
- };
3
+ const store = await getAidStore({ slotId: opts?.slotId ?? SLOT.cli, aunPath: opts?.aunPath });
4
+ try {
5
+ const client = await loadClient(store, aid);
6
+ await client.connect({ connection_kind: 'short', short_ttl_ms: 30000, auto_reconnect: false });
7
+ return {
8
+ async call(method, params) {
9
+ return client.call(method, params);
10
+ },
11
+ async close() {
12
+ try {
13
+ await client.close();
14
+ }
15
+ finally {
16
+ store.close();
17
+ }
18
+ },
19
+ };
20
+ }
21
+ catch (e) {
22
+ store.close();
23
+ throw e;
24
+ }
37
25
  }
@@ -1,4 +1,4 @@
1
- import { AUNClient, GatewayDiscovery, E2EEError } from '@agentunion/fastaun';
1
+ import { GatewayDiscovery, E2EEError } from '@agentunion/fastaun';
2
2
  import crypto from 'crypto';
3
3
  import fs from 'fs';
4
4
  import path from 'path';
@@ -12,6 +12,7 @@ import { appendAidEvent } from '../utils/instance-registry.js';
12
12
  import { appendMessageLog, buildOutboundEntry } from '../core/message/message-log.js';
13
13
  import { chatDirPath } from '../core/session/session-fs-store.js';
14
14
  import { appendAidLifecycle } from '../aun/aid/identity.js';
15
+ import { getAidStore, loadClient, SLOT } from '../aun/aid/store.js';
15
16
  import { loadAgent, saveAgent } from '../config-store.js';
16
17
  import { getProcessStartTime } from '../utils/process-introspect.js';
17
18
  import * as outbox from '../aun/outbox.js';
@@ -57,49 +58,12 @@ function getEvolclawVersion() {
57
58
  }
58
59
  return _cachedVersion;
59
60
  }
60
- function migrateAunData(targetPath) {
61
- const legacyPath = path.join(os.homedir(), '.aun');
62
- if (legacyPath === targetPath)
63
- return;
64
- // AIDs 迁移:逐个检查每个 AID,缺少 private 的就从 legacy 复制
65
- const srcAIDs = path.join(legacyPath, 'AIDs');
66
- const dstAIDs = path.join(targetPath, 'AIDs');
67
- if (fs.existsSync(srcAIDs)) {
68
- fs.mkdirSync(dstAIDs, { recursive: true });
69
- try {
70
- for (const entry of fs.readdirSync(srcAIDs, { withFileTypes: true })) {
71
- if (!entry.isDirectory())
72
- continue;
73
- const aidName = entry.name;
74
- const srcAidDir = path.join(srcAIDs, aidName);
75
- const dstAidDir = path.join(dstAIDs, aidName);
76
- const srcPrivate = path.join(srcAidDir, 'private');
77
- const dstPrivate = path.join(dstAidDir, 'private');
78
- // 如果目标 AID 目录不存在或缺少 private,从源复制整个 AID 目录
79
- if (fs.existsSync(srcPrivate) && !fs.existsSync(dstPrivate)) {
80
- fs.cpSync(srcAidDir, dstAidDir, { recursive: true });
81
- }
82
- }
83
- }
84
- catch { }
85
- }
86
- // CA 迁移
87
- const srcCA = path.join(legacyPath, 'CA');
88
- const dstCA = path.join(targetPath, 'CA');
89
- if (fs.existsSync(srcCA) && !fs.existsSync(dstCA)) {
90
- fs.cpSync(srcCA, dstCA, { recursive: true });
91
- }
92
- for (const file of ['.seed', '.device_id']) {
93
- const src = path.join(legacyPath, file);
94
- const dst = path.join(targetPath, file);
95
- if (fs.existsSync(src) && !fs.existsSync(dst)) {
96
- fs.copyFileSync(src, dst);
97
- }
98
- }
99
- }
100
61
  export class AUNChannel {
101
62
  config;
102
63
  client = null;
64
+ store = null;
65
+ /** 实际连接的网关 URL(来自 authenticate() 返回值 / connection.state 事件),替代旧 (client as any)._gatewayUrl。 */
66
+ gatewayUrl = '';
103
67
  projectPathProvider;
104
68
  messageHandler;
105
69
  recallHandler;
@@ -154,11 +118,6 @@ export class AUNChannel {
154
118
  */
155
119
  async callAndTrace(method, params, opts) {
156
120
  this.trace('OUT', method, params);
157
- // [DIAG-STALE] 记录调用瞬间 SDK 内部 _state,证明是否在 reconnecting 中误发
158
- const sdkStateBefore = this.client?._state ?? 'no-client';
159
- if (sdkStateBefore !== 'connected') {
160
- logger.warn(`[AUN][DIAG-STALE] callAndTrace ${method} on non-connected SDK: sdk_state=${sdkStateBefore} evolclaw_connected=${this.connected}`);
161
- }
162
121
  try {
163
122
  const result = await this.client.call(method, params);
164
123
  if (!opts?.silentOk) {
@@ -176,9 +135,6 @@ export class AUNChannel {
176
135
  code: e?.code,
177
136
  name: e?.name,
178
137
  });
179
- // [DIAG-STALE] 失败时再记录一次 SDK _state,看错误类型是否为 ConnectionError
180
- const sdkStateAfter = this.client?._state ?? 'no-client';
181
- logger.warn(`[AUN][DIAG-STALE] callAndTrace ${method} FAILED: err_name=${e?.name ?? '?'} err_code=${e?.code ?? '?'} sdk_state_before=${sdkStateBefore} sdk_state_after=${sdkStateAfter} evolclaw_connected=${this.connected}`);
182
138
  logger.warn(`${this.logPrefix()} rpc ${method} failed: ${e?.name ?? ''}(${e?.code ?? ''}) ${e?.message ?? e}`);
183
139
  throw e;
184
140
  }
@@ -584,12 +540,18 @@ export class AUNChannel {
584
540
  }
585
541
  this.client = null;
586
542
  }
543
+ if (this.store) {
544
+ try {
545
+ this.store.close();
546
+ }
547
+ catch { /* ignore */ }
548
+ this.store = null;
549
+ }
587
550
  this.connected = false;
588
551
  const aunPath = this.config.keystorePath || resolveRoot();
589
552
  const aidName = this.config.aid;
590
- const encryptionSeed = this.config.encryptionSeed || process.env.AUN_ENCRYPTION_SEED || undefined;
591
- // Migrate legacy ~/.aun data to EVOLCLAW_HOME on first run
592
- migrateAunData(aunPath);
553
+ // encryptionSeed getAidStore 内部解析(config / env / 'evol')
554
+ // Migration from ~/.aun is handled by ensureDataDirs() at startup with a marker file.
593
555
  // Gateway URL 解析:优先用配置的 gatewayUrl,否则通过 well-known 自动发现
594
556
  let gateway = this.config.gatewayUrl || '';
595
557
  if (!gateway) {
@@ -609,41 +571,43 @@ export class AUNChannel {
609
571
  throw new Error('Cannot resolve gateway URL from AID');
610
572
  }
611
573
  logger.info(`${this.logPrefix()} Initializing: aid=${aidName}, gateway=${gateway}, aun_path=${aunPath}`);
612
- // Create client with FileSecretStore (AES-256-GCM)
613
- // 不传 encryption_seed 时,SDK 自动从 {aun_path}/.seed 文件派生密钥(与 aun_cli.py 对齐)
614
- const rootCaPath = path.join(aunPath, 'CA', 'root', 'root.crt');
615
- this.client = new AUNClient({
616
- aun_path: aunPath,
617
- root_ca_path: rootCaPath,
618
- ...(encryptionSeed && { encryption_seed: encryptionSeed }),
619
- }, this.config.aunSdkLog ?? true);
620
- // Set gateway URL (internal property, same as Python SDK)
621
- this.client._gatewayUrl = gateway;
574
+ // 构造 AIDStore(slot=evolclaw daemon,与 cli/netcheck 共享 evolclaw 隔离键)
575
+ // encryptionSeed / rootCaPath getAidStore 内部注入
576
+ const store = await getAidStore({
577
+ slotId: SLOT.daemon,
578
+ aunPath,
579
+ debug: this.config.aunSdkLog ?? true,
580
+ });
581
+ this.store = store;
582
+ const client = await loadClient(store, aidName);
583
+ this.client = client;
584
+ // 记录应用层发现的 gateway 作为初始值(authenticate 后会用权威值覆盖)
585
+ this.gatewayUrl = gateway;
622
586
  // Register event handlers before connecting
623
- this.client.on('message.received', (data) => {
587
+ client.on('message.received', (data) => {
624
588
  this.trace('IN', 'message.received', data);
625
589
  const kind = (data && typeof data === 'object') ? data.kind ?? '' : '';
626
590
  const keys = (data && typeof data === 'object') ? Object.keys(data).join(',') : typeof data;
627
591
  logger.debug(`${this.logPrefix()}[DIAG] message.received: kind=${kind} keys=${keys}`);
628
592
  this.handleIncomingPrivateMessage(data);
629
593
  });
630
- this.client.on('group.message_created', (data) => {
594
+ client.on('group.message_created', (data) => {
631
595
  this.trace('IN', 'group.message_created', data);
632
596
  const gid = (data && typeof data === 'object') ? data.group_id ?? '' : '';
633
597
  const sender = (data && typeof data === 'object') ? data.sender_aid ?? '' : '';
634
598
  logger.debug(`${this.logPrefix()}[DIAG] group.message_created: group_id=${gid} sender=${sender}`);
635
599
  this.handleIncomingGroupMessage(data);
636
600
  });
637
- this.client.on('connection.state', (data) => {
601
+ client.on('connection.state', (data) => {
638
602
  // trace is handled inside handleConnectionState with throttling
639
603
  this.handleConnectionState(data);
640
604
  });
641
605
  // gateway 被踢/服务端主动断开(含同槽位互踢的 self/new extra_info)
642
- this.client.on('gateway.disconnect', (data) => {
606
+ client.on('gateway.disconnect', (data) => {
643
607
  this.trace('IN', 'gateway.disconnect', data);
644
608
  this.handleGatewayDisconnect(data);
645
609
  });
646
- this.client.on('message.recalled', (data) => {
610
+ client.on('message.recalled', (data) => {
647
611
  this.trace('IN', 'message.recalled', data);
648
612
  if (data && typeof data === 'object') {
649
613
  const ids = data.message_ids;
@@ -657,40 +621,26 @@ export class AUNChannel {
657
621
  }
658
622
  }
659
623
  });
660
- this.client.on('message.undecryptable', (data) => {
624
+ client.on('message.undecryptable', (data) => {
661
625
  this.trace('IN', 'message.undecryptable', data);
662
626
  const d = data;
663
627
  logger.warn(`${this.logPrefix()} Message undecryptable: from=${d.from} mid=${d.message_id} err=${d._decrypt_error}`);
664
628
  });
665
- this.client.on('group.message_undecryptable', (data) => {
629
+ client.on('group.message_undecryptable', (data) => {
666
630
  this.trace('IN', 'group.message_undecryptable', data);
667
631
  const d = data;
668
632
  logger.warn(`${this.logPrefix()} Group message undecryptable: group=${d.group_id} from=${d.from} mid=${d.message_id} err=${d._decrypt_error}`);
669
633
  });
670
- // Authenticate
671
- // Workaround: SDK 0.3.x _loadIdentityOrRaise doesn't set identity.aid from requested aid,
672
- // causing gateway "missing aid" error. Patch to backfill aid on loaded identity.
673
- const authFlow = this.client._auth;
674
- if (authFlow && typeof authFlow._loadIdentityOrRaise === 'function') {
675
- const origLoad = authFlow._loadIdentityOrRaise.bind(authFlow);
676
- authFlow._loadIdentityOrRaise = (aid) => {
677
- const identity = origLoad(aid);
678
- if (identity && !identity.aid)
679
- identity.aid = aid ?? authFlow._aid;
680
- return identity;
681
- };
682
- }
683
- let accessToken;
634
+ // Authenticate(拿权威 gateway 用于日志/状态;connect 内部也会复用 token)
684
635
  try {
685
636
  logger.info(`${this.logPrefix()} Authenticating as ${aidName}...`);
686
637
  this.trace('OUT', 'auth.authenticate', { aid: aidName });
687
- const auth = await this.client.auth.authenticate(aidName ? { aid: aidName } : undefined);
688
- this.trace('OUT', 'auth.authenticate.ok', { aid: auth.aid, gateway: auth.gateway, hasToken: !!auth.access_token });
689
- this.trace('IN', 'auth.result', { aid: auth.aid, gateway: auth.gateway, hasToken: !!auth.access_token });
690
- accessToken = auth.access_token;
691
- const resolvedGateway = auth.gateway || gateway;
692
- this.client._gatewayUrl = resolvedGateway;
693
- logger.info(`${this.logPrefix()} Authenticated as ${auth.aid ?? '?'}, gateway=${resolvedGateway}`);
638
+ const auth = await client.authenticate();
639
+ this.trace('OUT', 'auth.authenticate.ok', { aid: client.aid, gateway: auth?.gateway, hasToken: !!auth?.access_token });
640
+ this.trace('IN', 'auth.result', { aid: client.aid, gateway: auth?.gateway, hasToken: !!auth?.access_token });
641
+ const resolvedGateway = String(auth?.gateway ?? gateway);
642
+ this.gatewayUrl = resolvedGateway;
643
+ logger.info(`${this.logPrefix()} Authenticated as ${client.aid ?? '?'}, gateway=${resolvedGateway}`);
694
644
  }
695
645
  catch (e) {
696
646
  const errMsg = e.message || String(e);
@@ -699,15 +649,9 @@ export class AUNChannel {
699
649
  logger.error(`${this.logPrefix()} Authentication failed (${errName}): ${errMsg}`);
700
650
  if (e.stack)
701
651
  logger.debug(`${this.logPrefix()} Auth stack: ${e.stack}`);
702
- // Fallback: try direct token from env/config (legacy)
703
- accessToken = this.config.accessToken || process.env.AUN_ACCESS_TOKEN || '';
704
- if (!accessToken) {
705
- logger.error(`${this.logPrefix()} No accessToken fallback available, scheduling retry`);
706
- this.setAidStatus('failed', { lastError: `${errName}: ${errMsg}`.slice(0, 80) });
707
- this.scheduleReconnect();
708
- throw new Error('Authentication failed and no accessToken fallback available');
709
- }
710
- logger.warn(`${this.logPrefix()} Using accessToken fallback`);
652
+ this.setAidStatus('failed', { lastError: `${errName}: ${errMsg}`.slice(0, 80) });
653
+ this.scheduleReconnect();
654
+ throw new Error(`Authentication failed: ${errName}: ${errMsg}`);
711
655
  }
712
656
  // Connect (SDK auto_reconnect handles transient failures)
713
657
  try {
@@ -716,12 +660,19 @@ export class AUNChannel {
716
660
  agentName: this.config.agentName,
717
661
  channelName: this.config.channelName,
718
662
  });
719
- this.trace('OUT', 'client.connect', { gateway: this.client._gatewayUrl, extra_info: extraInfo });
720
- await this.client.connect({ access_token: accessToken, gateway: this.client._gatewayUrl, extra_info: extraInfo },
721
- // max_attempts=0 = 无限重试(与 Go/Python 对齐),交由 SDK 自己跑指数退避
722
- // initial_delay=1s,max_delay=300s(5min 封顶)
723
- { auto_reconnect: true, retry: { max_attempts: 0, initial_delay: 1.0, max_delay: 300.0 } });
724
- this.trace('OUT', 'client.connect.ok', { aid: this.client.aid });
663
+ this.trace('OUT', 'client.connect', { gateway: this.gatewayUrl, extra_info: extraInfo });
664
+ await client.connect({
665
+ // connection_kind 默认 long;slot 已由 AID 携带(evolclaw daemon)
666
+ // extra_info:互踢诊断名片(0.4.3 公开 connect 已支持透传)
667
+ extra_info: extraInfo,
668
+ // max_attempts=0 = 无限重试(与 Go/Python 对齐),交由 SDK 自己跑指数退避
669
+ // initial_delay=1s,max_delay=300s(5min 封顶)
670
+ auto_reconnect: true,
671
+ retry_max_attempts: 0,
672
+ retry_initial_delay: 1.0,
673
+ retry_max_delay: 300.0,
674
+ });
675
+ this.trace('OUT', 'client.connect.ok', { aid: client.aid });
725
676
  this._aid = this.client.aid ?? undefined;
726
677
  const deviceId = this.client._device_id ?? '';
727
678
  this._chatId = this._aid ? `${this._aid}:${deviceId}:` : '';
@@ -730,7 +681,7 @@ export class AUNChannel {
730
681
  this.aidStatsCollector.setSelfName(this.config.aid, this._selfName);
731
682
  this.connected = true;
732
683
  this.connectedAt = Date.now();
733
- this.setAidStatus('connected', { lastConnectedAt: Date.now(), lastError: undefined, gatewayUrl: this.client?._gatewayUrl });
684
+ this.setAidStatus('connected', { lastConnectedAt: Date.now(), lastError: undefined, gatewayUrl: this.gatewayUrl });
734
685
  // Workaround: SDK e2ee uses _identity.cert for sender_cert_fingerprint;
735
686
  // if cert is missing, it falls back to public key SPKI fingerprint which
736
687
  // causes peer cert lookup failures. Backfill from keystore if needed.
@@ -743,8 +694,8 @@ export class AUNChannel {
743
694
  }
744
695
  }
745
696
  logger.info(`${this.logPrefix()} Connected as ${this._aid}`);
746
- appendAidEvent({ ts: Date.now(), iso: new Date().toISOString(), event: 'connected', aid: this.config.aid, gateway: this.client._gatewayUrl });
747
- appendAidLifecycle({ ts: Date.now(), iso: new Date().toISOString(), event: 'connected', aid: this.config.aid, gateway: this.client._gatewayUrl });
697
+ appendAidEvent({ ts: Date.now(), iso: new Date().toISOString(), event: 'connected', aid: this.config.aid, gateway: this.gatewayUrl });
698
+ appendAidLifecycle({ ts: Date.now(), iso: new Date().toISOString(), event: 'connected', aid: this.config.aid, gateway: this.gatewayUrl });
748
699
  // Send welcome message to owner after first connection
749
700
  await this.sendWelcomeMessage();
750
701
  }
@@ -823,13 +774,10 @@ tags:
823
774
 
824
775
  EvolClaw AI Agent 网关,支持多项目会话管理和多 AI 后端切换。
825
776
  `;
826
- // Write locally
827
- fs.mkdirSync(path.dirname(agentMdLocalPath), { recursive: true });
828
- fs.writeFileSync(agentMdLocalPath, newAgentMd, 'utf-8');
829
- logger.info(`${this.logPrefix()} Updated agent.md for ${aidName}`);
830
- // Publish to AUN network via publishAgentMd (auto-sign)
777
+ // Write locally and publish to AUN network (auto-sign)
831
778
  try {
832
- await this.client.publishAgentMd();
779
+ const { agentmdPut } = await import('../aun/aid/agentmd.js');
780
+ await agentmdPut(newAgentMd, { aid: aidName, store: this.store });
833
781
  logger.info(`${this.logPrefix()} Published agent.md to AUN network`);
834
782
  }
835
783
  catch (e) {
@@ -999,7 +947,7 @@ EvolClaw AI Agent 网关,支持多项目会话管理和多 AI 后端切换。
999
947
  const chatId = fromAid;
1000
948
  // 解析对端身份(30天缓存)
1001
949
  const selfAgentDir = path.join(resolvePaths().agentsDir, this.config.aid);
1002
- const peerIdentity = await PeerIdentityCache.resolve('aun', fromAid, selfAgentDir, this.client, false);
950
+ const peerIdentity = await PeerIdentityCache.resolve('aun', fromAid, selfAgentDir, this.store, false);
1003
951
  const shortAid = this.getShortAid(fromAid);
1004
952
  const displayName = peerIdentity.name || shortAid;
1005
953
  // 详细 dispatch 决策日志:记录消息为何被路由到 agent
@@ -1215,7 +1163,7 @@ EvolClaw AI Agent 网关,支持多项目会话管理和多 AI 后端切换。
1215
1163
  }
1216
1164
  }
1217
1165
  const selfAgentDir = path.join(resolvePaths().agentsDir, this.config.aid);
1218
- const peerIdentity = await PeerIdentityCache.resolve('aun', senderAid, selfAgentDir, this.client, false);
1166
+ const peerIdentity = await PeerIdentityCache.resolve('aun', senderAid, selfAgentDir, this.store, false);
1219
1167
  const shortAid = this.getShortAid(senderAid);
1220
1168
  const displayName = peerIdentity.name || shortAid;
1221
1169
  // 详细 dispatch 决策日志:记录消息为何被路由到 agent
@@ -1306,7 +1254,7 @@ EvolClaw AI Agent 网关,支持多项目会话管理和多 AI 后端切换。
1306
1254
  channelId: event.channelId || '',
1307
1255
  channelType: 'aun',
1308
1256
  content: event.text || '',
1309
- selfId: this._aid,
1257
+ selfAID: this._aid,
1310
1258
  groupId: event.groupId,
1311
1259
  chatType: event.chatType,
1312
1260
  peerId: event.userId || event.channelId || '',
@@ -1389,17 +1337,16 @@ EvolClaw AI Agent 网关,支持多项目会话管理和多 AI 后端切换。
1389
1337
  if (!data || typeof data !== 'object')
1390
1338
  return;
1391
1339
  const state = data.state ?? '';
1392
- // [DIAG-STALE] 记录状态切换瞬间 evolclaw 的 connected 标志和 SDK 的内部 _state,
1393
- // 用于证明"reconnecting 时 connected 保持 true,导致 sendMessage 误放行"的假设
1394
- const sdkState = this.client?._state ?? 'no-client';
1395
- const connectedBefore = this.connected;
1396
- logger.info(`[AUN][DIAG-STALE] connection.state event: state=${state} attempt=${data.attempt ?? '-'} | connected_before=${connectedBefore} sdk_state=${sdkState}`);
1397
1340
  if (state === 'connected') {
1398
1341
  this.connected = true;
1399
1342
  this.connectedAt = Date.now();
1400
1343
  this.lastReconnectLogTime = 0;
1401
1344
  this.lastReconnectLogAttempt = 0;
1402
- this.setAidStatus('connected', { lastConnectedAt: Date.now(), lastError: undefined, gatewayUrl: this.client?._gatewayUrl });
1345
+ // connection.state 事件 payload 带实际连接的 gateway,更新本地缓存
1346
+ const evtGateway = data.gateway;
1347
+ if (typeof evtGateway === 'string' && evtGateway)
1348
+ this.gatewayUrl = evtGateway;
1349
+ this.setAidStatus('connected', { lastConnectedAt: Date.now(), lastError: undefined, gatewayUrl: this.gatewayUrl });
1403
1350
  this.trace('IN', 'connection.state', data);
1404
1351
  logger.info(`${this.logPrefix()} Connected`);
1405
1352
  // 不在这里清 flapCount —— 短命连接一上来就会触发本分支,
@@ -1854,11 +1801,11 @@ EvolClaw AI Agent 网关,支持多项目会话管理和多 AI 后端切换。
1854
1801
  appendOutboundJsonl(channelId, text, msgId, encrypt, context, isGroup, msgType = 'text', source = 'daemon') {
1855
1802
  try {
1856
1803
  const sessionsDir = resolvePaths().sessionsDir;
1857
- const selfId = this.config.aid;
1858
- const chatDir = chatDirPath(sessionsDir, 'aun', channelId, selfId);
1804
+ const selfAID = this.config.aid;
1805
+ const chatDir = chatDirPath(sessionsDir, 'aun', channelId, selfAID);
1859
1806
  const chatmode = context?.metadata?.chatmode;
1860
1807
  appendMessageLog(chatDir, buildOutboundEntry({
1861
- from: selfId,
1808
+ from: selfAID,
1862
1809
  to: channelId,
1863
1810
  chatType: isGroup ? 'group' : 'private',
1864
1811
  groupId: isGroup ? channelId : null,
@@ -1929,8 +1876,8 @@ EvolClaw AI Agent 网关,支持多项目会话管理和多 AI 后端切换。
1929
1876
  const tid = putRes?.thought_id;
1930
1877
  logger.info(`${this.logPrefix()} thought.put ok group=${targetId} task=${taskId} stage=${stage} encrypt=${encrypt} tid=${tid ?? '?'}`);
1931
1878
  this.eventBus?.publish?.({ type: 'message:thought-put', agentName: this.config.aid, channelId, taskId, text: thoughtText });
1932
- // 文本类 thought 写入 jsonl(只对有 text 的 item,过滤 tool 等结构化项)
1933
1879
  if (thoughtText) {
1880
+ this.aidStatsCollector?.recordOutbound(this.config.aid, channelId, Buffer.byteLength(thoughtText, 'utf-8'), thoughtText, false, encrypt, context?.metadata?.chatmode ?? 'proactive');
1934
1881
  this.appendOutboundJsonl(channelId, thoughtText, tid ?? `thought-${Date.now()}`, encrypt, context, true, 'thought', 'daemon');
1935
1882
  }
1936
1883
  }
@@ -1941,6 +1888,7 @@ EvolClaw AI Agent 网关,支持多项目会话管理和多 AI 后端切换。
1941
1888
  logger.info(`${this.logPrefix()} thought.put ok p2p=${this.peerLabel(targetId)} task=${taskId} stage=${stage} encrypt=${encrypt} tid=${tid ?? '?'}`);
1942
1889
  this.eventBus?.publish?.({ type: 'message:thought-put', agentName: this.config.aid, channelId, taskId, text: thoughtText });
1943
1890
  if (thoughtText) {
1891
+ this.aidStatsCollector?.recordOutbound(this.config.aid, targetId, Buffer.byteLength(thoughtText, 'utf-8'), thoughtText, false, encrypt, context?.metadata?.chatmode ?? 'proactive');
1944
1892
  this.appendOutboundJsonl(channelId, thoughtText, tid ?? `thought-${Date.now()}`, encrypt, context, false, 'thought', 'daemon');
1945
1893
  }
1946
1894
  }
@@ -2318,6 +2266,13 @@ EvolClaw AI Agent 网关,支持多项目会话管理和多 AI 后端切换。
2318
2266
  }
2319
2267
  this.client = null;
2320
2268
  }
2269
+ if (this.store) {
2270
+ try {
2271
+ this.store.close();
2272
+ }
2273
+ catch { /* ignore */ }
2274
+ this.store = null;
2275
+ }
2321
2276
  this.connected = false;
2322
2277
  appendAidEvent({ ts: Date.now(), iso: new Date().toISOString(), event: 'disconnected', aid: this.config.aid, reason: 'intentional' });
2323
2278
  appendAidLifecycle({ ts: Date.now(), iso: new Date().toISOString(), event: 'disconnected', aid: this.config.aid, reason: 'intentional' });
@@ -2418,13 +2373,10 @@ EvolClaw AI Agent 网关,支持多项目会话管理和多 AI 后端切换。
2418
2373
  if (!this.client)
2419
2374
  return { type: null };
2420
2375
  try {
2421
- const { agentmdSync } = await import('../aun/aid/agentmd.js');
2422
- const result = await agentmdSync(aid, { client: this.client });
2423
- const md = result.content ?? '';
2424
- const typeMatch = md.match(/^type:\s*["']?(\w+)["']?/m);
2425
- const nameMatch = md.match(/^name:\s*["']?(.+?)["']?\s*$/m);
2426
- const type = typeMatch?.[1] === 'human' ? 'human' : 'ai';
2427
- const name = nameMatch?.[1]?.trim() || undefined;
2376
+ const selfAgentDir = path.join(resolvePaths().agentsDir, this.config.aid);
2377
+ const identity = await PeerIdentityCache.resolve('aun', aid, selfAgentDir, this.store, false);
2378
+ const type = identity.type === 'human' ? 'human' : 'ai';
2379
+ const name = identity.name || undefined;
2428
2380
  const info = { type, name };
2429
2381
  this.peerInfoCache.set(aid, info);
2430
2382
  setTimeout(() => this.peerInfoCache.delete(aid), 30 * 60 * 1000);
@@ -2446,19 +2398,16 @@ EvolClaw AI Agent 网关,支持多项目会话管理和多 AI 后端切换。
2446
2398
  void this.fetchPeerInfo(aid).catch(() => { });
2447
2399
  }
2448
2400
  async uploadAgentMd(content) {
2449
- if (!this.client)
2401
+ if (!this.store)
2450
2402
  throw new Error('not connected');
2451
- const { agentMdPath } = await import('../paths.js');
2452
- const localPath = agentMdPath(this.config.aid);
2453
- fs.mkdirSync(path.dirname(localPath), { recursive: true });
2454
- fs.writeFileSync(localPath, content, 'utf-8');
2455
- await this.client.publishAgentMd();
2403
+ const { agentmdPut } = await import('../aun/aid/agentmd.js');
2404
+ await agentmdPut(content, { aid: this.config.aid, store: this.store });
2456
2405
  }
2457
2406
  async downloadAgentMd(aid) {
2458
- if (!this.client)
2407
+ if (!this.store)
2459
2408
  throw new Error('not connected');
2460
2409
  const { agentmdSync } = await import('../aun/aid/agentmd.js');
2461
- const result = await agentmdSync(aid, { client: this.client });
2410
+ const result = await agentmdSync(aid, { store: this.store ?? undefined });
2462
2411
  return result.content ?? '';
2463
2412
  }
2464
2413
  }
@@ -2486,7 +2435,6 @@ export class AUNChannelPlugin {
2486
2435
  gatewayUrl: inst.gatewayUrl,
2487
2436
  accessToken: inst.accessToken,
2488
2437
  flushDelay: inst.flushDelay,
2489
- encryptionSeed: inst.encryptionSeed,
2490
2438
  owner: inst.owner,
2491
2439
  agentName: inst.agentName,
2492
2440
  channelName: inst.name,
@@ -2677,7 +2625,7 @@ export class AUNChannelPlugin {
2677
2625
  channel: adapter.channelName,
2678
2626
  channelType,
2679
2627
  channelId: opts.channelId,
2680
- selfId: opts.selfId,
2628
+ selfAID: opts.selfAID,
2681
2629
  groupId: opts.groupId,
2682
2630
  content: opts.content,
2683
2631
  chatType: opts.chatType || 'private',
@@ -540,6 +540,7 @@ export class DingtalkChannelPlugin {
540
540
  channel: adapter.channelName,
541
541
  channelType,
542
542
  channelId: event.channelId,
543
+ selfAID: inst.agentName,
543
544
  content: event.content,
544
545
  images: event.images,
545
546
  chatType: event.chatType || 'private',