evolclaw 2.1.0 → 2.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 +9 -4
- package/dist/channels/feishu.js +7 -1
- package/dist/cli.js +13 -7
- package/dist/config.js +4 -1
- package/dist/core/agent-runner.js +32 -0
- package/dist/core/command-handler.js +196 -33
- package/dist/core/message-processor.js +39 -15
- package/dist/core/session-manager.js +24 -46
- package/dist/index.js +5 -2
- package/dist/index.js.bak +340 -0
- package/dist/utils/stream-flusher.js +42 -32
- package/package.json +1 -1
|
@@ -1,4 +1,19 @@
|
|
|
1
|
-
import
|
|
1
|
+
import fs from 'fs';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import { resolvePaths } from '../paths.js';
|
|
4
|
+
// 诊断日志(按需启用,通过 config.debug.flusherDiag 控制)
|
|
5
|
+
let diagStream = null;
|
|
6
|
+
function getDiagStream() {
|
|
7
|
+
if (!diagStream) {
|
|
8
|
+
const logDir = resolvePaths().logs;
|
|
9
|
+
diagStream = fs.createWriteStream(path.join(logDir, 'flusher-diag.log'), { flags: 'a' });
|
|
10
|
+
}
|
|
11
|
+
return diagStream;
|
|
12
|
+
}
|
|
13
|
+
function diag(instanceId, action, meta = {}) {
|
|
14
|
+
const line = JSON.stringify({ ts: new Date().toISOString(), id: instanceId, action, ...meta });
|
|
15
|
+
getDiagStream().write(line + '\n');
|
|
16
|
+
}
|
|
2
17
|
/**
|
|
3
18
|
* 流式输出缓冲器
|
|
4
19
|
* 按时间窗口批量推送文本和活动事件
|
|
@@ -12,6 +27,7 @@ import { logger } from './logger.js';
|
|
|
12
27
|
* - 下限:interval(额定值)
|
|
13
28
|
* - 上限:interval * 2.5
|
|
14
29
|
*/
|
|
30
|
+
let instanceCounter = 0;
|
|
15
31
|
export class StreamFlusher {
|
|
16
32
|
send;
|
|
17
33
|
interval;
|
|
@@ -24,19 +40,27 @@ export class StreamFlusher {
|
|
|
24
40
|
fileMarkerPattern;
|
|
25
41
|
flushCount = 0;
|
|
26
42
|
messageTimestamps = [];
|
|
27
|
-
|
|
43
|
+
instanceId;
|
|
44
|
+
createTime = Date.now();
|
|
45
|
+
diagEnabled;
|
|
46
|
+
constructor(send, interval = 4000, fileMarkerPattern, diagEnabled = false) {
|
|
28
47
|
this.send = send;
|
|
29
48
|
this.interval = interval;
|
|
30
49
|
this.fileMarkerPattern = fileMarkerPattern;
|
|
50
|
+
this.diagEnabled = diagEnabled;
|
|
51
|
+
this.instanceId = `F${++instanceCounter}`;
|
|
52
|
+
if (this.diagEnabled)
|
|
53
|
+
diag(this.instanceId, 'created', { interval });
|
|
31
54
|
}
|
|
32
55
|
addText(text) {
|
|
33
56
|
this.buffer += text;
|
|
34
57
|
this.allText += text;
|
|
35
58
|
this.messageTimestamps.push(Date.now());
|
|
59
|
+
if (this.diagEnabled)
|
|
60
|
+
diag(this.instanceId, 'addText', { len: text.length, preview: text.substring(0, 60), bufLen: this.buffer.length, actCount: this.activities.length });
|
|
36
61
|
this.scheduleFlush();
|
|
37
62
|
}
|
|
38
63
|
addTextBlock(text) {
|
|
39
|
-
// 用于 assistant 事件的完整文本块,需要换行分隔
|
|
40
64
|
if (this.buffer && !this.buffer.endsWith('\n')) {
|
|
41
65
|
this.buffer += '\n\n';
|
|
42
66
|
this.allText += '\n\n';
|
|
@@ -44,79 +68,69 @@ export class StreamFlusher {
|
|
|
44
68
|
this.buffer += text;
|
|
45
69
|
this.allText += text;
|
|
46
70
|
this.messageTimestamps.push(Date.now());
|
|
71
|
+
if (this.diagEnabled)
|
|
72
|
+
diag(this.instanceId, 'addTextBlock', { len: text.length, preview: text.substring(0, 60), bufLen: this.buffer.length });
|
|
47
73
|
this.scheduleFlush();
|
|
48
74
|
}
|
|
49
75
|
addActivity(desc) {
|
|
50
76
|
this.activities.push(desc);
|
|
51
77
|
this.messageTimestamps.push(Date.now());
|
|
78
|
+
if (this.diagEnabled)
|
|
79
|
+
diag(this.instanceId, 'addActivity', { desc: desc.substring(0, 80), actCount: this.activities.length });
|
|
52
80
|
this.scheduleFlush();
|
|
53
81
|
}
|
|
54
|
-
/** 当前 buffer 中是否有待发送内容 */
|
|
55
82
|
hasContent() {
|
|
56
83
|
return this.buffer.length > 0 || this.activities.length > 0;
|
|
57
84
|
}
|
|
58
|
-
/** 是否曾经发送过任何内容 */
|
|
59
85
|
hasSentContent() {
|
|
60
86
|
return this.sentContent;
|
|
61
87
|
}
|
|
62
|
-
/** 获取完整累积文本(用于文件标记提取) */
|
|
63
88
|
getFinalText() {
|
|
64
89
|
return this.allText;
|
|
65
90
|
}
|
|
66
|
-
/** 获取当前未发送的剩余文本 */
|
|
67
91
|
getRemainingText() {
|
|
68
92
|
return this.buffer;
|
|
69
93
|
}
|
|
70
|
-
/** 从当前 buffer 中移除匹配的模式 */
|
|
71
94
|
stripFromBuffer(pattern) {
|
|
72
95
|
this.buffer = this.buffer.replace(pattern, '').trim();
|
|
73
96
|
}
|
|
74
97
|
scheduleFlush() {
|
|
75
|
-
if (this.timer)
|
|
98
|
+
if (this.timer) {
|
|
99
|
+
if (this.diagEnabled)
|
|
100
|
+
diag(this.instanceId, 'scheduleFlush:skip', { reason: 'timer_exists' });
|
|
76
101
|
return;
|
|
77
|
-
|
|
102
|
+
}
|
|
78
103
|
let targetDelay;
|
|
79
104
|
if (this.flushCount === 0) {
|
|
80
|
-
// 第1次:立即发送
|
|
81
105
|
targetDelay = 0;
|
|
82
106
|
}
|
|
83
107
|
else if (this.flushCount <= 3) {
|
|
84
|
-
// 第2-4次:半延迟
|
|
85
108
|
targetDelay = Math.ceil(this.interval / 2);
|
|
86
109
|
}
|
|
87
110
|
else if (this.messageTimestamps.length >= 5) {
|
|
88
|
-
// 第5次起:动态自适应
|
|
89
111
|
targetDelay = this.calculateDynamicDelay();
|
|
90
112
|
}
|
|
91
113
|
else {
|
|
92
|
-
// 样本不足,使用额定延迟
|
|
93
114
|
targetDelay = this.interval;
|
|
94
115
|
}
|
|
95
116
|
const elapsed = Date.now() - this.lastFlush;
|
|
96
117
|
const delay = Math.max(0, targetDelay - elapsed);
|
|
118
|
+
if (this.diagEnabled)
|
|
119
|
+
diag(this.instanceId, 'scheduleFlush:set', { flushCount: this.flushCount, targetDelay, elapsed, actualDelay: delay });
|
|
97
120
|
this.timer = setTimeout(() => this.flush(), delay);
|
|
98
121
|
}
|
|
99
|
-
/**
|
|
100
|
-
* 计算动态延迟
|
|
101
|
-
* 基于最近10条消息的平均间隔
|
|
102
|
-
*/
|
|
103
122
|
calculateDynamicDelay() {
|
|
104
|
-
// 取最近10条(或实际条数)
|
|
105
123
|
const recent = this.messageTimestamps.slice(-10);
|
|
106
|
-
// 计算平均间隔
|
|
107
124
|
const intervals = [];
|
|
108
125
|
for (let i = 1; i < recent.length; i++) {
|
|
109
126
|
intervals.push(recent[i] - recent[i - 1]);
|
|
110
127
|
}
|
|
111
|
-
if (intervals.length === 0)
|
|
128
|
+
if (intervals.length === 0)
|
|
112
129
|
return this.interval;
|
|
113
|
-
}
|
|
114
130
|
const avgInterval = intervals.reduce((a, b) => a + b) / intervals.length;
|
|
115
|
-
// 动态延迟 = 平均间隔 * 3
|
|
116
131
|
let dynamicDelay = avgInterval * 3;
|
|
117
|
-
|
|
118
|
-
const
|
|
119
|
-
const maxDelay = this.interval * 2.5; // 上限:额定值 * 2.5
|
|
132
|
+
const minDelay = this.interval;
|
|
133
|
+
const maxDelay = this.interval * 2.5;
|
|
120
134
|
return Math.max(minDelay, Math.min(maxDelay, dynamicDelay));
|
|
121
135
|
}
|
|
122
136
|
async flush(isFinal) {
|
|
@@ -133,15 +147,11 @@ export class StreamFlusher {
|
|
|
133
147
|
output += this.buffer;
|
|
134
148
|
this.buffer = '';
|
|
135
149
|
}
|
|
136
|
-
// 移除文件标记(如果配置了)
|
|
137
150
|
if (output && this.fileMarkerPattern) {
|
|
138
|
-
const before = output;
|
|
139
151
|
output = output.replace(this.fileMarkerPattern, '').trim();
|
|
140
|
-
if (before !== output) {
|
|
141
|
-
logger.debug('[StreamFlusher] Removed file markers, before length:', before.length, 'after:', output.length);
|
|
142
|
-
}
|
|
143
152
|
}
|
|
144
|
-
|
|
153
|
+
if (this.diagEnabled)
|
|
154
|
+
diag(this.instanceId, 'flush', { isFinal, outputLen: output.length, flushCount: this.flushCount, sinceLastFlush: Date.now() - this.lastFlush, preview: output.substring(0, 80) });
|
|
145
155
|
if (output) {
|
|
146
156
|
await this.send(output, isFinal);
|
|
147
157
|
this.sentContent = true;
|
package/package.json
CHANGED