evolclaw 3.0.0 → 3.1.1

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 (104) hide show
  1. package/README.md +1 -1
  2. package/bin/ec.js +29 -0
  3. package/dist/agents/baseagent-normalize.js +19 -0
  4. package/dist/agents/claude-runner.js +47 -12
  5. package/dist/agents/codex-runner.js +2 -0
  6. package/dist/agents/gemini-runner.js +9 -9
  7. package/dist/agents/kit-renderer.js +281 -0
  8. package/dist/aun/aid/identity.js +28 -0
  9. package/dist/aun/aid/index.js +1 -1
  10. package/dist/aun/aid/lifecycle-log.js +33 -0
  11. package/dist/aun/msg/group.js +3 -1
  12. package/dist/aun/msg/p2p.js +42 -1
  13. package/dist/channels/aun.js +427 -146
  14. package/dist/channels/dingtalk.js +3 -1
  15. package/dist/channels/feishu.js +128 -7
  16. package/dist/channels/qqbot.js +3 -1
  17. package/dist/channels/wechat.js +4 -1
  18. package/dist/channels/wecom.js +3 -1
  19. package/dist/cli/bench.js +1219 -0
  20. package/dist/cli/index.js +418 -40
  21. package/dist/cli/init.js +3 -4
  22. package/dist/cli/link-rules.js +245 -0
  23. package/dist/cli/net-check.js +640 -0
  24. package/dist/cli/watch-msg.js +666 -0
  25. package/dist/config-store.js +82 -5
  26. package/dist/core/channel-loader.js +23 -10
  27. package/dist/core/command-handler.js +127 -99
  28. package/dist/core/evolagent.js +5 -10
  29. package/dist/core/message/im-renderer.js +93 -48
  30. package/dist/core/message/items-formatter.js +11 -4
  31. package/dist/core/message/message-bridge.js +11 -2
  32. package/dist/core/message/message-log.js +8 -1
  33. package/dist/core/message/message-processor.js +194 -127
  34. package/dist/core/message/message-queue.js +10 -3
  35. package/dist/core/permission.js +95 -3
  36. package/dist/core/relation/peer-identity.js +161 -0
  37. package/dist/core/session/session-manager.js +103 -65
  38. package/dist/core/trigger/manager.js +16 -0
  39. package/dist/core/trigger/parser.js +110 -0
  40. package/dist/core/trigger/scheduler.js +7 -1
  41. package/dist/data/error-dict.json +118 -0
  42. package/dist/eck/baseagent-caps.js +18 -0
  43. package/dist/eck/detect.js +47 -0
  44. package/dist/eck/init.js +77 -0
  45. package/dist/eck/rules-loader.js +28 -0
  46. package/dist/index.js +186 -19
  47. package/dist/net-check.js +640 -0
  48. package/dist/paths.js +31 -40
  49. package/dist/utils/aid-lifecycle-log.js +33 -0
  50. package/dist/utils/atomic-write.js +10 -0
  51. package/dist/utils/cross-platform.js +17 -8
  52. package/dist/utils/error-utils.js +27 -15
  53. package/dist/utils/instance-registry.js +6 -5
  54. package/dist/utils/log-writer.js +2 -1
  55. package/dist/utils/logger.js +10 -0
  56. package/dist/utils/npm-ops.js +35 -3
  57. package/dist/utils/process-introspect.js +16 -38
  58. package/dist/utils/stats.js +216 -2
  59. package/dist/watch-msg.js +26 -11
  60. package/evolclaw-install-aun.md +14 -2
  61. package/kits/docs/GUIDE.md +20 -0
  62. package/kits/docs/INDEX.md +52 -0
  63. package/kits/docs/aun/CHEATSHEET.md +17 -0
  64. package/kits/docs/aun/SYNC_PROTOCOL.md +15 -0
  65. package/kits/docs/channels/feishu.md +27 -0
  66. package/kits/docs/eck_templates/GUIDE.template.md +22 -0
  67. package/kits/docs/eck_templates/INDEX.template.md +28 -0
  68. package/kits/docs/eck_templates/path-registry.template.md +33 -0
  69. package/kits/docs/eck_templates/runtime.template.md +19 -0
  70. package/kits/docs/evolclaw/MSG_GROUP.md +30 -0
  71. package/kits/docs/evolclaw/MSG_PRIVATE.md +72 -0
  72. package/kits/docs/identity/AID_PROFILE_SPEC.md +27 -0
  73. package/kits/docs/identity/PATH_OPS.md +16 -0
  74. package/kits/docs/identity/ROLE_DETAIL.md +20 -0
  75. package/kits/docs/path-registry.md +43 -0
  76. package/kits/eck_manifest.json +95 -0
  77. package/kits/rules/01-overview.md +120 -0
  78. package/kits/rules/02-navigation.md +75 -0
  79. package/kits/rules/03-identity.md +34 -0
  80. package/kits/rules/04-relation.md +49 -0
  81. package/kits/rules/05-venue.md +45 -0
  82. package/kits/rules/06-channel.md +73 -0
  83. package/kits/templates/system-fragments/baseagent.md +2 -0
  84. package/kits/templates/system-fragments/channel.md +10 -0
  85. package/kits/templates/system-fragments/identity.md +12 -0
  86. package/kits/templates/system-fragments/relation.md +9 -0
  87. package/kits/templates/system-fragments/runtime.md +19 -0
  88. package/kits/templates/system-fragments/venue.md +5 -0
  89. package/package.json +7 -5
  90. package/dist/agents/templates.js +0 -122
  91. package/dist/data/prompts.md +0 -137
  92. package/kits/aun/meta.md +0 -25
  93. package/kits/aun/role.md +0 -25
  94. package/kits/templates/group.md +0 -20
  95. package/kits/templates/private.md +0 -9
  96. package/kits/templates/system-fragments/personal-context.md +0 -3
  97. package/kits/templates/system-fragments/self-intro.md +0 -5
  98. package/kits/templates/system-fragments/speaker-intro.md +0 -5
  99. package/kits/templates/system-fragments/venue-intro.md +0 -5
  100. /package/kits/{channels → docs/channels}/aun.md +0 -0
  101. /package/kits/{evolclaw/commands.md → docs/evolclaw/AGENT_CMD.md} +0 -0
  102. /package/kits/{evolclaw → docs/evolclaw}/self-summary.md +0 -0
  103. /package/kits/{evolclaw → docs/evolclaw}/tools.md +0 -0
  104. /package/kits/{evolclaw → docs/identity}/identity-tools.md +0 -0
package/dist/index.js CHANGED
@@ -1,9 +1,9 @@
1
1
  import { ClaudeSessionFileAdapter } from './core/session/adapters/claude-session-file-adapter.js';
2
2
  import { CodexSessionFileAdapter } from './core/session/adapters/codex-session-file-adapter.js';
3
3
  import { GeminiSessionFileAdapter } from './core/session/adapters/gemini-session-file-adapter.js';
4
- import { ensureDataDirs, resolvePaths, syncKitsFromPackage } from './paths.js';
4
+ import { ensureDataDirs, resolvePaths, getPackageRoot } from './paths.js';
5
5
  import { resolveAnthropicConfig } from './agents/resolve.js';
6
- import { loadDefaults, autoMigrateIfNeeded } from './config-store.js';
6
+ import { loadDefaults, autoMigrateIfNeeded, migrateIdentitiesIfNeeded } from './config-store.js';
7
7
  import { CONFIG_SCHEMA_VERSION } from './types.js';
8
8
  import { SessionManager } from './core/session/session-manager.js';
9
9
  import { ClaudeAgentPlugin } from './agents/claude-runner.js';
@@ -33,14 +33,53 @@ import { IpcServer } from './ipc.js';
33
33
  import { logger, setLogLevel } from './utils/logger.js';
34
34
  import { writeMain, removeAll, isMainWinner, scanInstances } from './utils/instance-registry.js';
35
35
  import { detectDuplicates } from './core/evolagent-registry.js';
36
- import { loadPromptTemplates } from './agents/templates.js';
36
+ import { loadKitManifest, cleanEckDebug, invalidateKitCache } from './agents/kit-renderer.js';
37
+ import { initEck } from './eck/init.js';
37
38
  import { TriggerManager } from './core/trigger/manager.js';
38
- import { TriggerScheduler } from './core/trigger/scheduler.js';
39
+ import { TriggerScheduler, calcNextFireAt } from './core/trigger/scheduler.js';
39
40
  import { agentTriggersDir } from './paths.js';
41
+ import { isLinkedInstall } from './utils/npm-ops.js';
40
42
  import path from 'path';
41
43
  import fs from 'fs';
42
44
  import os from 'os';
43
45
  import crypto from 'crypto';
46
+ import { fileURLToPath } from 'url';
47
+ /** 出站 payload 摘要(用于 channel-out.log) */
48
+ function summarizeOutboundPayload(payload) {
49
+ if (!payload)
50
+ return { kind: 'unknown' };
51
+ const s = { kind: payload.kind };
52
+ switch (payload.kind) {
53
+ case 'activity.batch':
54
+ s.itemCount = payload.items?.length ?? 0;
55
+ s.items = payload.items;
56
+ break;
57
+ case 'result.text':
58
+ s.isFinal = payload.isFinal;
59
+ s.text = payload.text;
60
+ break;
61
+ case 'result.file':
62
+ s.filePath = payload.filePath;
63
+ break;
64
+ case 'system.notice':
65
+ case 'system.error':
66
+ s.subtype = payload.subtype;
67
+ s.text = payload.text;
68
+ break;
69
+ case 'interaction':
70
+ s.interactionId = payload.interaction?.id;
71
+ s.interactionKind = payload.interaction?.kind?.kind;
72
+ break;
73
+ case 'status.started':
74
+ case 'status.completed':
75
+ case 'status.interrupted':
76
+ case 'status.error':
77
+ case 'status.timeout':
78
+ s.metadata = payload.metadata;
79
+ break;
80
+ }
81
+ return s;
82
+ }
44
83
  /**
45
84
  * 通过 adapter.send 发送系统类 payload(system.notice / system.error / 等)。
46
85
  *
@@ -54,7 +93,77 @@ import crypto from 'crypto';
54
93
  export async function sendSystemPayload(adapter, envelope, payload) {
55
94
  await adapter.send(envelope, payload);
56
95
  }
96
+ function readEvolclawVersion() {
97
+ try {
98
+ const pkg = JSON.parse(fs.readFileSync(path.join(getPackageRoot(), 'package.json'), 'utf-8'));
99
+ return pkg.version || 'unknown';
100
+ }
101
+ catch {
102
+ return 'unknown';
103
+ }
104
+ }
105
+ function readFastaunVersion() {
106
+ try {
107
+ const url = import.meta.resolve?.('@agentunion/fastaun');
108
+ if (!url)
109
+ return 'unknown';
110
+ let dir = path.dirname(fileURLToPath(url));
111
+ while (dir !== path.dirname(dir)) {
112
+ const pkgPath = path.join(dir, 'package.json');
113
+ if (fs.existsSync(pkgPath)) {
114
+ const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf-8'));
115
+ if (pkg.name === '@agentunion/fastaun')
116
+ return pkg.version || 'unknown';
117
+ }
118
+ dir = path.dirname(dir);
119
+ }
120
+ return 'unknown';
121
+ }
122
+ catch {
123
+ return 'unknown';
124
+ }
125
+ }
57
126
  async function main() {
127
+ // 启动信息:目录类型 + 版本号 + 代码最新时间戳
128
+ {
129
+ const pkgRoot = getPackageRoot();
130
+ const runDir = path.dirname(new URL(import.meta.url).pathname.replace(/^\/([A-Z]:)/, '$1'));
131
+ const isDist = runDir.includes(path.join(pkgRoot, 'dist'));
132
+ const isLinked = fs.existsSync(path.join(pkgRoot, '.git'));
133
+ const dirType = isDist ? (isLinked ? '开发仓/dist' : '安装路径/dist') : '源码(tsx)';
134
+ const scanDir = isDist ? path.join(pkgRoot, 'dist') : path.join(pkgRoot, 'src');
135
+ let latestMtime = 0;
136
+ const scanRecursive = (dir) => {
137
+ try {
138
+ for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
139
+ if (entry.name === 'node_modules')
140
+ continue;
141
+ const full = path.join(dir, entry.name);
142
+ if (entry.isDirectory()) {
143
+ scanRecursive(full);
144
+ continue;
145
+ }
146
+ if (entry.name.endsWith('.js') || entry.name.endsWith('.ts')) {
147
+ const mt = fs.statSync(full).mtimeMs;
148
+ if (mt > latestMtime)
149
+ latestMtime = mt;
150
+ }
151
+ }
152
+ }
153
+ catch { }
154
+ };
155
+ scanRecursive(scanDir);
156
+ let version = '?';
157
+ try {
158
+ version = JSON.parse(fs.readFileSync(path.join(pkgRoot, 'package.json'), 'utf-8')).version;
159
+ }
160
+ catch { }
161
+ const fmtTime = (ms) => { const d = new Date(ms); 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')}`; };
162
+ console.error(`[EvolClaw] EvolClaw v${version}`);
163
+ console.error(`[EvolClaw] 执行类型: ${dirType}`);
164
+ console.error(`[EvolClaw] 包路径: ${pkgRoot}`);
165
+ console.error(`[EvolClaw] 代码时间: ${latestMtime ? fmtTime(latestMtime) : '?'}`);
166
+ }
58
167
  // 过滤飞书 SDK 的 info 日志
59
168
  const originalLog = console.log;
60
169
  const originalInfo = console.info;
@@ -72,11 +181,9 @@ async function main() {
72
181
  return;
73
182
  originalInfo(...args);
74
183
  };
75
- logger.info('EvolClaw starting...');
184
+ logger.info(`EvolClaw v${readEvolclawVersion()} starting... (fastaun v${readFastaunVersion()})`);
76
185
  // 确保数据目录存在
77
186
  ensureDataDirs();
78
- // 同步包内 kits/ 到 EVOLCLAW_HOME/kits/(首次启动或升级时)
79
- syncKitsFromPackage();
80
187
  // ── 单实例保护(pre-check + post-write self-check)──
81
188
  // pre-check:发现已有活 main 直接退出,避免起任何副作用
82
189
  {
@@ -104,16 +211,20 @@ async function main() {
104
211
  process.exit(0);
105
212
  }
106
213
  }
107
- // 加载提示词模板
108
- loadPromptTemplates();
214
+ // ── 自动迁移 ──
215
+ migrateIdentitiesIfNeeded();
216
+ autoMigrateIfNeeded();
217
+ // ── ECK 运行时初始化 ──
218
+ initEck();
219
+ // 加载 ECK manifest + 清理旧调试文件
220
+ cleanEckDebug();
221
+ loadKitManifest();
109
222
  // 加载配置(新结构:defaults.json + per-agent config.json)
110
223
  const defaults = loadDefaults() ?? { $schema_version: CONFIG_SCHEMA_VERSION };
111
224
  // 应用配置中的日志级别(优先于环境变量)
112
225
  // logLevel 现在不在新结构中——若要保留,将来可加 defaults.debug.logLevel
113
226
  // 阶段 2c 暂跳过
114
227
  const paths = resolvePaths();
115
- // ── 自动迁移:旧 data/evolclaw.json → 新结构 ──
116
- autoMigrateIfNeeded();
117
228
  // ── EvolAgent Registry:加载 agents/<aid>/config.json ──
118
229
  const agentRegistry = new EvolAgentRegistry(paths.agentsDir);
119
230
  agentRegistry.loadAll();
@@ -190,16 +301,22 @@ async function main() {
190
301
  // 统计收集器(近 1 小时滚动统计)
191
302
  const statsCollector = new StatsCollector(eventBus);
192
303
  // Per-AID 消息统计收集器(累计,供 watch aid 实时展示)
193
- const aidStatsCollector = new AidStatsCollector();
304
+ const aidStatsCollector = new AidStatsCollector(eventBus);
305
+ aidStatsCollector.setSessionsDir(paths.sessionsDir);
194
306
  // 初始化 SessionManager(文件系统后端)
195
307
  const sessionManager = new SessionManager(paths.sessionsDir, eventBus, (channel, userId) => agentRegistry.isOwner(channel, userId), (channel, userId) => agentRegistry.isAdmin(channel, userId));
196
308
  // sessionMode 解析:从 channel 路由到具体 agent,按 agent.config.chatmode
197
- sessionManager.setSessionModeResolver((channelKey, chatType) => {
309
+ sessionManager.setSessionModeResolver((channelKey, chatType, peerType) => {
198
310
  const agent = agentRegistry.resolveByChannel(channelKey);
199
311
  const cm = agent?.config.chatmode;
200
312
  if (!cm)
201
313
  return undefined;
202
- return chatType === 'group' ? cm.group : cm.private;
314
+ // 优先级:群聊 > nothuman > private
315
+ if (chatType === 'group')
316
+ return cm.group;
317
+ if (peerType && peerType !== 'human' && peerType !== 'unknown')
318
+ return cm.nothuman;
319
+ return cm.private;
203
320
  });
204
321
  logger.info('✓ Database initialized');
205
322
  // 注册会话文件适配器(Claude / Codex 各自的会话文件操作)
@@ -361,6 +478,49 @@ async function main() {
361
478
  if (primaryAgentForTrigger?.triggerScheduler && primaryAgentForTrigger?.triggerManager) {
362
479
  cmdHandler.setTriggerScheduler(primaryAgentForTrigger.triggerScheduler, primaryAgentForTrigger.triggerManager);
363
480
  }
481
+ // Seed default __upgrade-check trigger (daily at random time 3:00~3:59)
482
+ // 用户可通过 /trigger cancel __upgrade-check 永久禁用(不会再自动重建)
483
+ if (!isLinkedInstall() && primaryAgentForTrigger?.triggerManager && primaryAgentForTrigger?.triggerScheduler) {
484
+ const mgr = primaryAgentForTrigger.triggerManager;
485
+ const sched = primaryAgentForTrigger.triggerScheduler;
486
+ const UPGRADE_TRIGGER_NAME = '__upgrade-check';
487
+ if (!mgr.getByName(UPGRADE_TRIGGER_NAME)) {
488
+ // Check history: if user cancelled it before, respect that decision
489
+ const { history } = mgr.listAll();
490
+ const wasCancelled = history.some(h => h.name === UPGRADE_TRIGGER_NAME && h.doneReason === 'cancelled');
491
+ if (!wasCancelled) {
492
+ // Random minute in 3:00~3:59 to avoid all instances hitting registry simultaneously
493
+ const randomMinute = Math.floor(Math.random() * 60);
494
+ const cronExpr = `${randomMinute} 3 * * *`;
495
+ // Use first channel instance as target (command doesn't need real channelId)
496
+ const firstChannel = channelInstances[0]?.adapter?.channelName || 'system';
497
+ const trigger = {
498
+ id: crypto.randomUUID(),
499
+ name: UPGRADE_TRIGGER_NAME,
500
+ scheduleType: 'cron',
501
+ scheduleValue: cronExpr,
502
+ nextFireAt: calcNextFireAt('cron', cronExpr),
503
+ targetChannel: firstChannel,
504
+ targetChannelId: '__system__',
505
+ targetSessionStrategy: 'silent',
506
+ prompt: '检查 evolclaw 是否有新版本可用。执行 `npm view evolclaw version` 获取最新版本,与当前版本(执行 `evolclaw --version`)对比。如果有新版本,执行 /restart 进行升级。如果已是最新版本,无需任何操作。',
507
+ createdByPeerId: '__system__',
508
+ createdByChannel: '__system__',
509
+ fireCount: 0,
510
+ createdAt: Date.now(),
511
+ updatedAt: Date.now(),
512
+ };
513
+ try {
514
+ mgr.register(trigger);
515
+ sched.register(trigger);
516
+ logger.info(`[Trigger] Seeded default trigger: ${UPGRADE_TRIGGER_NAME} (cron ${cronExpr})`);
517
+ }
518
+ catch (e) {
519
+ logger.warn(`[Trigger] Failed to seed ${UPGRADE_TRIGGER_NAME}: ${e}`);
520
+ }
521
+ }
522
+ }
523
+ }
364
524
  // 默认策略
365
525
  const defaultPolicy = {
366
526
  canSwitchProject: (chatType, role) => chatType === 'private' ? (role === 'owner' || role === 'admin') : role === 'owner',
@@ -378,6 +538,12 @@ async function main() {
378
538
  msgBridge.setAgentRegistry(agentRegistry);
379
539
  // ── Channel instance registration (shared by startup and hot-load) ──
380
540
  function registerChannelInstance(inst) {
541
+ // 0. 包装 adapter.send,记录所有出站到 channel-out.log
542
+ const originalSend = inst.adapter.send.bind(inst.adapter);
543
+ inst.adapter.send = async (envelope, payload) => {
544
+ logger.channelOut({ channel: inst.adapter.channelName, channelId: envelope.channelId, taskId: envelope.taskId, payload: summarizeOutboundPayload(payload) });
545
+ return originalSend(envelope, payload);
546
+ };
381
547
  // 1. 项目路径提供器
382
548
  if (inst.onProjectPathRequest && inst.channel.onProjectPathRequest) {
383
549
  inst.channel.onProjectPathRequest(async (channelId) => {
@@ -435,6 +601,10 @@ async function main() {
435
601
  agent.status = 'running';
436
602
  }
437
603
  }
604
+ // 写入 ready 信号(核心服务已就绪,channel 连接不阻塞启动判定)
605
+ const readySignalPath = resolvePaths().readySignal;
606
+ fs.writeFileSync(readySignalPath, String(Date.now()));
607
+ logger.info(`✓ Ready signal written: ${readySignalPath}`);
438
608
  // ── 连接所有渠道(异步,AUN 等 WebSocket 渠道在后台重连)──
439
609
  const connected = await channelLoader.connectAll(channelInstances);
440
610
  // 预填充 Feishu 已知 thread_id(重启后避免误判话题创建)
@@ -631,10 +801,6 @@ async function main() {
631
801
  logger.error('[Restart] Failed to send restart notification:', e);
632
802
  }
633
803
  }
634
- // 写入 ready 信号,供 restart-monitor 检测启动成功
635
- const readySignalPath = resolvePaths().readySignal;
636
- fs.writeFileSync(readySignalPath, String(Date.now()));
637
- logger.info(`✓ Ready signal written: ${readySignalPath}`);
638
804
  // IPC server — 供 CLI 查询实时状态 + Agent ctl 指令执行
639
805
  const ipcServer = new IpcServer(resolvePaths().socket, () => {
640
806
  const channels = {};
@@ -784,7 +950,8 @@ async function main() {
784
950
  results.push(`⚠ ${cfg.aid}: ${e?.message || e}`);
785
951
  }
786
952
  }
787
- // 重建 channel index
953
+ // 重建 channel index + 清除 kit 缓存
954
+ invalidateKitCache();
788
955
  agentRegistry.channelIndex.clear();
789
956
  agentRegistry.buildChannelIndex();
790
957
  logger.info(`[Resync] Done: ${results.length} agent(s) processed`);