evolclaw 3.1.1 → 3.1.3

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 (51) hide show
  1. package/CHANGELOG.md +428 -0
  2. package/README.md +3 -7
  3. package/SKILLS.md +311 -0
  4. package/dist/agents/claude-runner.js +1 -1
  5. package/dist/agents/codex-runner.js +75 -19
  6. package/dist/agents/gemini-runner.js +0 -2
  7. package/dist/agents/kit-renderer.js +59 -10
  8. package/dist/aun/aid/agentmd.js +50 -27
  9. package/dist/aun/aid/client.js +5 -11
  10. package/dist/aun/aid/identity.js +32 -13
  11. package/dist/aun/aid/index.js +1 -1
  12. package/dist/aun/msg/group.js +1 -0
  13. package/dist/aun/msg/p2p.js +15 -2
  14. package/dist/aun/msg/upload.js +57 -18
  15. package/dist/aun/rpc/connection.js +3 -0
  16. package/dist/channels/aun.js +122 -48
  17. package/dist/channels/dingtalk.js +1 -0
  18. package/dist/channels/feishu.js +5 -4
  19. package/dist/channels/qqbot.js +1 -0
  20. package/dist/channels/wechat.js +1 -0
  21. package/dist/channels/wecom.js +1 -0
  22. package/dist/cli/agent.js +142 -40
  23. package/dist/cli/index.js +103 -58
  24. package/dist/cli/init-channel.js +4 -2
  25. package/dist/cli/init.js +55 -26
  26. package/dist/cli/watch-msg.js +3 -1
  27. package/dist/config-store.js +22 -1
  28. package/dist/core/channel-loader.js +4 -4
  29. package/dist/core/command-handler.js +626 -538
  30. package/dist/core/evolagent-registry.js +45 -9
  31. package/dist/core/evolagent.js +35 -4
  32. package/dist/core/message/im-renderer.js +14 -4
  33. package/dist/core/message/message-bridge.js +149 -25
  34. package/dist/core/message/message-processor.js +45 -38
  35. package/dist/core/session/session-fs-store.js +23 -0
  36. package/dist/core/session/session-manager.js +188 -42
  37. package/dist/index.js +15 -17
  38. package/dist/paths.js +35 -0
  39. package/dist/utils/cross-platform.js +2 -1
  40. package/kits/docs/INDEX.md +6 -0
  41. package/kits/eck_manifest.json +3 -3
  42. package/kits/rules/02-navigation.md +1 -0
  43. package/kits/rules/06-channel.md +2 -18
  44. package/kits/templates/system-fragments/baseagent.md +2 -2
  45. package/kits/templates/system-fragments/channel.md +18 -9
  46. package/kits/templates/system-fragments/eckruntime.md +14 -0
  47. package/kits/templates/system-fragments/identity.md +5 -6
  48. package/kits/templates/system-fragments/relation.md +7 -5
  49. package/kits/templates/system-fragments/venue.md +2 -3
  50. package/package.json +5 -2
  51. package/kits/templates/system-fragments/runtime.md +0 -19
@@ -1,7 +1,7 @@
1
1
  import fs from 'fs';
2
2
  import path from 'path';
3
- import os from 'os';
4
3
  import { getAunClient } from './client.js';
4
+ import { agentMdPath, aidLocalDir, resolveRoot } from '../../paths.js';
5
5
  export function buildInitialAgentMd(opts) {
6
6
  const agentName = opts.aid.split('.')[0];
7
7
  const agentType = opts.type || 'ai';
@@ -82,16 +82,17 @@ async function verifyContent(content, aid, certPem, client) {
82
82
  * Create a bare AUNClient (no createAid) for read-only operations.
83
83
  */
84
84
  async function createBareClient(aunPath) {
85
+ const p = aunPath ?? resolveRoot();
85
86
  const { AUNClient } = await import('@agentunion/fastaun');
86
- const caCertPath = path.join(aunPath, 'CA', 'root', 'root.crt');
87
- const clientOpts = { aun_path: aunPath, debug: false };
87
+ const caCertPath = path.join(p, 'CA', 'root', 'root.crt');
88
+ const clientOpts = { aun_path: p, debug: false };
88
89
  if (fs.existsSync(caCertPath))
89
90
  clientOpts.root_ca_path = caCertPath;
90
91
  return new AUNClient(clientOpts);
91
92
  }
92
93
  export async function agentmdGet(aid, opts) {
93
- const aunPath = opts?.aunPath ?? path.join(os.homedir(), '.aun');
94
- const localPath = path.join(aunPath, 'AIDs', aid, 'agent.md');
94
+ const aunPath = opts?.aunPath ?? resolveRoot();
95
+ const localPath = agentMdPath(aid);
95
96
  // === Path A: local agent.md exists ===
96
97
  if (fs.existsSync(localPath)) {
97
98
  const content = fs.readFileSync(localPath, 'utf-8');
@@ -108,7 +109,8 @@ export async function agentmdGet(aid, opts) {
108
109
  }
109
110
  // Fallback: local invalid → try remote
110
111
  try {
111
- const remote = await client.auth.downloadAgentMd(aid);
112
+ const info = await client.fetchAgentMd(aid);
113
+ const remote = info.content;
112
114
  if (remote) {
113
115
  const remoteVerification = await verifyContent(remote, aid, certPem, client);
114
116
  if (remoteVerification.status === 'verified') {
@@ -132,20 +134,13 @@ export async function agentmdGet(aid, opts) {
132
134
  const client = opts?.client ?? await createBareClient(aunPath);
133
135
  const ownClient = !opts?.client;
134
136
  try {
135
- const raw = await client.auth.downloadAgentMd(aid);
137
+ const info = await client.fetchAgentMd(aid);
138
+ const raw = info.content;
136
139
  if (!opts?.withVerification) {
137
- // Persist without verification
138
- const aidDir = path.join(aunPath, 'AIDs', aid);
139
- fs.mkdirSync(aidDir, { recursive: true });
140
- fs.writeFileSync(path.join(aidDir, 'agent.md'), raw, 'utf-8');
141
140
  return raw;
142
141
  }
143
142
  const certPem = await obtainCertPem(aid, aunPath, client);
144
143
  const verification = await verifyContent(raw, aid, certPem, client);
145
- // Persist to local
146
- const aidDir = path.join(aunPath, 'AIDs', aid);
147
- fs.mkdirSync(aidDir, { recursive: true });
148
- fs.writeFileSync(path.join(aidDir, 'agent.md'), raw, 'utf-8');
149
144
  return { content: raw, verification };
150
145
  }
151
146
  finally {
@@ -157,24 +152,52 @@ export async function agentmdGet(aid, opts) {
157
152
  }
158
153
  }
159
154
  /**
160
- * Upload agent.md: auto-sign + upload + sync to local file.
155
+ * Upload agent.md: write to local file → publishAgentMd (auto-sign + upload).
161
156
  */
162
157
  export async function agentmdPut(content, opts) {
163
- const aunPath = opts.aunPath ?? path.join(os.homedir(), '.aun');
158
+ const aunPath = opts.aunPath ?? resolveRoot();
164
159
  const client = opts.client ?? await getAunClient(opts.aid, { aunPath });
165
160
  const ownClient = !opts.client;
161
+ const dir = aidLocalDir(opts.aid);
162
+ const filePath = path.join(dir, 'agent.md');
163
+ const existed = fs.existsSync(filePath);
166
164
  try {
167
- let signed;
168
- try {
169
- signed = await client.auth.signAgentMd(content);
170
- }
171
- catch {
172
- signed = content;
165
+ fs.mkdirSync(dir, { recursive: true });
166
+ fs.writeFileSync(filePath, content, 'utf-8');
167
+ await client.publishAgentMd();
168
+ }
169
+ catch (e) {
170
+ if (!existed)
171
+ try {
172
+ fs.unlinkSync(filePath);
173
+ }
174
+ catch { /* ignore */ }
175
+ throw e;
176
+ }
177
+ finally {
178
+ if (ownClient)
179
+ try {
180
+ await client.close();
181
+ }
182
+ catch { /* ignore */ }
183
+ }
184
+ }
185
+ /**
186
+ * Check if agent.md is up-to-date (30-day cache), fetch if changed.
187
+ * Returns changed=true + content when a new version was downloaded.
188
+ */
189
+ export async function agentmdSync(aid, opts) {
190
+ const client = opts?.client ?? await createBareClient();
191
+ const ownClient = !opts?.client;
192
+ try {
193
+ const state = await client.checkAgentMd(aid, 30);
194
+ if (!state.in_sync || !state.local_found) {
195
+ const info = await client.fetchAgentMd(aid);
196
+ return { changed: true, content: info.content };
173
197
  }
174
- await client.auth.uploadAgentMd(signed);
175
- const aidDir = path.join(aunPath, 'AIDs', opts.aid);
176
- fs.mkdirSync(aidDir, { recursive: true });
177
- fs.writeFileSync(path.join(aidDir, 'agent.md'), signed, 'utf-8');
198
+ const localPath = agentMdPath(aid);
199
+ const content = fs.existsSync(localPath) ? fs.readFileSync(localPath, 'utf-8') : undefined;
200
+ return { changed: false, content };
178
201
  }
179
202
  finally {
180
203
  if (ownClient)
@@ -14,21 +14,15 @@ export function suppressSdkLogs() {
14
14
  const _origLog = console.log;
15
15
  const _origInfo = console.info;
16
16
  const _origWarn = console.warn;
17
- const _origError = console.error;
18
- const SDK_LOG_RE = /^\[\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}\.\d+\]\[(?:DEBUG|INFO|WARN)\]/;
19
- const SDK_ERROR_RE = /^\[\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}\.\d+\]\[ERROR\]/;
20
- console.log = (...args) => { if (typeof args[0] === 'string') {
21
- if (SDK_LOG_RE.test(args[0]))
22
- return;
23
- if (SDK_ERROR_RE.test(args[0])) {
24
- _origError(...args);
25
- return;
26
- }
27
- } _origLog(...args); };
17
+ const SDK_LOG_RE = /^\[\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}\.\d+\]\[(?:DEBUG|INFO|WARN|ERROR)\]/;
18
+ console.log = (...args) => { if (typeof args[0] === 'string' && SDK_LOG_RE.test(args[0]))
19
+ return; _origLog(...args); };
28
20
  console.info = (...args) => { if (typeof args[0] === 'string' && SDK_LOG_RE.test(args[0]))
29
21
  return; _origInfo(...args); };
30
22
  console.warn = (...args) => { if (typeof args[0] === 'string' && SDK_LOG_RE.test(args[0]))
31
23
  return; _origWarn(...args); };
24
+ console.error = (...args) => { if (typeof args[0] === 'string' && SDK_LOG_RE.test(args[0]))
25
+ return; process.stderr.write(args.map(String).join(' ') + '\n'); };
32
26
  }
33
27
  // ==================== Constants ====================
34
28
  export const MIN_AUN_CORE_SDK = [0, 2, 17];
@@ -3,7 +3,7 @@ import path from 'path';
3
3
  import os from 'os';
4
4
  import crypto from 'crypto';
5
5
  import { getAunClient, downloadCaRoot } from './client.js';
6
- import { resolvePaths } from '../../paths.js';
6
+ import { resolvePaths, aidsDir as evolclawAidsDir, agentMdPath } from '../../paths.js';
7
7
  // ==================== Validation ====================
8
8
  export function isValidAid(name) {
9
9
  const labels = name.split('.');
@@ -11,17 +11,36 @@ export function isValidAid(name) {
11
11
  }
12
12
  // ==================== AID Operations ====================
13
13
  export function aidList(aunPath) {
14
- const aidsDir = path.join(aunPath ?? path.join(os.homedir(), '.aun'), 'AIDs');
15
- if (!fs.existsSync(aidsDir))
16
- return [];
17
- const entries = fs.readdirSync(aidsDir, { withFileTypes: true });
18
- return entries
19
- .filter(e => e.isDirectory())
20
- .map(e => ({
21
- aid: e.name,
22
- hasPrivateKey: fs.existsSync(path.join(aidsDir, e.name, 'private')),
23
- hasAgentMd: fs.existsSync(path.join(aidsDir, e.name, 'agent.md')),
24
- }));
14
+ const aunAidsDir = path.join(aunPath ?? path.join(os.homedir(), '.aun'), 'AIDs');
15
+ const ecAidsDir = evolclawAidsDir();
16
+ const seen = new Map();
17
+ // Scan ~/.aun/AIDs (private keys live here)
18
+ if (fs.existsSync(aunAidsDir)) {
19
+ for (const e of fs.readdirSync(aunAidsDir, { withFileTypes: true })) {
20
+ if (!e.isDirectory())
21
+ continue;
22
+ seen.set(e.name, {
23
+ aid: e.name,
24
+ hasPrivateKey: fs.existsSync(path.join(aunAidsDir, e.name, 'private')),
25
+ hasAgentMd: fs.existsSync(agentMdPath(e.name)),
26
+ });
27
+ }
28
+ }
29
+ // Scan $EVOLCLAW_HOME/AIDs (agent.md lives here)
30
+ if (fs.existsSync(ecAidsDir) && ecAidsDir !== aunAidsDir) {
31
+ for (const e of fs.readdirSync(ecAidsDir, { withFileTypes: true })) {
32
+ if (!e.isDirectory())
33
+ continue;
34
+ if (seen.has(e.name))
35
+ continue;
36
+ seen.set(e.name, {
37
+ aid: e.name,
38
+ hasPrivateKey: fs.existsSync(path.join(aunAidsDir, e.name, 'private')),
39
+ hasAgentMd: fs.existsSync(agentMdPath(e.name)),
40
+ });
41
+ }
42
+ }
43
+ return [...seen.values()];
25
44
  }
26
45
  export async function aidCreate(aid, opts) {
27
46
  const aunPath = opts?.aunPath ?? path.join(os.homedir(), '.aun');
@@ -71,7 +90,7 @@ export function aidShow(aid, opts) {
71
90
  const aunPath = opts?.aunPath ?? path.join(os.homedir(), '.aun');
72
91
  const aidDir = path.join(aunPath, 'AIDs', aid);
73
92
  const hasPrivateKey = fs.existsSync(path.join(aidDir, 'private'));
74
- const hasAgentMd = fs.existsSync(path.join(aidDir, 'agent.md'));
93
+ const hasAgentMd = fs.existsSync(agentMdPath(aid));
75
94
  let certExpiresAt = null;
76
95
  let certSubject = null;
77
96
  const certPath = path.join(aidDir, 'public', 'cert.pem');
@@ -1,3 +1,3 @@
1
1
  export { isValidAid, aidList, aidCreate, aidShow, aidDelete, aidLookup, appendAidLifecycle, readAidLifecycle } from './identity.js';
2
- export { buildInitialAgentMd, agentmdGet, agentmdPut } from './agentmd.js';
2
+ export { buildInitialAgentMd, agentmdGet, agentmdPut, agentmdSync } from './agentmd.js';
3
3
  export { MIN_AUN_CORE_SDK, AUN_CORE_SDK_PKG, isAunSdkVersionOk, resolveAunCoreSdkPkg, ensureAunSdk, isAunSdkReady, downloadCaRoot, getAunClient, suppressSdkLogs, } from './client.js';
@@ -17,6 +17,7 @@ export async function groupSend(args) {
17
17
  contentType: args.body.contentType,
18
18
  text: args.body.text,
19
19
  transcript: args.body.transcript,
20
+ onProgress: args.onUploadProgress,
20
21
  });
21
22
  payload = built.payload;
22
23
  break;
@@ -37,6 +37,7 @@ export async function msgSend(args) {
37
37
  contentType: args.body.contentType,
38
38
  text: args.body.text,
39
39
  transcript: args.body.transcript,
40
+ onProgress: args.onUploadProgress,
40
41
  });
41
42
  payload = built.payload;
42
43
  break;
@@ -44,11 +45,18 @@ export async function msgSend(args) {
44
45
  }
45
46
  // 4. 写入 payload.chatmode
46
47
  payload.chatmode = chatmode;
48
+ // 5. 写入 payload.thread_id(如果指定)
49
+ if (args.thread) {
50
+ payload.thread_id = args.thread;
51
+ }
47
52
  const sendParams = { to: args.to, payload };
48
53
  // Default: plaintext. Set encrypt: true to enable E2EE.
49
54
  sendParams.encrypt = args.encrypt === true;
50
55
  const result = await conn.call('message.send', sendParams);
51
- // 5. 写出方向 jsonl(与 daemon 一致格式,标记 source=cli
56
+ // 5. 写出方向 jsonl(与 daemon 一致格式,标记 source)
57
+ // source 标记:
58
+ // - 'cli': 用户手动调用 ec msg send
59
+ // - 'msg': agent 在会话中调用 ec msg send
52
60
  if (result?.message_id) {
53
61
  try {
54
62
  const sessionsDir = resolvePaths().sessionsDir;
@@ -57,6 +65,11 @@ export async function msgSend(args) {
57
65
  : args.body.mode === 'link' ? `[link] ${args.body.url}`
58
66
  : args.body.mode === 'file' ? `[file] ${args.body.filePath}`
59
67
  : `[payload]`;
68
+ // 判断是 agent 调用还是用户手动调用
69
+ // 优先通过 --thread 参数判断(有 thread → msg,无 → cli)
70
+ // 兜底通过环境变量判断(向后兼容)
71
+ const isInSession = !!process.env.EVOLCLAW_SESSION_ID;
72
+ const source = args.thread ? 'msg' : (isInSession ? 'msg' : 'cli');
60
73
  appendMessageLog(chatDir, buildOutboundEntry({
61
74
  from: args.from,
62
75
  to: args.to,
@@ -66,7 +79,7 @@ export async function msgSend(args) {
66
79
  encrypt: args.encrypt === true,
67
80
  chatmode, // 使用解析出的 chatmode
68
81
  msgType: 'text',
69
- source: 'cli',
82
+ source,
70
83
  }));
71
84
  }
72
85
  catch { }
@@ -3,21 +3,19 @@ import fs from 'fs';
3
3
  import path from 'path';
4
4
  import { guessMime, formatSize } from '../../utils/media-cache.js';
5
5
  import { inferPayloadType, isValidPayloadType } from './payload-type.js';
6
- /** 小文件阈值:≤64KB 走 storage.put_object 内联 base64;>64KB 走 create_upload_session + HTTP PUT。 */
7
- const INLINE_UPLOAD_LIMIT = 64 * 1024;
8
6
  /** 单次上传最大大小(与 daemon sendFile 一致)。 */
9
7
  const MAX_FILE_SIZE = 10 * 1024 * 1024;
8
+ /** server 端 inline 上限错误的识别特征。优先匹配错误消息里的“inline 上限”字样。 */
9
+ function isInlineLimitError(err) {
10
+ const msg = err?.message ?? '';
11
+ return /inline\s*上限|inline limit|create_upload_session/i.test(msg);
12
+ }
10
13
  /**
11
14
  * 上传本地文件并构造发送用的 payload。
12
15
  *
13
- * 流程:
14
- * 1. 读文件、算 sha256、推断 content_type
15
- * 2. 小文件 storage.put_object,大文件 storage.create_upload_session + HTTP PUT + storage.complete_upload
16
- * 3. 按 as / 扩展名 确定 payload.type
17
- * 4. 构造 payload(含 attachments 引用)
18
- *
19
- * 不做 outbox 持久化、不做 E2EE 加密兜底——这些是 daemon 的职责。
20
- * CLI 短连接场景假定网络稳定,失败抛异常给调用方处理。
16
+ * 策略:先无脑 storage.put_object(内联 base64)。server 报“超过 inline 上限”
17
+ * 时降级到 storage.create_upload_session + HTTP PUT + storage.complete_upload,
18
+ * 此时通过 onProgress 上报字节级进度。
21
19
  */
22
20
  export async function uploadFileAndBuildPayload(conn, ownerAid, filePath, opts) {
23
21
  const absPath = path.resolve(filePath);
@@ -29,14 +27,19 @@ export async function uploadFileAndBuildPayload(conn, ownerAid, filePath, opts)
29
27
  throw new Error(`文件为空: ${absPath}`);
30
28
  }
31
29
  if (stat.size > MAX_FILE_SIZE) {
32
- throw new Error(`文件过大 (${formatSize(stat.size)}, ${formatSize(MAX_FILE_SIZE)}): ${absPath}`);
30
+ throw new Error(`文件过大 (${formatSize(stat.size)}, 上限 ${formatSize(MAX_FILE_SIZE)}): ${absPath}`);
33
31
  }
34
32
  const filename = path.basename(absPath);
35
33
  const fileData = fs.readFileSync(absPath);
36
34
  const sha256 = crypto.createHash('sha256').update(fileData).digest('hex');
37
35
  const contentType = opts?.contentType ?? guessMime(filename);
38
36
  const objectKey = `shared/${crypto.randomUUID()}/${filename}`;
39
- if (stat.size <= INLINE_UPLOAD_LIMIT) {
37
+ const total = stat.size;
38
+ const report = (phase, bytes) => opts?.onProgress?.({ phase, bytes, total });
39
+ // 1. 先按 inline 走
40
+ let inlineRejected = false;
41
+ try {
42
+ report('inline', 0);
40
43
  await conn.call('storage.put_object', {
41
44
  object_key: objectKey,
42
45
  content: fileData.toString('base64'),
@@ -44,32 +47,68 @@ export async function uploadFileAndBuildPayload(conn, ownerAid, filePath, opts)
44
47
  is_private: false,
45
48
  overwrite: true,
46
49
  });
50
+ report('inline', total);
47
51
  }
48
- else {
52
+ catch (e) {
53
+ if (!isInlineLimitError(e))
54
+ throw e;
55
+ inlineRejected = true;
56
+ }
57
+ // 2. inline 被拒:降级到 session + HTTP PUT
58
+ if (inlineRejected) {
59
+ report('session-create', 0);
49
60
  const session = await conn.call('storage.create_upload_session', {
50
61
  object_key: objectKey,
51
- size_bytes: stat.size,
62
+ size_bytes: total,
52
63
  content_type: contentType,
53
64
  });
54
65
  const uploadUrl = session?.upload_url;
55
66
  if (!uploadUrl)
56
67
  throw new Error('storage.create_upload_session 未返回 upload_url');
57
- const uploadResp = await fetch(uploadUrl, { method: 'PUT', body: fileData });
68
+ // 简单 Buffer 上传 + 周期性进度(不用流,避免某些 storage 网关对 chunked / duplex 不友好)
69
+ report('http-put', 0);
70
+ const PROGRESS_TICK_MS = 250;
71
+ let lastBytes = 0;
72
+ const tickerStart = Date.now();
73
+ // 简单的“估算”进度:实际上一次性发,但在等待响应期间按 elapsed 模拟字节数,让用户看到在跑
74
+ const ticker = setInterval(() => {
75
+ const elapsed = Date.now() - tickerStart;
76
+ // 假设 2MB/s 估算;不超过 99%
77
+ const estimated = Math.min(total - 1, Math.floor((elapsed / 1000) * 2 * 1024 * 1024));
78
+ if (estimated > lastBytes) {
79
+ lastBytes = estimated;
80
+ report('http-put', estimated);
81
+ }
82
+ }, PROGRESS_TICK_MS);
83
+ let uploadResp;
84
+ try {
85
+ uploadResp = await fetch(uploadUrl, {
86
+ method: 'PUT',
87
+ body: new Blob([new Uint8Array(fileData)], { type: contentType }),
88
+ headers: { 'Content-Type': contentType },
89
+ });
90
+ }
91
+ finally {
92
+ clearInterval(ticker);
93
+ }
58
94
  if (!uploadResp.ok)
59
95
  throw new Error(`HTTP 上传失败: ${uploadResp.status}`);
96
+ report('http-put', total);
97
+ report('session-complete', total);
60
98
  await conn.call('storage.complete_upload', {
61
99
  object_key: objectKey,
62
100
  sha256,
63
101
  content_type: contentType,
64
102
  is_private: false,
65
- size_bytes: stat.size,
103
+ size_bytes: total,
66
104
  });
67
105
  }
106
+ report('done', total);
68
107
  const attachment = {
69
108
  owner_aid: ownerAid,
70
109
  object_key: objectKey,
71
110
  filename,
72
- size_bytes: stat.size,
111
+ size_bytes: total,
73
112
  sha256,
74
113
  content_type: contentType,
75
114
  };
@@ -91,7 +130,7 @@ export async function uploadFileAndBuildPayload(conn, ownerAid, filePath, opts)
91
130
  if (opts?.text)
92
131
  payload.text = opts.text;
93
132
  else if (type === 'file')
94
- payload.text = `📎 ${filename} (${formatSize(stat.size)})`;
133
+ payload.text = `📎 ${filename} (${formatSize(total)})`;
95
134
  if (type === 'voice' && opts?.transcript)
96
135
  payload.transcript = opts.transcript;
97
136
  return { payload, type, attachment };
@@ -6,9 +6,12 @@ export async function createShortConnection(aid, opts) {
6
6
  const slotId = opts?.slotId ?? '';
7
7
  const caCertPath = path.join(aunPath, 'CA', 'root', 'root.crt');
8
8
  const { AUNClient } = await import('@agentunion/fastaun');
9
+ const encryptionSeed = process.env.AUN_ENCRYPTION_SEED || undefined;
9
10
  const clientOpts = { aun_path: aunPath, debug: false };
10
11
  if (fs.existsSync(caCertPath))
11
12
  clientOpts.root_ca_path = caCertPath;
13
+ if (encryptionSeed)
14
+ clientOpts.encryption_seed = encryptionSeed;
12
15
  const client = new AUNClient(clientOpts);
13
16
  await client.auth.createAid({ aid });
14
17
  const authResult = await client.auth.authenticate({ aid });