foliko 1.1.93 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (212) hide show
  1. package/.claude/settings.local.json +2 -1
  2. package/CLAUDE.md +56 -30
  3. package/REFACTORING_PLAN.md +645 -0
  4. package/docs/architecture.md +131 -0
  5. package/docs/migration.md +57 -0
  6. package/docs/public-api.md +138 -0
  7. package/docs/usage.md +385 -0
  8. package/examples/ambient-example.js +20 -137
  9. package/examples/basic.js +21 -48
  10. package/examples/bootstrap.js +16 -74
  11. package/examples/mcp-example.js +6 -29
  12. package/examples/skill-example.js +6 -19
  13. package/examples/workflow.js +8 -56
  14. package/package.json +8 -4
  15. package/plugins/README.md +49 -0
  16. package/plugins/{ambient-agent → ambient}/EventWatcher.js +1 -1
  17. package/plugins/{ambient-agent → ambient}/ExplorerLoop.js +3 -3
  18. package/plugins/{ambient-agent → ambient}/GoalManager.js +2 -2
  19. package/plugins/ambient/README.md +14 -0
  20. package/plugins/{ambient-agent → ambient}/Reflector.js +1 -1
  21. package/plugins/{ambient-agent → ambient}/StateStore.js +1 -1
  22. package/plugins/{ambient-agent → ambient}/index.js +2 -2
  23. package/plugins/{ai-plugin.js → core/ai/index.js} +14 -30
  24. package/plugins/{audit-plugin.js → core/audit/index.js} +3 -30
  25. package/plugins/{coordinator-plugin.js → core/coordinator/index.js} +3 -35
  26. package/plugins/core/default/bootstrap.js +202 -0
  27. package/plugins/core/default/config.js +220 -0
  28. package/plugins/core/default/index.js +58 -0
  29. package/plugins/core/mcp/index.js +1 -0
  30. package/plugins/{python-plugin-loader.js → core/python-loader/index.js} +7 -187
  31. package/plugins/{rules-plugin.js → core/rules/index.js} +121 -64
  32. package/plugins/{scheduler-plugin.js → core/scheduler/index.js} +12 -114
  33. package/plugins/{session-plugin.js → core/session/index.js} +9 -73
  34. package/{src/capabilities/skill-manager.js → plugins/core/skill-manager/index.js} +64 -18
  35. package/plugins/{storage-plugin.js → core/storage/index.js} +5 -29
  36. package/plugins/{subagent-plugin.js → core/sub-agent/index.js} +10 -171
  37. package/plugins/{think-plugin.js → core/think/index.js} +24 -91
  38. package/{src/capabilities/workflow-engine.js → plugins/core/workflow/index.js} +87 -85
  39. package/plugins/default-plugins.js +6 -720
  40. package/plugins/{data-splitter-plugin.js → executors/data-splitter/index.js} +9 -83
  41. package/plugins/{extension-executor-plugin.js → executors/extension/index.js} +13 -97
  42. package/plugins/{python-executor-plugin.js → executors/python/index.js} +6 -31
  43. package/plugins/{shell-executor-plugin.js → executors/shell/index.js} +2 -5
  44. package/plugins/install/README.md +9 -0
  45. package/plugins/{install-plugin.js → install/index.js} +3 -3
  46. package/plugins/{file-system-plugin.js → io/file-system/index.js} +34 -236
  47. package/plugins/{web-plugin.js → io/web/index.js} +11 -113
  48. package/plugins/memory/README.md +13 -0
  49. package/plugins/{memory-plugin.js → memory/index.js} +4 -18
  50. package/plugins/messaging/email/README.md +19 -0
  51. package/plugins/{email → messaging/email}/index.js +2 -2
  52. package/plugins/{feishu-plugin.js → messaging/feishu/index.js} +3 -3
  53. package/plugins/{qq-plugin.js → messaging/qq/index.js} +5 -16
  54. package/plugins/{telegram-plugin.js → messaging/telegram/index.js} +3 -3
  55. package/plugins/{weixin-plugin.js → messaging/weixin/index.js} +15 -15
  56. package/plugins/{plugin-manager-plugin.js → plugin-manager/index.js} +36 -180
  57. package/plugins/{tools-plugin.js → tools/index.js} +68 -116
  58. package/plugins/trading/README.md +15 -0
  59. package/plugins/{gate-trading.js → trading/index.js} +8 -8
  60. package/{examples → sandbox}/test-concurrent-chat.js +2 -2
  61. package/{examples → sandbox}/test-long-chat.js +2 -2
  62. package/{examples → sandbox}/test-session-chat.js +2 -2
  63. package/{examples → sandbox}/test-web-plugin.js +1 -1
  64. package/{examples → sandbox}/test-weixin-feishu.js +2 -2
  65. package/src/agent/base.js +56 -0
  66. package/src/{core/agent-chat.js → agent/chat.js} +11 -11
  67. package/src/{core/coordinator-manager.js → agent/coordinator.js} +3 -3
  68. package/src/agent/index.js +111 -0
  69. package/src/agent/main.js +337 -0
  70. package/src/agent/prompt.js +78 -0
  71. package/src/agent/sub.js +198 -0
  72. package/src/agent/worker.js +104 -0
  73. package/{cli/bin/foliko.js → src/cli/bin.js} +1 -1
  74. package/{cli/src → src/cli}/commands/chat.js +25 -21
  75. package/{cli/src → src/cli}/index.js +1 -0
  76. package/{cli/src → src/cli}/ui/chat-ui-old.js +40 -178
  77. package/{cli/src → src/cli}/ui/chat-ui.js +3 -3
  78. package/{cli/src → src/cli}/ui/components/footer-bar.js +1 -1
  79. package/src/common/errors.js +402 -0
  80. package/src/{utils → common}/logger.js +33 -0
  81. package/src/{utils/chat-queue.js → common/queue.js} +2 -2
  82. package/src/config/plugin-config.js +50 -0
  83. package/src/context/agent.js +32 -0
  84. package/src/context/compaction-prompts.js +170 -0
  85. package/src/context/compaction-utils.js +191 -0
  86. package/src/context/compressor.js +413 -0
  87. package/src/context/index.js +9 -0
  88. package/src/{core/context-manager.js → context/manager.js} +1 -1
  89. package/src/context/request.js +50 -0
  90. package/src/context/session.js +33 -0
  91. package/src/context/storage.js +30 -0
  92. package/src/executors/mcp-client.js +153 -0
  93. package/src/executors/mcp-desc.js +236 -0
  94. package/src/executors/mcp-executor.js +91 -956
  95. package/src/{core → framework}/command-registry.js +1 -1
  96. package/src/framework/framework.js +300 -0
  97. package/src/framework/index.js +18 -0
  98. package/src/framework/lifecycle.js +203 -0
  99. package/src/framework/loader.js +78 -0
  100. package/src/framework/registry.js +86 -0
  101. package/src/{core/ui-extension-context.js → framework/ui-extension.js} +1 -1
  102. package/src/index.js +130 -15
  103. package/src/llm/index.js +26 -0
  104. package/src/llm/provider.js +212 -0
  105. package/src/llm/registry.js +11 -0
  106. package/src/{core/token-counter.js → llm/tokens.js} +4 -37
  107. package/src/{core/plugin-base.js → plugin/base.js} +10 -136
  108. package/src/plugin/index.js +14 -0
  109. package/src/plugin/loader.js +101 -0
  110. package/src/plugin/manager.js +261 -0
  111. package/src/{core → session}/branch-summary-auto.js +2 -2
  112. package/src/{core/chat-session.js → session/chat.js} +2 -2
  113. package/src/session/index.js +7 -0
  114. package/src/{core/session-manager.js → session/session.js} +2 -2
  115. package/src/session/ttl.js +92 -0
  116. package/src/{core/jsonl-storage.js → storage/jsonl.js} +1 -1
  117. package/src/tool/executor.js +85 -0
  118. package/src/tool/index.js +15 -0
  119. package/src/tool/registry.js +143 -0
  120. package/src/{core/tool-router.js → tool/router.js} +17 -124
  121. package/src/tool/schema.js +108 -0
  122. package/src/utils/data-splitter.js +1 -1
  123. package/src/utils/download.js +1 -1
  124. package/src/utils/index.js +6 -6
  125. package/src/utils/message-validator.js +1 -1
  126. package/tests/core/context-storage.test.js +46 -0
  127. package/tests/core/llm.test.js +54 -0
  128. package/tests/core/plugin.test.js +42 -0
  129. package/tests/core/tool.test.js +60 -0
  130. package/tests/setup.js +10 -0
  131. package/tests/smoke.test.js +58 -0
  132. package/vitest.config.js +9 -0
  133. package/cli/src/daemon.js +0 -149
  134. package/docs/CONTEXT_DESIGN.md +0 -1596
  135. package/docs/ai-sdk-optimization.md +0 -655
  136. package/docs/features.md +0 -120
  137. package/docs/qq-bot.md +0 -976
  138. package/docs/quick-reference.md +0 -160
  139. package/docs/user-manual.md +0 -1391
  140. package/images/geometric_shapes.jpg +0 -0
  141. package/images/sunset_mountain_lake.jpg +0 -0
  142. package/skills/poster-guide/SKILL.md +0 -792
  143. package/src/capabilities/index.js +0 -11
  144. package/src/core/agent.js +0 -808
  145. package/src/core/context-compressor.js +0 -959
  146. package/src/core/enhanced-context-compressor.js +0 -210
  147. package/src/core/framework.js +0 -1422
  148. package/src/core/index.js +0 -30
  149. package/src/core/plugin-manager.js +0 -961
  150. package/src/core/provider-registry.js +0 -159
  151. package/src/core/provider.js +0 -156
  152. package/src/core/request-context.js +0 -98
  153. package/src/core/subagent.js +0 -442
  154. package/src/core/system-prompt-builder.js +0 -120
  155. package/src/core/tool-executor.js +0 -202
  156. package/src/core/tool-registry.js +0 -517
  157. package/src/core/worker-agent.js +0 -192
  158. package/src/executors/executor-base.js +0 -58
  159. package/src/utils/error-boundary.js +0 -363
  160. package/src/utils/error.js +0 -374
  161. package/system.md +0 -1645
  162. package/website_v2/README.md +0 -57
  163. package/website_v2/SPEC.md +0 -1
  164. package/website_v2/docs/api.html +0 -128
  165. package/website_v2/docs/configuration.html +0 -147
  166. package/website_v2/docs/plugin-development.html +0 -129
  167. package/website_v2/docs/project-structure.html +0 -89
  168. package/website_v2/docs/skill-development.html +0 -85
  169. package/website_v2/index.html +0 -489
  170. package/website_v2/scripts/main.js +0 -93
  171. package/website_v2/styles/animations.css +0 -8
  172. package/website_v2/styles/docs.css +0 -83
  173. package/website_v2/styles/main.css +0 -417
  174. package/xhs_auth.json +0 -268
  175. package//346/265/267/346/212/245/346/217/222/344/273/266.md +0 -621
  176. /package/plugins/{ambient-agent → ambient}/constants.js +0 -0
  177. /package/plugins/{email → messaging/email}/constants.js +0 -0
  178. /package/plugins/{email → messaging/email}/handlers.js +0 -0
  179. /package/plugins/{email → messaging/email}/monitor.js +0 -0
  180. /package/plugins/{email → messaging/email}/parser.js +0 -0
  181. /package/plugins/{email → messaging/email}/reply.js +0 -0
  182. /package/plugins/{email → messaging/email}/utils.js +0 -0
  183. /package/{examples → sandbox}/test-chat.js +0 -0
  184. /package/{examples → sandbox}/test-mcp.js +0 -0
  185. /package/{examples → sandbox}/test-reload.js +0 -0
  186. /package/{examples → sandbox}/test-telegram.js +0 -0
  187. /package/{examples → sandbox}/test-tg-bot.js +0 -0
  188. /package/{examples → sandbox}/test-tg-simple.js +0 -0
  189. /package/{examples → sandbox}/test-tg.js +0 -0
  190. /package/{examples → sandbox}/test-think.js +0 -0
  191. /package/src/{core/sub-agent-config.js → agent/sub-config.js} +0 -0
  192. /package/{cli/src → src/cli}/commands/daemon.js +0 -0
  193. /package/{cli/src → src/cli}/commands/list.js +0 -0
  194. /package/{cli/src → src/cli}/commands/plugin.js +0 -0
  195. /package/{cli/src → src/cli}/ui/components/agent-mention-provider.js +0 -0
  196. /package/{cli/src → src/cli}/ui/components/chained-autocomplete-provider.js +0 -0
  197. /package/{cli/src → src/cli}/ui/components/message-bubble.js +0 -0
  198. /package/{cli/src → src/cli}/ui/components/status-bar.js +0 -0
  199. /package/{cli/src → src/cli}/utils/ansi.js +0 -0
  200. /package/{cli/src → src/cli}/utils/config.js +0 -0
  201. /package/{cli/src → src/cli}/utils/markdown.js +0 -0
  202. /package/{cli/src → src/cli}/utils/plugin-config.js +0 -0
  203. /package/{cli/src → src/cli}/utils/render-diff.js +0 -0
  204. /package/src/{utils/circuit-breaker.js → common/circuit.js} +0 -0
  205. /package/src/{core → common}/constants.js +0 -0
  206. /package/src/{utils/edit-diff.js → common/diff.js} +0 -0
  207. /package/src/{utils/event-emitter.js → common/events.js} +0 -0
  208. /package/src/{utils → common}/id.js +0 -0
  209. /package/src/{utils → common}/retry.js +0 -0
  210. /package/src/{core/notification-manager.js → notification/manager.js} +0 -0
  211. /package/src/{core/session-entry.js → session/entry.js} +0 -0
  212. /package/src/{core/storage-manager.js → storage/manager.js} +0 -0
@@ -1,19 +1,12 @@
1
1
  /**
2
2
  * DataSplitterPlugin — 大数据自动分拆插件
3
- *
4
- * 功能:
5
- * 1. 注册 `split_and_process` 工具:AI 可主动调用,将大文本分拆给子 Agent 处理
6
- * 2. 注册 `get_content_preview` 工具:获取大内容的前 N 行,判断是否需要分拆
7
- * 3. 自动检测工具返回的大数据,触发透明分拆
8
- * 4. 系统提示词中告知 AI 大数据处理能力
9
3
  */
10
4
 
11
- const { Plugin } = require('../src/core/plugin-base');
5
+ const { Plugin } = require('../../../src/plugin/base');
12
6
  const { z } = require('zod');
13
- const { logger } = require('../src/utils/logger');
14
- const { DataSplitter } = require('../src/utils/data-splitter');
7
+ const { logger } = require('../../../src/common/logger');
8
+ const { DataSplitter } = require('../../../src/utils/data-splitter');
15
9
 
16
- // 超过此大小的工具结果自动触发分拆(字符数,默认 100K tokens ≈ 200K chars)
17
10
  const AUTO_SPLIT_THRESHOLD = 50000;
18
11
 
19
12
  class DataSplitterPlugin extends Plugin {
@@ -27,7 +20,6 @@ class DataSplitterPlugin extends Plugin {
27
20
  this._framework = null;
28
21
  this._splitter = null;
29
22
 
30
- // 配置
31
23
  this.config = {
32
24
  autoSplitThreshold: config.autoSplitThreshold || AUTO_SPLIT_THRESHOLD,
33
25
  chunkSize: config.chunkSize || 60000,
@@ -48,34 +40,20 @@ class DataSplitterPlugin extends Plugin {
48
40
  }
49
41
 
50
42
  start(framework) {
51
- // 注册大数据处理工具
52
43
  this._registerSplitTools();
53
-
54
- // 注册系统提示词
55
44
  this.registerPromptPart('data-splitter-rules', 85, () => this._getPromptRules());
56
-
57
- // 注册工具结果监听器(自动检测大结果)
58
45
  this._registerAutoSplitHook();
59
-
60
- // log.info('[DataSplitterPlugin] 已启动,阈值:', this.config.autoSplitThreshold);
61
46
  }
62
47
 
63
- /**
64
- * 注册分拆工具
65
- * @private
66
- */
67
48
  _registerSplitTools() {
68
49
  const framework = this._framework;
69
50
 
70
- // ─── 工具1: split_and_process — AI 主动调用分拆 ───
71
51
  framework.registerTool({
72
52
  name: 'split_and_process',
73
- description: `将大文本内容按大小分块,创建多个子 Agent 并行处理每块内容,最后自动汇总结果。
74
- 当你读取文件或抓取网页返回的内容超过 10 万字符时,应该使用此工具来分拆处理。
75
- 它会将内容拆成多块,每块由一个子 Agent 独立处理,最后给你一个汇总。`,
53
+ description: `将大文本内容按大小分块,创建多个子 Agent 并行处理每块内容,最后自动汇总结果。`,
76
54
  inputSchema: z.object({
77
- content: z.string().describe('要分拆处理的大文本内容(如果超过 100K 字符建议使用此工具)'),
78
- taskDescription: z.string().describe('每个子 Agent 要执行的任务描述,例如"提取所有函数定义"、"总结内容要点"等'),
55
+ content: z.string().describe('要分拆处理的大文本内容'),
56
+ taskDescription: z.string().describe('每个子 Agent 要执行的任务描述'),
79
57
  chunkSize: z.number().optional().default(60000).describe('每块最大字符数,默认 60000'),
80
58
  maxConcurrent: z.number().optional().default(3).describe('最大并行子 Agent 数,默认 3'),
81
59
  }),
@@ -89,7 +67,6 @@ class DataSplitterPlugin extends Plugin {
89
67
 
90
68
  const stats = splitter.getContentStats(content);
91
69
  if (!splitter.needsSplit(content)) {
92
- // 内容较小,直接返回
93
70
  return {
94
71
  success: true,
95
72
  data: content,
@@ -101,15 +78,9 @@ class DataSplitterPlugin extends Plugin {
101
78
  };
102
79
  }
103
80
 
104
- // 生成上下文中的 sessionId 用于取消信号
105
81
  const sessionCtx = ctx?.getCurrentSessionContext?.();
106
82
  const signal = sessionCtx?.abortSignal;
107
83
 
108
- log.debug(
109
- `[split_and_process] 开始分拆: ${stats.chars} 字符, ` +
110
- `${stats.chunks} 块, 任务="${taskDescription?.slice(0, 40)}..."`
111
- );
112
-
113
84
  const startTime = Date.now();
114
85
  const chunks = splitter.splitContent(content, chunkSize);
115
86
  const result = await splitter.dispatchToSubAgents({
@@ -135,11 +106,9 @@ class DataSplitterPlugin extends Plugin {
135
106
  },
136
107
  });
137
108
 
138
- // ─── 工具2: get_content_preview — 获取大内容预览信息 ───
139
109
  framework.registerTool({
140
110
  name: 'get_content_preview',
141
- description: `获取大文本内容的预览信息(前 20 行 + 统计),判断是否需要分拆处理。
142
- 适合在读取大文件或抓取网页后用于检查内容大小。`,
111
+ description: `获取大文本内容的预览信息(前 20 行 + 统计),判断是否需要分拆处理。`,
143
112
  inputSchema: z.object({
144
113
  content: z.string().describe('要预览的大文本内容'),
145
114
  previewLines: z.number().optional().default(20).describe('预览前 N 行,默认 20'),
@@ -168,10 +137,6 @@ class DataSplitterPlugin extends Plugin {
168
137
  });
169
138
  }
170
139
 
171
- /**
172
- * 获取系统提示词
173
- * @private
174
- */
175
140
  _getPromptRules() {
176
141
  return `## 大数据处理能力
177
142
 
@@ -188,22 +153,14 @@ class DataSplitterPlugin extends Plugin {
188
153
  - 分拆时指定清晰的任务描述,例如"提取所有关键代码函数"、"总结每段内容要点"`;
189
154
  }
190
155
 
191
- /**
192
- * 注册自动分拆钩子
193
- * 监听 tool:result 事件,如果结果太大自动触发分拆
194
- * @private
195
- */
196
156
  _registerAutoSplitHook() {
197
157
  const framework = this._framework;
198
158
 
199
- // 监听工具结果,检测大数据
200
159
  this._toolResultHandler = (data) => {
201
160
  const { name, result } = data;
202
161
 
203
- // 跳过已经标记为分拆结果的工具
204
162
  if (name === 'split_and_process') return;
205
163
 
206
- // 检查工具返回的内容是否过大(优先使用 data 字段)
207
164
  let checkContent = '';
208
165
  if (result && typeof result === 'object' && result.data) {
209
166
  checkContent = typeof result.data === 'string' ? result.data : JSON.stringify(result.data);
@@ -211,27 +168,16 @@ class DataSplitterPlugin extends Plugin {
211
168
  checkContent = typeof result === 'string' ? result : JSON.stringify(result);
212
169
  }
213
170
  if (!checkContent || checkContent.length < this.config.autoSplitThreshold) return;
214
-
215
- // 不阻塞执行,只在日志中记录建议
216
- // log.info(
217
- // `[DataSplitter] 检测到大数据工具结果: ${name}, ` +
218
- // `${checkContent.length} 字符, 建议使用 split_and_process 分拆处理`
219
- // );
220
171
  };
221
172
 
222
173
  framework.on('tool:result', this._toolResultHandler);
223
174
  }
224
175
 
225
- /**
226
- * 获取 DataSplitter 实例
227
- * @private
228
- */
229
176
  _getSplitter(ctx) {
230
177
  return this._splitter || new DataSplitter(this._framework, this.config);
231
178
  }
232
179
 
233
180
  reload(framework) {
234
- // 清理旧监听器
235
181
  if (this._toolResultHandler) {
236
182
  framework.off('tool:result', this._toolResultHandler);
237
183
  }
@@ -239,12 +185,10 @@ class DataSplitterPlugin extends Plugin {
239
185
  this._framework = framework;
240
186
  this._splitter = new DataSplitter(framework, this.config);
241
187
 
242
- // 重新注册
243
188
  if (framework._mainAgent) {
244
189
  this.start(framework);
245
190
  }
246
191
 
247
- // 请求刷新 mainAgent 的 system prompt
248
192
  if (framework._mainAgent) {
249
193
  framework._mainAgent._refreshContext();
250
194
  }
@@ -257,27 +201,14 @@ class DataSplitterPlugin extends Plugin {
257
201
  }
258
202
  }
259
203
 
260
- /**
261
- * 自动检测工具结果是否过大,并透明处理
262
- * 在 agent-chat 中调用
263
- *
264
- * @param {string} toolName - 工具名称
265
- * @param {*} result - 工具返回结果
266
- * @param {Object} framework - Framework 实例
267
- * @returns {Promise<{ wasSplit: boolean, result: *, splitterResult?: Object }>}
268
- */
269
204
  async function autoSplitToolResult(toolName, result, framework) {
270
- // 跳过某些工具
271
205
  const skipTools = ['split_and_process', 'get_content_preview'];
272
206
  if (skipTools.includes(toolName)) return { wasSplit: false, result };
273
207
 
274
- // 无 framework 时无法创建子 Agent,跳过
275
208
  if (!framework || typeof framework.createSubAgent !== 'function') {
276
209
  return { wasSplit: false, result };
277
210
  }
278
211
 
279
- // 提取用于判断大小的内容
280
- // 优先使用 result.data(统一格式),再回退到整个结果字符串
281
212
  let checkContent = '';
282
213
  if (typeof result === 'object' && result !== null && result.data) {
283
214
  checkContent = typeof result.data === 'string' ? result.data : JSON.stringify(result.data);
@@ -291,10 +222,6 @@ async function autoSplitToolResult(toolName, result, framework) {
291
222
  const splitter = new DataSplitter(framework);
292
223
  const taskDescription = `从以下内容中提取关键信息(代码、配置、数据等),以结构化方式输出。`;
293
224
 
294
- // log.info(
295
- // `[DataSplitter] 自动分拆工具 "${toolName}": ${checkContent.length} 字符`
296
- // );
297
-
298
225
  try {
299
226
  const chunks = splitter.splitContent(checkContent);
300
227
  const splitResult = await splitter.dispatchToSubAgents({
@@ -305,12 +232,11 @@ async function autoSplitToolResult(toolName, result, framework) {
305
232
 
306
233
  return {
307
234
  wasSplit: true,
308
- // 返回原始结果 + 分拆汇总,AI 可以同时看到两者
309
235
  result: {
310
236
  _autoSplit: true,
311
237
  _originalSize: checkContent.length,
312
238
  _summary: splitResult.summary,
313
- _originalResult: result, // 保留原始结果供参考
239
+ _originalResult: result,
314
240
  },
315
241
  splitterResult: splitResult,
316
242
  };
@@ -320,4 +246,4 @@ async function autoSplitToolResult(toolName, result, framework) {
320
246
  }
321
247
  }
322
248
 
323
- module.exports = { DataSplitterPlugin, autoSplitToolResult };
249
+ module.exports = { DataSplitterPlugin, autoSplitToolResult };
@@ -3,11 +3,11 @@
3
3
  * 统一管理扩展插件(如 gate-trading)的工具,通过 ext_call 调用
4
4
  */
5
5
 
6
- const { Plugin } = require('../src/core/plugin-base');
7
- const { logger } = require('../src/utils/logger');
6
+ const { Plugin } = require('../../../src/plugin/base');
7
+ const { logger } = require('../../../src/common/logger');
8
8
  const { z } = require('zod');
9
- const { PROMPT_PRIORITY } = require('../src/core/constants');
10
- const { zodSchemaToMarkdown,zodSchemaToTable } = require('@chnak/zod-to-markdown');
9
+ const { PROMPT_PRIORITY } = require('../../../src/common/constants');
10
+ const { zodSchemaToMarkdown, zodSchemaToTable } = require('@chnak/zod-to-markdown');
11
11
 
12
12
  const log = logger.child('ExtensionExecutor');
13
13
 
@@ -27,40 +27,29 @@ class ExtensionExecutorPlugin extends Plugin {
27
27
  install(framework) {
28
28
  this._framework = framework;
29
29
 
30
- // 监听插件加载事件,自动扫描插件的 this.tools
31
30
  framework.on('plugin:loaded', (plugin) => {
32
31
  this._scanPluginTools(plugin);
33
32
  });
34
33
 
35
- // 监听插件重载事件,重新扫描工具
36
34
  framework.on('plugin:reloaded', (plugin) => {
37
35
  this._rescanPluginTools(plugin);
38
36
  });
39
37
 
40
- // 监听框架就绪事件,刷新所有动态注册的工具(确保在 install() 中通过 registerTool() 注册的工具能被扫描到)
41
38
  framework.on('framework:ready', () => {
42
39
  log.debug('Framework ready, rescanning all plugin tools...');
43
40
  const plugins = framework.pluginManager.getAll();
44
41
  for (const { instance: plugin } of plugins) {
45
- // skill-manager 插件的工具已通过 Skill.registerCommands 直接注册,
46
- // 不需要再通过 _scanPluginTools 扫描,避免重复注册
47
42
  if (plugin.name === 'skill-manager') {
48
43
  continue;
49
44
  }
50
45
  this._scanPluginTools(plugin);
51
46
  }
52
- // 刷新所有 Agent 的扩展提示词
53
47
  this._refreshAllAgentsExtPrompt(framework);
54
48
  });
55
49
 
56
50
  return this;
57
51
  }
58
52
 
59
- /**
60
- * 扫描插件的 this.tools 并注册到扩展系统
61
- * @param {Object} plugin - 插件实例
62
- * @param {string} [extName] - 可选的扩展名(用于统一命名)
63
- */
64
53
  _scanPluginTools(plugin, extName) {
65
54
  if (!plugin || !plugin.tools || typeof plugin.tools !== 'object') {
66
55
  return;
@@ -69,17 +58,14 @@ class ExtensionExecutorPlugin extends Plugin {
69
58
  const pluginName = plugin.name;
70
59
  if (!pluginName) return;
71
60
 
72
- // 确定扩展名:优先使用 extName,否则使用 pluginName
73
61
  const useExtName = extName || pluginName;
74
62
 
75
- // 获取插件信息
76
63
  const pluginInfo = {
77
64
  name: useExtName,
78
65
  description: plugin.description || '',
79
66
  version: plugin.version || '1.0.0',
80
67
  };
81
68
 
82
- // 遍历插件的所有工具
83
69
  for (const [toolName, toolDef] of Object.entries(plugin.tools)) {
84
70
  if (!toolDef || typeof toolDef !== 'object') continue;
85
71
 
@@ -93,15 +79,11 @@ class ExtensionExecutorPlugin extends Plugin {
93
79
 
94
80
  log.debug(` Scanned ${Object.keys(plugin.tools).length} tools from plugin '${pluginName}' (as '${useExtName}')`);
95
81
 
96
- // 刷新所有 Agent 的扩展提示词
97
82
  if (this._framework && this._framework._ready) {
98
83
  this._refreshAllAgentsExtPrompt(this._framework);
99
84
  }
100
85
  }
101
86
 
102
- /**
103
- * 重新扫描插件的工具(用于热重载)
104
- */
105
87
  _rescanPluginTools(plugin) {
106
88
  if (!plugin || !plugin.tools || typeof plugin.tools !== 'object') {
107
89
  return;
@@ -110,16 +92,13 @@ class ExtensionExecutorPlugin extends Plugin {
110
92
  const pluginName = plugin.name;
111
93
  if (!pluginName) return;
112
94
 
113
- // skill-manager 插件使用 'skill' 作为扩展名(保持与 Skill.registerCommands 一致)
114
95
  const extName = pluginName === 'skill-manager' ? 'skill' : pluginName;
115
96
 
116
- // 先移除旧工具(同时检查 'skill' 和 'skill-manager' 两个扩展名)
117
97
  const keysToRemove = [extName, pluginName];
118
98
  for (const key of keysToRemove) {
119
99
  if (this._extensions.has(key)) {
120
100
  const ext = this._extensions.get(key);
121
101
  for (const tool of ext.tools || []) {
122
- // 从 toolRouter 移除
123
102
  if (this._framework?.toolRouter) {
124
103
  this._framework.toolRouter.unregister(`ext_${tool.name}`);
125
104
  }
@@ -128,19 +107,18 @@ class ExtensionExecutorPlugin extends Plugin {
128
107
  }
129
108
  }
130
109
 
131
- // 重新扫描工具,使用正确的扩展名
132
110
  this._scanPluginTools(plugin, extName);
133
- //log.info(` Rescanned tools from plugin '${pluginName}' (as '${extName}')`);
134
111
  }
135
112
 
136
113
  async start(framework) {
137
- // 扫描所有已加载插件的 tools
138
114
  const plugins = framework.pluginManager.getAll();
139
115
  for (const { instance: plugin } of plugins) {
116
+ if (plugin.name === 'skill-manager') {
117
+ continue;
118
+ }
140
119
  this._scanPluginTools(plugin);
141
120
  }
142
121
 
143
- // 获取 MCP executor 引用(用于 ext_call 统一路由)
144
122
  this._mcpExecutor = framework.pluginManager?.get('mcp') || null;
145
123
 
146
124
  framework.registerTool({
@@ -153,13 +131,10 @@ class ExtensionExecutorPlugin extends Plugin {
153
131
  }),
154
132
  execute: async (args) => {
155
133
  const { plugin, tool, args: toolArgs = {} } = args;
156
- //log.info(`[Extension] ext_call: plugin=${plugin}, tool=${tool}`);
157
134
 
158
- // MCP 服务器工具(已注册为 server_toolname 格式,如 github_search)
159
135
  if (plugin === 'mcp' && this._mcpExecutor) {
160
136
  const mcpToolDef = this._mcpExecutor.tools?.[tool];
161
137
  if (mcpToolDef && mcpToolDef.execute) {
162
- //log.info(` ext_call [MCP]: tool=${tool}`);
163
138
  return await mcpToolDef.execute(toolArgs || {});
164
139
  }
165
140
  return { success: false, error: `MCP tool '${tool}' not found` };
@@ -167,7 +142,6 @@ class ExtensionExecutorPlugin extends Plugin {
167
142
 
168
143
  const ext = this._extensions.get(plugin);
169
144
  if (!ext) {
170
- // 触发错误事件
171
145
  framework.emit('tool:error', {
172
146
  name: `${plugin}:${tool}`,
173
147
  args: toolArgs,
@@ -179,7 +153,6 @@ class ExtensionExecutorPlugin extends Plugin {
179
153
 
180
154
  const toolDef = ext.tools.find((t) => t.name === tool);
181
155
  if (!toolDef) {
182
- // 触发错误事件
183
156
  framework.emit('tool:error', {
184
157
  name: `${plugin}:${tool}`,
185
158
  args: toolArgs,
@@ -190,7 +163,6 @@ class ExtensionExecutorPlugin extends Plugin {
190
163
  }
191
164
 
192
165
  try {
193
- // 触发扩展工具开始事件
194
166
  framework.emit('tool:call', {
195
167
  name: `${plugin}:${tool}`,
196
168
  args: toolArgs,
@@ -202,12 +174,9 @@ class ExtensionExecutorPlugin extends Plugin {
202
174
  source: 'extension'
203
175
  });
204
176
 
205
- // 统一 execute 签名为 (args, framework)
206
177
  const result = await toolDef.execute(toolArgs, framework);
207
178
 
208
- // 检查结果是否为错误
209
179
  if (result && result.success === false && result.error) {
210
- // 触发扩展工具错误事件
211
180
  framework.emit('tool:error', {
212
181
  name: `${plugin}:${tool}`,
213
182
  args: toolArgs,
@@ -217,7 +186,6 @@ class ExtensionExecutorPlugin extends Plugin {
217
186
  return result;
218
187
  }
219
188
 
220
- // 触发扩展工具完成事件
221
189
  framework.emit('tool:result', {
222
190
  name: `${plugin}:${tool}`,
223
191
  args: toolArgs,
@@ -229,7 +197,6 @@ class ExtensionExecutorPlugin extends Plugin {
229
197
  } catch (err) {
230
198
  log.error(` Tool '${tool}' failed:`, err.message);
231
199
 
232
- // 触发扩展工具错误事件
233
200
  framework.emit('tool:error', {
234
201
  name: `${plugin}:${tool}`,
235
202
  args: toolArgs,
@@ -267,8 +234,8 @@ class ExtensionExecutorPlugin extends Plugin {
267
234
  plugin: z.string().describe('扩展插件名称')
268
235
  }),
269
236
  execute: async (args) => {
270
- const { plugin} = args;
271
- if(plugin === 'skill'){
237
+ const { plugin } = args;
238
+ if (plugin === 'skill') {
272
239
  return "请使用 `loadSkill` 获取skill的使用详情"
273
240
  }
274
241
  return this.bindAllToolSkill(plugin)
@@ -287,7 +254,6 @@ class ExtensionExecutorPlugin extends Plugin {
287
254
  });
288
255
  }
289
256
 
290
- // 注册扩展插件的提示词部分(使用基类方法)
291
257
  this.registerPromptPart('extension-tools', PROMPT_PRIORITY.EXTENSION_TOOLS, () => this._buildExtensionsDescription());
292
258
 
293
259
  return this;
@@ -316,27 +282,23 @@ class ExtensionExecutorPlugin extends Plugin {
316
282
  inputSchema: toolDef.inputSchema,
317
283
  execute: toolDef.execute,
318
284
  };
319
- // 保留额外属性(如 _options)
320
285
  if (toolDef._options) {
321
286
  toolEntry._options = toolDef._options;
322
287
  }
323
288
  if (existingIdx >= 0) {
324
- // 更新已存在的工具
325
289
  ext.tools[existingIdx] = toolEntry;
326
290
  } else {
327
- // 添加新工具
328
291
  ext.tools.push(toolEntry);
329
292
  }
330
293
 
331
294
  log.debug(` Registered tool '${toolDef.name}' for extension '${pluginName}'`);
332
295
 
333
- // 刷新所有 Agent 的扩展提示词
334
296
  if (this._framework && this._framework._ready) {
335
297
  this._refreshAllAgentsExtPrompt(this._framework);
336
298
  }
337
299
  }
338
300
 
339
- bindAllToolSkill(pluginName){
301
+ bindAllToolSkill(pluginName) {
340
302
  const mcp = this._framework.pluginManager.get('mcp');
341
303
  const sections = [
342
304
  '你可以通过 `ext_call` 工具调用以下扩展插件的功能。',
@@ -353,11 +315,10 @@ class ExtensionExecutorPlugin extends Plugin {
353
315
  ];
354
316
 
355
317
  const ext = this._extensions.get(pluginName);
356
- if(!ext)return '找不到扩展!'
318
+ if (!ext) return '找不到扩展!'
357
319
  for (const tool of ext.tools) {
358
320
  sections.push(`#### \`${tool.name}\``, '');
359
321
  sections.push(tool.description || '无描述', '');
360
- // 使用 zodSchemaToMarkdown 解析参数
361
322
  if (ext.name === 'mcp' && mcp) {
362
323
  const mcp_desc = mcp._bindMcpParamsDesc(tool.inputSchema);
363
324
  sections.push(mcp_desc, '');
@@ -376,7 +337,6 @@ class ExtensionExecutorPlugin extends Plugin {
376
337
 
377
338
  sections.push('');
378
339
  return sections.join('\n')
379
-
380
340
  }
381
341
 
382
342
  _refreshAllAgentsExtPrompt(framework) {
@@ -401,18 +361,13 @@ class ExtensionExecutorPlugin extends Plugin {
401
361
 
402
362
  let existingPrompt = agent._originalPrompt || '';
403
363
 
404
- // 移除所有已存在的 ## 【Extensions】 部分(防止重复累积)
405
364
  const extRegex = /## 【Extensions】[\s\S]*?(?=\n## 【|$)/;
406
365
  existingPrompt = existingPrompt.replace(extRegex, '').trim();
407
-
408
- // 添加新的 Extensions 部分
409
- //agent.setSystemPrompt(existingPrompt + '\n\n' + extDesc);
410
366
  }
411
367
 
412
368
  _buildExtensionsDescription() {
413
369
  let desc = '';
414
370
 
415
- // 扩展插件工具(仅列出名称和简介,详细信息通过 ext_schema 按需获取)
416
371
  if (this._extensions.size > 0 || (this._mcpExecutor && Object.keys(this._mcpExecutor.tools || {}).length > 0)) {
417
372
  desc += '## 【Extensions】扩展插件\n\n';
418
373
  desc += '**使用流程(必须按顺序执行):**\n';
@@ -421,15 +376,13 @@ class ExtensionExecutorPlugin extends Plugin {
421
376
  desc += '> **警告**:禁止在未执行第1步获取参数的情况下直接调用 `ext_call`!\n\n';
422
377
 
423
378
  for (const [name, ext] of this._extensions) {
424
- if (name === 'mcp'|| name === 'skill') continue; // MCP 和 Skill 工具单独列出
379
+ if (name === 'mcp' || name === 'skill') continue;
425
380
  desc += `### ${ext.name || name}\n`;
426
381
  desc += `${ext.description || '无描述'}\n`;
427
382
  const validTools = ext.tools.filter(t => t && t.name);
428
383
  desc += `**工具:** ${validTools.map(t => `\`${t.name}\``).join(', ')}\n\n`;
429
-
430
384
  }
431
385
 
432
- // MCP 服务器工具
433
386
  if (this._mcpExecutor && Object.keys(this._mcpExecutor.tools || {}).length > 0) {
434
387
  desc += '### mcp (MCP 服务器工具)\n';
435
388
  desc += '已注册为 `服务器_工具名` 格式\n';
@@ -447,26 +400,18 @@ class ExtensionExecutorPlugin extends Plugin {
447
400
  return desc.trim();
448
401
  }
449
402
 
450
- /**
451
- * 将 Schema 转换为 Markdown 格式的参数描述
452
- * 支持 Zod schema 和 JSON Schema
453
- */
454
403
  _convertSchemaToMarkdown(inputSchema) {
455
404
  if (!inputSchema) return null;
456
405
 
457
- // 提取实际 schema(处理 { jsonSchema: {...} } 或 { inputSchema: {...} } 格式)
458
406
  let schema = inputSchema;
459
407
  if (inputSchema.jsonSchema) schema = inputSchema.jsonSchema;
460
408
  if (inputSchema.inputSchema) schema = inputSchema.inputSchema;
461
409
 
462
- // 检查是否是 Zod schema
463
410
  const isZodSchema = typeof schema.shape === 'function' || (schema._def && schema._def.typeName);
464
411
  if (isZodSchema) {
465
- // Zod schema 直接转换
466
412
  return zodSchemaToMarkdown(schema);
467
413
  }
468
414
 
469
- // JSON Schema 转换为 Zod 再转换
470
415
  if (schema.properties) {
471
416
  const zodSchema = this._jsonSchemaToZod(schema);
472
417
  if (zodSchema) {
@@ -474,13 +419,9 @@ class ExtensionExecutorPlugin extends Plugin {
474
419
  }
475
420
  }
476
421
 
477
- // 如果都没有,回退到简单格式
478
422
  return this._schemaToMarkdown(schema);
479
423
  }
480
424
 
481
- /**
482
- * 将 JSON Schema 转换为 Zod schema
483
- */
484
425
  _jsonSchemaToZod(jsonSchema) {
485
426
  if (!jsonSchema || !jsonSchema.properties) return null;
486
427
 
@@ -499,9 +440,6 @@ class ExtensionExecutorPlugin extends Plugin {
499
440
  }
500
441
  }
501
442
 
502
- /**
503
- * 将 JSON Schema 属性转换为 Zod 类型
504
- */
505
443
  _jsonSchemaPropToZod(prop, isRequired) {
506
444
  if (prop.enum) {
507
445
  let zodType = z.string().enum(prop.enum);
@@ -534,9 +472,6 @@ class ExtensionExecutorPlugin extends Plugin {
534
472
  }
535
473
  }
536
474
 
537
- /**
538
- * 将 JSON Schema 转换为 Markdown(简单格式)
539
- */
540
475
  _schemaToMarkdown(schema) {
541
476
  if (!schema || !schema.properties) return null;
542
477
 
@@ -562,9 +497,6 @@ class ExtensionExecutorPlugin extends Plugin {
562
497
  }));
563
498
  }
564
499
 
565
- /**
566
- * 获取 skill 命令列表(用于 autocomplete)
567
- */
568
500
  getSkillCommands() {
569
501
  const ext = this._extensions.get('skill');
570
502
  if (!ext) return [];
@@ -577,9 +509,6 @@ class ExtensionExecutorPlugin extends Plugin {
577
509
  }));
578
510
  }
579
511
 
580
- /**
581
- * 获取 skill 命令帮助文本
582
- */
583
512
  getSkillCommandsHelp() {
584
513
  const ext = this._extensions.get('skill');
585
514
  if (!ext || ext.tools.length === 0) return '';
@@ -595,12 +524,6 @@ class ExtensionExecutorPlugin extends Plugin {
595
524
  return lines.join('\n');
596
525
  }
597
526
 
598
- /**
599
- * 检查并执行 skill 命令
600
- * @param {string} command - 命令名(如 "skillname:cmdname")
601
- * @param {string} args - 命令参数
602
- * @returns {Promise<object|null>} 执行结果,失败返回 null
603
- */
604
527
  async executeSkillCommand(command, args) {
605
528
  if (!command.includes(':')) return null;
606
529
  try {
@@ -618,24 +541,17 @@ class ExtensionExecutorPlugin extends Plugin {
618
541
 
619
542
  async reload(framework) {
620
543
  this._framework = framework;
621
- // 重新扫描所有已加载插件的 tools
622
544
  this._extensions.clear();
623
545
  const plugins = framework.pluginManager.getAll();
624
546
  for (const { instance: plugin } of plugins) {
625
- // 使用 _rescanPluginTools 以正确处理 skill-manager 的 'skill' extName 映射
626
- // (_scanPluginTools 不传 extName 时会用 pluginName,对 skill-manager 不正确)
627
547
  this._rescanPluginTools(plugin);
628
548
  }
629
- // 重新获取 MCP executor 引用
630
549
  this._mcpExecutor = framework.pluginManager?.get('mcp') || null;
631
- // 刷新所有 Agent 的扩展提示词
632
550
  this._refreshAllAgentsExtPrompt(framework);
633
551
 
634
- // 同时重载 MCP 配置
635
552
  await this._reloadMCPConfig(framework);
636
553
  }
637
554
 
638
- // 重载 MCP 配置
639
555
  async _reloadMCPConfig(framework) {
640
556
  const fs = require('fs');
641
557
  const path = require('path');
@@ -661,4 +577,4 @@ class ExtensionExecutorPlugin extends Plugin {
661
577
  }
662
578
  }
663
579
 
664
- module.exports = ExtensionExecutorPlugin
580
+ module.exports = ExtensionExecutorPlugin