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.
- package/CHANGELOG.md +27 -0
- package/assets/.env.template +4 -0
- package/assets/config.json.template +6 -0
- package/assets/wechat-group-qr.jpeg +0 -0
- package/dist/agents/claude-runner.js +348 -156
- package/dist/agents/kit-renderer.js +211 -42
- package/dist/aun/aid/agentmd.js +75 -139
- package/dist/aun/aid/client.js +1 -14
- package/dist/aun/aid/identity.js +381 -54
- package/dist/aun/aid/index.js +3 -2
- package/dist/aun/aid/store.js +74 -0
- package/dist/aun/msg/p2p.js +26 -2
- package/dist/aun/rpc/connection.js +23 -35
- package/dist/channels/aun.js +92 -144
- package/dist/channels/dingtalk.js +1 -0
- package/dist/channels/feishu.js +270 -190
- package/dist/channels/qqbot.js +1 -0
- package/dist/channels/wechat.js +1 -0
- package/dist/channels/wecom.js +1 -0
- package/dist/cli/agent.js +26 -27
- package/dist/cli/bench.js +45 -34
- package/dist/cli/help.js +23 -0
- package/dist/cli/index.js +538 -77
- package/dist/cli/init-channel.js +7 -4
- package/dist/cli/link-rules.js +2 -1
- package/dist/cli/model.js +324 -0
- package/dist/cli/net-check.js +138 -56
- package/dist/cli/watch-msg.js +7 -7
- package/dist/cli/watch-web/debug-log.js +18 -0
- package/dist/cli/watch-web/server.js +306 -0
- package/dist/cli/watch-web/sources/aid.js +63 -0
- package/dist/cli/watch-web/sources/msg.js +70 -0
- package/dist/cli/watch-web/sources/session.js +638 -0
- package/dist/cli/watch-web/sources/types.js +10 -0
- package/dist/cli/watch-web/static/app.js +546 -0
- package/dist/cli/watch-web/static/index.html +54 -0
- package/dist/cli/watch-web/static/style.css +247 -0
- package/dist/core/channel-loader.js +7 -4
- package/dist/core/command-handler.js +87 -93
- package/dist/core/evolagent-registry.js +1 -1
- package/dist/core/evolagent.js +4 -4
- package/dist/core/interaction-router.js +59 -0
- package/dist/core/message/message-bridge.js +6 -6
- package/dist/core/message/message-log.js +2 -2
- package/dist/core/message/message-processor.js +104 -118
- package/dist/core/message/stream-idle-monitor.js +21 -0
- package/dist/core/model/model-catalog.js +215 -0
- package/dist/core/model/model-scope.js +250 -0
- package/dist/core/relation/peer-identity.js +78 -44
- package/dist/core/relation/peer-key.js +16 -0
- package/dist/core/session/session-fs-store.js +34 -55
- package/dist/core/session/session-key.js +24 -0
- package/dist/core/session/session-manager.js +312 -251
- package/dist/core/session/session-mapper.js +9 -4
- package/dist/core/trigger/manager.js +37 -0
- package/dist/core/trigger/scheduler.js +2 -1
- package/dist/index.js +10 -3
- package/dist/ipc.js +22 -0
- package/dist/paths.js +87 -16
- package/dist/utils/npm-ops.js +18 -11
- package/kits/docs/GUIDE.md +2 -2
- package/kits/docs/INDEX.md +11 -7
- package/kits/docs/channels/aun.md +56 -17
- package/kits/docs/channels/feishu.md +41 -12
- package/kits/docs/context-assembly.md +181 -0
- package/kits/docs/evolclaw/agent.md +49 -0
- package/kits/docs/evolclaw/aid.md +49 -0
- package/kits/docs/evolclaw/ctl.md +46 -0
- package/kits/docs/evolclaw/group.md +82 -0
- package/kits/docs/evolclaw/msg.md +86 -0
- package/kits/docs/evolclaw/rpc.md +35 -0
- package/kits/docs/evolclaw/storage.md +49 -0
- package/kits/docs/venues/aun-group.md +10 -0
- package/kits/docs/venues/aun-private.md +10 -0
- package/kits/docs/venues/client-desktop.md +10 -0
- package/kits/docs/venues/client-mobile.md +10 -0
- package/kits/docs/venues/feishu-group.md +13 -0
- package/kits/docs/venues/feishu-private.md +9 -0
- package/kits/docs/venues/group.md +11 -0
- package/kits/docs/venues/private.md +10 -0
- package/kits/eck_manifest.json +75 -39
- package/kits/rules/01-overview.md +20 -10
- package/kits/rules/05-venue.md +2 -2
- package/kits/rules/06-channel.md +30 -27
- package/kits/templates/system-fragments/baseagent.md +7 -1
- package/kits/templates/system-fragments/channel.md +4 -1
- package/kits/templates/system-fragments/identity.md +4 -4
- package/kits/templates/system-fragments/relation.md +8 -5
- package/kits/templates/system-fragments/session.md +27 -0
- package/kits/templates/system-fragments/venue.md +13 -1
- package/package.json +13 -6
- package/dist/aun/aid/lifecycle-log.js +0 -33
- package/dist/net-check.js +0 -640
- package/dist/utils/aid-lifecycle-log.js +0 -33
- package/dist/watch-msg.js +0 -544
- package/kits/docs/evolclaw/AGENT_CMD.md +0 -31
- package/kits/docs/evolclaw/MSG_GROUP.md +0 -30
- package/kits/docs/evolclaw/MSG_PRIVATE.md +0 -72
- package/kits/docs/evolclaw/tools.md +0 -25
- package/kits/templates/system-fragments/eckruntime.md +0 -14
|
@@ -1,37 +1,25 @@
|
|
|
1
|
-
import
|
|
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
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
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
|
}
|
package/dist/channels/aun.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
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
|
-
|
|
591
|
-
//
|
|
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
|
-
//
|
|
613
|
-
//
|
|
614
|
-
const
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
this.client
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
606
|
+
client.on('gateway.disconnect', (data) => {
|
|
643
607
|
this.trace('IN', 'gateway.disconnect', data);
|
|
644
608
|
this.handleGatewayDisconnect(data);
|
|
645
609
|
});
|
|
646
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
688
|
-
this.trace('OUT', 'auth.authenticate.ok', { aid:
|
|
689
|
-
this.trace('IN', 'auth.result', { aid:
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
this.client.
|
|
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
|
-
|
|
703
|
-
|
|
704
|
-
|
|
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.
|
|
720
|
-
await
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
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.
|
|
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.
|
|
747
|
-
appendAidLifecycle({ ts: Date.now(), iso: new Date().toISOString(), event: 'connected', aid: this.config.aid, gateway: this.
|
|
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
|
|
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.
|
|
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.
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
1858
|
-
const chatDir = chatDirPath(sessionsDir, 'aun', channelId,
|
|
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:
|
|
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
|
|
2422
|
-
const
|
|
2423
|
-
const
|
|
2424
|
-
const
|
|
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.
|
|
2401
|
+
if (!this.store)
|
|
2450
2402
|
throw new Error('not connected');
|
|
2451
|
-
const {
|
|
2452
|
-
|
|
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.
|
|
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, {
|
|
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
|
-
|
|
2628
|
+
selfAID: opts.selfAID,
|
|
2681
2629
|
groupId: opts.groupId,
|
|
2682
2630
|
content: opts.content,
|
|
2683
2631
|
chatType: opts.chatType || 'private',
|