foliko 1.0.75 → 1.0.76

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 (88) hide show
  1. package/.claude/settings.local.json +159 -157
  2. package/cli/bin/foliko.js +12 -12
  3. package/cli/src/commands/chat.js +143 -143
  4. package/cli/src/commands/list.js +93 -93
  5. package/cli/src/index.js +75 -75
  6. package/cli/src/ui/chat-ui.js +201 -201
  7. package/cli/src/utils/ansi.js +40 -40
  8. package/cli/src/utils/markdown.js +292 -292
  9. package/examples/ambient-example.js +194 -194
  10. package/examples/basic.js +115 -115
  11. package/examples/bootstrap.js +121 -121
  12. package/examples/mcp-example.js +56 -56
  13. package/examples/skill-example.js +49 -49
  14. package/examples/test-chat.js +137 -137
  15. package/examples/test-mcp.js +85 -85
  16. package/examples/test-reload.js +59 -59
  17. package/examples/test-telegram.js +50 -50
  18. package/examples/test-tg-bot.js +45 -45
  19. package/examples/test-tg-simple.js +47 -47
  20. package/examples/test-tg.js +62 -62
  21. package/examples/test-think.js +43 -43
  22. package/examples/test-web-plugin.js +103 -103
  23. package/examples/test-weixin-feishu.js +103 -103
  24. package/examples/workflow.js +158 -158
  25. package/package.json +1 -1
  26. package/plugins/ai-plugin.js +102 -102
  27. package/plugins/ambient-agent/EventWatcher.js +113 -113
  28. package/plugins/ambient-agent/ExplorerLoop.js +640 -640
  29. package/plugins/ambient-agent/GoalManager.js +197 -197
  30. package/plugins/ambient-agent/Reflector.js +95 -95
  31. package/plugins/ambient-agent/StateStore.js +90 -90
  32. package/plugins/ambient-agent/constants.js +101 -101
  33. package/plugins/ambient-agent/index.js +579 -579
  34. package/plugins/audit-plugin.js +187 -187
  35. package/plugins/default-plugins.js +662 -662
  36. package/plugins/email/constants.js +64 -64
  37. package/plugins/email/handlers.js +461 -461
  38. package/plugins/email/index.js +278 -278
  39. package/plugins/email/monitor.js +269 -269
  40. package/plugins/email/parser.js +138 -138
  41. package/plugins/email/reply.js +151 -151
  42. package/plugins/email/utils.js +124 -124
  43. package/plugins/feishu-plugin.js +481 -481
  44. package/plugins/file-system-plugin.js +826 -826
  45. package/plugins/install-plugin.js +199 -199
  46. package/plugins/python-executor-plugin.js +367 -367
  47. package/plugins/python-plugin-loader.js +481 -481
  48. package/plugins/rules-plugin.js +294 -294
  49. package/plugins/scheduler-plugin.js +691 -691
  50. package/plugins/session-plugin.js +369 -369
  51. package/plugins/shell-executor-plugin.js +197 -197
  52. package/plugins/storage-plugin.js +240 -240
  53. package/plugins/subagent-plugin.js +845 -845
  54. package/plugins/telegram-plugin.js +482 -482
  55. package/plugins/think-plugin.js +345 -345
  56. package/plugins/tools-plugin.js +196 -196
  57. package/plugins/web-plugin.js +606 -606
  58. package/plugins/weixin-plugin.js +545 -545
  59. package/src/capabilities/index.js +11 -11
  60. package/src/capabilities/skill-manager.js +609 -609
  61. package/src/capabilities/workflow-engine.js +1109 -1109
  62. package/src/core/agent-chat.js +882 -882
  63. package/src/core/agent.js +892 -892
  64. package/src/core/framework.js +465 -465
  65. package/src/core/index.js +19 -19
  66. package/src/core/plugin-base.js +219 -219
  67. package/src/core/plugin-manager.js +863 -863
  68. package/src/core/provider.js +114 -114
  69. package/src/core/sub-agent-config.js +264 -264
  70. package/src/core/system-prompt-builder.js +120 -120
  71. package/src/core/tool-registry.js +517 -517
  72. package/src/core/tool-router.js +297 -297
  73. package/src/executors/executor-base.js +58 -58
  74. package/src/executors/mcp-executor.js +741 -741
  75. package/src/index.js +25 -25
  76. package/src/utils/circuit-breaker.js +301 -301
  77. package/src/utils/error-boundary.js +363 -363
  78. package/src/utils/error.js +374 -374
  79. package/src/utils/event-emitter.js +97 -97
  80. package/src/utils/id.js +133 -133
  81. package/src/utils/index.js +217 -217
  82. package/src/utils/logger.js +181 -181
  83. package/src/utils/plugin-helpers.js +90 -90
  84. package/src/utils/retry.js +122 -122
  85. package/src/utils/sandbox.js +292 -292
  86. package/test/tool-registry-validation.test.js +218 -218
  87. package/website/script.js +136 -136
  88. package/foliko-1.0.75.tgz +0 -0
@@ -1,846 +1,846 @@
1
- /**
2
- * SubAgent 子Agent插件
3
- * 创建具有独立工具集的子Agent,用于分工处理不同领域的任务
4
- */
5
-
6
- const fs = require('fs')
7
- const path = require('path')
8
- const { Plugin } = require('../src/core/plugin-base')
9
- const { logger } = require('../src/utils/logger')
10
- const log = logger.child('SubAgent')
11
- const { z } = require('zod')
12
- const { Agent } = require('../src/core/agent')
13
-
14
- class SubAgentPlugin extends Plugin {
15
- constructor(config = {}) {
16
- super()
17
- this.name = config.name
18
- this.version = '1.0.0'
19
- this.description = config.description || `子Agent: ${config.name}`
20
- this.priority = 10
21
-
22
- this.system = true
23
-
24
- this.role= config.role || config.name
25
- this.tools = config.tools || {}
26
- this.parentTools = config.parentTools || []
27
- this.llmConfig = config.llmConfig || null
28
-
29
- // 子Agent配置
30
- // tools: { toolName: toolFn } 自定义工具(只属于此子Agent)
31
- // parentTools: ['read_file'] 从父Agent继承的工具名称列表
32
- this.config = {}
33
-
34
- this._framework = null
35
- this._agent = null
36
- this._parentAgent = null
37
- }
38
-
39
- install(framework) {
40
- this._framework = framework
41
- return this
42
- }
43
-
44
- start(framework) {
45
- // 创建子Agent(可能需要等待父agent准备好)
46
- this._createSubAgent()
47
-
48
- // 注册委托工具到主Agent(通过framework获取父agent)
49
- this._registerDelegateTool()
50
-
51
- return this
52
- }
53
-
54
- /**
55
- * 创建子Agent
56
- */
57
- _createSubAgent() {
58
- // 获取父Agent(主Agent)
59
- const parentAgent = this._getParentAgent()
60
- if (!parentAgent) {
61
- // 父agent还没创建,监听事件等其创建
62
- if (this._framework) {
63
- this._framework.on('agent:created', (agent) => {
64
- if (this._framework._mainAgent && agent === this._framework._mainAgent) {
65
- // 父agent已创建,更新父agent引用
66
- this._parentAgent = agent
67
- // 重新尝试创建子agent
68
- this._doCreateSubAgent(agent)
69
- // 注册委托工具
70
- this._registerDelegateTool()
71
- }
72
- })
73
- }
74
- return
75
- }
76
-
77
- this._doCreateSubAgent(parentAgent)
78
- }
79
-
80
- /**
81
- * 实际执行创建子Agent
82
- */
83
- _doCreateSubAgent(parentAgent) {
84
- // 避免重复创建
85
- if (this._agent) {
86
- return
87
- }
88
-
89
- // 获取从父Agent继承的工具
90
- const parentTools = this._getParentTools(parentAgent)
91
-
92
- // 确定LLM配置
93
- const aiPlugin = this._framework.pluginManager.get('ai')
94
- const llmConfig = this.llmConfig || (aiPlugin ? aiPlugin.getConfig() : {})
95
-
96
-
97
- // 创建子Agent,使用完整的 md 文件内容作为系统提示词
98
- this._agent = this._framework.createSubAgent({
99
- name: this.name,
100
- systemPrompt: this._getFullSystemPrompt(),
101
- model: llmConfig.model,
102
- provider: llmConfig.provider,
103
- apiKey: llmConfig.apiKey,
104
- baseURL: llmConfig.baseURL,
105
- tools: this.tools||{}, // 自定义工具
106
- parentTools: parentTools
107
- })
108
-
109
- // 注册从父Agent继承的工具
110
- // for (const tool of parentTools) {
111
- // this._framework.registerTool(tool)
112
- // }
113
-
114
- // Register custom tools (only for this subAgent, won't pollute global)
115
- // tools format: { toolName: toolDef }
116
-
117
- this._parentAgent = parentAgent
118
-
119
- // 注册子Agent到父Agent
120
- parentAgent.registerSubAgent(this.name, this._agent, this.role, this.description)
121
-
122
- // 监听子Agent的chunk事件并转发给父Agent
123
- this._agent.on('chunk', (chunk) => {
124
- chunk.fromSubAgent = this.name
125
- parentAgent.emit('chunk', chunk)
126
- })
127
- }
128
-
129
- /**
130
- * 获取父Agent
131
- */
132
- _getParentAgent() {
133
- // 优先使用外部传入的获取器
134
- if (this.config._parentAgentGetter) {
135
- return this.config._parentAgentGetter()
136
- }
137
-
138
- // 尝试从framework获取主agent
139
- if (this._framework._mainAgent) {
140
- return this._framework._mainAgent
141
- }
142
-
143
- // 查找最后一个创建的agent作为父agent
144
- const agents = this._framework._agents || []
145
- if (agents.length > 0) {
146
- return agents[agents.length - 1]
147
- }
148
-
149
- return null
150
- }
151
-
152
- /**
153
- * 获取从父Agent继承的工具
154
- */
155
- _getParentTools(parentAgent) {
156
- const allTools = parentAgent.getTools ? parentAgent.getTools() : []
157
- const toolMap = new Map(allTools.map(t => [t.name, t]))
158
-
159
- // 如果没有指定 parentTools,返回全部
160
- if (!this.parentTools || this.parentTools.length === 0) {
161
- return allTools
162
- }
163
- // 过滤指定工具
164
- const filtered = []
165
- for (const toolName of this.parentTools) {
166
- const tool = toolMap.get(toolName)
167
- if (tool) {
168
- filtered.push(tool)
169
- }
170
- // else {
171
- // console.warn(`[SubAgent:${this.config.name}] Parent tool not found: ${toolName}`)
172
- // }
173
- }
174
-
175
- return filtered
176
- }
177
-
178
- /**
179
- * 构建系统提示
180
- */
181
- _buildSystemPrompt() {
182
- return `你是 ${this.role}。
183
-
184
- 角色描述:${this.description || '一个专业的子Agent'}
185
-
186
- 当你被调用时,你应该:
187
- 1. 仔细理解任务要求
188
- 2. 使用你的工具集完成任务
189
- 3. 返回完整的操作结果
190
-
191
- 重要:返回结果要简洁明确,只包含最终结果,不需要解释过程。`
192
- }
193
-
194
- /**
195
- * 注册委托工具到框架和主Agent
196
- */
197
- _registerDelegateTool() {
198
- const framework = this._framework
199
- if (!framework) return
200
-
201
- const agentName = this.name
202
- const role = this.role
203
-
204
- const toolDef = {
205
- name: agentName,
206
- description: `${role}:当需要${role}时,调用此工具执行任务。如:编译代码、运行测试、代码重构等。`,
207
- inputSchema: z.object({
208
- task: z.string().describe('给子Agent的具体任务描述')
209
- }),
210
- execute: async (args, fw) => {
211
- if (!this._agent) {
212
- return { error: `SubAgent ${agentName} not initialized` }
213
- }
214
-
215
- const parentAgent = fw._mainAgent
216
- if (!parentAgent) {
217
- return { error: `Parent agent not found` }
218
- }
219
-
220
- // 发射子Agent开始处理事件
221
- parentAgent.emit('subagent:chat:start', {
222
- parentAgent,
223
- subAgent: this._agent,
224
- subAgentName: agentName,
225
- task: args.task
226
- })
227
-
228
- try {
229
- const result = await this._agent.chat(args.task)
230
- // 发射子Agent完成处理事件
231
- parentAgent.emit('subagent:chat:end', {
232
- parentAgent,
233
- subAgent: this._agent,
234
- subAgentName: agentName,
235
- result
236
- })
237
- return {
238
- agent: agentName,
239
- result: result.message || result.error || result,
240
- success: result.success !== false
241
- }
242
- } catch (err) {
243
- // 发射子Agent错误事件
244
- parentAgent.emit('subagent:error', {
245
- parentAgent,
246
- subAgent: this._agent,
247
- subAgentName: agentName,
248
- error: err.message
249
- })
250
- return {
251
- agent: agentName,
252
- error: err.message,
253
- success: false
254
- }
255
- }
256
- }
257
- }
258
-
259
- // 注册到框架(供后续创建的 Agent 同步)
260
- // framework.registerTool(toolDef)
261
-
262
- // // 如果主 Agent 已存在,直接注册到它
263
- // if (framework._mainAgent) {
264
- // framework._mainAgent.registerTool(toolDef)
265
- // }
266
-
267
- //console.log(`[SubAgent:${this.config.name}] Delegate tool registered`)
268
- }
269
-
270
- /**
271
- * 获取子Agent实例
272
- */
273
- getAgent() {
274
- return this._agent
275
- }
276
-
277
- /**
278
- * 调用子Agent处理任务
279
- * 延迟创建 Agent,使用完整的 md 文件内容作为系统提示词
280
- */
281
- async chat(task) {
282
- // 延迟创建 Agent(如果尚未创建)
283
- if (!this._agent) {
284
- await this._createSubAgentForChat();
285
- } else {
286
- // 每次调用时更新系统提示词,确保使用最新的 md 文件内容
287
- this._updateAgentSystemPrompt();
288
- }
289
- return this._agent.chat(task)
290
- }
291
-
292
- /**
293
- * 创建用于聊天的高完整度 Agent
294
- * 使用 SubAgentConfig 读取完整的 md 文件内容
295
- */
296
- async _createSubAgentForChat() {
297
- const parentAgent = this._getParentAgent();
298
- if (!parentAgent) {
299
- throw new Error(`SubAgent ${this.name}: parent agent not found`);
300
- }
301
-
302
- // 使用 SubAgentConfig 获取完整的系统提示词
303
- const systemPrompt = this._getFullSystemPrompt();
304
- const parentTools = this._getParentTools(parentAgent)
305
- // 获取 LLM 配置
306
- const aiPlugin = this._framework.pluginManager.get('ai');
307
- const llmConfig = this.llmConfig || (aiPlugin ? aiPlugin.getConfig() : {});
308
-
309
- // 创建 Agent,使用完整的 md 内容作为系统提示词
310
- this._agent = this._framework.createSubAgent({
311
- name: this.name,
312
- systemPrompt: systemPrompt,
313
- model: llmConfig.model,
314
- provider: llmConfig.provider,
315
- apiKey: llmConfig.apiKey,
316
- baseURL: llmConfig.baseURL,
317
- tools: this.tools||{}, // 自定义工具
318
- parentTools: parentTools
319
- });
320
-
321
- // 注册到父 Agent
322
- this._parentAgent = parentAgent;
323
- parentAgent.registerSubAgent(this.name, this._agent, this.role, this.description);
324
- }
325
-
326
- /**
327
- * 获取完整的系统提示词(从 md 文件读取)
328
- */
329
- _getFullSystemPrompt() {
330
- // 尝试从 SubAgentConfigManager 获取完整内容
331
- const configManager = this._framework._subAgentConfigManager;
332
- if (configManager) {
333
- const config = configManager.get(this.name);
334
- if (config) {
335
- return config.getSystemPrompt();
336
- }
337
- }
338
-
339
- // 回退到默认的 _buildSystemPrompt
340
- return this._buildSystemPrompt();
341
- }
342
-
343
- /**
344
- * 更新 Agent 的系统提示词
345
- */
346
- _updateAgentSystemPrompt() {
347
- if (!this._agent) return;
348
- const systemPrompt = this._getFullSystemPrompt();
349
- this._agent.setSystemPrompt(systemPrompt);
350
- }
351
-
352
- reload(framework) {
353
- this._framework = framework
354
- this._createSubAgent()
355
- }
356
-
357
- uninstall(framework) {
358
- if (this._parentAgent) {
359
- this._parentAgent.unregisterSubAgent(this.name)
360
- }
361
- if (this._agent) {
362
- this._agent.destroy()
363
- this._agent = null
364
- }
365
- this._parentAgent = null
366
- this._framework = null
367
- }
368
- }
369
-
370
- /**
371
- * SubAgentManager - 管理多个子Agent
372
- */
373
- class SubAgentManagerPlugin extends Plugin {
374
- constructor(config = {}) {
375
- super()
376
- this.name = 'subagent-manager'
377
- this.version = '1.0.0'
378
- this.description = '子Agent管理器,统一管理多个子Agent'
379
-
380
- this.systemPrompt= `你是一个子Agent管理器,负责管理多个子Agent。每个子Agent都有独立的工具集和角色描述。当需要处理特定任务时,你会选择合适的子Agent来执行。`
381
-
382
- this.priority = 10
383
- this.agents = config.agents || [] // 预定义的子Agent配置列表
384
- this.config = {
385
- agentsDir: config.agentsDir || null, // 子Agent配置目录
386
- }
387
-
388
- this._framework = null
389
- this._subAgents = new Map()
390
- this._fileWatcher = null
391
- this._lastFileStates = new Map() // 记录文件状态用于检测变化
392
- }
393
-
394
- install(framework) {
395
- this._framework = framework
396
- return this
397
- }
398
-
399
- start(framework) {
400
- // 从 agentsDir 加载子Agent配置
401
- this._loadAgentsFromDir()
402
-
403
- // 创建所有配置的子Agent
404
- for (const agentConfig of this.agents) {
405
- const plugin = new SubAgentPlugin(agentConfig)
406
- plugin.install(framework)
407
- plugin.start(framework)
408
- this._subAgents.set(agentConfig.name, plugin)
409
- }
410
-
411
- // 注册管理工具
412
- // 如果主 Agent 已存在,直接注册到它(因为它不会自动同步框架工具)
413
- if (framework._mainAgent) {
414
- this._registerToolsToAgent(framework._mainAgent)
415
- }
416
- // 同时注册到框架(供后续创建的 Agent 同步)
417
- this._registerTools(framework)
418
-
419
- // 监听主 Agent 创建事件,为后续创建的主 Agent 注册工具
420
- framework.on('agent:created', (agent) => {
421
- if (framework._mainAgent && agent === framework._mainAgent) {
422
- this._registerToolsToAgent(agent)
423
- }
424
- })
425
-
426
- // 启动文件监控
427
- this._startFileWatcher()
428
-
429
- return this
430
- }
431
-
432
- /**
433
- * 注册管理工具到指定 Agent
434
- */
435
- _registerToolsToAgent(agent) {
436
- agent.registerTool({
437
- name: 'subagent_list',
438
- description: '列出所有子Agent',
439
- inputSchema: z.object({}),
440
- execute: async () => {
441
- const agents = Array.from(this._subAgents.values()).map(p => ({
442
- name: p.name,
443
- role: p.role,
444
- description: p.description,
445
- toolCount: p._agent ? p._agent.getTools().length : 0
446
- }))
447
- return { success: true, agents }
448
- }
449
- })
450
-
451
- agent.registerTool({
452
- name: 'subagent_call',
453
- description: '调用指定的子Agent处理任务',
454
- inputSchema: z.object({
455
- agentName: z.string().describe('子Agent名称'),
456
- task: z.string().describe('任务描述')
457
- }),
458
- execute: async (args) => {
459
- const plugin = this._subAgents.get(args.agentName)
460
- if (!plugin) {
461
- return { success: false, error: `SubAgent ${args.agentName} not found` }
462
- }
463
-
464
- try {
465
- const result = await plugin.chat(args.task)
466
- return {
467
- success: true,
468
- agent: args.agentName,
469
- result: result.message || result,
470
- success: result.success !== false
471
- }
472
- } catch (err) {
473
- return { success: false, error: err.message }
474
- }
475
- }
476
- })
477
-
478
- agent.registerTool({
479
- name: 'subagent_reload',
480
- description: '重新加载子Agent配置(当新增或删除.agent/agents下的文件后使用)',
481
- inputSchema: z.object({}),
482
- execute: async () => {
483
- try {
484
- this.reload(this._framework)
485
- return { success: true, message: '子Agent配置已重新加载' }
486
- } catch (err) {
487
- return { success: false, error: err.message }
488
- }
489
- }
490
- })
491
-
492
- //log.info(' Management tools registered to agent')
493
- }
494
-
495
- /**
496
- * 注册管理工具到框架工具注册表
497
- */
498
- _registerTools(framework) {
499
- framework.registerTool({
500
- name: 'subagent_list',
501
- description: '列出所有子Agent',
502
- inputSchema: z.object({}),
503
- execute: async () => {
504
- const agents = Array.from(this._subAgents.values()).map(p => ({
505
- name: p.name,
506
- role: p.role,
507
- description: p.description,
508
- toolCount: p._agent ? p._agent.getTools().length : 0
509
- }))
510
- return { success: true, agents }
511
- }
512
- })
513
-
514
- framework.registerTool({
515
- name: 'subagent_call',
516
- description: '调用指定的子Agent处理任务',
517
- inputSchema: z.object({
518
- agentName: z.string().describe('子Agent名称'),
519
- task: z.string().describe('任务描述')
520
- }),
521
- execute: async (args) => {
522
- const plugin = this._subAgents.get(args.agentName)
523
- if (!plugin) {
524
- return { success: false, error: `SubAgent ${args.agentName} not found` }
525
- }
526
-
527
- try {
528
- const result = await plugin.chat(args.task)
529
- return {
530
- success: true,
531
- agent: args.agentName,
532
- result: result.message || result,
533
- success: result.success !== false
534
- }
535
- } catch (err) {
536
- return { success: false, error: err.message }
537
- }
538
- }
539
- })
540
-
541
- framework.registerTool({
542
- name: 'subagent_reload',
543
- description: '重新加载子Agent配置(当新增或删除.agent/agents下的文件后使用)',
544
- inputSchema: z.object({}),
545
- execute: async () => {
546
- try {
547
- this.reload(this._framework)
548
- return { success: true, message: '子Agent配置已重新加载' }
549
- } catch (err) {
550
- return { success: false, error: err.message }
551
- }
552
- }
553
- })
554
-
555
- //log.info(' Management tools registered to framework')
556
- }
557
-
558
- /**
559
- * 从 agentsDir 目录加载子Agent配置
560
- */
561
- _loadAgentsFromDir() {
562
- const agentsDir = this.config.agentsDir
563
- //log.info(' _loadAgentsFromDir called, agentsDir:', agentsDir)
564
- if (!agentsDir || !fs.existsSync(agentsDir)) {
565
- log.info(' agentsDir not found or does not exist')
566
- return
567
- }
568
-
569
- const entries = fs.readdirSync(agentsDir, { withFileTypes: true })
570
- //log.info(' Found entries:', entries.length)
571
-
572
- for (const entry of entries) {
573
- if (entry.isFile() && (entry.name.endsWith('.js') || entry.name.endsWith('.json') || entry.name.endsWith('.md'))) {
574
- const baseName = entry.name.replace(/\.(js|json|md)$/, '')
575
- const filePath = path.join(agentsDir, entry.name)
576
-
577
- // 跳过与已有配置同名的
578
- if (this.agents.some(a => a.name === baseName)) {
579
- continue
580
- }
581
-
582
- try {
583
- let agentConfig
584
- if (entry.name.endsWith('.json')) {
585
- const content = fs.readFileSync(filePath, 'utf-8')
586
- agentConfig = JSON.parse(content)
587
- } else if (entry.name.endsWith('.md')) {
588
- // 解析 markdown 文件,提取 JSON 配置
589
- agentConfig = this._parseMarkdownConfig(filePath, baseName)
590
- //log.info(' Parsed md:', baseName, agentConfig)
591
- } else {
592
- // 清除缓存并加载
593
- delete require.cache[require.resolve(filePath)]
594
- const mod = require(filePath)
595
- agentConfig = typeof mod === 'function' ? mod() : mod
596
- }
597
-
598
- if (agentConfig && agentConfig.name) {
599
- agentConfig._fromDir = true
600
- this.agents.push(agentConfig)
601
- //log.info(' Loaded agent:', agentConfig.name)
602
- } else {
603
- log.warn(' Agent config has no name:', baseName, agentConfig)
604
- }
605
- } catch (err) {
606
- log.warn(` Failed to load agent config from ${filePath}:`, err.message, err.stack)
607
- }
608
- }
609
- }
610
- }
611
-
612
- /**
613
- * 解析 markdown 文件中的配置
614
- * 支持格式:
615
- * ```json
616
- * { "name": "xxx", "role": "xxx" }
617
- * ```
618
- * 或直接用 frontmatter 格式
619
- */
620
- _parseMarkdownConfig(filePath, defaultName) {
621
- const content = fs.readFileSync(filePath, 'utf-8')
622
- //log.info(' _parseMarkdownConfig:', filePath)
623
-
624
- // 尝试从 code block 中提取 JSON
625
- const jsonMatch = content.match(/```(?:json)?\s*\n([\s\S]*?)\n\s*```/)
626
- if (jsonMatch) {
627
- try {
628
- const config = JSON.parse(jsonMatch[1].trim())
629
- //log.info(' Found JSON in code block')
630
- return config
631
- } catch (err) {
632
- // JSON 解析失败,继续
633
- }
634
- }
635
-
636
- // 尝试从 frontmatter 格式中提取
637
- const frontmatterMatch = content.match(/^---\n([\s\S]*?)\n---/)
638
- if (frontmatterMatch) {
639
- //log.info(' Found frontmatter')
640
- const frontmatterContent = frontmatterMatch[1]
641
- const parsed = this._parseYamlLike(frontmatterContent)
642
- if (parsed && parsed.name) {
643
- //log.info(' Parsed frontmatter:', parsed)
644
- return { name: parsed.name || defaultName, ...parsed }
645
- }
646
- }
647
-
648
- // 从 markdown 内容中提取配置(key: value 格式)
649
- const config = { name: defaultName }
650
- const yamlMatch = content.match(/^(name|role|description|parentTools|tools):\s*(.+)$/m)
651
- if (yamlMatch) {
652
- config[yamlMatch[1]] = yamlMatch[2].trim()
653
- }
654
-
655
- // 如果没有找到配置,使用文件名作为 name
656
- return config
657
- }
658
-
659
- /**
660
- * 获取所有 subAgent 的基本信息列表
661
- */
662
- getAllSubAgents() {
663
- return Array.from(this._subAgents.values());
664
- }
665
-
666
- /**
667
- * 解析类似 YAML 的配置
668
- */
669
- _parseYamlLike(content) {
670
- const result = {}
671
- const lines = content.split('\n')
672
-
673
- for (const line of lines) {
674
- const match = line.match(/^(\w+):\s*(.+)$/)
675
- if (match) {
676
- const key = match[1]
677
- let value = match[2].trim()
678
-
679
- // 处理数组格式 [a, b, c] 或 [a b c]
680
- if (value.startsWith('[') && value.endsWith(']')) {
681
- value = value.slice(1, -1).split(/[,\s]+/).filter(Boolean)
682
- }
683
-
684
- result[key] = value
685
- }
686
- }
687
-
688
- return result
689
- }
690
-
691
- /**
692
- * 启动文件监控
693
- */
694
- _startFileWatcher() {
695
- const agentsDir = this.config.agentsDir
696
- if (!agentsDir || !fs.existsSync(agentsDir)) {
697
- return
698
- }
699
-
700
- // 记录初始文件状态
701
- this._updateFileStates()
702
-
703
- // 使用 fs.watch 监控目录变化
704
- this._fileWatcher = fs.watch(agentsDir, { recursive: false }, (eventType, filename) => {
705
- if (filename && (filename.endsWith('.js') || filename.endsWith('.json') || filename.endsWith('.md'))) {
706
- //console.log(`[SubAgentManager] Detected change in agents dir: ${eventType} - ${filename}`)
707
-
708
- // 防抖:延迟处理,等文件操作完成
709
- setTimeout(() => {
710
- this._checkAndReload()
711
- }, 500)
712
- }
713
- })
714
-
715
- //console.log(`[SubAgentManager] Watching agents dir for changes: ${agentsDir}`)
716
- }
717
-
718
- /**
719
- * 更新文件状态
720
- */
721
- _updateFileStates() {
722
- const agentsDir = this.config.agentsDir
723
- if (!agentsDir || !fs.existsSync(agentsDir)) {
724
- return
725
- }
726
-
727
- this._lastFileStates.clear()
728
- const entries = fs.readdirSync(agentsDir, { withFileTypes: true })
729
-
730
- for (const entry of entries) {
731
- if (entry.isFile() && (entry.name.endsWith('.js') || entry.name.endsWith('.json') || entry.name.endsWith('.md'))) {
732
- const filePath = path.join(agentsDir, entry.name)
733
- try {
734
- const stat = fs.statSync(filePath)
735
- this._lastFileStates.set(entry.name, {
736
- mtime: stat.mtime.getTime(),
737
- size: stat.size
738
- })
739
- } catch (err) {
740
- // 忽略
741
- }
742
- }
743
- }
744
- }
745
-
746
- /**
747
- * 检查并重载(如果有变化)
748
- */
749
- _checkAndReload() {
750
- const agentsDir = this.config.agentsDir
751
- if (!agentsDir || !fs.existsSync(agentsDir)) {
752
- return
753
- }
754
-
755
- const currentStates = new Map()
756
- const entries = fs.readdirSync(agentsDir, { withFileTypes: true })
757
- let hasChanges = false
758
-
759
- // 检查当前文件
760
- for (const entry of entries) {
761
- if (entry.isFile() && (entry.name.endsWith('.js') || entry.name.endsWith('.json') || entry.name.endsWith('.md'))) {
762
- const filePath = path.join(agentsDir, entry.name)
763
- try {
764
- const stat = fs.statSync(filePath)
765
- currentStates.set(entry.name, {
766
- mtime: stat.mtime.getTime(),
767
- size: stat.size
768
- })
769
-
770
- const lastState = this._lastFileStates.get(entry.name)
771
- if (!lastState || lastState.mtime !== stat.mtime.getTime() || lastState.size !== stat.size) {
772
- hasChanges = true
773
- }
774
- } catch (err) {
775
- // 忽略
776
- }
777
- }
778
- }
779
-
780
- // 检查删除的文件
781
- for (const [name] of this._lastFileStates) {
782
- if (!currentStates.has(name)) {
783
- hasChanges = true
784
- break
785
- }
786
- }
787
-
788
- if (hasChanges) {
789
- //log.info(' File changes detected, reloading...')
790
- this._updateFileStates()
791
- this.reload(this._framework)
792
- }
793
- }
794
-
795
- /**
796
- * 获取子Agent
797
- */
798
- getSubAgent(name) {
799
- const plugin = this._subAgents.get(name)
800
- return plugin ? plugin.getAgent() : null
801
- }
802
-
803
- reload(framework) {
804
- // 停止文件监控
805
- if (this._fileWatcher) {
806
- this._fileWatcher.close()
807
- this._fileWatcher = null
808
- }
809
-
810
- // 销毁旧的子Agent
811
- for (const plugin of this._subAgents.values()) {
812
- plugin.uninstall(framework)
813
- }
814
- this._subAgents.clear()
815
-
816
- // 清空从目录加载的配置,重新加载
817
- this.agents = this.agents.filter(a => !a._fromDir)
818
-
819
- // 刷新 SubAgentConfigManager(如果存在)
820
- if (framework._subAgentConfigManager) {
821
- framework._subAgentConfigManager.refresh()
822
- }
823
-
824
- // 重新创建
825
- this.start(framework)
826
- }
827
-
828
- uninstall(framework) {
829
- // 停止文件监控
830
- if (this._fileWatcher) {
831
- this._fileWatcher.close()
832
- this._fileWatcher = null
833
- }
834
-
835
- for (const plugin of this._subAgents.values()) {
836
- plugin.uninstall(framework)
837
- }
838
- this._subAgents.clear()
839
- this._framework = null
840
- }
841
- }
842
-
843
- module.exports = {
844
- SubAgentPlugin,
845
- SubAgentManagerPlugin
1
+ /**
2
+ * SubAgent 子Agent插件
3
+ * 创建具有独立工具集的子Agent,用于分工处理不同领域的任务
4
+ */
5
+
6
+ const fs = require('fs')
7
+ const path = require('path')
8
+ const { Plugin } = require('../src/core/plugin-base')
9
+ const { logger } = require('../src/utils/logger')
10
+ const log = logger.child('SubAgent')
11
+ const { z } = require('zod')
12
+ const { Agent } = require('../src/core/agent')
13
+
14
+ class SubAgentPlugin extends Plugin {
15
+ constructor(config = {}) {
16
+ super()
17
+ this.name = config.name
18
+ this.version = '1.0.0'
19
+ this.description = config.description || `子Agent: ${config.name}`
20
+ this.priority = 10
21
+
22
+ this.system = true
23
+
24
+ this.role= config.role || config.name
25
+ this.tools = config.tools || {}
26
+ this.parentTools = config.parentTools || []
27
+ this.llmConfig = config.llmConfig || null
28
+
29
+ // 子Agent配置
30
+ // tools: { toolName: toolFn } 自定义工具(只属于此子Agent)
31
+ // parentTools: ['read_file'] 从父Agent继承的工具名称列表
32
+ this.config = {}
33
+
34
+ this._framework = null
35
+ this._agent = null
36
+ this._parentAgent = null
37
+ }
38
+
39
+ install(framework) {
40
+ this._framework = framework
41
+ return this
42
+ }
43
+
44
+ start(framework) {
45
+ // 创建子Agent(可能需要等待父agent准备好)
46
+ this._createSubAgent()
47
+
48
+ // 注册委托工具到主Agent(通过framework获取父agent)
49
+ this._registerDelegateTool()
50
+
51
+ return this
52
+ }
53
+
54
+ /**
55
+ * 创建子Agent
56
+ */
57
+ _createSubAgent() {
58
+ // 获取父Agent(主Agent)
59
+ const parentAgent = this._getParentAgent()
60
+ if (!parentAgent) {
61
+ // 父agent还没创建,监听事件等其创建
62
+ if (this._framework) {
63
+ this._framework.on('agent:created', (agent) => {
64
+ if (this._framework._mainAgent && agent === this._framework._mainAgent) {
65
+ // 父agent已创建,更新父agent引用
66
+ this._parentAgent = agent
67
+ // 重新尝试创建子agent
68
+ this._doCreateSubAgent(agent)
69
+ // 注册委托工具
70
+ this._registerDelegateTool()
71
+ }
72
+ })
73
+ }
74
+ return
75
+ }
76
+
77
+ this._doCreateSubAgent(parentAgent)
78
+ }
79
+
80
+ /**
81
+ * 实际执行创建子Agent
82
+ */
83
+ _doCreateSubAgent(parentAgent) {
84
+ // 避免重复创建
85
+ if (this._agent) {
86
+ return
87
+ }
88
+
89
+ // 获取从父Agent继承的工具
90
+ const parentTools = this._getParentTools(parentAgent)
91
+
92
+ // 确定LLM配置
93
+ const aiPlugin = this._framework.pluginManager.get('ai')
94
+ const llmConfig = this.llmConfig || (aiPlugin ? aiPlugin.getConfig() : {})
95
+
96
+
97
+ // 创建子Agent,使用完整的 md 文件内容作为系统提示词
98
+ this._agent = this._framework.createSubAgent({
99
+ name: this.name,
100
+ systemPrompt: this._getFullSystemPrompt(),
101
+ model: llmConfig.model,
102
+ provider: llmConfig.provider,
103
+ apiKey: llmConfig.apiKey,
104
+ baseURL: llmConfig.baseURL,
105
+ tools: this.tools||{}, // 自定义工具
106
+ parentTools: parentTools
107
+ })
108
+
109
+ // 注册从父Agent继承的工具
110
+ // for (const tool of parentTools) {
111
+ // this._framework.registerTool(tool)
112
+ // }
113
+
114
+ // Register custom tools (only for this subAgent, won't pollute global)
115
+ // tools format: { toolName: toolDef }
116
+
117
+ this._parentAgent = parentAgent
118
+
119
+ // 注册子Agent到父Agent
120
+ parentAgent.registerSubAgent(this.name, this._agent, this.role, this.description)
121
+
122
+ // 监听子Agent的chunk事件并转发给父Agent
123
+ this._agent.on('chunk', (chunk) => {
124
+ chunk.fromSubAgent = this.name
125
+ parentAgent.emit('chunk', chunk)
126
+ })
127
+ }
128
+
129
+ /**
130
+ * 获取父Agent
131
+ */
132
+ _getParentAgent() {
133
+ // 优先使用外部传入的获取器
134
+ if (this.config._parentAgentGetter) {
135
+ return this.config._parentAgentGetter()
136
+ }
137
+
138
+ // 尝试从framework获取主agent
139
+ if (this._framework._mainAgent) {
140
+ return this._framework._mainAgent
141
+ }
142
+
143
+ // 查找最后一个创建的agent作为父agent
144
+ const agents = this._framework._agents || []
145
+ if (agents.length > 0) {
146
+ return agents[agents.length - 1]
147
+ }
148
+
149
+ return null
150
+ }
151
+
152
+ /**
153
+ * 获取从父Agent继承的工具
154
+ */
155
+ _getParentTools(parentAgent) {
156
+ const allTools = parentAgent.getTools ? parentAgent.getTools() : []
157
+ const toolMap = new Map(allTools.map(t => [t.name, t]))
158
+
159
+ // 如果没有指定 parentTools,返回全部
160
+ if (!this.parentTools || this.parentTools.length === 0) {
161
+ return allTools
162
+ }
163
+ // 过滤指定工具
164
+ const filtered = []
165
+ for (const toolName of this.parentTools) {
166
+ const tool = toolMap.get(toolName)
167
+ if (tool) {
168
+ filtered.push(tool)
169
+ }
170
+ // else {
171
+ // console.warn(`[SubAgent:${this.config.name}] Parent tool not found: ${toolName}`)
172
+ // }
173
+ }
174
+
175
+ return filtered
176
+ }
177
+
178
+ /**
179
+ * 构建系统提示
180
+ */
181
+ _buildSystemPrompt() {
182
+ return `你是 ${this.role}。
183
+
184
+ 角色描述:${this.description || '一个专业的子Agent'}
185
+
186
+ 当你被调用时,你应该:
187
+ 1. 仔细理解任务要求
188
+ 2. 使用你的工具集完成任务
189
+ 3. 返回完整的操作结果
190
+
191
+ 重要:返回结果要简洁明确,只包含最终结果,不需要解释过程。`
192
+ }
193
+
194
+ /**
195
+ * 注册委托工具到框架和主Agent
196
+ */
197
+ _registerDelegateTool() {
198
+ const framework = this._framework
199
+ if (!framework) return
200
+
201
+ const agentName = this.name
202
+ const role = this.role
203
+
204
+ const toolDef = {
205
+ name: agentName,
206
+ description: `${role}:当需要${role}时,调用此工具执行任务。如:编译代码、运行测试、代码重构等。`,
207
+ inputSchema: z.object({
208
+ task: z.string().describe('给子Agent的具体任务描述')
209
+ }),
210
+ execute: async (args, fw) => {
211
+ if (!this._agent) {
212
+ return { error: `SubAgent ${agentName} not initialized` }
213
+ }
214
+
215
+ const parentAgent = fw._mainAgent
216
+ if (!parentAgent) {
217
+ return { error: `Parent agent not found` }
218
+ }
219
+
220
+ // 发射子Agent开始处理事件
221
+ parentAgent.emit('subagent:chat:start', {
222
+ parentAgent,
223
+ subAgent: this._agent,
224
+ subAgentName: agentName,
225
+ task: args.task
226
+ })
227
+
228
+ try {
229
+ const result = await this._agent.chat(args.task)
230
+ // 发射子Agent完成处理事件
231
+ parentAgent.emit('subagent:chat:end', {
232
+ parentAgent,
233
+ subAgent: this._agent,
234
+ subAgentName: agentName,
235
+ result
236
+ })
237
+ return {
238
+ agent: agentName,
239
+ result: result.message || result.error || result,
240
+ success: result.success !== false
241
+ }
242
+ } catch (err) {
243
+ // 发射子Agent错误事件
244
+ parentAgent.emit('subagent:error', {
245
+ parentAgent,
246
+ subAgent: this._agent,
247
+ subAgentName: agentName,
248
+ error: err.message
249
+ })
250
+ return {
251
+ agent: agentName,
252
+ error: err.message,
253
+ success: false
254
+ }
255
+ }
256
+ }
257
+ }
258
+
259
+ // 注册到框架(供后续创建的 Agent 同步)
260
+ // framework.registerTool(toolDef)
261
+
262
+ // // 如果主 Agent 已存在,直接注册到它
263
+ // if (framework._mainAgent) {
264
+ // framework._mainAgent.registerTool(toolDef)
265
+ // }
266
+
267
+ //console.log(`[SubAgent:${this.config.name}] Delegate tool registered`)
268
+ }
269
+
270
+ /**
271
+ * 获取子Agent实例
272
+ */
273
+ getAgent() {
274
+ return this._agent
275
+ }
276
+
277
+ /**
278
+ * 调用子Agent处理任务
279
+ * 延迟创建 Agent,使用完整的 md 文件内容作为系统提示词
280
+ */
281
+ async chat(task) {
282
+ // 延迟创建 Agent(如果尚未创建)
283
+ if (!this._agent) {
284
+ await this._createSubAgentForChat();
285
+ } else {
286
+ // 每次调用时更新系统提示词,确保使用最新的 md 文件内容
287
+ this._updateAgentSystemPrompt();
288
+ }
289
+ return this._agent.chat(task)
290
+ }
291
+
292
+ /**
293
+ * 创建用于聊天的高完整度 Agent
294
+ * 使用 SubAgentConfig 读取完整的 md 文件内容
295
+ */
296
+ async _createSubAgentForChat() {
297
+ const parentAgent = this._getParentAgent();
298
+ if (!parentAgent) {
299
+ throw new Error(`SubAgent ${this.name}: parent agent not found`);
300
+ }
301
+
302
+ // 使用 SubAgentConfig 获取完整的系统提示词
303
+ const systemPrompt = this._getFullSystemPrompt();
304
+ const parentTools = this._getParentTools(parentAgent)
305
+ // 获取 LLM 配置
306
+ const aiPlugin = this._framework.pluginManager.get('ai');
307
+ const llmConfig = this.llmConfig || (aiPlugin ? aiPlugin.getConfig() : {});
308
+
309
+ // 创建 Agent,使用完整的 md 内容作为系统提示词
310
+ this._agent = this._framework.createSubAgent({
311
+ name: this.name,
312
+ systemPrompt: systemPrompt,
313
+ model: llmConfig.model,
314
+ provider: llmConfig.provider,
315
+ apiKey: llmConfig.apiKey,
316
+ baseURL: llmConfig.baseURL,
317
+ tools: this.tools||{}, // 自定义工具
318
+ parentTools: parentTools
319
+ });
320
+
321
+ // 注册到父 Agent
322
+ this._parentAgent = parentAgent;
323
+ parentAgent.registerSubAgent(this.name, this._agent, this.role, this.description);
324
+ }
325
+
326
+ /**
327
+ * 获取完整的系统提示词(从 md 文件读取)
328
+ */
329
+ _getFullSystemPrompt() {
330
+ // 尝试从 SubAgentConfigManager 获取完整内容
331
+ const configManager = this._framework._subAgentConfigManager;
332
+ if (configManager) {
333
+ const config = configManager.get(this.name);
334
+ if (config) {
335
+ return config.getSystemPrompt();
336
+ }
337
+ }
338
+
339
+ // 回退到默认的 _buildSystemPrompt
340
+ return this._buildSystemPrompt();
341
+ }
342
+
343
+ /**
344
+ * 更新 Agent 的系统提示词
345
+ */
346
+ _updateAgentSystemPrompt() {
347
+ if (!this._agent) return;
348
+ const systemPrompt = this._getFullSystemPrompt();
349
+ this._agent.setSystemPrompt(systemPrompt);
350
+ }
351
+
352
+ reload(framework) {
353
+ this._framework = framework
354
+ this._createSubAgent()
355
+ }
356
+
357
+ uninstall(framework) {
358
+ if (this._parentAgent) {
359
+ this._parentAgent.unregisterSubAgent(this.name)
360
+ }
361
+ if (this._agent) {
362
+ this._agent.destroy()
363
+ this._agent = null
364
+ }
365
+ this._parentAgent = null
366
+ this._framework = null
367
+ }
368
+ }
369
+
370
+ /**
371
+ * SubAgentManager - 管理多个子Agent
372
+ */
373
+ class SubAgentManagerPlugin extends Plugin {
374
+ constructor(config = {}) {
375
+ super()
376
+ this.name = 'subagent-manager'
377
+ this.version = '1.0.0'
378
+ this.description = '子Agent管理器,统一管理多个子Agent'
379
+
380
+ this.systemPrompt= `你是一个子Agent管理器,负责管理多个子Agent。每个子Agent都有独立的工具集和角色描述。当需要处理特定任务时,你会选择合适的子Agent来执行。`
381
+
382
+ this.priority = 10
383
+ this.agents = config.agents || [] // 预定义的子Agent配置列表
384
+ this.config = {
385
+ agentsDir: config.agentsDir || null, // 子Agent配置目录
386
+ }
387
+
388
+ this._framework = null
389
+ this._subAgents = new Map()
390
+ this._fileWatcher = null
391
+ this._lastFileStates = new Map() // 记录文件状态用于检测变化
392
+ }
393
+
394
+ install(framework) {
395
+ this._framework = framework
396
+ return this
397
+ }
398
+
399
+ start(framework) {
400
+ // 从 agentsDir 加载子Agent配置
401
+ this._loadAgentsFromDir()
402
+
403
+ // 创建所有配置的子Agent
404
+ for (const agentConfig of this.agents) {
405
+ const plugin = new SubAgentPlugin(agentConfig)
406
+ plugin.install(framework)
407
+ plugin.start(framework)
408
+ this._subAgents.set(agentConfig.name, plugin)
409
+ }
410
+
411
+ // 注册管理工具
412
+ // 如果主 Agent 已存在,直接注册到它(因为它不会自动同步框架工具)
413
+ if (framework._mainAgent) {
414
+ this._registerToolsToAgent(framework._mainAgent)
415
+ }
416
+ // 同时注册到框架(供后续创建的 Agent 同步)
417
+ this._registerTools(framework)
418
+
419
+ // 监听主 Agent 创建事件,为后续创建的主 Agent 注册工具
420
+ framework.on('agent:created', (agent) => {
421
+ if (framework._mainAgent && agent === framework._mainAgent) {
422
+ this._registerToolsToAgent(agent)
423
+ }
424
+ })
425
+
426
+ // 启动文件监控
427
+ this._startFileWatcher()
428
+
429
+ return this
430
+ }
431
+
432
+ /**
433
+ * 注册管理工具到指定 Agent
434
+ */
435
+ _registerToolsToAgent(agent) {
436
+ agent.registerTool({
437
+ name: 'subagent_list',
438
+ description: '列出所有子Agent',
439
+ inputSchema: z.object({}),
440
+ execute: async () => {
441
+ const agents = Array.from(this._subAgents.values()).map(p => ({
442
+ name: p.name,
443
+ role: p.role,
444
+ description: p.description,
445
+ toolCount: p._agent ? p._agent.getTools().length : 0
446
+ }))
447
+ return { success: true, agents }
448
+ }
449
+ })
450
+
451
+ agent.registerTool({
452
+ name: 'subagent_call',
453
+ description: '调用指定的子Agent处理任务',
454
+ inputSchema: z.object({
455
+ agentName: z.string().describe('子Agent名称'),
456
+ task: z.string().describe('任务描述')
457
+ }),
458
+ execute: async (args) => {
459
+ const plugin = this._subAgents.get(args.agentName)
460
+ if (!plugin) {
461
+ return { success: false, error: `SubAgent ${args.agentName} not found` }
462
+ }
463
+
464
+ try {
465
+ const result = await plugin.chat(args.task)
466
+ return {
467
+ success: true,
468
+ agent: args.agentName,
469
+ result: result.message || result,
470
+ success: result.success !== false
471
+ }
472
+ } catch (err) {
473
+ return { success: false, error: err.message }
474
+ }
475
+ }
476
+ })
477
+
478
+ agent.registerTool({
479
+ name: 'subagent_reload',
480
+ description: '重新加载子Agent配置(当新增或删除.agent/agents下的文件后使用)',
481
+ inputSchema: z.object({}),
482
+ execute: async () => {
483
+ try {
484
+ this.reload(this._framework)
485
+ return { success: true, message: '子Agent配置已重新加载' }
486
+ } catch (err) {
487
+ return { success: false, error: err.message }
488
+ }
489
+ }
490
+ })
491
+
492
+ //log.info(' Management tools registered to agent')
493
+ }
494
+
495
+ /**
496
+ * 注册管理工具到框架工具注册表
497
+ */
498
+ _registerTools(framework) {
499
+ framework.registerTool({
500
+ name: 'subagent_list',
501
+ description: '列出所有子Agent',
502
+ inputSchema: z.object({}),
503
+ execute: async () => {
504
+ const agents = Array.from(this._subAgents.values()).map(p => ({
505
+ name: p.name,
506
+ role: p.role,
507
+ description: p.description,
508
+ toolCount: p._agent ? p._agent.getTools().length : 0
509
+ }))
510
+ return { success: true, agents }
511
+ }
512
+ })
513
+
514
+ framework.registerTool({
515
+ name: 'subagent_call',
516
+ description: '调用指定的子Agent处理任务',
517
+ inputSchema: z.object({
518
+ agentName: z.string().describe('子Agent名称'),
519
+ task: z.string().describe('任务描述')
520
+ }),
521
+ execute: async (args) => {
522
+ const plugin = this._subAgents.get(args.agentName)
523
+ if (!plugin) {
524
+ return { success: false, error: `SubAgent ${args.agentName} not found` }
525
+ }
526
+
527
+ try {
528
+ const result = await plugin.chat(args.task)
529
+ return {
530
+ success: true,
531
+ agent: args.agentName,
532
+ result: result.message || result,
533
+ success: result.success !== false
534
+ }
535
+ } catch (err) {
536
+ return { success: false, error: err.message }
537
+ }
538
+ }
539
+ })
540
+
541
+ framework.registerTool({
542
+ name: 'subagent_reload',
543
+ description: '重新加载子Agent配置(当新增或删除.agent/agents下的文件后使用)',
544
+ inputSchema: z.object({}),
545
+ execute: async () => {
546
+ try {
547
+ this.reload(this._framework)
548
+ return { success: true, message: '子Agent配置已重新加载' }
549
+ } catch (err) {
550
+ return { success: false, error: err.message }
551
+ }
552
+ }
553
+ })
554
+
555
+ //log.info(' Management tools registered to framework')
556
+ }
557
+
558
+ /**
559
+ * 从 agentsDir 目录加载子Agent配置
560
+ */
561
+ _loadAgentsFromDir() {
562
+ const agentsDir = this.config.agentsDir
563
+ //log.info(' _loadAgentsFromDir called, agentsDir:', agentsDir)
564
+ if (!agentsDir || !fs.existsSync(agentsDir)) {
565
+ log.info(' agentsDir not found or does not exist')
566
+ return
567
+ }
568
+
569
+ const entries = fs.readdirSync(agentsDir, { withFileTypes: true })
570
+ //log.info(' Found entries:', entries.length)
571
+
572
+ for (const entry of entries) {
573
+ if (entry.isFile() && (entry.name.endsWith('.js') || entry.name.endsWith('.json') || entry.name.endsWith('.md'))) {
574
+ const baseName = entry.name.replace(/\.(js|json|md)$/, '')
575
+ const filePath = path.join(agentsDir, entry.name)
576
+
577
+ // 跳过与已有配置同名的
578
+ if (this.agents.some(a => a.name === baseName)) {
579
+ continue
580
+ }
581
+
582
+ try {
583
+ let agentConfig
584
+ if (entry.name.endsWith('.json')) {
585
+ const content = fs.readFileSync(filePath, 'utf-8')
586
+ agentConfig = JSON.parse(content)
587
+ } else if (entry.name.endsWith('.md')) {
588
+ // 解析 markdown 文件,提取 JSON 配置
589
+ agentConfig = this._parseMarkdownConfig(filePath, baseName)
590
+ //log.info(' Parsed md:', baseName, agentConfig)
591
+ } else {
592
+ // 清除缓存并加载
593
+ delete require.cache[require.resolve(filePath)]
594
+ const mod = require(filePath)
595
+ agentConfig = typeof mod === 'function' ? mod() : mod
596
+ }
597
+
598
+ if (agentConfig && agentConfig.name) {
599
+ agentConfig._fromDir = true
600
+ this.agents.push(agentConfig)
601
+ //log.info(' Loaded agent:', agentConfig.name)
602
+ } else {
603
+ log.warn(' Agent config has no name:', baseName, agentConfig)
604
+ }
605
+ } catch (err) {
606
+ log.warn(` Failed to load agent config from ${filePath}:`, err.message, err.stack)
607
+ }
608
+ }
609
+ }
610
+ }
611
+
612
+ /**
613
+ * 解析 markdown 文件中的配置
614
+ * 支持格式:
615
+ * ```json
616
+ * { "name": "xxx", "role": "xxx" }
617
+ * ```
618
+ * 或直接用 frontmatter 格式
619
+ */
620
+ _parseMarkdownConfig(filePath, defaultName) {
621
+ const content = fs.readFileSync(filePath, 'utf-8')
622
+ //log.info(' _parseMarkdownConfig:', filePath)
623
+
624
+ // 尝试从 code block 中提取 JSON
625
+ const jsonMatch = content.match(/```(?:json)?\s*\n([\s\S]*?)\n\s*```/)
626
+ if (jsonMatch) {
627
+ try {
628
+ const config = JSON.parse(jsonMatch[1].trim())
629
+ //log.info(' Found JSON in code block')
630
+ return config
631
+ } catch (err) {
632
+ // JSON 解析失败,继续
633
+ }
634
+ }
635
+
636
+ // 尝试从 frontmatter 格式中提取
637
+ const frontmatterMatch = content.match(/^---\n([\s\S]*?)\n---/)
638
+ if (frontmatterMatch) {
639
+ //log.info(' Found frontmatter')
640
+ const frontmatterContent = frontmatterMatch[1]
641
+ const parsed = this._parseYamlLike(frontmatterContent)
642
+ if (parsed && parsed.name) {
643
+ //log.info(' Parsed frontmatter:', parsed)
644
+ return { name: parsed.name || defaultName, ...parsed }
645
+ }
646
+ }
647
+
648
+ // 从 markdown 内容中提取配置(key: value 格式)
649
+ const config = { name: defaultName }
650
+ const yamlMatch = content.match(/^(name|role|description|parentTools|tools):\s*(.+)$/m)
651
+ if (yamlMatch) {
652
+ config[yamlMatch[1]] = yamlMatch[2].trim()
653
+ }
654
+
655
+ // 如果没有找到配置,使用文件名作为 name
656
+ return config
657
+ }
658
+
659
+ /**
660
+ * 获取所有 subAgent 的基本信息列表
661
+ */
662
+ getAllSubAgents() {
663
+ return Array.from(this._subAgents.values());
664
+ }
665
+
666
+ /**
667
+ * 解析类似 YAML 的配置
668
+ */
669
+ _parseYamlLike(content) {
670
+ const result = {}
671
+ const lines = content.split('\n')
672
+
673
+ for (const line of lines) {
674
+ const match = line.match(/^(\w+):\s*(.+)$/)
675
+ if (match) {
676
+ const key = match[1]
677
+ let value = match[2].trim()
678
+
679
+ // 处理数组格式 [a, b, c] 或 [a b c]
680
+ if (value.startsWith('[') && value.endsWith(']')) {
681
+ value = value.slice(1, -1).split(/[,\s]+/).filter(Boolean)
682
+ }
683
+
684
+ result[key] = value
685
+ }
686
+ }
687
+
688
+ return result
689
+ }
690
+
691
+ /**
692
+ * 启动文件监控
693
+ */
694
+ _startFileWatcher() {
695
+ const agentsDir = this.config.agentsDir
696
+ if (!agentsDir || !fs.existsSync(agentsDir)) {
697
+ return
698
+ }
699
+
700
+ // 记录初始文件状态
701
+ this._updateFileStates()
702
+
703
+ // 使用 fs.watch 监控目录变化
704
+ this._fileWatcher = fs.watch(agentsDir, { recursive: false }, (eventType, filename) => {
705
+ if (filename && (filename.endsWith('.js') || filename.endsWith('.json') || filename.endsWith('.md'))) {
706
+ //console.log(`[SubAgentManager] Detected change in agents dir: ${eventType} - ${filename}`)
707
+
708
+ // 防抖:延迟处理,等文件操作完成
709
+ setTimeout(() => {
710
+ this._checkAndReload()
711
+ }, 500)
712
+ }
713
+ })
714
+
715
+ //console.log(`[SubAgentManager] Watching agents dir for changes: ${agentsDir}`)
716
+ }
717
+
718
+ /**
719
+ * 更新文件状态
720
+ */
721
+ _updateFileStates() {
722
+ const agentsDir = this.config.agentsDir
723
+ if (!agentsDir || !fs.existsSync(agentsDir)) {
724
+ return
725
+ }
726
+
727
+ this._lastFileStates.clear()
728
+ const entries = fs.readdirSync(agentsDir, { withFileTypes: true })
729
+
730
+ for (const entry of entries) {
731
+ if (entry.isFile() && (entry.name.endsWith('.js') || entry.name.endsWith('.json') || entry.name.endsWith('.md'))) {
732
+ const filePath = path.join(agentsDir, entry.name)
733
+ try {
734
+ const stat = fs.statSync(filePath)
735
+ this._lastFileStates.set(entry.name, {
736
+ mtime: stat.mtime.getTime(),
737
+ size: stat.size
738
+ })
739
+ } catch (err) {
740
+ // 忽略
741
+ }
742
+ }
743
+ }
744
+ }
745
+
746
+ /**
747
+ * 检查并重载(如果有变化)
748
+ */
749
+ _checkAndReload() {
750
+ const agentsDir = this.config.agentsDir
751
+ if (!agentsDir || !fs.existsSync(agentsDir)) {
752
+ return
753
+ }
754
+
755
+ const currentStates = new Map()
756
+ const entries = fs.readdirSync(agentsDir, { withFileTypes: true })
757
+ let hasChanges = false
758
+
759
+ // 检查当前文件
760
+ for (const entry of entries) {
761
+ if (entry.isFile() && (entry.name.endsWith('.js') || entry.name.endsWith('.json') || entry.name.endsWith('.md'))) {
762
+ const filePath = path.join(agentsDir, entry.name)
763
+ try {
764
+ const stat = fs.statSync(filePath)
765
+ currentStates.set(entry.name, {
766
+ mtime: stat.mtime.getTime(),
767
+ size: stat.size
768
+ })
769
+
770
+ const lastState = this._lastFileStates.get(entry.name)
771
+ if (!lastState || lastState.mtime !== stat.mtime.getTime() || lastState.size !== stat.size) {
772
+ hasChanges = true
773
+ }
774
+ } catch (err) {
775
+ // 忽略
776
+ }
777
+ }
778
+ }
779
+
780
+ // 检查删除的文件
781
+ for (const [name] of this._lastFileStates) {
782
+ if (!currentStates.has(name)) {
783
+ hasChanges = true
784
+ break
785
+ }
786
+ }
787
+
788
+ if (hasChanges) {
789
+ //log.info(' File changes detected, reloading...')
790
+ this._updateFileStates()
791
+ this.reload(this._framework)
792
+ }
793
+ }
794
+
795
+ /**
796
+ * 获取子Agent
797
+ */
798
+ getSubAgent(name) {
799
+ const plugin = this._subAgents.get(name)
800
+ return plugin ? plugin.getAgent() : null
801
+ }
802
+
803
+ reload(framework) {
804
+ // 停止文件监控
805
+ if (this._fileWatcher) {
806
+ this._fileWatcher.close()
807
+ this._fileWatcher = null
808
+ }
809
+
810
+ // 销毁旧的子Agent
811
+ for (const plugin of this._subAgents.values()) {
812
+ plugin.uninstall(framework)
813
+ }
814
+ this._subAgents.clear()
815
+
816
+ // 清空从目录加载的配置,重新加载
817
+ this.agents = this.agents.filter(a => !a._fromDir)
818
+
819
+ // 刷新 SubAgentConfigManager(如果存在)
820
+ if (framework._subAgentConfigManager) {
821
+ framework._subAgentConfigManager.refresh()
822
+ }
823
+
824
+ // 重新创建
825
+ this.start(framework)
826
+ }
827
+
828
+ uninstall(framework) {
829
+ // 停止文件监控
830
+ if (this._fileWatcher) {
831
+ this._fileWatcher.close()
832
+ this._fileWatcher = null
833
+ }
834
+
835
+ for (const plugin of this._subAgents.values()) {
836
+ plugin.uninstall(framework)
837
+ }
838
+ this._subAgents.clear()
839
+ this._framework = null
840
+ }
841
+ }
842
+
843
+ module.exports = {
844
+ SubAgentPlugin,
845
+ SubAgentManagerPlugin
846
846
  }