foliko 1.0.25 → 1.0.27
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/.claude/settings.local.json +8 -1
- package/README.md +114 -29
- package/cli/src/commands/chat.js +0 -12
- package/package.json +1 -1
- package/plugins/ai-plugin.js +0 -7
- package/plugins/default-plugins.js +9 -11
- package/plugins/subagent-plugin.js +144 -46
- package/plugins/telegram-plugin.js +2 -0
- package/plugins/tools-plugin.js +6 -4
- package/plugins/weixin-plugin.js +5 -3
- package/src/core/plugin-base.js +7 -0
- package/src/core/plugin-manager.js +70 -4
|
@@ -57,7 +57,14 @@
|
|
|
57
57
|
"Bash(node -e \"const { WeixinBot } = require\\('@chnak/weixin-bot'\\); console.log\\(typeof WeixinBot\\)\" 2>&1)",
|
|
58
58
|
"Bash(node -e \"import\\('@chnak/weixin-bot'\\).then\\(m => console.log\\('OK:', typeof m.WeixinBot\\)\\).catch\\(e => console.error\\('Error:', e.message\\)\\)\" 2>&1)",
|
|
59
59
|
"Bash(ls -la D:/code/vb-agent/cli/bin/ && cat D:/code/vb-agent/cli/bin/*.js 2>/dev/null | head -50)",
|
|
60
|
-
"Bash(npm config:*)"
|
|
60
|
+
"Bash(npm config:*)",
|
|
61
|
+
"Bash(node -c plugins/subagent-plugin.js 2>&1)",
|
|
62
|
+
"Bash(node -c plugins/default-plugins.js 2>&1)",
|
|
63
|
+
"Bash(node -c plugins/default-plugins.js && node -c src/core/plugin-manager.js && node -c plugins/tools-plugin.js 2>&1)",
|
|
64
|
+
"Bash(node -c src/core/plugin-base.js && node -c src/core/plugin-manager.js 2>&1)",
|
|
65
|
+
"Bash(node -c plugins/telegram-plugin.js && node -c plugins/weixin-plugin.js 2>&1)",
|
|
66
|
+
"Bash(node -c src/core/plugin-manager.js 2>&1)",
|
|
67
|
+
"Bash(node -c src/core/plugin-manager.js && node -c src/core/plugin-base.js && node -c plugins/default-plugins.js && node -c plugins/telegram-plugin.js && node -c plugins/weixin-plugin.js 2>&1)"
|
|
61
68
|
]
|
|
62
69
|
}
|
|
63
70
|
}
|
package/README.md
CHANGED
|
@@ -11,15 +11,42 @@
|
|
|
11
11
|
- **技能系统** - 可扩展的 Skill 管理
|
|
12
12
|
- **会话管理** - 支持多会话切换
|
|
13
13
|
- **规则引擎** - 可配置的行为规则
|
|
14
|
+
- **子 Agent** - 支持多子 Agent 分工协作
|
|
15
|
+
|
|
16
|
+
## 安装
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
# 方式一:npm 安装
|
|
20
|
+
npm install -g foliko
|
|
21
|
+
|
|
22
|
+
# 方式二:Windows 一键脚本安装
|
|
23
|
+
irm https://folikoai.com/install.ps1 | iex
|
|
24
|
+
|
|
25
|
+
# 方式三:Mac/Linux 一键脚本安装
|
|
26
|
+
curl -fsSL https://folikoai.com/install.sh | bash
|
|
27
|
+
```
|
|
14
28
|
|
|
15
29
|
## 快速开始
|
|
16
30
|
|
|
17
31
|
```bash
|
|
18
|
-
#
|
|
19
|
-
|
|
32
|
+
# 全局安装后
|
|
33
|
+
foliko chat
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## CLI 命令
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
# 聊天模式(使用 .env 中的配置)
|
|
40
|
+
foliko chat
|
|
41
|
+
|
|
42
|
+
# 指定完整配置
|
|
43
|
+
foliko chat --provider deepseek --model deepseek-chat --base-url https://api.deepseek.com/v1 --api-key sk-xxx
|
|
44
|
+
|
|
45
|
+
# 只指定 provider(使用 provider 默认 model 和 baseURL)
|
|
46
|
+
foliko chat --provider deepseek --api-key sk-xxx
|
|
20
47
|
|
|
21
|
-
#
|
|
22
|
-
|
|
48
|
+
# 列出所有子Agent配置
|
|
49
|
+
foliko list
|
|
23
50
|
```
|
|
24
51
|
|
|
25
52
|
## 项目结构
|
|
@@ -30,7 +57,7 @@ foliko/
|
|
|
30
57
|
│ └── bin/foliko.js # CLI 入口
|
|
31
58
|
├── src/ # 核心框架
|
|
32
59
|
│ ├── core/ # 核心组件
|
|
33
|
-
│ ├── capabilities/
|
|
60
|
+
│ ├── capabilities/ # 能力插件
|
|
34
61
|
│ └── executors/ # 执行器
|
|
35
62
|
├── plugins/ # 内置插件
|
|
36
63
|
├── skills/ # 技能目录
|
|
@@ -50,19 +77,19 @@ FOLIKO_PROVIDER=minimax
|
|
|
50
77
|
# AI Model(可选,不填则使用 provider 默认值)
|
|
51
78
|
# MiniMax: MiniMax-M2.7
|
|
52
79
|
# DeepSeek: deepseek-chat, deepseek-coder 等
|
|
53
|
-
FOLIKO_MODEL=
|
|
80
|
+
FOLIKO_MODEL=MiniMax-M2.7
|
|
54
81
|
|
|
55
82
|
# API Base URL(可选,不填则使用 provider 默认值)
|
|
56
83
|
# MiniMax: https://api.minimaxi.com/v1
|
|
57
84
|
# DeepSeek: https://api.deepseek.com/v1
|
|
58
|
-
FOLIKO_BASE_URL=
|
|
85
|
+
FOLIKO_BASE_URL=https://api.minimaxi.com/v1
|
|
59
86
|
|
|
60
87
|
# API Key(通用,不填则使用 provider 专用 key)
|
|
61
|
-
FOLIKO_API_KEY=
|
|
88
|
+
FOLIKO_API_KEY=sk-your-minimax-api-key
|
|
62
89
|
|
|
63
90
|
# Provider 专用 API Key(可选)
|
|
64
|
-
DEEPSEEK_API_KEY=sk-your-deepseek-api-key
|
|
65
|
-
MINIMAX_API_KEY=sk-your-minimax-api-key
|
|
91
|
+
# DEEPSEEK_API_KEY=sk-your-deepseek-api-key
|
|
92
|
+
# MINIMAX_API_KEY=sk-your-minimax-api-key
|
|
66
93
|
```
|
|
67
94
|
|
|
68
95
|
**配置优先级**:命令行参数 > .env配置 > provider默认值
|
|
@@ -74,11 +101,49 @@ MINIMAX_API_KEY=sk-your-minimax-api-key
|
|
|
74
101
|
├── config # 配置文件
|
|
75
102
|
├── ai.json # AI 配置
|
|
76
103
|
├── mcp_config.json # MCP 服务器配置
|
|
104
|
+
├── agents/ # 子Agent配置目录
|
|
77
105
|
├── plugins/ # 用户插件目录
|
|
78
106
|
├── skills/ # 用户技能目录
|
|
79
107
|
└── data/ # 数据目录
|
|
80
108
|
```
|
|
81
109
|
|
|
110
|
+
### 子 Agent 配置 (.agent/agents/)
|
|
111
|
+
|
|
112
|
+
在 `.agent/agents/` 目录下放置子 Agent 配置文件,支持 `.js`、`.json`、`.md` 格式:
|
|
113
|
+
|
|
114
|
+
```
|
|
115
|
+
.agent/agents/
|
|
116
|
+
├── backend-specialist.md
|
|
117
|
+
├── frontend-specialist.md
|
|
118
|
+
└── database-architect.md
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
#### JSON 格式
|
|
122
|
+
|
|
123
|
+
```json
|
|
124
|
+
{
|
|
125
|
+
"name": "backend-specialist",
|
|
126
|
+
"role": "Backend Specialist",
|
|
127
|
+
"description": "Expert in backend development",
|
|
128
|
+
"parentTools": ["read_file", "run_command"]
|
|
129
|
+
}
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
#### Markdown 格式
|
|
133
|
+
|
|
134
|
+
```markdown
|
|
135
|
+
# Backend Specialist
|
|
136
|
+
|
|
137
|
+
```json
|
|
138
|
+
{
|
|
139
|
+
"name": "backend-specialist",
|
|
140
|
+
"role": "Backend Specialist",
|
|
141
|
+
"description": "Expert in backend development",
|
|
142
|
+
"parentTools": ["read_file", "run_command"]
|
|
143
|
+
}
|
|
144
|
+
```
|
|
145
|
+
```
|
|
146
|
+
|
|
82
147
|
### ai.json 格式
|
|
83
148
|
|
|
84
149
|
```json
|
|
@@ -191,37 +256,57 @@ allowed-tools: tool1,tool2
|
|
|
191
256
|
技能内容...
|
|
192
257
|
```
|
|
193
258
|
|
|
194
|
-
## CLI 命令
|
|
195
|
-
|
|
196
|
-
```bash
|
|
197
|
-
# 聊天模式(使用 .env 中的配置)
|
|
198
|
-
npm run chat
|
|
199
|
-
|
|
200
|
-
# 指定完整配置
|
|
201
|
-
foliko chat --provider deepseek --model deepseek-chat --base-url https://api.deepseek.com/v1 --api-key sk-xxx
|
|
202
|
-
|
|
203
|
-
# 只指定 provider(使用 provider 默认 model 和 baseURL)
|
|
204
|
-
foliko chat --provider deepseek --api-key sk-xxx
|
|
205
|
-
|
|
206
|
-
# 只指定 api-key(使用 FOLIKO_PROVIDER 设定的 provider)
|
|
207
|
-
foliko chat --api-key sk-xxx
|
|
208
|
-
```
|
|
209
|
-
|
|
210
259
|
## 内置工具
|
|
211
260
|
|
|
261
|
+
### 通用工具
|
|
262
|
+
|
|
212
263
|
| 工具 | 说明 |
|
|
213
264
|
|------|------|
|
|
214
265
|
| `loadSkill` | 加载技能 |
|
|
215
|
-
| `
|
|
216
|
-
| `
|
|
217
|
-
| `
|
|
266
|
+
| `list_plugins` | 列出所有插件 |
|
|
267
|
+
| `list_tools` | 列出所有工具 |
|
|
268
|
+
| `reload_plugins` | 重载插件 |
|
|
269
|
+
| `shell` | 执行 Shell 命令 |
|
|
270
|
+
| `powershell` | 执行 PowerShell 命令 |
|
|
271
|
+
| `python-execute` | 执行 Python 代码 |
|
|
272
|
+
| `python_script` | 执行 Python 脚本 |
|
|
273
|
+
| `pip_install` | 安装 Python 包 |
|
|
218
274
|
| `install` | 安装 npm 包 |
|
|
219
275
|
| `mcp_reload` | 重载 MCP 服务器 |
|
|
220
276
|
| `audit_query` | 查询审计日志 |
|
|
221
277
|
|
|
278
|
+
### 子 Agent 管理工具
|
|
279
|
+
|
|
280
|
+
| 工具 | 说明 |
|
|
281
|
+
|------|------|
|
|
282
|
+
| `subagent_list` | 列出所有子Agent |
|
|
283
|
+
| `subagent_call` | 调用指定的子Agent处理任务 |
|
|
284
|
+
| `subagent_reload` | 重新加载子Agent配置 |
|
|
285
|
+
|
|
286
|
+
### 会话工具
|
|
287
|
+
|
|
288
|
+
| 工具 | 说明 |
|
|
289
|
+
|------|------|
|
|
290
|
+
| `session_create` | 创建会话 |
|
|
291
|
+
| `session_get` | 获取会话 |
|
|
292
|
+
| `session_list` | 列出所有会话 |
|
|
293
|
+
| `session_delete` | 删除会话 |
|
|
294
|
+
| `session_history` | 获取会话历史 |
|
|
295
|
+
|
|
296
|
+
### 定时任务工具
|
|
297
|
+
|
|
298
|
+
| 工具 | 说明 |
|
|
299
|
+
|------|------|
|
|
300
|
+
| `schedule_task` | 创建定时任务 |
|
|
301
|
+
| `schedule_list` | 列出定时任务 |
|
|
302
|
+
| `schedule_cancel` | 取消定时任务 |
|
|
303
|
+
| `cron_examples` | 显示 cron 示例 |
|
|
304
|
+
|
|
222
305
|
## 工作目录
|
|
223
306
|
|
|
224
307
|
CLI 工作目录默认使用执行命令的目录。
|
|
308
|
+
- `.agent/` 目录会创建在工作目录下
|
|
309
|
+
- 配置文件、插件、技能等都放在工作目录的 `.agent/` 下
|
|
225
310
|
|
|
226
311
|
## 许可证
|
|
227
312
|
|
package/cli/src/commands/chat.js
CHANGED
|
@@ -81,20 +81,8 @@ function parseArgs(args) {
|
|
|
81
81
|
* Chat 命令入口
|
|
82
82
|
*/
|
|
83
83
|
async function chatCommand(args) {
|
|
84
|
-
console.log('[chatCommand] Starting with env:', {
|
|
85
|
-
FOLIKO_PROVIDER: process.env.FOLIKO_PROVIDER,
|
|
86
|
-
FOLIKO_MODEL: process.env.FOLIKO_MODEL,
|
|
87
|
-
MINIMAX_API_KEY: process.env.MINIMAX_API_KEY ? '***' + process.env.MINIMAX_API_KEY.slice(-4) : 'null'
|
|
88
|
-
})
|
|
89
84
|
|
|
90
85
|
const options = parseArgs(args)
|
|
91
|
-
console.log('[chatCommand] Parsed options:', {
|
|
92
|
-
provider: options.provider,
|
|
93
|
-
model: options.model,
|
|
94
|
-
baseURL: options.baseURL,
|
|
95
|
-
apiKey: options.apiKey ? '***' + options.apiKey.slice(-4) : 'null'
|
|
96
|
-
})
|
|
97
|
-
|
|
98
86
|
console.log('=== Foliko 持续对话 ===\n')
|
|
99
87
|
console.log('输入 exit 或 quit 退出\n')
|
|
100
88
|
|
package/package.json
CHANGED
package/plugins/ai-plugin.js
CHANGED
|
@@ -38,13 +38,6 @@ class AIPlugin extends Plugin {
|
|
|
38
38
|
}
|
|
39
39
|
|
|
40
40
|
_initAIClient() {
|
|
41
|
-
console.log('[AIPlugin] initAIClient called')
|
|
42
|
-
console.log('[AIPlugin] config:', JSON.stringify({
|
|
43
|
-
provider: this.config.provider,
|
|
44
|
-
model: this.config.model,
|
|
45
|
-
apiKey: this.config.apiKey ? '***' + this.config.apiKey.slice(-4) : 'null',
|
|
46
|
-
baseURL: this.config.baseURL
|
|
47
|
-
}))
|
|
48
41
|
|
|
49
42
|
if (!this.config.apiKey) {
|
|
50
43
|
console.warn('[AIPlugin] No API key provided, AI features disabled')
|
|
@@ -393,19 +393,18 @@ async function bootstrapDefaults(framework, config = {}) {
|
|
|
393
393
|
console.log('[Bootstrap] Python Plugin Loader loaded')
|
|
394
394
|
}
|
|
395
395
|
|
|
396
|
-
// 12.6 Telegram
|
|
396
|
+
// 12.6 Telegram 插件(默认禁用,需要在 plugins.json 中设置 enabled: true)
|
|
397
397
|
if (shouldLoad('telegram')) {
|
|
398
|
-
const
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
await framework.loadPlugin(new TelegramPlugin({ botToken: telegramToken }))
|
|
404
|
-
console.log('[Bootstrap] Telegram Plugin loaded')
|
|
398
|
+
const { Plugin } = require('../src/core/plugin-base')
|
|
399
|
+
const createTelegramPlugin = require('./telegram-plugin')
|
|
400
|
+
const TelegramPlugin = createTelegramPlugin(Plugin)
|
|
401
|
+
const telegramConfig = {
|
|
402
|
+
botToken: process.env.TELEGRAM_BOT_TOKEN || agentConfig.telegram?.botToken
|
|
405
403
|
}
|
|
404
|
+
await framework.loadPlugin(new TelegramPlugin(telegramConfig))
|
|
406
405
|
}
|
|
407
406
|
|
|
408
|
-
// 12.7 WeChat
|
|
407
|
+
// 12.7 WeChat 插件(默认禁用,需要在 plugins.json 中设置 enabled: true)
|
|
409
408
|
if (shouldLoad('weixin')) {
|
|
410
409
|
try {
|
|
411
410
|
const { Plugin } = require('../src/core/plugin-base')
|
|
@@ -417,13 +416,12 @@ async function bootstrapDefaults(framework, config = {}) {
|
|
|
417
416
|
allowedUsers: agentConfig.weixin?.allowedUsers || []
|
|
418
417
|
}
|
|
419
418
|
await framework.loadPlugin(new WeixinPlugin(weixinConfig))
|
|
420
|
-
console.log('[Bootstrap] WeChat Plugin loaded')
|
|
421
419
|
} catch (err) {
|
|
422
420
|
console.warn('[Bootstrap] WeChat Plugin not available:', err.message)
|
|
423
421
|
}
|
|
424
422
|
}
|
|
425
423
|
|
|
426
|
-
// 12.8 SubAgent
|
|
424
|
+
// 12.8 SubAgent 管理器
|
|
427
425
|
if (shouldLoad('subagent-manager')) {
|
|
428
426
|
try {
|
|
429
427
|
const { SubAgentManagerPlugin } = require('./subagent-plugin')
|
|
@@ -40,7 +40,7 @@ class SubAgentPlugin extends Plugin {
|
|
|
40
40
|
}
|
|
41
41
|
|
|
42
42
|
start(framework) {
|
|
43
|
-
// 创建子Agent
|
|
43
|
+
// 创建子Agent(可能需要等待父agent准备好)
|
|
44
44
|
this._createSubAgent()
|
|
45
45
|
|
|
46
46
|
// 注册委托工具到主Agent(通过framework获取父agent)
|
|
@@ -56,7 +56,30 @@ class SubAgentPlugin extends Plugin {
|
|
|
56
56
|
// 获取父Agent(主Agent)
|
|
57
57
|
const parentAgent = this._getParentAgent()
|
|
58
58
|
if (!parentAgent) {
|
|
59
|
-
|
|
59
|
+
// 父agent还没创建,监听事件等其创建
|
|
60
|
+
if (this._framework) {
|
|
61
|
+
const framework = this._framework
|
|
62
|
+
framework.on('agent:created', (agent) => {
|
|
63
|
+
if (framework._mainAgent && agent === framework._mainAgent) {
|
|
64
|
+
// 父agent已创建,重新尝试创建子agent
|
|
65
|
+
this._doCreateSubAgent(agent)
|
|
66
|
+
// 注册委托工具
|
|
67
|
+
this._registerDelegateTool()
|
|
68
|
+
}
|
|
69
|
+
})
|
|
70
|
+
}
|
|
71
|
+
return
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
this._doCreateSubAgent(parentAgent)
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* 实际执行创建子Agent
|
|
79
|
+
*/
|
|
80
|
+
_doCreateSubAgent(parentAgent) {
|
|
81
|
+
// 避免重复创建
|
|
82
|
+
if (this._agent) {
|
|
60
83
|
return
|
|
61
84
|
}
|
|
62
85
|
|
|
@@ -79,7 +102,7 @@ class SubAgentPlugin extends Plugin {
|
|
|
79
102
|
|
|
80
103
|
// 注册从父Agent继承的工具
|
|
81
104
|
for (const tool of parentTools) {
|
|
82
|
-
this.
|
|
105
|
+
this._framework.registerTool(tool)
|
|
83
106
|
}
|
|
84
107
|
|
|
85
108
|
// 注册自定义工具(只属于此子Agent,不污染全局)
|
|
@@ -88,10 +111,10 @@ class SubAgentPlugin extends Plugin {
|
|
|
88
111
|
for (const [toolName, toolDef] of Object.entries(this.config.tools)) {
|
|
89
112
|
const tool = typeof toolDef === 'function' ? toolDef() : toolDef
|
|
90
113
|
if (typeof tool === 'object' && tool.name) {
|
|
91
|
-
this.
|
|
114
|
+
this._framework.registerTool(tool)
|
|
92
115
|
} else {
|
|
93
116
|
// 假设是简化的工具定义,需要补充 name
|
|
94
|
-
this.
|
|
117
|
+
this._framework.registerTool({ name: toolName, ...tool })
|
|
95
118
|
}
|
|
96
119
|
}
|
|
97
120
|
}
|
|
@@ -186,27 +209,31 @@ class SubAgentPlugin extends Plugin {
|
|
|
186
209
|
}
|
|
187
210
|
|
|
188
211
|
/**
|
|
189
|
-
*
|
|
212
|
+
* 注册委托工具到框架和主Agent
|
|
190
213
|
*/
|
|
191
214
|
_registerDelegateTool() {
|
|
192
|
-
const
|
|
193
|
-
if (!
|
|
215
|
+
const framework = this._framework
|
|
216
|
+
if (!framework) return
|
|
194
217
|
|
|
195
218
|
const agentName = this.config.name
|
|
196
219
|
const role = this.config.role
|
|
197
220
|
|
|
198
|
-
|
|
199
|
-
parentAgent.registerTool({
|
|
221
|
+
const toolDef = {
|
|
200
222
|
name: agentName,
|
|
201
223
|
description: `${role}:当需要${role}时,调用此工具执行任务。如:编译代码、运行测试、代码重构等。`,
|
|
202
224
|
inputSchema: z.object({
|
|
203
225
|
task: z.string().describe('给子Agent的具体任务描述')
|
|
204
226
|
}),
|
|
205
|
-
execute: async (args,
|
|
227
|
+
execute: async (args, fw) => {
|
|
206
228
|
if (!this._agent) {
|
|
207
229
|
return { error: `SubAgent ${agentName} not initialized` }
|
|
208
230
|
}
|
|
209
231
|
|
|
232
|
+
const parentAgent = fw._mainAgent
|
|
233
|
+
if (!parentAgent) {
|
|
234
|
+
return { error: `Parent agent not found` }
|
|
235
|
+
}
|
|
236
|
+
|
|
210
237
|
// 发射子Agent开始处理事件
|
|
211
238
|
parentAgent.emit('subagent:chat:start', {
|
|
212
239
|
parentAgent,
|
|
@@ -244,9 +271,17 @@ class SubAgentPlugin extends Plugin {
|
|
|
244
271
|
}
|
|
245
272
|
}
|
|
246
273
|
}
|
|
247
|
-
}
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
// 注册到框架(供后续创建的 Agent 同步)
|
|
277
|
+
framework.registerTool(toolDef)
|
|
248
278
|
|
|
249
|
-
|
|
279
|
+
// 如果主 Agent 已存在,直接注册到它
|
|
280
|
+
if (framework._mainAgent) {
|
|
281
|
+
framework._mainAgent.registerTool(toolDef)
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
console.log(`[SubAgent:${this.config.name}] Delegate tool registered`)
|
|
250
285
|
}
|
|
251
286
|
|
|
252
287
|
/**
|
|
@@ -323,11 +358,20 @@ class SubAgentManagerPlugin extends Plugin {
|
|
|
323
358
|
this._subAgents.set(agentConfig.name, plugin)
|
|
324
359
|
}
|
|
325
360
|
|
|
326
|
-
//
|
|
327
|
-
|
|
328
|
-
if (
|
|
329
|
-
this._registerToolsToAgent(
|
|
361
|
+
// 注册管理工具
|
|
362
|
+
// 如果主 Agent 已存在,直接注册到它(因为它不会自动同步框架工具)
|
|
363
|
+
if (framework._mainAgent) {
|
|
364
|
+
this._registerToolsToAgent(framework._mainAgent)
|
|
330
365
|
}
|
|
366
|
+
// 同时注册到框架(供后续创建的 Agent 同步)
|
|
367
|
+
this._registerTools(framework)
|
|
368
|
+
|
|
369
|
+
// 监听主 Agent 创建事件,为后续创建的主 Agent 注册工具
|
|
370
|
+
framework.on('agent:created', (agent) => {
|
|
371
|
+
if (framework._mainAgent && agent === framework._mainAgent) {
|
|
372
|
+
this._registerToolsToAgent(agent)
|
|
373
|
+
}
|
|
374
|
+
})
|
|
331
375
|
|
|
332
376
|
// 启动文件监控
|
|
333
377
|
this._startFileWatcher()
|
|
@@ -339,8 +383,6 @@ class SubAgentManagerPlugin extends Plugin {
|
|
|
339
383
|
* 注册管理工具到指定 Agent
|
|
340
384
|
*/
|
|
341
385
|
_registerToolsToAgent(agent) {
|
|
342
|
-
if (!agent) return
|
|
343
|
-
|
|
344
386
|
agent.registerTool({
|
|
345
387
|
name: 'subagent_list',
|
|
346
388
|
description: '列出所有子Agent',
|
|
@@ -400,16 +442,82 @@ class SubAgentManagerPlugin extends Plugin {
|
|
|
400
442
|
console.log('[SubAgentManager] Management tools registered to agent')
|
|
401
443
|
}
|
|
402
444
|
|
|
445
|
+
/**
|
|
446
|
+
* 注册管理工具到框架工具注册表
|
|
447
|
+
*/
|
|
448
|
+
_registerTools(framework) {
|
|
449
|
+
framework.registerTool({
|
|
450
|
+
name: 'subagent_list',
|
|
451
|
+
description: '列出所有子Agent',
|
|
452
|
+
inputSchema: z.object({}),
|
|
453
|
+
execute: async () => {
|
|
454
|
+
const agents = Array.from(this._subAgents.values()).map(p => ({
|
|
455
|
+
name: p.config.name,
|
|
456
|
+
role: p.config.role,
|
|
457
|
+
description: p.config.description,
|
|
458
|
+
toolCount: p._agent ? p._agent.getTools().length : 0
|
|
459
|
+
}))
|
|
460
|
+
return { success: true, agents }
|
|
461
|
+
}
|
|
462
|
+
})
|
|
463
|
+
|
|
464
|
+
framework.registerTool({
|
|
465
|
+
name: 'subagent_call',
|
|
466
|
+
description: '调用指定的子Agent处理任务',
|
|
467
|
+
inputSchema: z.object({
|
|
468
|
+
agentName: z.string().describe('子Agent名称'),
|
|
469
|
+
task: z.string().describe('任务描述')
|
|
470
|
+
}),
|
|
471
|
+
execute: async (args) => {
|
|
472
|
+
const plugin = this._subAgents.get(args.agentName)
|
|
473
|
+
if (!plugin) {
|
|
474
|
+
return { success: false, error: `SubAgent ${args.agentName} not found` }
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
try {
|
|
478
|
+
const result = await plugin.chat(args.task)
|
|
479
|
+
return {
|
|
480
|
+
success: true,
|
|
481
|
+
agent: args.agentName,
|
|
482
|
+
result: result.message || result,
|
|
483
|
+
success: result.success !== false
|
|
484
|
+
}
|
|
485
|
+
} catch (err) {
|
|
486
|
+
return { success: false, error: err.message }
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
})
|
|
490
|
+
|
|
491
|
+
framework.registerTool({
|
|
492
|
+
name: 'subagent_reload',
|
|
493
|
+
description: '重新加载子Agent配置(当新增或删除.agent/agents下的文件后使用)',
|
|
494
|
+
inputSchema: z.object({}),
|
|
495
|
+
execute: async () => {
|
|
496
|
+
try {
|
|
497
|
+
this.reload(this._framework)
|
|
498
|
+
return { success: true, message: '子Agent配置已重新加载' }
|
|
499
|
+
} catch (err) {
|
|
500
|
+
return { success: false, error: err.message }
|
|
501
|
+
}
|
|
502
|
+
}
|
|
503
|
+
})
|
|
504
|
+
|
|
505
|
+
console.log('[SubAgentManager] Management tools registered to framework')
|
|
506
|
+
}
|
|
507
|
+
|
|
403
508
|
/**
|
|
404
509
|
* 从 agentsDir 目录加载子Agent配置
|
|
405
510
|
*/
|
|
406
511
|
_loadAgentsFromDir() {
|
|
407
512
|
const agentsDir = this.config.agentsDir
|
|
513
|
+
console.log('[SubAgentManager] _loadAgentsFromDir called, agentsDir:', agentsDir)
|
|
408
514
|
if (!agentsDir || !fs.existsSync(agentsDir)) {
|
|
515
|
+
console.log('[SubAgentManager] agentsDir not found or does not exist')
|
|
409
516
|
return
|
|
410
517
|
}
|
|
411
518
|
|
|
412
519
|
const entries = fs.readdirSync(agentsDir, { withFileTypes: true })
|
|
520
|
+
console.log('[SubAgentManager] Found entries:', entries.length)
|
|
413
521
|
|
|
414
522
|
for (const entry of entries) {
|
|
415
523
|
if (entry.isFile() && (entry.name.endsWith('.js') || entry.name.endsWith('.json') || entry.name.endsWith('.md'))) {
|
|
@@ -429,6 +537,7 @@ class SubAgentManagerPlugin extends Plugin {
|
|
|
429
537
|
} else if (entry.name.endsWith('.md')) {
|
|
430
538
|
// 解析 markdown 文件,提取 JSON 配置
|
|
431
539
|
agentConfig = this._parseMarkdownConfig(filePath, baseName)
|
|
540
|
+
console.log('[SubAgentManager] Parsed md:', baseName, agentConfig)
|
|
432
541
|
} else {
|
|
433
542
|
// 清除缓存并加载
|
|
434
543
|
delete require.cache[require.resolve(filePath)]
|
|
@@ -439,9 +548,12 @@ class SubAgentManagerPlugin extends Plugin {
|
|
|
439
548
|
if (agentConfig && agentConfig.name) {
|
|
440
549
|
agentConfig._fromDir = true
|
|
441
550
|
this.config.agents.push(agentConfig)
|
|
551
|
+
console.log('[SubAgentManager] Loaded agent:', agentConfig.name)
|
|
552
|
+
} else {
|
|
553
|
+
console.warn('[SubAgentManager] Agent config has no name:', baseName, agentConfig)
|
|
442
554
|
}
|
|
443
555
|
} catch (err) {
|
|
444
|
-
console.warn(`[SubAgentManager] Failed to load agent config from ${filePath}:`, err.message)
|
|
556
|
+
console.warn(`[SubAgentManager] Failed to load agent config from ${filePath}:`, err.message, err.stack)
|
|
445
557
|
}
|
|
446
558
|
}
|
|
447
559
|
}
|
|
@@ -457,48 +569,34 @@ class SubAgentManagerPlugin extends Plugin {
|
|
|
457
569
|
*/
|
|
458
570
|
_parseMarkdownConfig(filePath, defaultName) {
|
|
459
571
|
const content = fs.readFileSync(filePath, 'utf-8')
|
|
572
|
+
console.log('[SubAgentManager] _parseMarkdownConfig:', filePath)
|
|
460
573
|
|
|
461
574
|
// 尝试从 code block 中提取 JSON
|
|
462
575
|
const jsonMatch = content.match(/```(?:json)?\s*\n([\s\S]*?)\n\s*```/)
|
|
463
576
|
if (jsonMatch) {
|
|
464
577
|
try {
|
|
465
578
|
const config = JSON.parse(jsonMatch[1].trim())
|
|
579
|
+
console.log('[SubAgentManager] Found JSON in code block')
|
|
466
580
|
return config
|
|
467
581
|
} catch (err) {
|
|
468
582
|
// JSON 解析失败,继续
|
|
469
583
|
}
|
|
470
584
|
}
|
|
471
585
|
|
|
472
|
-
// 尝试从
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
if (line.trim() === '---') {
|
|
482
|
-
if (inFrontmatter) {
|
|
483
|
-
// 解析 frontmatter
|
|
484
|
-
try {
|
|
485
|
-
const parsed = this._parseYamlLike(frontmatterContent)
|
|
486
|
-
return { ...config, ...parsed }
|
|
487
|
-
} catch (err) {
|
|
488
|
-
// 解析失败
|
|
489
|
-
}
|
|
490
|
-
}
|
|
491
|
-
inFrontmatter = true
|
|
492
|
-
frontmatterContent = ''
|
|
493
|
-
continue
|
|
494
|
-
}
|
|
495
|
-
|
|
496
|
-
if (inFrontmatter) {
|
|
497
|
-
frontmatterContent += line + '\n'
|
|
586
|
+
// 尝试从 frontmatter 格式中提取
|
|
587
|
+
const frontmatterMatch = content.match(/^---\n([\s\S]*?)\n---/)
|
|
588
|
+
if (frontmatterMatch) {
|
|
589
|
+
console.log('[SubAgentManager] Found frontmatter')
|
|
590
|
+
const frontmatterContent = frontmatterMatch[1]
|
|
591
|
+
const parsed = this._parseYamlLike(frontmatterContent)
|
|
592
|
+
if (parsed && parsed.name) {
|
|
593
|
+
console.log('[SubAgentManager] Parsed frontmatter:', parsed)
|
|
594
|
+
return { name: parsed.name || defaultName, ...parsed }
|
|
498
595
|
}
|
|
499
596
|
}
|
|
500
597
|
|
|
501
598
|
// 从 markdown 内容中提取配置(key: value 格式)
|
|
599
|
+
const config = { name: defaultName }
|
|
502
600
|
const yamlMatch = content.match(/^(name|role|description|parentTools|tools):\s*(.+)$/m)
|
|
503
601
|
if (yamlMatch) {
|
|
504
602
|
config[yamlMatch[1]] = yamlMatch[2].trim()
|
|
@@ -27,6 +27,8 @@ module.exports = function(Plugin) {
|
|
|
27
27
|
this.version = '2.0.0'
|
|
28
28
|
this.description = 'Telegram 对话插件,绑定主Agent进行持续对话'
|
|
29
29
|
this.priority = 80
|
|
30
|
+
// 默认不启用,需要在 plugins.json 中设置 enabled: true
|
|
31
|
+
this.enabled = false
|
|
30
32
|
|
|
31
33
|
this.config = {
|
|
32
34
|
botToken: config.botToken || process.env.TELEGRAM_BOT_TOKEN,
|
package/plugins/tools-plugin.js
CHANGED
|
@@ -61,18 +61,20 @@ class ToolsPlugin extends Plugin {
|
|
|
61
61
|
}
|
|
62
62
|
})
|
|
63
63
|
|
|
64
|
-
//
|
|
64
|
+
// 列出所有插件工具(包括未加载的)
|
|
65
65
|
framework.registerTool({
|
|
66
66
|
name: 'list_plugins',
|
|
67
|
-
description: '
|
|
67
|
+
description: '列出所有插件(包括未加载的)',
|
|
68
68
|
inputSchema: z.object({}),
|
|
69
69
|
execute: async () => {
|
|
70
|
-
const plugins = framework.pluginManager.
|
|
70
|
+
const plugins = framework.pluginManager.getAllKnown()
|
|
71
71
|
return {
|
|
72
72
|
success: true,
|
|
73
73
|
plugins: plugins.map(p => ({
|
|
74
74
|
name: p.name,
|
|
75
|
-
|
|
75
|
+
status: p.status,
|
|
76
|
+
enabled: p.enabled,
|
|
77
|
+
version: p.version
|
|
76
78
|
}))
|
|
77
79
|
}
|
|
78
80
|
}
|
package/plugins/weixin-plugin.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* WeChat 插件
|
|
3
|
-
*
|
|
3
|
+
* 使用 @chnak/weixin-bot 实现微信对话
|
|
4
4
|
*
|
|
5
5
|
* 配置:
|
|
6
6
|
* - forceLogin: 是否强制重新扫码登录
|
|
@@ -18,6 +18,8 @@ module.exports = function(Plugin) {
|
|
|
18
18
|
this.version = '1.0.0'
|
|
19
19
|
this.description = '微信对话插件,使用微信网页账号进行对话'
|
|
20
20
|
this.priority = 80
|
|
21
|
+
// 默认不启用,需要在 plugins.json 中设置 enabled: true
|
|
22
|
+
this.enabled = false
|
|
21
23
|
|
|
22
24
|
this.config = {
|
|
23
25
|
forceLogin: config.forceLogin || process.env.WEIXIN_FORCE_LOGIN === 'true',
|
|
@@ -73,8 +75,8 @@ module.exports = function(Plugin) {
|
|
|
73
75
|
const module = await import('@chnak/weixin-bot')
|
|
74
76
|
WeixinBot = module.WeixinBot
|
|
75
77
|
} catch (err) {
|
|
76
|
-
console.warn('[WeChat] Failed to load@chnak/weixin-bot:', err.message)
|
|
77
|
-
console.warn('[WeChat] Make sure@chnak/weixin-bot is installed: npm install@chnak/weixin-bot qrcode-terminal')
|
|
78
|
+
console.warn('[WeChat] Failed to load @chnak/weixin-bot:', err.message)
|
|
79
|
+
console.warn('[WeChat] Make sure @chnak/weixin-bot is installed: npm install @chnak/weixin-bot qrcode-terminal')
|
|
78
80
|
return
|
|
79
81
|
}
|
|
80
82
|
|
package/src/core/plugin-base.js
CHANGED
|
@@ -16,6 +16,7 @@ class PluginManager {
|
|
|
16
16
|
this._plugins = new Map()
|
|
17
17
|
this._loading = false
|
|
18
18
|
this._stateFile = path.join(process.cwd(), '.agent', 'data', 'plugins-state.json')
|
|
19
|
+
this._knownPlugins = new Set() // 已知插件列表(包括未加载的)
|
|
19
20
|
}
|
|
20
21
|
|
|
21
22
|
/**
|
|
@@ -89,10 +90,13 @@ class PluginManager {
|
|
|
89
90
|
pluginInstance.config = { ...pluginInstance.config, ...savedConfig }
|
|
90
91
|
}
|
|
91
92
|
|
|
93
|
+
// 使用保存的状态,否则使用插件实例的 enabled,默认 true
|
|
94
|
+
const finalEnabled = savedEnabled !== undefined ? savedEnabled : (pluginInstance.enabled !== undefined ? pluginInstance.enabled : true)
|
|
95
|
+
|
|
92
96
|
this._plugins.set(pluginInstance.name, {
|
|
93
97
|
instance: pluginInstance,
|
|
94
98
|
status: 'registered',
|
|
95
|
-
enabled:
|
|
99
|
+
enabled: finalEnabled
|
|
96
100
|
})
|
|
97
101
|
|
|
98
102
|
this.framework.emit('plugin:registered', pluginInstance)
|
|
@@ -116,7 +120,7 @@ class PluginManager {
|
|
|
116
120
|
pluginInstance = this._createFromObject(plugin)
|
|
117
121
|
}
|
|
118
122
|
|
|
119
|
-
//
|
|
123
|
+
// 如果已注册,使用已注册的实例和状态
|
|
120
124
|
const existing = this._plugins.get(pluginInstance.name)
|
|
121
125
|
if (existing) {
|
|
122
126
|
pluginInstance = existing.instance
|
|
@@ -424,12 +428,59 @@ class PluginManager {
|
|
|
424
428
|
.map(e => ({ name: e.instance.name, instance: e.instance }))
|
|
425
429
|
}
|
|
426
430
|
|
|
431
|
+
/**
|
|
432
|
+
* 注册一个已知插件(但不加载)
|
|
433
|
+
* 用于显示所有可用插件列表
|
|
434
|
+
* @param {string} name - 插件名称
|
|
435
|
+
* @param {Object} info - 插件信息
|
|
436
|
+
*/
|
|
437
|
+
registerKnownPlugin(name, info = {}) {
|
|
438
|
+
this._knownPlugins.add(name)
|
|
439
|
+
// 如果插件还没注册过,记录它的信息
|
|
440
|
+
if (!this._plugins.has(name)) {
|
|
441
|
+
this._plugins.set(name, {
|
|
442
|
+
instance: { name, ...info },
|
|
443
|
+
status: 'known',
|
|
444
|
+
enabled: info.enabled !== undefined ? info.enabled : false
|
|
445
|
+
})
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
/**
|
|
450
|
+
* 获取所有已知插件(包括未加载的)
|
|
451
|
+
* @returns {Array} 插件列表,包含 name, status, enabled
|
|
452
|
+
*/
|
|
453
|
+
getAllKnown() {
|
|
454
|
+
const result = []
|
|
455
|
+
for (const name of this._knownPlugins) {
|
|
456
|
+
const entry = this._plugins.get(name)
|
|
457
|
+
result.push({
|
|
458
|
+
name,
|
|
459
|
+
status: entry?.status || 'unknown',
|
|
460
|
+
enabled: entry?.enabled || false,
|
|
461
|
+
version: entry?.instance?.version
|
|
462
|
+
})
|
|
463
|
+
}
|
|
464
|
+
// 也加入已加载但不在 knownPlugins 中的
|
|
465
|
+
for (const [name, entry] of this._plugins) {
|
|
466
|
+
if (!result.find(p => p.name === name)) {
|
|
467
|
+
result.push({
|
|
468
|
+
name,
|
|
469
|
+
status: entry.status,
|
|
470
|
+
enabled: entry.enabled,
|
|
471
|
+
version: entry.instance?.version
|
|
472
|
+
})
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
return result
|
|
476
|
+
}
|
|
477
|
+
|
|
427
478
|
/**
|
|
428
479
|
* 检查插件是否存在
|
|
429
480
|
* @param {string} name - 插件名称
|
|
430
481
|
*/
|
|
431
482
|
has(name) {
|
|
432
|
-
return this._plugins.has(name)
|
|
483
|
+
return this._plugins.has(name) || this._knownPlugins.has(name)
|
|
433
484
|
}
|
|
434
485
|
|
|
435
486
|
/**
|
|
@@ -464,8 +515,12 @@ class PluginManager {
|
|
|
464
515
|
}
|
|
465
516
|
|
|
466
517
|
entry.enabled = true
|
|
518
|
+
// 同步更新插件实例的 enabled 属性,避免 load() 时被跳过
|
|
519
|
+
if (entry.instance) {
|
|
520
|
+
entry.instance.enabled = true
|
|
521
|
+
}
|
|
467
522
|
|
|
468
|
-
//
|
|
523
|
+
// 如果插件已加载,尝试重新启动
|
|
469
524
|
if (entry.status === 'loaded') {
|
|
470
525
|
try {
|
|
471
526
|
// 如果之前已经启动过,先调用 stop 停止旧实例
|
|
@@ -481,6 +536,13 @@ class PluginManager {
|
|
|
481
536
|
} catch (err) {
|
|
482
537
|
console.error(`[PluginManager] Enable/reload failed for '${name}':`, err.message)
|
|
483
538
|
}
|
|
539
|
+
} else if (entry.status === 'registered' || entry.status === 'known') {
|
|
540
|
+
// 插件只注册过但未加载,现在加载它
|
|
541
|
+
try {
|
|
542
|
+
await this.load(entry.instance)
|
|
543
|
+
} catch (err) {
|
|
544
|
+
console.error(`[PluginManager] Enable/load failed for '${name}':`, err.message)
|
|
545
|
+
}
|
|
484
546
|
}
|
|
485
547
|
|
|
486
548
|
this.framework.emit('plugin:enabled', entry.instance)
|
|
@@ -504,6 +566,10 @@ class PluginManager {
|
|
|
504
566
|
}
|
|
505
567
|
|
|
506
568
|
entry.enabled = false
|
|
569
|
+
// 同步更新插件实例的 enabled 属性
|
|
570
|
+
if (entry.instance) {
|
|
571
|
+
entry.instance.enabled = false
|
|
572
|
+
}
|
|
507
573
|
|
|
508
574
|
// 如果插件正在运行,停止它
|
|
509
575
|
if (entry.instance._started) {
|