foliko 2.0.21 → 2.0.23
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.
- package/.editorconfig +56 -56
- package/.lintstagedrc +7 -7
- package/.prettierignore +29 -29
- package/.prettierrc +11 -11
- package/Dockerfile +63 -63
- package/install.ps1 +129 -129
- package/install.sh +121 -121
- package/package.json +3 -5
- package/plugins/core/skill-manager/index.js +34 -23
- package/plugins/core/sub-agent/index.js +103 -14
- package/plugins/core/workflow/context.js +941 -941
- package/plugins/core/workflow/engine.js +66 -66
- package/plugins/core/workflow/js-runner.js +318 -318
- package/plugins/core/workflow/json-runner.js +323 -323
- package/plugins/core/workflow/stages/choice.js +74 -74
- package/plugins/core/workflow/stages/each.js +123 -123
- package/plugins/core/workflow/stages/parallel.js +69 -69
- package/plugins/executors/extension/extension-registry.js +72 -1
- package/plugins/executors/extension/index.js +68 -9
- package/plugins/io/file-system/index.js +377 -153
- package/skills/find-skills/SKILL.md +133 -133
- package/src/agent/chat.js +207 -222
- package/src/agent/sub.js +29 -26
- package/src/agent/tool-loop.js +648 -0
- package/src/llm/provider.js +12 -28
- package/src/plugin/base.js +17 -14
- package/src/plugin/manager.js +19 -0
- package/src/tool/router.js +2 -2
- package/src/utils/message-validator.js +186 -0
- package/tests/core/chat-tool.test.js +187 -0
- package/tests/core/disable-thinking.test.js +64 -0
- package/tests/core/edit-file.test.js +194 -0
- package/tests/core/ext-call-empty-args.test.js +136 -0
- package/tests/core/reasoning-content.test.js +129 -0
- package/tests/core/sanitize-for-llm.test.js +152 -0
- package/tests/core/search.test.js +212 -0
- package/tests/core/skill-input-schema.test.js +150 -0
- package/tests/core/strip-stale-tool-calls.test.js +154 -0
- package/tests/core/sub-agent-parse.test.js +247 -0
- package/tests/core/sub-agent-skills.test.js +197 -0
- package/tests/core/tool-loop.test.js +208 -0
- package/weixin_o9cq80zgZqKPA2-s59PN43GdDy1w@im.wechat.jsonl +0 -76
|
@@ -219,28 +219,35 @@ class ExtensionExecutorPlugin extends Plugin {
|
|
|
219
219
|
description:
|
|
220
220
|
'【⚡ 执行扩展工具】调用某个扩展插件的具体工具。\n' +
|
|
221
221
|
'\n' +
|
|
222
|
-
'
|
|
222
|
+
'**三个参数都必须传**(即使 args 是空对象也要写成 `args: {}`):\n' +
|
|
223
223
|
'- `plugin`:插件名(命名空间支持 4 种:普通插件名 `email` / MCP 服务器 `mcp:<server>` / Skill `skill:<name>` / Python 插件 `python:<plugin>`)\n' +
|
|
224
224
|
'- `tool`:工具名(来自 ext_list 或 ext_skill 输出)\n' +
|
|
225
|
-
'- `args
|
|
225
|
+
'- `args`:**参数对象**。绝大多数工具都需要参数(id、name、path 等),传 `args: {}` 会导致"参数缺失"错误\n' +
|
|
226
226
|
'\n' +
|
|
227
|
-
'
|
|
227
|
+
'**🚨 重要工作流**:\n' +
|
|
228
|
+
'1. 先调 `ext_list` 找到工具名\n' +
|
|
229
|
+
'2. **必须先调 `ext_skill({plugin: "..."})` 拿到该工具的完整参数 schema**(必填项、类型)\n' +
|
|
230
|
+
'3. 按 schema 填好 `args` 再调 `ext_call`\n' +
|
|
228
231
|
'\n' +
|
|
229
|
-
'
|
|
232
|
+
'**调用示例**(多参数必填):\n' +
|
|
230
233
|
'```js\n' +
|
|
231
234
|
'ext_call({\n' +
|
|
232
|
-
' plugin: "
|
|
233
|
-
' tool: "
|
|
234
|
-
' args: {
|
|
235
|
+
' plugin: "skill:video-creator",\n' +
|
|
236
|
+
' tool: "addCover",\n' +
|
|
237
|
+
' args: { id: "abc123", title: "视频标题", subtitle: "副标题", duration: 3, background: "#0a0e27" }\n' +
|
|
235
238
|
'})\n' +
|
|
236
239
|
'```\n' +
|
|
237
240
|
'\n' +
|
|
238
241
|
'**返回值**:`{success: true, data: <工具返回的数据>}` 或 `{success: false, error: "<错误信息>"}`\n' +
|
|
239
|
-
'
|
|
242
|
+
'\n' +
|
|
243
|
+
'**常见错误**:\n' +
|
|
244
|
+
'- `参数缺失:...` → 你传了 `args: {}`,但工具需要参数。先调 `ext_skill` 查必填字段\n' +
|
|
245
|
+
'- `视频项目不存在: undefined` → `args.id` 没传,参考 ext_skill 输出的 `id` 必填项\n',
|
|
240
246
|
inputSchema: z.object({
|
|
241
247
|
plugin: z.string().describe('插件名称(如 email, gate-trading, mcp:designmd, skill:my-skill, python:stock)'),
|
|
242
248
|
tool: z.string().describe('工具名称(来自 ext_list 输出)'),
|
|
243
|
-
|
|
249
|
+
// ★ 关键:保持 optional 让 LLM 调用简单,但 description 强调必填
|
|
250
|
+
args: z.record(z.any()).optional().describe('⚠️ 必填!工具参数对象。必须先调 ext_skill({plugin: "..."}) 查必填项,然后传完整对象'),
|
|
244
251
|
}),
|
|
245
252
|
execute: async (args) => {
|
|
246
253
|
const { plugin, tool, args: toolArgs = {} } = args;
|
|
@@ -358,6 +365,10 @@ class ExtensionExecutorPlugin extends Plugin {
|
|
|
358
365
|
if (toolDef.inputSchema && typeof toolDef.inputSchema.safeParse === 'function') {
|
|
359
366
|
const parsed = toolDef.inputSchema.safeParse(toolArgs);
|
|
360
367
|
if (!parsed.success) {
|
|
368
|
+
// ★ 优化:如果 args 是空对象且 schema 有必填项,给 LLM 更明确的提示
|
|
369
|
+
if (toolArgs && typeof toolArgs === 'object' && Object.keys(toolArgs).length === 0) {
|
|
370
|
+
return this._emptyArgsError(plugin, tool, toolArgs, toolDef, parsed.error);
|
|
371
|
+
}
|
|
361
372
|
return this._paramValidationError(plugin, tool, parsed.error);
|
|
362
373
|
}
|
|
363
374
|
}
|
|
@@ -432,6 +443,54 @@ class ExtensionExecutorPlugin extends Plugin {
|
|
|
432
443
|
};
|
|
433
444
|
}
|
|
434
445
|
|
|
446
|
+
/**
|
|
447
|
+
* 专门处理 args={} 的情况 —— LLM 经常忘记传 args
|
|
448
|
+
* 给一个非常具体的错误,让 LLM 自我纠正
|
|
449
|
+
* @private
|
|
450
|
+
*/
|
|
451
|
+
_emptyArgsError(plugin, tool, toolArgs, toolDef, zodError) {
|
|
452
|
+
// 尝试从 toolDef 的 inputSchema 提取必填字段
|
|
453
|
+
let requiredFields = [];
|
|
454
|
+
try {
|
|
455
|
+
const schemaShape = toolDef.inputSchema?.shape || toolDef.inputSchema?._def?.schema?.shape;
|
|
456
|
+
if (schemaShape) {
|
|
457
|
+
for (const [key, value] of Object.entries(schemaShape)) {
|
|
458
|
+
// zod 字段上 isOptional() 返回 false 表示必填
|
|
459
|
+
if (value && typeof value.isOptional === 'function' && !value.isOptional()) {
|
|
460
|
+
requiredFields.push(key);
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
} catch { /* ignore */ }
|
|
465
|
+
|
|
466
|
+
const requiredHint = requiredFields.length > 0
|
|
467
|
+
? `\n 必填字段:${requiredFields.join(', ')}`
|
|
468
|
+
: '';
|
|
469
|
+
|
|
470
|
+
// 直接从 issues 中提取 path 作为必填字段
|
|
471
|
+
if (requiredFields.length === 0 && zodError?.issues) {
|
|
472
|
+
const paths = new Set();
|
|
473
|
+
for (const iss of zodError.issues) {
|
|
474
|
+
if (iss.path && iss.path.length > 0) paths.add(iss.path[0]);
|
|
475
|
+
}
|
|
476
|
+
if (paths.size > 0) {
|
|
477
|
+
requiredFields = Array.from(paths);
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
const finalHint = requiredFields.length > 0
|
|
482
|
+
? `\n 必填字段:${requiredFields.join(', ')}`
|
|
483
|
+
: '';
|
|
484
|
+
|
|
485
|
+
return {
|
|
486
|
+
success: false,
|
|
487
|
+
error: `参数缺失:'${plugin}:${tool}' 调用时 args 为空对象 {}。该工具需要参数。`,
|
|
488
|
+
receivedArgs: toolArgs,
|
|
489
|
+
hint: `请使用 ext_skill({ plugin: "${plugin}" }) 查看 "${tool}" 工具的完整参数说明(包括必填字段)。${finalHint}`,
|
|
490
|
+
action: 'call_ext_skill_first',
|
|
491
|
+
};
|
|
492
|
+
}
|
|
493
|
+
|
|
435
494
|
/** 执行时错误 */
|
|
436
495
|
_executionError(plugin, tool, err) {
|
|
437
496
|
const message = (err && err.message) || String(err);
|