foliko 1.0.73 → 1.0.75

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 (237) 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 +157 -149
  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 +4 -4
  128. package/cli/src/commands/chat.js +53 -51
  129. package/cli/src/commands/list.js +40 -37
  130. package/cli/src/index.js +18 -18
  131. package/cli/src/ui/chat-ui.js +78 -76
  132. package/cli/src/utils/ansi.js +15 -15
  133. package/cli/src/utils/markdown.js +112 -116
  134. package/docker-compose.yml +1 -1
  135. package/docs/ai-sdk-optimization.md +655 -636
  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 +95 -97
  140. package/examples/basic.js +115 -110
  141. package/examples/bootstrap.js +52 -43
  142. package/examples/mcp-example.js +56 -53
  143. package/examples/skill-example.js +49 -49
  144. package/examples/test-chat.js +60 -58
  145. package/examples/test-mcp.js +49 -43
  146. package/examples/test-reload.js +38 -40
  147. package/examples/test-telegram.js +3 -3
  148. package/examples/test-tg-bot.js +7 -4
  149. package/examples/test-tg-simple.js +4 -3
  150. package/examples/test-tg.js +3 -3
  151. package/examples/test-think.js +13 -7
  152. package/examples/test-web-plugin.js +61 -56
  153. package/examples/test-weixin-feishu.js +40 -37
  154. package/examples/workflow.js +49 -49
  155. package/foliko-1.0.75.tgz +0 -0
  156. package/package.json +37 -3
  157. package/plugins/ai-plugin.js +7 -5
  158. package/plugins/ambient-agent/EventWatcher.js +113 -0
  159. package/plugins/ambient-agent/ExplorerLoop.js +640 -0
  160. package/plugins/ambient-agent/GoalManager.js +197 -0
  161. package/plugins/ambient-agent/Reflector.js +95 -0
  162. package/plugins/ambient-agent/StateStore.js +90 -0
  163. package/plugins/ambient-agent/constants.js +101 -0
  164. package/plugins/ambient-agent/index.js +579 -0
  165. package/plugins/default-plugins.js +62 -49
  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 +23 -19
  174. package/plugins/file-system-plugin.js +469 -120
  175. package/plugins/install-plugin.js +6 -4
  176. package/plugins/python-executor-plugin.js +3 -1
  177. package/plugins/python-plugin-loader.js +10 -8
  178. package/plugins/rules-plugin.js +5 -3
  179. package/plugins/scheduler-plugin.js +18 -16
  180. package/plugins/session-plugin.js +3 -1
  181. package/plugins/storage-plugin.js +5 -3
  182. package/plugins/subagent-plugin.js +152 -92
  183. package/plugins/telegram-plugin.js +26 -19
  184. package/plugins/think-plugin.js +4 -2
  185. package/plugins/tools-plugin.js +3 -1
  186. package/plugins/web-plugin.js +15 -13
  187. package/plugins/weixin-plugin.js +43 -36
  188. package/reports/system-health-report-20260401.md +79 -0
  189. package/skills/ambient-agent/SKILL.md +49 -39
  190. package/skills/foliko-dev/AGENTS.md +64 -61
  191. package/skills/foliko-dev/SKILL.md +125 -119
  192. package/skills/mcp-usage/SKILL.md +19 -17
  193. package/skills/python-plugin-dev/SKILL.md +16 -15
  194. package/skills/skill-guide/SKILL.md +12 -12
  195. package/skills/subagent-guide/SKILL.md +237 -0
  196. package/skills/workflow-guide/SKILL.md +90 -45
  197. package/skills/workflow-troubleshooting/DEBUGGING.md +36 -21
  198. package/skills/workflow-troubleshooting/SKILL.md +156 -79
  199. package/src/capabilities/index.js +4 -4
  200. package/src/capabilities/skill-manager.js +211 -197
  201. package/src/capabilities/workflow-engine.js +461 -547
  202. package/src/core/agent-chat.js +426 -279
  203. package/src/core/agent.js +453 -248
  204. package/src/core/framework.js +183 -149
  205. package/src/core/index.js +8 -8
  206. package/src/core/plugin-base.js +52 -52
  207. package/src/core/plugin-manager.js +377 -281
  208. package/src/core/provider.js +35 -32
  209. package/src/core/sub-agent-config.js +264 -0
  210. package/src/core/system-prompt-builder.js +120 -0
  211. package/src/core/tool-registry.js +416 -33
  212. package/src/core/tool-router.js +149 -68
  213. package/src/executors/executor-base.js +58 -58
  214. package/src/executors/mcp-executor.js +269 -257
  215. package/src/index.js +5 -17
  216. package/src/utils/circuit-breaker.js +301 -0
  217. package/src/utils/error-boundary.js +363 -0
  218. package/src/utils/error.js +374 -0
  219. package/src/utils/event-emitter.js +20 -20
  220. package/src/utils/id.js +133 -0
  221. package/src/utils/index.js +217 -3
  222. package/src/utils/logger.js +181 -0
  223. package/src/utils/plugin-helpers.js +90 -0
  224. package/src/utils/retry.js +122 -0
  225. package/src/utils/sandbox.js +292 -0
  226. package/test/tool-registry-validation.test.js +218 -0
  227. package/test_report.md +70 -0
  228. package/website/docs/api.html +169 -107
  229. package/website/docs/configuration.html +296 -144
  230. package/website/docs/plugin-development.html +154 -85
  231. package/website/docs/project-structure.html +110 -109
  232. package/website/docs/skill-development.html +117 -61
  233. package/website/index.html +209 -205
  234. package/website/script.js +20 -17
  235. package/website/styles.css +1 -1
  236. package/plugins/ambient-agent-plugin.js +0 -1565
  237. package/plugins/email.js +0 -1142
@@ -3,12 +3,30 @@
3
3
  * 统一管理所有工具
4
4
  */
5
5
 
6
- const { EventEmitter } = require('../utils/event-emitter')
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');
7
14
 
8
15
  class ToolRegistry extends EventEmitter {
9
- constructor() {
10
- super()
11
- this._tools = new Map()
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
+ };
12
30
  }
13
31
 
14
32
  /**
@@ -18,13 +36,15 @@ class ToolRegistry extends EventEmitter {
18
36
  * @param {string} tool.description - 工具描述
19
37
  * @param {Function} tool.execute - 执行函数
20
38
  * @param {Object} [tool.parameters] - 参数 schema
39
+ * @param {Object} [tool.circuitBreaker] - 熔断器选项(覆盖默认值)
40
+ * @param {Object} [tool.retry] - 重试配置(可使用 'fast', 'standard', 'conservative' 或自定义配置)
21
41
  */
22
42
  register(tool) {
23
43
  if (!tool.name) {
24
- throw new Error('Tool must have a name')
44
+ throw new Error('Tool must have a name');
25
45
  }
26
46
  if (typeof tool.execute !== 'function') {
27
- throw new Error(`Tool '${tool.name}' must have an execute function`)
47
+ throw new Error(`Tool '${tool.name}' must have an execute function`);
28
48
  }
29
49
 
30
50
  this._tools.set(tool.name, {
@@ -32,11 +52,13 @@ class ToolRegistry extends EventEmitter {
32
52
  description: tool.description || '',
33
53
  inputSchema: tool.inputSchema || null,
34
54
  parameters: tool.parameters || null,
35
- execute: tool.execute
36
- })
55
+ execute: tool.execute,
56
+ circuitBreakerOptions: tool.circuitBreaker || null,
57
+ retryOptions: tool.retry || null, // 新的重试配置
58
+ });
37
59
 
38
- this.emit('tool:registered', tool)
39
- return this
60
+ this.emit('tool:registered', tool);
61
+ return this;
40
62
  }
41
63
 
42
64
  /**
@@ -45,9 +67,9 @@ class ToolRegistry extends EventEmitter {
45
67
  */
46
68
  registerMany(tools) {
47
69
  for (const tool of tools) {
48
- this.register(tool)
70
+ this.register(tool);
49
71
  }
50
- return this
72
+ return this;
51
73
  }
52
74
 
53
75
  /**
@@ -55,12 +77,14 @@ class ToolRegistry extends EventEmitter {
55
77
  * @param {string} name - 工具名称
56
78
  */
57
79
  unregister(name) {
58
- const tool = this._tools.get(name)
80
+ const tool = this._tools.get(name);
59
81
  if (tool) {
60
- this._tools.delete(name)
61
- this.emit('tool:unregistered', tool)
82
+ this._tools.delete(name);
83
+ // 清理该工具的熔断器
84
+ this._circuitBreaker.remove(name);
85
+ this.emit('tool:unregistered', tool);
62
86
  }
63
- return this
87
+ return this;
64
88
  }
65
89
 
66
90
  /**
@@ -69,7 +93,7 @@ class ToolRegistry extends EventEmitter {
69
93
  * @returns {Object|undefined}
70
94
  */
71
95
  get(name) {
72
- return this._tools.get(name)
96
+ return this._tools.get(name);
73
97
  }
74
98
 
75
99
  /**
@@ -77,7 +101,7 @@ class ToolRegistry extends EventEmitter {
77
101
  * @returns {Array<Object>}
78
102
  */
79
103
  getAll() {
80
- return Array.from(this._tools.values())
104
+ return Array.from(this._tools.values());
81
105
  }
82
106
 
83
107
  /**
@@ -86,40 +110,244 @@ class ToolRegistry extends EventEmitter {
86
110
  * @returns {boolean}
87
111
  */
88
112
  has(name) {
89
- return this._tools.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
+ }
90
153
  }
91
154
 
92
155
  /**
93
- * 执行工具
156
+ * 执行工具(带熔断保护、参数验证、重试策略和错误边界)
94
157
  * @param {string} name - 工具名称
95
158
  * @param {Object} args - 参数
96
159
  * @param {Framework} framework - 框架实例
97
160
  * @returns {Promise<any>}
98
161
  */
99
162
  async execute(name, args, framework) {
100
- const tool = this._tools.get(name)
163
+ const tool = this._tools.get(name);
101
164
  if (!tool) {
102
- throw new Error(`Tool '${name}' not found`)
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
+ }
103
213
  }
104
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
+
105
242
  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
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;
110
293
  } catch (err) {
111
- this.emit('tool:error', { name, args, error: err })
112
- throw err
294
+ // 包装错误信息,添加详细上下文
295
+ const wrappedError = this._wrapToolError(err, name, validatedArgs);
296
+
297
+ this.emit('tool:error', { name, args, error: wrappedError });
298
+ throw wrappedError;
113
299
  }
114
300
  }
115
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
+
116
343
  /**
117
344
  * 清空所有工具
118
345
  */
119
346
  clear() {
120
- this._tools.clear()
121
- this.emit('tool:cleared')
122
- return this
347
+ this._tools.clear();
348
+ this._circuitBreaker.clear();
349
+ this.emit('tool:cleared');
350
+ return this;
123
351
  }
124
352
 
125
353
  /**
@@ -127,8 +355,163 @@ class ToolRegistry extends EventEmitter {
127
355
  * @returns {number}
128
356
  */
129
357
  size() {
130
- return this._tools.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;
131
514
  }
132
515
  }
133
516
 
134
- module.exports = { ToolRegistry }
517
+ module.exports = { ToolRegistry };