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
package/dist/cli/agent.js CHANGED
@@ -8,6 +8,7 @@ import { ipcQuery } from '../ipc.js';
8
8
  import { CONFIG_SCHEMA_VERSION } from '../types.js';
9
9
  import { isValidChannelName } from '../core/channel-loader.js';
10
10
  import { commandExists } from '../utils/cross-platform.js';
11
+ import { isCodexSdkAvailable } from '../agents/codex-runner.js';
11
12
  // ==================== Helpers ====================
12
13
  const BASEAGENT_CANDIDATES = ['claude', 'codex', 'gemini'];
13
14
  const BASEAGENT_ENV_KEY = {
@@ -15,8 +16,13 @@ const BASEAGENT_ENV_KEY = {
15
16
  codex: 'OPENAI_API_KEY',
16
17
  gemini: 'GEMINI_API_KEY',
17
18
  };
19
+ function isBaseagentAvailable(baseagent) {
20
+ if (baseagent === 'codex')
21
+ return isCodexSdkAvailable();
22
+ return commandExists(baseagent);
23
+ }
18
24
  function detectAvailableBaseagents() {
19
- return BASEAGENT_CANDIDATES.filter(b => commandExists(b));
25
+ return BASEAGENT_CANDIDATES.filter(isBaseagentAvailable);
20
26
  }
21
27
  function pickDefaultBaseagent(available) {
22
28
  if (available.length === 0)
@@ -239,22 +245,30 @@ export async function agentShow(aid) {
239
245
  }
240
246
  export async function agentCreateInteractive(opts = {}) {
241
247
  const p = resolvePaths();
242
- const rl = readline.createInterface({
248
+ const ownRl = !opts.rl;
249
+ const rl = opts.rl ?? readline.createInterface({
243
250
  input: opts.stdin || process.stdin,
244
251
  output: opts.stdout || process.stdout,
245
252
  });
246
253
  const ask = (q) => new Promise(r => rl.question(q, r));
247
254
  try {
255
+ const { isValidAid, aidCreate } = await import('../aun/aid/index.js');
248
256
  const aidPrompt = opts.suggestedName
249
257
  ? `AID [${opts.suggestedName}]: `
250
258
  : 'AID (e.g. mybot.agentid.pub): ';
251
- const aidInput = (await ask(aidPrompt)).trim();
252
- const aid = aidInput || opts.suggestedName;
253
- if (!aid)
254
- return { ok: false, error: 'AID is required.' };
255
- const { isValidAid, aidCreate } = await import('../aun/aid/index.js');
256
- if (!isValidAid(aid)) {
257
- return { ok: false, error: `Invalid AID "${aid}": must be a valid multi-level domain (e.g. mybot.agentid.pub)` };
259
+ let aid = '';
260
+ while (!aid) {
261
+ const aidInput = (await ask(aidPrompt)).trim();
262
+ const candidate = aidInput || opts.suggestedName;
263
+ if (!candidate) {
264
+ console.log(' ⚠ AID is required.');
265
+ continue;
266
+ }
267
+ if (!isValidAid(candidate)) {
268
+ console.log(` ⚠ Invalid AID "${candidate}": must be a valid multi-level domain (e.g. mybot.agentid.pub)`);
269
+ continue;
270
+ }
271
+ aid = candidate;
258
272
  }
259
273
  const agentDirPath = path.join(p.agentsDir, aid);
260
274
  const configExists = fs.existsSync(path.join(agentDirPath, 'config.json'));
@@ -308,29 +322,51 @@ export async function agentCreateInteractive(opts = {}) {
308
322
  // Baseagent
309
323
  const available = detectAvailableBaseagents();
310
324
  if (available.length === 0) {
311
- return { ok: false, error: `No baseagent CLI detected on PATH. Install one of: ${BASEAGENT_CANDIDATES.join('/')}` };
325
+ return { ok: false, error: `No usable baseagent detected. Install claude/gemini CLI or optional dependency @openai/codex-sdk.` };
312
326
  }
313
327
  const defaultBa = pickDefaultBaseagent(available);
314
- let baseagent = null;
315
- while (baseagent === null) {
316
- const input = (await ask(`Baseagent (${available.join('/')}) [${defaultBa}]: `)).trim() || defaultBa;
317
- if (!BASEAGENT_CANDIDATES.includes(input)) {
318
- console.log(` Invalid choice. Options: ${BASEAGENT_CANDIDATES.join('/')}`);
319
- continue;
328
+ let baseagent;
329
+ if (available.length === 1) {
330
+ console.log(` Baseagent: ${defaultBa}`);
331
+ baseagent = defaultBa;
332
+ }
333
+ else {
334
+ let chosen = null;
335
+ while (chosen === null) {
336
+ const input = (await ask(`Baseagent (${available.join('/')}) [${defaultBa}]: `)).trim() || defaultBa;
337
+ if (!BASEAGENT_CANDIDATES.includes(input)) {
338
+ console.log(` Invalid choice. Options: ${BASEAGENT_CANDIDATES.join('/')}`);
339
+ continue;
340
+ }
341
+ if (!available.includes(input)) {
342
+ console.log(` ${input} is not available in the current environment. Available: ${available.join('/')}`);
343
+ continue;
344
+ }
345
+ chosen = input;
320
346
  }
321
- if (!available.includes(input)) {
322
- console.log(` ${input} not detected on PATH. Available: ${available.join('/')}`);
347
+ baseagent = chosen;
348
+ }
349
+ // Owner
350
+ let owner;
351
+ while (true) {
352
+ const ownerInput = (await ask('Owner AID (leave empty for auto-bind on first message): ')).trim();
353
+ if (!ownerInput) {
354
+ owner = undefined;
355
+ break;
356
+ }
357
+ if (!isValidAid(ownerInput)) {
358
+ console.log(` ⚠ Invalid Owner AID "${ownerInput}": must be a valid multi-level domain (e.g. alice.agentid.pub)`);
323
359
  continue;
324
360
  }
325
- baseagent = input;
361
+ owner = ownerInput;
362
+ break;
326
363
  }
327
- // Owner
328
- const owner = (await ask('Owner AID (leave empty for auto-bind on first message): ')).trim() || undefined;
329
364
  // Name + description for agent.md
330
365
  const defaultName = aid.split('.')[0];
331
366
  const agentName = (await ask(`Display name [${defaultName}]: `)).trim() || defaultName;
332
367
  const agentDescription = (await ask('Description (optional): ')).trim() || '';
333
- rl.close();
368
+ if (ownRl)
369
+ rl.close();
334
370
  const agentConfig = {
335
371
  $schema_version: CONFIG_SCHEMA_VERSION,
336
372
  aid,
@@ -359,31 +395,64 @@ export async function agentCreateInteractive(opts = {}) {
359
395
  const agentMdPath = path.join(aunPath, 'AIDs', aid, 'agent.md');
360
396
  fs.mkdirSync(path.dirname(agentMdPath), { recursive: true });
361
397
  fs.writeFileSync(agentMdPath, content, 'utf-8');
362
- try {
363
- await agentmdPut(content, { aid, aunPath });
364
- agentmdUploaded = true;
398
+ // Upload with retry (3 attempts, 2s delay between retries)
399
+ const MAX_ATTEMPTS = 3;
400
+ const RETRY_DELAY_MS = 2000;
401
+ let lastError;
402
+ for (let attempt = 1; attempt <= MAX_ATTEMPTS; attempt++) {
403
+ try {
404
+ if (attempt > 1) {
405
+ process.stdout.write(` ↻ agent.md 上传重试 (${attempt}/${MAX_ATTEMPTS})...\n`);
406
+ await new Promise(r => setTimeout(r, RETRY_DELAY_MS));
407
+ }
408
+ await agentmdPut(content, { aid, aunPath });
409
+ agentmdUploaded = true;
410
+ break;
411
+ }
412
+ catch (e) {
413
+ lastError = e;
414
+ }
365
415
  }
366
- catch (e) {
367
- console.warn(` ⚠ agent.md upload failed: ${e?.message || e}`);
416
+ if (!agentmdUploaded) {
417
+ console.warn(` ⚠ agent.md upload failed: ${lastError?.message || lastError}`);
368
418
  console.warn(` → Retry later with: evolclaw aid agentmd put ${aid}`);
369
419
  }
420
+ // Yield to allow the SDK WebSocket to fully close before IPC
421
+ await new Promise(r => setTimeout(r, 0));
370
422
  }
371
423
  catch (e) {
372
424
  console.warn(` ⚠ agent.md generation failed: ${e?.message || e}`);
373
425
  }
426
+ // Attempt hot-load via IPC (if daemon is running)
427
+ let hotLoaded = false;
428
+ let hotLoadError;
429
+ try {
430
+ const ipcResult = await ipcQuery(p.socket, { type: 'evolagent.load', aid });
431
+ if (ipcResult?.ok) {
432
+ hotLoaded = true;
433
+ }
434
+ else if (ipcResult) {
435
+ hotLoadError = ipcResult.error;
436
+ }
437
+ }
438
+ catch { /* daemon not running */ }
374
439
  return {
375
440
  ok: true,
376
441
  aid,
377
442
  configPath: toPosix(path.join(agentDirPath, 'config.json')),
378
443
  aidCreated,
379
444
  agentmdUploaded,
445
+ hotLoaded,
446
+ hotLoadError,
380
447
  };
381
448
  }
382
449
  finally {
383
- try {
384
- rl.close();
450
+ if (ownRl) {
451
+ try {
452
+ rl.close();
453
+ }
454
+ catch { /* ignore */ }
385
455
  }
386
- catch { /* ignore */ }
387
456
  }
388
457
  }
389
458
  export async function agentCreateNonInteractive(opts) {
@@ -400,7 +469,7 @@ export async function agentCreateNonInteractive(opts) {
400
469
  // Baseagent
401
470
  const available = detectAvailableBaseagents();
402
471
  if (available.length === 0) {
403
- return { ok: false, error: `No baseagent CLI detected on PATH. Install one of: ${BASEAGENT_CANDIDATES.join('/')}` };
472
+ return { ok: false, error: `No usable baseagent detected. Install claude/gemini CLI or optional dependency @openai/codex-sdk.` };
404
473
  }
405
474
  let baseagent;
406
475
  if (opts.baseagent) {
@@ -408,7 +477,7 @@ export async function agentCreateNonInteractive(opts) {
408
477
  return { ok: false, error: `Invalid baseagent: ${opts.baseagent} (options: ${BASEAGENT_CANDIDATES.join('/')})` };
409
478
  }
410
479
  if (!available.includes(opts.baseagent)) {
411
- return { ok: false, error: `${opts.baseagent} not detected on PATH (available: ${available.join('/')})` };
480
+ return { ok: false, error: `${opts.baseagent} is not available in the current environment (available: ${available.join('/')})` };
412
481
  }
413
482
  baseagent = opts.baseagent;
414
483
  }
@@ -482,27 +551,55 @@ export async function agentCreateNonInteractive(opts) {
482
551
  const agentMdPath = path.join(aunPath, 'AIDs', opts.aid, 'agent.md');
483
552
  fs.mkdirSync(path.dirname(agentMdPath), { recursive: true });
484
553
  fs.writeFileSync(agentMdPath, content, 'utf-8');
485
- try {
486
- await agentmdPut(content, { aid: opts.aid, aunPath });
487
- agentmdUploaded = true;
554
+ const MAX_ATTEMPTS = 3;
555
+ const RETRY_DELAY_MS = 2000;
556
+ let lastError;
557
+ for (let attempt = 1; attempt <= MAX_ATTEMPTS; attempt++) {
558
+ try {
559
+ if (attempt > 1)
560
+ await new Promise(r => setTimeout(r, RETRY_DELAY_MS));
561
+ await agentmdPut(content, { aid: opts.aid, aunPath });
562
+ agentmdUploaded = true;
563
+ break;
564
+ }
565
+ catch (e) {
566
+ lastError = e;
567
+ }
488
568
  }
489
- catch (e) {
490
- console.warn(`⚠ agent.md upload failed: ${e?.message || e}`);
569
+ if (!agentmdUploaded) {
570
+ console.warn(`⚠ agent.md upload failed: ${lastError?.message || lastError}`);
491
571
  console.warn(` Retry later with: evolclaw aid agentmd put ${opts.aid}`);
492
572
  }
573
+ await new Promise(r => setTimeout(r, 0));
493
574
  }
494
575
  catch (e) {
495
576
  console.warn(`⚠ agent.md generation failed: ${e?.message || e}`);
496
577
  }
578
+ // Attempt hot-load via IPC (if daemon is running)
579
+ let hotLoaded = false;
580
+ let hotLoadError;
581
+ try {
582
+ const ipcResult = await ipcQuery(p.socket, { type: 'evolagent.load', aid: opts.aid });
583
+ if (ipcResult?.ok) {
584
+ hotLoaded = true;
585
+ }
586
+ else if (ipcResult) {
587
+ hotLoadError = ipcResult.error;
588
+ }
589
+ }
590
+ catch { /* daemon not running */ }
497
591
  return {
498
592
  ok: true,
499
593
  aid: opts.aid,
500
594
  configPath: toPosix(path.join(agentDirPath, 'config.json')),
501
595
  aidCreated,
502
596
  agentmdUploaded,
597
+ hotLoaded,
598
+ hotLoadError,
503
599
  };
504
600
  }
505
- // ==================== agentSyncAids ====================
601
+ // ==================== agentSyncAids (deprecated) ====================
602
+ /** @deprecated sync-aids 已废弃,不再从 CLI 调用 */
506
603
  export async function agentSyncAids() {
507
604
  const p = resolvePaths();
508
605
  const { aidList } = await import('../aun/aid/index.js');
@@ -665,7 +762,12 @@ export async function agentSet(aid, key, rawValue) {
665
762
  }
666
763
  const value = parseJsonValue(rawValue);
667
764
  setNestedValue(config, key, value);
668
- saveAgent(config);
765
+ try {
766
+ saveAgent(config);
767
+ }
768
+ catch (e) {
769
+ return { ok: false, error: e?.message || String(e) };
770
+ }
669
771
  // Try hot-reload
670
772
  let reloaded = false;
671
773
  try {
@@ -733,7 +835,7 @@ export async function agentChannelUpsert(opts) {
733
835
  return {
734
836
  ok: true,
735
837
  aid: opts.aid,
736
- channelKey: `${opts.aid}#${opts.channel.type}#${opts.channel.name}`,
838
+ channelKey: `${opts.channel.type}#${encodeURIComponent(opts.aid)}#${opts.channel.name}`,
737
839
  reloaded,
738
840
  };
739
841
  }
package/dist/cli/index.js CHANGED
@@ -4,7 +4,7 @@ import path from 'path';
4
4
  import os from 'os';
5
5
  import { spawn, execFileSync, execFile } from 'child_process';
6
6
  import { promisify } from 'util';
7
- import { resolveRoot, resolvePaths, ensureDataDirs, getPackageRoot } from '../paths.js';
7
+ import { resolveRoot, resolvePaths, ensureDataDirs, getPackageRoot, agentMdPath } from '../paths.js';
8
8
  import { loadDefaults, loadAllAgents, mergeForAgent } from '../config-store.js';
9
9
  import { resolveAnthropicConfig } from '../agents/resolve.js';
10
10
  import { migrateProject } from '../config-store.js';
@@ -235,7 +235,7 @@ function formatLocalTime(ms) {
235
235
  const d = new Date(ms);
236
236
  return `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, '0')}-${String(d.getDate()).padStart(2, '0')} ${String(d.getHours()).padStart(2, '0')}:${String(d.getMinutes()).padStart(2, '0')}:${String(d.getSeconds()).padStart(2, '0')}`;
237
237
  }
238
- function printStartupInfo() {
238
+ function printStartupInfo(opts = {}) {
239
239
  const pkgRoot = getPackageRoot();
240
240
  const isNpmInstall = pkgRoot.includes('node_modules');
241
241
  const cliRunsSource = !import.meta.url.includes('/dist/');
@@ -268,7 +268,15 @@ function printStartupInfo() {
268
268
  version = JSON.parse(fs.readFileSync(path.join(pkgRoot, 'package.json'), 'utf-8')).version;
269
269
  }
270
270
  catch { }
271
- console.log(` EvolClaw v${version}`);
271
+ let aunVer = null;
272
+ try {
273
+ aunVer = JSON.parse(fs.readFileSync(path.join(pkgRoot, 'node_modules', '@agentunion', 'fastaun', 'package.json'), 'utf-8')).version;
274
+ }
275
+ catch { }
276
+ const pidPart = opts.pid ? ` (PID: ${opts.pid})` : '';
277
+ const aunPart = aunVer ? ` fastaun v${aunVer}` : '';
278
+ const prefix = opts.running ? '✓ EvolClaw is running , v' : ' EvolClaw v';
279
+ console.log(`${prefix}${version}${pidPart}${aunPart}`);
272
280
  console.log(` 包路径: ${pkgRoot}`);
273
281
  console.log(` 安装类型: ${isNpmInstall ? 'npm全局安装' : '开发仓(link)'}`);
274
282
  console.log(` CLI执行: ${cliRunsSource ? '源码(tsx)' : '编译产物(dist)'}`);
@@ -829,7 +837,7 @@ async function cmdStatus() {
829
837
  console.log('');
830
838
  }
831
839
  if (pid) {
832
- console.log(`✓ EvolClaw is running (PID: ${pid})`);
840
+ printStartupInfo({ pid, running: true });
833
841
  console.log('');
834
842
  console.log('📊 Process Info:');
835
843
  try {
@@ -875,8 +883,8 @@ async function cmdStatus() {
875
883
  const configChannelNames = new Set();
876
884
  for (const cfg of agents) {
877
885
  for (const inst of cfg.channels) {
878
- // effective key: <aid>#<type>#<name>
879
- configChannelNames.add(`${cfg.aid}#${inst.type}#${inst.name}`);
886
+ // effective key: <type>#<urlEncode(selfPeerId)>#<name>
887
+ configChannelNames.add(`${inst.type}#${encodeURIComponent(cfg.aid)}#${inst.name}`);
880
888
  }
881
889
  }
882
890
  for (const s of allSessions) {
@@ -1005,7 +1013,7 @@ async function cmdStatus() {
1005
1013
  }
1006
1014
  }
1007
1015
  /**
1008
- * 把 channel fingerprint 列表(`<aid>#<type>#<name>`)折叠成展示用摘要。
1016
+ * 把 channel fingerprint 列表(`<type>#<selfPeerId>#<name>`)折叠成展示用摘要。
1009
1017
  *
1010
1018
  * 聚合规则:
1011
1019
  * - 按 type 分组
@@ -1027,8 +1035,8 @@ function summarizeChannelFingerprints(fingerprints) {
1027
1035
  }
1028
1036
  continue;
1029
1037
  }
1030
- const type = parts[1];
1031
- const name = parts.slice(2).join('#');
1038
+ const type = parts[0];
1039
+ const name = parts[2];
1032
1040
  if (!groups.has(type)) {
1033
1041
  groups.set(type, []);
1034
1042
  order.push(type);
@@ -1734,10 +1742,10 @@ async function cmdWatchAid() {
1734
1742
  const refreshedAids = new Set();
1735
1743
  function readLocalName(aid) {
1736
1744
  try {
1737
- const agentMdPath = path.join(os.homedir(), '.aun', 'AIDs', aid, 'agent.md');
1738
- if (!fs.existsSync(agentMdPath))
1745
+ const mdPath = agentMdPath(aid);
1746
+ if (!fs.existsSync(mdPath))
1739
1747
  return undefined;
1740
- const content = fs.readFileSync(agentMdPath, 'utf-8');
1748
+ const content = fs.readFileSync(mdPath, 'utf-8');
1741
1749
  const fmMatch = content.match(/^---\n([\s\S]*?)\n---/);
1742
1750
  if (!fmMatch)
1743
1751
  return undefined;
@@ -2463,12 +2471,14 @@ function archiveSelfHealLog(p, log) {
2463
2471
  * Searches across all channel types (feishu, wechat, aun) for a matching instance.
2464
2472
  */
2465
2473
  function resolveInstanceConfig(instanceName) {
2466
- // 新结构:channel key 是 <aid>#<type>#<name>,解析后从对应 agent 的 channels[] 找
2474
+ // 新结构:channel key 是 <type>#<selfPeerId>#<name>,解析后从对应 agent 的 channels[] 找
2467
2475
  const parts = instanceName.split('#');
2468
2476
  if (parts.length === 3) {
2469
- const [aid, type, name] = parts;
2477
+ const [type, encodedSelfPeerId, name] = parts;
2478
+ const selfPeerId = decodeURIComponent(encodedSelfPeerId);
2470
2479
  const { agents } = loadAllAgents();
2471
- const agent = agents.find(a => a.aid === aid);
2480
+ // AUN channel selfPeerId 就是 agent.aid
2481
+ const agent = agents.find(a => a.aid === selfPeerId);
2472
2482
  if (!agent)
2473
2483
  return null;
2474
2484
  const inst = agent.channels.find((c) => c.type === type && c.name === name);
@@ -2729,6 +2739,7 @@ async function cmdCtl(args) {
2729
2739
  查询:
2730
2740
  status 查看会话状态
2731
2741
  check 检查渠道健康状态
2742
+ pwd 显示当前项目路径
2732
2743
  help 显示帮助
2733
2744
 
2734
2745
  配置:
@@ -2737,15 +2748,14 @@ async function cmdCtl(args) {
2737
2748
  compact 压缩当前会话上下文
2738
2749
  perm [mode] 查看/切换权限模式
2739
2750
 
2740
- 项目:
2741
- bind <path> 注册项目目录(不切换当前会话)
2742
-
2743
2751
  消息:
2744
2752
  send <消息内容> 主动发送文本消息(proactive 模式)
2745
2753
  file [channel] <path> 发送项目内文件
2746
2754
 
2755
+ Agent:
2756
+ agent <subcommand> EvolAgent 管理(list/show/new/enable/disable/reload/delete)
2757
+
2747
2758
  运维:
2748
- agentmd [put|set <内容>] 查看/管理 agent.md(仅 AUN 通道)
2749
2759
  restart [channel] 重启服务或重连指定渠道
2750
2760
 
2751
2761
  示例:
@@ -2798,7 +2808,6 @@ Commands:
2798
2808
  show <aid> 查看 agent 详情(身份 + 配置 + 连接 + 会话 + 路径)
2799
2809
  new [aid] 交互式创建 agent
2800
2810
  new <aid> --non-interactive ... 非交互式创建
2801
- sync-aids 从本地 AID 批量创建 agent
2802
2811
  enable <aid> 启用 agent
2803
2812
  disable <aid> 停用 agent
2804
2813
  get <aid> <key> 读取单个配置字段(支持点路径)
@@ -2821,7 +2830,7 @@ Options:
2821
2830
  evolclaw agent delete mybot.agentid.pub --purge`);
2822
2831
  return;
2823
2832
  }
2824
- const { agentList, agentShow, agentCreateInteractive, agentCreateNonInteractive, agentSyncAids, agentReload, agentEnable, agentDisable, agentGet, agentSet, agentDelete, agentRename, } = await import('./agent.js');
2833
+ const { agentList, agentShow, agentCreateInteractive, agentCreateNonInteractive, agentReload, agentEnable, agentDisable, agentGet, agentSet, agentDelete, agentRename, } = await import('./agent.js');
2825
2834
  // --- list ---
2826
2835
  if (!sub || sub === 'list') {
2827
2836
  const result = await agentList();
@@ -2921,7 +2930,11 @@ Options:
2921
2930
  console.log(result.agentmdUploaded
2922
2931
  ? ' ✓ agent.md 已发布'
2923
2932
  : ' ⚠ agent.md 上传失败(可用 evolclaw aid agentmd put 重试)');
2924
- console.log(' Run `evolclaw restart` to activate.');
2933
+ console.log(result.hotLoaded
2934
+ ? ' ✓ 已热重载,agent 已上线'
2935
+ : result.hotLoadError
2936
+ ? ` ✗ 热重载失败:${result.hotLoadError}`
2937
+ : ' ⚠ 服务未运行,下次 evolclaw start 时生效');
2925
2938
  }
2926
2939
  }
2927
2940
  else {
@@ -2943,41 +2956,37 @@ Options:
2943
2956
  console.log(result.agentmdUploaded
2944
2957
  ? ' ✓ agent.md 已发布'
2945
2958
  : ' ⚠ agent.md 上传失败(可用 evolclaw aid agentmd put 重试)');
2946
- console.log(' Run `evolclaw restart` to activate.');
2959
+ console.log(result.hotLoaded
2960
+ ? ' ✓ 已热重载,agent 已上线'
2961
+ : result.hotLoadError
2962
+ ? ` ✗ 热重载失败:${result.hotLoadError}`
2963
+ : ' ⚠ 服务未运行,下次 evolclaw start 时生效');
2947
2964
  }
2948
2965
  }
2949
2966
  return;
2950
2967
  }
2951
- // --- sync-aids ---
2952
- if (sub === 'sync-aids') {
2953
- const result = await agentSyncAids();
2954
- if (!result.ok) {
2955
- if (formatJson) {
2956
- console.log(JSON.stringify(result));
2957
- }
2958
- else {
2959
- console.error(`❌ ${result.error}`);
2960
- }
2961
- process.exit(1);
2962
- }
2963
- if (formatJson) {
2964
- console.log(JSON.stringify(result, null, 2));
2965
- return;
2966
- }
2967
- if (result.created.length === 0) {
2968
- console.log('所有本地 AID 都已有对应 agent,无需同步。');
2969
- }
2970
- else {
2971
- console.log(`✓ 同步完成:新建 ${result.created.length} 个 agent(模板: ${result.template})`);
2972
- for (const aid of result.created)
2973
- console.log(` ✓ ${aid}`);
2974
- if (result.hotReloaded)
2975
- console.log(' ✓ 已热加载到运行中的进程');
2976
- else
2977
- console.log(' evolclaw 未运行,新 agent 将在下次启动时加载。');
2978
- }
2979
- return;
2980
- }
2968
+ // --- sync-aids (deprecated, commented out) ---
2969
+ // if (sub === 'sync-aids') {
2970
+ // const result = await agentSyncAids();
2971
+ // if (!result.ok) {
2972
+ // if (formatJson) { console.log(JSON.stringify(result)); }
2973
+ // else { console.error(`❌ ${result.error}`); }
2974
+ // process.exit(1);
2975
+ // }
2976
+ // if (formatJson) {
2977
+ // console.log(JSON.stringify(result, null, 2));
2978
+ // return;
2979
+ // }
2980
+ // if (result.created.length === 0) {
2981
+ // console.log('所有本地 AID 都已有对应 agent,无需同步。');
2982
+ // } else {
2983
+ // console.log(`✓ 同步完成:新建 ${result.created.length} 个 agent(模板: ${result.template})`);
2984
+ // for (const aid of result.created) console.log(` ✓ ${aid}`);
2985
+ // if (result.hotReloaded) console.log(' 已热加载到运行中的进程');
2986
+ // else console.log(' evolclaw 未运行,新 agent 将在下次启动时加载。');
2987
+ // }
2988
+ // return;
2989
+ // }
2981
2990
  // --- reload ---
2982
2991
  if (sub === 'reload') {
2983
2992
  const target = args[1] && !args[1].startsWith('--') ? args[1] : undefined;
@@ -3438,8 +3447,7 @@ Options:
3438
3447
  console.error(`❌ 无效 AID 格式: ${aid}`);
3439
3448
  process.exit(1);
3440
3449
  }
3441
- const aunBase = aunPath ?? path.join(os.homedir(), '.aun');
3442
- const localPath = path.join(aunBase, 'AIDs', aid, 'agent.md');
3450
+ const localPath = agentMdPath(aid);
3443
3451
  if (!fs.existsSync(localPath)) {
3444
3452
  console.error(`❌ 本地无 agent.md: ${aid}`);
3445
3453
  process.exit(1);
@@ -3747,12 +3755,15 @@ Options:
3747
3755
  --app <name> 指定应用 slot(隔离 ack 游标)
3748
3756
  --as-daemon ack 时显式以 daemon 身份(高危,会污染 daemon 游标)
3749
3757
  --format json 输出 JSON 格式
3758
+ --encrypt 启用端到端加密
3759
+ --thread <id> 指定话题 ID(用于多话题路由)
3750
3760
  --content-type <mime> 显式覆盖 MIME(仅 --file 模式)
3751
3761
  --text <说明> 附件说明文字(仅 --file 模式)
3752
3762
  --transcript <text> 语音转写(仅 --as voice)
3753
3763
 
3754
3764
  示例:
3755
3765
  evolclaw msg send alice.agentid.pub bob.agentid.pub "hello"
3766
+ evolclaw msg send alice.agentid.pub bob.agentid.pub "讨论项目A" --thread "project-A"
3756
3767
  evolclaw msg send alice.agentid.pub bob.agentid.pub --file ./pic.png
3757
3768
  evolclaw msg send alice.agentid.pub bob.agentid.pub --file ./demo.mp4 --as video
3758
3769
  evolclaw msg send alice.agentid.pub bob.agentid.pub --link https://example.com --title "AUN"
@@ -3826,7 +3837,29 @@ Options:
3826
3837
  body = { mode: 'text', text };
3827
3838
  }
3828
3839
  const encrypt = args.includes('--encrypt');
3829
- const result = await msgSend({ from, to, body, encrypt, ...commonOpts });
3840
+ const thread = getArgValue(args, '--thread');
3841
+ // 文件上传进度展示(非 JSON 输出时)。仅在大文件降级到 HTTP PUT 阶段会逐块更新。
3842
+ let lastPctShown = -1;
3843
+ const onUploadProgress = formatJson ? undefined : (info) => {
3844
+ if (info.phase === 'inline')
3845
+ return; // 内联阶段不分块,跳过
3846
+ if (info.phase === 'http-put') {
3847
+ const pct = info.total > 0 ? Math.floor((info.bytes / info.total) * 100) : 0;
3848
+ if (pct === lastPctShown && info.bytes < info.total)
3849
+ return;
3850
+ lastPctShown = pct;
3851
+ const mb = (n) => (n / 1024 / 1024).toFixed(2);
3852
+ const eol = info.bytes >= info.total ? '\n' : '\r';
3853
+ process.stderr.write(` ⏫ uploading: ${pct}% (${mb(info.bytes)}/${mb(info.total)} MB)${eol}`);
3854
+ }
3855
+ else if (info.phase === 'session-create') {
3856
+ process.stderr.write(' ⏫ requesting upload session...\n');
3857
+ }
3858
+ else if (info.phase === 'session-complete') {
3859
+ process.stderr.write(' ⏫ finalizing upload...\n');
3860
+ }
3861
+ };
3862
+ const result = await msgSend({ from, to, body, encrypt, thread, onUploadProgress, ...commonOpts });
3830
3863
  if (!result.ok) {
3831
3864
  if (formatJson) {
3832
3865
  console.log(JSON.stringify(result));
@@ -4408,15 +4441,23 @@ export async function main(args) {
4408
4441
  evolclaw init wecom 企业微信 AI Bot 配置(手动输入)`);
4409
4442
  }
4410
4443
  else if (args[1] === 'wechat') {
4444
+ const { suppressSdkLogs } = await import('../aun/aid/index.js');
4445
+ suppressSdkLogs();
4411
4446
  await cmdInitWechat();
4412
4447
  }
4413
4448
  else if (args[1] === 'feishu') {
4449
+ const { suppressSdkLogs } = await import('../aun/aid/index.js');
4450
+ suppressSdkLogs();
4414
4451
  await cmdInitFeishu();
4415
4452
  }
4416
4453
  else if (args[1] === 'dingtalk') {
4454
+ const { suppressSdkLogs } = await import('../aun/aid/index.js');
4455
+ suppressSdkLogs();
4417
4456
  await cmdInitDingtalk();
4418
4457
  }
4419
4458
  else if (args[1] === 'qqbot') {
4459
+ const { suppressSdkLogs } = await import('../aun/aid/index.js');
4460
+ suppressSdkLogs();
4420
4461
  await cmdInitQQBot();
4421
4462
  }
4422
4463
  else if (args[1] === 'wecom') {
@@ -4429,6 +4470,8 @@ export async function main(args) {
4429
4470
  process.exit(1);
4430
4471
  }
4431
4472
  else {
4473
+ const { suppressSdkLogs } = await import('../aun/aid/index.js');
4474
+ suppressSdkLogs();
4432
4475
  const nonInteractive = args.includes('--non-interactive');
4433
4476
  await cmdInit({
4434
4477
  nonInteractive,
@@ -4509,9 +4552,12 @@ export async function main(args) {
4509
4552
  case 'ctl':
4510
4553
  await cmdCtl(args.slice(1));
4511
4554
  break;
4512
- case 'agent':
4555
+ case 'agent': {
4556
+ const { suppressSdkLogs } = await import('../aun/aid/index.js');
4557
+ suppressSdkLogs();
4513
4558
  await cmdAgent(args.slice(1));
4514
4559
  break;
4560
+ }
4515
4561
  case 'aid': {
4516
4562
  const { suppressSdkLogs } = await import('../aun/aid/index.js');
4517
4563
  suppressSdkLogs();
@@ -4590,7 +4636,6 @@ Commands:
4590
4636
  --name <display-name>
4591
4637
  --description <text>
4592
4638
  --force (覆盖已有 config.json)
4593
- agent sync-aids 从本地 AID 批量同步创建 agent(以最早 agent 为模板)
4594
4639
  agent reload 全量 resync(扫磁盘,新增上线、删除下线、修改热更新)
4595
4640
  agent reload <n> 热重载指定 agent 配置
4596
4641
  aid AID 身份管理
@@ -10,6 +10,7 @@ import readline from 'readline';
10
10
  import path from 'path';
11
11
  import os from 'os';
12
12
  import crypto from 'crypto';
13
+ import { aidLocalDir } from '../paths.js';
13
14
  import { selectInstance } from './init.js';
14
15
  import { npmInstallGlobal } from '../utils/npm-ops.js';
15
16
  import { loadAllAgents, loadAgent } from '../config-store.js';
@@ -470,8 +471,9 @@ export async function setupAunAid(rl, _config) {
470
471
  console.log(` ⚠ agent.md 发布失败(首次连接将自动重试): ${String(e.message || e).slice(0, 100)}`);
471
472
  // Still write local copy as fallback
472
473
  try {
473
- fs.mkdirSync(aidDir, { recursive: true });
474
- fs.writeFileSync(path.join(aidDir, 'agent.md'), content, 'utf-8');
474
+ const localDir = aidLocalDir(aid);
475
+ fs.mkdirSync(localDir, { recursive: true });
476
+ fs.writeFileSync(path.join(localDir, 'agent.md'), content, 'utf-8');
475
477
  console.log(' ✓ agent.md 已写入本地');
476
478
  }
477
479
  catch (we) {