evolclaw 3.1.11 → 3.3.0

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 (89) hide show
  1. package/CHANGELOG.md +41 -0
  2. package/README.md +27 -2
  3. package/dist/agents/{resolve.js → baseagent.js} +34 -5
  4. package/dist/agents/claude-runner.js +120 -27
  5. package/dist/agents/codex-app-server-client.js +364 -0
  6. package/dist/agents/codex-runner.js +1069 -141
  7. package/dist/agents/gemini-runner.js +2 -2
  8. package/dist/agents/runner-types.js +28 -0
  9. package/dist/aun/aid/control-aid.js +67 -0
  10. package/dist/aun/aid/identity.js +20 -7
  11. package/dist/aun/aid/store.js +2 -2
  12. package/dist/aun/storage/download.js +1 -1
  13. package/dist/aun/storage/upload.js +13 -1
  14. package/dist/channels/aun.js +538 -325
  15. package/dist/channels/dingtalk.js +77 -140
  16. package/dist/channels/feishu.js +98 -151
  17. package/dist/channels/qqbot.js +75 -138
  18. package/dist/channels/wechat.js +75 -136
  19. package/dist/channels/wecom.js +75 -138
  20. package/dist/cli/agent.js +44 -13
  21. package/dist/cli/index.js +207 -46
  22. package/dist/cli/init-channel.js +38 -148
  23. package/dist/cli/init.js +192 -85
  24. package/dist/cli/model.js +1 -1
  25. package/dist/cli/stats.js +558 -0
  26. package/dist/cli/version.js +87 -0
  27. package/dist/cli/watch-msg.js +5 -2
  28. package/dist/config-store.js +48 -11
  29. package/dist/core/channel-loader.js +84 -82
  30. package/dist/core/command-handler.js +754 -172
  31. package/dist/core/daemon-file-cache.js +216 -0
  32. package/dist/core/evolagent-registry.js +4 -0
  33. package/dist/core/evolagent.js +28 -23
  34. package/dist/core/interaction-router.js +8 -0
  35. package/dist/core/message/command-handler-agent-control.js +215 -0
  36. package/dist/core/message/create-status.js +67 -0
  37. package/dist/core/message/im-renderer.js +35 -13
  38. package/dist/core/message/items-formatter.js +9 -1
  39. package/dist/core/message/message-bridge.js +52 -22
  40. package/dist/core/message/message-log.js +1 -0
  41. package/dist/core/message/message-processor.js +336 -68
  42. package/dist/core/message/message-queue.js +15 -8
  43. package/dist/core/message/pending-hints.js +232 -0
  44. package/dist/core/message/response-depth.js +56 -0
  45. package/dist/core/model/model-catalog.js +1 -1
  46. package/dist/core/model/model-scope.js +40 -7
  47. package/dist/core/permission.js +9 -12
  48. package/dist/core/relation/peer-identity.js +16 -1
  49. package/dist/core/session/adapters/claude-session-file-adapter.js +48 -5
  50. package/dist/core/session/adapters/codex-session-file-adapter.js +4 -2
  51. package/dist/core/session/session-manager.js +27 -13
  52. package/dist/core/session/session-title.js +26 -0
  53. package/dist/core/stats/billing.js +151 -0
  54. package/dist/core/stats/budget.js +93 -0
  55. package/dist/core/stats/db.js +314 -0
  56. package/dist/core/stats/eck-vars.js +84 -0
  57. package/dist/core/stats/index.js +10 -0
  58. package/dist/core/stats/normalizer.js +78 -0
  59. package/dist/core/stats/query.js +760 -0
  60. package/dist/core/stats/writer.js +115 -0
  61. package/dist/core/trigger/manager.js +34 -0
  62. package/dist/core/trigger/parser.js +9 -3
  63. package/dist/core/trigger/scheduler.js +20 -17
  64. package/dist/{agents → eck}/kit-renderer.js +5 -1
  65. package/dist/{agents → eck}/manifest-engine.js +127 -35
  66. package/dist/{agents → eck}/message-renderer.js +26 -1
  67. package/dist/index.js +185 -8
  68. package/dist/ipc.js +22 -0
  69. package/dist/paths.js +7 -3
  70. package/dist/utils/cross-platform.js +23 -5
  71. package/dist/utils/ecweb-pair.js +20 -0
  72. package/dist/utils/stats.js +14 -0
  73. package/kits/docs/evolclaw/INDEX.md +3 -1
  74. package/kits/docs/evolclaw/fs-architecture.md +1215 -0
  75. package/kits/docs/evolclaw/fs.md +131 -0
  76. package/kits/docs/evolclaw/group-fs.md +209 -0
  77. package/kits/docs/evolclaw/stats.md +70 -0
  78. package/kits/docs/venues/aun-group.md +29 -6
  79. package/kits/docs/venues/group.md +5 -4
  80. package/kits/eck_manifest.json +12 -0
  81. package/kits/eck_message_manifest.json +30 -3
  82. package/kits/rules/05-venue.md +1 -1
  83. package/kits/templates/message-fragments/inject-default.md +2 -0
  84. package/kits/templates/message-fragments/item.md +1 -1
  85. package/kits/templates/system-fragments/response-depth.md +16 -0
  86. package/package.json +4 -4
  87. package/dist/agents/baseagent-normalize.js +0 -19
  88. package/dist/core/relation/peer-key.js +0 -16
  89. package/dist/utils/channel-helpers.js +0 -46
@@ -34,7 +34,7 @@ function formatItem(item) {
34
34
  case 'tool_result': {
35
35
  if (!item.ok) {
36
36
  const errMsg = item.error || (typeof item.result === 'string' ? item.result : '执行失败');
37
- return `⚠️ ${item.name}: ${errMsg}`;
37
+ return `⚠️ ${item.name}: ${capLines(errMsg, 5)}`;
38
38
  }
39
39
  return item.text ? `✓ ${item.name}: ${item.text}` : `✓ ${item.name}`;
40
40
  }
@@ -48,6 +48,14 @@ function formatItem(item) {
48
48
  return '';
49
49
  }
50
50
  }
51
+ /** 把多行文本截断到最多 maxLines 行,超出部分用省略提示替代。用于工具报错输出,避免刷屏。 */
52
+ function capLines(text, maxLines) {
53
+ const lines = text.split('\n');
54
+ if (lines.length <= maxLines)
55
+ return text;
56
+ const omitted = lines.length - maxLines;
57
+ return lines.slice(0, maxLines).join('\n') + `\n…(省略 ${omitted} 行)`;
58
+ }
51
59
  function summarizeArgs(args) {
52
60
  if (!args || typeof args !== 'object')
53
61
  return '';
@@ -109,6 +109,10 @@ export class MessageBridge {
109
109
  return;
110
110
  // 3. session 解析(使用 Channel 层填充的 chatType)
111
111
  const chatType = msg.chatType || 'private';
112
+ if (!(await this.canCreateThreadSession(channelName, msg, chatType))) {
113
+ await sendReply(msg.channelId, '群聊中无权限创建话题', msg.replyContext);
114
+ return;
115
+ }
112
116
  const metadata = {};
113
117
  // 话题会话创建时写入 replyContext(用于 threadId 路由);主会话不写(避免群聊覆盖)
114
118
  if (msg.threadId && msg.replyContext)
@@ -134,7 +138,7 @@ export class MessageBridge {
134
138
  const owningAgent = this.agentRegistry?.resolveByChannel(channelName);
135
139
  const effectiveProjectPath = owningAgent?.projectPath
136
140
  ?? 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.selfAID, msg.channelType || effectiveChannelType, msg.peerType);
141
+ const session = await this.sessionManager.getOrCreateSession(channelName, msg.channelId, effectiveProjectPath, msg.threadId, Object.keys(metadata).length ? metadata : undefined, this.extractTopicName(msg), msg.peerId, chatType, undefined, msg.selfAID, msg.channelType || effectiveChannelType, msg.peerType);
138
142
  // 4. 群聊发送者标注由消息渲染层(message-renderer)逐条承担,不再在此硬编码前缀,
139
143
  // 消息日志因此保存干净原文。policy.messagePrefix 暂保留(未来清理)。
140
144
  // 5. 构造完整消息(channel 字段存实例名,用于 session 精确匹配)
@@ -143,6 +147,7 @@ export class MessageBridge {
143
147
  channelType: msg.channelType || effectiveChannelType,
144
148
  channelId: msg.channelId, content,
145
149
  selfAID: msg.selfAID,
150
+ agentId: session.agentId,
146
151
  chatType,
147
152
  images: msg.images, timestamp: Date.now(),
148
153
  peerId: msg.peerId, peerName: msg.peerName,
@@ -151,26 +156,31 @@ export class MessageBridge {
151
156
  sameNetwork: msg.sameNetwork,
152
157
  sameEgressIp: msg.sameEgressIp,
153
158
  messageId: msg.messageId,
154
- mentions: msg.mentions, threadId: msg.threadId,
159
+ mentions: msg.mentions, mentionAids: msg.mentionAids, threadId: msg.threadId,
160
+ topicName: this.extractTopicName(msg),
155
161
  replyContext: msg.replyContext,
162
+ source: msg.source,
163
+ dispatchMode: msg.dispatchMode,
156
164
  };
157
- // 5.5 写入消息记录(入方向)
158
- const chatDir = this.sessionManager.getChatDir(session);
159
- const inboundEncrypt = msg.replyContext?.metadata?.encrypted != null ? !!(msg.replyContext.metadata.encrypted) : undefined;
160
- const inboundChatmode = msg.replyContext?.metadata?.chatmode;
161
- appendMessageLog(chatDir, buildInboundEntry({
162
- from: msg.peerId || 'unknown',
163
- to: msg.selfAID || 'self',
164
- chatType,
165
- groupId: msg.groupId ?? null,
166
- msgId: msg.messageId ?? null,
167
- content,
168
- replyTo: msg.replyContext?.replyToMessageId ?? null,
169
- permMode: session.identity?.role ?? null,
170
- timestamp: fullMessage.timestamp,
171
- encrypt: inboundEncrypt,
172
- chatmode: inboundChatmode,
173
- }));
165
+ // 5.5 写入消息记录(入方向)。
166
+ {
167
+ const chatDir = this.sessionManager.getChatDir(session);
168
+ const inboundEncrypt = msg.replyContext?.metadata?.encrypted != null ? !!(msg.replyContext.metadata.encrypted) : undefined;
169
+ const inboundChatmode = msg.replyContext?.metadata?.chatmode;
170
+ appendMessageLog(chatDir, buildInboundEntry({
171
+ from: msg.peerId || 'unknown',
172
+ to: msg.selfAID || 'self',
173
+ chatType,
174
+ groupId: msg.groupId ?? null,
175
+ msgId: msg.messageId ?? null,
176
+ content,
177
+ replyTo: msg.replyContext?.replyToMessageId ?? null,
178
+ permMode: session.identity?.role ?? null,
179
+ timestamp: fullMessage.timestamp,
180
+ encrypt: inboundEncrypt,
181
+ chatmode: inboundChatmode,
182
+ }));
183
+ }
174
184
  // 6. ACK + debounce/enqueue
175
185
  // ACK 在到达时立即做(每条独立 ACK),不等合并
176
186
  // Interrupt 模式(单聊)→ 入队前 debounce 合并
@@ -209,6 +219,7 @@ export class MessageBridge {
209
219
  static MENU_NAME_MAP = {
210
220
  pwd: '/pwd',
211
221
  session: '/session',
222
+ topic: '/topic',
212
223
  baseagent: '/baseagent',
213
224
  model: '/model',
214
225
  effort: '/effort',
@@ -218,7 +229,26 @@ export class MessageBridge {
218
229
  activity: '/activity',
219
230
  system: '/system',
220
231
  cli: '/cli',
232
+ agent: '/agent',
233
+ trigger: '/trigger',
221
234
  };
235
+ extractTopicName(msg) {
236
+ const raw = msg.topicName
237
+ ?? msg.replyContext?.title
238
+ ?? msg.replyContext?.metadata?.topicName
239
+ ?? msg.replyContext?.metadata?.title;
240
+ const name = typeof raw === 'string' ? raw.trim() : '';
241
+ return name || undefined;
242
+ }
243
+ async canCreateThreadSession(channel, msg, chatType) {
244
+ if (chatType !== 'group' || !msg.threadId)
245
+ return true;
246
+ const existing = await this.sessionManager.getThreadSession(channel, msg.channelId, msg.threadId);
247
+ if (existing)
248
+ return true;
249
+ const role = this.sessionManager.resolveIdentity(channel, msg.peerId).role;
250
+ return role === 'owner' || role === 'admin';
251
+ }
222
252
  resolveCmd(name, cmd) {
223
253
  if (cmd)
224
254
  return cmd;
@@ -276,7 +306,7 @@ export class MessageBridge {
276
306
  const { id, name, cmd } = req;
277
307
  try {
278
308
  const resolvedCmd = this.resolveCmd(name, cmd);
279
- const result = await this.cmdHandler.execMenuQuery(resolvedCmd, channel, msg.channelId, msg.peerId);
309
+ const result = await this.cmdHandler.execMenuQuery(resolvedCmd, channel, msg.channelId, msg.peerId, req.args, msg.chatType);
280
310
  if ('error' in result)
281
311
  throw { code: result.code || 'EXEC_FAILED', message: result.error };
282
312
  await this.sendMenuResponse(adapter, channel, msg.channelId, { type: 'menu.response', id, name, data: result.data }, sendReply);
@@ -292,7 +322,7 @@ export class MessageBridge {
292
322
  const { id, name, cmd } = req;
293
323
  try {
294
324
  const resolvedCmd = this.resolveCmd(name, cmd);
295
- const data = await this.cmdHandler.getSubMenuItems(resolvedCmd, channel, msg.channelId, msg.peerId) ?? [];
325
+ const data = await this.cmdHandler.getSubMenuItems(resolvedCmd, channel, msg.channelId, msg.peerId, req.args, undefined, msg.chatType) ?? [];
296
326
  await this.sendMenuResponse(adapter, channel, msg.channelId, { type: 'menu.response', id, name, data }, sendReply);
297
327
  }
298
328
  catch (err) {
@@ -326,7 +356,7 @@ export class MessageBridge {
326
356
  if (!action)
327
357
  throw { code: 'MISSING_VALUE', message: '缺少 action 参数' };
328
358
  const resolvedCmd = this.resolveCmd(name, cmd);
329
- const result = await this.cmdHandler.execMenuAction(resolvedCmd, action, args, channel, msg.channelId, msg.peerId);
359
+ const result = await this.cmdHandler.execMenuAction(resolvedCmd, action, args, channel, msg.channelId, msg.peerId, undefined, msg.chatType);
330
360
  if ('error' in result)
331
361
  throw { code: result.code || 'EXEC_FAILED', message: result.error };
332
362
  await this.sendMenuResponse(adapter, channel, msg.channelId, { type: 'menu.response', id, name, data: result.data }, sendReply);
@@ -68,6 +68,7 @@ export function buildInboundEntry(opts) {
68
68
  durationMs: null,
69
69
  encrypt: opts.encrypt,
70
70
  chatmode: opts.chatmode,
71
+ source: opts.source,
71
72
  };
72
73
  }
73
74
  export function buildOutboundEntry(opts) {