foliko 2.0.5 → 2.0.7

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 (93) hide show
  1. package/.claude/settings.local.json +4 -1
  2. package/.cli_default_systemPrompt.md +291 -0
  3. package/CLAUDE.md +3 -0
  4. package/README.md +20 -3
  5. package/docs/architecture.md +34 -2
  6. package/docs/extensions.md +199 -0
  7. package/docs/migration.md +100 -0
  8. package/docs/public-api.md +280 -24
  9. package/docs/usage.md +122 -30
  10. package/package.json +1 -1
  11. package/plugins/core/audit/index.js +1 -1
  12. package/plugins/core/default/bootstrap.js +44 -19
  13. package/plugins/core/python-loader/index.js +43 -25
  14. package/plugins/core/scheduler/index.js +1 -0
  15. package/plugins/core/skill-manager/PROMPT.md +6 -0
  16. package/plugins/core/skill-manager/index.js +402 -115
  17. package/plugins/core/sub-agent/PROMPT.md +10 -0
  18. package/plugins/core/sub-agent/index.js +36 -3
  19. package/plugins/core/think/index.js +1 -0
  20. package/plugins/core/workflow/context.js +941 -0
  21. package/plugins/core/workflow/engine.js +66 -0
  22. package/plugins/core/workflow/examples/01-basic.js +42 -0
  23. package/plugins/core/workflow/examples/01-basic.json +30 -0
  24. package/plugins/core/workflow/examples/02-choice.js +75 -0
  25. package/plugins/core/workflow/examples/02-choice.json +59 -0
  26. package/plugins/core/workflow/examples/03-chain-style.js +114 -0
  27. package/plugins/core/workflow/examples/03-each.json +41 -0
  28. package/plugins/core/workflow/examples/04-parallel.js +52 -0
  29. package/plugins/core/workflow/examples/04-parallel.json +51 -0
  30. package/plugins/core/workflow/examples/05-chain-style.json +68 -0
  31. package/plugins/core/workflow/examples/05-each.js +65 -0
  32. package/plugins/core/workflow/examples/06-script.js +82 -0
  33. package/plugins/core/workflow/examples/07-module-export.js +43 -0
  34. package/plugins/core/workflow/examples/08-default-export.js +29 -0
  35. package/plugins/core/workflow/examples/09-next-with-args.js +50 -0
  36. package/plugins/core/workflow/examples/10-logger.js +34 -0
  37. package/plugins/core/workflow/examples/11-storage.js +68 -0
  38. package/plugins/core/workflow/examples/simple.js +77 -0
  39. package/plugins/core/workflow/examples/simple.json +75 -0
  40. package/plugins/core/workflow/index.js +204 -78
  41. package/plugins/core/workflow/js-runner.js +318 -0
  42. package/plugins/core/workflow/json-runner.js +323 -0
  43. package/plugins/core/workflow/stages/action.js +211 -0
  44. package/plugins/core/workflow/stages/choice.js +74 -0
  45. package/plugins/core/workflow/stages/delay.js +73 -0
  46. package/plugins/core/workflow/stages/each.js +123 -0
  47. package/plugins/core/workflow/stages/parallel.js +69 -0
  48. package/plugins/core/workflow/stages/try.js +142 -0
  49. package/plugins/executors/data-splitter/PROMPT.md +13 -0
  50. package/plugins/executors/data-splitter/index.js +8 -6
  51. package/plugins/executors/extension/extension-registry.js +145 -0
  52. package/plugins/executors/extension/index.js +405 -437
  53. package/plugins/executors/extension/prompt-builder.js +359 -0
  54. package/plugins/executors/extension/skill-helper.js +143 -0
  55. package/plugins/messaging/feishu/index.js +5 -3
  56. package/plugins/messaging/qq/index.js +6 -4
  57. package/plugins/messaging/telegram/index.js +6 -3
  58. package/plugins/messaging/weixin/index.js +5 -3
  59. package/plugins/tools/PROMPT.md +26 -0
  60. package/plugins/tools/index.js +6 -5
  61. package/sandbox/check-context.js +5 -0
  62. package/sandbox/test-context.js +27 -0
  63. package/sandbox/test-fixes.js +40 -0
  64. package/sandbox/test-hello-js.js +46 -0
  65. package/skills/foliko/AGENTS.md +196 -43
  66. package/skills/foliko/SKILL.md +157 -28
  67. package/skills/mcp/SKILL.md +77 -118
  68. package/skills/plugins/SKILL.md +89 -3
  69. package/skills/python/SKILL.md +57 -39
  70. package/skills/skill-guide/SKILL.md +42 -34
  71. package/skills/workflows/SKILL.md +753 -436
  72. package/src/agent/chat.js +56 -28
  73. package/src/agent/main.js +39 -14
  74. package/src/agent/prompt-registry.js +56 -16
  75. package/src/agent/prompts/PROMPT.md +3 -0
  76. package/src/agent/sub.js +1 -1
  77. package/src/cli/ui/chat-ui-old.js +5 -2
  78. package/src/cli/ui/chat-ui.js +9 -5
  79. package/src/common/constants.js +12 -0
  80. package/src/common/error-capture.js +91 -0
  81. package/src/common/json-safe.js +20 -0
  82. package/src/common/logger.js +2 -2
  83. package/src/context/compressor.js +6 -2
  84. package/src/executors/mcp-executor.js +105 -125
  85. package/src/framework/framework.js +78 -12
  86. package/src/index.js +4 -0
  87. package/src/plugin/base.js +908 -5
  88. package/src/plugin/manager.js +124 -9
  89. package/src/tool/schema.js +32 -9
  90. package/src/utils/sandbox.js +1 -1
  91. package/website/index.html +821 -0
  92. package/skills/workflows/workflow-troubleshooting/DEBUGGING.md +0 -197
  93. package/skills/workflows/workflow-troubleshooting/SKILL.md +0 -391
@@ -77,10 +77,21 @@ async function loadCustomPlugins(framework, agentConfig) {
77
77
  // 类式:new PluginClass()
78
78
  instance = new pluginClass(pluginConfig);
79
79
  } else {
80
- // function(foliko) 格式:直接调用,传入 framework
81
- pluginClass(framework);
82
- // function(foliko) 不需要 return,插件内部自己注册
83
- instance = null; // 不再作为插件实例加载
80
+ // function(Plugin) 或 function(foliko) 格式:尝试调用
81
+ try {
82
+ // 优先尝试 function(Plugin) 格式
83
+ const Result = pluginClass(Plugin);
84
+ if (Result && Result.prototype instanceof Plugin) {
85
+ instance = new Result(pluginConfig);
86
+ } else {
87
+ // function(foliko) 格式:传入 framework
88
+ pluginClass(framework);
89
+ instance = null;
90
+ }
91
+ } catch (err) {
92
+ log.error(`Failed to call plugin factory ${pluginName}:`, err.message);
93
+ continue;
94
+ }
84
95
  }
85
96
  } else {
86
97
  instance = pluginClass;
@@ -148,21 +159,6 @@ async function bootstrapDefaults(framework, config = {}) {
148
159
  }));
149
160
  }
150
161
 
151
- // 1.5 Main Agent
152
- if (!framework._mainAgent) {
153
- const { Agent } = require('../../../src/agent/main');
154
- const aiPlugin = framework.pluginManager.get('ai');
155
- framework._mainAgent = framework.createAgent({
156
- name: 'MainAgent',
157
- systemPrompt: '你是一个智能助手。当用户提出问题或任务时,你会主动分析需求,选择合适的工具来获取信息或执行操作。你善于将复杂任务拆解为多个步骤,通过工具协作完成。',
158
- model: aiConfig.model || 'deepseek-chat',
159
- provider: aiConfig.provider || 'deepseek',
160
- apiKey: aiPlugin ? aiPlugin.config.apiKey : (aiConfig.apiKey || envApiKey),
161
- baseURL: aiConfig.baseURL,
162
- });
163
- framework._agents.push(framework._mainAgent);
164
- }
165
-
166
162
  // 1.875 Extension executor (needed by skill-manager for slash commands)
167
163
  if (shouldLoad('extension-executor')) {
168
164
  const ExtensionExecutorPlugin = require('../../executors/extension');
@@ -251,6 +247,35 @@ async function bootstrapDefaults(framework, config = {}) {
251
247
  // 统一启动所有插件
252
248
  await framework.pluginManager.startAll();
253
249
 
250
+ // 等待 skill-manager 完成首次异步加载 (`_loadAllSkills` 包含动态 import ESM 的 index.js)
251
+ // 否则 MainAgent 首次 _buildSystemPrompt() 会错过 skill 命令
252
+ const skillManagerEntry = framework.pluginManager.get('skill-manager');
253
+ if (skillManagerEntry && typeof skillManagerEntry._loadReady?.then === 'function') {
254
+ try {
255
+ await skillManagerEntry._loadReady;
256
+ } catch (_) { /* 加载失败已在 skill-manager 内 log */ }
257
+ }
258
+
259
+ // ============= Main Agent (在所有 plugins 加载并 start 之后) =============
260
+ // 修复: 必须放到所有 plugins 加载并 start 之后再创建,并且 await skill-manager 的
261
+ // _loadReady。
262
+ // 原顺序下 MainAgent.constructor 会立即 _buildSystemPrompt(),此时 extension-executor
263
+ // 和 skill-manager 还没注册工具到 prompt registry,导致系统提示词里看不到 skill 命令
264
+ // —— 之前用户必须 reload 一次才看到就是这个 race。
265
+ if (!framework._mainAgent) {
266
+ const { Agent } = require('../../../src/agent/main');
267
+ const aiPlugin = framework.pluginManager.get('ai');
268
+ framework._mainAgent = framework.createAgent({
269
+ name: 'MainAgent',
270
+ systemPrompt: '你是一个智能助手。当用户提出问题或任务时,你会主动分析需求,选择合适的工具来获取信息或执行操作。你善于将复杂任务拆解为多个步骤,通过工具协作完成。',
271
+ model: aiConfig.model || 'deepseek-chat',
272
+ provider: aiConfig.provider || 'deepseek',
273
+ apiKey: aiPlugin ? aiPlugin.config.apiKey : (aiConfig.apiKey || envApiKey),
274
+ baseURL: aiConfig.baseURL,
275
+ });
276
+ framework._agents.push(framework._mainAgent);
277
+ }
278
+
254
279
  // 退出 bootstrap 模式
255
280
  framework.pluginManager.setBootstrapping(false);
256
281
  }
@@ -53,15 +53,14 @@ class PythonPluginLoader extends Plugin {
53
53
  }
54
54
 
55
55
  let desc = '## 【Python 插件】\n\n'
56
- desc += 'Python 插件工具通过 `ext_call` 调用:\n\n'
56
+ desc += '每个 Python 插件自动注册为 `python:<插件名>` 扩展,通过 `ext_skill` + `ext_call` 统一调用:\n\n'
57
57
 
58
58
  for (const [name, plugin] of this._pythonPlugins) {
59
- desc += `### ${plugin.info.name}\n`
59
+ desc += `### python:${plugin.info.name}\n`
60
60
  desc += `${plugin.info.description || ''}\n\n`
61
61
  if (plugin.info.tools && Array.isArray(plugin.info.tools)) {
62
62
  for (const tool of plugin.info.tools) {
63
- const fullName = `${name}_${tool.name}`
64
- desc += `- **${fullName}**: ${tool.description || '无描述'}\n`
63
+ desc += `- **${tool.name}**: ${tool.description || '无描述'}\n`
65
64
  // 使用 zodSchemaToMarkdown 生成参数描述
66
65
  const params = tool.params || {}
67
66
  if (Object.keys(params).length > 0) {
@@ -82,7 +81,9 @@ class PythonPluginLoader extends Plugin {
82
81
  }
83
82
 
84
83
  desc += '**调用格式:**\n'
85
- desc += '```\next_call({ plugin: "python", tool: "插件名_工具名", args: {...} })\n'
84
+ desc += '```\n'
85
+ desc += '// 1. 查询参数\next_skill({ plugin: "python:<插件名>" })\n'
86
+ desc += '// 2. 调用\next_call({ plugin: "python:<插件名>", tool: "<工具名>", args: {...} })\n'
86
87
  desc += '```\n'
87
88
  return desc.trim()
88
89
  }
@@ -151,7 +152,7 @@ class PythonPluginLoader extends Plugin {
151
152
  // 注册插件的每个工具
152
153
  if (pluginInfo.tools && Array.isArray(pluginInfo.tools)) {
153
154
  for (const tool of pluginInfo.tools) {
154
- this._registerPythonTool(pluginName, tool)
155
+ this._registerPythonTool(pluginName, tool, pluginInfo);
155
156
  }
156
157
  }
157
158
  }
@@ -160,22 +161,32 @@ class PythonPluginLoader extends Plugin {
160
161
  }
161
162
  }
162
163
 
164
+ // 所有 Python 插件注册完毕后,强制失效 prompt 缓存,让 LLM 看到新的扩展
165
+ this._extensionExecutor?._invalidatePromptCaches?.(this._framework);
166
+
163
167
  //log.info(` Total Python plugins: ${this._pythonPlugins.size}`)
164
168
  }
165
169
 
166
170
  /**
167
- * 注册 Python 插件的工具
168
- * 工具名格式:插件名_工具名(如 stock_get_price)
169
- * 直接注册到 ExtensionExecutor,使用 ext_call({ plugin: "python", tool: "插件名_工具名", args: {...} }) 调用
171
+ * 注册 Python 插件的单个工具到 ExtensionExecutor
172
+ *
173
+ * 每个 Python 插件注册为独立扩展 `python:<插件名>`,工具名是原始名(不带前缀)。
174
+ * LLM 通过以下方式调用:
175
+ * 1. 查询参数:ext_skill({ plugin: "python:<插件名>" })
176
+ * 2. 调用:ext_call({ plugin: "python:<插件名>", tool: "<工具名>", args: {...} })
177
+ *
178
+ * @param {string} pluginName Python 插件名(PLUGIN.name,例如 'stock')
179
+ * @param {Object} tool 工具定义对象
180
+ * @param {string} tool.name 工具名(PLUGIN.TOOLS[].name,例如 'get_price')
181
+ * @param {string} [tool.description] 工具描述(用于提示词)
182
+ * @param {Object} [tool.params] 工具参数定义(key → 类型占位符或 JSON Schema)
183
+ * @param {Object} [pluginMeta] 插件元信息(含 description),用于丰富扩展描述
170
184
  */
171
- _registerPythonTool(pluginName, tool) {
185
+ _registerPythonTool(pluginName, tool, pluginMeta = null) {
172
186
  if (!tool.name) return
173
187
 
174
- // 格式:pluginname_toolname
175
- const fullToolName = `${pluginName}_${tool.name}`
176
-
177
188
  const toolDef = {
178
- name: fullToolName,
189
+ name: tool.name, // 工具原始名(如 get_price),不带 pluginName_ 前缀
179
190
  description: tool.description || `${pluginName} 的 ${tool.name} 工具`,
180
191
  inputSchema: this._parseToolParams(tool.params || {}),
181
192
  execute: async (args) => {
@@ -183,26 +194,33 @@ class PythonPluginLoader extends Plugin {
183
194
  }
184
195
  }
185
196
 
197
+ // 每个 Python 插件注册为独立扩展 `python:<pluginName>`
198
+ // 优先用 PLUGIN.description,fallback 到默认描述
199
+ const extName = `python:${pluginName}`;
200
+ const extDescription = pluginMeta?.description
201
+ ? pluginMeta.description
202
+ : `Python 插件 ${pluginName}`;
203
+ const pluginInfo = {
204
+ name: extName,
205
+ description: extDescription,
206
+ version: pluginMeta?.version || '1.0.0',
207
+ };
208
+
186
209
  try {
187
- // 直接注册到 ExtensionExecutor(如果可用)
188
210
  if (this._extensionExecutor) {
189
- this._extensionExecutor.registerTool(
190
- 'python', // 插件名,用于 ext_call 的 plugin 参数
191
- { name: 'python', description: 'Python 插件工具', version: '1.0.0' },
192
- toolDef
193
- )
211
+ this._extensionExecutor.registerTool(extName, pluginInfo, toolDef);
194
212
  } else {
195
- // 回退到基类的 registerTool
213
+ // 回退到基类的 registerTool(带 pluginName 前缀以避免冲突)
196
214
  this.registerTool({
197
- name: fullToolName,
198
- description: `[${pluginName}] ${tool.description || ''}`,
215
+ name: `${pluginName}_${tool.name}`,
216
+ description: tool.description || `${pluginName} ${tool.name} 工具`,
199
217
  pluginName: pluginName,
200
218
  inputSchema: this._parseToolParams(tool.params || {}),
201
219
  execute: toolDef.execute
202
- })
220
+ });
203
221
  }
204
222
  } catch (err) {
205
- log.error(` Failed to register tool ${fullToolName}:`, err.message)
223
+ log.error(` Failed to register tool ${tool.name} for ${extName}:`, err.message)
206
224
  }
207
225
  }
208
226
 
@@ -773,6 +773,7 @@ class SchedulerPlugin extends Plugin {
773
773
  }
774
774
 
775
775
  reload(framework) {
776
+ this._tasks.clear()
776
777
  this._framework = framework
777
778
  this._agent = this._getAgent()
778
779
  this._startScheduler()
@@ -0,0 +1,6 @@
1
+ ## 可用技能
2
+
3
+ > 命令详情(参数、调用方式)请查看上方【Extensions】扩展插件段落。
4
+ > 详细使用指南用 `skill_load("技能名")` 加载。
5
+
6
+ {{skills}}