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
@@ -467,7 +467,8 @@ export class DingtalkChannelPlugin {
467
467
  await channel.sendImage(channelId, payload.data);
468
468
  return;
469
469
  case 'activity.batch': {
470
- const text = formatItemsAsText(payload.items);
470
+ const filtered = payload.items.filter((i) => !(i.kind === 'tool_result' && i.ok));
471
+ const text = formatItemsAsText(filtered);
471
472
  if (text)
472
473
  await channel.sendMessage(channelId, text);
473
474
  return;
@@ -481,6 +482,7 @@ export class DingtalkChannelPlugin {
481
482
  case 'status.interrupted':
482
483
  case 'status.error':
483
484
  case 'status.timeout':
485
+ case 'status.progress':
484
486
  case 'custom':
485
487
  return;
486
488
  default:
@@ -82,8 +82,6 @@ export class FeishuChannel {
82
82
  if (msg.thread_id) {
83
83
  logger.info('[Feishu] Thread message, thread_id:', msg.thread_id, 'root_id:', msg.root_id);
84
84
  }
85
- // [DEBUG] 临时:记录所有消息的 root_id/thread_id,用于排查图片回复带引用问题
86
- logger.info('[Feishu][DEBUG] msg_type:', msg.message_type, 'root_id:', msg.root_id ?? '(empty)', 'thread_id:', msg.thread_id ?? '(empty)', 'parent_id:', msg.parent_id ?? '(empty)');
87
85
  // 提取 @ 提及列表(排除机器人自身)
88
86
  const mentions = (msg.mentions || []).map((m) => ({
89
87
  userId: m.id?.open_id || '',
@@ -181,6 +179,16 @@ export class FeishuChannel {
181
179
  quotedText = `> 以下是引用的原消息\n> ================\n> [文件消息]\n> ================\n\n`;
182
180
  }
183
181
  }
182
+ else if (quotedMsgType === 'merge_forward') {
183
+ const { text: mergedText, images: mergedImages } = await this.extractMergeForwardContent(msg.parent_id, msg.chat_id);
184
+ if (mergedText) {
185
+ quotedText = `> 以下是引用的原消息\n> ================\n> [合并转发消息]\n> ================\n\n${mergedText}\n\n`;
186
+ quotedImages.push(...mergedImages);
187
+ }
188
+ else {
189
+ quotedText = `> 以下是引用的原消息\n> ================\n> [合并转发消息]\n> ================\n\n`;
190
+ }
191
+ }
184
192
  else {
185
193
  quotedText = `> 以下是引用的原消息\n> ================\n> [${quotedMsgType}消息]\n> ================\n\n`;
186
194
  }
@@ -189,6 +197,7 @@ export class FeishuChannel {
189
197
  logger.warn({ err }, '[Feishu] Failed to fetch quoted message');
190
198
  }
191
199
  }
200
+ logger.info(`[Feishu] Incoming message_type=${msg.message_type} content=${msg.content?.substring(0, 200)}`);
192
201
  // 处理文本消息
193
202
  if (msg.message_type === 'text') {
194
203
  const parsed = JSON.parse(msg.content);
@@ -210,7 +219,7 @@ export class FeishuChannel {
210
219
  const imageData = await this.downloadAndSaveImage(imageKey, msg.chat_id, msg.message_id, projectPath);
211
220
  if (imageData) {
212
221
  const allImages = [...quotedImages, imageData];
213
- const prompt = quotedText + '用户发送了一张图片,请分析这张图片的内容。';
222
+ const prompt = quotedText + '用户发送了一张图片,请结合上下文理解用户意图并回应。';
214
223
  await this.messageHandler({ channelId: msg.chat_id, content: prompt, images: allImages, peerId, peerName, messageId: msg.message_id, threadId, rootId, chatType });
215
224
  }
216
225
  else {
@@ -269,6 +278,19 @@ export class FeishuChannel {
269
278
  const allImages = [...quotedImages, ...postImages];
270
279
  await this.messageHandler({ channelId: msg.chat_id, content: finalContent, images: allImages.length > 0 ? allImages : undefined, peerId, peerName, messageId: msg.message_id, threadId, rootId, chatType });
271
280
  }
281
+ // 处理合并转发消息
282
+ else if (msg.message_type === 'merge_forward') {
283
+ const { text: mergedText, images: mergedImages } = await this.extractMergeForwardContent(msg.message_id, msg.chat_id);
284
+ if (mergedText) {
285
+ const finalContent = quotedText + mergedText;
286
+ const allImages = [...quotedImages, ...mergedImages];
287
+ await this.messageHandler({ channelId: msg.chat_id, content: finalContent, images: allImages.length > 0 ? allImages : undefined, peerId, peerName, messageId: msg.message_id, threadId, rootId, chatType });
288
+ }
289
+ else {
290
+ const prompt = quotedText + '[合并转发消息解析失败]';
291
+ await this.messageHandler({ channelId: msg.chat_id, content: prompt, images: quotedImages.length > 0 ? quotedImages : undefined, peerId, peerName, messageId: msg.message_id, threadId, rootId, chatType });
292
+ }
293
+ }
272
294
  // 处理其他类型消息
273
295
  else {
274
296
  logger.debug('[Feishu] Unsupported message type:', msg.message_type);
@@ -551,7 +573,14 @@ export class FeishuChannel {
551
573
  const truncated = content.slice(0, 28000) + '\n\n⚠️ 消息过长,已截断';
552
574
  return this.sendMessage(chatId, truncated, options);
553
575
  }
554
- logger.error('[Feishu] Failed to send message:', error);
576
+ const respData = error?.response?.data;
577
+ const code = respData?.code;
578
+ logger.error('[Feishu] Failed to send message:', respData ? JSON.stringify(respData) : error?.message ?? error);
579
+ // post 格式相关错误(400/230001):降级为纯文本重试
580
+ if (!options?.forceText && (error?.response?.status === 400 || code === 230001)) {
581
+ logger.warn('[Feishu] Retrying as plain text (forceText)');
582
+ return this.sendMessage(chatId, content, { ...options, forceText: true });
583
+ }
555
584
  throw error;
556
585
  }
557
586
  }
@@ -802,6 +831,94 @@ export class FeishuChannel {
802
831
  return null;
803
832
  }
804
833
  }
834
+ /**
835
+ * 提取合并转发消息的子消息内容。
836
+ * 调用 im.message.get 获取子消息列表,逐条解析 text/image/post/file 类型。
837
+ */
838
+ async extractMergeForwardContent(messageId, chatId) {
839
+ const empty = { text: '', images: [] };
840
+ if (!this.client)
841
+ return empty;
842
+ try {
843
+ const res = await this.client.im.message.get({
844
+ path: { message_id: messageId }
845
+ });
846
+ const items = res.data?.items;
847
+ if (!items || items.length === 0) {
848
+ logger.warn('[Feishu] merge_forward: no sub-messages found');
849
+ return empty;
850
+ }
851
+ logger.info(`[Feishu] merge_forward: ${items.length} sub-messages`);
852
+ const projectPath = this.projectPathProvider
853
+ ? await this.projectPathProvider(chatId)
854
+ : process.cwd();
855
+ const textParts = [];
856
+ const images = [];
857
+ const MAX_IMAGES = 10;
858
+ textParts.push('以下是用户转发的合并消息:\n---');
859
+ for (const item of items) {
860
+ const msgType = item.msg_type;
861
+ const content = item.body?.content;
862
+ if (!content)
863
+ continue;
864
+ try {
865
+ if (msgType === 'text') {
866
+ const parsed = JSON.parse(content);
867
+ textParts.push(parsed.text || '');
868
+ }
869
+ else if (msgType === 'post') {
870
+ const parsed = JSON.parse(content);
871
+ let text = '';
872
+ const postContent = parsed.zh_cn?.content || parsed.en_us?.content || parsed.content;
873
+ if (postContent) {
874
+ for (const line of postContent) {
875
+ for (const elem of line) {
876
+ if (elem.tag === 'img' && elem.image_key && item.message_id && images.length < MAX_IMAGES) {
877
+ const imageData = await this.downloadAndSaveImage(elem.image_key, chatId, item.message_id, projectPath);
878
+ if (imageData)
879
+ images.push(imageData);
880
+ }
881
+ else if (elem.text) {
882
+ text += elem.text;
883
+ }
884
+ }
885
+ text += '\n';
886
+ }
887
+ }
888
+ const title = parsed.zh_cn?.title || parsed.en_us?.title || parsed.title;
889
+ textParts.push(title ? `${title}\n${text.trim()}` : text.trim());
890
+ }
891
+ else if (msgType === 'image' && item.message_id) {
892
+ const parsed = JSON.parse(content);
893
+ if (parsed.image_key && images.length < MAX_IMAGES) {
894
+ const imageData = await this.downloadAndSaveImage(parsed.image_key, chatId, item.message_id, projectPath);
895
+ if (imageData) {
896
+ images.push(imageData);
897
+ textParts.push('[图片]');
898
+ }
899
+ }
900
+ }
901
+ else if (msgType === 'file') {
902
+ const parsed = JSON.parse(content);
903
+ textParts.push(`[文件: ${parsed.file_name || 'unknown'}]`);
904
+ }
905
+ else {
906
+ textParts.push(`[${msgType}]`);
907
+ }
908
+ }
909
+ catch (parseErr) {
910
+ logger.debug('[Feishu] merge_forward: failed to parse sub-message:', parseErr);
911
+ textParts.push(`[${msgType}: 解析失败]`);
912
+ }
913
+ }
914
+ textParts.push('---');
915
+ return { text: textParts.join('\n'), images };
916
+ }
917
+ catch (error) {
918
+ logger.error('[Feishu] Failed to extract merge_forward content:', error);
919
+ return empty;
920
+ }
921
+ }
805
922
  async downloadFile(fileKey, fileName, messageId, projectPath) {
806
923
  if (!this.client)
807
924
  return null;
@@ -1223,7 +1340,7 @@ export class FeishuChannelPlugin {
1223
1340
  case 'result.error': {
1224
1341
  const sendCtx = { ...(ctx ?? {}) };
1225
1342
  if (payload.kind === 'result.text' && payload.isFinal)
1226
- sendCtx.title = ' 最终回复:';
1343
+ sendCtx.title = ' 最终回复:';
1227
1344
  await channel.sendMessage(channelId, payload.text, sendCtx);
1228
1345
  return;
1229
1346
  }
@@ -1234,9 +1351,12 @@ export class FeishuChannelPlugin {
1234
1351
  await channel.sendImage(channelId, payload.data, ctx);
1235
1352
  return;
1236
1353
  case 'activity.batch': {
1237
- const text = formatItemsAsText(payload.items);
1238
- if (text)
1354
+ // Feishu 不发送成功的 tool_result(信息密度低,刷屏)
1355
+ const filtered = payload.items.filter((i) => !(i.kind === 'tool_result' && i.ok));
1356
+ const text = formatItemsAsText(filtered);
1357
+ if (text) {
1239
1358
  await channel.sendMessage(channelId, text, ctx);
1359
+ }
1240
1360
  return;
1241
1361
  }
1242
1362
  case 'status.started':
@@ -1244,6 +1364,7 @@ export class FeishuChannelPlugin {
1244
1364
  case 'status.interrupted':
1245
1365
  case 'status.error':
1246
1366
  case 'status.timeout':
1367
+ case 'status.progress':
1247
1368
  // Feishu 通过 acknowledge (✓ 表情) 表达状态,由 channel 自行处理
1248
1369
  return;
1249
1370
  case 'interaction':
@@ -354,7 +354,8 @@ export class QQBotChannelPlugin {
354
354
  await channel.sendImage(channelId, payload.data);
355
355
  return;
356
356
  case 'activity.batch': {
357
- const text = formatItemsAsText(payload.items);
357
+ const filtered = payload.items.filter((i) => !(i.kind === 'tool_result' && i.ok));
358
+ const text = formatItemsAsText(filtered);
358
359
  if (text)
359
360
  await channel.sendMessage(channelId, text);
360
361
  return;
@@ -368,6 +369,7 @@ export class QQBotChannelPlugin {
368
369
  case 'status.interrupted':
369
370
  case 'status.error':
370
371
  case 'status.timeout':
372
+ case 'status.progress':
371
373
  case 'custom':
372
374
  return;
373
375
  default:
@@ -750,7 +750,9 @@ export class WechatChannelPlugin {
750
750
  case 'result.image':
751
751
  return;
752
752
  case 'activity.batch': {
753
- const text = formatItemsAsText(payload.items);
753
+ // WeChat 不发送成功的 tool_result
754
+ const filtered = payload.items.filter((i) => !(i.kind === 'tool_result' && i.ok));
755
+ const text = formatItemsAsText(filtered);
754
756
  if (text)
755
757
  await channel.sendMessage(channelId, text);
756
758
  return;
@@ -764,6 +766,7 @@ export class WechatChannelPlugin {
764
766
  case 'status.interrupted':
765
767
  case 'status.error':
766
768
  case 'status.timeout':
769
+ case 'status.progress':
767
770
  case 'custom':
768
771
  return;
769
772
  default:
@@ -510,7 +510,8 @@ export class WecomChannelPlugin {
510
510
  await channel.sendImage(channelId, payload.data);
511
511
  return;
512
512
  case 'activity.batch': {
513
- const text = formatItemsAsText(payload.items);
513
+ const filtered = payload.items.filter((i) => !(i.kind === 'tool_result' && i.ok));
514
+ const text = formatItemsAsText(filtered);
514
515
  if (text)
515
516
  await channel.sendMessage(channelId, text);
516
517
  return;
@@ -524,6 +525,7 @@ export class WecomChannelPlugin {
524
525
  case 'status.interrupted':
525
526
  case 'status.error':
526
527
  case 'status.timeout':
528
+ case 'status.progress':
527
529
  case 'custom':
528
530
  return;
529
531
  default: