evolclaw 2.8.0 → 2.8.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.
@@ -128,7 +128,6 @@ export class CommandHandler {
128
128
  permissionGateway;
129
129
  interactionRouter;
130
130
  statsCollector;
131
- hotLoadChannel;
132
131
  agentMap;
133
132
  defaultAgentId;
134
133
  /** 按 agentId 获取 agent,回退到默认 */
@@ -294,9 +293,6 @@ export class CommandHandler {
294
293
  setMessageQueue(messageQueue) {
295
294
  this.messageQueue = messageQueue;
296
295
  }
297
- setHotLoadChannel(fn) {
298
- this.hotLoadChannel = fn;
299
- }
300
296
  setPermissionGateway(gateway) {
301
297
  this.permissionGateway = gateway;
302
298
  }
@@ -442,9 +438,9 @@ export class CommandHandler {
442
438
  ] : []),
443
439
  ...(isOwner ? [
444
440
  { cmd: '/file', label: '发送项目内文件', desc: '将项目目录内的文件发送给用户' },
445
- { cmd: '/aid', label: 'AID 管理', desc: '创建新 AID 并上线新 Agent 实例', next: { type: 'select', items: [
446
- { value: 'list', label: '列表', desc: '列出所有 AUN 实例及连接状态' },
447
- { value: 'new', label: '创建', desc: '创建新 AID 并热加载上线', next: { type: 'text' } },
441
+ { cmd: '/aid', label: 'AID 身份管理', desc: '管理本地 AID 身份(创建/列表)', next: { type: 'select', items: [
442
+ { value: 'list', label: '列表', desc: '列出本地所有 AID' },
443
+ { value: 'new', label: '创建', desc: '创建新 AID 身份', next: { type: 'text' } },
448
444
  ] } },
449
445
  { cmd: '/agentmd', label: '管理 agent.md', desc: '查看或更新 AUN 网络上的 agent.md 身份文件', next: { type: 'select', items: [
450
446
  { value: 'put', label: '上传当前', desc: '将本地 agent.md 上传到 AUN 网络' },
@@ -741,7 +737,7 @@ export class CommandHandler {
741
737
  ...(isOwner ? [
742
738
  ' /restart - 重启服务',
743
739
  ' /file [channel] <path> - 发送项目内文件',
744
- ' /aid [list|new <aid>] - AID 管理',
740
+ ' /aid [list|new <aid>] - AID 身份管理',
745
741
  ' /agentmd [put|set <内容>] - 管理 agent.md',
746
742
  ] : []),
747
743
  '',
@@ -1299,87 +1295,51 @@ export class CommandHandler {
1299
1295
  }
1300
1296
  return `✓ 推理强度: ${newEffort}`;
1301
1297
  }
1302
- // /aid 命令:AID 管理(list / new)
1298
+ // /aid 命令:AID 身份管理(list / new)
1303
1299
  if (normalizedContent === '/aid' || normalizedContent === '/aid list' || normalizedContent.startsWith('/aid ')) {
1304
1300
  if (!isOwner)
1305
1301
  return '❌ 无权限:此命令仅限 owner 使用';
1306
- const adapter = this.adapters.get(channel);
1307
- const channelType = this.channelTypeMap.get(channel);
1308
- if (channelType !== 'aun')
1309
- return '❌ 此命令仅在 AUN 通道中可用';
1310
1302
  const arg = normalizedContent.slice(4).trim();
1311
- // /aid /aid list 列出所有 AUN 实例
1303
+ const { aidList, aidCreate, agentmdPut, buildInitialAgentMd, isValidAid } = await import('../channels/aun-ops.js');
1304
+ // /aid 或 /aid list — 列出本地所有 AID
1312
1305
  if (!arg || arg === 'list') {
1313
- const { normalizeChannelInstances } = await import('../config.js');
1314
- const instances = normalizeChannelInstances(this.config.channels?.aun, 'aun');
1315
- if (instances.length === 0)
1316
- return '暂无 AUN 实例';
1317
- const lines = ['AUN 实例:'];
1318
- for (const inst of instances) {
1319
- if (inst.enabled === false || !inst.aid)
1320
- continue;
1321
- const channelObj = this.channelObjects.get(inst.name);
1322
- const status = channelObj?.getStatus?.();
1323
- const connected = status?.connected ?? false;
1324
- const icon = connected ? '' : '✗';
1325
- const state = connected ? '已连接' : '未连接';
1326
- lines.push(` ${icon} ${inst.name} ${inst.aid} ${state}`);
1327
- }
1306
+ const aids = aidList();
1307
+ if (aids.length === 0)
1308
+ return '本地无 AID';
1309
+ const lines = ['本地 AID:'];
1310
+ for (const a of aids) {
1311
+ const icons = [
1312
+ a.hasPrivateKey ? '🔑' : ' ',
1313
+ a.hasAgentMd ? '📄' : ' ',
1314
+ ].join('');
1315
+ lines.push(` ${icons} ${a.aid}`);
1316
+ }
1317
+ lines.push('\n🔑=私钥 📄=agent.md');
1328
1318
  return lines.join('\n');
1329
1319
  }
1330
- // /aid new <aid> — 创建新 AID 并热加载
1320
+ // /aid new <aid> — 创建 AID(纯身份,不动 config)
1331
1321
  if (arg.startsWith('new ')) {
1332
- const rawName = arg.slice(4).trim();
1333
- if (!rawName)
1334
- return '用法: /aid new <aid>\n例: /aid new reviewer';
1335
- if (!this.hotLoadChannel)
1336
- return '❌ 热加载未就绪';
1337
- // Derive full AID: if no dots, append domain from current AID
1338
- const selfAid = typeof adapter._selfAid === 'function' ? adapter._selfAid() : '';
1339
- let fullAid = rawName;
1340
- if (!rawName.includes('.')) {
1341
- const domain = selfAid.split('.').slice(1).join('.');
1342
- if (!domain)
1343
- return '❌ 无法推导 AID 域(当前实例未连接)';
1344
- fullAid = `${rawName}.${domain}`;
1345
- }
1346
- // Validate AID format
1347
- const { isValidAid } = await import('../utils/init-channel.js');
1348
- if (!isValidAid(fullAid))
1349
- return `❌ 无效 AID 格式: ${fullAid}`;
1350
- // Check instance name conflict
1351
- const instName = rawName.includes('.') ? rawName.split('.')[0] : rawName;
1352
- const { normalizeChannelInstances } = await import('../config.js');
1353
- const existing = normalizeChannelInstances(this.config.channels?.aun, 'aun');
1354
- if (existing.some(e => e.name === instName)) {
1355
- return `❌ 实例名 "${instName}" 已存在`;
1356
- }
1357
- if (existing.some(e => e.aid === fullAid)) {
1358
- return `❌ AID ${fullAid} 已在配置中`;
1359
- }
1360
- // Create AID (reuse init-channel.ts silent logic)
1322
+ const rawAid = arg.slice(4).trim();
1323
+ if (!rawAid)
1324
+ return '用法: /aid new <完整AID>\n例: /aid new reviewer.agentid.pub';
1325
+ if (!isValidAid(rawAid))
1326
+ return `❌ 无效 AID 格式: ${rawAid}`;
1361
1327
  try {
1362
- const { createAidSilent, appendAunInstance } = await import('../utils/init-channel.js');
1363
- const createResult = await createAidSilent({ aid: fullAid, owner: selfAid });
1364
- // Resolve owner from current AUN instance config
1365
- const owner = this.config.channels?.aun
1366
- ? (Array.isArray(this.config.channels.aun)
1367
- ? this.config.channels.aun.find((a) => a.aid === selfAid)?.owner
1368
- : this.config.channels.aun.owner)
1369
- : undefined;
1370
- // Hot-load: build and register new channel instance BEFORE writing config
1371
- const { AUNChannelPlugin } = await import('../channels/aun.js');
1372
- const plugin = new AUNChannelPlugin();
1373
- const tempConfig = JSON.parse(JSON.stringify(this.config));
1374
- tempConfig.channels.aun = [{ name: instName, enabled: true, aid: fullAid, owner }];
1375
- const newInstances = await plugin.createChannels(tempConfig);
1376
- if (newInstances.length === 0)
1377
- return '❌ 通道实例创建失败';
1378
- await this.hotLoadChannel(newInstances[0]);
1379
- // Write config only after successful hot-load
1380
- appendAunInstance(this.config, { name: instName, aid: fullAid, owner });
1381
- const verb = createResult.alreadyExisted ? '已存在,现已上线' : '已创建并上线';
1382
- return `✓ ${fullAid} ${verb}\n 实例名: ${instName}\n 可在 AUN 中搜索该 AID 开始对话`;
1328
+ const result = await aidCreate(rawAid);
1329
+ if (!result.alreadyExisted) {
1330
+ const content = buildInitialAgentMd({ aid: rawAid });
1331
+ try {
1332
+ await agentmdPut(content, { aid: rawAid, client: result.client });
1333
+ }
1334
+ catch { /* non-fatal */ }
1335
+ }
1336
+ try {
1337
+ await result.client.close();
1338
+ }
1339
+ catch { /* ignore */ }
1340
+ const verb = result.alreadyExisted ? '已存在' : '已创建';
1341
+ return `✓ ${rawAid} ${verb}
1342
+ 如需上线 AUN 通道,运行 evolclaw init aun`;
1383
1343
  }
1384
1344
  catch (e) {
1385
1345
  return `❌ 创建失败: ${String(e.message || e).slice(0, 200)}`;
@@ -1387,7 +1347,7 @@ export class CommandHandler {
1387
1347
  }
1388
1348
  return '用法: /aid [list|new <aid>]';
1389
1349
  }
1390
- // /activity 命令:控制中间输出显示模式
1350
+ // /agentmd 命令:管理 agent.md 身份文件
1391
1351
  if (normalizedContent === '/agentmd' || normalizedContent.startsWith('/agentmd ')) {
1392
1352
  if (!isOwner)
1393
1353
  return '❌ 无权限:此命令仅限 owner 使用';
@@ -1396,7 +1356,8 @@ export class CommandHandler {
1396
1356
  return '❌ 当前通道不支持 agent.md 操作';
1397
1357
  const selfAid = typeof adapter._selfAid === 'function' ? adapter._selfAid() : '';
1398
1358
  const arg = normalizedContent.slice(9).trim();
1399
- // put read local ~/.aun/AIDs/{aid}/agent.md and upload
1359
+ const { agentmdGet, agentmdPut } = await import('../channels/aun-ops.js');
1360
+ // put — read local agent.md and upload to network
1400
1361
  if (arg === 'put') {
1401
1362
  if (!selfAid)
1402
1363
  return '❌ 未连接,无法确定本地 AID';
@@ -1405,15 +1366,17 @@ export class CommandHandler {
1405
1366
  const { join } = await import('node:path');
1406
1367
  const { homedir } = await import('node:os');
1407
1368
  const localPath = join(homedir(), '.aun', 'AIDs', selfAid, 'agent.md');
1369
+ if (!readFileSync)
1370
+ return '❌ 读取失败';
1408
1371
  const content = readFileSync(localPath, 'utf-8');
1409
- await adapter.uploadAgentMd(content);
1372
+ await agentmdPut(content, { aid: selfAid });
1410
1373
  return '✅ agent.md 已发布';
1411
1374
  }
1412
1375
  catch (e) {
1413
1376
  return `❌ 发布失败: ${String(e.message || e).slice(0, 100)}`;
1414
1377
  }
1415
1378
  }
1416
- // set <content> — upload inline content and sync to local
1379
+ // set <content> — upload inline content
1417
1380
  if (arg.startsWith('set ')) {
1418
1381
  const content = arg.slice(4).trim();
1419
1382
  if (!content)
@@ -1421,13 +1384,7 @@ export class CommandHandler {
1421
1384
  if (!selfAid)
1422
1385
  return '❌ 未连接,无法确定本地 AID';
1423
1386
  try {
1424
- await adapter.uploadAgentMd(content);
1425
- const { writeFileSync, mkdirSync } = await import('node:fs');
1426
- const { join } = await import('node:path');
1427
- const { homedir } = await import('node:os');
1428
- const localDir = join(homedir(), '.aun', 'AIDs', selfAid);
1429
- mkdirSync(localDir, { recursive: true });
1430
- writeFileSync(join(localDir, 'agent.md'), content, 'utf-8');
1387
+ await agentmdPut(content, { aid: selfAid });
1431
1388
  return '✅ agent.md 已更新并发布到AUN网络';
1432
1389
  }
1433
1390
  catch (e) {
@@ -1439,7 +1396,7 @@ export class CommandHandler {
1439
1396
  if (!aidToView)
1440
1397
  return '用法:/agentmd [<aid>] | put | set <内容>';
1441
1398
  try {
1442
- const md = await adapter.downloadAgentMd(aidToView);
1399
+ const md = await agentmdGet(aidToView);
1443
1400
  if (!md || !md.trim())
1444
1401
  return `ℹ️ ${aidToView} 尚未设置 agent.md`;
1445
1402
  return `\`\`\`\n${md.slice(0, 1500)}\n\`\`\``;
@@ -2530,11 +2487,11 @@ export class CommandHandler {
2530
2487
  return `❌ 序号超出范围 (1-${visibleSessions.length})\n使用 /s 查看可用会话`;
2531
2488
  }
2532
2489
  }
2533
- if (!targetSession && sessionName.length === 8) {
2490
+ if (!targetSession && sessionName.length >= 8) {
2534
2491
  targetSession = await this.sessionManager.getSessionByUuidPrefix(channel, channelId, sessionName);
2535
2492
  }
2536
2493
  const canImport = policy.canImportCliSession(session?.chatType || 'private', identity.role);
2537
- if (!targetSession && sessionName.length === 8 && canImport) {
2494
+ if (!targetSession && sessionName.length >= 8 && canImport) {
2538
2495
  const projectPaths = Object.values(this.projects);
2539
2496
  if (session) {
2540
2497
  projectPaths.unshift(session.projectPath);
@@ -2637,7 +2594,7 @@ export class CommandHandler {
2637
2594
  return `❌ 序号超出范围 (1-${visibleSessions.length})\n使用 /s 查看可用会话`;
2638
2595
  }
2639
2596
  }
2640
- if (!targetSession && sessionName.length === 8) {
2597
+ if (!targetSession && sessionName.length >= 8) {
2641
2598
  targetSession = await this.sessionManager.getSessionByUuidPrefix(channel, channelId, sessionName);
2642
2599
  }
2643
2600
  if (!targetSession) {
@@ -0,0 +1,72 @@
1
+ import path from 'path';
2
+ const VALID_BASEAGENTS = new Set(['claude', 'codex', 'gemini', 'hermes']);
3
+ const VALID_CHANNEL_TYPES = new Set(['feishu', 'aun', 'wechat', 'wecom', 'dingtalk', 'qqbot']);
4
+ const VALID_CHATMODES = new Set(['interactive', 'proactive']);
5
+ export function validateEvolAgentConfig(raw) {
6
+ const errors = [];
7
+ if (!raw || typeof raw !== 'object') {
8
+ return { valid: false, errors: ['config must be an object'] };
9
+ }
10
+ if (typeof raw.name !== 'string' || raw.name.trim() === '') {
11
+ errors.push('name is required and must be a non-empty string');
12
+ }
13
+ if (raw.enabled !== undefined && typeof raw.enabled !== 'boolean') {
14
+ errors.push('enabled must be a boolean if present');
15
+ }
16
+ if (!raw.agents || typeof raw.agents !== 'object') {
17
+ errors.push('agents must be an object with exactly one baseagent block');
18
+ }
19
+ else {
20
+ const keys = Object.keys(raw.agents).filter(k => VALID_BASEAGENTS.has(k));
21
+ const unknownKeys = Object.keys(raw.agents).filter(k => !VALID_BASEAGENTS.has(k));
22
+ if (unknownKeys.length > 0) {
23
+ errors.push(`agents contains unknown baseagent keys: ${unknownKeys.join(', ')}`);
24
+ }
25
+ if (keys.length === 0) {
26
+ errors.push('agents must contain exactly one of: claude | codex | gemini | hermes');
27
+ }
28
+ else if (keys.length > 1) {
29
+ errors.push(`agents must contain exactly one baseagent (single baseagent only), got: ${keys.join(', ')}`);
30
+ }
31
+ }
32
+ if (!raw.channels || typeof raw.channels !== 'object') {
33
+ errors.push('channels is required');
34
+ }
35
+ else {
36
+ const channelKeys = Object.keys(raw.channels);
37
+ if (channelKeys.length === 0) {
38
+ errors.push('channels must contain at least one channel type');
39
+ }
40
+ for (const key of channelKeys) {
41
+ if (!VALID_CHANNEL_TYPES.has(key)) {
42
+ errors.push(`unknown channel type: ${key}`);
43
+ }
44
+ }
45
+ }
46
+ if (!raw.projects || typeof raw.projects !== 'object') {
47
+ errors.push('projects is required');
48
+ }
49
+ else {
50
+ const p = raw.projects.defaultPath;
51
+ if (typeof p !== 'string' || p === '') {
52
+ errors.push('projects.defaultPath is required');
53
+ }
54
+ else if (!path.isAbsolute(p)) {
55
+ errors.push(`projects.defaultPath must be absolute, got: ${p}`);
56
+ }
57
+ }
58
+ if (raw.chatmode !== undefined) {
59
+ if (typeof raw.chatmode !== 'object' || raw.chatmode === null) {
60
+ errors.push('chatmode must be an object if present');
61
+ }
62
+ else {
63
+ for (const key of ['private', 'group']) {
64
+ const val = raw.chatmode[key];
65
+ if (val !== undefined && !VALID_CHATMODES.has(val)) {
66
+ errors.push(`chatmode.${key} must be 'interactive' or 'proactive'`);
67
+ }
68
+ }
69
+ }
70
+ }
71
+ return { valid: errors.length === 0, errors };
72
+ }
@@ -0,0 +1,66 @@
1
+ export class EvolAgent {
2
+ name;
3
+ configPath;
4
+ config;
5
+ isDefault;
6
+ channels = new Map();
7
+ activeSessions = 0;
8
+ lastActivity;
9
+ status;
10
+ error;
11
+ constructor(configPath, config, opts = {}) {
12
+ this.configPath = configPath;
13
+ this.config = config;
14
+ this.name = config.name;
15
+ this.isDefault = opts.isDefault === true;
16
+ this.status = config.enabled === false ? 'disabled' : 'stopped';
17
+ }
18
+ get baseagent() {
19
+ const keys = Object.keys(this.config.agents);
20
+ return keys[0] || 'claude';
21
+ }
22
+ get model() {
23
+ return this.config.agents[this.baseagent]?.model;
24
+ }
25
+ get effort() {
26
+ return this.config.agents[this.baseagent]?.effort;
27
+ }
28
+ get projectPath() {
29
+ return this.config.projects.defaultPath;
30
+ }
31
+ channelInstanceNames() {
32
+ const names = [];
33
+ for (const [type, raw] of Object.entries(this.config.channels || {})) {
34
+ const instances = Array.isArray(raw) ? raw : [raw];
35
+ for (const inst of instances) {
36
+ if (!inst || typeof inst !== 'object')
37
+ continue;
38
+ names.push(inst.name ?? type);
39
+ }
40
+ }
41
+ return names;
42
+ }
43
+ getContext(channelName, chatType, globalChatmode) {
44
+ const chatMode = this.resolveChatMode(chatType, globalChatmode);
45
+ return {
46
+ name: this.name,
47
+ isOwned: !this.isDefault,
48
+ baseagent: this.baseagent,
49
+ model: this.model,
50
+ effort: this.effort,
51
+ chatMode,
52
+ projectPath: this.projectPath,
53
+ };
54
+ }
55
+ resolveChatMode(chatType, globalChatmode) {
56
+ const agentCm = this.config.chatmode;
57
+ const key = chatType === 'group' ? 'group' : 'private';
58
+ if (agentCm) {
59
+ return (agentCm[key] || 'interactive');
60
+ }
61
+ if (globalChatmode) {
62
+ return (globalChatmode[key] || 'interactive');
63
+ }
64
+ return 'interactive';
65
+ }
66
+ }
@@ -318,10 +318,15 @@ export class MessageProcessor {
318
318
  const modeInfo = isBackground ? ' [\u540e\u53f0]' : '';
319
319
  const e2eeInfo = message.replyContext?.metadata?.encrypted != null ? ` encrypt=${message.replyContext.metadata.encrypted}` : '';
320
320
  logger.info(`[${message.channel}] ${message.channelId}: ${message.content}${imageInfo}${modeInfo}${e2eeInfo}`);
321
- logger.info(`[MessageProcessor] session=${session.id} task=${taskId} chatType=${session.chatType} sessionMode=${session.sessionMode} agentId=${session.agentId} msgChatType=${message.chatType ?? 'n/a'}`);
321
+ // 构建 peer 标识(优先 peerName,退化到 peerId / channelId)
322
+ const peerName = session.metadata?.peerName ?? message.peerName;
323
+ const peerId = session.metadata?.peerId ?? message.peerId ?? message.channelId;
324
+ const peerShort = peerId ? peerId.split('.')[0].split(':')[0] : '?';
325
+ const peerLabel = peerName && peerName !== peerShort ? `${peerShort}(${peerName})` : peerShort;
326
+ logger.info(`[MessageProcessor] session=${session.id} task=${taskId} peer=${peerLabel} chatType=${session.chatType} sessionMode=${session.sessionMode} agentId=${session.agentId} msgChatType=${message.chatType ?? 'n/a'}`);
322
327
  // 记录开始处理
323
328
  this.eventBus.publish({ type: 'message:processing', sessionId: session.id });
324
- adapter.sendProcessingStatus?.(message.channelId, 'start', session.id, taskId, this.getReplyContext(message));
329
+ adapter.sendProcessingStatus?.(message.channelId, 'start', session.id, taskId, taskReplyContext());
325
330
  logger.message({
326
331
  msgId: messageId,
327
332
  sessionId: session.id,
@@ -531,8 +536,13 @@ export class MessageProcessor {
531
536
  for (const match of fileMatches) {
532
537
  // 兼容旧格式 (1组) 和新格式 (2组)
533
538
  const hasChannelGroup = match.length >= 3;
534
- const targetSpec = hasChannelGroup ? (match[1] ?? undefined) : undefined;
535
- const filePath = (hasChannelGroup ? match[2] : match[1]).trim();
539
+ let targetSpec = hasChannelGroup ? (match[1] ?? undefined) : undefined;
540
+ let filePath = (hasChannelGroup ? match[2] : match[1]).trim();
541
+ // 白名单校验:targetSpec 必须是已注册通道,否则视为路径的一部分(如 Windows 盘符 C:)
542
+ if (targetSpec && !this.channels.has(targetSpec) && !this.channelTypeMap.has(targetSpec)) {
543
+ filePath = `${targetSpec}:${filePath}`;
544
+ targetSpec = undefined;
545
+ }
536
546
  if (this.isPlaceholderPath(filePath)) {
537
547
  logger.info(`[${adapter.channelName}] Skipped placeholder file marker: [SEND_FILE:${filePath}]`);
538
548
  continue;
@@ -639,7 +649,7 @@ export class MessageProcessor {
639
649
  const errorSummary = streamResult.errors?.join('; ') || '任务执行失败';
640
650
  const rawSubtype = streamResult.subtype || 'agent_error';
641
651
  const errorType = prefixErrorType(ERROR_PREFIX.AGENT, rawSubtype);
642
- adapter.sendProcessingStatus?.(message.channelId, 'error', session.id, taskId, this.getReplyContext(message));
652
+ adapter.sendProcessingStatus?.(message.channelId, 'error', session.id, taskId, taskReplyContext());
643
653
  this.eventBus.publish({
644
654
  type: 'message:error',
645
655
  sessionId: session.id,
@@ -667,7 +677,7 @@ export class MessageProcessor {
667
677
  }
668
678
  else {
669
679
  // 真正的成功
670
- adapter.sendProcessingStatus?.(message.channelId, interruptReason ? 'interrupted' : 'done', session.id, taskId, this.getReplyContext(message));
680
+ adapter.sendProcessingStatus?.(message.channelId, interruptReason ? 'interrupted' : 'done', session.id, taskId, taskReplyContext());
671
681
  await this.sessionManager.recordSuccess(session.id);
672
682
  this.eventBus.publish({
673
683
  type: 'message:completed',
@@ -722,7 +732,7 @@ export class MessageProcessor {
722
732
  // 用户主动中断(新消息打断 或 /stop 命令)时静默,不发送中断/错误提示
723
733
  if (!isUserInterrupt) {
724
734
  try {
725
- adapter.sendProcessingStatus?.(message.channelId, procStatus, session.id, taskId, this.getReplyContext(message));
735
+ adapter.sendProcessingStatus?.(message.channelId, procStatus, session.id, taskId, taskReplyContext());
726
736
  }
727
737
  catch { }
728
738
  }
package/dist/index.js CHANGED
@@ -348,18 +348,6 @@ async function main() {
348
348
  for (const inst of channelInstances) {
349
349
  registerChannelInstance(inst);
350
350
  }
351
- // ── 设置热加载回调 ──
352
- cmdHandler.setHotLoadChannel(async (inst) => {
353
- registerChannelInstance(inst);
354
- channelInstances.push(inst);
355
- await inst.connect();
356
- eventBus.publish({
357
- type: 'channel:connected',
358
- channel: (inst.channelType || inst.adapter.channelName).toLowerCase(),
359
- channelName: inst.adapter.channelName,
360
- timestamp: Date.now(),
361
- });
362
- });
363
351
  // ── 连接所有渠道 ──
364
352
  const connected = await channelLoader.connectAll(channelInstances);
365
353
  // 预填充 Feishu 已知 thread_id(重启后避免误判话题创建)
package/dist/paths.js CHANGED
@@ -32,6 +32,7 @@ export function resolvePaths() {
32
32
  pid: path.join(root, 'logs', 'evolclaw.pid'),
33
33
  dataDir: path.join(root, 'data'),
34
34
  logs: path.join(root, 'logs'),
35
+ agentsDir: path.join(root, 'agents'),
35
36
  lineStats: path.join(root, 'logs', 'line-stats.log'),
36
37
  readySignal: path.join(root, 'logs', 'ready.signal'),
37
38
  selfHealLog: path.join(root, 'logs', 'self-heal.md'),
@@ -49,6 +50,7 @@ export function ensureDataDirs() {
49
50
  const p = resolvePaths();
50
51
  fs.mkdirSync(p.dataDir, { recursive: true });
51
52
  fs.mkdirSync(p.logs, { recursive: true });
53
+ fs.mkdirSync(p.agentsDir, { recursive: true });
52
54
  }
53
55
  export function getPackageRoot() {
54
56
  // import.meta.dirname is available in Node.js 21.2+ and always returns