evolclaw 3.2.0 → 3.4.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.
- package/CHANGELOG.md +53 -0
- package/README.md +7 -4
- package/dist/agents/{resolve.js → baseagent.js} +34 -5
- package/dist/agents/claude-runner.js +120 -31
- package/dist/agents/codex-app-server-client.js +364 -0
- package/dist/agents/codex-runner.js +1152 -140
- package/dist/agents/gemini-runner.js +2 -2
- package/dist/agents/runner-types.js +58 -0
- package/dist/aun/aid/store.js +1 -1
- package/dist/aun/outbox.js +14 -2
- package/dist/aun/storage/download.js +1 -1
- package/dist/aun/storage/upload.js +13 -1
- package/dist/channels/aun.js +869 -358
- package/dist/channels/dingtalk.js +77 -140
- package/dist/channels/feishu.js +125 -154
- package/dist/channels/qqbot.js +75 -138
- package/dist/channels/wechat.js +75 -136
- package/dist/channels/wecom.js +75 -138
- package/dist/cli/agent-command.js +591 -0
- package/dist/cli/agent.js +23 -8
- package/dist/cli/aun-commands.js +1444 -0
- package/dist/cli/ctl-command.js +78 -0
- package/dist/cli/daemon-commands.js +2707 -0
- package/dist/cli/index.js +23 -4905
- package/dist/cli/init.js +33 -6
- package/dist/cli/model.js +1 -1
- package/dist/cli/restart-monitor.js +539 -0
- package/dist/cli/stats.js +558 -0
- package/dist/cli/version.js +87 -0
- package/dist/cli/watch-logs.js +33 -0
- package/dist/cli/watch-msg.js +5 -2
- package/dist/config-store.js +12 -6
- package/dist/core/channel-loader.js +88 -83
- package/dist/core/command/command-handler.js +1189 -0
- package/dist/core/command/menu-handler.js +1478 -0
- package/dist/core/command/slash-gate.js +142 -0
- package/dist/core/command/slash-handler.js +2090 -0
- package/dist/core/evolagent-registry.js +82 -0
- package/dist/core/evolagent.js +17 -1
- package/dist/core/interaction-router.js +8 -0
- package/dist/core/message/command-handler-agent-control.js +63 -1
- package/dist/core/message/im-renderer.js +91 -51
- package/dist/core/message/items-formatter.js +9 -1
- package/dist/core/message/message-bridge.js +73 -24
- package/dist/core/message/message-log.js +1 -0
- package/dist/core/message/message-processor.js +432 -94
- package/dist/core/message/message-queue.js +70 -2
- package/dist/core/message/pending-hints.js +232 -0
- package/dist/core/model/model-catalog.js +1 -1
- package/dist/core/model/model-scope.js +2 -2
- package/dist/core/permission.js +25 -12
- package/dist/core/relation/peer-identity.js +16 -1
- package/dist/core/session/adapters/codex-session-file-adapter.js +4 -2
- package/dist/core/session/session-manager.js +86 -26
- package/dist/core/session/session-title.js +26 -0
- package/dist/core/stats/billing.js +151 -0
- package/dist/core/stats/budget.js +93 -0
- package/dist/core/stats/db.js +334 -0
- package/dist/core/stats/eck-vars.js +84 -0
- package/dist/core/stats/index.js +10 -0
- package/dist/core/stats/normalizer.js +78 -0
- package/dist/core/stats/query.js +760 -0
- package/dist/core/stats/writer.js +115 -0
- package/dist/core/trigger/manager.js +34 -0
- package/dist/core/trigger/parser.js +9 -3
- package/dist/core/trigger/scheduler.js +20 -17
- package/dist/data/error-dict.json +7 -0
- package/dist/{agents → eck}/manifest-engine.js +20 -1
- package/dist/{agents → eck}/message-renderer.js +24 -1
- package/dist/index.js +174 -9
- package/dist/ipc.js +116 -1
- package/dist/utils/cross-platform.js +58 -5
- package/dist/utils/ecweb-launch.js +49 -0
- package/dist/utils/ecweb-pair.js +20 -0
- package/dist/utils/error-utils.js +18 -5
- package/dist/utils/npm-ops.js +38 -8
- package/dist/utils/stats.js +77 -6
- package/kits/docs/evolclaw/INDEX.md +3 -1
- package/kits/docs/evolclaw/fs-architecture.md +1215 -0
- package/kits/docs/evolclaw/fs.md +131 -0
- package/kits/docs/evolclaw/group-fs.md +209 -0
- package/kits/docs/evolclaw/stats.md +70 -0
- package/kits/docs/venues/aun-group.md +29 -6
- package/kits/docs/venues/group.md +5 -4
- package/kits/eck_message_manifest.json +30 -3
- package/kits/rules/05-venue.md +1 -1
- package/kits/templates/message-fragments/inject-default.md +2 -0
- package/package.json +5 -6
- package/dist/agents/baseagent-normalize.js +0 -19
- package/dist/core/command-handler.js +0 -3876
- package/dist/core/relation/peer-key.js +0 -16
- package/dist/evolclaw-config.js +0 -11
- package/dist/utils/channel-helpers.js +0 -46
- /package/dist/core/{cache/file-cache.js → daemon-file-cache.js} +0 -0
- /package/dist/{agents → eck}/kit-renderer.js +0 -0
|
@@ -109,6 +109,14 @@ 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
|
+
// 静默丢弃:绝不向群里注入回复。
|
|
114
|
+
// 拒绝消息本身会带原 thread_id(AUN replyContext 透传),变成一条新群消息;
|
|
115
|
+
// 若发送者也是 agent,该拒绝消息又会 @ 回对方 → A 拒绝→B 收到→B 拒绝→A 收到 的无限循环。
|
|
116
|
+
// AUN 自主模式下「不响应」是合法的,因此无权限创建话题时只记日志、直接 return。
|
|
117
|
+
logger.info(`[MessageBridge] Thread creation denied (silent drop): channel=${channelName} channelId=${msg.channelId} thread=${msg.threadId} sender=${msg.peerId}`);
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
112
120
|
const metadata = {};
|
|
113
121
|
// 话题会话创建时写入 replyContext(用于 threadId 路由);主会话不写(避免群聊覆盖)
|
|
114
122
|
if (msg.threadId && msg.replyContext)
|
|
@@ -134,7 +142,7 @@ export class MessageBridge {
|
|
|
134
142
|
const owningAgent = this.agentRegistry?.resolveByChannel(channelName);
|
|
135
143
|
const effectiveProjectPath = owningAgent?.projectPath
|
|
136
144
|
?? this.defaultProjectPath;
|
|
137
|
-
const session = await this.sessionManager.getOrCreateSession(channelName, msg.channelId, effectiveProjectPath, msg.threadId, Object.keys(metadata).length ? metadata : undefined,
|
|
145
|
+
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
146
|
// 4. 群聊发送者标注由消息渲染层(message-renderer)逐条承担,不再在此硬编码前缀,
|
|
139
147
|
// 消息日志因此保存干净原文。policy.messagePrefix 暂保留(未来清理)。
|
|
140
148
|
// 5. 构造完整消息(channel 字段存实例名,用于 session 精确匹配)
|
|
@@ -143,6 +151,7 @@ export class MessageBridge {
|
|
|
143
151
|
channelType: msg.channelType || effectiveChannelType,
|
|
144
152
|
channelId: msg.channelId, content,
|
|
145
153
|
selfAID: msg.selfAID,
|
|
154
|
+
agentId: session.agentId,
|
|
146
155
|
chatType,
|
|
147
156
|
images: msg.images, timestamp: Date.now(),
|
|
148
157
|
peerId: msg.peerId, peerName: msg.peerName,
|
|
@@ -152,25 +161,30 @@ export class MessageBridge {
|
|
|
152
161
|
sameEgressIp: msg.sameEgressIp,
|
|
153
162
|
messageId: msg.messageId,
|
|
154
163
|
mentions: msg.mentions, mentionAids: msg.mentionAids, threadId: msg.threadId,
|
|
164
|
+
topicName: this.extractTopicName(msg),
|
|
155
165
|
replyContext: msg.replyContext,
|
|
166
|
+
source: msg.source,
|
|
167
|
+
dispatchMode: msg.dispatchMode,
|
|
156
168
|
};
|
|
157
|
-
// 5.5
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
169
|
+
// 5.5 写入消息记录(入方向)。
|
|
170
|
+
{
|
|
171
|
+
const chatDir = this.sessionManager.getChatDir(session);
|
|
172
|
+
const inboundEncrypt = msg.replyContext?.metadata?.encrypted != null ? !!(msg.replyContext.metadata.encrypted) : undefined;
|
|
173
|
+
const inboundChatmode = msg.replyContext?.metadata?.chatmode;
|
|
174
|
+
appendMessageLog(chatDir, buildInboundEntry({
|
|
175
|
+
from: msg.peerId || 'unknown',
|
|
176
|
+
to: msg.selfAID || 'self',
|
|
177
|
+
chatType,
|
|
178
|
+
groupId: msg.groupId ?? null,
|
|
179
|
+
msgId: msg.messageId ?? null,
|
|
180
|
+
content,
|
|
181
|
+
replyTo: msg.replyContext?.replyToMessageId ?? null,
|
|
182
|
+
permMode: session.identity?.role ?? null,
|
|
183
|
+
timestamp: fullMessage.timestamp,
|
|
184
|
+
encrypt: inboundEncrypt,
|
|
185
|
+
chatmode: inboundChatmode,
|
|
186
|
+
}));
|
|
187
|
+
}
|
|
174
188
|
// 6. ACK + debounce/enqueue
|
|
175
189
|
// ACK 在到达时立即做(每条独立 ACK),不等合并
|
|
176
190
|
// Interrupt 模式(单聊)→ 入队前 debounce 合并
|
|
@@ -209,6 +223,7 @@ export class MessageBridge {
|
|
|
209
223
|
static MENU_NAME_MAP = {
|
|
210
224
|
pwd: '/pwd',
|
|
211
225
|
session: '/session',
|
|
226
|
+
topic: '/topic',
|
|
212
227
|
baseagent: '/baseagent',
|
|
213
228
|
model: '/model',
|
|
214
229
|
effort: '/effort',
|
|
@@ -220,7 +235,33 @@ export class MessageBridge {
|
|
|
220
235
|
cli: '/cli',
|
|
221
236
|
agent: '/agent',
|
|
222
237
|
trigger: '/trigger',
|
|
238
|
+
file: '/file',
|
|
223
239
|
};
|
|
240
|
+
extractTopicName(msg) {
|
|
241
|
+
const raw = msg.topicName
|
|
242
|
+
?? msg.replyContext?.title
|
|
243
|
+
?? msg.replyContext?.metadata?.topicName
|
|
244
|
+
?? msg.replyContext?.metadata?.title;
|
|
245
|
+
const name = typeof raw === 'string' ? raw.trim() : '';
|
|
246
|
+
return name || undefined;
|
|
247
|
+
}
|
|
248
|
+
async canCreateThreadSession(channel, msg, chatType) {
|
|
249
|
+
if (chatType !== 'group' || !msg.threadId)
|
|
250
|
+
return true;
|
|
251
|
+
const existing = await this.sessionManager.getThreadSession(channel, msg.channelId, msg.threadId);
|
|
252
|
+
if (existing)
|
|
253
|
+
return true;
|
|
254
|
+
// 群话题创建权限只看「发送者在该群里的角色」(AUN 经 group.get_admins 实时查询,权威源)。
|
|
255
|
+
// 仅群 owner/admin 可建话题;member / 非成员 / 查询失败(undefined)一律 fail-closed 拒绝。
|
|
256
|
+
// 这与 bot 的 owner/admin 无关——不引入 resolveIdentity 兜底。
|
|
257
|
+
// 不暴露群角色的渠道(adapter 无 getGroupMemberRole)不受此守卫约束,放行。
|
|
258
|
+
const adapter = this.processor?.getChannelInfo?.(channel)?.adapter;
|
|
259
|
+
if (adapter?.getGroupMemberRole) {
|
|
260
|
+
const groupRole = await adapter.getGroupMemberRole(msg.channelId, msg.peerId);
|
|
261
|
+
return groupRole === 'owner' || groupRole === 'admin';
|
|
262
|
+
}
|
|
263
|
+
return true;
|
|
264
|
+
}
|
|
224
265
|
resolveCmd(name, cmd) {
|
|
225
266
|
if (cmd)
|
|
226
267
|
return cmd;
|
|
@@ -264,7 +305,7 @@ export class MessageBridge {
|
|
|
264
305
|
const { id } = req;
|
|
265
306
|
try {
|
|
266
307
|
const identity = this.sessionManager.resolveIdentity(channel, msg.peerId);
|
|
267
|
-
const data = this.cmdHandler.getMenuItems(identity.role, msg.chatType || 'private');
|
|
308
|
+
const data = this.cmdHandler.getMenuItems(identity.role, msg.chatType || 'private', msg.isControlChannel ? 'control' : 'agent');
|
|
268
309
|
await this.sendMenuResponse(adapter, channel, msg.channelId, { type: 'menu.response', id, data }, sendReply);
|
|
269
310
|
}
|
|
270
311
|
catch (err) {
|
|
@@ -278,7 +319,7 @@ export class MessageBridge {
|
|
|
278
319
|
const { id, name, cmd } = req;
|
|
279
320
|
try {
|
|
280
321
|
const resolvedCmd = this.resolveCmd(name, cmd);
|
|
281
|
-
const result = await this.cmdHandler.execMenuQuery(resolvedCmd, channel, msg.channelId, msg.peerId, req.args);
|
|
322
|
+
const result = await this.cmdHandler.execMenuQuery(resolvedCmd, channel, msg.channelId, msg.peerId, req.args, msg.chatType, msg.isControlChannel ?? false);
|
|
282
323
|
if ('error' in result)
|
|
283
324
|
throw { code: result.code || 'EXEC_FAILED', message: result.error };
|
|
284
325
|
await this.sendMenuResponse(adapter, channel, msg.channelId, { type: 'menu.response', id, name, data: result.data }, sendReply);
|
|
@@ -294,7 +335,7 @@ export class MessageBridge {
|
|
|
294
335
|
const { id, name, cmd } = req;
|
|
295
336
|
try {
|
|
296
337
|
const resolvedCmd = this.resolveCmd(name, cmd);
|
|
297
|
-
const data = await this.cmdHandler.getSubMenuItems(resolvedCmd, channel, msg.channelId, msg.peerId, req.args) ?? [];
|
|
338
|
+
const data = await this.cmdHandler.getSubMenuItems(resolvedCmd, channel, msg.channelId, msg.peerId, req.args, undefined, msg.chatType, msg.isControlChannel ?? false) ?? [];
|
|
298
339
|
await this.sendMenuResponse(adapter, channel, msg.channelId, { type: 'menu.response', id, name, data }, sendReply);
|
|
299
340
|
}
|
|
300
341
|
catch (err) {
|
|
@@ -305,12 +346,12 @@ export class MessageBridge {
|
|
|
305
346
|
}
|
|
306
347
|
}
|
|
307
348
|
async handleMenuUpdate(req, channel, msg, adapter, sendReply) {
|
|
308
|
-
const { id, name, cmd, value } = req;
|
|
349
|
+
const { id, name, cmd, value, args } = req;
|
|
309
350
|
try {
|
|
310
351
|
if (!value)
|
|
311
352
|
throw { code: 'MISSING_VALUE', message: '缺少 value 参数' };
|
|
312
353
|
const resolvedCmd = this.resolveCmd(name, cmd);
|
|
313
|
-
const result = await this.cmdHandler.execMenuUpdate(resolvedCmd, value, channel, msg.channelId, msg.peerId);
|
|
354
|
+
const result = await this.cmdHandler.execMenuUpdate(resolvedCmd, value, channel, msg.channelId, msg.peerId, undefined, msg.isControlChannel ?? false, args);
|
|
314
355
|
if ('error' in result)
|
|
315
356
|
throw { code: result.code || 'EXEC_FAILED', message: result.error };
|
|
316
357
|
await this.sendMenuResponse(adapter, channel, msg.channelId, { type: 'menu.response', id, name, data: result.data }, sendReply);
|
|
@@ -328,7 +369,7 @@ export class MessageBridge {
|
|
|
328
369
|
if (!action)
|
|
329
370
|
throw { code: 'MISSING_VALUE', message: '缺少 action 参数' };
|
|
330
371
|
const resolvedCmd = this.resolveCmd(name, cmd);
|
|
331
|
-
const result = await this.cmdHandler.execMenuAction(resolvedCmd, action, args, channel, msg.channelId, msg.peerId);
|
|
372
|
+
const result = await this.cmdHandler.execMenuAction(resolvedCmd, action, args, channel, msg.channelId, msg.peerId, undefined, msg.chatType, id, msg.isControlChannel ?? false);
|
|
332
373
|
if ('error' in result)
|
|
333
374
|
throw { code: result.code || 'EXEC_FAILED', message: result.error };
|
|
334
375
|
await this.sendMenuResponse(adapter, channel, msg.channelId, { type: 'menu.response', id, name, data: result.data }, sendReply);
|
|
@@ -445,4 +486,12 @@ export class MessageBridge {
|
|
|
445
486
|
d.dispose();
|
|
446
487
|
this.debouncers.clear();
|
|
447
488
|
}
|
|
489
|
+
/** 注销单个渠道的 debouncer(热重载断开渠道时调用) */
|
|
490
|
+
removeChannel(channelName) {
|
|
491
|
+
const d = this.debouncers.get(channelName);
|
|
492
|
+
if (d) {
|
|
493
|
+
d.dispose();
|
|
494
|
+
this.debouncers.delete(channelName);
|
|
495
|
+
}
|
|
496
|
+
}
|
|
448
497
|
}
|