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.
Files changed (212) hide show
  1. package/.claude/settings.local.json +2 -1
  2. package/CLAUDE.md +56 -30
  3. package/REFACTORING_PLAN.md +645 -0
  4. package/docs/architecture.md +131 -0
  5. package/docs/migration.md +57 -0
  6. package/docs/public-api.md +138 -0
  7. package/docs/usage.md +385 -0
  8. package/examples/ambient-example.js +20 -137
  9. package/examples/basic.js +21 -48
  10. package/examples/bootstrap.js +16 -74
  11. package/examples/mcp-example.js +6 -29
  12. package/examples/skill-example.js +6 -19
  13. package/examples/workflow.js +8 -56
  14. package/package.json +8 -4
  15. package/plugins/README.md +49 -0
  16. package/plugins/{ambient-agent → ambient}/EventWatcher.js +1 -1
  17. package/plugins/{ambient-agent → ambient}/ExplorerLoop.js +3 -3
  18. package/plugins/{ambient-agent → ambient}/GoalManager.js +2 -2
  19. package/plugins/ambient/README.md +14 -0
  20. package/plugins/{ambient-agent → ambient}/Reflector.js +1 -1
  21. package/plugins/{ambient-agent → ambient}/StateStore.js +1 -1
  22. package/plugins/{ambient-agent → ambient}/index.js +2 -2
  23. package/plugins/{ai-plugin.js → core/ai/index.js} +14 -30
  24. package/plugins/{audit-plugin.js → core/audit/index.js} +3 -30
  25. package/plugins/{coordinator-plugin.js → core/coordinator/index.js} +3 -35
  26. package/plugins/core/default/bootstrap.js +202 -0
  27. package/plugins/core/default/config.js +220 -0
  28. package/plugins/core/default/index.js +58 -0
  29. package/plugins/core/mcp/index.js +1 -0
  30. package/plugins/{python-plugin-loader.js → core/python-loader/index.js} +7 -187
  31. package/plugins/{rules-plugin.js → core/rules/index.js} +121 -64
  32. package/plugins/{scheduler-plugin.js → core/scheduler/index.js} +12 -114
  33. package/plugins/{session-plugin.js → core/session/index.js} +9 -73
  34. package/{src/capabilities/skill-manager.js → plugins/core/skill-manager/index.js} +64 -18
  35. package/plugins/{storage-plugin.js → core/storage/index.js} +5 -29
  36. package/plugins/{subagent-plugin.js → core/sub-agent/index.js} +10 -171
  37. package/plugins/{think-plugin.js → core/think/index.js} +24 -91
  38. package/{src/capabilities/workflow-engine.js → plugins/core/workflow/index.js} +87 -85
  39. package/plugins/default-plugins.js +6 -720
  40. package/plugins/{data-splitter-plugin.js → executors/data-splitter/index.js} +9 -83
  41. package/plugins/{extension-executor-plugin.js → executors/extension/index.js} +13 -97
  42. package/plugins/{python-executor-plugin.js → executors/python/index.js} +6 -31
  43. package/plugins/{shell-executor-plugin.js → executors/shell/index.js} +2 -5
  44. package/plugins/install/README.md +9 -0
  45. package/plugins/{install-plugin.js → install/index.js} +3 -3
  46. package/plugins/{file-system-plugin.js → io/file-system/index.js} +34 -236
  47. package/plugins/{web-plugin.js → io/web/index.js} +11 -113
  48. package/plugins/memory/README.md +13 -0
  49. package/plugins/{memory-plugin.js → memory/index.js} +4 -18
  50. package/plugins/messaging/email/README.md +19 -0
  51. package/plugins/{email → messaging/email}/index.js +2 -2
  52. package/plugins/{feishu-plugin.js → messaging/feishu/index.js} +3 -3
  53. package/plugins/{qq-plugin.js → messaging/qq/index.js} +5 -16
  54. package/plugins/{telegram-plugin.js → messaging/telegram/index.js} +3 -3
  55. package/plugins/{weixin-plugin.js → messaging/weixin/index.js} +15 -15
  56. package/plugins/{plugin-manager-plugin.js → plugin-manager/index.js} +36 -180
  57. package/plugins/{tools-plugin.js → tools/index.js} +68 -116
  58. package/plugins/trading/README.md +15 -0
  59. package/plugins/{gate-trading.js → trading/index.js} +8 -8
  60. package/{examples → sandbox}/test-concurrent-chat.js +2 -2
  61. package/{examples → sandbox}/test-long-chat.js +2 -2
  62. package/{examples → sandbox}/test-session-chat.js +2 -2
  63. package/{examples → sandbox}/test-web-plugin.js +1 -1
  64. package/{examples → sandbox}/test-weixin-feishu.js +2 -2
  65. package/src/agent/base.js +56 -0
  66. package/src/{core/agent-chat.js → agent/chat.js} +11 -11
  67. package/src/{core/coordinator-manager.js → agent/coordinator.js} +3 -3
  68. package/src/agent/index.js +111 -0
  69. package/src/agent/main.js +337 -0
  70. package/src/agent/prompt.js +78 -0
  71. package/src/agent/sub.js +198 -0
  72. package/src/agent/worker.js +104 -0
  73. package/{cli/bin/foliko.js → src/cli/bin.js} +1 -1
  74. package/{cli/src → src/cli}/commands/chat.js +25 -21
  75. package/{cli/src → src/cli}/index.js +1 -0
  76. package/{cli/src → src/cli}/ui/chat-ui-old.js +40 -178
  77. package/{cli/src → src/cli}/ui/chat-ui.js +3 -3
  78. package/{cli/src → src/cli}/ui/components/footer-bar.js +1 -1
  79. package/src/common/errors.js +402 -0
  80. package/src/{utils → common}/logger.js +33 -0
  81. package/src/{utils/chat-queue.js → common/queue.js} +2 -2
  82. package/src/config/plugin-config.js +50 -0
  83. package/src/context/agent.js +32 -0
  84. package/src/context/compaction-prompts.js +170 -0
  85. package/src/context/compaction-utils.js +191 -0
  86. package/src/context/compressor.js +413 -0
  87. package/src/context/index.js +9 -0
  88. package/src/{core/context-manager.js → context/manager.js} +1 -1
  89. package/src/context/request.js +50 -0
  90. package/src/context/session.js +33 -0
  91. package/src/context/storage.js +30 -0
  92. package/src/executors/mcp-client.js +153 -0
  93. package/src/executors/mcp-desc.js +236 -0
  94. package/src/executors/mcp-executor.js +91 -956
  95. package/src/{core → framework}/command-registry.js +1 -1
  96. package/src/framework/framework.js +300 -0
  97. package/src/framework/index.js +18 -0
  98. package/src/framework/lifecycle.js +203 -0
  99. package/src/framework/loader.js +78 -0
  100. package/src/framework/registry.js +86 -0
  101. package/src/{core/ui-extension-context.js → framework/ui-extension.js} +1 -1
  102. package/src/index.js +130 -15
  103. package/src/llm/index.js +26 -0
  104. package/src/llm/provider.js +212 -0
  105. package/src/llm/registry.js +11 -0
  106. package/src/{core/token-counter.js → llm/tokens.js} +4 -37
  107. package/src/{core/plugin-base.js → plugin/base.js} +10 -136
  108. package/src/plugin/index.js +14 -0
  109. package/src/plugin/loader.js +101 -0
  110. package/src/plugin/manager.js +261 -0
  111. package/src/{core → session}/branch-summary-auto.js +2 -2
  112. package/src/{core/chat-session.js → session/chat.js} +2 -2
  113. package/src/session/index.js +7 -0
  114. package/src/{core/session-manager.js → session/session.js} +2 -2
  115. package/src/session/ttl.js +92 -0
  116. package/src/{core/jsonl-storage.js → storage/jsonl.js} +1 -1
  117. package/src/tool/executor.js +85 -0
  118. package/src/tool/index.js +15 -0
  119. package/src/tool/registry.js +143 -0
  120. package/src/{core/tool-router.js → tool/router.js} +17 -124
  121. package/src/tool/schema.js +108 -0
  122. package/src/utils/data-splitter.js +1 -1
  123. package/src/utils/download.js +1 -1
  124. package/src/utils/index.js +6 -6
  125. package/src/utils/message-validator.js +1 -1
  126. package/tests/core/context-storage.test.js +46 -0
  127. package/tests/core/llm.test.js +54 -0
  128. package/tests/core/plugin.test.js +42 -0
  129. package/tests/core/tool.test.js +60 -0
  130. package/tests/setup.js +10 -0
  131. package/tests/smoke.test.js +58 -0
  132. package/vitest.config.js +9 -0
  133. package/cli/src/daemon.js +0 -149
  134. package/docs/CONTEXT_DESIGN.md +0 -1596
  135. package/docs/ai-sdk-optimization.md +0 -655
  136. package/docs/features.md +0 -120
  137. package/docs/qq-bot.md +0 -976
  138. package/docs/quick-reference.md +0 -160
  139. package/docs/user-manual.md +0 -1391
  140. package/images/geometric_shapes.jpg +0 -0
  141. package/images/sunset_mountain_lake.jpg +0 -0
  142. package/skills/poster-guide/SKILL.md +0 -792
  143. package/src/capabilities/index.js +0 -11
  144. package/src/core/agent.js +0 -808
  145. package/src/core/context-compressor.js +0 -959
  146. package/src/core/enhanced-context-compressor.js +0 -210
  147. package/src/core/framework.js +0 -1422
  148. package/src/core/index.js +0 -30
  149. package/src/core/plugin-manager.js +0 -961
  150. package/src/core/provider-registry.js +0 -159
  151. package/src/core/provider.js +0 -156
  152. package/src/core/request-context.js +0 -98
  153. package/src/core/subagent.js +0 -442
  154. package/src/core/system-prompt-builder.js +0 -120
  155. package/src/core/tool-executor.js +0 -202
  156. package/src/core/tool-registry.js +0 -517
  157. package/src/core/worker-agent.js +0 -192
  158. package/src/executors/executor-base.js +0 -58
  159. package/src/utils/error-boundary.js +0 -363
  160. package/src/utils/error.js +0 -374
  161. package/system.md +0 -1645
  162. package/website_v2/README.md +0 -57
  163. package/website_v2/SPEC.md +0 -1
  164. package/website_v2/docs/api.html +0 -128
  165. package/website_v2/docs/configuration.html +0 -147
  166. package/website_v2/docs/plugin-development.html +0 -129
  167. package/website_v2/docs/project-structure.html +0 -89
  168. package/website_v2/docs/skill-development.html +0 -85
  169. package/website_v2/index.html +0 -489
  170. package/website_v2/scripts/main.js +0 -93
  171. package/website_v2/styles/animations.css +0 -8
  172. package/website_v2/styles/docs.css +0 -83
  173. package/website_v2/styles/main.css +0 -417
  174. package/xhs_auth.json +0 -268
  175. package//346/265/267/346/212/245/346/217/222/344/273/266.md +0 -621
  176. /package/plugins/{ambient-agent → ambient}/constants.js +0 -0
  177. /package/plugins/{email → messaging/email}/constants.js +0 -0
  178. /package/plugins/{email → messaging/email}/handlers.js +0 -0
  179. /package/plugins/{email → messaging/email}/monitor.js +0 -0
  180. /package/plugins/{email → messaging/email}/parser.js +0 -0
  181. /package/plugins/{email → messaging/email}/reply.js +0 -0
  182. /package/plugins/{email → messaging/email}/utils.js +0 -0
  183. /package/{examples → sandbox}/test-chat.js +0 -0
  184. /package/{examples → sandbox}/test-mcp.js +0 -0
  185. /package/{examples → sandbox}/test-reload.js +0 -0
  186. /package/{examples → sandbox}/test-telegram.js +0 -0
  187. /package/{examples → sandbox}/test-tg-bot.js +0 -0
  188. /package/{examples → sandbox}/test-tg-simple.js +0 -0
  189. /package/{examples → sandbox}/test-tg.js +0 -0
  190. /package/{examples → sandbox}/test-think.js +0 -0
  191. /package/src/{core/sub-agent-config.js → agent/sub-config.js} +0 -0
  192. /package/{cli/src → src/cli}/commands/daemon.js +0 -0
  193. /package/{cli/src → src/cli}/commands/list.js +0 -0
  194. /package/{cli/src → src/cli}/commands/plugin.js +0 -0
  195. /package/{cli/src → src/cli}/ui/components/agent-mention-provider.js +0 -0
  196. /package/{cli/src → src/cli}/ui/components/chained-autocomplete-provider.js +0 -0
  197. /package/{cli/src → src/cli}/ui/components/message-bubble.js +0 -0
  198. /package/{cli/src → src/cli}/ui/components/status-bar.js +0 -0
  199. /package/{cli/src → src/cli}/utils/ansi.js +0 -0
  200. /package/{cli/src → src/cli}/utils/config.js +0 -0
  201. /package/{cli/src → src/cli}/utils/markdown.js +0 -0
  202. /package/{cli/src → src/cli}/utils/plugin-config.js +0 -0
  203. /package/{cli/src → src/cli}/utils/render-diff.js +0 -0
  204. /package/src/{utils/circuit-breaker.js → common/circuit.js} +0 -0
  205. /package/src/{core → common}/constants.js +0 -0
  206. /package/src/{utils/edit-diff.js → common/diff.js} +0 -0
  207. /package/src/{utils/event-emitter.js → common/events.js} +0 -0
  208. /package/src/{utils → common}/id.js +0 -0
  209. /package/src/{utils → common}/retry.js +0 -0
  210. /package/src/{core/notification-manager.js → notification/manager.js} +0 -0
  211. /package/src/{core/session-entry.js → session/entry.js} +0 -0
  212. /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('./context-compressor');
12
+ } = require('../context/compressor');
13
13
 
14
- const { logger } = require('../utils/logger');
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('../utils/event-emitter');
12
- const { logger } = require('../utils/logger');
11
+ const { EventEmitter } = require('../common/events');
12
+ const { logger } = require('../common/logger');
13
13
 
14
14
  /**
15
15
  * Session 作用域的事件监听器
@@ -0,0 +1,7 @@
1
+ 'use strict';
2
+
3
+ module.exports = {
4
+ SessionManager: require('./session').SessionManager,
5
+ SessionEntry: require('./entry'),
6
+ ChatSession: require('./chat').ChatSession,
7
+ };
@@ -6,7 +6,7 @@
6
6
 
7
7
  const fs = require('fs');
8
8
  const path = require('path');
9
- const { JsonlSessionStorage } = require('./jsonl-storage');
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('./session-entry');
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 };
@@ -6,7 +6,7 @@
6
6
 
7
7
  const fs = require('fs');
8
8
  const path = require('path');
9
- const { generateEntryId, EntryTypes } = require('./session-entry');
9
+ const { generateEntryId, EntryTypes } = require('../session/entry');
10
10
 
11
11
  const CURRENT_SESSION_VERSION = 3;
12
12
 
@@ -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('../utils/event-emitter');
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
- 'write_file',
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 [];