foliko 1.0.0

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 (54) hide show
  1. package/.claude/settings.local.json +30 -0
  2. package/22.txt +10 -0
  3. package/README.md +218 -0
  4. package/SPEC.md +452 -0
  5. package/cli/bin/foliko.js +12 -0
  6. package/cli/src/commands/chat.js +75 -0
  7. package/cli/src/index.js +64 -0
  8. package/cli/src/ui/chat-ui.js +272 -0
  9. package/cli/src/utils/ansi.js +40 -0
  10. package/cli/src/utils/markdown.js +296 -0
  11. package/docs/quick-reference.md +131 -0
  12. package/docs/user-manual.md +1205 -0
  13. package/examples/basic.js +110 -0
  14. package/examples/bootstrap.js +93 -0
  15. package/examples/mcp-example.js +53 -0
  16. package/examples/skill-example.js +49 -0
  17. package/examples/workflow.js +158 -0
  18. package/package.json +36 -0
  19. package/plugins/ai-plugin.js +89 -0
  20. package/plugins/audit-plugin.js +187 -0
  21. package/plugins/default-plugins.js +412 -0
  22. package/plugins/file-system-plugin.js +344 -0
  23. package/plugins/install-plugin.js +93 -0
  24. package/plugins/python-executor-plugin.js +331 -0
  25. package/plugins/rules-plugin.js +292 -0
  26. package/plugins/scheduler-plugin.js +426 -0
  27. package/plugins/session-plugin.js +343 -0
  28. package/plugins/shell-executor-plugin.js +196 -0
  29. package/plugins/storage-plugin.js +237 -0
  30. package/plugins/subagent-plugin.js +395 -0
  31. package/plugins/think-plugin.js +329 -0
  32. package/plugins/tools-plugin.js +114 -0
  33. package/skills/mcp-usage/SKILL.md +198 -0
  34. package/skills/vb-agent-dev/AGENTS.md +162 -0
  35. package/skills/vb-agent-dev/SKILL.md +370 -0
  36. package/src/capabilities/index.js +11 -0
  37. package/src/capabilities/skill-manager.js +319 -0
  38. package/src/capabilities/workflow-engine.js +401 -0
  39. package/src/core/agent-chat.js +311 -0
  40. package/src/core/agent.js +573 -0
  41. package/src/core/framework.js +255 -0
  42. package/src/core/index.js +19 -0
  43. package/src/core/plugin-base.js +205 -0
  44. package/src/core/plugin-manager.js +392 -0
  45. package/src/core/provider.js +108 -0
  46. package/src/core/tool-registry.js +134 -0
  47. package/src/core/tool-router.js +216 -0
  48. package/src/executors/executor-base.js +58 -0
  49. package/src/executors/mcp-executor.js +728 -0
  50. package/src/index.js +37 -0
  51. package/src/utils/event-emitter.js +97 -0
  52. package/test-chat.js +129 -0
  53. package/test-mcp.js +79 -0
  54. package/test-reload.js +61 -0
@@ -0,0 +1,573 @@
1
+ /**
2
+ * Agent 类
3
+ * 负责对话和推理
4
+ */
5
+
6
+ const { EventEmitter } = require('../utils/event-emitter')
7
+ const { AgentChatHandler } = require('./agent-chat')
8
+ const os = require('os')
9
+
10
+ class Agent extends EventEmitter {
11
+ /**
12
+ * @param {Framework} framework - 框架实例
13
+ * @param {Object} config - 配置
14
+ * @param {string} [config.name] - Agent 名称
15
+ * @param {string} [config.systemPrompt] - 系统提示
16
+ * @param {string} [config.sharedPrompt] - 共享提示模板,支持 {{VAR}} 占位符
17
+ * @param {Object} [config.metadata] - 元数据,注入到 sharedPrompt
18
+ * @param {boolean} [config.enableToolRouting=true] - 是否启用工具路由
19
+ */
20
+ constructor(framework, config = {}) {
21
+ super()
22
+
23
+ this.framework = framework
24
+ this.config = config
25
+
26
+ this.name = config.name || 'Agent'
27
+ this.model = config.model || 'deepseek-chat'
28
+ this.apiKey = config.apiKey
29
+ this.baseURL = config.baseURL
30
+ this.provider = config.provider || 'deepseek'
31
+ this.providerOptions = config.providerOptions || {}
32
+
33
+ // 原始 system prompt
34
+ this._originalPrompt = config.systemPrompt || 'You are a helpful assistant.'
35
+
36
+ // 共享提示模板
37
+ this._sharedPrompt = config.sharedPrompt || ''
38
+
39
+ // 元数据
40
+ const metadata = config.metadata || {}
41
+ this._metadata = new Map(Object.entries(metadata))
42
+
43
+ this._chatHandler = null
44
+ this._tools = new Map()
45
+ this._status = 'idle'
46
+
47
+ // 子Agent管理
48
+ this._subAgents = new Map()
49
+
50
+ // 处理后的 system prompt (带上下文)
51
+ this.systemPrompt = this._buildSystemPrompt()
52
+
53
+ // 初始化聊天处理器
54
+ this._initChatHandler()
55
+ }
56
+
57
+ /**
58
+ * 获取元数据值
59
+ */
60
+ _getMetadataValue(key) {
61
+ // 优先从 metadata 获取
62
+ if (this._metadata.has(key)) {
63
+ return this._metadata.get(key)
64
+ }
65
+
66
+ // 内置变量
67
+ switch (key) {
68
+ case 'WORK_DIR':
69
+ case 'CWD':
70
+ return process.cwd()
71
+ case 'HOME_DIR':
72
+ return os.homedir()
73
+ case 'HOST_NAME':
74
+ return os.hostname()
75
+ case 'PLATFORM':
76
+ return process.platform
77
+ case 'TIME':
78
+ return new Date().toLocaleString('zh-CN', { timeZone: 'Asia/Shanghai' })
79
+ case 'DATE':
80
+ return new Date().toISOString().split('T')[0]
81
+ default:
82
+ return `{{${key}}}`
83
+ }
84
+ }
85
+
86
+ /**
87
+ * 替换 sharedPrompt 中的占位符
88
+ */
89
+ _replacePlaceholders(template) {
90
+ return template.replace(/\{\{(\w+)\}\}/g, (match, key) => {
91
+ return this._getMetadataValue(key)
92
+ })
93
+ }
94
+
95
+ /**
96
+ * 构建工具描述
97
+ */
98
+ _buildToolsDescription() {
99
+ if (this._tools.size === 0) {
100
+ return ''
101
+ }
102
+
103
+ let desc = '【可用工具】\n'
104
+ for (const [name, tool] of this._tools) {
105
+ desc += `- ${name}: ${tool.description || '无描述'}\n`
106
+ }
107
+ return desc.trim()
108
+ }
109
+
110
+ /**
111
+ * 构建技能描述
112
+ */
113
+ _buildSkillsDescription() {
114
+ const skillManager = this.framework.pluginManager.get('skill-manager')
115
+ if (!skillManager) {
116
+ return ''
117
+ }
118
+
119
+ const skills = skillManager.getAllSkills()
120
+ if (!skills || skills.length === 0) {
121
+ return ''
122
+ }
123
+
124
+ let desc = '【可用技能】\n'
125
+ for (const skill of skills) {
126
+ const name = skill.metadata?.name || skill.name || 'unknown'
127
+ const descText = skill.metadata?.description || ''
128
+ desc += `- ${name}: ${descText}\n`
129
+ }
130
+ desc += '\n重要:当需要开发插件、执行专业任务时,必须先使用 loadSkill 工具加载对应技能,获取专业指导。'
131
+ return desc.trim()
132
+ }
133
+
134
+ /**
135
+ * 构建系统能力描述
136
+ */
137
+ _buildCapabilitiesDescription() {
138
+ const plugins = this.framework.pluginManager.getAll()
139
+ if (!plugins || plugins.length === 0) {
140
+ return ''
141
+ }
142
+
143
+ // 过滤出有描述的关键插件(排除内部插件)
144
+ const keyPlugins = plugins.filter(p => {
145
+ const name = p.name
146
+ // 排除默认配置插件和内部插件
147
+ return name !== 'defaults' && name !== 'agent'
148
+ })
149
+
150
+ if (keyPlugins.length === 0) {
151
+ return ''
152
+ }
153
+
154
+ let desc = '【系统能力】\n'
155
+ for (const plugin of keyPlugins) {
156
+ const name = plugin.instance?.name || plugin.name || 'unknown'
157
+ const description = plugin.instance?.description || '无描述'
158
+ desc += `- ${name}: ${description}\n`
159
+ }
160
+
161
+ return desc.trim()
162
+ }
163
+
164
+ /**
165
+ * 构建子Agent描述
166
+ */
167
+ _buildSubAgentsDescription() {
168
+ if (this._subAgents.size === 0) {
169
+ return ''
170
+ }
171
+
172
+ let desc = '【子 Agent 分配规则 - 必须遵守】\n'
173
+ desc += '1. 每个子Agent有固定的专业领域,必须委托给对应的专家:\n'
174
+ for (const [name, { role, goal }] of this._subAgents) {
175
+ desc += ` - ${name}: ${role || goal || '子代理'}\n`
176
+ }
177
+ desc += '2. 你需要对任务进行拆分,传递给对应的子Agent执行。\n'
178
+ desc += '3. 多个不同类型的任务必须分别委托给不同的子Agent,每个子Agent只处理自己专业领域的任务。\n'
179
+ desc += '\n【可委托的子 Agent】\n'
180
+ for (const [name, { role, goal }] of this._subAgents) {
181
+ desc += ` - ${name}: ${role || goal || '子代理'}\n`
182
+ }
183
+ desc += '\n使用相应子Agent名称的工具将任务委托给相应子代理。'
184
+
185
+ return desc.trim()
186
+ }
187
+
188
+ /**
189
+ * 构建系统提示(带上下文)
190
+ */
191
+ _buildSystemPrompt() {
192
+ const parts = []
193
+
194
+ // 1. 原始 system prompt
195
+ parts.push(this._originalPrompt)
196
+
197
+ // 2. 共享提示模板(带占位符替换)
198
+ if (this._sharedPrompt) {
199
+ const replaced = this._replacePlaceholders(this._sharedPrompt)
200
+ parts.push(replaced)
201
+ }
202
+
203
+ // 3. 元数据
204
+ if (this._metadata.size > 0) {
205
+ const metaParts = ['【元数据】']
206
+ for (const [key, value] of this._metadata) {
207
+ if (typeof value === 'object') {
208
+ metaParts.push(`- ${key}: ${JSON.stringify(value)}`)
209
+ } else {
210
+ metaParts.push(`- ${key}: ${value}`)
211
+ }
212
+ }
213
+ parts.push(metaParts.join('\n'))
214
+ }
215
+
216
+ // 4. 工具列表
217
+ const toolsDesc = this._buildToolsDescription()
218
+ if (toolsDesc) {
219
+ parts.push(toolsDesc)
220
+ }
221
+
222
+ // 5. 技能列表
223
+ const skillsDesc = this._buildSkillsDescription()
224
+ if (skillsDesc) {
225
+ parts.push(skillsDesc)
226
+ }
227
+
228
+ // 6. 子Agent列表
229
+ const subAgentsDesc = this._buildSubAgentsDescription()
230
+ if (subAgentsDesc) {
231
+ parts.push(subAgentsDesc)
232
+ }
233
+
234
+ // 7. 系统能力
235
+ const capabilitiesDesc = this._buildCapabilitiesDescription()
236
+ if (capabilitiesDesc) {
237
+ parts.push(capabilitiesDesc)
238
+ }
239
+
240
+ return parts.join('\n\n')
241
+ }
242
+
243
+ /**
244
+ * 刷新上下文
245
+ */
246
+ _refreshContext() {
247
+ this.systemPrompt = this._buildSystemPrompt()
248
+ if (this._chatHandler) {
249
+ this._chatHandler.setSystemPrompt(this.systemPrompt)
250
+ }
251
+ }
252
+
253
+ /**
254
+ * 初始化聊天处理器
255
+ * @private
256
+ */
257
+ _initChatHandler() {
258
+ let aiClient = null
259
+ const aiPlugin = this.framework.pluginManager.get('ai')
260
+ if (aiPlugin) {
261
+ aiClient = aiPlugin.getAIClient()
262
+ }
263
+
264
+ this._chatHandler = new AgentChatHandler(this, {
265
+ model: this.model,
266
+ provider: this.provider,
267
+ apiKey: this.apiKey,
268
+ baseURL: this.baseURL,
269
+ providerOptions: this.providerOptions
270
+ })
271
+
272
+ if (aiClient) {
273
+ this._chatHandler.setAIClient(aiClient)
274
+ }
275
+
276
+ // 转发事件
277
+ this._chatHandler.on('message', (msg) => this.emit('message', msg))
278
+ this._chatHandler.on('chunk', (chunk) => this.emit('chunk', chunk))
279
+ this._chatHandler.on('tool-call', (tool) => this.emit('tool-call', tool))
280
+ this._chatHandler.on('tool-result', (result) => this.emit('tool-result', result))
281
+ this._chatHandler.on('error', (err) => this.emit('error', err))
282
+
283
+ this._syncTools()
284
+ }
285
+
286
+ /**
287
+ * 设置系统提示
288
+ */
289
+ setSystemPrompt(prompt) {
290
+ this._originalPrompt = prompt
291
+ this.systemPrompt = this._buildSystemPrompt()
292
+ if (this._chatHandler) {
293
+ this._chatHandler.setSystemPrompt(this.systemPrompt)
294
+ }
295
+ return this
296
+ }
297
+
298
+ /**
299
+ * 注册工具到 Agent
300
+ */
301
+ registerTool(tool) {
302
+ this._tools.set(tool.name, tool)
303
+ if (this._chatHandler) {
304
+ this._chatHandler.registerTool(tool)
305
+ }
306
+ this._refreshContext()
307
+ return this
308
+ }
309
+
310
+ /**
311
+ * 获取已注册工具
312
+ */
313
+ getTools() {
314
+ return Array.from(this._tools.values())
315
+ }
316
+
317
+ /**
318
+ * 同步框架中的工具到 Agent
319
+ * @private
320
+ */
321
+ _syncTools() {
322
+ if (this.framework.toolRegistry.size() > 0) {
323
+ for (const tool of this.framework.getTools()) {
324
+ if (!this._tools.has(tool.name)) {
325
+ this.registerTool(tool)
326
+ }
327
+ }
328
+ }
329
+ }
330
+
331
+ /**
332
+ * 注册子Agent
333
+ * @param {string} name - 子Agent名称
334
+ * @param {Agent} agent - 子Agent实例
335
+ * @param {string} role - 角色描述
336
+ * @param {string} goal - 目标描述
337
+ */
338
+ registerSubAgent(name, agent, role, goal) {
339
+ this._subAgents.set(name, { agent, role, goal })
340
+ this._refreshContext()
341
+ return this
342
+ }
343
+
344
+ /**
345
+ * 注销子Agent
346
+ */
347
+ unregisterSubAgent(name) {
348
+ this._subAgents.delete(name)
349
+ this._refreshContext()
350
+ return this
351
+ }
352
+
353
+ /**
354
+ * 获取所有子Agent
355
+ */
356
+ getSubAgents() {
357
+ return this._subAgents
358
+ }
359
+
360
+ /**
361
+ * 获取待处理的调度通知并清除(下次不再重复显示)
362
+ */
363
+ _getAndClearSchedulerNotifications() {
364
+ try {
365
+ const scheduler = this.framework.pluginManager.get('scheduler')
366
+ if (scheduler && scheduler.instance && scheduler.instance.getPendingNotifications) {
367
+ const results = scheduler.instance.getPendingNotifications()
368
+ if (results.length > 0) {
369
+ const notifications = results.slice(-5).reverse() // 最多5条,最新的在前
370
+ // 清除已发送的通知
371
+ if (scheduler.instance.clearDeliveredNotifications) {
372
+ scheduler.instance.clearDeliveredNotifications(notifications.length)
373
+ }
374
+ return notifications
375
+ }
376
+ }
377
+ } catch (err) {
378
+ // 忽略错误,避免影响主流程
379
+ }
380
+ return []
381
+ }
382
+
383
+ /**
384
+ * 获取待处理的思考结果并清除
385
+ */
386
+ _getAndClearThinkNotifications() {
387
+ try {
388
+ const think = this.framework.pluginManager.get('think')
389
+ if (think && think.instance && think.instance.getPendingThoughts) {
390
+ const thoughts = think.instance.getPendingThoughts()
391
+ if (thoughts.length > 0) {
392
+ return thoughts.slice(-5).reverse() // 最多5条,最新的在前
393
+ }
394
+ }
395
+ } catch (err) {
396
+ // 忽略错误,避免影响主流程
397
+ }
398
+ return []
399
+ }
400
+
401
+ /**
402
+ * 获取所有待处理的系统通知(调度 + 思考)
403
+ */
404
+ _getAllPendingNotifications() {
405
+ const notifications = []
406
+
407
+ // 调度通知
408
+ const schedulerNotifs = this._getAndClearSchedulerNotifications()
409
+ for (const n of schedulerNotifs) {
410
+ notifications.push(`【定时任务通知】\n任务: ${n.taskName || n.taskId}\n执行时间: ${n.executedAt}\n结果: ${n.result || n.action || '执行完成'}`)
411
+ }
412
+
413
+ // 思考通知
414
+ const thinkNotifs = this._getAndClearThinkNotifications()
415
+ for (const t of thinkNotifs) {
416
+ notifications.push(`【主动思考】\n模式: ${t.mode}\n主题: ${t.topic}\n结果: ${t.result || '思考完成'}`)
417
+ }
418
+
419
+ return notifications
420
+ }
421
+
422
+ /**
423
+ * 发送消息
424
+ */
425
+ async chat(message, options = {}) {
426
+ if (this._status === 'busy') {
427
+ throw new Error('Agent is busy')
428
+ }
429
+
430
+ if (this._status === 'error') {
431
+ this._status = 'idle'
432
+ }
433
+
434
+ this._status = 'busy'
435
+ this.emit('status', { status: 'busy' })
436
+
437
+ try {
438
+ // 检查是否有待处理的系统通知(调度 + 思考)
439
+ const notifications = this._getAllPendingNotifications()
440
+ let enhancedMessage = message
441
+ if (notifications.length > 0) {
442
+ enhancedMessage = `【系统通知】\n${notifications.join('\n\n')}\n\n---\n用户消息: ${message}`
443
+ }
444
+
445
+ this._syncTools()
446
+
447
+ const result = await this._chatHandler.chat(enhancedMessage, options)
448
+ this._status = 'idle'
449
+ this.emit('status', { status: 'idle' })
450
+ return result
451
+ } catch (err) {
452
+ this._status = 'error'
453
+ this.emit('status', { status: 'error', error: err.message })
454
+ throw err
455
+ }
456
+ }
457
+
458
+ /**
459
+ * 发送消息(流式)
460
+ */
461
+ async *chatStream(message, options = {}) {
462
+ if (this._status === 'busy') {
463
+ throw new Error('Agent is busy')
464
+ }
465
+
466
+ // 允许从 error 状态重试
467
+ if (this._status === 'error') {
468
+ this._status = 'idle'
469
+ }
470
+
471
+ this._status = 'busy'
472
+ this.emit('status', { status: 'busy' })
473
+
474
+ try {
475
+ // 检查是否有待处理的系统通知(调度 + 思考)
476
+ const notifications = this._getAllPendingNotifications()
477
+ let enhancedMessage = message
478
+ if (notifications.length > 0) {
479
+ enhancedMessage = `【系统通知】\n${notifications.join('\n\n')}\n\n---\n用户消息: ${message}`
480
+ }
481
+
482
+ this._syncTools()
483
+
484
+ yield* this._chatHandler.chatStream(enhancedMessage, options)
485
+ this._status = 'idle'
486
+ this.emit('status', { status: 'idle' })
487
+ } catch (err) {
488
+ this._status = 'error'
489
+ this.emit('status', { status: 'error', error: err.message })
490
+ throw err
491
+ }
492
+ }
493
+
494
+ /**
495
+ * 设置元数据
496
+ */
497
+ setMetadata(keyOrObj, value) {
498
+ if (typeof keyOrObj === 'string') {
499
+ this._metadata.set(keyOrObj, value)
500
+ } else if (keyOrObj && typeof keyOrObj === 'object') {
501
+ for (const [key, val] of Object.entries(keyOrObj)) {
502
+ this._metadata.set(key, val)
503
+ }
504
+ }
505
+ this._refreshContext()
506
+ return this
507
+ }
508
+
509
+ /**
510
+ * 获取元数据
511
+ */
512
+ getMetadata(key) {
513
+ return this._metadata.get(key)
514
+ }
515
+
516
+ /**
517
+ * 删除元数据
518
+ */
519
+ deleteMetadata(key) {
520
+ this._metadata.delete(key)
521
+ this._refreshContext()
522
+ return this
523
+ }
524
+
525
+ /**
526
+ * 清空元数据
527
+ */
528
+ clearMetadata() {
529
+ this._metadata.clear()
530
+ this._refreshContext()
531
+ return this
532
+ }
533
+
534
+ /**
535
+ * 清空对话历史
536
+ */
537
+ clearHistory() {
538
+ if (this._chatHandler) {
539
+ this._chatHandler.clearHistory()
540
+ }
541
+ return this
542
+ }
543
+
544
+ /**
545
+ * 获取状态
546
+ */
547
+ getStatus() {
548
+ return this._status
549
+ }
550
+
551
+ /**
552
+ * 重置状态(从卡住状态恢复)
553
+ */
554
+ resetStatus() {
555
+ this._status = 'idle'
556
+ this.emit('status', { status: 'idle' })
557
+ return this
558
+ }
559
+
560
+ /**
561
+ * 销毁 Agent
562
+ */
563
+ destroy() {
564
+ if (this._chatHandler) {
565
+ this._chatHandler.destroy()
566
+ }
567
+ this._tools.clear()
568
+ this.removeAllListeners()
569
+ this.emit('destroyed')
570
+ }
571
+ }
572
+
573
+ module.exports = { Agent }