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,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 }