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.
- package/README.md +1 -1
- package/bin/ec.js +29 -0
- package/dist/agents/baseagent-normalize.js +19 -0
- package/dist/agents/claude-runner.js +47 -12
- package/dist/agents/codex-runner.js +2 -0
- package/dist/agents/gemini-runner.js +9 -9
- package/dist/agents/kit-renderer.js +281 -0
- package/dist/aun/aid/identity.js +28 -0
- package/dist/aun/aid/index.js +1 -1
- package/dist/aun/aid/lifecycle-log.js +33 -0
- package/dist/aun/msg/group.js +3 -1
- package/dist/aun/msg/p2p.js +42 -1
- package/dist/channels/aun.js +427 -146
- package/dist/channels/dingtalk.js +3 -1
- package/dist/channels/feishu.js +128 -7
- package/dist/channels/qqbot.js +3 -1
- package/dist/channels/wechat.js +4 -1
- package/dist/channels/wecom.js +3 -1
- package/dist/cli/bench.js +1219 -0
- package/dist/cli/index.js +418 -40
- package/dist/cli/init.js +3 -4
- package/dist/cli/link-rules.js +245 -0
- package/dist/cli/net-check.js +640 -0
- package/dist/cli/watch-msg.js +666 -0
- package/dist/config-store.js +82 -5
- package/dist/core/channel-loader.js +23 -10
- package/dist/core/command-handler.js +127 -99
- package/dist/core/evolagent.js +5 -10
- package/dist/core/message/im-renderer.js +93 -48
- package/dist/core/message/items-formatter.js +11 -4
- package/dist/core/message/message-bridge.js +11 -2
- package/dist/core/message/message-log.js +8 -1
- package/dist/core/message/message-processor.js +194 -127
- package/dist/core/message/message-queue.js +10 -3
- package/dist/core/permission.js +95 -3
- package/dist/core/relation/peer-identity.js +161 -0
- package/dist/core/session/session-manager.js +103 -65
- package/dist/core/trigger/manager.js +16 -0
- package/dist/core/trigger/parser.js +110 -0
- package/dist/core/trigger/scheduler.js +7 -1
- package/dist/data/error-dict.json +118 -0
- package/dist/eck/baseagent-caps.js +18 -0
- package/dist/eck/detect.js +47 -0
- package/dist/eck/init.js +77 -0
- package/dist/eck/rules-loader.js +28 -0
- package/dist/index.js +186 -19
- package/dist/net-check.js +640 -0
- package/dist/paths.js +31 -40
- package/dist/utils/aid-lifecycle-log.js +33 -0
- package/dist/utils/atomic-write.js +10 -0
- package/dist/utils/cross-platform.js +17 -8
- package/dist/utils/error-utils.js +27 -15
- package/dist/utils/instance-registry.js +6 -5
- package/dist/utils/log-writer.js +2 -1
- package/dist/utils/logger.js +10 -0
- package/dist/utils/npm-ops.js +35 -3
- package/dist/utils/process-introspect.js +16 -38
- package/dist/utils/stats.js +216 -2
- package/dist/watch-msg.js +26 -11
- package/evolclaw-install-aun.md +14 -2
- package/kits/docs/GUIDE.md +20 -0
- package/kits/docs/INDEX.md +52 -0
- package/kits/docs/aun/CHEATSHEET.md +17 -0
- package/kits/docs/aun/SYNC_PROTOCOL.md +15 -0
- package/kits/docs/channels/feishu.md +27 -0
- package/kits/docs/eck_templates/GUIDE.template.md +22 -0
- package/kits/docs/eck_templates/INDEX.template.md +28 -0
- package/kits/docs/eck_templates/path-registry.template.md +33 -0
- package/kits/docs/eck_templates/runtime.template.md +19 -0
- package/kits/docs/evolclaw/MSG_GROUP.md +30 -0
- package/kits/docs/evolclaw/MSG_PRIVATE.md +72 -0
- package/kits/docs/identity/AID_PROFILE_SPEC.md +27 -0
- package/kits/docs/identity/PATH_OPS.md +16 -0
- package/kits/docs/identity/ROLE_DETAIL.md +20 -0
- package/kits/docs/path-registry.md +43 -0
- package/kits/eck_manifest.json +95 -0
- package/kits/rules/01-overview.md +120 -0
- package/kits/rules/02-navigation.md +75 -0
- package/kits/rules/03-identity.md +34 -0
- package/kits/rules/04-relation.md +49 -0
- package/kits/rules/05-venue.md +45 -0
- package/kits/rules/06-channel.md +73 -0
- package/kits/templates/system-fragments/baseagent.md +2 -0
- package/kits/templates/system-fragments/channel.md +10 -0
- package/kits/templates/system-fragments/identity.md +12 -0
- package/kits/templates/system-fragments/relation.md +9 -0
- package/kits/templates/system-fragments/runtime.md +19 -0
- package/kits/templates/system-fragments/venue.md +5 -0
- package/package.json +7 -5
- package/dist/agents/templates.js +0 -122
- package/dist/data/prompts.md +0 -137
- package/kits/aun/meta.md +0 -25
- package/kits/aun/role.md +0 -25
- package/kits/templates/group.md +0 -20
- package/kits/templates/private.md +0 -9
- package/kits/templates/system-fragments/personal-context.md +0 -3
- package/kits/templates/system-fragments/self-intro.md +0 -5
- package/kits/templates/system-fragments/speaker-intro.md +0 -5
- package/kits/templates/system-fragments/venue-intro.md +0 -5
- /package/kits/{channels → docs/channels}/aun.md +0 -0
- /package/kits/{evolclaw/commands.md → docs/evolclaw/AGENT_CMD.md} +0 -0
- /package/kits/{evolclaw → docs/evolclaw}/self-summary.md +0 -0
- /package/kits/{evolclaw → docs/evolclaw}/tools.md +0 -0
- /package/kits/{evolclaw → docs/identity}/identity-tools.md +0 -0
package/dist/core/evolagent.js
CHANGED
|
@@ -150,17 +150,12 @@ export class EvolAgent {
|
|
|
150
150
|
this.persist();
|
|
151
151
|
}
|
|
152
152
|
// ── ShowActivities ────────────────────────────────────────────────────
|
|
153
|
-
getShowActivities(
|
|
154
|
-
|
|
155
|
-
return inst?.showActivities ?? this.merged.show_activities ?? 'all';
|
|
153
|
+
getShowActivities(_channelKey) {
|
|
154
|
+
return this.merged.show_activities ?? 'all';
|
|
156
155
|
}
|
|
157
|
-
setShowActivities(
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
logger.warn(`[EvolAgent ${this.aid}] setShowActivities: channel "${channelKey}" not found`);
|
|
161
|
-
return;
|
|
162
|
-
}
|
|
163
|
-
inst.showActivities = mode;
|
|
156
|
+
setShowActivities(_channelKey, mode) {
|
|
157
|
+
this.rawAgent.show_activities = mode;
|
|
158
|
+
this.merged.show_activities = mode;
|
|
164
159
|
this.persist();
|
|
165
160
|
}
|
|
166
161
|
// ── Baseagent 字段写入 ────────────────────────────────────────────────
|
|
@@ -1,7 +1,23 @@
|
|
|
1
1
|
import { logger } from '../../utils/logger.js';
|
|
2
|
+
import { summarizeToolInput } from '../permission.js';
|
|
2
3
|
import fs from 'fs';
|
|
3
4
|
import path from 'path';
|
|
4
5
|
import { resolvePaths } from '../../paths.js';
|
|
6
|
+
/**
|
|
7
|
+
* 检测是否为上下文过长错误
|
|
8
|
+
* 统一的检测逻辑,覆盖所有已知的错误文本模式
|
|
9
|
+
*/
|
|
10
|
+
function isContextTooLongError(text) {
|
|
11
|
+
if (!text)
|
|
12
|
+
return false;
|
|
13
|
+
const lower = text.toLowerCase();
|
|
14
|
+
return (lower.includes('prompt is too long') ||
|
|
15
|
+
lower.includes('input is too long') ||
|
|
16
|
+
lower.includes('context too long') ||
|
|
17
|
+
lower.includes('context limit') ||
|
|
18
|
+
lower.includes('context_length_exceeded') ||
|
|
19
|
+
text.includes('上下文过长'));
|
|
20
|
+
}
|
|
5
21
|
let diagStream = null;
|
|
6
22
|
function getDiagStream() {
|
|
7
23
|
if (!diagStream) {
|
|
@@ -29,8 +45,8 @@ export class IMRenderer {
|
|
|
29
45
|
diagEnabled;
|
|
30
46
|
/** 串行发送队列:保证消息按序到达 */
|
|
31
47
|
sendChain = Promise.resolve();
|
|
32
|
-
/** proactive:是否已发过
|
|
33
|
-
|
|
48
|
+
/** proactive:是否已发过 text 文本(用于去重 complete.result) */
|
|
49
|
+
hasEmittedText = false;
|
|
34
50
|
/** 自增 callId 兜底(runner 没提供时用) */
|
|
35
51
|
syntheticCallSeq = 0;
|
|
36
52
|
constructor(opts) {
|
|
@@ -75,7 +91,34 @@ export class IMRenderer {
|
|
|
75
91
|
}
|
|
76
92
|
/** 是否有 pending 内容 */
|
|
77
93
|
hasContent() {
|
|
78
|
-
return this.textBuffer.length > 0 || this.itemsQueue.some(it => it.kind !== '
|
|
94
|
+
return this.textBuffer.length > 0 || this.itemsQueue.some(it => it.kind !== 'text');
|
|
95
|
+
}
|
|
96
|
+
/** 是否有待发送的文本 */
|
|
97
|
+
hasTextPending() {
|
|
98
|
+
return this.textBuffer.length > 0;
|
|
99
|
+
}
|
|
100
|
+
/** flush 当前 textBuffer 作为独立的 result.text(非 final),然后清空 buffer */
|
|
101
|
+
async flushText() {
|
|
102
|
+
if (this.opts.envelope.chatmode === 'proactive')
|
|
103
|
+
return;
|
|
104
|
+
if (this.textBuffer.length === 0)
|
|
105
|
+
return;
|
|
106
|
+
if (this.timer) {
|
|
107
|
+
clearTimeout(this.timer);
|
|
108
|
+
this.timer = undefined;
|
|
109
|
+
}
|
|
110
|
+
const text = this.textBuffer;
|
|
111
|
+
this.textBuffer = '';
|
|
112
|
+
// 清掉 itemsQueue 中的 text items(已发出)
|
|
113
|
+
this.itemsQueue = this.itemsQueue.filter(it => it.kind !== 'text');
|
|
114
|
+
const payload = { kind: 'result.text', text, isFinal: false };
|
|
115
|
+
this.sentContent = true;
|
|
116
|
+
this.sendChain = this.sendChain
|
|
117
|
+
.then(() => this.opts.send(payload))
|
|
118
|
+
.catch(e => logger.warn('[IMRenderer] flushText send failed:', e));
|
|
119
|
+
await this.sendChain;
|
|
120
|
+
this.lastFlush = Date.now();
|
|
121
|
+
this.flushCount++;
|
|
79
122
|
}
|
|
80
123
|
/** 是否已发送过内容(用于决定最终 flush 是否带 isFinal 标题) */
|
|
81
124
|
hasSentContent() {
|
|
@@ -92,27 +135,28 @@ export class IMRenderer {
|
|
|
92
135
|
/** 从 buffer 中移除指定 pattern(用于文件标记预处理) */
|
|
93
136
|
stripFromBuffer(pattern) {
|
|
94
137
|
this.textBuffer = this.textBuffer.replace(pattern, '').trim();
|
|
95
|
-
// itemsQueue 中的
|
|
138
|
+
// itemsQueue 中的 text items 也同步过滤
|
|
96
139
|
for (const item of this.itemsQueue) {
|
|
97
|
-
if (item.kind === '
|
|
140
|
+
if (item.kind === 'text') {
|
|
98
141
|
item.text = item.text.replace(pattern, '');
|
|
99
142
|
}
|
|
100
143
|
}
|
|
101
144
|
}
|
|
102
145
|
// ── 文本/活动注入(替代 StreamFlusher.addText/addActivity)──
|
|
103
146
|
/** 添加文本片段(流式 text) */
|
|
104
|
-
addText(text) {
|
|
147
|
+
addText(text, outputTokens, turn) {
|
|
148
|
+
this.emitProgress('text', outputTokens, turn);
|
|
105
149
|
if (this.opts.envelope.chatmode === 'proactive')
|
|
106
150
|
return;
|
|
107
151
|
if (!text)
|
|
108
152
|
return;
|
|
109
|
-
// 同一窗口内连续 text delta 合并到最后一个
|
|
153
|
+
// 同一窗口内连续 text delta 合并到最后一个 text item
|
|
110
154
|
const last = this.itemsQueue[this.itemsQueue.length - 1];
|
|
111
|
-
if (last && last.kind === '
|
|
155
|
+
if (last && last.kind === 'text') {
|
|
112
156
|
last.text += text;
|
|
113
157
|
}
|
|
114
158
|
else {
|
|
115
|
-
this.itemsQueue.push({ kind: '
|
|
159
|
+
this.itemsQueue.push({ kind: 'text', text });
|
|
116
160
|
}
|
|
117
161
|
this.textBuffer += text;
|
|
118
162
|
this.allText += text;
|
|
@@ -127,7 +171,8 @@ export class IMRenderer {
|
|
|
127
171
|
this.scheduleFlush();
|
|
128
172
|
}
|
|
129
173
|
/** 添加工具调用 */
|
|
130
|
-
addToolCall(name, input, callId, descText) {
|
|
174
|
+
addToolCall(name, input, callId, descText, turn, outputTokens) {
|
|
175
|
+
this.emitProgress('tool_call', outputTokens, turn);
|
|
131
176
|
if (this.opts.envelope.chatmode === 'proactive')
|
|
132
177
|
return;
|
|
133
178
|
if (this.opts.suppressActivities)
|
|
@@ -146,6 +191,7 @@ export class IMRenderer {
|
|
|
146
191
|
}
|
|
147
192
|
/** 添加工具结果 */
|
|
148
193
|
addToolResult(name, ok, result, error, callId, durationMs, descText) {
|
|
194
|
+
this.emitProgress('tool_result');
|
|
149
195
|
if (this.opts.envelope.chatmode === 'proactive')
|
|
150
196
|
return;
|
|
151
197
|
if (this.opts.suppressActivities)
|
|
@@ -241,17 +287,17 @@ export class IMRenderer {
|
|
|
241
287
|
const maxDelay = interval * 2.5;
|
|
242
288
|
return Math.max(minDelay, Math.min(maxDelay, dynamicDelay));
|
|
243
289
|
}
|
|
244
|
-
/** 仅 flush 非
|
|
290
|
+
/** 仅 flush 非 text items(text items 和 textBuffer 保留,等待下次完整 flush) */
|
|
245
291
|
async flushActivitiesInternal() {
|
|
246
|
-
const nonThinking = this.itemsQueue.filter(it => it.kind !== '
|
|
292
|
+
const nonThinking = this.itemsQueue.filter(it => it.kind !== 'text');
|
|
247
293
|
if (nonThinking.length === 0)
|
|
248
294
|
return;
|
|
249
295
|
if (this.timer) {
|
|
250
296
|
clearTimeout(this.timer);
|
|
251
297
|
this.timer = undefined;
|
|
252
298
|
}
|
|
253
|
-
// 移除已 flush 的 non-
|
|
254
|
-
this.itemsQueue = this.itemsQueue.filter(it => it.kind === '
|
|
299
|
+
// 移除已 flush 的 non-text items,保留 text items
|
|
300
|
+
this.itemsQueue = this.itemsQueue.filter(it => it.kind === 'text');
|
|
255
301
|
const payload = { kind: 'activity.batch', items: nonThinking };
|
|
256
302
|
if (this.diagEnabled)
|
|
257
303
|
diag(this.instanceId, 'flushActivitiesOnly', { itemCount: nonThinking.length });
|
|
@@ -276,13 +322,13 @@ export class IMRenderer {
|
|
|
276
322
|
if (this.opts.fileMarkerPattern) {
|
|
277
323
|
this.textBuffer = this.textBuffer.replace(this.opts.fileMarkerPattern, '').trim();
|
|
278
324
|
for (const item of this.itemsQueue) {
|
|
279
|
-
if (item.kind === '
|
|
325
|
+
if (item.kind === 'text')
|
|
280
326
|
item.text = item.text.replace(this.opts.fileMarkerPattern, '');
|
|
281
327
|
}
|
|
282
328
|
}
|
|
283
|
-
// 清掉空
|
|
329
|
+
// 清掉空 text items
|
|
284
330
|
const items = this.itemsQueue.filter(it => {
|
|
285
|
-
if (it.kind === '
|
|
331
|
+
if (it.kind === 'text')
|
|
286
332
|
return it.text.length > 0;
|
|
287
333
|
return true;
|
|
288
334
|
});
|
|
@@ -290,7 +336,6 @@ export class IMRenderer {
|
|
|
290
336
|
const finalText = isFinal ? this.textBuffer : '';
|
|
291
337
|
if (isFinal)
|
|
292
338
|
this.textBuffer = '';
|
|
293
|
-
// 非 final flush 保留 textBuffer,供最终 result.text 使用
|
|
294
339
|
if (this.diagEnabled) {
|
|
295
340
|
diag(this.instanceId, 'flush', {
|
|
296
341
|
isFinal,
|
|
@@ -300,12 +345,8 @@ export class IMRenderer {
|
|
|
300
345
|
sinceLastFlush: Date.now() - this.lastFlush,
|
|
301
346
|
});
|
|
302
347
|
}
|
|
303
|
-
// 1. interactive
|
|
304
|
-
|
|
305
|
-
let itemsForBatch = items;
|
|
306
|
-
if (isFinal) {
|
|
307
|
-
itemsForBatch = items.filter(it => it.kind !== 'thinking');
|
|
308
|
-
}
|
|
348
|
+
// 1. interactive 模式下:不发 text items(由 result.text 统一发送最终文本)
|
|
349
|
+
let itemsForBatch = items.filter(it => it.kind !== 'text');
|
|
309
350
|
if (itemsForBatch.length > 0) {
|
|
310
351
|
const payload = { kind: 'activity.batch', items: itemsForBatch };
|
|
311
352
|
this.sentContent = true;
|
|
@@ -328,22 +369,31 @@ export class IMRenderer {
|
|
|
328
369
|
this.flushCount++;
|
|
329
370
|
}
|
|
330
371
|
}
|
|
372
|
+
// ── 内部:status.progress 发送 ──
|
|
373
|
+
emitProgress(activityType, outputTokens, turn) {
|
|
374
|
+
const payload = { kind: 'status.progress', metadata: { activityType, ...(turn != null && { turn }), ...(outputTokens != null && { outputTokens }) } };
|
|
375
|
+
this.opts.send(payload).catch(() => { });
|
|
376
|
+
}
|
|
331
377
|
// ── 内部:proactive 模式(逐事件 activity.batch[1 item]) ──
|
|
332
378
|
emitProactive(event) {
|
|
333
379
|
// 对齐 interactive 的 dedup:流式 text 已推过时,complete.result 不再重复发 summary
|
|
334
380
|
if (event.type === 'complete' &&
|
|
335
381
|
!event.isError &&
|
|
336
382
|
event.result &&
|
|
337
|
-
this.
|
|
383
|
+
this.hasEmittedText) {
|
|
338
384
|
return;
|
|
339
385
|
}
|
|
340
386
|
const item = this.mapEventToItem(event);
|
|
341
387
|
if (!item)
|
|
342
388
|
return;
|
|
343
|
-
if (item.kind === '
|
|
344
|
-
this.
|
|
389
|
+
if (item.kind === 'text') {
|
|
390
|
+
this.hasEmittedText = true;
|
|
345
391
|
this.allText += item.text;
|
|
346
392
|
}
|
|
393
|
+
const outputTokens = event.outputTokens;
|
|
394
|
+
const turn = event.turn;
|
|
395
|
+
const activityType = item.kind === 'text' ? 'text' : item.kind === 'tool_call' ? 'tool_call' : 'tool_result';
|
|
396
|
+
this.emitProgress(activityType, outputTokens, turn);
|
|
347
397
|
const payload = { kind: 'activity.batch', items: [item] };
|
|
348
398
|
// fire-and-forget
|
|
349
399
|
this.opts.send(payload).catch(err => {
|
|
@@ -355,9 +405,9 @@ export class IMRenderer {
|
|
|
355
405
|
case 'text':
|
|
356
406
|
if (!event.text)
|
|
357
407
|
return null;
|
|
358
|
-
return { kind: '
|
|
408
|
+
return { kind: 'text', text: event.text };
|
|
359
409
|
case 'tool_use': {
|
|
360
|
-
const desc =
|
|
410
|
+
const desc = summarizeToolInput(event.name, event.input || {});
|
|
361
411
|
return {
|
|
362
412
|
kind: 'tool_call',
|
|
363
413
|
call_id: event.callId || this.synthCallId(),
|
|
@@ -407,9 +457,20 @@ export class IMRenderer {
|
|
|
407
457
|
duration_ms: event.durationMs,
|
|
408
458
|
};
|
|
409
459
|
}
|
|
410
|
-
case 'error':
|
|
460
|
+
case 'error': {
|
|
461
|
+
// 上下文过长错误不输出(留给外层 auto-compact 处理)
|
|
462
|
+
if (isContextTooLongError(event.error || ''))
|
|
463
|
+
return null;
|
|
411
464
|
return { kind: 'notice', text: event.error, severity: 'warn' };
|
|
412
|
-
|
|
465
|
+
}
|
|
466
|
+
case 'complete': {
|
|
467
|
+
// 上下文过长错误不输出(留给外层 auto-compact 处理)
|
|
468
|
+
const hasContextError = event.terminalReason === 'prompt_too_long'
|
|
469
|
+
|| isContextTooLongError(event.errors?.join(' ') || '')
|
|
470
|
+
|| isContextTooLongError(event.result || '');
|
|
471
|
+
if (event.isError && hasContextError) {
|
|
472
|
+
return null;
|
|
473
|
+
}
|
|
413
474
|
if (event.isError) {
|
|
414
475
|
const errText = event.errors?.join('; ') || event.result || '任务失败';
|
|
415
476
|
return {
|
|
@@ -429,6 +490,7 @@ export class IMRenderer {
|
|
|
429
490
|
};
|
|
430
491
|
}
|
|
431
492
|
return null;
|
|
493
|
+
}
|
|
432
494
|
case 'session_id':
|
|
433
495
|
case 'state_changed':
|
|
434
496
|
case 'status':
|
|
@@ -437,23 +499,6 @@ export class IMRenderer {
|
|
|
437
499
|
return null;
|
|
438
500
|
}
|
|
439
501
|
}
|
|
440
|
-
summarizeInput(input, toolName) {
|
|
441
|
-
if (!input || typeof input !== 'object')
|
|
442
|
-
return '';
|
|
443
|
-
if (toolName === 'Bash' && typeof input.command === 'string') {
|
|
444
|
-
const cmd = input.command;
|
|
445
|
-
if (cmd.includes('evolclaw ctl send') || cmd.includes('evolclaw ctl file')) {
|
|
446
|
-
return cmd;
|
|
447
|
-
}
|
|
448
|
-
}
|
|
449
|
-
return (input.description ||
|
|
450
|
-
input.file_path ||
|
|
451
|
-
input.pattern ||
|
|
452
|
-
(typeof input.command === 'string' ? input.command.substring(0, 80) : '') ||
|
|
453
|
-
(typeof input.prompt === 'string' ? input.prompt.substring(0, 80) : '') ||
|
|
454
|
-
(typeof input.query === 'string' ? input.query.substring(0, 80) : '') ||
|
|
455
|
-
'');
|
|
456
|
-
}
|
|
457
502
|
stringifyResult(result) {
|
|
458
503
|
if (result === null || result === undefined)
|
|
459
504
|
return '';
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
*/
|
|
6
6
|
export function formatItemsAsText(items) {
|
|
7
7
|
if (!items || items.length === 0)
|
|
8
|
-
return '';
|
|
8
|
+
return ''; // early exit
|
|
9
9
|
const lines = [];
|
|
10
10
|
for (const item of items) {
|
|
11
11
|
const line = formatItem(item);
|
|
@@ -16,20 +16,27 @@ export function formatItemsAsText(items) {
|
|
|
16
16
|
}
|
|
17
17
|
function formatItem(item) {
|
|
18
18
|
switch (item.kind) {
|
|
19
|
-
case '
|
|
19
|
+
case 'text':
|
|
20
20
|
return item.text;
|
|
21
21
|
case 'reasoning':
|
|
22
22
|
return `💭 ${item.text}`;
|
|
23
23
|
case 'tool_call': {
|
|
24
24
|
const desc = item.text || summarizeArgs(item.arguments);
|
|
25
|
-
|
|
25
|
+
if (!desc)
|
|
26
|
+
return `🔧 ${item.name}`;
|
|
27
|
+
// 多行 desc(如 Edit diff):第一行跟工具名同行,代码块从新行开始
|
|
28
|
+
if (desc.includes('\n')) {
|
|
29
|
+
const nlIdx = desc.indexOf('\n');
|
|
30
|
+
return `🔧 ${item.name} ${desc.slice(0, nlIdx)}\n${desc.slice(nlIdx + 1)}`;
|
|
31
|
+
}
|
|
32
|
+
return `🔧 ${item.name}: ${desc}`;
|
|
26
33
|
}
|
|
27
34
|
case 'tool_result': {
|
|
28
35
|
if (!item.ok) {
|
|
29
36
|
const errMsg = item.error || (typeof item.result === 'string' ? item.result : '执行失败');
|
|
30
37
|
return `⚠️ ${item.name}: ${errMsg}`;
|
|
31
38
|
}
|
|
32
|
-
return item.text ?
|
|
39
|
+
return item.text ? `✓ ${item.name}: ${item.text}` : `✓ ${item.name}`;
|
|
33
40
|
}
|
|
34
41
|
case 'progress':
|
|
35
42
|
return `⏳ ${item.text}`;
|
|
@@ -63,6 +63,8 @@ export class MessageBridge {
|
|
|
63
63
|
onMessage(async (msg) => {
|
|
64
64
|
try {
|
|
65
65
|
let content = msg.content.trim();
|
|
66
|
+
// 渠道入站日志
|
|
67
|
+
logger.channelIn({ channel: channelName, channelId: msg.channelId, peerId: msg.peerId, peerName: msg.peerName, chatType: msg.chatType, msgId: msg.messageId, threadId: msg.threadId, content, images: msg.images?.length ?? 0, mentions: msg.mentions, replyContext: msg.replyContext });
|
|
66
68
|
// 0. 自定义消息快速路径(menu.query 等)
|
|
67
69
|
if (await this.handleCustomPayload(content, channelName, msg, sendReply, adapter))
|
|
68
70
|
return;
|
|
@@ -75,7 +77,10 @@ export class MessageBridge {
|
|
|
75
77
|
if (this.cmdHandler.isCommand(cmdContent)) {
|
|
76
78
|
logger.debug(`[MessageBridge] Command detected: "${cmdContent}", routing to handler`);
|
|
77
79
|
}
|
|
78
|
-
if (await this.handleCommand(cmdContent, channelName, msg.channelId, (text) =>
|
|
80
|
+
if (await this.handleCommand(cmdContent, channelName, msg.channelId, (text) => {
|
|
81
|
+
logger.channelOut({ channel: channelName, channelId: msg.channelId, taskId: `cmd-${msg.messageId || Date.now()}`, payload: { kind: 'command.result', text } });
|
|
82
|
+
return sendReply(msg.channelId, text, msg.replyContext);
|
|
83
|
+
}, msg.peerId, msg.threadId, msg.chatType, msg.source, msg.replyContext))
|
|
79
84
|
return;
|
|
80
85
|
// 3. session 解析(使用 Channel 层填充的 chatType)
|
|
81
86
|
const chatType = msg.chatType || 'private';
|
|
@@ -104,7 +109,7 @@ export class MessageBridge {
|
|
|
104
109
|
const owningAgent = this.agentRegistry?.resolveByChannel(channelName);
|
|
105
110
|
const effectiveProjectPath = owningAgent?.projectPath
|
|
106
111
|
?? this.defaultProjectPath;
|
|
107
|
-
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);
|
|
112
|
+
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);
|
|
108
113
|
// 4. 消息前缀(由 policy 决定)
|
|
109
114
|
const channelInfo = this.processor.getChannelInfo?.(channelName);
|
|
110
115
|
if (channelInfo?.policy) {
|
|
@@ -128,6 +133,8 @@ export class MessageBridge {
|
|
|
128
133
|
};
|
|
129
134
|
// 5.5 写入消息记录(入方向)
|
|
130
135
|
const chatDir = this.sessionManager.getChatDir(session);
|
|
136
|
+
const inboundEncrypt = msg.replyContext?.metadata?.encrypted != null ? !!(msg.replyContext.metadata.encrypted) : undefined;
|
|
137
|
+
const inboundChatmode = msg.replyContext?.metadata?.chatmode;
|
|
131
138
|
appendMessageLog(chatDir, buildInboundEntry({
|
|
132
139
|
from: msg.peerId || 'unknown',
|
|
133
140
|
to: msg.selfId || 'self',
|
|
@@ -138,6 +145,8 @@ export class MessageBridge {
|
|
|
138
145
|
replyTo: msg.replyContext?.replyToMessageId ?? null,
|
|
139
146
|
permMode: session.identity?.role ?? null,
|
|
140
147
|
timestamp: fullMessage.timestamp,
|
|
148
|
+
encrypt: inboundEncrypt,
|
|
149
|
+
chatmode: inboundChatmode,
|
|
141
150
|
}));
|
|
142
151
|
// 6. ACK + debounce/enqueue
|
|
143
152
|
// ACK 在到达时立即做(每条独立 ACK),不等合并
|
|
@@ -66,6 +66,8 @@ export function buildInboundEntry(opts) {
|
|
|
66
66
|
permMode: opts.permMode ?? null,
|
|
67
67
|
cmdParsed: isCommand ? opts.content.split(/\s/)[0] : null,
|
|
68
68
|
durationMs: null,
|
|
69
|
+
encrypt: opts.encrypt,
|
|
70
|
+
chatmode: opts.chatmode,
|
|
69
71
|
};
|
|
70
72
|
}
|
|
71
73
|
export function buildOutboundEntry(opts) {
|
|
@@ -79,7 +81,7 @@ export function buildOutboundEntry(opts) {
|
|
|
79
81
|
chatType: opts.chatType,
|
|
80
82
|
groupId: opts.groupId ?? null,
|
|
81
83
|
msgId: opts.msgId ?? null,
|
|
82
|
-
msgType: 'text',
|
|
84
|
+
msgType: opts.msgType ?? 'text',
|
|
83
85
|
content: opts.content,
|
|
84
86
|
replyTo: opts.replyTo ?? null,
|
|
85
87
|
agent: opts.agent ?? null,
|
|
@@ -87,5 +89,10 @@ export function buildOutboundEntry(opts) {
|
|
|
87
89
|
permMode: null,
|
|
88
90
|
cmdParsed: null,
|
|
89
91
|
durationMs: opts.durationMs ?? null,
|
|
92
|
+
numTurns: opts.numTurns ?? null,
|
|
93
|
+
usage: opts.usage ?? null,
|
|
94
|
+
encrypt: opts.encrypt,
|
|
95
|
+
chatmode: opts.chatmode,
|
|
96
|
+
source: opts.source ?? 'daemon',
|
|
90
97
|
};
|
|
91
98
|
}
|