evolclaw 2.0.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/README.md +191 -0
- package/bin/evolclaw +10 -0
- package/data/evolclaw.sample.json +39 -0
- package/dist/channels/aun.js +28 -0
- package/dist/channels/feishu.js +452 -0
- package/dist/cli.js +759 -0
- package/dist/config.js +81 -0
- package/dist/core/agent-runner.js +326 -0
- package/dist/core/command-handler.js +823 -0
- package/dist/core/message-cache.js +56 -0
- package/dist/core/message-processor.js +516 -0
- package/dist/core/message-queue.js +110 -0
- package/dist/core/message-stream.js +59 -0
- package/dist/core/session-manager.js +803 -0
- package/dist/index.js +239 -0
- package/dist/paths.js +45 -0
- package/dist/types.js +1 -0
- package/dist/utils/error-utils.js +54 -0
- package/dist/utils/init.js +352 -0
- package/dist/utils/logger.js +47 -0
- package/dist/utils/markdown-to-feishu.js +38 -0
- package/dist/utils/permission.js +36 -0
- package/dist/utils/session-file-health.js +67 -0
- package/dist/utils/stream-flusher.js +151 -0
- package/dist/utils/stream-idle-monitor.js +103 -0
- package/package.json +38 -0
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 事件流空闲监控器
|
|
3
|
+
* 记录事件流执行上下文,分级响应空闲状态
|
|
4
|
+
*/
|
|
5
|
+
// 分级倍率
|
|
6
|
+
const NOTIFY_MULTIPLIER = 1; // 1× idleMs
|
|
7
|
+
const WARN_MULTIPLIER = 2.5; // 2.5× idleMs
|
|
8
|
+
const KILL_MULTIPLIER = 5; // 5× idleMs
|
|
9
|
+
export class StreamIdleMonitor {
|
|
10
|
+
state;
|
|
11
|
+
triggeredLevels = new Set();
|
|
12
|
+
idleMs;
|
|
13
|
+
constructor(idleMs) {
|
|
14
|
+
this.idleMs = idleMs;
|
|
15
|
+
this.state = {
|
|
16
|
+
lastEventType: '',
|
|
17
|
+
lastEventTime: Date.now(),
|
|
18
|
+
lastToolName: undefined,
|
|
19
|
+
lastToolStartTime: undefined,
|
|
20
|
+
totalEvents: 0,
|
|
21
|
+
totalToolCalls: 0,
|
|
22
|
+
hasReceivedText: false,
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* 记录 SDK 事件,更新状态并重置空闲计时
|
|
27
|
+
*/
|
|
28
|
+
recordEvent(type, toolName) {
|
|
29
|
+
this.state.lastEventType = type;
|
|
30
|
+
this.state.lastEventTime = Date.now();
|
|
31
|
+
this.state.totalEvents++;
|
|
32
|
+
if (toolName) {
|
|
33
|
+
this.state.lastToolName = toolName;
|
|
34
|
+
this.state.lastToolStartTime = Date.now();
|
|
35
|
+
this.state.totalToolCalls++;
|
|
36
|
+
}
|
|
37
|
+
if (type === 'text_delta' || type === 'result') {
|
|
38
|
+
this.state.hasReceivedText = true;
|
|
39
|
+
}
|
|
40
|
+
// 收到新事件,重置已触发级别
|
|
41
|
+
this.triggeredLevels.clear();
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* 检查空闲状态,返回 null(未空闲)或分级结果
|
|
45
|
+
*/
|
|
46
|
+
check() {
|
|
47
|
+
const now = Date.now();
|
|
48
|
+
const idleDuration = now - this.state.lastEventTime;
|
|
49
|
+
const notifyThreshold = this.idleMs * NOTIFY_MULTIPLIER;
|
|
50
|
+
const warnThreshold = this.idleMs * WARN_MULTIPLIER;
|
|
51
|
+
const killThreshold = this.idleMs * KILL_MULTIPLIER;
|
|
52
|
+
// Check from lowest to highest — return first untriggered level
|
|
53
|
+
if (idleDuration >= notifyThreshold && !this.triggeredLevels.has('notify')) {
|
|
54
|
+
this.triggeredLevels.add('notify');
|
|
55
|
+
return {
|
|
56
|
+
action: 'notify',
|
|
57
|
+
idleSec: Math.round(idleDuration / 1000),
|
|
58
|
+
state: { ...this.state },
|
|
59
|
+
message: this.buildMessage('notify', idleDuration),
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
if (idleDuration >= warnThreshold && !this.triggeredLevels.has('warn')) {
|
|
63
|
+
this.triggeredLevels.add('warn');
|
|
64
|
+
return {
|
|
65
|
+
action: 'warn',
|
|
66
|
+
idleSec: Math.round(idleDuration / 1000),
|
|
67
|
+
state: { ...this.state },
|
|
68
|
+
message: this.buildMessage('warn', idleDuration),
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
if (idleDuration >= killThreshold && !this.triggeredLevels.has('kill')) {
|
|
72
|
+
this.triggeredLevels.add('kill');
|
|
73
|
+
return {
|
|
74
|
+
action: 'kill',
|
|
75
|
+
idleSec: Math.round(idleDuration / 1000),
|
|
76
|
+
state: { ...this.state },
|
|
77
|
+
message: this.buildMessage('kill', idleDuration),
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
return null;
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* 获取当前执行状态快照
|
|
84
|
+
*/
|
|
85
|
+
getState() {
|
|
86
|
+
return { ...this.state };
|
|
87
|
+
}
|
|
88
|
+
buildMessage(action, idleDuration) {
|
|
89
|
+
const idleSec = Math.round(idleDuration / 1000);
|
|
90
|
+
const lastToolInfo = this.state.lastToolName
|
|
91
|
+
? `🔧 ${this.state.lastToolName}`
|
|
92
|
+
: '无工具调用';
|
|
93
|
+
const timeSinceLastEvent = idleSec;
|
|
94
|
+
if (action === 'kill') {
|
|
95
|
+
return `🛑 执行超时(${idleSec}秒无响应),已自动中断\n\n执行状态:\n- 已处理 ${this.state.totalEvents} 个事件,${this.state.totalToolCalls} 次工具调用\n- 最后活动:${lastToolInfo}(${timeSinceLastEvent}秒前)`;
|
|
96
|
+
}
|
|
97
|
+
if (action === 'warn') {
|
|
98
|
+
return `⚠️ 执行监控:已 ${idleSec} 秒无输出\n\n执行状态:\n- 已处理 ${this.state.totalEvents} 个事件,${this.state.totalToolCalls} 次工具调用\n- 最后活动:${lastToolInfo}(${timeSinceLastEvent}秒前)\n\n如果持续无响应,将在 ${Math.round((this.idleMs * KILL_MULTIPLIER - idleDuration) / 1000)} 秒后自动中断。\n使用 /stop 可立即中断当前任务。`;
|
|
99
|
+
}
|
|
100
|
+
// notify
|
|
101
|
+
return `🔍 执行监控:已 ${idleSec} 秒无输出\n\n执行状态:\n- 已处理 ${this.state.totalEvents} 个事件,${this.state.totalToolCalls} 次工具调用\n- 最后活动:${lastToolInfo}(${timeSinceLastEvent}秒前)\n\n仍在运行中。使用 /stop 可中断当前任务。`;
|
|
102
|
+
}
|
|
103
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "evolclaw",
|
|
3
|
+
"version": "2.0.0",
|
|
4
|
+
"description": "Lightweight AI Agent gateway connecting Claude Agent SDK to messaging channels (Feishu, ACP) with multi-project session management",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "dist/index.js",
|
|
7
|
+
"bin": {
|
|
8
|
+
"evolclaw": "./bin/evolclaw"
|
|
9
|
+
},
|
|
10
|
+
"files": [
|
|
11
|
+
"dist/",
|
|
12
|
+
"!dist/experimental/",
|
|
13
|
+
"bin/",
|
|
14
|
+
"data/evolclaw.sample.json"
|
|
15
|
+
],
|
|
16
|
+
"scripts": {
|
|
17
|
+
"dev": "tsx watch src/index.ts",
|
|
18
|
+
"build": "tsc",
|
|
19
|
+
"start": "node dist/index.js",
|
|
20
|
+
"test": "vitest run",
|
|
21
|
+
"test:watch": "vitest",
|
|
22
|
+
"test:hooks": "tsx test-sdk-hooks.ts",
|
|
23
|
+
"prepublishOnly": "npm run build && npm test"
|
|
24
|
+
},
|
|
25
|
+
"dependencies": {
|
|
26
|
+
"@anthropic-ai/claude-agent-sdk": "^0.2.77",
|
|
27
|
+
"@larksuiteoapi/node-sdk": "^1.59.0",
|
|
28
|
+
"dotenv": "^16.6.1",
|
|
29
|
+
"image-type": "^6.0.0"
|
|
30
|
+
},
|
|
31
|
+
"devDependencies": {
|
|
32
|
+
"@types/node": "^22.0.0",
|
|
33
|
+
"@vitest/coverage-v8": "^2.1.9",
|
|
34
|
+
"tsx": "^4.19.0",
|
|
35
|
+
"typescript": "^5.6.0",
|
|
36
|
+
"vitest": "^2.1.9"
|
|
37
|
+
}
|
|
38
|
+
}
|