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.
- package/.claude/settings.local.json +30 -0
- package/22.txt +10 -0
- package/README.md +218 -0
- package/SPEC.md +452 -0
- package/cli/bin/foliko.js +12 -0
- package/cli/src/commands/chat.js +75 -0
- package/cli/src/index.js +64 -0
- package/cli/src/ui/chat-ui.js +272 -0
- package/cli/src/utils/ansi.js +40 -0
- package/cli/src/utils/markdown.js +296 -0
- package/docs/quick-reference.md +131 -0
- package/docs/user-manual.md +1205 -0
- package/examples/basic.js +110 -0
- package/examples/bootstrap.js +93 -0
- package/examples/mcp-example.js +53 -0
- package/examples/skill-example.js +49 -0
- package/examples/workflow.js +158 -0
- package/package.json +36 -0
- package/plugins/ai-plugin.js +89 -0
- package/plugins/audit-plugin.js +187 -0
- package/plugins/default-plugins.js +412 -0
- package/plugins/file-system-plugin.js +344 -0
- package/plugins/install-plugin.js +93 -0
- package/plugins/python-executor-plugin.js +331 -0
- package/plugins/rules-plugin.js +292 -0
- package/plugins/scheduler-plugin.js +426 -0
- package/plugins/session-plugin.js +343 -0
- package/plugins/shell-executor-plugin.js +196 -0
- package/plugins/storage-plugin.js +237 -0
- package/plugins/subagent-plugin.js +395 -0
- package/plugins/think-plugin.js +329 -0
- package/plugins/tools-plugin.js +114 -0
- package/skills/mcp-usage/SKILL.md +198 -0
- package/skills/vb-agent-dev/AGENTS.md +162 -0
- package/skills/vb-agent-dev/SKILL.md +370 -0
- package/src/capabilities/index.js +11 -0
- package/src/capabilities/skill-manager.js +319 -0
- package/src/capabilities/workflow-engine.js +401 -0
- package/src/core/agent-chat.js +311 -0
- package/src/core/agent.js +573 -0
- package/src/core/framework.js +255 -0
- package/src/core/index.js +19 -0
- package/src/core/plugin-base.js +205 -0
- package/src/core/plugin-manager.js +392 -0
- package/src/core/provider.js +108 -0
- package/src/core/tool-registry.js +134 -0
- package/src/core/tool-router.js +216 -0
- package/src/executors/executor-base.js +58 -0
- package/src/executors/mcp-executor.js +728 -0
- package/src/index.js +37 -0
- package/src/utils/event-emitter.js +97 -0
- package/test-chat.js +129 -0
- package/test-mcp.js +79 -0
- package/test-reload.js +61 -0
|
@@ -0,0 +1,311 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AgentChatHandler 聊天处理器
|
|
3
|
+
* 使用 AI SDK 的 ToolLoopAgent 处理工具调用循环
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
const { EventEmitter } = require('../utils/event-emitter')
|
|
7
|
+
|
|
8
|
+
class AgentChatHandler extends EventEmitter {
|
|
9
|
+
/**
|
|
10
|
+
* @param {Agent} agent - Agent 实例
|
|
11
|
+
* @param {Object} config - 配置
|
|
12
|
+
*/
|
|
13
|
+
constructor(agent, config = {}) {
|
|
14
|
+
super()
|
|
15
|
+
|
|
16
|
+
this.agent = agent
|
|
17
|
+
this.config = config
|
|
18
|
+
|
|
19
|
+
this.model = config.model || 'deepseek-chat'
|
|
20
|
+
this.provider = config.provider || 'deepseek'
|
|
21
|
+
this.apiKey = config.apiKey
|
|
22
|
+
this.baseURL = config.baseURL
|
|
23
|
+
this.providerOptions = config.providerOptions || {}
|
|
24
|
+
|
|
25
|
+
this._systemPrompt = config.systemPrompt || 'You are a helpful assistant.'
|
|
26
|
+
this._messages = []
|
|
27
|
+
this._tools = new Map()
|
|
28
|
+
this._maxSteps = 20
|
|
29
|
+
|
|
30
|
+
// AI 客户端
|
|
31
|
+
this._aiClient = null
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* 设置 AI 客户端
|
|
36
|
+
* @param {Object} client - AI 模型客户端
|
|
37
|
+
*/
|
|
38
|
+
setAIClient(client) {
|
|
39
|
+
this._aiClient = client
|
|
40
|
+
return this
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* 设置系统提示
|
|
45
|
+
* @param {string} prompt
|
|
46
|
+
*/
|
|
47
|
+
setSystemPrompt(prompt) {
|
|
48
|
+
this._systemPrompt = prompt
|
|
49
|
+
return this
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* 注册工具
|
|
54
|
+
* @param {Object} toolDef - 工具定义
|
|
55
|
+
*/
|
|
56
|
+
registerTool(toolDef) {
|
|
57
|
+
this._tools.set(toolDef.name, toolDef)
|
|
58
|
+
return this
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* 清空对话历史
|
|
63
|
+
*/
|
|
64
|
+
clearHistory() {
|
|
65
|
+
this._messages = []
|
|
66
|
+
return this
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* 获取已注册的工具
|
|
71
|
+
* @returns {Array}
|
|
72
|
+
*/
|
|
73
|
+
getTools() {
|
|
74
|
+
return Array.from(this._tools.values())
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* 导入 AI SDK(动态导入)
|
|
79
|
+
* @private
|
|
80
|
+
*/
|
|
81
|
+
async _importAI() {
|
|
82
|
+
try {
|
|
83
|
+
const ai = require('ai')
|
|
84
|
+
return {
|
|
85
|
+
tool: ai.tool,
|
|
86
|
+
ToolLoopAgent: ai.ToolLoopAgent
|
|
87
|
+
}
|
|
88
|
+
} catch (err) {
|
|
89
|
+
throw new Error('AI SDK not found. Please install: npm install ai')
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* 发送消息(非流式)
|
|
95
|
+
* @param {string|Object} message - 消息
|
|
96
|
+
* @param {Object} options - 选项
|
|
97
|
+
*/
|
|
98
|
+
async chat(message, options = {}) {
|
|
99
|
+
// 动态导入 AI SDK
|
|
100
|
+
const { tool, ToolLoopAgent } = await this._importAI()
|
|
101
|
+
|
|
102
|
+
const userMessage = typeof message === 'string'
|
|
103
|
+
? { role: 'user', content: message }
|
|
104
|
+
: message
|
|
105
|
+
|
|
106
|
+
this._messages.push(userMessage)
|
|
107
|
+
|
|
108
|
+
const maxSteps = options.maxSteps || this._maxSteps
|
|
109
|
+
const tools = this._getAITools(tool)
|
|
110
|
+
|
|
111
|
+
// 如果没有 AI 客户端,抛出错误
|
|
112
|
+
if (!this._aiClient) {
|
|
113
|
+
throw new Error('AI client not configured. Please set up AI plugin.')
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
try {
|
|
117
|
+
// 使用 ToolLoopAgent 处理工具调用循环
|
|
118
|
+
const agent = new ToolLoopAgent({
|
|
119
|
+
model: this._aiClient,
|
|
120
|
+
instructions: this._systemPrompt,
|
|
121
|
+
tools: tools,
|
|
122
|
+
stopWhen: (step) => step.stepCount >= maxSteps
|
|
123
|
+
})
|
|
124
|
+
|
|
125
|
+
// 构建消息列表
|
|
126
|
+
const messages = [
|
|
127
|
+
...this._cleanMessages(this._messages)
|
|
128
|
+
]
|
|
129
|
+
|
|
130
|
+
// 生成响应
|
|
131
|
+
const result = await agent.generate({
|
|
132
|
+
messages: messages,
|
|
133
|
+
providerOptions: this.providerOptions
|
|
134
|
+
})
|
|
135
|
+
|
|
136
|
+
// 添加助手消息到历史
|
|
137
|
+
if (result.text) {
|
|
138
|
+
this._messages.push({
|
|
139
|
+
role: 'assistant',
|
|
140
|
+
content: result.text
|
|
141
|
+
})
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
return {
|
|
145
|
+
success: true,
|
|
146
|
+
message: result.text || '',
|
|
147
|
+
stepCount: result.stepCount || 1
|
|
148
|
+
}
|
|
149
|
+
} catch (err) {
|
|
150
|
+
this.emit('error', { error: err.message })
|
|
151
|
+
// 抛出错误而不是返回错误响应,让 Agent 能捕获
|
|
152
|
+
throw err
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* 发送消息(流式)
|
|
158
|
+
* @param {string|Object} message - 消息
|
|
159
|
+
* @param {Object} options - 选项
|
|
160
|
+
*/
|
|
161
|
+
async *chatStream(message, options = {}) {
|
|
162
|
+
// 动态导入 AI SDK
|
|
163
|
+
const { tool, ToolLoopAgent } = await this._importAI()
|
|
164
|
+
|
|
165
|
+
const userMessage = typeof message === 'string'
|
|
166
|
+
? { role: 'user', content: message }
|
|
167
|
+
: message
|
|
168
|
+
|
|
169
|
+
this._messages.push(userMessage)
|
|
170
|
+
|
|
171
|
+
const maxSteps = options.maxSteps || this._maxSteps
|
|
172
|
+
const tools = this._getAITools(tool)
|
|
173
|
+
|
|
174
|
+
// 如果没有 AI 客户端,抛出错误
|
|
175
|
+
if (!this._aiClient) {
|
|
176
|
+
throw new Error('AI client not configured. Please set up AI plugin.')
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
// 使用 ToolLoopAgent 流式接口
|
|
180
|
+
const agent = new ToolLoopAgent({
|
|
181
|
+
model: this._aiClient,
|
|
182
|
+
instructions: this._systemPrompt,
|
|
183
|
+
tools: tools,
|
|
184
|
+
stopWhen: (step) => step.stepCount >= maxSteps
|
|
185
|
+
})
|
|
186
|
+
|
|
187
|
+
const messages = [
|
|
188
|
+
...this._cleanMessages(this._messages)
|
|
189
|
+
]
|
|
190
|
+
|
|
191
|
+
try {
|
|
192
|
+
const result = await agent.stream({
|
|
193
|
+
messages: messages,
|
|
194
|
+
providerOptions: this.providerOptions
|
|
195
|
+
})
|
|
196
|
+
|
|
197
|
+
const stream = result.fullStream
|
|
198
|
+
let fullText = ''
|
|
199
|
+
|
|
200
|
+
for await (const part of stream) {
|
|
201
|
+
if (part.type === 'text-delta') {
|
|
202
|
+
const text = part.text || part.textDelta || ''
|
|
203
|
+
fullText += text
|
|
204
|
+
yield { type: 'text', text }
|
|
205
|
+
} else if (part.type === 'reasoning') {
|
|
206
|
+
// 通过事件发射推理内容
|
|
207
|
+
this.emit('thinking', { content: part.text })
|
|
208
|
+
} else if (part.type === 'tool-call') {
|
|
209
|
+
yield { type: 'tool-call', toolName: part.toolName, args: part.input }
|
|
210
|
+
} else if (part.type === 'tool-result') {
|
|
211
|
+
yield { type: 'tool-result', toolName: part.toolName, result: part.output }
|
|
212
|
+
} else if (part.type === 'error') {
|
|
213
|
+
yield { type: 'error', error: part.error }
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
// 保存消息到历史
|
|
218
|
+
if (fullText) {
|
|
219
|
+
this._messages.push({
|
|
220
|
+
role: 'assistant',
|
|
221
|
+
content: fullText
|
|
222
|
+
})
|
|
223
|
+
}
|
|
224
|
+
} catch (err) {
|
|
225
|
+
this.emit('error', { error: err.message })
|
|
226
|
+
yield { type: 'error', error: err.message }
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
/**
|
|
231
|
+
* 获取 AI SDK 格式的工具
|
|
232
|
+
* AI SDK 6.x 需要对象形式 { toolName: tool }
|
|
233
|
+
* @param {Function} toolFn - AI SDK 的 tool 函数
|
|
234
|
+
* @private
|
|
235
|
+
*/
|
|
236
|
+
_getAITools(toolFn) {
|
|
237
|
+
const tools = {}
|
|
238
|
+
|
|
239
|
+
for (const [name, toolDef] of this._tools) {
|
|
240
|
+
const toolName = toolDef.name || name
|
|
241
|
+
|
|
242
|
+
// 使用 AI SDK 的 tool() 格式
|
|
243
|
+
// 支持 inputSchema (zod schema) 或 parameters (旧格式)
|
|
244
|
+
const toolConfig = {
|
|
245
|
+
name: toolName,
|
|
246
|
+
description: toolDef.description || '',
|
|
247
|
+
execute: async (args) => {
|
|
248
|
+
// 执行工具
|
|
249
|
+
this.emit('tool-call', { name: toolName, args })
|
|
250
|
+
try {
|
|
251
|
+
const result = await toolDef.execute(args, this.agent.framework)
|
|
252
|
+
this.emit('tool-result', { name: toolName, args, result })
|
|
253
|
+
return result
|
|
254
|
+
} catch (err) {
|
|
255
|
+
this.emit('tool-error', { name: toolName, args, error: err.message })
|
|
256
|
+
return { error: err.message }
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
// 支持 inputSchema (zod) 或 parameters (旧格式)
|
|
262
|
+
if (toolDef.inputSchema) {
|
|
263
|
+
toolConfig.inputSchema = toolDef.inputSchema
|
|
264
|
+
} else if (toolDef.parameters) {
|
|
265
|
+
toolConfig.parameters = toolDef.parameters
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
// AI SDK 6.x 使用对象形式,键为工具名
|
|
269
|
+
tools[toolName] = toolFn(toolConfig)
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
return tools
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
/**
|
|
276
|
+
* 清理消息格式
|
|
277
|
+
* @private
|
|
278
|
+
*/
|
|
279
|
+
_cleanMessages(messages) {
|
|
280
|
+
return messages.map(msg => {
|
|
281
|
+
if (!msg || typeof msg !== 'object') {
|
|
282
|
+
return { role: 'user', content: '' }
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
const cleaned = {
|
|
286
|
+
role: msg.role || 'user'
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
if (Array.isArray(msg.content)) {
|
|
290
|
+
cleaned.content = msg.content
|
|
291
|
+
} else if (msg.content !== undefined) {
|
|
292
|
+
cleaned.content = msg.content
|
|
293
|
+
} else {
|
|
294
|
+
cleaned.content = msg.text || msg.input || ''
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
return cleaned
|
|
298
|
+
})
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
/**
|
|
302
|
+
* 销毁
|
|
303
|
+
*/
|
|
304
|
+
destroy() {
|
|
305
|
+
this._messages = []
|
|
306
|
+
this._tools.clear()
|
|
307
|
+
this.removeAllListeners()
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
module.exports = { AgentChatHandler }
|