evolclaw 3.1.3 → 3.1.5

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 (100) hide show
  1. package/CHANGELOG.md +27 -0
  2. package/assets/.env.template +4 -0
  3. package/assets/config.json.template +6 -0
  4. package/assets/wechat-group-qr.jpeg +0 -0
  5. package/dist/agents/claude-runner.js +348 -156
  6. package/dist/agents/kit-renderer.js +211 -42
  7. package/dist/aun/aid/agentmd.js +75 -139
  8. package/dist/aun/aid/client.js +1 -14
  9. package/dist/aun/aid/identity.js +381 -54
  10. package/dist/aun/aid/index.js +3 -2
  11. package/dist/aun/aid/store.js +74 -0
  12. package/dist/aun/msg/p2p.js +26 -2
  13. package/dist/aun/rpc/connection.js +23 -35
  14. package/dist/channels/aun.js +92 -144
  15. package/dist/channels/dingtalk.js +1 -0
  16. package/dist/channels/feishu.js +270 -190
  17. package/dist/channels/qqbot.js +1 -0
  18. package/dist/channels/wechat.js +1 -0
  19. package/dist/channels/wecom.js +1 -0
  20. package/dist/cli/agent.js +26 -27
  21. package/dist/cli/bench.js +45 -34
  22. package/dist/cli/help.js +23 -0
  23. package/dist/cli/index.js +538 -77
  24. package/dist/cli/init-channel.js +7 -4
  25. package/dist/cli/link-rules.js +2 -1
  26. package/dist/cli/model.js +324 -0
  27. package/dist/cli/net-check.js +138 -56
  28. package/dist/cli/watch-msg.js +7 -7
  29. package/dist/cli/watch-web/debug-log.js +18 -0
  30. package/dist/cli/watch-web/server.js +306 -0
  31. package/dist/cli/watch-web/sources/aid.js +63 -0
  32. package/dist/cli/watch-web/sources/msg.js +70 -0
  33. package/dist/cli/watch-web/sources/session.js +638 -0
  34. package/dist/cli/watch-web/sources/types.js +10 -0
  35. package/dist/cli/watch-web/static/app.js +546 -0
  36. package/dist/cli/watch-web/static/index.html +54 -0
  37. package/dist/cli/watch-web/static/style.css +247 -0
  38. package/dist/core/channel-loader.js +7 -4
  39. package/dist/core/command-handler.js +87 -93
  40. package/dist/core/evolagent-registry.js +1 -1
  41. package/dist/core/evolagent.js +4 -4
  42. package/dist/core/interaction-router.js +59 -0
  43. package/dist/core/message/message-bridge.js +6 -6
  44. package/dist/core/message/message-log.js +2 -2
  45. package/dist/core/message/message-processor.js +104 -118
  46. package/dist/core/message/stream-idle-monitor.js +21 -0
  47. package/dist/core/model/model-catalog.js +215 -0
  48. package/dist/core/model/model-scope.js +250 -0
  49. package/dist/core/relation/peer-identity.js +78 -44
  50. package/dist/core/relation/peer-key.js +16 -0
  51. package/dist/core/session/session-fs-store.js +34 -55
  52. package/dist/core/session/session-key.js +24 -0
  53. package/dist/core/session/session-manager.js +312 -251
  54. package/dist/core/session/session-mapper.js +9 -4
  55. package/dist/core/trigger/manager.js +37 -0
  56. package/dist/core/trigger/scheduler.js +2 -1
  57. package/dist/index.js +10 -3
  58. package/dist/ipc.js +22 -0
  59. package/dist/paths.js +87 -16
  60. package/dist/utils/npm-ops.js +18 -11
  61. package/kits/docs/GUIDE.md +2 -2
  62. package/kits/docs/INDEX.md +11 -7
  63. package/kits/docs/channels/aun.md +56 -17
  64. package/kits/docs/channels/feishu.md +41 -12
  65. package/kits/docs/context-assembly.md +181 -0
  66. package/kits/docs/evolclaw/agent.md +49 -0
  67. package/kits/docs/evolclaw/aid.md +49 -0
  68. package/kits/docs/evolclaw/ctl.md +46 -0
  69. package/kits/docs/evolclaw/group.md +82 -0
  70. package/kits/docs/evolclaw/msg.md +86 -0
  71. package/kits/docs/evolclaw/rpc.md +35 -0
  72. package/kits/docs/evolclaw/storage.md +49 -0
  73. package/kits/docs/venues/aun-group.md +10 -0
  74. package/kits/docs/venues/aun-private.md +10 -0
  75. package/kits/docs/venues/client-desktop.md +10 -0
  76. package/kits/docs/venues/client-mobile.md +10 -0
  77. package/kits/docs/venues/feishu-group.md +13 -0
  78. package/kits/docs/venues/feishu-private.md +9 -0
  79. package/kits/docs/venues/group.md +11 -0
  80. package/kits/docs/venues/private.md +10 -0
  81. package/kits/eck_manifest.json +75 -39
  82. package/kits/rules/01-overview.md +20 -10
  83. package/kits/rules/05-venue.md +2 -2
  84. package/kits/rules/06-channel.md +30 -27
  85. package/kits/templates/system-fragments/baseagent.md +7 -1
  86. package/kits/templates/system-fragments/channel.md +4 -1
  87. package/kits/templates/system-fragments/identity.md +4 -4
  88. package/kits/templates/system-fragments/relation.md +8 -5
  89. package/kits/templates/system-fragments/session.md +27 -0
  90. package/kits/templates/system-fragments/venue.md +13 -1
  91. package/package.json +13 -6
  92. package/dist/aun/aid/lifecycle-log.js +0 -33
  93. package/dist/net-check.js +0 -640
  94. package/dist/utils/aid-lifecycle-log.js +0 -33
  95. package/dist/watch-msg.js +0 -544
  96. package/kits/docs/evolclaw/AGENT_CMD.md +0 -31
  97. package/kits/docs/evolclaw/MSG_GROUP.md +0 -30
  98. package/kits/docs/evolclaw/MSG_PRIVATE.md +0 -72
  99. package/kits/docs/evolclaw/tools.md +0 -25
  100. package/kits/templates/system-fragments/eckruntime.md +0 -14
@@ -1,14 +1,55 @@
1
1
  import { logger } from '../utils/logger.js';
2
2
  export class InteractionRouter {
3
3
  handlers = new Map();
4
+ /** sessionId → 该会话当前待应答的交互数量,用于触发 wait 生命周期钩子 */
5
+ pendingBySession = new Map();
6
+ waitHooks;
7
+ setWaitHooks(hooks) {
8
+ this.waitHooks = hooks;
9
+ }
10
+ /**
11
+ * 在 register() 之前提前标记 session 为等待状态(适用于发卡片有异步延迟的场景)。
12
+ * 必须与 unmarkWaiting() 配对使用,或后续 register() 会接管计数。
13
+ */
14
+ markWaiting(sessionId) {
15
+ this.incPending(sessionId);
16
+ }
17
+ /** 取消 markWaiting() 的占位(后续若有 register() 接管则不需调用此方法) */
18
+ unmarkWaiting(sessionId) {
19
+ this.decPending(sessionId);
20
+ }
21
+ /** 登记一个待应答交互;session 计数 0→1 时触发 onWaitStart */
22
+ incPending(sessionId) {
23
+ const next = (this.pendingBySession.get(sessionId) ?? 0) + 1;
24
+ this.pendingBySession.set(sessionId, next);
25
+ if (next === 1)
26
+ this.waitHooks?.onWaitStart(sessionId);
27
+ }
28
+ /** 注销一个待应答交互;session 计数 1→0 时触发 onWaitEnd */
29
+ decPending(sessionId) {
30
+ const cur = this.pendingBySession.get(sessionId) ?? 0;
31
+ if (cur <= 0)
32
+ return;
33
+ const next = cur - 1;
34
+ if (next === 0) {
35
+ this.pendingBySession.delete(sessionId);
36
+ this.waitHooks?.onWaitEnd(sessionId);
37
+ }
38
+ else {
39
+ this.pendingBySession.set(sessionId, next);
40
+ }
41
+ }
4
42
  register(id, sessionId, callback, opts) {
43
+ // 同 id 替换:槽位本就占用,计数不变,不触发 wait 钩子
5
44
  const existing = this.handlers.get(id);
6
45
  if (existing?.timer)
7
46
  clearTimeout(existing.timer);
47
+ const isReplacement = !!existing;
8
48
  let timer;
9
49
  if (opts?.timeoutMs && opts.timeoutMs > 0) {
10
50
  timer = setTimeout(() => {
11
51
  this.handlers.delete(id);
52
+ this.decPending(sessionId);
12
53
  logger.debug(`[InteractionRouter] Timeout for interaction: ${id}`);
13
54
  opts.onTimeout?.();
14
55
  }, opts.timeoutMs);
@@ -20,6 +61,8 @@ export class InteractionRouter {
20
61
  initiatorId: opts?.initiatorId,
21
62
  fallbackCommand: opts?.fallbackCommand,
22
63
  });
64
+ if (!isReplacement)
65
+ this.incPending(sessionId);
23
66
  }
24
67
  handle(response) {
25
68
  const handler = this.handlers.get(response.id);
@@ -28,6 +71,7 @@ export class InteractionRouter {
28
71
  if (handler.timer)
29
72
  clearTimeout(handler.timer);
30
73
  this.handlers.delete(response.id);
74
+ this.decPending(handler.sessionId);
31
75
  try {
32
76
  const result = handler.callback(response.action, response.values, response.operatorId);
33
77
  if (result && typeof result.catch === 'function') {
@@ -47,6 +91,7 @@ export class InteractionRouter {
47
91
  if (handler.timer)
48
92
  clearTimeout(handler.timer);
49
93
  this.handlers.delete(id);
94
+ this.decPending(handler.sessionId);
50
95
  }
51
96
  }
52
97
  }
@@ -56,6 +101,7 @@ export class InteractionRouter {
56
101
  if (handler.timer)
57
102
  clearTimeout(handler.timer);
58
103
  this.handlers.delete(id);
104
+ this.decPending(handler.sessionId);
59
105
  }
60
106
  }
61
107
  getPending(sessionId) {
@@ -100,6 +146,16 @@ export function renderActionAsText(req) {
100
146
  const lines = [action.title];
101
147
  if (action.body)
102
148
  lines.push(action.body);
149
+ // checkers 多选:渲染选项列表
150
+ if (action.checkers?.length) {
151
+ lines.push('');
152
+ action.checkers.forEach((chk, idx) => {
153
+ const desc = chk.description ? ` — ${chk.description}` : '';
154
+ lines.push(` ${idx + 1}. ${chk.label}${desc}`);
155
+ });
156
+ lines.push('', '回复选项编号(多选用逗号分隔),或输入自定义内容');
157
+ return lines.join('\n');
158
+ }
103
159
  if (!fb) {
104
160
  return lines.join('\n');
105
161
  }
@@ -111,5 +167,8 @@ export function renderActionAsText(req) {
111
167
  if (fb.acceptFreeText && fb.freeTextHint) {
112
168
  lines.push(` ${fb.freeTextHint}`);
113
169
  }
170
+ if (action.allowCustomInput) {
171
+ lines.push(` 或直接输入自定义内容`);
172
+ }
114
173
  return lines.join('\n');
115
174
  }
@@ -81,12 +81,12 @@ export class MessageBridge {
81
81
  logger.debug(`[MessageBridge] Command detected: "${cmdContent}", routing to handler`);
82
82
  // 命令也要记录入方向 jsonl(不创建 session,直接用 chatDirPath 计算路径)
83
83
  try {
84
- const chatDir = chatDirPath(resolvePaths().sessionsDir, msg.channelType || effectiveChannelType, msg.channelId, msg.selfId);
84
+ const chatDir = chatDirPath(resolvePaths().sessionsDir, msg.channelType || effectiveChannelType, msg.channelId, msg.selfAID || '');
85
85
  const inboundEncrypt = msg.replyContext?.metadata?.encrypted != null ? !!(msg.replyContext.metadata.encrypted) : undefined;
86
86
  const inboundChatmode = msg.replyContext?.metadata?.chatmode;
87
87
  appendMessageLog(chatDir, buildInboundEntry({
88
88
  from: msg.peerId || 'unknown',
89
- to: msg.selfId || 'self',
89
+ to: msg.selfAID || 'self',
90
90
  chatType: msg.chatType || 'private',
91
91
  groupId: msg.groupId ?? null,
92
92
  msgId: msg.messageId ?? null,
@@ -114,7 +114,7 @@ export class MessageBridge {
114
114
  if (msg.threadId && msg.replyContext)
115
115
  metadata.replyContext = msg.replyContext;
116
116
  // 写入实例名(审计 + 精确出站路由)
117
- metadata.channelName = channelName;
117
+ metadata.channelKey = channelName;
118
118
  if (chatType === 'private' && msg.peerId) {
119
119
  metadata.peerId = msg.peerId;
120
120
  if (msg.peerName)
@@ -134,7 +134,7 @@ export class MessageBridge {
134
134
  const owningAgent = this.agentRegistry?.resolveByChannel(channelName);
135
135
  const effectiveProjectPath = owningAgent?.projectPath
136
136
  ?? this.defaultProjectPath;
137
- const session = await this.sessionManager.getOrCreateSession(channelName, msg.channelId, effectiveProjectPath, msg.threadId, Object.keys(metadata).length ? metadata : undefined, undefined, msg.peerId, chatType, undefined, msg.selfId, msg.channelType || effectiveChannelType, msg.peerType);
137
+ const session = await this.sessionManager.getOrCreateSession(channelName, msg.channelId, effectiveProjectPath, msg.threadId, Object.keys(metadata).length ? metadata : undefined, undefined, msg.peerId, chatType, undefined, msg.selfAID, msg.channelType || effectiveChannelType, msg.peerType);
138
138
  // 4. 消息前缀(由 policy 决定)
139
139
  const channelInfo = this.processor.getChannelInfo?.(channelName);
140
140
  if (channelInfo?.policy) {
@@ -147,7 +147,7 @@ export class MessageBridge {
147
147
  channel: channelName,
148
148
  channelType: msg.channelType || effectiveChannelType,
149
149
  channelId: msg.channelId, content,
150
- selfId: msg.selfId,
150
+ selfAID: msg.selfAID,
151
151
  chatType,
152
152
  images: msg.images, timestamp: Date.now(),
153
153
  peerId: msg.peerId, peerName: msg.peerName,
@@ -162,7 +162,7 @@ export class MessageBridge {
162
162
  const inboundChatmode = msg.replyContext?.metadata?.chatmode;
163
163
  appendMessageLog(chatDir, buildInboundEntry({
164
164
  from: msg.peerId || 'unknown',
165
- to: msg.selfId || 'self',
165
+ to: msg.selfAID || 'self',
166
166
  chatType,
167
167
  groupId: msg.groupId ?? null,
168
168
  msgId: msg.messageId ?? null,
@@ -31,8 +31,8 @@ function formatTimestampMs(epochMs) {
31
31
  export function messageLogPath(chatDir) {
32
32
  return path.join(chatDir, MESSAGE_LOG_FILE);
33
33
  }
34
- export function resolveChatDir(sessionsDir, channelType, channelId, selfId) {
35
- return chatDirPath(sessionsDir, channelType, channelId, selfId);
34
+ export function resolveChatDir(sessionsDir, channelType, channelId, selfAID) {
35
+ return chatDirPath(sessionsDir, channelType, channelId, selfAID);
36
36
  }
37
37
  export function appendMessageLog(chatDir, entry) {
38
38
  if (entry.dir === 'in' && isDuplicate(entry.msgId)) {