foliko 1.0.76 → 1.0.78
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/.agent/data/default.json +31559 -0
- package/.agent/data/plugins-state.json +10 -1
- package/.claude/settings.local.json +13 -2
- package/.env.example +56 -54
- package/examples/basic.js +1 -1
- package/package.json +83 -81
- package/plugins/ai-plugin.js +2 -2
- package/plugins/ambient-agent/index.js +1 -1
- package/plugins/audit-plugin.js +1 -1
- package/plugins/default-plugins.js +92 -209
- package/plugins/email/index.js +1 -1
- package/plugins/extension-executor-plugin.js +326 -0
- package/plugins/feishu-plugin.js +1 -1
- package/plugins/file-system-plugin.js +57 -6
- package/plugins/gate-trading.js +747 -0
- package/plugins/install-plugin.js +1 -1
- package/plugins/python-executor-plugin.js +1 -1
- package/plugins/python-plugin-loader.js +275 -105
- package/plugins/rules-plugin.js +1 -1
- package/plugins/scheduler-plugin.js +1 -1
- package/plugins/session-plugin.js +132 -7
- package/plugins/shell-executor-plugin.js +1 -1
- package/plugins/storage-plugin.js +24 -1
- package/plugins/subagent-plugin.js +2 -2
- package/plugins/telegram-plugin.js +1 -1
- package/plugins/think-plugin.js +10 -10
- package/plugins/tools-plugin.js +1 -1
- package/plugins/web-plugin.js +49 -18
- package/plugins/weixin-plugin.js +1 -1
- package/skills/foliko-dev/SKILL.md +583 -500
- package/skills/python-plugin-dev/SKILL.md +238 -266
- package/src/core/agent-chat.js +103 -4
- package/src/core/agent.js +84 -18
- package/src/core/plugin-base.js +43 -0
- package/src/executors/mcp-executor.js +126 -22
package/src/core/agent.js
CHANGED
|
@@ -3,9 +3,11 @@
|
|
|
3
3
|
* 负责对话和推理
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
+
const { pl } = require('zod/v4/locales');
|
|
6
7
|
const { EventEmitter } = require('../utils/event-emitter');
|
|
7
8
|
const { AgentChatHandler } = require('./agent-chat');
|
|
8
9
|
const { SystemPromptBuilder } = require('./system-prompt-builder');
|
|
10
|
+
const { zodSchemaToMarkdown } = require('@chnak/zod-to-markdown');
|
|
9
11
|
const os = require('os');
|
|
10
12
|
|
|
11
13
|
class Agent extends EventEmitter {
|
|
@@ -108,9 +110,16 @@ class Agent extends EventEmitter {
|
|
|
108
110
|
this._systemPromptBuilder.register('subagents', 600, () => this._buildSubAgentsDescription());
|
|
109
111
|
|
|
110
112
|
// 7. 系统能力 (优先级 700)
|
|
111
|
-
this._systemPromptBuilder.register('capabilities', 700, () =>
|
|
112
|
-
|
|
113
|
-
|
|
113
|
+
this._systemPromptBuilder.register('capabilities', 700, () =>
|
|
114
|
+
this._buildCapabilitiesDescription()
|
|
115
|
+
);
|
|
116
|
+
// 8. MCP 工具列表 (优先级 750)
|
|
117
|
+
this._systemPromptBuilder.register('mcp-tools', 750, () => this._buildMcpToolsDescription());
|
|
118
|
+
// 9. 扩展工具列表 (优先级 800)
|
|
119
|
+
this._systemPromptBuilder.register('extension-tools', 800, () =>
|
|
120
|
+
this._buildExtensionToolsDescription()
|
|
121
|
+
);
|
|
122
|
+
// 10. 工具调用核心规则 (优先级 1000)
|
|
114
123
|
this._systemPromptBuilder.register('tool-core-rules', 1000, () => this._getToolCoreRules());
|
|
115
124
|
}
|
|
116
125
|
|
|
@@ -153,26 +162,35 @@ class Agent extends EventEmitter {
|
|
|
153
162
|
}
|
|
154
163
|
|
|
155
164
|
/**
|
|
156
|
-
*
|
|
165
|
+
* 构建工具描述(直接调用的工具)
|
|
157
166
|
*/
|
|
158
167
|
_buildToolsDescription() {
|
|
159
168
|
if (this._tools.size === 0) {
|
|
160
169
|
return '';
|
|
161
170
|
}
|
|
162
171
|
|
|
163
|
-
|
|
164
|
-
// const excludedTools = new Set(['subagent_call', 'subagent_list', 'subagent_reload']);
|
|
172
|
+
const directTools = [];
|
|
165
173
|
|
|
166
|
-
let desc = '【可用工具】\n';
|
|
167
174
|
for (const [name, tool] of this._tools) {
|
|
168
|
-
// 跳过没有描述的工具(对LLM无帮助)
|
|
169
175
|
if (!tool.description) continue;
|
|
170
|
-
|
|
171
|
-
//
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
176
|
+
|
|
177
|
+
// MCP 和 ext 工具不在这里显示,它们通过专门的描述部分提供
|
|
178
|
+
if (name.startsWith('mcp_') || name.startsWith('ext_') || name.startsWith('workflow_')) {
|
|
179
|
+
continue;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
directTools.push({ name, tool });
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
if (directTools.length === 0) {
|
|
186
|
+
return '';
|
|
175
187
|
}
|
|
188
|
+
|
|
189
|
+
let desc = '## 可用工具\n\n';
|
|
190
|
+
for (const { name, tool } of directTools) {
|
|
191
|
+
desc += `- **${name}**: ${tool.description || '无描述'}\n`;
|
|
192
|
+
}
|
|
193
|
+
|
|
176
194
|
return desc.trim();
|
|
177
195
|
}
|
|
178
196
|
|
|
@@ -222,15 +240,61 @@ class Agent extends EventEmitter {
|
|
|
222
240
|
}
|
|
223
241
|
|
|
224
242
|
let desc = '【系统能力】\n';
|
|
225
|
-
|
|
243
|
+
keyPlugins.map((plugin, index) => {
|
|
226
244
|
const name = plugin.instance?.name || plugin.name || 'unknown';
|
|
227
245
|
const description = plugin.instance?.description || '无描述';
|
|
228
|
-
desc +=
|
|
246
|
+
desc += `${index + 1}. ${name}: ${description}\n`;
|
|
247
|
+
});
|
|
248
|
+
|
|
249
|
+
return desc.trim();
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
_buildExtensionToolsDescription() {
|
|
253
|
+
const extensionExecutor = this.framework.pluginManager.get('extension-executor');
|
|
254
|
+
if (!extensionExecutor) {
|
|
255
|
+
return '';
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
const extensions = extensionExecutor.getExtensions();
|
|
259
|
+
if (!extensions || extensions.length === 0) {
|
|
260
|
+
return '';
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
let desc = '【Extensions 扩展工具】\n\n';
|
|
264
|
+
desc += '你可以通过 `ext_call` 工具调用以下扩展插件的功能。\n\n';
|
|
265
|
+
|
|
266
|
+
for (const ext of extensions) {
|
|
267
|
+
desc += `### ${ext.name}\n\n`;
|
|
268
|
+
desc += `${ext.description || ''}\n\n`;
|
|
269
|
+
for (const tool of ext.tools) {
|
|
270
|
+
desc += `- **${tool.name}**: ${tool.description || '无描述'}\n`;
|
|
271
|
+
// 添加参数描述
|
|
272
|
+
if (tool.inputSchema) {
|
|
273
|
+
try {
|
|
274
|
+
desc += `**参数:**\n\n`;
|
|
275
|
+
desc += zodSchemaToMarkdown(tool.inputSchema) + '\n\n';
|
|
276
|
+
} catch (e) {
|
|
277
|
+
// 忽略转换错误
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
desc += '\n';
|
|
229
282
|
}
|
|
230
283
|
|
|
284
|
+
desc += '**调用格式:**\n';
|
|
285
|
+
desc += '```\next_call({ plugin: "插件名", tool: "工具名", args: {参数} })\n';
|
|
286
|
+
desc += '```\n';
|
|
231
287
|
return desc.trim();
|
|
232
288
|
}
|
|
233
289
|
|
|
290
|
+
_buildMcpToolsDescription() {
|
|
291
|
+
const mcpExecutor = this.framework.pluginManager.get('mcp-executor');
|
|
292
|
+
if (!mcpExecutor) {
|
|
293
|
+
return '';
|
|
294
|
+
}
|
|
295
|
+
return mcpExecutor._buildMCPServersDescription();
|
|
296
|
+
}
|
|
297
|
+
|
|
234
298
|
/**
|
|
235
299
|
* 构建子Agent描述
|
|
236
300
|
* 动态从配置文件读取每个子Agent的专业领域
|
|
@@ -366,8 +430,10 @@ class Agent extends EventEmitter {
|
|
|
366
430
|
const key = line.substring(0, colonIndex).trim();
|
|
367
431
|
let value = line.substring(colonIndex + 1).trim();
|
|
368
432
|
// 移除可能的引号
|
|
369
|
-
if (
|
|
370
|
-
|
|
433
|
+
if (
|
|
434
|
+
(value.startsWith('"') && value.endsWith('"')) ||
|
|
435
|
+
(value.startsWith("'") && value.endsWith("'"))
|
|
436
|
+
) {
|
|
371
437
|
value = value.slice(1, -1);
|
|
372
438
|
}
|
|
373
439
|
config[key] = value;
|
|
@@ -377,7 +443,7 @@ class Agent extends EventEmitter {
|
|
|
377
443
|
if (config.description) {
|
|
378
444
|
const desc = config.description;
|
|
379
445
|
// 按逗号/顿号分隔
|
|
380
|
-
const parts = desc.split(/[,,、]/).map(s => s.trim());
|
|
446
|
+
const parts = desc.split(/[,,、]/).map((s) => s.trim());
|
|
381
447
|
// 取第一部分(通常是最核心的能力描述)
|
|
382
448
|
const firstPart = parts[0];
|
|
383
449
|
// 如果第一部分太长(说明是句子),取前50个字符
|
package/src/core/plugin-base.js
CHANGED
|
@@ -52,6 +52,13 @@ class Plugin {
|
|
|
52
52
|
_framework = null;
|
|
53
53
|
_subAgents = [];
|
|
54
54
|
|
|
55
|
+
/**
|
|
56
|
+
* 插件注册的工具集(供 ExtensionExecutor 自动加载)
|
|
57
|
+
* 格式: { toolName: { name, description, inputSchema, execute } }
|
|
58
|
+
* @type {Object}
|
|
59
|
+
*/
|
|
60
|
+
tools = {};
|
|
61
|
+
|
|
55
62
|
/**
|
|
56
63
|
* 安装插件 - 注册工具/事件等
|
|
57
64
|
* @param {Framework} framework - 框架实例
|
|
@@ -61,6 +68,42 @@ class Plugin {
|
|
|
61
68
|
return this;
|
|
62
69
|
}
|
|
63
70
|
|
|
71
|
+
/**
|
|
72
|
+
* 注册工具到扩展执行器(供 ExtensionExecutor 统一管理)
|
|
73
|
+
* 支持三种调用方式:
|
|
74
|
+
* this.registerTool('name', { description, inputSchema, execute })
|
|
75
|
+
* this.registerTool({ name, description, inputSchema, execute })
|
|
76
|
+
* this.registerTool({ name, description, inputSchema, execute, pluginName: 'xxx' })
|
|
77
|
+
* @param {string|Object} name - 工具名称或工具定义对象
|
|
78
|
+
* @param {Object} [def] - 工具定义 { description, inputSchema, execute }
|
|
79
|
+
*/
|
|
80
|
+
registerTool(name, def) {
|
|
81
|
+
if (!this._framework) return;
|
|
82
|
+
// 支持 this.registerTool({ name, description, ... }) 形式
|
|
83
|
+
if (typeof name === 'object' && name !== null) {
|
|
84
|
+
def = name;
|
|
85
|
+
name = def.name;
|
|
86
|
+
}
|
|
87
|
+
if (!name || !def) return;
|
|
88
|
+
|
|
89
|
+
// 支持自定义插件名
|
|
90
|
+
const pluginName = def.pluginName || this.name;
|
|
91
|
+
delete def.pluginName;
|
|
92
|
+
|
|
93
|
+
const extExec = this._framework.pluginManager.get('extension-executor');
|
|
94
|
+
if (extExec) {
|
|
95
|
+
extExec.registerTool(
|
|
96
|
+
pluginName,
|
|
97
|
+
{
|
|
98
|
+
name: pluginName,
|
|
99
|
+
description: def.description || this.description,
|
|
100
|
+
version: this.version,
|
|
101
|
+
},
|
|
102
|
+
{ name, ...def }
|
|
103
|
+
);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
64
107
|
/**
|
|
65
108
|
* 启动插件 - 初始化完成后的启动
|
|
66
109
|
* @param {Framework} framework - 框架实例
|
|
@@ -10,6 +10,10 @@ const path = require('path');
|
|
|
10
10
|
const { z } = require('zod');
|
|
11
11
|
const { createMCPClient } = require('@ai-sdk/mcp');
|
|
12
12
|
const { StdioClientTransport } = require('@modelcontextprotocol/sdk/client/stdio.js');
|
|
13
|
+
const { zodSchemaToMarkdown } = require('@chnak/zod-to-markdown');
|
|
14
|
+
const { logger } = require('../utils/logger');
|
|
15
|
+
|
|
16
|
+
const log = logger.child('MCPExecutor');
|
|
13
17
|
|
|
14
18
|
/**
|
|
15
19
|
* MCP 客户端包装器
|
|
@@ -495,39 +499,139 @@ class MCPExecutorPlugin extends Plugin {
|
|
|
495
499
|
return '';
|
|
496
500
|
}
|
|
497
501
|
|
|
498
|
-
let desc = '
|
|
499
|
-
desc += '你可以通过 mcp_call 工具调用以下 MCP 服务器的工具。\n\n';
|
|
502
|
+
let desc = '[MCP Servers]\n\n';
|
|
503
|
+
desc += '你可以通过 `mcp_call` 工具调用以下 MCP 服务器的工具。\n\n';
|
|
500
504
|
|
|
501
505
|
for (const [serverName, info] of this._clients) {
|
|
502
|
-
desc +=
|
|
506
|
+
desc += `### ${serverName}\n\n`;
|
|
503
507
|
for (const tool of info.tools) {
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
const required = schema.required || [];
|
|
508
|
+
desc += `#### ${tool.name}\n`;
|
|
509
|
+
desc += `${tool.description || '无描述'}\n\n`;
|
|
507
510
|
|
|
508
|
-
//
|
|
509
|
-
|
|
510
|
-
if (
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
511
|
+
// 使用 zodSchemaToMarkdown 生成参数文档
|
|
512
|
+
const schema = tool.inputSchema;
|
|
513
|
+
if (schema) {
|
|
514
|
+
try {
|
|
515
|
+
if (typeof schema.toJSON === 'function') {
|
|
516
|
+
// Zod schema 直接转换
|
|
517
|
+
desc += `**参数:**\n\n`;
|
|
518
|
+
desc += zodSchemaToMarkdown(schema) + '\n\n';
|
|
519
|
+
} else if (schema.jsonSchema) {
|
|
520
|
+
// JSON Schema 格式,尝试转换为 Zod schema
|
|
521
|
+
const zodSchema = this._jsonSchemaToZod(schema.jsonSchema || schema);
|
|
522
|
+
if (zodSchema) {
|
|
523
|
+
desc += `**参数:**\n\n`;
|
|
524
|
+
desc += zodSchemaToMarkdown(zodSchema) + '\n\n';
|
|
525
|
+
}
|
|
526
|
+
} else if (schema.properties) {
|
|
527
|
+
// 已经是对象格式
|
|
528
|
+
const zodSchema = this._jsonSchemaToZod(schema);
|
|
529
|
+
if (zodSchema) {
|
|
530
|
+
desc += `**参数:**\n\n`;
|
|
531
|
+
desc += zodSchemaToMarkdown(zodSchema) + '\n\n';
|
|
532
|
+
}
|
|
533
|
+
}
|
|
534
|
+
} catch (e) {
|
|
535
|
+
// 如果转换失败,使用简化格式
|
|
536
|
+
desc += this._fallbackParamsDesc(schema);
|
|
537
|
+
}
|
|
516
538
|
}
|
|
517
|
-
|
|
518
|
-
// 截取描述的第一句
|
|
519
|
-
const shortDesc = tool.description?.split('.')[0] || '无描述';
|
|
520
|
-
desc += `- ${tool.name}: ${shortDesc}${paramStr}\n`;
|
|
521
539
|
}
|
|
522
|
-
desc += '\n';
|
|
540
|
+
desc += '---\n\n';
|
|
523
541
|
}
|
|
524
542
|
|
|
525
|
-
desc +=
|
|
526
|
-
|
|
527
|
-
desc += '
|
|
543
|
+
desc += '**调用格式:**\n';
|
|
544
|
+
desc += '```\nmcp_call({ server: "服务器", tool: "工具", args_json: \'{"参数": "值"}\' })\n';
|
|
545
|
+
desc += '```\n';
|
|
528
546
|
return desc.trim();
|
|
529
547
|
}
|
|
530
548
|
|
|
549
|
+
/**
|
|
550
|
+
* 将 JSON Schema 转换为 Zod schema
|
|
551
|
+
*/
|
|
552
|
+
_jsonSchemaToZod(jsonSchema) {
|
|
553
|
+
if (!jsonSchema || !jsonSchema.properties) {
|
|
554
|
+
return null;
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
try {
|
|
558
|
+
const shape = {};
|
|
559
|
+
const properties = jsonSchema.properties;
|
|
560
|
+
const required = jsonSchema.required || [];
|
|
561
|
+
|
|
562
|
+
for (const [key, prop] of Object.entries(properties)) {
|
|
563
|
+
shape[key] = this._jsonSchemaPropToZod(prop, required.includes(key));
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
return z.object(shape);
|
|
567
|
+
} catch (e) {
|
|
568
|
+
return null;
|
|
569
|
+
}
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
/**
|
|
573
|
+
* 将 JSON Schema 属性转换为 Zod 类型
|
|
574
|
+
*/
|
|
575
|
+
_jsonSchemaPropToZod(prop, isRequired) {
|
|
576
|
+
if (prop.$ref) {
|
|
577
|
+
return isRequired ? z.any() : z.any().optional();
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
if (prop.enum) {
|
|
581
|
+
let zodType = z.string().enum(prop.enum);
|
|
582
|
+
if (prop.nullable) {
|
|
583
|
+
zodType = zodType.nullable();
|
|
584
|
+
}
|
|
585
|
+
return isRequired ? zodType : zodType.optional();
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
const type = prop.type || 'string';
|
|
589
|
+
|
|
590
|
+
switch (type) {
|
|
591
|
+
case 'string':
|
|
592
|
+
return isRequired ? z.string() : z.string().optional();
|
|
593
|
+
case 'number':
|
|
594
|
+
case 'integer':
|
|
595
|
+
return isRequired ? z.number() : z.number().optional();
|
|
596
|
+
case 'boolean':
|
|
597
|
+
return isRequired ? z.boolean() : z.boolean().optional();
|
|
598
|
+
case 'array':
|
|
599
|
+
return isRequired ? z.array(z.any()) : z.array(z.any()).optional();
|
|
600
|
+
case 'object':
|
|
601
|
+
if (prop.properties) {
|
|
602
|
+
const nested = {};
|
|
603
|
+
for (const [k, v] of Object.entries(prop.properties)) {
|
|
604
|
+
nested[k] = this._jsonSchemaPropToZod(v, prop.required?.includes(k) || false);
|
|
605
|
+
}
|
|
606
|
+
return isRequired ? z.object(nested) : z.object(nested).optional();
|
|
607
|
+
}
|
|
608
|
+
return isRequired ? z.record(z.any()) : z.record(z.any()).optional();
|
|
609
|
+
default:
|
|
610
|
+
return isRequired ? z.any() : z.any().optional();
|
|
611
|
+
}
|
|
612
|
+
}
|
|
613
|
+
|
|
614
|
+
/**
|
|
615
|
+
* 回退的参数描述(当 zodSchemaToMarkdown 失败时)
|
|
616
|
+
*/
|
|
617
|
+
_fallbackParamsDesc(schema) {
|
|
618
|
+
const props = schema.properties || {};
|
|
619
|
+
const required = schema.required || [];
|
|
620
|
+
|
|
621
|
+
if (Object.keys(props).length === 0) {
|
|
622
|
+
return '';
|
|
623
|
+
}
|
|
624
|
+
|
|
625
|
+
let desc = '**参数:**\n\n';
|
|
626
|
+
for (const [key, prop] of Object.entries(props)) {
|
|
627
|
+
const isRequired = required.includes(key);
|
|
628
|
+
const type = prop.type || 'any';
|
|
629
|
+
const desc_text = prop.description || '';
|
|
630
|
+
desc += `- \`${key}\`${isRequired ? ' (必填)' : ''}: ${type} ${desc_text}\n`;
|
|
631
|
+
}
|
|
632
|
+
return desc + '\n';
|
|
633
|
+
}
|
|
634
|
+
|
|
531
635
|
_getParentAgent() {
|
|
532
636
|
// 优先返回已缓存的主 agent
|
|
533
637
|
if (this._mainAgent) {
|