foliko 1.0.74 → 1.0.76

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 (238) hide show
  1. package/.agent/.shared/ui-ux-pro-max/data/charts.csv +26 -0
  2. package/.agent/.shared/ui-ux-pro-max/data/colors.csv +97 -0
  3. package/.agent/.shared/ui-ux-pro-max/data/icons.csv +101 -0
  4. package/.agent/.shared/ui-ux-pro-max/data/landing.csv +31 -0
  5. package/.agent/.shared/ui-ux-pro-max/data/products.csv +97 -0
  6. package/.agent/.shared/ui-ux-pro-max/data/prompts.csv +24 -0
  7. package/.agent/.shared/ui-ux-pro-max/data/react-performance.csv +45 -0
  8. package/.agent/.shared/ui-ux-pro-max/data/stacks/flutter.csv +53 -0
  9. package/.agent/.shared/ui-ux-pro-max/data/stacks/html-tailwind.csv +56 -0
  10. package/.agent/.shared/ui-ux-pro-max/data/stacks/jetpack-compose.csv +53 -0
  11. package/.agent/.shared/ui-ux-pro-max/data/stacks/nextjs.csv +53 -0
  12. package/.agent/.shared/ui-ux-pro-max/data/stacks/nuxt-ui.csv +51 -0
  13. package/.agent/.shared/ui-ux-pro-max/data/stacks/nuxtjs.csv +59 -0
  14. package/.agent/.shared/ui-ux-pro-max/data/stacks/react-native.csv +52 -0
  15. package/.agent/.shared/ui-ux-pro-max/data/stacks/react.csv +54 -0
  16. package/.agent/.shared/ui-ux-pro-max/data/stacks/shadcn.csv +61 -0
  17. package/.agent/.shared/ui-ux-pro-max/data/stacks/svelte.csv +54 -0
  18. package/.agent/.shared/ui-ux-pro-max/data/stacks/swiftui.csv +51 -0
  19. package/.agent/.shared/ui-ux-pro-max/data/stacks/vue.csv +50 -0
  20. package/.agent/.shared/ui-ux-pro-max/data/styles.csv +59 -0
  21. package/.agent/.shared/ui-ux-pro-max/data/typography.csv +58 -0
  22. package/.agent/.shared/ui-ux-pro-max/data/ui-reasoning.csv +101 -0
  23. package/.agent/.shared/ui-ux-pro-max/data/ux-guidelines.csv +100 -0
  24. package/.agent/.shared/ui-ux-pro-max/data/web-interface.csv +31 -0
  25. package/.agent/.shared/ui-ux-pro-max/scripts/__pycache__/core.cpython-313.pyc +0 -0
  26. package/.agent/.shared/ui-ux-pro-max/scripts/__pycache__/design_system.cpython-313.pyc +0 -0
  27. package/.agent/.shared/ui-ux-pro-max/scripts/core.py +258 -0
  28. package/.agent/.shared/ui-ux-pro-max/scripts/design_system.py +1067 -0
  29. package/.agent/.shared/ui-ux-pro-max/scripts/search.py +106 -0
  30. package/.agent/ARCHITECTURE.md +288 -0
  31. package/.agent/agents/ambient-agent.md +57 -0
  32. package/.agent/agents/debugger.md +55 -0
  33. package/.agent/agents/email-assistant.md +49 -0
  34. package/.agent/agents/file-manager.md +42 -0
  35. package/.agent/agents/python-developer.md +60 -0
  36. package/.agent/agents/scheduler.md +59 -0
  37. package/.agent/agents/web-developer.md +45 -0
  38. package/.agent/data/default.json +29 -0
  39. package/.agent/data/plugins-state.json +255 -0
  40. package/.agent/mcp_config.json +4 -0
  41. package/.agent/mcp_config_updated.json +12 -0
  42. package/.agent/plugins.json +5 -0
  43. package/.agent/rules/GEMINI.md +273 -0
  44. package/.agent/rules/allow-rule.md +77 -0
  45. package/.agent/rules/log-rule.md +83 -0
  46. package/.agent/rules/security-rule.md +93 -0
  47. package/.agent/scripts/auto_preview.py +148 -0
  48. package/.agent/scripts/checklist.py +217 -0
  49. package/.agent/scripts/session_manager.py +120 -0
  50. package/.agent/scripts/verify_all.py +327 -0
  51. package/.agent/skills/api-patterns/SKILL.md +81 -0
  52. package/.agent/skills/api-patterns/api-style.md +42 -0
  53. package/.agent/skills/api-patterns/auth.md +24 -0
  54. package/.agent/skills/api-patterns/documentation.md +26 -0
  55. package/.agent/skills/api-patterns/graphql.md +41 -0
  56. package/.agent/skills/api-patterns/rate-limiting.md +31 -0
  57. package/.agent/skills/api-patterns/response.md +37 -0
  58. package/.agent/skills/api-patterns/rest.md +40 -0
  59. package/.agent/skills/api-patterns/scripts/api_validator.py +211 -0
  60. package/.agent/skills/api-patterns/security-testing.md +122 -0
  61. package/.agent/skills/api-patterns/trpc.md +41 -0
  62. package/.agent/skills/api-patterns/versioning.md +22 -0
  63. package/.agent/skills/app-builder/SKILL.md +75 -0
  64. package/.agent/skills/app-builder/agent-coordination.md +71 -0
  65. package/.agent/skills/app-builder/feature-building.md +53 -0
  66. package/.agent/skills/app-builder/project-detection.md +34 -0
  67. package/.agent/skills/app-builder/scaffolding.md +118 -0
  68. package/.agent/skills/app-builder/tech-stack.md +40 -0
  69. package/.agent/skills/app-builder/templates/SKILL.md +39 -0
  70. package/.agent/skills/app-builder/templates/astro-static/TEMPLATE.md +76 -0
  71. package/.agent/skills/app-builder/templates/chrome-extension/TEMPLATE.md +92 -0
  72. package/.agent/skills/app-builder/templates/cli-tool/TEMPLATE.md +88 -0
  73. package/.agent/skills/app-builder/templates/electron-desktop/TEMPLATE.md +88 -0
  74. package/.agent/skills/app-builder/templates/express-api/TEMPLATE.md +83 -0
  75. package/.agent/skills/app-builder/templates/flutter-app/TEMPLATE.md +90 -0
  76. package/.agent/skills/app-builder/templates/monorepo-turborepo/TEMPLATE.md +90 -0
  77. package/.agent/skills/app-builder/templates/nextjs-fullstack/TEMPLATE.md +122 -0
  78. package/.agent/skills/app-builder/templates/nextjs-saas/TEMPLATE.md +122 -0
  79. package/.agent/skills/app-builder/templates/nextjs-static/TEMPLATE.md +169 -0
  80. package/.agent/skills/app-builder/templates/nuxt-app/TEMPLATE.md +134 -0
  81. package/.agent/skills/app-builder/templates/python-fastapi/TEMPLATE.md +83 -0
  82. package/.agent/skills/app-builder/templates/react-native-app/TEMPLATE.md +119 -0
  83. package/.agent/skills/architecture/SKILL.md +55 -0
  84. package/.agent/skills/architecture/context-discovery.md +43 -0
  85. package/.agent/skills/architecture/examples.md +94 -0
  86. package/.agent/skills/architecture/pattern-selection.md +68 -0
  87. package/.agent/skills/architecture/patterns-reference.md +50 -0
  88. package/.agent/skills/architecture/trade-off-analysis.md +77 -0
  89. package/.agent/skills/clean-code/SKILL.md +201 -0
  90. package/.agent/skills/doc.md +177 -0
  91. package/.agent/skills/frontend-design/SKILL.md +418 -0
  92. package/.agent/skills/frontend-design/animation-guide.md +331 -0
  93. package/.agent/skills/frontend-design/color-system.md +311 -0
  94. package/.agent/skills/frontend-design/decision-trees.md +418 -0
  95. package/.agent/skills/frontend-design/motion-graphics.md +306 -0
  96. package/.agent/skills/frontend-design/scripts/accessibility_checker.py +183 -0
  97. package/.agent/skills/frontend-design/scripts/ux_audit.py +722 -0
  98. package/.agent/skills/frontend-design/typography-system.md +345 -0
  99. package/.agent/skills/frontend-design/ux-psychology.md +1116 -0
  100. package/.agent/skills/frontend-design/visual-effects.md +383 -0
  101. package/.agent/skills/i18n-localization/SKILL.md +154 -0
  102. package/.agent/skills/i18n-localization/scripts/i18n_checker.py +241 -0
  103. package/.agent/skills/mcp-builder/SKILL.md +176 -0
  104. package/.agent/skills/web-design-guidelines/SKILL.md +57 -0
  105. package/.agent/workflows/brainstorm.md +113 -0
  106. package/.agent/workflows/create.md +59 -0
  107. package/.agent/workflows/debug.md +103 -0
  108. package/.agent/workflows/deploy.md +176 -0
  109. package/.agent/workflows/enhance.md +63 -0
  110. package/.agent/workflows/orchestrate.md +237 -0
  111. package/.agent/workflows/plan.md +89 -0
  112. package/.agent/workflows/preview.md +81 -0
  113. package/.agent/workflows/simple-test.md +42 -0
  114. package/.agent/workflows/status.md +86 -0
  115. package/.agent/workflows/structured-orchestrate.md +180 -0
  116. package/.agent/workflows/test.md +144 -0
  117. package/.agent/workflows/ui-ux-pro-max.md +296 -0
  118. package/.claude/settings.local.json +11 -1
  119. package/.editorconfig +56 -0
  120. package/.husky/pre-commit +4 -0
  121. package/.lintstagedrc +7 -0
  122. package/.prettierignore +29 -0
  123. package/.prettierrc +11 -0
  124. package/CLAUDE.md +2 -0
  125. package/README.md +64 -55
  126. package/SPEC.md +102 -61
  127. package/cli/bin/foliko.js +11 -11
  128. package/cli/src/commands/chat.js +143 -141
  129. package/cli/src/commands/list.js +93 -90
  130. package/cli/src/index.js +75 -75
  131. package/cli/src/ui/chat-ui.js +201 -199
  132. package/cli/src/utils/ansi.js +40 -40
  133. package/cli/src/utils/markdown.js +292 -296
  134. package/docker-compose.yml +1 -1
  135. package/docs/ai-sdk-optimization.md +655 -643
  136. package/docs/features.md +80 -80
  137. package/docs/quick-reference.md +49 -46
  138. package/docs/user-manual.md +411 -380
  139. package/examples/ambient-example.js +194 -196
  140. package/examples/basic.js +50 -45
  141. package/examples/bootstrap.js +121 -112
  142. package/examples/mcp-example.js +19 -16
  143. package/examples/skill-example.js +20 -20
  144. package/examples/test-chat.js +137 -135
  145. package/examples/test-mcp.js +85 -79
  146. package/examples/test-reload.js +59 -61
  147. package/examples/test-telegram.js +50 -50
  148. package/examples/test-tg-bot.js +45 -42
  149. package/examples/test-tg-simple.js +47 -46
  150. package/examples/test-tg.js +62 -62
  151. package/examples/test-think.js +43 -37
  152. package/examples/test-web-plugin.js +103 -98
  153. package/examples/test-weixin-feishu.js +103 -100
  154. package/examples/workflow.js +158 -158
  155. package/package.json +37 -3
  156. package/plugins/ai-plugin.js +102 -100
  157. package/plugins/ambient-agent/EventWatcher.js +113 -0
  158. package/plugins/ambient-agent/ExplorerLoop.js +640 -0
  159. package/plugins/ambient-agent/GoalManager.js +197 -0
  160. package/plugins/ambient-agent/Reflector.js +95 -0
  161. package/plugins/ambient-agent/StateStore.js +90 -0
  162. package/plugins/ambient-agent/constants.js +101 -0
  163. package/plugins/ambient-agent/index.js +579 -0
  164. package/plugins/audit-plugin.js +187 -187
  165. package/plugins/default-plugins.js +662 -649
  166. package/plugins/email/constants.js +64 -0
  167. package/plugins/email/handlers.js +461 -0
  168. package/plugins/email/index.js +278 -0
  169. package/plugins/email/monitor.js +269 -0
  170. package/plugins/email/parser.js +138 -0
  171. package/plugins/email/reply.js +151 -0
  172. package/plugins/email/utils.js +124 -0
  173. package/plugins/feishu-plugin.js +481 -477
  174. package/plugins/file-system-plugin.js +826 -476
  175. package/plugins/install-plugin.js +199 -197
  176. package/plugins/python-executor-plugin.js +367 -365
  177. package/plugins/python-plugin-loader.js +481 -479
  178. package/plugins/rules-plugin.js +294 -292
  179. package/plugins/scheduler-plugin.js +691 -689
  180. package/plugins/session-plugin.js +369 -367
  181. package/plugins/shell-executor-plugin.js +197 -197
  182. package/plugins/storage-plugin.js +240 -238
  183. package/plugins/subagent-plugin.js +845 -785
  184. package/plugins/telegram-plugin.js +482 -475
  185. package/plugins/think-plugin.js +345 -343
  186. package/plugins/tools-plugin.js +196 -194
  187. package/plugins/web-plugin.js +606 -604
  188. package/plugins/weixin-plugin.js +545 -538
  189. package/reports/system-health-report-20260401.md +79 -0
  190. package/skills/ambient-agent/SKILL.md +49 -39
  191. package/skills/foliko-dev/AGENTS.md +64 -61
  192. package/skills/foliko-dev/SKILL.md +125 -119
  193. package/skills/mcp-usage/SKILL.md +19 -17
  194. package/skills/python-plugin-dev/SKILL.md +16 -15
  195. package/skills/skill-guide/SKILL.md +12 -12
  196. package/skills/subagent-guide/SKILL.md +237 -0
  197. package/skills/workflow-guide/SKILL.md +90 -45
  198. package/skills/workflow-troubleshooting/DEBUGGING.md +36 -21
  199. package/skills/workflow-troubleshooting/SKILL.md +156 -79
  200. package/src/capabilities/index.js +11 -11
  201. package/src/capabilities/skill-manager.js +609 -595
  202. package/src/capabilities/workflow-engine.js +1109 -1195
  203. package/src/core/agent-chat.js +882 -735
  204. package/src/core/agent.js +892 -688
  205. package/src/core/framework.js +465 -431
  206. package/src/core/index.js +19 -19
  207. package/src/core/plugin-base.js +219 -219
  208. package/src/core/plugin-manager.js +863 -767
  209. package/src/core/provider.js +114 -111
  210. package/src/core/sub-agent-config.js +264 -0
  211. package/src/core/system-prompt-builder.js +120 -0
  212. package/src/core/tool-registry.js +517 -134
  213. package/src/core/tool-router.js +297 -216
  214. package/src/executors/executor-base.js +12 -12
  215. package/src/executors/mcp-executor.js +741 -729
  216. package/src/index.js +25 -37
  217. package/src/utils/circuit-breaker.js +301 -0
  218. package/src/utils/error-boundary.js +363 -0
  219. package/src/utils/error.js +374 -0
  220. package/src/utils/event-emitter.js +97 -97
  221. package/src/utils/id.js +133 -0
  222. package/src/utils/index.js +217 -3
  223. package/src/utils/logger.js +181 -0
  224. package/src/utils/plugin-helpers.js +90 -0
  225. package/src/utils/retry.js +122 -0
  226. package/src/utils/sandbox.js +292 -0
  227. package/test/tool-registry-validation.test.js +218 -0
  228. package/test_report.md +70 -0
  229. package/website/docs/api.html +169 -107
  230. package/website/docs/configuration.html +296 -144
  231. package/website/docs/plugin-development.html +154 -85
  232. package/website/docs/project-structure.html +110 -109
  233. package/website/docs/skill-development.html +117 -61
  234. package/website/index.html +209 -205
  235. package/website/script.js +136 -133
  236. package/website/styles.css +1 -1
  237. package/plugins/ambient-agent-plugin.js +0 -1565
  238. package/plugins/email.js +0 -1142
@@ -1,134 +1,517 @@
1
- /**
2
- * ToolRegistry 工具注册表
3
- * 统一管理所有工具
4
- */
5
-
6
- const { EventEmitter } = require('../utils/event-emitter')
7
-
8
- class ToolRegistry extends EventEmitter {
9
- constructor() {
10
- super()
11
- this._tools = new Map()
12
- }
13
-
14
- /**
15
- * 注册工具
16
- * @param {Object} tool - 工具定义
17
- * @param {string} tool.name - 工具名称
18
- * @param {string} tool.description - 工具描述
19
- * @param {Function} tool.execute - 执行函数
20
- * @param {Object} [tool.parameters] - 参数 schema
21
- */
22
- register(tool) {
23
- if (!tool.name) {
24
- throw new Error('Tool must have a name')
25
- }
26
- if (typeof tool.execute !== 'function') {
27
- throw new Error(`Tool '${tool.name}' must have an execute function`)
28
- }
29
-
30
- this._tools.set(tool.name, {
31
- name: tool.name,
32
- description: tool.description || '',
33
- inputSchema: tool.inputSchema || null,
34
- parameters: tool.parameters || null,
35
- execute: tool.execute
36
- })
37
-
38
- this.emit('tool:registered', tool)
39
- return this
40
- }
41
-
42
- /**
43
- * 批量注册工具
44
- * @param {Array<Object>} tools - 工具数组
45
- */
46
- registerMany(tools) {
47
- for (const tool of tools) {
48
- this.register(tool)
49
- }
50
- return this
51
- }
52
-
53
- /**
54
- * 注销工具
55
- * @param {string} name - 工具名称
56
- */
57
- unregister(name) {
58
- const tool = this._tools.get(name)
59
- if (tool) {
60
- this._tools.delete(name)
61
- this.emit('tool:unregistered', tool)
62
- }
63
- return this
64
- }
65
-
66
- /**
67
- * 获取工具
68
- * @param {string} name - 工具名称
69
- * @returns {Object|undefined}
70
- */
71
- get(name) {
72
- return this._tools.get(name)
73
- }
74
-
75
- /**
76
- * 获取所有工具
77
- * @returns {Array<Object>}
78
- */
79
- getAll() {
80
- return Array.from(this._tools.values())
81
- }
82
-
83
- /**
84
- * 检查工具是否存在
85
- * @param {string} name - 工具名称
86
- * @returns {boolean}
87
- */
88
- has(name) {
89
- return this._tools.has(name)
90
- }
91
-
92
- /**
93
- * 执行工具
94
- * @param {string} name - 工具名称
95
- * @param {Object} args - 参数
96
- * @param {Framework} framework - 框架实例
97
- * @returns {Promise<any>}
98
- */
99
- async execute(name, args, framework) {
100
- const tool = this._tools.get(name)
101
- if (!tool) {
102
- throw new Error(`Tool '${name}' not found`)
103
- }
104
-
105
- try {
106
- this.emit('tool:call', { name, args })
107
- const result = await tool.execute(args, framework)
108
- this.emit('tool:result', { name, args, result })
109
- return result
110
- } catch (err) {
111
- this.emit('tool:error', { name, args, error: err })
112
- throw err
113
- }
114
- }
115
-
116
- /**
117
- * 清空所有工具
118
- */
119
- clear() {
120
- this._tools.clear()
121
- this.emit('tool:cleared')
122
- return this
123
- }
124
-
125
- /**
126
- * 获取工具数量
127
- * @returns {number}
128
- */
129
- size() {
130
- return this._tools.size
131
- }
132
- }
133
-
134
- module.exports = { ToolRegistry }
1
+ /**
2
+ * ToolRegistry 工具注册表
3
+ * 统一管理所有工具
4
+ */
5
+
6
+ const { EventEmitter } = require('../utils/event-emitter');
7
+ const { globalCircuitBreaker } = require('../utils/circuit-breaker');
8
+ const { logger } = require('../utils/logger');
9
+ const { z } = require('zod');
10
+ const { withRetry, isNetworkError, PRESETS } = require('../utils/retry');
11
+ const { ErrorBoundary, Severity, RecoveryAction } = require('../utils/error-boundary');
12
+
13
+ const log = logger.child('ToolRegistry');
14
+
15
+ class ToolRegistry extends EventEmitter {
16
+ constructor(options = {}) {
17
+ super();
18
+ this._tools = new Map();
19
+
20
+ // 熔断器配置
21
+ this._circuitBreakerEnabled = options.circuitBreaker !== false; // 默认启用
22
+ this._circuitBreaker = globalCircuitBreaker;
23
+
24
+ // 熔断器配置选项(可被工具覆盖)
25
+ this._defaultCircuitBreakerOptions = {
26
+ failureThreshold: options.failureThreshold || 3,
27
+ successThreshold: options.successThreshold || 2,
28
+ timeout: options.circuitBreakerTimeout || 60000,
29
+ };
30
+ }
31
+
32
+ /**
33
+ * 注册工具
34
+ * @param {Object} tool - 工具定义
35
+ * @param {string} tool.name - 工具名称
36
+ * @param {string} tool.description - 工具描述
37
+ * @param {Function} tool.execute - 执行函数
38
+ * @param {Object} [tool.parameters] - 参数 schema
39
+ * @param {Object} [tool.circuitBreaker] - 熔断器选项(覆盖默认值)
40
+ * @param {Object} [tool.retry] - 重试配置(可使用 'fast', 'standard', 'conservative' 或自定义配置)
41
+ */
42
+ register(tool) {
43
+ if (!tool.name) {
44
+ throw new Error('Tool must have a name');
45
+ }
46
+ if (typeof tool.execute !== 'function') {
47
+ throw new Error(`Tool '${tool.name}' must have an execute function`);
48
+ }
49
+
50
+ this._tools.set(tool.name, {
51
+ name: tool.name,
52
+ description: tool.description || '',
53
+ inputSchema: tool.inputSchema || null,
54
+ parameters: tool.parameters || null,
55
+ execute: tool.execute,
56
+ circuitBreakerOptions: tool.circuitBreaker || null,
57
+ retryOptions: tool.retry || null, // 新的重试配置
58
+ });
59
+
60
+ this.emit('tool:registered', tool);
61
+ return this;
62
+ }
63
+
64
+ /**
65
+ * 批量注册工具
66
+ * @param {Array<Object>} tools - 工具数组
67
+ */
68
+ registerMany(tools) {
69
+ for (const tool of tools) {
70
+ this.register(tool);
71
+ }
72
+ return this;
73
+ }
74
+
75
+ /**
76
+ * 注销工具
77
+ * @param {string} name - 工具名称
78
+ */
79
+ unregister(name) {
80
+ const tool = this._tools.get(name);
81
+ if (tool) {
82
+ this._tools.delete(name);
83
+ // 清理该工具的熔断器
84
+ this._circuitBreaker.remove(name);
85
+ this.emit('tool:unregistered', tool);
86
+ }
87
+ return this;
88
+ }
89
+
90
+ /**
91
+ * 获取工具
92
+ * @param {string} name - 工具名称
93
+ * @returns {Object|undefined}
94
+ */
95
+ get(name) {
96
+ return this._tools.get(name);
97
+ }
98
+
99
+ /**
100
+ * 获取所有工具
101
+ * @returns {Array<Object>}
102
+ */
103
+ getAll() {
104
+ return Array.from(this._tools.values());
105
+ }
106
+
107
+ /**
108
+ * 检查工具是否存在
109
+ * @param {string} name - 工具名称
110
+ * @returns {boolean}
111
+ */
112
+ has(name) {
113
+ return this._tools.has(name);
114
+ }
115
+
116
+ /**
117
+ * 获取工具熔断器状态
118
+ * @param {string} name - 工具名称
119
+ * @returns {Object|null}
120
+ */
121
+ getCircuitBreakerStatus(name) {
122
+ if (!this._circuitBreakerEnabled) {
123
+ return { enabled: false };
124
+ }
125
+ const breaker = this._circuitBreaker.get(name);
126
+ return breaker ? breaker.getStatus() : null;
127
+ }
128
+
129
+ /**
130
+ * 获取所有工具的熔断器状态
131
+ * @returns {Object[]}
132
+ */
133
+ getAllCircuitBreakerStatus() {
134
+ if (!this._circuitBreakerEnabled) {
135
+ return [];
136
+ }
137
+ return this._circuitBreaker.getAllStatus();
138
+ }
139
+
140
+ /**
141
+ * 重置工具的熔断器
142
+ * @param {string} [name] - 工具名称,不传则重置所有
143
+ */
144
+ resetCircuitBreaker(name) {
145
+ if (!this._circuitBreakerEnabled) {
146
+ return;
147
+ }
148
+ if (name) {
149
+ this._circuitBreaker.get(name)?.reset();
150
+ } else {
151
+ this._circuitBreaker.resetAll();
152
+ }
153
+ }
154
+
155
+ /**
156
+ * 执行工具(带熔断保护、参数验证、重试策略和错误边界)
157
+ * @param {string} name - 工具名称
158
+ * @param {Object} args - 参数
159
+ * @param {Framework} framework - 框架实例
160
+ * @returns {Promise<any>}
161
+ */
162
+ async execute(name, args, framework) {
163
+ const tool = this._tools.get(name);
164
+ if (!tool) {
165
+ const error = new Error(`Tool '${name}' not found`);
166
+ this.emit('tool:error', { name, args, error });
167
+ throw error;
168
+ }
169
+
170
+ // ========== 参数验证 ==========
171
+ let validatedArgs = args;
172
+ if (tool.inputSchema) {
173
+ try {
174
+ // 如果 inputSchema 是 Zod schema,直接验证
175
+ if (tool.inputSchema instanceof z.ZodType) {
176
+ validatedArgs = tool.inputSchema.parse(args);
177
+ }
178
+ // 如果是普通对象(由 inputSchema.toJSON() 返回的格式),尝试构建 Zod schema
179
+ else if (
180
+ tool.inputSchema.type === 'object' ||
181
+ (tool.inputSchema.jsonSchema && tool.inputSchema.jsonSchema.type === 'object')
182
+ ) {
183
+ const schemaDef = tool.inputSchema.jsonSchema || tool.inputSchema;
184
+ const zodSchema = this._buildZodSchema(schemaDef);
185
+ if (zodSchema) {
186
+ validatedArgs = zodSchema.parse(args);
187
+ }
188
+ }
189
+ } catch (err) {
190
+ // 参数验证失败,返回详细的验证错误
191
+ if (err instanceof z.ZodError) {
192
+ const error = new Error(`Tool '${name}' 参数验证失败`);
193
+ error.validationErrors = err.errors.map((e) => ({
194
+ path: e.path.join('.'),
195
+ message: e.message,
196
+ expected: e.expected,
197
+ received: e.received,
198
+ }));
199
+ error.toolName = name;
200
+ error.toolArgs = args;
201
+ error.errorType = 'VALIDATION_ERROR';
202
+ this.emit('tool:error', { name, args, error });
203
+ throw error;
204
+ }
205
+ // 其他错误(可能是非 Zod 错误),包装后抛出
206
+ const error = new Error(`Tool '${name}' 参数解析失败: ${err.message}`);
207
+ error.toolName = name;
208
+ error.toolArgs = args;
209
+ error.errorType = 'PARSE_ERROR';
210
+ this.emit('tool:error', { name, args, error });
211
+ throw error;
212
+ }
213
+ }
214
+
215
+ // 获取该工具的熔断器选项和重试配置
216
+ const cbOptions = {
217
+ ...this._defaultCircuitBreakerOptions,
218
+ ...(tool.circuitBreakerOptions || {}),
219
+ };
220
+
221
+ // 创建工具执行的错误边界
222
+ const toolErrorBoundary = new ErrorBoundary({
223
+ name: `tool:${name}`,
224
+ logErrors: true,
225
+ propagateErrors: false,
226
+ onError: (error, context) => {
227
+ // 判断错误类型,决定恢复策略
228
+ if (isNetworkError(error)) {
229
+ return RecoveryAction.RETRY;
230
+ }
231
+ if (error.circuitBreakerOpen) {
232
+ return RecoveryAction.SKIP;
233
+ }
234
+ // 参数验证错误不重试
235
+ if (error.errorType === 'VALIDATION_ERROR' || error.errorType === 'PARSE_ERROR') {
236
+ return RecoveryAction.ABORT;
237
+ }
238
+ return RecoveryAction.RETRY;
239
+ },
240
+ });
241
+
242
+ try {
243
+ this.emit('tool:call', { name, args: validatedArgs });
244
+
245
+ // 确定重试配置
246
+ let retryConfig = tool.retryOptions;
247
+ if (retryConfig) {
248
+ // 如果是字符串,转换为预设配置
249
+ if (typeof retryConfig === 'string') {
250
+ retryConfig = PRESETS[retryConfig] || PRESETS.standard;
251
+ }
252
+ // 添加网络错误判断
253
+ retryConfig.shouldRetry = retryConfig.shouldRetry || isNetworkError;
254
+ }
255
+
256
+ // 定义实际执行函数
257
+ const executeTool = async () => {
258
+ if (this._circuitBreakerEnabled) {
259
+ // 使用熔断器执行
260
+ return await this._circuitBreaker.execute(
261
+ name,
262
+ async () => {
263
+ return await tool.execute(validatedArgs, framework);
264
+ },
265
+ cbOptions
266
+ );
267
+ } else {
268
+ // 不使用熔断器,直接执行
269
+ return await tool.execute(validatedArgs, framework);
270
+ }
271
+ };
272
+
273
+ let result;
274
+ if (retryConfig) {
275
+ // 使用重试策略执行
276
+ result = await withRetry(executeTool, {
277
+ ...PRESETS.standard,
278
+ ...retryConfig,
279
+ onRetry: (error, attempt, delay) => {
280
+ log.debug(
281
+ `Tool '${name}' retry attempt ${attempt}: ${error.message} (delay: ${delay}ms)`
282
+ );
283
+ this.emit('tool:retry', { name, args: validatedArgs, error, attempt, delay });
284
+ },
285
+ });
286
+ } else {
287
+ // 不使用重试,直接执行
288
+ result = await executeTool();
289
+ }
290
+
291
+ this.emit('tool:result', { name, args, result });
292
+ return result;
293
+ } catch (err) {
294
+ // 包装错误信息,添加详细上下文
295
+ const wrappedError = this._wrapToolError(err, name, validatedArgs);
296
+
297
+ this.emit('tool:error', { name, args, error: wrappedError });
298
+ throw wrappedError;
299
+ }
300
+ }
301
+
302
+ /**
303
+ * 包装工具错误,添加详细上下文信息
304
+ * @private
305
+ */
306
+ _wrapToolError(err, toolName, args) {
307
+ const wrappedError = new Error(`Tool '${toolName}' execution failed: ${err.message}`);
308
+
309
+ // 保留原始错误引用
310
+ wrappedError.originalError = err;
311
+ wrappedError.toolName = toolName;
312
+ wrappedError.toolArgs = args;
313
+
314
+ // 错误分类
315
+ if (isNetworkError(err)) {
316
+ wrappedError.errorType = 'NETWORK_ERROR';
317
+ wrappedError.recoverable = true;
318
+ } else if (err.message && err.message.includes('Circuit breaker is OPEN')) {
319
+ wrappedError.errorType = 'CIRCUIT_BREAKER_OPEN';
320
+ wrappedError.circuitBreakerOpen = true;
321
+ wrappedError.recoverable = false;
322
+ log.warn(`Circuit breaker open for tool '${toolName}':`, err.message);
323
+ } else if (err.name === 'ValidationError' || err.name === 'ZodError') {
324
+ wrappedError.errorType = 'VALIDATION_ERROR';
325
+ wrappedError.recoverable = false;
326
+ } else if (err.name === 'TimeoutError') {
327
+ wrappedError.errorType = 'TIMEOUT_ERROR';
328
+ wrappedError.recoverable = true;
329
+ } else {
330
+ wrappedError.errorType = 'UNKNOWN_ERROR';
331
+ wrappedError.recoverable = false;
332
+ }
333
+
334
+ // 错误发生时间
335
+ wrappedError.timestamp = new Date().toISOString();
336
+
337
+ // 堆栈跟踪(保留原始堆栈)
338
+ wrappedError.stack = err.stack;
339
+
340
+ return wrappedError;
341
+ }
342
+
343
+ /**
344
+ * 清空所有工具
345
+ */
346
+ clear() {
347
+ this._tools.clear();
348
+ this._circuitBreaker.clear();
349
+ this.emit('tool:cleared');
350
+ return this;
351
+ }
352
+
353
+ /**
354
+ * 获取工具数量
355
+ * @returns {number}
356
+ */
357
+ size() {
358
+ return this._tools.size;
359
+ }
360
+
361
+ /**
362
+ * 将 JSON Schema 转换为 Zod Schema
363
+ * @param {Object} schema - JSON Schema 对象
364
+ * @returns {z.ZodObject|null} - 转换后的 Zod Schema,或 null 如果转换失败
365
+ */
366
+ _buildZodSchema(schema) {
367
+ if (!schema || !schema.properties) {
368
+ return null;
369
+ }
370
+
371
+ try {
372
+ const shape = {};
373
+ const properties = schema.properties || {};
374
+ const required = schema.required || [];
375
+
376
+ for (const [key, prop] of Object.entries(properties)) {
377
+ shape[key] = this._jsonSchemaToZod(prop, key, required.includes(key));
378
+ }
379
+
380
+ const zodSchema = z.object(shape);
381
+
382
+ // 如果没有必需字段,直接返回
383
+ if (required.length === 0) {
384
+ return zodSchema.optional();
385
+ }
386
+
387
+ // 返回带有必需字段约束的 schema
388
+ return zodSchema;
389
+ } catch (err) {
390
+ log.warn('Failed to build Zod schema from JSON Schema:', err.message);
391
+ return null;
392
+ }
393
+ }
394
+
395
+ /**
396
+ * 将单个 JSON Schema 属性转换为 Zod Type
397
+ * @param {Object} prop - JSON Schema 属性定义
398
+ * @param {string} name - 属性名称(用于错误消息)
399
+ * @param {boolean} isRequired - 是否为必需字段
400
+ * @returns {z.ZodType} - Zod 类型
401
+ */
402
+ _jsonSchemaToZod(prop, name, isRequired = false) {
403
+ // 处理 $ref(不支持,返回 any)
404
+ if (prop.$ref) {
405
+ log.warn(`Property '${name}' has $ref, using z.any()`);
406
+ return isRequired ? z.any() : z.any().optional();
407
+ }
408
+
409
+ // 处理 enum
410
+ if (prop.enum) {
411
+ let zodType = z.string().enum(prop.enum);
412
+ if (prop.nullable) {
413
+ zodType = zodType.nullable();
414
+ }
415
+ return isRequired ? zodType : zodType.optional();
416
+ }
417
+
418
+ // 根据 type 确定 Zod 类型
419
+ const type = prop.type || 'string';
420
+
421
+ let zodType;
422
+ switch (type) {
423
+ case 'string':
424
+ zodType = z.string();
425
+ if (prop.minLength !== undefined) {
426
+ zodType = zodType.min(
427
+ prop.minLength,
428
+ prop.description || `Minimum length is ${prop.minLength}`
429
+ );
430
+ }
431
+ if (prop.maxLength !== undefined) {
432
+ zodType = zodType.max(
433
+ prop.maxLength,
434
+ prop.description || `Maximum length is ${prop.maxLength}`
435
+ );
436
+ }
437
+ if (prop.pattern) {
438
+ zodType = zodType.regex(
439
+ new RegExp(prop.pattern),
440
+ prop.description || `Pattern: ${prop.pattern}`
441
+ );
442
+ }
443
+ break;
444
+
445
+ case 'number':
446
+ case 'integer':
447
+ zodType = type === 'integer' ? z.number().int() : z.number();
448
+ if (prop.minimum !== undefined) {
449
+ zodType = zodType.min(prop.minimum);
450
+ }
451
+ if (prop.maximum !== undefined) {
452
+ zodType = zodType.max(prop.maximum);
453
+ }
454
+ break;
455
+
456
+ case 'boolean':
457
+ zodType = z.boolean();
458
+ break;
459
+
460
+ case 'array':
461
+ zodType = z.array(
462
+ prop.items ? this._jsonSchemaToZod(prop.items, `${name}[]`, false) : z.any()
463
+ );
464
+ if (prop.minItems !== undefined) {
465
+ zodType = zodType.min(prop.minItems);
466
+ }
467
+ if (prop.maxItems !== undefined) {
468
+ zodType = zodType.max(prop.maxItems);
469
+ }
470
+ break;
471
+
472
+ case 'object':
473
+ if (prop.properties) {
474
+ // 递归处理嵌套对象时,保持必需性信息
475
+ const nestedRequired = prop.required || [];
476
+ zodType = z.object(
477
+ Object.fromEntries(
478
+ Object.entries(prop.properties).map(([k, v]) => [
479
+ k,
480
+ this._jsonSchemaToZod(v, k, nestedRequired.includes(k)),
481
+ ])
482
+ )
483
+ );
484
+ } else {
485
+ zodType = z.record(z.any());
486
+ }
487
+ break;
488
+
489
+ case 'null':
490
+ zodType = z.null();
491
+ break;
492
+
493
+ default:
494
+ zodType = z.any();
495
+ }
496
+
497
+ // 处理 nullable
498
+ if (prop.nullable === true || (Array.isArray(prop.type) && prop.type.includes('null'))) {
499
+ zodType = zodType.nullable();
500
+ }
501
+
502
+ // 处理 default 值(作为可选的默认值)
503
+ if (prop.default !== undefined) {
504
+ zodType = zodType.optional().default(prop.default);
505
+ }
506
+
507
+ // 如果不是必需的,标记为可选
508
+ // 注意:必需性由父级 object 的 required 数组决定
509
+ if (!isRequired && prop.default === undefined) {
510
+ zodType = zodType.optional();
511
+ }
512
+
513
+ return zodType;
514
+ }
515
+ }
516
+
517
+ module.exports = { ToolRegistry };