foliko 1.1.93 → 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/.claude/settings.local.json +2 -1
- package/CLAUDE.md +56 -30
- package/REFACTORING_PLAN.md +645 -0
- package/docs/architecture.md +131 -0
- package/docs/migration.md +57 -0
- package/docs/public-api.md +138 -0
- package/docs/usage.md +385 -0
- package/examples/ambient-example.js +20 -137
- package/examples/basic.js +21 -48
- package/examples/bootstrap.js +16 -74
- package/examples/mcp-example.js +6 -29
- package/examples/skill-example.js +6 -19
- package/examples/workflow.js +8 -56
- package/package.json +8 -4
- package/plugins/README.md +49 -0
- package/plugins/{ambient-agent → ambient}/EventWatcher.js +1 -1
- package/plugins/{ambient-agent → ambient}/ExplorerLoop.js +3 -3
- package/plugins/{ambient-agent → ambient}/GoalManager.js +2 -2
- package/plugins/ambient/README.md +14 -0
- package/plugins/{ambient-agent → ambient}/Reflector.js +1 -1
- package/plugins/{ambient-agent → ambient}/StateStore.js +1 -1
- package/plugins/{ambient-agent → ambient}/index.js +2 -2
- package/plugins/{ai-plugin.js → core/ai/index.js} +14 -30
- package/plugins/{audit-plugin.js → core/audit/index.js} +3 -30
- package/plugins/{coordinator-plugin.js → core/coordinator/index.js} +3 -35
- package/plugins/core/default/bootstrap.js +202 -0
- package/plugins/core/default/config.js +220 -0
- package/plugins/core/default/index.js +58 -0
- package/plugins/core/mcp/index.js +1 -0
- package/plugins/{python-plugin-loader.js → core/python-loader/index.js} +7 -187
- package/plugins/{rules-plugin.js → core/rules/index.js} +121 -64
- package/plugins/{scheduler-plugin.js → core/scheduler/index.js} +12 -114
- package/plugins/{session-plugin.js → core/session/index.js} +9 -73
- package/{src/capabilities/skill-manager.js → plugins/core/skill-manager/index.js} +64 -18
- package/plugins/{storage-plugin.js → core/storage/index.js} +5 -29
- package/plugins/{subagent-plugin.js → core/sub-agent/index.js} +10 -171
- package/plugins/{think-plugin.js → core/think/index.js} +24 -91
- package/{src/capabilities/workflow-engine.js → plugins/core/workflow/index.js} +87 -85
- package/plugins/default-plugins.js +6 -720
- package/plugins/{data-splitter-plugin.js → executors/data-splitter/index.js} +9 -83
- package/plugins/{extension-executor-plugin.js → executors/extension/index.js} +13 -97
- package/plugins/{python-executor-plugin.js → executors/python/index.js} +6 -31
- package/plugins/{shell-executor-plugin.js → executors/shell/index.js} +2 -5
- package/plugins/install/README.md +9 -0
- package/plugins/{install-plugin.js → install/index.js} +3 -3
- package/plugins/{file-system-plugin.js → io/file-system/index.js} +34 -236
- package/plugins/{web-plugin.js → io/web/index.js} +11 -113
- package/plugins/memory/README.md +13 -0
- package/plugins/{memory-plugin.js → memory/index.js} +4 -18
- package/plugins/messaging/email/README.md +19 -0
- package/plugins/{email → messaging/email}/index.js +2 -2
- package/plugins/{feishu-plugin.js → messaging/feishu/index.js} +3 -3
- package/plugins/{qq-plugin.js → messaging/qq/index.js} +5 -16
- package/plugins/{telegram-plugin.js → messaging/telegram/index.js} +3 -3
- package/plugins/{weixin-plugin.js → messaging/weixin/index.js} +15 -15
- package/plugins/{plugin-manager-plugin.js → plugin-manager/index.js} +36 -180
- package/plugins/{tools-plugin.js → tools/index.js} +68 -116
- package/plugins/trading/README.md +15 -0
- package/plugins/{gate-trading.js → trading/index.js} +8 -8
- package/{examples → sandbox}/test-concurrent-chat.js +2 -2
- package/{examples → sandbox}/test-long-chat.js +2 -2
- package/{examples → sandbox}/test-session-chat.js +2 -2
- package/{examples → sandbox}/test-web-plugin.js +1 -1
- package/{examples → sandbox}/test-weixin-feishu.js +2 -2
- package/src/agent/base.js +56 -0
- package/src/{core/agent-chat.js → agent/chat.js} +11 -11
- package/src/{core/coordinator-manager.js → agent/coordinator.js} +3 -3
- package/src/agent/index.js +111 -0
- package/src/agent/main.js +337 -0
- package/src/agent/prompt.js +78 -0
- package/src/agent/sub.js +198 -0
- package/src/agent/worker.js +104 -0
- package/{cli/bin/foliko.js → src/cli/bin.js} +1 -1
- package/{cli/src → src/cli}/commands/chat.js +25 -21
- package/{cli/src → src/cli}/index.js +1 -0
- package/{cli/src → src/cli}/ui/chat-ui-old.js +40 -178
- package/{cli/src → src/cli}/ui/chat-ui.js +3 -3
- package/{cli/src → src/cli}/ui/components/footer-bar.js +1 -1
- package/src/common/errors.js +402 -0
- package/src/{utils → common}/logger.js +33 -0
- package/src/{utils/chat-queue.js → common/queue.js} +2 -2
- package/src/config/plugin-config.js +50 -0
- package/src/context/agent.js +32 -0
- package/src/context/compaction-prompts.js +170 -0
- package/src/context/compaction-utils.js +191 -0
- package/src/context/compressor.js +413 -0
- package/src/context/index.js +9 -0
- package/src/{core/context-manager.js → context/manager.js} +1 -1
- package/src/context/request.js +50 -0
- package/src/context/session.js +33 -0
- package/src/context/storage.js +30 -0
- package/src/executors/mcp-client.js +153 -0
- package/src/executors/mcp-desc.js +236 -0
- package/src/executors/mcp-executor.js +91 -956
- package/src/{core → framework}/command-registry.js +1 -1
- package/src/framework/framework.js +300 -0
- package/src/framework/index.js +18 -0
- package/src/framework/lifecycle.js +203 -0
- package/src/framework/loader.js +78 -0
- package/src/framework/registry.js +86 -0
- package/src/{core/ui-extension-context.js → framework/ui-extension.js} +1 -1
- package/src/index.js +130 -15
- package/src/llm/index.js +26 -0
- package/src/llm/provider.js +212 -0
- package/src/llm/registry.js +11 -0
- package/src/{core/token-counter.js → llm/tokens.js} +4 -37
- package/src/{core/plugin-base.js → plugin/base.js} +10 -136
- package/src/plugin/index.js +14 -0
- package/src/plugin/loader.js +101 -0
- package/src/plugin/manager.js +261 -0
- package/src/{core → session}/branch-summary-auto.js +2 -2
- package/src/{core/chat-session.js → session/chat.js} +2 -2
- package/src/session/index.js +7 -0
- package/src/{core/session-manager.js → session/session.js} +2 -2
- package/src/session/ttl.js +92 -0
- package/src/{core/jsonl-storage.js → storage/jsonl.js} +1 -1
- package/src/tool/executor.js +85 -0
- package/src/tool/index.js +15 -0
- package/src/tool/registry.js +143 -0
- package/src/{core/tool-router.js → tool/router.js} +17 -124
- package/src/tool/schema.js +108 -0
- package/src/utils/data-splitter.js +1 -1
- package/src/utils/download.js +1 -1
- package/src/utils/index.js +6 -6
- package/src/utils/message-validator.js +1 -1
- package/tests/core/context-storage.test.js +46 -0
- package/tests/core/llm.test.js +54 -0
- package/tests/core/plugin.test.js +42 -0
- package/tests/core/tool.test.js +60 -0
- package/tests/setup.js +10 -0
- package/tests/smoke.test.js +58 -0
- package/vitest.config.js +9 -0
- package/cli/src/daemon.js +0 -149
- package/docs/CONTEXT_DESIGN.md +0 -1596
- package/docs/ai-sdk-optimization.md +0 -655
- package/docs/features.md +0 -120
- package/docs/qq-bot.md +0 -976
- package/docs/quick-reference.md +0 -160
- package/docs/user-manual.md +0 -1391
- package/images/geometric_shapes.jpg +0 -0
- package/images/sunset_mountain_lake.jpg +0 -0
- package/skills/poster-guide/SKILL.md +0 -792
- package/src/capabilities/index.js +0 -11
- package/src/core/agent.js +0 -808
- package/src/core/context-compressor.js +0 -959
- package/src/core/enhanced-context-compressor.js +0 -210
- package/src/core/framework.js +0 -1422
- package/src/core/index.js +0 -30
- package/src/core/plugin-manager.js +0 -961
- package/src/core/provider-registry.js +0 -159
- package/src/core/provider.js +0 -156
- package/src/core/request-context.js +0 -98
- package/src/core/subagent.js +0 -442
- package/src/core/system-prompt-builder.js +0 -120
- package/src/core/tool-executor.js +0 -202
- package/src/core/tool-registry.js +0 -517
- package/src/core/worker-agent.js +0 -192
- package/src/executors/executor-base.js +0 -58
- package/src/utils/error-boundary.js +0 -363
- package/src/utils/error.js +0 -374
- package/system.md +0 -1645
- package/website_v2/README.md +0 -57
- package/website_v2/SPEC.md +0 -1
- package/website_v2/docs/api.html +0 -128
- package/website_v2/docs/configuration.html +0 -147
- package/website_v2/docs/plugin-development.html +0 -129
- package/website_v2/docs/project-structure.html +0 -89
- package/website_v2/docs/skill-development.html +0 -85
- package/website_v2/index.html +0 -489
- package/website_v2/scripts/main.js +0 -93
- package/website_v2/styles/animations.css +0 -8
- package/website_v2/styles/docs.css +0 -83
- package/website_v2/styles/main.css +0 -417
- package/xhs_auth.json +0 -268
- package//346/265/267/346/212/245/346/217/222/344/273/266.md +0 -621
- /package/plugins/{ambient-agent → ambient}/constants.js +0 -0
- /package/plugins/{email → messaging/email}/constants.js +0 -0
- /package/plugins/{email → messaging/email}/handlers.js +0 -0
- /package/plugins/{email → messaging/email}/monitor.js +0 -0
- /package/plugins/{email → messaging/email}/parser.js +0 -0
- /package/plugins/{email → messaging/email}/reply.js +0 -0
- /package/plugins/{email → messaging/email}/utils.js +0 -0
- /package/{examples → sandbox}/test-chat.js +0 -0
- /package/{examples → sandbox}/test-mcp.js +0 -0
- /package/{examples → sandbox}/test-reload.js +0 -0
- /package/{examples → sandbox}/test-telegram.js +0 -0
- /package/{examples → sandbox}/test-tg-bot.js +0 -0
- /package/{examples → sandbox}/test-tg-simple.js +0 -0
- /package/{examples → sandbox}/test-tg.js +0 -0
- /package/{examples → sandbox}/test-think.js +0 -0
- /package/src/{core/sub-agent-config.js → agent/sub-config.js} +0 -0
- /package/{cli/src → src/cli}/commands/daemon.js +0 -0
- /package/{cli/src → src/cli}/commands/list.js +0 -0
- /package/{cli/src → src/cli}/commands/plugin.js +0 -0
- /package/{cli/src → src/cli}/ui/components/agent-mention-provider.js +0 -0
- /package/{cli/src → src/cli}/ui/components/chained-autocomplete-provider.js +0 -0
- /package/{cli/src → src/cli}/ui/components/message-bubble.js +0 -0
- /package/{cli/src → src/cli}/ui/components/status-bar.js +0 -0
- /package/{cli/src → src/cli}/utils/ansi.js +0 -0
- /package/{cli/src → src/cli}/utils/config.js +0 -0
- /package/{cli/src → src/cli}/utils/markdown.js +0 -0
- /package/{cli/src → src/cli}/utils/plugin-config.js +0 -0
- /package/{cli/src → src/cli}/utils/render-diff.js +0 -0
- /package/src/{utils/circuit-breaker.js → common/circuit.js} +0 -0
- /package/src/{core → common}/constants.js +0 -0
- /package/src/{utils/edit-diff.js → common/diff.js} +0 -0
- /package/src/{utils/event-emitter.js → common/events.js} +0 -0
- /package/src/{utils → common}/id.js +0 -0
- /package/src/{utils → common}/retry.js +0 -0
- /package/src/{core/notification-manager.js → notification/manager.js} +0 -0
- /package/src/{core/session-entry.js → session/entry.js} +0 -0
- /package/src/{core/storage-manager.js → storage/manager.js} +0 -0
|
@@ -9,9 +9,9 @@ const {
|
|
|
9
9
|
BRANCH_SUMMARY_PREAMBLE,
|
|
10
10
|
BRANCH_SUMMARY_PROMPT,
|
|
11
11
|
serializeConversation
|
|
12
|
-
} = require('
|
|
12
|
+
} = require('../context/compressor');
|
|
13
13
|
|
|
14
|
-
const { logger } = require('../
|
|
14
|
+
const { logger } = require('../common/logger');
|
|
15
15
|
|
|
16
16
|
class BranchSummaryAuto {
|
|
17
17
|
/**
|
|
@@ -8,8 +8,8 @@
|
|
|
8
8
|
* 4. SessionScope 事件作用域
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
|
-
const { EventEmitter } = require('../
|
|
12
|
-
const { logger } = require('../
|
|
11
|
+
const { EventEmitter } = require('../common/events');
|
|
12
|
+
const { logger } = require('../common/logger');
|
|
13
13
|
|
|
14
14
|
/**
|
|
15
15
|
* Session 作用域的事件监听器
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
|
|
7
7
|
const fs = require('fs');
|
|
8
8
|
const path = require('path');
|
|
9
|
-
const { JsonlSessionStorage } = require('
|
|
9
|
+
const { JsonlSessionStorage } = require('../storage/jsonl');
|
|
10
10
|
const {
|
|
11
11
|
EntryTypes,
|
|
12
12
|
generateEntryId,
|
|
@@ -15,7 +15,7 @@ const {
|
|
|
15
15
|
createBranchSummaryMessage,
|
|
16
16
|
createCompactionSummaryMessage,
|
|
17
17
|
createCustomMessage
|
|
18
|
-
} = require('./
|
|
18
|
+
} = require('./entry');
|
|
19
19
|
|
|
20
20
|
const CURRENT_SESSION_VERSION = 3;
|
|
21
21
|
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* SessionTTL - Session 过期清理管理
|
|
5
|
+
* 从 framework.js 中提取的 TTL 清理逻辑
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const { DEFAULT_SESSION_CLEANUP_INTERVAL_MS } = require('../common/constants');
|
|
9
|
+
|
|
10
|
+
class SessionTTL {
|
|
11
|
+
/**
|
|
12
|
+
* @param {Object} options
|
|
13
|
+
* @param {Function} options.getSessionContexts - 返回 session 上下文 Map
|
|
14
|
+
* @param {Function} options.destroySession - 销毁 session 的函数
|
|
15
|
+
* @param {number} [options.ttlMs=1800000] - TTL 毫秒数,默认 30 分钟
|
|
16
|
+
* @param {number} [options.checkIntervalMs=60000] - 检查间隔,默认 60 秒
|
|
17
|
+
*/
|
|
18
|
+
constructor(options = {}) {
|
|
19
|
+
this._getSessionContexts = options.getSessionContexts;
|
|
20
|
+
this._destroySession = options.destroySession;
|
|
21
|
+
this._ttlMs = options.ttlMs || 30 * 60 * 1000;
|
|
22
|
+
this._checkIntervalMs = options.checkIntervalMs || DEFAULT_SESSION_CLEANUP_INTERVAL_MS;
|
|
23
|
+
this._intervalId = null;
|
|
24
|
+
this._lastAccessMap = new Map();
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* 设置 TTL
|
|
29
|
+
*/
|
|
30
|
+
setTTL(ttlMs) {
|
|
31
|
+
this._ttlMs = ttlMs;
|
|
32
|
+
return this;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* 启动定期清理
|
|
37
|
+
*/
|
|
38
|
+
start() {
|
|
39
|
+
if (this._intervalId) return;
|
|
40
|
+
this._intervalId = setInterval(() => this._cleanup(), this._checkIntervalMs);
|
|
41
|
+
this._intervalId.unref();
|
|
42
|
+
return this;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* 停止定期清理
|
|
47
|
+
*/
|
|
48
|
+
stop() {
|
|
49
|
+
if (this._intervalId) {
|
|
50
|
+
clearInterval(this._intervalId);
|
|
51
|
+
this._intervalId = null;
|
|
52
|
+
}
|
|
53
|
+
return this;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* 标记 session 活跃(更新最后访问时间)
|
|
58
|
+
*/
|
|
59
|
+
touch(sessionId) {
|
|
60
|
+
this._lastAccessMap.set(sessionId, Date.now());
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* 清理过期 session
|
|
65
|
+
*/
|
|
66
|
+
_cleanup() {
|
|
67
|
+
if (!this._getSessionContexts) return;
|
|
68
|
+
const contexts = this._getSessionContexts();
|
|
69
|
+
if (!contexts || contexts.size === 0) return;
|
|
70
|
+
|
|
71
|
+
const now = Date.now();
|
|
72
|
+
for (const [sessionId] of contexts) {
|
|
73
|
+
const lastAccess = this._lastAccessMap.get(sessionId) || now;
|
|
74
|
+
if (now - lastAccess > this._ttlMs) {
|
|
75
|
+
this._lastAccessMap.delete(sessionId);
|
|
76
|
+
if (this._destroySession) {
|
|
77
|
+
this._destroySession(sessionId);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* 销毁
|
|
85
|
+
*/
|
|
86
|
+
destroy() {
|
|
87
|
+
this.stop();
|
|
88
|
+
this._lastAccessMap.clear();
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
module.exports = { SessionTTL };
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* ToolExecutor - 工具执行器
|
|
5
|
+
*
|
|
6
|
+
* 职责:
|
|
7
|
+
* 1. 工具发现和注册
|
|
8
|
+
* 2. 工具执行
|
|
9
|
+
* 3. 工具调用验证
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
const { EventEmitter } = require('../common/events');
|
|
13
|
+
const { logger } = require('../common/logger');
|
|
14
|
+
const { validateToolCalls } = require('../utils/message-validator');
|
|
15
|
+
|
|
16
|
+
class ToolExecutor extends EventEmitter {
|
|
17
|
+
constructor(config = {}) {
|
|
18
|
+
super();
|
|
19
|
+
|
|
20
|
+
this.config = config;
|
|
21
|
+
this.agent = config.agent;
|
|
22
|
+
this.framework = config.framework;
|
|
23
|
+
|
|
24
|
+
this._tools = new Map();
|
|
25
|
+
this._toolStats = {
|
|
26
|
+
totalCalls: 0,
|
|
27
|
+
failedCalls: 0,
|
|
28
|
+
lastCall: null,
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
registerTool(tool) {
|
|
33
|
+
if (!tool || !tool.name) {
|
|
34
|
+
logger.warn('ToolExecutor', 'Ignoring tool with no name');
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
this._tools.set(tool.name, tool);
|
|
38
|
+
logger.debug('ToolExecutor', `Registered tool: ${tool.name}`);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
unregisterTool(name) {
|
|
42
|
+
this._tools.delete(name);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
getTool(name) {
|
|
46
|
+
return this._tools.get(name) || null;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
getAllTools() {
|
|
50
|
+
return Array.from(this._tools.values());
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
async executeTool(name, args, context = {}) {
|
|
54
|
+
const tool = this._tools.get(name);
|
|
55
|
+
if (!tool) {
|
|
56
|
+
throw new Error(`Tool '${name}' not found`);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
this._toolStats.totalCalls++;
|
|
60
|
+
this._toolStats.lastCall = Date.now();
|
|
61
|
+
|
|
62
|
+
try {
|
|
63
|
+
const result = await tool.execute(args, context);
|
|
64
|
+
this.emit('tool:executed', { name, args, result });
|
|
65
|
+
return result;
|
|
66
|
+
} catch (err) {
|
|
67
|
+
this._toolStats.failedCalls++;
|
|
68
|
+
this.emit('tool:error', { name, args, error: err });
|
|
69
|
+
throw err;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
validateToolCalls(messages) {
|
|
74
|
+
return validateToolCalls(messages);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
getStats() {
|
|
78
|
+
return {
|
|
79
|
+
...this._toolStats,
|
|
80
|
+
toolCount: this._tools.size,
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
module.exports = { ToolExecutor };
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const { ToolRegistry } = require('./registry');
|
|
4
|
+
const { ToolExecutor } = require('./executor');
|
|
5
|
+
const { ToolRouter, INTENT_PATTERNS } = require('./router');
|
|
6
|
+
const { jsonSchemaToZod, toAISDKTool } = require('./schema');
|
|
7
|
+
|
|
8
|
+
module.exports = {
|
|
9
|
+
ToolRegistry,
|
|
10
|
+
ToolExecutor,
|
|
11
|
+
ToolRouter,
|
|
12
|
+
INTENT_PATTERNS,
|
|
13
|
+
jsonSchemaToZod,
|
|
14
|
+
toAISDKTool,
|
|
15
|
+
};
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* ToolRegistry 工具注册表
|
|
5
|
+
* 统一管理所有工具
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const { EventEmitter } = require('../common/events');
|
|
9
|
+
const { globalCircuitBreaker } = require('../common/circuit');
|
|
10
|
+
const { logger } = require('../common/logger');
|
|
11
|
+
const { z } = require('zod');
|
|
12
|
+
const { withRetry, isNetworkError, PRESETS } = require('../common/retry');
|
|
13
|
+
const { ErrorBoundary, Severity, RecoveryAction } = require('../common/errors');
|
|
14
|
+
const { jsonSchemaToZod } = require('./schema');
|
|
15
|
+
|
|
16
|
+
const log = logger.child('ToolRegistry');
|
|
17
|
+
|
|
18
|
+
class ToolRegistry extends EventEmitter {
|
|
19
|
+
constructor(options = {}) {
|
|
20
|
+
super();
|
|
21
|
+
this._tools = new Map();
|
|
22
|
+
|
|
23
|
+
this._circuitBreakerEnabled = options.circuitBreaker !== false;
|
|
24
|
+
this._circuitBreaker = globalCircuitBreaker;
|
|
25
|
+
|
|
26
|
+
this._defaultCircuitBreakerOptions = {
|
|
27
|
+
failureThreshold: options.failureThreshold || 3,
|
|
28
|
+
successThreshold: options.successThreshold || 2,
|
|
29
|
+
timeout: options.circuitBreakerTimeout || 60000,
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
register(tool) {
|
|
34
|
+
if (!tool.name) {
|
|
35
|
+
throw new Error('Tool must have a name');
|
|
36
|
+
}
|
|
37
|
+
if (typeof tool.execute !== 'function') {
|
|
38
|
+
throw new Error(`Tool '${tool.name}' must have an execute function`);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
this._tools.set(tool.name, {
|
|
42
|
+
name: tool.name,
|
|
43
|
+
description: tool.description || '',
|
|
44
|
+
execute: tool.execute,
|
|
45
|
+
parameters: tool.parameters || {},
|
|
46
|
+
inputSchema: tool.inputSchema || null,
|
|
47
|
+
retry: tool.retry,
|
|
48
|
+
circuitBreaker: tool.circuitBreaker,
|
|
49
|
+
});
|
|
50
|
+
log.debug(`Tool registered: ${tool.name}`);
|
|
51
|
+
this.emit('tool:registered', tool);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
get(name) {
|
|
55
|
+
return this._tools.get(name) || null;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
has(name) {
|
|
59
|
+
return this._tools.has(name);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
getAll() {
|
|
63
|
+
return Array.from(this._tools.values());
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
getNames() {
|
|
67
|
+
return Array.from(this._tools.keys());
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
unregister(name) {
|
|
71
|
+
this._tools.delete(name);
|
|
72
|
+
this.emit('tool:unregistered', { name });
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
clear() {
|
|
76
|
+
this._tools.clear();
|
|
77
|
+
this._tools = new Map();
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
count() {
|
|
81
|
+
return this._tools.size;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
async execute(name, args, context = {}) {
|
|
85
|
+
const tool = this._tools.get(name);
|
|
86
|
+
if (!tool) {
|
|
87
|
+
throw new Error(`Tool '${name}' not found`);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
const executeFn = async () => {
|
|
91
|
+
const startTime = Date.now();
|
|
92
|
+
let result;
|
|
93
|
+
let error;
|
|
94
|
+
|
|
95
|
+
try {
|
|
96
|
+
result = await tool.execute(args, context);
|
|
97
|
+
} catch (err) {
|
|
98
|
+
error = err;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
const duration = Date.now() - startTime;
|
|
102
|
+
|
|
103
|
+
if (error) {
|
|
104
|
+
this.emit('tool:error', { name, args, error, duration });
|
|
105
|
+
throw error;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
this.emit('tool:result', { name, args, result, duration });
|
|
109
|
+
return result;
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
if (tool.retry) {
|
|
113
|
+
const retryConfig = typeof tool.retry === 'string'
|
|
114
|
+
? PRESETS[tool.retry] || PRESETS.standard
|
|
115
|
+
: tool.retry;
|
|
116
|
+
|
|
117
|
+
return withRetry(executeFn, {
|
|
118
|
+
...retryConfig,
|
|
119
|
+
onRetry: (err, attempt) => {
|
|
120
|
+
log.warn(`Tool '${name}' failed (attempt ${attempt}), retrying: ${err.message}`);
|
|
121
|
+
},
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
return executeFn();
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
getCircuitBreakerStatus() {
|
|
129
|
+
return {
|
|
130
|
+
enabled: this._circuitBreakerEnabled,
|
|
131
|
+
state: this._circuitBreaker.getState(),
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
_jsonSchemaToZod(schema, key, isRequired) {
|
|
136
|
+
if (!schema) return z.any();
|
|
137
|
+
|
|
138
|
+
// 委托给 schema.js 处理
|
|
139
|
+
return jsonSchemaToZod(schema, isRequired);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
module.exports = { ToolRegistry };
|
|
@@ -1,75 +1,36 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
1
3
|
/**
|
|
2
4
|
* 意图路由工具管理器
|
|
3
5
|
* 根据用户输入智能选择要传递给 LLM 的工具
|
|
4
6
|
*/
|
|
5
7
|
|
|
6
|
-
const { EventEmitter } = require('../
|
|
8
|
+
const { EventEmitter } = require('../common/events');
|
|
7
9
|
|
|
8
|
-
/**
|
|
9
|
-
* 内置意图分类
|
|
10
|
-
*/
|
|
11
10
|
const INTENT_PATTERNS = {
|
|
12
11
|
file_operation: {
|
|
13
12
|
keywords: [
|
|
14
|
-
'读取文件',
|
|
15
|
-
'
|
|
16
|
-
'创建文件',
|
|
17
|
-
'删除文件',
|
|
18
|
-
'修改文件',
|
|
19
|
-
'查看文件',
|
|
20
|
-
'read',
|
|
21
|
-
'write',
|
|
22
|
-
'create',
|
|
23
|
-
'delete',
|
|
24
|
-
'modify',
|
|
25
|
-
'file',
|
|
26
|
-
'文件',
|
|
13
|
+
'读取文件', '写文件', '创建文件', '删除文件', '修改文件', '查看文件',
|
|
14
|
+
'read', 'write', 'create', 'delete', 'modify', 'file', '文件',
|
|
27
15
|
],
|
|
28
16
|
tools: [
|
|
29
|
-
'read_file',
|
|
30
|
-
'
|
|
31
|
-
'delete_file',
|
|
32
|
-
'modify_file',
|
|
33
|
-
'read_directory',
|
|
34
|
-
'create_directory',
|
|
35
|
-
'search_file',
|
|
17
|
+
'read_file', 'write_file', 'delete_file', 'modify_file',
|
|
18
|
+
'read_directory', 'create_directory', 'search_file',
|
|
36
19
|
],
|
|
37
20
|
},
|
|
38
21
|
code_development: {
|
|
39
22
|
keywords: [
|
|
40
|
-
'写代码',
|
|
41
|
-
'
|
|
42
|
-
'编程',
|
|
43
|
-
'创建插件',
|
|
44
|
-
'编写函数',
|
|
45
|
-
'code',
|
|
46
|
-
'develop',
|
|
47
|
-
'plugin',
|
|
48
|
-
'编程',
|
|
23
|
+
'写代码', '开发', '编程', '创建插件', '编写函数',
|
|
24
|
+
'code', 'develop', 'plugin', '编程',
|
|
49
25
|
],
|
|
50
26
|
tools: ['loadSkill', 'read_file', 'write_file', 'execute_command', 'shell'],
|
|
51
27
|
},
|
|
52
28
|
system_info: {
|
|
53
29
|
keywords: [
|
|
54
|
-
'系统信息',
|
|
55
|
-
'
|
|
56
|
-
'CPU',
|
|
57
|
-
'磁盘',
|
|
58
|
-
'进程',
|
|
59
|
-
'system',
|
|
60
|
-
'memory',
|
|
61
|
-
'cpu',
|
|
62
|
-
'disk',
|
|
63
|
-
'process',
|
|
64
|
-
'info',
|
|
65
|
-
],
|
|
66
|
-
tools: [
|
|
67
|
-
'get_system_info',
|
|
68
|
-
'get_memory_usage',
|
|
69
|
-
'get_cpu_info',
|
|
70
|
-
'get_disk_space',
|
|
71
|
-
'get_process_info',
|
|
30
|
+
'系统信息', '内存', 'CPU', '磁盘', '进程',
|
|
31
|
+
'system', 'memory', 'cpu', 'disk', 'process', 'info',
|
|
72
32
|
],
|
|
33
|
+
tools: ['get_system_info', 'get_memory_usage', 'get_cpu_info', 'get_disk_space', 'get_process_info'],
|
|
73
34
|
},
|
|
74
35
|
data_analysis: {
|
|
75
36
|
keywords: ['分析', '数据', '统计', '查询', 'analyze', 'data', 'statistics', 'query'],
|
|
@@ -89,43 +50,14 @@ const INTENT_PATTERNS = {
|
|
|
89
50
|
},
|
|
90
51
|
scheduling: {
|
|
91
52
|
keywords: [
|
|
92
|
-
'定时',
|
|
93
|
-
'
|
|
94
|
-
'
|
|
95
|
-
'schedule',
|
|
96
|
-
'remind',
|
|
97
|
-
'notify',
|
|
98
|
-
'cron',
|
|
99
|
-
'任务',
|
|
100
|
-
'稍后',
|
|
101
|
-
'天后',
|
|
102
|
-
'秒后',
|
|
103
|
-
'分钟后',
|
|
104
|
-
'小时后',
|
|
105
|
-
'明天',
|
|
106
|
-
'今天',
|
|
107
|
-
],
|
|
108
|
-
tools: [
|
|
109
|
-
'schedule_cron',
|
|
110
|
-
'schedule_once',
|
|
111
|
-
'schedule_list',
|
|
112
|
-
'schedule_cancel',
|
|
113
|
-
'schedule_get_results',
|
|
114
|
-
'schedule_notify',
|
|
53
|
+
'定时', '提醒', '通知', 'schedule', 'remind', 'notify',
|
|
54
|
+
'cron', '任务', '稍后', '天后', '秒后', '分钟后', '小时后',
|
|
55
|
+
'明天', '今天',
|
|
115
56
|
],
|
|
57
|
+
tools: ['schedule_cron', 'schedule_once', 'schedule_list', 'schedule_cancel', 'schedule_get_results', 'schedule_notify'],
|
|
116
58
|
},
|
|
117
59
|
thinking: {
|
|
118
|
-
keywords: [
|
|
119
|
-
'思考',
|
|
120
|
-
'反思',
|
|
121
|
-
'想想',
|
|
122
|
-
'think',
|
|
123
|
-
'reflect',
|
|
124
|
-
'分析一下',
|
|
125
|
-
'头脑风暴',
|
|
126
|
-
'持续思考',
|
|
127
|
-
'自动思考',
|
|
128
|
-
],
|
|
60
|
+
keywords: ['思考', '反思', '想想', 'think', 'reflect', '分析一下', '头脑风暴', '持续思考', '自动思考'],
|
|
129
61
|
tools: ['think_now', 'think_continue', 'think_stop', 'think_get_thoughts'],
|
|
130
62
|
},
|
|
131
63
|
};
|
|
@@ -136,36 +68,23 @@ class ToolRouter extends EventEmitter {
|
|
|
136
68
|
this._tools = new Map();
|
|
137
69
|
this._defaultTools = new Set();
|
|
138
70
|
|
|
139
|
-
// 注册工具
|
|
140
71
|
for (const tool of tools) {
|
|
141
72
|
this.register(tool);
|
|
142
73
|
}
|
|
143
74
|
}
|
|
144
75
|
|
|
145
|
-
/**
|
|
146
|
-
* 注册工具
|
|
147
|
-
*/
|
|
148
76
|
register(tool) {
|
|
149
77
|
this._tools.set(tool.name, tool);
|
|
150
78
|
}
|
|
151
79
|
|
|
152
|
-
/**
|
|
153
|
-
* 设置默认工具(每个请求都会包含)
|
|
154
|
-
*/
|
|
155
80
|
setDefaultTools(toolNames) {
|
|
156
81
|
this._defaultTools = new Set(toolNames);
|
|
157
82
|
}
|
|
158
83
|
|
|
159
|
-
/**
|
|
160
|
-
* 判断是否为默认工具
|
|
161
|
-
*/
|
|
162
84
|
isDefaultTool(name) {
|
|
163
85
|
return this._defaultTools.has(name);
|
|
164
86
|
}
|
|
165
87
|
|
|
166
|
-
/**
|
|
167
|
-
* 分析用户输入,识别意图
|
|
168
|
-
*/
|
|
169
88
|
analyzeIntent(userMessage) {
|
|
170
89
|
const message = userMessage.toLowerCase();
|
|
171
90
|
|
|
@@ -189,15 +108,11 @@ class ToolRouter extends EventEmitter {
|
|
|
189
108
|
}
|
|
190
109
|
}
|
|
191
110
|
|
|
192
|
-
// 按分数排序
|
|
193
111
|
matchedIntents.sort((a, b) => b.score - a.score);
|
|
194
112
|
|
|
195
113
|
return matchedIntents;
|
|
196
114
|
}
|
|
197
115
|
|
|
198
|
-
/**
|
|
199
|
-
* 获取指定意图对应的工具
|
|
200
|
-
*/
|
|
201
116
|
getToolsForIntent(intent) {
|
|
202
117
|
const result = [];
|
|
203
118
|
|
|
@@ -211,42 +126,29 @@ class ToolRouter extends EventEmitter {
|
|
|
211
126
|
return result;
|
|
212
127
|
}
|
|
213
128
|
|
|
214
|
-
/**
|
|
215
|
-
* 为用户消息选择合适的工具
|
|
216
|
-
* @param {string} userMessage - 用户消息
|
|
217
|
-
* @param {Object} options - 选项
|
|
218
|
-
* @param {number} options.maxTools - 最大工具数量(默认10)
|
|
219
|
-
* @param {boolean} options.forceIntent - 强制使用指定意图
|
|
220
|
-
*/
|
|
221
129
|
selectTools(userMessage, options = {}) {
|
|
222
130
|
const { maxTools = 10 } = options;
|
|
223
131
|
|
|
224
|
-
// 如果没有工具,直接返回空的
|
|
225
132
|
if (this._tools.size === 0) {
|
|
226
133
|
return [];
|
|
227
134
|
}
|
|
228
135
|
|
|
229
|
-
// 如果消息为空,返回默认工具
|
|
230
136
|
if (!userMessage || userMessage.trim() === '') {
|
|
231
137
|
return this.getDefaultTools();
|
|
232
138
|
}
|
|
233
139
|
|
|
234
|
-
// 分析意图
|
|
235
140
|
const intents = this.analyzeIntent(userMessage);
|
|
236
141
|
|
|
237
|
-
// 如果没有匹配到意图,返回默认工具
|
|
238
142
|
if (intents.length === 0) {
|
|
239
143
|
return this.getDefaultTools();
|
|
240
144
|
}
|
|
241
145
|
|
|
242
146
|
const selectedTools = new Map();
|
|
243
147
|
|
|
244
|
-
// 添加默认工具
|
|
245
148
|
for (const tool of this.getDefaultTools()) {
|
|
246
149
|
selectedTools.set(tool.name, tool);
|
|
247
150
|
}
|
|
248
151
|
|
|
249
|
-
// 按意图选择工具
|
|
250
152
|
for (const intent of intents) {
|
|
251
153
|
if (selectedTools.size >= maxTools) break;
|
|
252
154
|
|
|
@@ -262,9 +164,6 @@ class ToolRouter extends EventEmitter {
|
|
|
262
164
|
return Array.from(selectedTools.values());
|
|
263
165
|
}
|
|
264
166
|
|
|
265
|
-
/**
|
|
266
|
-
* 获取默认工具
|
|
267
|
-
*/
|
|
268
167
|
getDefaultTools() {
|
|
269
168
|
const result = [];
|
|
270
169
|
for (const name of this._defaultTools) {
|
|
@@ -276,16 +175,10 @@ class ToolRouter extends EventEmitter {
|
|
|
276
175
|
return result;
|
|
277
176
|
}
|
|
278
177
|
|
|
279
|
-
/**
|
|
280
|
-
* 获取所有工具
|
|
281
|
-
*/
|
|
282
178
|
getAllTools() {
|
|
283
179
|
return Array.from(this._tools.values());
|
|
284
180
|
}
|
|
285
181
|
|
|
286
|
-
/**
|
|
287
|
-
* 按类别获取工具
|
|
288
|
-
*/
|
|
289
182
|
getToolsByCategory(category) {
|
|
290
183
|
const intent = INTENT_PATTERNS[category];
|
|
291
184
|
if (!intent) return [];
|