evolclaw 2.2.0 → 2.4.0

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 (43) hide show
  1. package/README.md +49 -27
  2. package/data/evolclaw.sample.json +6 -3
  3. package/dist/agents/claude-runner.js +125 -52
  4. package/dist/agents/codex-runner.js +10 -5
  5. package/dist/agents/gemini-runner.js +425 -0
  6. package/dist/channels/aun.js +283 -95
  7. package/dist/channels/feishu.js +556 -96
  8. package/dist/channels/wechat.js +98 -74
  9. package/dist/cli.js +232 -57
  10. package/dist/config.js +185 -31
  11. package/dist/core/channel-loader.js +11 -4
  12. package/dist/core/command-handler.js +803 -247
  13. package/dist/core/interaction-router.js +68 -0
  14. package/dist/core/message/message-bridge.js +217 -0
  15. package/dist/core/{message-processor.js → message/message-processor.js} +411 -124
  16. package/dist/core/{message-queue.js → message/message-queue.js} +1 -1
  17. package/dist/{utils → core/message}/stream-debouncer.js +1 -1
  18. package/dist/{utils → core/message}/stream-flusher.js +73 -13
  19. package/dist/core/permission.js +212 -11
  20. package/dist/core/{adapters → session/adapters}/claude-session-file-adapter.js +2 -2
  21. package/dist/core/{adapters → session/adapters}/codex-session-file-adapter.js +117 -52
  22. package/dist/core/session/adapters/gemini-session-file-adapter.js +177 -0
  23. package/dist/{utils → core/session}/session-file-health.js +1 -1
  24. package/dist/core/{session-manager.js → session/session-manager.js} +61 -11
  25. package/dist/index.js +140 -57
  26. package/dist/{core/ipc-server.js → ipc.js} +36 -1
  27. package/dist/types.js +3 -0
  28. package/dist/utils/cross-platform.js +38 -1
  29. package/dist/utils/error-utils.js +130 -5
  30. package/dist/utils/init-channel.js +649 -0
  31. package/dist/utils/init.js +55 -150
  32. package/dist/utils/logger.js +8 -3
  33. package/dist/utils/media-cache.js +207 -0
  34. package/dist/{core → utils}/stats-collector.js +16 -0
  35. package/package.json +3 -3
  36. package/dist/core/message-bridge.js +0 -187
  37. package/dist/utils/init-feishu.js +0 -263
  38. package/dist/utils/init-wechat.js +0 -172
  39. package/dist/utils/ipc-client.js +0 -36
  40. package/dist/utils/permission-utils.js +0 -71
  41. /package/dist/{utils → core/message}/message-cache.js +0 -0
  42. /package/dist/{utils → core/message}/stream-idle-monitor.js +0 -0
  43. /package/dist/core/{session-file-adapter.js → session/session-file-adapter.js} +0 -0
@@ -1,10 +1,73 @@
1
- import { AUNClient } from '@aun/core-node';
2
- import { logger } from '../utils/logger.js';
1
+ import { AUNClient } from '@eleans/aun-core-node';
2
+ import fs from 'fs';
3
+ import path from 'path';
4
+ import { logger, localTimestamp } from '../utils/logger.js';
5
+ import { normalizeChannelInstances } from '../config.js';
6
+ import { resolvePaths } from '../paths.js';
3
7
  export class AUNChannel {
4
8
  config;
5
9
  client = null;
6
10
  messageHandler;
7
11
  connected = false;
12
+ traceStream = null;
13
+ trace(dir, event, data) {
14
+ if (!this.traceStream)
15
+ return;
16
+ const line = JSON.stringify({ ts: localTimestamp(), dir, event, data });
17
+ this.traceStream.write(line + '\n');
18
+ }
19
+ /** 判断 channelId 是否为群组 ID(g-xxx.agentid.pub 或 grp_ 前缀) */
20
+ isGroupId(id) {
21
+ return id.startsWith('grp_') || (id.startsWith('g-') && id.includes('.'));
22
+ }
23
+ getShortAid(aid) {
24
+ if (!aid)
25
+ return undefined;
26
+ const trimmed = aid.trim();
27
+ if (!trimmed)
28
+ return undefined;
29
+ return trimmed.split('.')[0] || trimmed;
30
+ }
31
+ extractTextPayload(payload) {
32
+ if (typeof payload === 'string')
33
+ return payload;
34
+ if (payload && typeof payload === 'object') {
35
+ const text = payload.text;
36
+ if (typeof text === 'string')
37
+ return text;
38
+ return JSON.stringify(payload);
39
+ }
40
+ return '';
41
+ }
42
+ hasExplicitMention(text, target) {
43
+ const escaped = target.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
44
+ return new RegExp(`(^|\\s)@${escaped}(?=$|\\s|[.,!?;:,。!?;:]|[\\u4e00-\\u9fff])`).test(text);
45
+ }
46
+ stripTriggerMentions(text, selfAid) {
47
+ let result = text;
48
+ if (selfAid) {
49
+ const escapedAid = selfAid.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
50
+ result = result.replace(new RegExp(`(^|\\s)@${escapedAid}(?=$|\\s|[.,!?;:,。!?;:]|[\\u4e00-\\u9fff])`, 'g'), '$1');
51
+ }
52
+ result = result.replace(/(^|\s)@all(?=$|\s|[.,!?;:,。!?;:]|[\u4e00-\u9fff])/gi, '$1');
53
+ return result.replace(/[ \t]+/g, ' ').trim();
54
+ }
55
+ buildGroupReplyContext(taskId, senderAid) {
56
+ const replyContext = {};
57
+ if (taskId)
58
+ replyContext.threadId = taskId;
59
+ replyContext.peerId = senderAid;
60
+ return replyContext;
61
+ }
62
+ acknowledgeImmediately(messageId, seq) {
63
+ if (seq != null && this.client) {
64
+ this.client.call('message.ack', { seq }).catch(e => {
65
+ logger.debug(`[AUN] Immediate ack failed: ${e}`);
66
+ });
67
+ }
68
+ if (messageId)
69
+ this.messageSeqMap.delete(messageId);
70
+ }
8
71
  _aid;
9
72
  seenMessages = new Map();
10
73
  messageSeqMap = new Map(); // messageId → seq (for ack)
@@ -17,6 +80,11 @@ export class AUNChannel {
17
80
  onChannelDown;
18
81
  constructor(config) {
19
82
  this.config = config;
83
+ if (config.aunTrace) {
84
+ const logPath = path.join(resolvePaths().logs, 'aun-trace.log');
85
+ this.traceStream = fs.createWriteStream(logPath, { flags: 'a' });
86
+ logger.info(`[AUN] Trace logging enabled: ${logPath}`);
87
+ }
20
88
  }
21
89
  async connect() {
22
90
  this.intentionalDisconnect = false;
@@ -53,21 +121,51 @@ export class AUNChannel {
53
121
  logger.info(`[AUN] Initializing: aid=${aidName}, gateway=${gateway}, aun_path=${aunPath}`);
54
122
  // Create client with FileSecretStore (AES-256-GCM)
55
123
  // 不传 encryption_seed 时,SDK 自动从 {aun_path}/.seed 文件派生密钥(与 aun_cli.py 对齐)
124
+ const rootCaPath = `${aunPath}/CA/root/root.crt`;
56
125
  this.client = new AUNClient({
57
126
  aun_path: aunPath,
127
+ root_ca_path: rootCaPath,
58
128
  ...(encryptionSeed && { encryption_seed: encryptionSeed }),
59
129
  });
60
130
  // Set gateway URL (internal property, same as Python SDK)
61
131
  this.client._gatewayUrl = gateway;
62
132
  // Register event handlers before connecting
63
- this.client.on('message.received', (data) => this.handleIncomingPrivateMessage(data));
64
- this.client.on('group.message_created', (data) => this.handleIncomingGroupMessage(data));
65
- this.client.on('connection.state', (data) => this.handleConnectionState(data));
133
+ this.client.on('message.received', (data) => {
134
+ this.trace('IN', 'message.received', data);
135
+ const kind = (data && typeof data === 'object') ? data.kind ?? '' : '';
136
+ const keys = (data && typeof data === 'object') ? Object.keys(data).join(',') : typeof data;
137
+ logger.info(`[AUN][DIAG] message.received: kind=${kind} keys=${keys}`);
138
+ this.handleIncomingPrivateMessage(data);
139
+ });
140
+ this.client.on('group.message_created', (data) => {
141
+ this.trace('IN', 'group.message_created', data);
142
+ const gid = (data && typeof data === 'object') ? data.group_id ?? '' : '';
143
+ const sender = (data && typeof data === 'object') ? data.sender_aid ?? '' : '';
144
+ logger.info(`[AUN][DIAG] group.message_created: group_id=${gid} sender=${sender}`);
145
+ this.handleIncomingGroupMessage(data);
146
+ });
147
+ this.client.on('connection.state', (data) => {
148
+ this.trace('IN', 'connection.state', data);
149
+ this.handleConnectionState(data);
150
+ });
66
151
  // Authenticate
152
+ // Workaround: SDK 0.3.x _loadIdentityOrRaise doesn't set identity.aid from requested aid,
153
+ // causing gateway "missing aid" error. Patch to backfill aid on loaded identity.
154
+ const authFlow = this.client._auth;
155
+ if (authFlow && typeof authFlow._loadIdentityOrRaise === 'function') {
156
+ const origLoad = authFlow._loadIdentityOrRaise.bind(authFlow);
157
+ authFlow._loadIdentityOrRaise = (aid) => {
158
+ const identity = origLoad(aid);
159
+ if (identity && !identity.aid)
160
+ identity.aid = aid ?? authFlow._aid;
161
+ return identity;
162
+ };
163
+ }
67
164
  let accessToken;
68
165
  try {
69
166
  logger.info(`[AUN] Authenticating as ${aidName}...`);
70
167
  const auth = await this.client.auth.authenticate(aidName ? { aid: aidName } : undefined);
168
+ this.trace('IN', 'auth.result', { aid: auth.aid, gateway: auth.gateway, hasToken: !!auth.access_token });
71
169
  accessToken = auth.access_token;
72
170
  const resolvedGateway = auth.gateway || gateway;
73
171
  this.client._gatewayUrl = resolvedGateway;
@@ -82,7 +180,8 @@ export class AUNChannel {
82
180
  // Fallback: try direct token from env/config (legacy)
83
181
  accessToken = this.config.accessToken || process.env.AUN_ACCESS_TOKEN || '';
84
182
  if (!accessToken) {
85
- logger.error(`[AUN] No accessToken fallback available, AUN channel disabled`);
183
+ logger.error(`[AUN] No accessToken fallback available, scheduling retry`);
184
+ this.scheduleReconnect();
86
185
  return;
87
186
  }
88
187
  logger.warn(`[AUN] Using accessToken fallback`);
@@ -93,10 +192,22 @@ export class AUNChannel {
93
192
  this._aid = this.client.aid ?? undefined;
94
193
  this.connected = true;
95
194
  this.reconnectAttempt = 0;
195
+ // Workaround: SDK e2ee uses _identity.cert for sender_cert_fingerprint;
196
+ // if cert is missing, it falls back to public key SPKI fingerprint which
197
+ // causes peer cert lookup failures. Backfill from keystore if needed.
198
+ const clientAny = this.client;
199
+ if (clientAny._identity && !clientAny._identity.cert) {
200
+ const cert = clientAny._keystore?.loadCert?.(aidName);
201
+ if (cert) {
202
+ clientAny._identity.cert = cert;
203
+ logger.info('[AUN] Backfilled identity.cert from keystore for e2ee fingerprint');
204
+ }
205
+ }
96
206
  logger.info(`[AUN] Connected as ${this._aid}`);
97
207
  }
98
208
  catch (e) {
99
209
  logger.error(`[AUN] Connection failed: ${e}`);
210
+ this.scheduleReconnect();
100
211
  return;
101
212
  }
102
213
  }
@@ -107,7 +218,7 @@ export class AUNChannel {
107
218
  const msg = data;
108
219
  const fromAid = msg.from ?? '';
109
220
  const payload = msg.payload ?? '';
110
- const text = typeof payload === 'string' ? payload : (payload ? JSON.stringify(payload) : '');
221
+ const text = this.extractTextPayload(payload);
111
222
  const taskId = msg.task_id;
112
223
  const messageId = msg.message_id ?? '';
113
224
  const seq = msg.seq;
@@ -134,24 +245,48 @@ export class AUNChannel {
134
245
  const groupId = msg.group_id ?? '';
135
246
  const senderAid = msg.sender_aid ?? msg.from ?? '';
136
247
  const payload = msg.payload ?? '';
137
- const text = typeof payload === 'string' ? payload : (payload ? JSON.stringify(payload) : '');
248
+ const text = this.extractTextPayload(payload);
138
249
  const taskId = msg.task_id;
139
250
  const messageId = msg.message_id ?? '';
140
251
  const seq = msg.seq;
141
- // Detect @mentions
142
- const mentions = [];
143
- if (this._aid && text.includes(`@${this._aid}`)) {
144
- mentions.push(this._aid);
252
+ // Extract structured mentions from payload (e.g. payload.mentions: ["evolai.agentid.pub"])
253
+ const payloadMentions = Array.isArray(payload?.mentions)
254
+ ? payload.mentions.filter((m) => typeof m === 'string')
255
+ : [];
256
+ logger.info(`[AUN][DIAG-GRP] full_msg=${JSON.stringify(msg).substring(0, 500)}`);
257
+ if (!groupId || !senderAid) {
258
+ this.acknowledgeImmediately(messageId, seq);
259
+ return;
260
+ }
261
+ if (this._aid && senderAid === this._aid) {
262
+ this.acknowledgeImmediately(messageId, seq);
263
+ return;
264
+ }
265
+ const mentionedSelf = this._aid
266
+ ? (this.hasExplicitMention(text, this._aid) || payloadMentions.includes(this._aid))
267
+ : false;
268
+ const mentionedAll = this.hasExplicitMention(text, 'all') || payloadMentions.includes('all');
269
+ if (!mentionedSelf && !mentionedAll) {
270
+ this.acknowledgeImmediately(messageId, seq);
271
+ return;
145
272
  }
273
+ const strippedText = this.stripTriggerMentions(text, this._aid);
274
+ if (!strippedText) {
275
+ this.acknowledgeImmediately(messageId, seq);
276
+ return;
277
+ }
278
+ const mentions = mentionedAll ? ['all'] : (this._aid ? [this._aid] : []);
146
279
  this.dispatchMessage({
147
280
  channelId: groupId,
148
281
  userId: senderAid,
149
- text,
282
+ peerName: this.getShortAid(senderAid),
283
+ text: strippedText,
150
284
  chatType: 'group',
151
285
  messageId,
152
286
  seq,
153
287
  taskId,
154
288
  mentions,
289
+ replyContext: this.buildGroupReplyContext(taskId, senderAid),
155
290
  });
156
291
  }
157
292
  dispatchMessage(event) {
@@ -169,8 +304,10 @@ export class AUNChannel {
169
304
  if (!this.messageHandler)
170
305
  return;
171
306
  const mentionObjects = event.mentions?.map(aid => ({ userId: aid }));
172
- let replyContext;
173
- if (event.taskId) {
307
+ // Use caller-supplied replyContext (group path builds mentionUserIds);
308
+ // fall back to simple threadId-only context for private messages
309
+ let replyContext = event.replyContext;
310
+ if (!replyContext && event.taskId) {
174
311
  replyContext = { threadId: event.taskId };
175
312
  }
176
313
  this.messageHandler({
@@ -178,6 +315,7 @@ export class AUNChannel {
178
315
  content: event.text || '',
179
316
  chatType: event.chatType,
180
317
  peerId: event.userId || event.channelId || '',
318
+ peerName: event.peerName,
181
319
  messageId: event.messageId,
182
320
  threadId: event.taskId,
183
321
  mentions: mentionObjects,
@@ -230,60 +368,88 @@ export class AUNChannel {
230
368
  finalText = '最终回复\n' + text;
231
369
  }
232
370
  this.sentCount.set(channelId, (this.sentCount.get(channelId) || 0) + 1);
233
- const params = { payload: finalText, encrypt: true };
371
+ // 群聊 @ 兜底:提示词已告知 agent @,但如果 agent 没写,系统自动补上
372
+ if (this.isGroupId(channelId) && context?.peerId) {
373
+ if (!finalText.includes(`@${context.peerId}`)) {
374
+ finalText = `@${context.peerId} ` + finalText;
375
+ }
376
+ }
377
+ const params = { payload: { text: finalText }, encrypt: true };
234
378
  if (context?.threadId)
235
379
  params.task_id = context.threadId;
236
380
  try {
237
- if (channelId.startsWith('grp_')) {
381
+ if (this.isGroupId(channelId)) {
238
382
  params.group_id = channelId;
383
+ this.trace('OUT', 'group.send', params);
239
384
  await this.client.call('group.send', params);
240
385
  }
241
386
  else {
242
387
  params.to = channelId;
388
+ this.trace('OUT', 'message.send', params);
243
389
  await this.client.call('message.send', params);
244
390
  }
245
391
  }
246
392
  catch (e) {
393
+ this.trace('OUT', 'send.error', { channelId, error: String(e) });
247
394
  logger.error(`[AUN] Send failed to ${channelId}: ${e}`);
248
395
  }
249
396
  }
250
397
  acknowledge(messageId) {
251
- const seq = this.messageSeqMap.get(messageId);
252
- if (seq != null && this.client) {
253
- this.client.call('message.ack', { seq }).catch(e => {
254
- logger.debug(`[AUN] Ack failed: ${e}`);
255
- });
256
- this.messageSeqMap.delete(messageId);
257
- }
398
+ // Gateway auto-delivery-ack is sufficient; skip explicit message.ack RPC
399
+ // to avoid duplicate "已送达" at the sender CLI
400
+ this.messageSeqMap.delete(messageId);
258
401
  }
259
402
  sendProcessingStatus(channelId, status, sessionId, context) {
260
403
  if (status === 'start')
261
404
  this.sentCount.delete(channelId); // 新任务开始,重置计数
262
405
  if (!this.client || !this.connected)
263
406
  return;
264
- const payload = JSON.stringify({
407
+ const payload = {
265
408
  type: 'processing',
266
409
  status,
267
410
  sessionId,
268
411
  timestamp: Math.floor(Date.now() / 1000),
269
- });
412
+ };
270
413
  const params = {
271
- to: channelId, payload,
272
- encrypt: true, persist: false,
414
+ payload,
415
+ encrypt: true,
273
416
  };
274
417
  if (context?.threadId)
275
418
  params.task_id = context.threadId;
276
- this.client.call('message.send', params).catch(e => {
277
- logger.debug(`[AUN] Processing status failed: ${e}`);
278
- });
419
+ if (this.isGroupId(channelId)) {
420
+ params.group_id = channelId;
421
+ this.trace('OUT', 'group.send.status', params);
422
+ this.client.call('group.send', params).catch(e => {
423
+ logger.debug(`[AUN] Processing status failed: ${e}`);
424
+ });
425
+ }
426
+ else {
427
+ params.to = channelId;
428
+ this.trace('OUT', 'message.send.status', params);
429
+ this.client.call('message.send', params).catch(e => {
430
+ logger.debug(`[AUN] Processing status failed: ${e}`);
431
+ });
432
+ }
279
433
  }
280
434
  sendCustomPayload(channelId, payload) {
281
435
  if (!this.client || !this.connected)
282
436
  return;
283
- this.client.call('message.send', {
284
- to: channelId, payload,
285
- encrypt: true, persist: false,
286
- }).catch(e => {
437
+ // SDK 0.3.0 E2EE requires payload to be an object
438
+ let payloadObj;
439
+ try {
440
+ const parsed = JSON.parse(payload);
441
+ payloadObj = (parsed && typeof parsed === 'object' && !Array.isArray(parsed))
442
+ ? parsed : { text: payload };
443
+ }
444
+ catch {
445
+ payloadObj = { text: payload };
446
+ }
447
+ const sendParams = {
448
+ to: channelId, payload: payloadObj,
449
+ encrypt: true,
450
+ };
451
+ this.trace('OUT', 'message.send.custom', sendParams);
452
+ this.client.call('message.send', sendParams).catch(e => {
287
453
  logger.debug(`[AUN] Custom payload failed: ${e}`);
288
454
  });
289
455
  }
@@ -301,6 +467,10 @@ export class AUNChannel {
301
467
  this.client = null;
302
468
  }
303
469
  this.connected = false;
470
+ if (this.traceStream) {
471
+ this.traceStream.end();
472
+ this.traceStream = null;
473
+ }
304
474
  logger.info('[AUN] Disconnected');
305
475
  }
306
476
  // ── TS-layer reconnect (fallback when SDK auto_reconnect exhausted) ──
@@ -367,69 +537,87 @@ export class AUNChannel {
367
537
  export class AUNChannelPlugin {
368
538
  name = 'aun';
369
539
  isEnabled(config) {
370
- return config.channels?.aun?.enabled !== false && !!config.channels?.aun?.aid;
540
+ const raw = config.channels?.aun;
541
+ if (!raw)
542
+ return false;
543
+ if (Array.isArray(raw)) {
544
+ return raw.some(inst => inst.enabled !== false && !!inst.aid);
545
+ }
546
+ return raw.enabled !== false && !!raw.aid;
547
+ }
548
+ async createChannels(config) {
549
+ const instances = normalizeChannelInstances(config.channels?.aun, 'aun');
550
+ const result = [];
551
+ for (const inst of instances) {
552
+ if (inst.enabled === false || !inst.aid)
553
+ continue;
554
+ const channel = new AUNChannel({
555
+ aid: inst.aid,
556
+ keystorePath: inst.keystorePath,
557
+ gatewayPort: inst.gatewayPort,
558
+ gatewayUrl: inst.gatewayUrl,
559
+ accessToken: inst.accessToken,
560
+ flushDelay: inst.flushDelay,
561
+ encryptionSeed: inst.encryptionSeed,
562
+ aunTrace: config.debug?.aunTrace,
563
+ });
564
+ const adapter = {
565
+ channelName: inst.name,
566
+ sendText: (id, text, context) => channel.sendMessage(id, text, context),
567
+ acknowledge: (messageId) => { channel.acknowledge(messageId); return Promise.resolve(); },
568
+ sendProcessingStatus: (id, status, sessionId, context) => channel.sendProcessingStatus(id, status, sessionId, context),
569
+ sendCustomPayload: (id, payload) => channel.sendCustomPayload(id, payload),
570
+ };
571
+ const policy = {
572
+ canSwitchProject: (chatType, identity) => identity === 'owner',
573
+ canListProjects: (chatType, identity) => identity === 'owner',
574
+ canCreateSession: (chatType, identity) => true,
575
+ canDeleteSession: (chatType, identity) => true,
576
+ canImportCliSession: (chatType, identity) => identity === 'owner',
577
+ messagePrefix: (chatType, peerName) => (chatType === 'group' && peerName) ? `[${peerName}] ` : '',
578
+ showMiddleResult: (chatType, identity) => {
579
+ const mode = inst.showActivities ?? config.showActivities ?? 'all';
580
+ if (mode === 'none')
581
+ return false;
582
+ if (mode === 'dm-only')
583
+ return chatType === 'private';
584
+ if (mode === 'owner-dm-only')
585
+ return chatType === 'private' && identity === 'owner';
586
+ return true;
587
+ },
588
+ showIdleMonitor: (chatType, identity) => {
589
+ const mode = inst.showActivities ?? config.showActivities ?? 'all';
590
+ if (mode === 'none')
591
+ return false;
592
+ if (mode === 'dm-only')
593
+ return chatType === 'private';
594
+ if (mode === 'owner-dm-only')
595
+ return chatType === 'private' && identity === 'owner';
596
+ return true;
597
+ },
598
+ accumulateErrors: (chatType, identity) => true,
599
+ };
600
+ const options = {
601
+ flushDelay: inst.flushDelay ?? 3,
602
+ fileMarkerPattern: /\[SEND_FILE:(?:(\w+):)?([^\]]+)\]/g,
603
+ };
604
+ result.push({
605
+ channelType: 'aun',
606
+ adapter,
607
+ channel,
608
+ policy,
609
+ options,
610
+ connect: () => channel.connect(),
611
+ disconnect: () => channel.disconnect(),
612
+ });
613
+ }
614
+ return result;
371
615
  }
372
616
  async createChannel(config) {
373
- const aunConfig = config.channels?.aun;
374
- if (!aunConfig?.aid) {
617
+ const instances = await this.createChannels(config);
618
+ if (instances.length === 0) {
375
619
  throw new Error('AUN config missing (aid required, e.g. "mybot.agentid.pub")');
376
620
  }
377
- const channel = new AUNChannel({
378
- aid: aunConfig.aid,
379
- keystorePath: aunConfig.keystorePath,
380
- gatewayPort: aunConfig.gatewayPort,
381
- gatewayUrl: aunConfig.gatewayUrl,
382
- accessToken: aunConfig.accessToken,
383
- flushDelay: aunConfig.flushDelay,
384
- encryptionSeed: aunConfig.encryptionSeed,
385
- });
386
- const adapter = {
387
- name: 'aun',
388
- sendText: (id, text, context) => channel.sendMessage(id, text, context),
389
- acknowledge: (messageId) => { channel.acknowledge(messageId); return Promise.resolve(); },
390
- sendProcessingStatus: (id, status, sessionId, context) => channel.sendProcessingStatus(id, status, sessionId, context),
391
- sendCustomPayload: (id, payload) => channel.sendCustomPayload(id, payload),
392
- };
393
- const policy = {
394
- canSwitchProject: (chatType, identity) => identity === 'owner',
395
- canListProjects: (chatType, identity) => identity === 'owner',
396
- canCreateSession: (chatType, identity) => true,
397
- canDeleteSession: (chatType, identity) => true,
398
- canImportCliSession: (chatType, identity) => identity === 'owner',
399
- messagePrefix: (chatType, peerName) => (chatType === 'group' && peerName) ? `[${peerName}] ` : '',
400
- showMiddleResult: (chatType, identity) => {
401
- const mode = aunConfig.showActivities ?? config.showActivities ?? 'all';
402
- if (mode === 'none')
403
- return false;
404
- if (mode === 'dm-only')
405
- return chatType === 'private';
406
- if (mode === 'owner-dm-only')
407
- return chatType === 'private' && identity === 'owner';
408
- return true;
409
- },
410
- showIdleMonitor: (chatType, identity) => {
411
- const mode = aunConfig.showActivities ?? config.showActivities ?? 'all';
412
- if (mode === 'none')
413
- return false;
414
- if (mode === 'dm-only')
415
- return chatType === 'private';
416
- if (mode === 'owner-dm-only')
417
- return chatType === 'private' && identity === 'owner';
418
- return true;
419
- },
420
- accumulateErrors: (chatType, identity) => true,
421
- };
422
- const options = {
423
- flushDelay: aunConfig.flushDelay ?? 3,
424
- fileMarkerPattern: /\[SEND_FILE:(?:(\w+):)?([^\]]+)\]/g,
425
- };
426
- return {
427
- adapter,
428
- channel,
429
- policy,
430
- options,
431
- connect: () => channel.connect(),
432
- disconnect: () => channel.disconnect(),
433
- };
621
+ return instances[0];
434
622
  }
435
623
  }