foliko 1.0.80 → 1.0.81

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 (235) hide show
  1. package/.agent/agents/code-assistant.json +14 -0
  2. package/.agent/agents/email-assistant.json +14 -0
  3. package/.agent/agents/file-assistant.json +15 -0
  4. package/.agent/agents/system-assistant.json +15 -0
  5. package/.agent/agents/web-assistant.json +12 -0
  6. package/.agent/data/ambient/goals.json +50 -0
  7. package/.agent/data/ambient/memories.json +7 -0
  8. package/.agent/data/default.json +15 -31894
  9. package/.agent/data/plugins-state.json +146 -181
  10. package/.agent/data/scheduler/tasks.json +1 -0
  11. package/.agent/mcp_config.json +1 -0
  12. package/.agent/package.json +8 -0
  13. package/.agent/plugins/__pycache__/test_plugin.cpython-312.pyc +0 -0
  14. package/.agent/plugins/system-info/index.js +387 -0
  15. package/.agent/plugins/system-info/package.json +4 -0
  16. package/.agent/plugins/system-info/test.js +40 -0
  17. package/.agent/plugins/test_plugin.py +304 -0
  18. package/.agent/plugins.json +14 -5
  19. package/.agent/python-scripts/test_sample.py +24 -0
  20. package/.agent/skills/sysinfo/SKILL.md +38 -0
  21. package/.agent/skills/sysinfo/system-info.sh +130 -0
  22. package/.agent/skills/workflow/SKILL.md +324 -0
  23. package/.agent/workflows/email-digest.json +50 -0
  24. package/.agent/workflows/file-backup.json +21 -0
  25. package/.agent/workflows/get-ip-notify.json +32 -0
  26. package/.agent/workflows/news-aggregator.json +93 -0
  27. package/.agent/workflows/news-dashboard-v2.json +94 -0
  28. package/.agent/workflows/notification-batch.json +32 -0
  29. package/.claude/settings.local.json +171 -171
  30. package/.env.example +56 -56
  31. package/cli/bin/foliko.js +12 -12
  32. package/cli/src/commands/chat.js +143 -143
  33. package/cli/src/commands/list.js +93 -93
  34. package/cli/src/index.js +75 -75
  35. package/cli/src/ui/chat-ui.js +201 -201
  36. package/cli/src/utils/ansi.js +40 -40
  37. package/cli/src/utils/markdown.js +292 -292
  38. package/examples/ambient-example.js +194 -194
  39. package/examples/basic.js +115 -115
  40. package/examples/bootstrap.js +121 -121
  41. package/examples/mcp-example.js +56 -56
  42. package/examples/skill-example.js +49 -49
  43. package/examples/test-chat.js +137 -137
  44. package/examples/test-mcp.js +85 -85
  45. package/examples/test-reload.js +59 -59
  46. package/examples/test-telegram.js +50 -50
  47. package/examples/test-tg-bot.js +45 -45
  48. package/examples/test-tg-simple.js +47 -47
  49. package/examples/test-tg.js +62 -62
  50. package/examples/test-think.js +43 -43
  51. package/examples/test-web-plugin.js +103 -103
  52. package/examples/test-weixin-feishu.js +103 -103
  53. package/examples/workflow.js +158 -158
  54. package/package.json +83 -83
  55. package/plugins/ai-plugin.js +102 -102
  56. package/plugins/ambient-agent/EventWatcher.js +113 -113
  57. package/plugins/ambient-agent/ExplorerLoop.js +640 -640
  58. package/plugins/ambient-agent/GoalManager.js +197 -197
  59. package/plugins/ambient-agent/Reflector.js +95 -95
  60. package/plugins/ambient-agent/StateStore.js +90 -90
  61. package/plugins/ambient-agent/constants.js +101 -101
  62. package/plugins/ambient-agent/index.js +579 -579
  63. package/plugins/audit-plugin.js +187 -187
  64. package/plugins/default-plugins.js +548 -548
  65. package/plugins/email/constants.js +64 -64
  66. package/plugins/email/handlers.js +461 -461
  67. package/plugins/email/index.js +278 -278
  68. package/plugins/email/monitor.js +269 -269
  69. package/plugins/email/parser.js +138 -138
  70. package/plugins/email/reply.js +151 -151
  71. package/plugins/email/utils.js +124 -124
  72. package/plugins/extension-executor-plugin.js +326 -326
  73. package/plugins/feishu-plugin.js +481 -481
  74. package/plugins/file-system-plugin.js +920 -877
  75. package/plugins/gate-trading.js +747 -747
  76. package/plugins/install-plugin.js +199 -199
  77. package/plugins/python-executor-plugin.js +367 -367
  78. package/plugins/python-plugin-loader.js +651 -651
  79. package/plugins/rules-plugin.js +294 -294
  80. package/plugins/scheduler-plugin.js +691 -691
  81. package/plugins/session-plugin.js +494 -494
  82. package/plugins/shell-executor-plugin.js +197 -197
  83. package/plugins/storage-plugin.js +263 -263
  84. package/plugins/subagent-plugin.js +845 -845
  85. package/plugins/telegram-plugin.js +482 -482
  86. package/plugins/think-plugin.js +345 -345
  87. package/plugins/tools-plugin.js +196 -196
  88. package/plugins/web-plugin.js +637 -637
  89. package/plugins/weixin-plugin.js +545 -545
  90. package/skills/find-skills/AGENTS.md +162 -162
  91. package/skills/find-skills/SKILL.md +133 -133
  92. package/skills/foliko-dev/SKILL.md +563 -583
  93. package/skills/python-plugin-dev/SKILL.md +238 -238
  94. package/src/capabilities/index.js +11 -11
  95. package/src/capabilities/skill-manager.js +609 -609
  96. package/src/capabilities/workflow-engine.js +1109 -1109
  97. package/src/core/agent-chat.js +141 -134
  98. package/src/core/agent.js +958 -958
  99. package/src/core/framework.js +465 -465
  100. package/src/core/index.js +19 -19
  101. package/src/core/plugin-base.js +262 -262
  102. package/src/core/plugin-manager.js +863 -863
  103. package/src/core/provider.js +114 -114
  104. package/src/core/sub-agent-config.js +264 -264
  105. package/src/core/system-prompt-builder.js +120 -120
  106. package/src/core/tool-registry.js +517 -517
  107. package/src/core/tool-router.js +297 -297
  108. package/src/executors/executor-base.js +58 -58
  109. package/src/executors/mcp-executor.js +845 -845
  110. package/src/index.js +25 -25
  111. package/src/utils/circuit-breaker.js +301 -301
  112. package/src/utils/error-boundary.js +363 -363
  113. package/src/utils/error.js +374 -374
  114. package/src/utils/event-emitter.js +97 -97
  115. package/src/utils/id.js +133 -133
  116. package/src/utils/index.js +217 -217
  117. package/src/utils/logger.js +181 -181
  118. package/src/utils/plugin-helpers.js +90 -90
  119. package/src/utils/retry.js +122 -122
  120. package/src/utils/sandbox.js +292 -292
  121. package/test/tool-registry-validation.test.js +218 -218
  122. package/website/script.js +136 -136
  123. package/.agent/.shared/ui-ux-pro-max/data/charts.csv +0 -26
  124. package/.agent/.shared/ui-ux-pro-max/data/colors.csv +0 -97
  125. package/.agent/.shared/ui-ux-pro-max/data/icons.csv +0 -101
  126. package/.agent/.shared/ui-ux-pro-max/data/landing.csv +0 -31
  127. package/.agent/.shared/ui-ux-pro-max/data/products.csv +0 -97
  128. package/.agent/.shared/ui-ux-pro-max/data/prompts.csv +0 -24
  129. package/.agent/.shared/ui-ux-pro-max/data/react-performance.csv +0 -45
  130. package/.agent/.shared/ui-ux-pro-max/data/stacks/flutter.csv +0 -53
  131. package/.agent/.shared/ui-ux-pro-max/data/stacks/html-tailwind.csv +0 -56
  132. package/.agent/.shared/ui-ux-pro-max/data/stacks/jetpack-compose.csv +0 -53
  133. package/.agent/.shared/ui-ux-pro-max/data/stacks/nextjs.csv +0 -53
  134. package/.agent/.shared/ui-ux-pro-max/data/stacks/nuxt-ui.csv +0 -51
  135. package/.agent/.shared/ui-ux-pro-max/data/stacks/nuxtjs.csv +0 -59
  136. package/.agent/.shared/ui-ux-pro-max/data/stacks/react-native.csv +0 -52
  137. package/.agent/.shared/ui-ux-pro-max/data/stacks/react.csv +0 -54
  138. package/.agent/.shared/ui-ux-pro-max/data/stacks/shadcn.csv +0 -61
  139. package/.agent/.shared/ui-ux-pro-max/data/stacks/svelte.csv +0 -54
  140. package/.agent/.shared/ui-ux-pro-max/data/stacks/swiftui.csv +0 -51
  141. package/.agent/.shared/ui-ux-pro-max/data/stacks/vue.csv +0 -50
  142. package/.agent/.shared/ui-ux-pro-max/data/styles.csv +0 -59
  143. package/.agent/.shared/ui-ux-pro-max/data/typography.csv +0 -58
  144. package/.agent/.shared/ui-ux-pro-max/data/ui-reasoning.csv +0 -101
  145. package/.agent/.shared/ui-ux-pro-max/data/ux-guidelines.csv +0 -100
  146. package/.agent/.shared/ui-ux-pro-max/data/web-interface.csv +0 -31
  147. package/.agent/.shared/ui-ux-pro-max/scripts/__pycache__/core.cpython-313.pyc +0 -0
  148. package/.agent/.shared/ui-ux-pro-max/scripts/__pycache__/design_system.cpython-313.pyc +0 -0
  149. package/.agent/.shared/ui-ux-pro-max/scripts/core.py +0 -258
  150. package/.agent/.shared/ui-ux-pro-max/scripts/design_system.py +0 -1067
  151. package/.agent/.shared/ui-ux-pro-max/scripts/search.py +0 -106
  152. package/.agent/ARCHITECTURE.md +0 -288
  153. package/.agent/agents/ambient-agent.md +0 -57
  154. package/.agent/agents/debugger.md +0 -55
  155. package/.agent/agents/email-assistant.md +0 -49
  156. package/.agent/agents/file-manager.md +0 -42
  157. package/.agent/agents/python-developer.md +0 -60
  158. package/.agent/agents/scheduler.md +0 -59
  159. package/.agent/agents/web-developer.md +0 -45
  160. package/.agent/mcp_config_updated.json +0 -12
  161. package/.agent/rules/GEMINI.md +0 -273
  162. package/.agent/rules/allow-rule.md +0 -77
  163. package/.agent/rules/log-rule.md +0 -83
  164. package/.agent/rules/security-rule.md +0 -93
  165. package/.agent/scripts/auto_preview.py +0 -148
  166. package/.agent/scripts/checklist.py +0 -217
  167. package/.agent/scripts/session_manager.py +0 -120
  168. package/.agent/scripts/verify_all.py +0 -327
  169. package/.agent/skills/api-patterns/SKILL.md +0 -81
  170. package/.agent/skills/api-patterns/api-style.md +0 -42
  171. package/.agent/skills/api-patterns/auth.md +0 -24
  172. package/.agent/skills/api-patterns/documentation.md +0 -26
  173. package/.agent/skills/api-patterns/graphql.md +0 -41
  174. package/.agent/skills/api-patterns/rate-limiting.md +0 -31
  175. package/.agent/skills/api-patterns/response.md +0 -37
  176. package/.agent/skills/api-patterns/rest.md +0 -40
  177. package/.agent/skills/api-patterns/scripts/api_validator.py +0 -211
  178. package/.agent/skills/api-patterns/security-testing.md +0 -122
  179. package/.agent/skills/api-patterns/trpc.md +0 -41
  180. package/.agent/skills/api-patterns/versioning.md +0 -22
  181. package/.agent/skills/app-builder/SKILL.md +0 -75
  182. package/.agent/skills/app-builder/agent-coordination.md +0 -71
  183. package/.agent/skills/app-builder/feature-building.md +0 -53
  184. package/.agent/skills/app-builder/project-detection.md +0 -34
  185. package/.agent/skills/app-builder/scaffolding.md +0 -118
  186. package/.agent/skills/app-builder/tech-stack.md +0 -40
  187. package/.agent/skills/app-builder/templates/SKILL.md +0 -39
  188. package/.agent/skills/app-builder/templates/astro-static/TEMPLATE.md +0 -76
  189. package/.agent/skills/app-builder/templates/chrome-extension/TEMPLATE.md +0 -92
  190. package/.agent/skills/app-builder/templates/cli-tool/TEMPLATE.md +0 -88
  191. package/.agent/skills/app-builder/templates/electron-desktop/TEMPLATE.md +0 -88
  192. package/.agent/skills/app-builder/templates/express-api/TEMPLATE.md +0 -83
  193. package/.agent/skills/app-builder/templates/flutter-app/TEMPLATE.md +0 -90
  194. package/.agent/skills/app-builder/templates/monorepo-turborepo/TEMPLATE.md +0 -90
  195. package/.agent/skills/app-builder/templates/nextjs-fullstack/TEMPLATE.md +0 -122
  196. package/.agent/skills/app-builder/templates/nextjs-saas/TEMPLATE.md +0 -122
  197. package/.agent/skills/app-builder/templates/nextjs-static/TEMPLATE.md +0 -169
  198. package/.agent/skills/app-builder/templates/nuxt-app/TEMPLATE.md +0 -134
  199. package/.agent/skills/app-builder/templates/python-fastapi/TEMPLATE.md +0 -83
  200. package/.agent/skills/app-builder/templates/react-native-app/TEMPLATE.md +0 -119
  201. package/.agent/skills/architecture/SKILL.md +0 -55
  202. package/.agent/skills/architecture/context-discovery.md +0 -43
  203. package/.agent/skills/architecture/examples.md +0 -94
  204. package/.agent/skills/architecture/pattern-selection.md +0 -68
  205. package/.agent/skills/architecture/patterns-reference.md +0 -50
  206. package/.agent/skills/architecture/trade-off-analysis.md +0 -77
  207. package/.agent/skills/clean-code/SKILL.md +0 -201
  208. package/.agent/skills/doc.md +0 -177
  209. package/.agent/skills/frontend-design/SKILL.md +0 -418
  210. package/.agent/skills/frontend-design/animation-guide.md +0 -331
  211. package/.agent/skills/frontend-design/color-system.md +0 -311
  212. package/.agent/skills/frontend-design/decision-trees.md +0 -418
  213. package/.agent/skills/frontend-design/motion-graphics.md +0 -306
  214. package/.agent/skills/frontend-design/scripts/accessibility_checker.py +0 -183
  215. package/.agent/skills/frontend-design/scripts/ux_audit.py +0 -722
  216. package/.agent/skills/frontend-design/typography-system.md +0 -345
  217. package/.agent/skills/frontend-design/ux-psychology.md +0 -1116
  218. package/.agent/skills/frontend-design/visual-effects.md +0 -383
  219. package/.agent/skills/i18n-localization/SKILL.md +0 -154
  220. package/.agent/skills/i18n-localization/scripts/i18n_checker.py +0 -241
  221. package/.agent/skills/mcp-builder/SKILL.md +0 -176
  222. package/.agent/skills/web-design-guidelines/SKILL.md +0 -57
  223. package/.agent/workflows/brainstorm.md +0 -113
  224. package/.agent/workflows/create.md +0 -59
  225. package/.agent/workflows/debug.md +0 -103
  226. package/.agent/workflows/deploy.md +0 -176
  227. package/.agent/workflows/enhance.md +0 -63
  228. package/.agent/workflows/orchestrate.md +0 -237
  229. package/.agent/workflows/plan.md +0 -89
  230. package/.agent/workflows/preview.md +0 -81
  231. package/.agent/workflows/simple-test.md +0 -42
  232. package/.agent/workflows/status.md +0 -86
  233. package/.agent/workflows/structured-orchestrate.md +0 -180
  234. package/.agent/workflows/test.md +0 -144
  235. package/.agent/workflows/ui-ux-pro-max.md +0 -296
@@ -1,517 +1,517 @@
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 };
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 };