foliko 2.0.2 → 2.0.3
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/README.md +6 -6
- package/docs/public-api.md +91 -0
- package/docs/system-prompt.md +219 -0
- package/docs/usage.md +6 -6
- package/package.json +1 -1
- package/plugins/ambient/ExplorerLoop.js +1 -0
- package/plugins/ambient/index.js +1 -0
- package/plugins/core/coordinator/index.js +10 -5
- package/plugins/core/default/bootstrap.js +21 -3
- package/plugins/core/default/config.js +12 -3
- package/plugins/core/python-loader/index.js +3 -3
- package/plugins/core/scheduler/index.js +26 -2
- package/plugins/core/skill-manager/index.js +198 -151
- package/plugins/core/sub-agent/index.js +34 -15
- package/plugins/core/think/index.js +1 -0
- package/plugins/core/workflow/index.js +5 -4
- package/plugins/executors/data-splitter/index.js +14 -1
- package/plugins/executors/extension/index.js +51 -37
- package/plugins/executors/python/index.js +3 -3
- package/plugins/executors/shell/index.js +6 -4
- package/plugins/io/web/index.js +2 -1
- package/plugins/memory/index.js +29 -74
- package/plugins/messaging/email/handlers.js +1 -19
- package/plugins/messaging/email/monitor.js +2 -17
- package/plugins/messaging/email/reply.js +2 -1
- package/plugins/messaging/email/utils.js +20 -1
- package/plugins/messaging/feishu/index.js +1 -1
- package/plugins/messaging/qq/index.js +1 -1
- package/plugins/messaging/telegram/index.js +1 -1
- package/plugins/messaging/weixin/index.js +1 -1
- package/plugins/plugin-manager/index.js +1 -33
- package/plugins/tools/index.js +14 -1
- package/skills/plugins/SKILL.md +316 -0
- package/skills/skill-guide/SKILL.md +5 -5
- package/skills/{subagent-guide → subagents}/SKILL.md +1 -1
- package/skills/{workflow-guide → workflows}/SKILL.md +3 -3
- package/skills/{workflow-troubleshooting → workflows/workflow-troubleshooting}/DEBUGGING.md +197 -197
- package/skills/{workflow-troubleshooting → workflows/workflow-troubleshooting}/SKILL.md +391 -391
- package/src/agent/base.js +5 -20
- package/src/agent/index.js +20 -8
- package/src/agent/main.js +100 -23
- package/src/agent/prompt-registry.js +296 -0
- package/src/agent/sub-config.js +1 -78
- package/src/agent/sub.js +19 -24
- package/src/cli/commands/plugin.js +1 -60
- package/src/cli/ui/chat-ui-old.js +1 -1
- package/src/cli/ui/chat-ui.js +1 -1
- package/src/cli/ui/components/agent-mention-provider.js +1 -1
- package/src/common/constants.js +42 -0
- package/src/common/id.js +13 -0
- package/src/common/queue.js +2 -2
- package/src/context/compressor.js +17 -9
- package/src/framework/framework.js +185 -0
- package/src/framework/index.js +1 -2
- package/src/framework/lifecycle.js +102 -1
- package/src/framework/loader.js +1 -55
- package/src/index.js +11 -2
- package/src/plugin/base.js +69 -55
- package/src/plugin/loader.js +1 -57
- package/src/session/entry.js +1 -11
- package/src/storage/manager.js +1 -12
- package/src/tool/executor.js +2 -1
- package/src/tool/router.js +5 -5
- package/src/utils/data-splitter.js +2 -1
- package/src/utils/index.js +150 -0
- package/src/utils/message-validator.js +21 -17
- package/src/utils/plugin-helpers.js +19 -5
- package/subagent.md +2 -2
- package/tests/core/plugin-prompts.test.js +219 -0
- package/tests/core/prompt-registry.test.js +209 -0
- package/src/cli/utils/plugin-config.js +0 -50
- package/src/config/plugin-config.js +0 -50
- /package/skills/{ambient-agent → ambient}/SKILL.md +0 -0
- /package/skills/{foliko-dev → foliko}/AGENTS.md +0 -0
- /package/skills/{foliko-dev → foliko}/SKILL.md +0 -0
- /package/skills/{mcp-usage → mcp}/SKILL.md +0 -0
- /package/skills/{plugin-guide → plugins-guide}/SKILL.md +0 -0
- /package/skills/{python-plugin-dev → python}/SKILL.md +0 -0
package/README.md
CHANGED
|
@@ -365,15 +365,15 @@ allowed-tools: tool1,tool2
|
|
|
365
365
|
|
|
366
366
|
| 工具 | 说明 |
|
|
367
367
|
| ---------------- | -------------------- |
|
|
368
|
-
| `
|
|
368
|
+
| `skill_load` | 加载技能 |
|
|
369
369
|
| `list_plugins` | 列出所有插件 |
|
|
370
370
|
| `list_tools` | 列出所有工具 |
|
|
371
371
|
| `reload_plugins` | 重载插件 |
|
|
372
|
-
| `
|
|
373
|
-
| `powershell`
|
|
374
|
-
| `
|
|
375
|
-
| `
|
|
376
|
-
| `
|
|
372
|
+
| `shell_exec` | 执行 Shell 命令 |
|
|
373
|
+
| `powershell` | 执行 PowerShell 命令 |
|
|
374
|
+
| `py_execute` | 执行 Python 代码 |
|
|
375
|
+
| `py_script` | 执行 Python 脚本 |
|
|
376
|
+
| `py_pip_install` | 安装 Python 包 |
|
|
377
377
|
| `install` | 安装 npm 包 |
|
|
378
378
|
| `mcp_reload` | 重载 MCP 服务器 |
|
|
379
379
|
| `audit_query` | 查询审计日志 |
|
package/docs/public-api.md
CHANGED
|
@@ -78,6 +78,12 @@ framework.pluginManager.getAll();
|
|
|
78
78
|
framework.pluginManager.isEnabled(name);
|
|
79
79
|
framework.pluginManager.getNames();
|
|
80
80
|
|
|
81
|
+
// 快捷插件访问
|
|
82
|
+
framework.plugin(name); // 获取插件实例
|
|
83
|
+
framework.plugins.get(name); // 同上
|
|
84
|
+
framework.plugins.list(); // 列出所有插件名
|
|
85
|
+
framework.plugins.has(name); // 检查插件是否存在
|
|
86
|
+
|
|
81
87
|
// Agent 管理
|
|
82
88
|
framework.getMainAgent();
|
|
83
89
|
framework.getAgents();
|
|
@@ -97,13 +103,98 @@ framework.registerTool(toolDef);
|
|
|
97
103
|
framework.getTools();
|
|
98
104
|
framework.executeTool(name, args);
|
|
99
105
|
|
|
106
|
+
// tools 对象
|
|
107
|
+
framework.tools.tool({ name, description, inputSchema, execute });
|
|
108
|
+
framework.tools.register({ name, description, inputSchema, execute });
|
|
109
|
+
framework.tools.list();
|
|
110
|
+
framework.tools.has(name);
|
|
111
|
+
framework.tools.get(name);
|
|
112
|
+
framework.tools.execute(name, args);
|
|
113
|
+
|
|
114
|
+
// 扩展工具
|
|
115
|
+
framework.registerExtensionTools(pluginName, tools);
|
|
116
|
+
framework.extension.tool(pluginName, tools);
|
|
117
|
+
framework.extension.execute(plugin, tool, args);
|
|
118
|
+
framework.extension.list();
|
|
119
|
+
framework.extension.listPlugins();
|
|
120
|
+
framework.extension.listTools(pluginName);
|
|
121
|
+
framework.extension.getTool(pluginName, toolName);
|
|
122
|
+
|
|
100
123
|
// 事件
|
|
101
124
|
framework.on('email:received', handler);
|
|
102
125
|
framework.on('framework:ready', handler);
|
|
103
126
|
framework.on('tool:call', handler);
|
|
104
127
|
framework.on('session:context-created', handler);
|
|
128
|
+
|
|
129
|
+
// 日志
|
|
130
|
+
framework.logger.info(msg);
|
|
131
|
+
framework.logger.warn(msg);
|
|
132
|
+
framework.logger.error(msg);
|
|
133
|
+
framework.logger.debug(msg);
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
## function(foliko) 插件 API
|
|
137
|
+
|
|
138
|
+
`function(foliko)` 插件是推荐的开发方式,插件代码放在 `.foliko/plugins/*.js`:
|
|
139
|
+
|
|
140
|
+
```js
|
|
141
|
+
// .foliko/plugins/my-plugin.js
|
|
142
|
+
module.exports = function (foliko) {
|
|
143
|
+
// 工具注册
|
|
144
|
+
foliko.tools.tool({
|
|
145
|
+
name: 'hello',
|
|
146
|
+
description: '打招呼',
|
|
147
|
+
inputSchema: foliko.z.object({
|
|
148
|
+
name: foliko.z.string()
|
|
149
|
+
}),
|
|
150
|
+
execute: (args) => ({ message: `Hello, ${args.name}!` })
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
// 扩展工具注册(LLM 可通过 ext_call 调用)
|
|
154
|
+
foliko.extension.tool('my-ext', {
|
|
155
|
+
greet: {
|
|
156
|
+
description: '扩展打招呼',
|
|
157
|
+
inputSchema: foliko.z.object({
|
|
158
|
+
name: foliko.z.string()
|
|
159
|
+
}),
|
|
160
|
+
execute: (args) => ({ message: `Hi, ${args.name}!` })
|
|
161
|
+
}
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
// 事件监听
|
|
165
|
+
foliko.on('framework:ready', () => { /* ... */ });
|
|
166
|
+
|
|
167
|
+
// 获取其他插件
|
|
168
|
+
const ai = foliko.plugin('ai');
|
|
169
|
+
|
|
170
|
+
// 日志
|
|
171
|
+
foliko.logger.info('插件加载');
|
|
172
|
+
};
|
|
105
173
|
```
|
|
106
174
|
|
|
175
|
+
### 可用 API
|
|
176
|
+
|
|
177
|
+
| API | 说明 |
|
|
178
|
+
|-----|------|
|
|
179
|
+
| `foliko.tools.tool()` | 注册普通工具 |
|
|
180
|
+
| `foliko.tools.register()` | 注册普通工具(同上) |
|
|
181
|
+
| `foliko.tools.execute()` | 执行工具 |
|
|
182
|
+
| `foliko.tools.list()` | 列出工具 |
|
|
183
|
+
| `foliko.tools.has()` | 检查工具存在 |
|
|
184
|
+
| `foliko.tools.get()` | 获取工具定义 |
|
|
185
|
+
| `foliko.extension.tool()` | 注册扩展工具 |
|
|
186
|
+
| `foliko.extension.execute()` | 执行扩展工具 |
|
|
187
|
+
| `foliko.extension.list()` | 列出扩展 |
|
|
188
|
+
| `foliko.plugin(name)` | 获取插件实例 |
|
|
189
|
+
| `foliko.plugins.get(name)` | 同上 |
|
|
190
|
+
| `foliko.plugins.list()` | 列出插件名 |
|
|
191
|
+
| `foliko.plugins.has(name)` | 检查插件存在 |
|
|
192
|
+
| `foliko.on(event, handler)` | 监听事件 |
|
|
193
|
+
| `foliko.emit(event, data)` | 发送事件 |
|
|
194
|
+
| `foliko.logger.info/warn/error/debug()` | 日志 |
|
|
195
|
+
| `foliko.z` | Zod schema 创建 |
|
|
196
|
+
| `foliko.Plugin` | Plugin 基类 |
|
|
197
|
+
|
|
107
198
|
## 导出类型
|
|
108
199
|
|
|
109
200
|
```js
|
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
# 系统提示词系统
|
|
2
|
+
|
|
3
|
+
## 概述
|
|
4
|
+
|
|
5
|
+
系统提示词由多个 **Prompt Part** 按 **语义槽位(Slot)** 排序拼接而成。分为两类内容:
|
|
6
|
+
|
|
7
|
+
| 内容 | 来源 | 示例 |
|
|
8
|
+
|------|------|------|
|
|
9
|
+
| **Agent 身份** | `createAgent({ systemPrompt })` | `你是一个智能助手...` |
|
|
10
|
+
| **共享内容** | `framework.prompts` 注册表 | 技能列表、行为规则、记忆上下文 |
|
|
11
|
+
|
|
12
|
+
每个 Prompt Part 由 `(owner, name, provider)` 三元组唯一标识。`owner` 通常是插件名,确保不同插件不会命名冲突。
|
|
13
|
+
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
## 架构
|
|
17
|
+
|
|
18
|
+
```
|
|
19
|
+
┌────────────────────────────────────────────┐
|
|
20
|
+
│ PromptRegistry │
|
|
21
|
+
│ (framework.prompts / agent.promptRegistry)│
|
|
22
|
+
│ │
|
|
23
|
+
│ register(owner, name, provider, options) │
|
|
24
|
+
│ unregister(owner, name) │
|
|
25
|
+
│ clearOwner(owner) │
|
|
26
|
+
│ list() / inspect() / build() / preview() │
|
|
27
|
+
└────────────────────────────────────────────┘
|
|
28
|
+
▲ ▲
|
|
29
|
+
│ │
|
|
30
|
+
Plugin.prompts MainAgent 兼容层
|
|
31
|
+
(声明式) registerPromptPart()
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
### Prompt Part 数据结构
|
|
35
|
+
|
|
36
|
+
```
|
|
37
|
+
{ owner, name, provider, slot, order, description }
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
- `owner` — 拥有者(通常是插件名),用于命名空间隔离
|
|
41
|
+
- `name` — part 名,在 owner 内唯一
|
|
42
|
+
- `provider` — `() => string|null`,返回 null/空串时该 part 在 build 中被跳过
|
|
43
|
+
- `slot` — 语义槽位名,决定排序位置(见下文)
|
|
44
|
+
- `order` — 可选,覆盖 slot 默认顺序(同 slot 内用 owner→name 字母序稳定排序)
|
|
45
|
+
- `description` — 可选,用于调试/文档
|
|
46
|
+
|
|
47
|
+
---
|
|
48
|
+
|
|
49
|
+
## 语义槽位(Slot)
|
|
50
|
+
|
|
51
|
+
Slot 决定 Prompt Part 在最终提示词中的先后顺序。定义在 `src/common/constants.js`:
|
|
52
|
+
|
|
53
|
+
| Slot | order | 含义 | 包含内容 |
|
|
54
|
+
|------|-------|------|---------|
|
|
55
|
+
| `IDENTITY` | 10 | 角色身份 | Agent 的系统提示词 |
|
|
56
|
+
| `ENVIRONMENT` | 20 | 运行环境 | 当前时间(合并到「运行环境」中) |
|
|
57
|
+
| `USER` | 30 | 用户指令 | sharedPrompt、运行时元数据 |
|
|
58
|
+
| `CAPABILITY` | 40 | 可用能力 | 插件概览、Extensions、技能列表、子Agent 匹配表 |
|
|
59
|
+
| `BEHAVIOR` | 50 | 行为规则 | 工具调用核心规则、大数据分拆规则、Coordinator 模式 |
|
|
60
|
+
| `CONTEXT` | 60 | 上下文摘要 | 记忆上下文 |
|
|
61
|
+
| `CUSTOM` | 999 | 自定义 | 未映射到上述槽位的兜底 |
|
|
62
|
+
|
|
63
|
+
```js
|
|
64
|
+
const { PROMPT_SLOT } = require('./src/common/constants');
|
|
65
|
+
// 或从 src/index 导出:
|
|
66
|
+
const { PROMPT_SLOT } = require('foliko');
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
---
|
|
70
|
+
|
|
71
|
+
## 使用方法
|
|
72
|
+
|
|
73
|
+
### 方式一:Plugin 声明式(推荐)
|
|
74
|
+
|
|
75
|
+
在 Plugin 子类中用 `prompts` 字段声明:
|
|
76
|
+
|
|
77
|
+
```js
|
|
78
|
+
const { Plugin } = require('foliko');
|
|
79
|
+
|
|
80
|
+
class MyPlugin extends Plugin {
|
|
81
|
+
name = 'my-plugin';
|
|
82
|
+
|
|
83
|
+
prompts = [
|
|
84
|
+
{
|
|
85
|
+
name: 'my-rules',
|
|
86
|
+
slot: 'BEHAVIOR',
|
|
87
|
+
description: '我的自定义规则',
|
|
88
|
+
provider: function () {
|
|
89
|
+
return this._buildRules();
|
|
90
|
+
},
|
|
91
|
+
},
|
|
92
|
+
];
|
|
93
|
+
|
|
94
|
+
_buildRules() {
|
|
95
|
+
return '## 我的规则\n1. 规则一\n2. 规则二';
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
- 基类 `start()` 自动调用 `_autoRegisterPrompts()` 注册到 `framework.prompts`
|
|
101
|
+
- `provider` 中的 `this` 绑定到 Plugin 实例(必须用 `function` 不能用箭头函数)
|
|
102
|
+
- 失败一个 entry 不影响其他
|
|
103
|
+
- `reload()` / `uninstall()` 时自动清理
|
|
104
|
+
|
|
105
|
+
### 方式二:直接注册到 framework.prompts
|
|
106
|
+
|
|
107
|
+
```js
|
|
108
|
+
const fw = new Framework();
|
|
109
|
+
|
|
110
|
+
fw.prompts.register('my-plugin', 'my-part', () => '内容', {
|
|
111
|
+
slot: 'BEHAVIOR', // 必填,PROMPT_SLOT 中的一项
|
|
112
|
+
order: null, // 可选,覆盖 slot 默认 order
|
|
113
|
+
description: '说明', // 可选
|
|
114
|
+
});
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
### 方式三:旧兼容 API
|
|
118
|
+
|
|
119
|
+
```js
|
|
120
|
+
agent.registerPromptPart(name, priority, provider);
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
该 API 保留为薄转发层,内部调用 `framework.prompts.register()`。**新代码请使用方式一或二**。
|
|
124
|
+
|
|
125
|
+
---
|
|
126
|
+
|
|
127
|
+
## 动态刷新
|
|
128
|
+
|
|
129
|
+
`framework.prompts` 变更(register / unregister / clearOwner)时,自动失效所有 Agent 的系统提示词缓存。
|
|
130
|
+
|
|
131
|
+
```
|
|
132
|
+
插件 reload → _cleanupPrompts() → _autoRegisterPrompts()
|
|
133
|
+
→ framework.prompts 发出事件
|
|
134
|
+
→ 所有 agent._systemPromptDirty = true
|
|
135
|
+
→ 下次 chat() 时 _buildSystemPrompt() 重建
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
无需手动刷新。如果确实需要强制刷新:
|
|
139
|
+
|
|
140
|
+
```js
|
|
141
|
+
agent._invalidateSystemPromptCache();
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
---
|
|
145
|
+
|
|
146
|
+
## 调试与观测
|
|
147
|
+
|
|
148
|
+
### 列出所有已注册的 Prompt Part
|
|
149
|
+
|
|
150
|
+
```js
|
|
151
|
+
fw.prompts.inspect();
|
|
152
|
+
// 返回:
|
|
153
|
+
// [
|
|
154
|
+
// { owner, name, slot, order, hasContent, length, contentPreview },
|
|
155
|
+
// ...
|
|
156
|
+
// ]
|
|
157
|
+
|
|
158
|
+
fw.prompts.list();
|
|
159
|
+
// 返回按 slot 排序的 PromptPart 实例数组
|
|
160
|
+
|
|
161
|
+
fw.prompts.preview();
|
|
162
|
+
// 渲染完整提示词(等效于 build())
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
### 检查特定 part
|
|
166
|
+
|
|
167
|
+
```js
|
|
168
|
+
fw.prompts.has('my-plugin', 'my-part'); // true/false
|
|
169
|
+
fw.prompts.getPart('my-plugin', 'my-part'); // PromptPart 实例
|
|
170
|
+
fw.prompts.getPart('my-plugin', 'my-part').get(); // 渲染后的内容
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
### Slot 分布统计
|
|
174
|
+
|
|
175
|
+
```js
|
|
176
|
+
const bySlot = {};
|
|
177
|
+
for (const p of fw.prompts.inspect()) {
|
|
178
|
+
bySlot[p.slot] = (bySlot[p.slot] || 0) + 1;
|
|
179
|
+
}
|
|
180
|
+
console.log(bySlot);
|
|
181
|
+
// { IDENTITY: 1, ENVIRONMENT: 1, USER: 1, CAPABILITY: 4, BEHAVIOR: 3, CONTEXT: 1 }
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
---
|
|
185
|
+
|
|
186
|
+
## 最终 Prompt 排列示例
|
|
187
|
+
|
|
188
|
+
```
|
|
189
|
+
你是一个智能助手... ← IDENTITY(Agent 身份)
|
|
190
|
+
|
|
191
|
+
【运行环境】 ← USER(时间 + 目录 + 平台)
|
|
192
|
+
- 当前时间: 2026/6/25
|
|
193
|
+
- 工作目录: / 平台 / 主机
|
|
194
|
+
|
|
195
|
+
## 插件概览 ← CAPABILITY(按类别分组)
|
|
196
|
+
## 扩展插件 ← CAPABILITY(ext_call 工具)
|
|
197
|
+
## 可用技能 ← CAPABILITY(紧凑列表)
|
|
198
|
+
## 子 Agent 匹配表 ← CAPABILITY
|
|
199
|
+
|
|
200
|
+
## 工具调用核心规则 ← BEHAVIOR
|
|
201
|
+
## 大数据处理能力 ← BEHAVIOR
|
|
202
|
+
## 并行任务处理 ← BEHAVIOR
|
|
203
|
+
|
|
204
|
+
## 【记忆上下文】 ← CONTEXT
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
---
|
|
208
|
+
|
|
209
|
+
## 与旧系统的对比
|
|
210
|
+
|
|
211
|
+
| | 旧系统 | 新系统 |
|
|
212
|
+
|--|--------|--------|
|
|
213
|
+
| 注册方式 | `this.registerPromptPart(name, priority, provider)` | `prompts = [{ name, slot, provider }]` |
|
|
214
|
+
| 排序依据 | 魔法数字(50/85/90/120/140/350/650) | 语义槽位 IDENTITY→ENVIRONMENT→USER→CAPABILITY→BEHAVIOR→CONTEXT |
|
|
215
|
+
| 命名空间 | 无(name 全局唯一,冲突时后注册的静默失败) | owner::name 复合 key,自动隔离 |
|
|
216
|
+
| 生命周期 | 需手动在 uninstall/reload 清理 | 基类自动注册/清理 |
|
|
217
|
+
| 可观测性 | 无 | `inspect() / list() / preview()` |
|
|
218
|
+
| 动态刷新 | 无(需手动 _refreshContext) | 注册表变更时自动失效所有 Agent 缓存 |
|
|
219
|
+
| 重复注册 | 无法检测 | 按 owner::name 去重 |
|
package/docs/usage.md
CHANGED
|
@@ -187,10 +187,10 @@ module.exports = [
|
|
|
187
187
|
|
|
188
188
|
### 相关工具
|
|
189
189
|
|
|
190
|
-
- `
|
|
191
|
-
- `
|
|
192
|
-
- `
|
|
193
|
-
- `
|
|
190
|
+
- `skill_load({ skill: "name" })` — 获取技能内容
|
|
191
|
+
- `skill_reload` — 重载所有技能
|
|
192
|
+
- `skill_load_reference({ skill, reference })` — 加载技能的参考文档
|
|
193
|
+
- `skill_list_scripts` / `skill_load_script` — 技能脚本管理
|
|
194
194
|
|
|
195
195
|
## Workflows
|
|
196
196
|
|
|
@@ -215,8 +215,8 @@ module.exports = [
|
|
|
215
215
|
### 相关工具
|
|
216
216
|
|
|
217
217
|
- `execute_workflow({ workflow, input })` — 执行工作流
|
|
218
|
-
- `
|
|
219
|
-
- `
|
|
218
|
+
- `workflow_list` — 列出所有已加载工作流
|
|
219
|
+
- `workflow_reload` — 重载工作流
|
|
220
220
|
|
|
221
221
|
## Rules
|
|
222
222
|
|
package/package.json
CHANGED
package/plugins/ambient/index.js
CHANGED
|
@@ -53,11 +53,11 @@ class CoordinatorPlugin extends Plugin {
|
|
|
53
53
|
}
|
|
54
54
|
|
|
55
55
|
_registerCoordinatorPrompt(agent) {
|
|
56
|
-
if (!agent ||
|
|
57
|
-
return;
|
|
58
|
-
}
|
|
56
|
+
if (!agent || !this.enabled) return;
|
|
59
57
|
|
|
60
|
-
|
|
58
|
+
const reg = this._framework?.prompts;
|
|
59
|
+
if (reg) {
|
|
60
|
+
reg.register(this.name, 'coordinator-mode', () => `
|
|
61
61
|
### 【并行任务处理 - Coordinator 模式】
|
|
62
62
|
当遇到可并行处理的任务时(如多主题搜索、多文件处理、多计算任务),使用以下工具:
|
|
63
63
|
|
|
@@ -77,7 +77,12 @@ class CoordinatorPlugin extends Plugin {
|
|
|
77
77
|
- 可同时创建多个Worker并行处理不同任务
|
|
78
78
|
- Worker完成后生成 <task-notification> JSON 通知
|
|
79
79
|
- 你需要发送 send_message_to_worker 等待所有worker工作完成
|
|
80
|
-
|
|
80
|
+
`, { slot: 'BEHAVIOR', description: '并行任务处理 - Coordinator 模式' });
|
|
81
|
+
if (typeof agent._invalidateSystemPromptCache === 'function') {
|
|
82
|
+
agent._invalidateSystemPromptCache();
|
|
83
|
+
agent._refreshContext();
|
|
84
|
+
}
|
|
85
|
+
}
|
|
81
86
|
}
|
|
82
87
|
|
|
83
88
|
_setupWorkerEventListeners(framework) {
|
|
@@ -22,6 +22,9 @@ async function loadCustomPlugins(framework, agentConfig) {
|
|
|
22
22
|
{ dir: path.resolve(cwd, 'plugins'), forceEnabled: false },
|
|
23
23
|
{ dir: path.resolve(__dirname, '..', '..', '..', 'plugins'), forceEnabled: false },
|
|
24
24
|
];
|
|
25
|
+
// 跟踪已加载的插件路径(避免重复加载)
|
|
26
|
+
const loadedPaths = new Set();
|
|
27
|
+
|
|
25
28
|
for (const { dir, forceEnabled } of dirs) {
|
|
26
29
|
if (!fs.existsSync(dir)) continue;
|
|
27
30
|
const names = scanPluginNames(dir);
|
|
@@ -29,6 +32,11 @@ async function loadCustomPlugins(framework, agentConfig) {
|
|
|
29
32
|
try {
|
|
30
33
|
const resolved = resolvePluginPath(dir, pluginName);
|
|
31
34
|
if (!resolved) continue;
|
|
35
|
+
|
|
36
|
+
// 按路径去重
|
|
37
|
+
if (loadedPaths.has(resolved.path)) continue;
|
|
38
|
+
loadedPaths.add(resolved.path);
|
|
39
|
+
|
|
32
40
|
delete require.cache[require.resolve(resolved.path)];
|
|
33
41
|
const mod = require(resolved.path);
|
|
34
42
|
let pluginClass = mod.default || mod;
|
|
@@ -43,12 +51,22 @@ async function loadCustomPlugins(framework, agentConfig) {
|
|
|
43
51
|
if (framework.pluginManager.has(resolvedName) && framework.pluginManager.get(resolvedName)?._started) continue;
|
|
44
52
|
const pluginConfig = agentConfig[resolvedName] || {};
|
|
45
53
|
let instance;
|
|
46
|
-
if (typeof pluginClass === 'function'
|
|
47
|
-
|
|
54
|
+
if (typeof pluginClass === 'function') {
|
|
55
|
+
if (pluginClass.prototype instanceof Plugin) {
|
|
56
|
+
// 类式:new PluginClass()
|
|
57
|
+
instance = new pluginClass(pluginConfig);
|
|
58
|
+
} else {
|
|
59
|
+
// function(foliko) 格式:直接调用,传入 framework
|
|
60
|
+
pluginClass(framework);
|
|
61
|
+
// function(foliko) 不需要 return,插件内部自己注册
|
|
62
|
+
instance = null; // 不再作为插件实例加载
|
|
63
|
+
}
|
|
48
64
|
} else {
|
|
49
65
|
instance = pluginClass;
|
|
50
66
|
}
|
|
51
|
-
|
|
67
|
+
if (instance) {
|
|
68
|
+
await framework.pluginManager.load(instance, { forceEnabled });
|
|
69
|
+
}
|
|
52
70
|
} catch (err) { log.error(`Failed to load plugin ${pluginName}:`, err.message); }
|
|
53
71
|
}
|
|
54
72
|
}
|
|
@@ -148,9 +148,18 @@ function loadAgentConfig(framework, agentDir = '.foliko') {
|
|
|
148
148
|
const cmdSkillsAbs = path.resolve(cwd, cmdSkillsRel);
|
|
149
149
|
if (fs.existsSync(cmdSkillsAbs)) config.skillsDirs.push(cmdSkillsRel);
|
|
150
150
|
|
|
151
|
-
// 5. plugins/core/skills —
|
|
152
|
-
const
|
|
153
|
-
if (fs.existsSync(
|
|
151
|
+
// 5. plugins/core/skills — 插件内置全局(最低优先级)
|
|
152
|
+
const coreSkillsDir = path.join(path.dirname(__dirname), 'skills');
|
|
153
|
+
if (fs.existsSync(coreSkillsDir)) config.skillsDirs.push(coreSkillsDir);
|
|
154
|
+
|
|
155
|
+
// 6. 框架根目录 skills/ — 随框架安装的内置技能
|
|
156
|
+
const fwRoot = path.resolve(__dirname, '..', '..', '..');
|
|
157
|
+
const fwSkillsDir = path.join(fwRoot, 'skills');
|
|
158
|
+
if (fs.existsSync(fwSkillsDir)) config.skillsDirs.push(fwSkillsDir);
|
|
159
|
+
|
|
160
|
+
// 7. 框架 .foliko/skills/ — 框架本地技能
|
|
161
|
+
const fwDotSkillsDir = path.join(fwRoot, '.foliko', 'skills');
|
|
162
|
+
if (fs.existsSync(fwDotSkillsDir)) config.skillsDirs.push(fwDotSkillsDir);
|
|
154
163
|
}
|
|
155
164
|
|
|
156
165
|
// Home dir (~/.foliko/) fallback
|
|
@@ -920,10 +920,10 @@ class PythonPluginLoader extends Plugin {
|
|
|
920
920
|
].join('\n')
|
|
921
921
|
|
|
922
922
|
try {
|
|
923
|
-
// 通过
|
|
924
|
-
const pythonExe = this._framework.toolRegistry._tools.get('
|
|
923
|
+
// 通过 py_execute 工具执行
|
|
924
|
+
const pythonExe = this._framework.toolRegistry._tools.get('py_execute')
|
|
925
925
|
if (!pythonExe) {
|
|
926
|
-
return { success: false, error: '
|
|
926
|
+
return { success: false, error: 'py_execute tool not found. Please install python-executor-plugin.' }
|
|
927
927
|
}
|
|
928
928
|
|
|
929
929
|
const pythonResult = await pythonExe.execute({ code: execCode })
|
|
@@ -605,7 +605,30 @@ class SchedulerPlugin extends Plugin {
|
|
|
605
605
|
|
|
606
606
|
try {
|
|
607
607
|
if (task.type === 'shell') {
|
|
608
|
-
|
|
608
|
+
let result;
|
|
609
|
+
if (this._framework.toolRegistry?.has?.('shell_exec') || this._framework.toolRegistry?.has?.('shell')) {
|
|
610
|
+
const toolName = this._framework.toolRegistry.has('shell_exec') ? 'shell_exec' : 'shell';
|
|
611
|
+
result = await this._framework.executeTool(toolName, { command: task.shell });
|
|
612
|
+
// 适配 shell_exec 的返回格式({success, stdout, stderr})到统一格式
|
|
613
|
+
if (result.success === false && result.error) {
|
|
614
|
+
// shell_exec 已捕获错误
|
|
615
|
+
} else if (result.success === false && result.exitCode !== 0) {
|
|
616
|
+
result = { error: `退出码 ${result.exitCode}: ${result.stderr}`, stdout: result.stdout, stderr: result.stderr };
|
|
617
|
+
} else {
|
|
618
|
+
result = { output: result.stdout, stdout: result.stdout, stderr: result.stderr };
|
|
619
|
+
}
|
|
620
|
+
} else {
|
|
621
|
+
// 兜底:shell 工具不存在时直接用 child_process 执行
|
|
622
|
+
const { exec } = require('child_process');
|
|
623
|
+
const util = require('util');
|
|
624
|
+
const execAsync = util.promisify(exec);
|
|
625
|
+
try {
|
|
626
|
+
const { stdout, stderr } = await execAsync(task.shell, { timeout: 60000 });
|
|
627
|
+
result = { output: stdout.trim(), stdout: stdout.trim(), stderr: stderr.trim() };
|
|
628
|
+
} catch (e) {
|
|
629
|
+
result = { error: e.message, stderr: e.stderr || '' };
|
|
630
|
+
}
|
|
631
|
+
}
|
|
609
632
|
this._taskStats.completed++
|
|
610
633
|
|
|
611
634
|
const output = result.error ? `执行失败: ${result.error}` : (result.output || result.stdout || JSON.stringify(result))
|
|
@@ -633,7 +656,8 @@ class SchedulerPlugin extends Plugin {
|
|
|
633
656
|
} else if (task.llm) {
|
|
634
657
|
const schedulerAgent = this._framework.createSubAgent({
|
|
635
658
|
name: 'scheduler_task',
|
|
636
|
-
role: '定时任务执行助手,专注于处理定时提醒和任务执行'
|
|
659
|
+
role: '定时任务执行助手,专注于处理定时提醒和任务执行',
|
|
660
|
+
hidden: true
|
|
637
661
|
})
|
|
638
662
|
|
|
639
663
|
const result = await schedulerAgent.chat(task.message)
|