foliko 1.0.68 → 1.0.70
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 +148 -145
- package/examples/basic.js +110 -110
- package/examples/mcp-example.js +53 -53
- package/examples/skill-example.js +49 -49
- package/examples/test-mcp.js +79 -79
- package/examples/test-reload.js +61 -61
- package/package.json +1 -1
- package/plugins/ambient-agent-plugin.js +333 -22
- package/plugins/email.js +106 -20
- package/plugins/file-system-plugin.js +76 -30
- package/plugins/python-executor-plugin.js +41 -8
- package/plugins/scheduler-plugin.js +6 -10
- package/plugins/web-plugin.js +8 -6
- package/skills/ambient-agent/SKILL.md +84 -14
- package/skills/workflow-guide/SKILL.md +214 -2
- package/skills/workflow-troubleshooting/DEBUGGING.md +182 -0
- package/skills/workflow-troubleshooting/SKILL.md +314 -0
- package/src/capabilities/workflow-engine.js +367 -22
- package/src/core/agent-chat.js +109 -14
- package/src/core/framework.js +81 -1
- package/src/executors/executor-base.js +58 -58
- package/test-server.js +0 -25
- package/test.txt +0 -3
package/src/core/agent-chat.js
CHANGED
|
@@ -58,6 +58,9 @@ class AgentChatHandler extends EventEmitter {
|
|
|
58
58
|
this._encoder = null
|
|
59
59
|
this._compressionCount = 0 // 压缩次数统计
|
|
60
60
|
|
|
61
|
+
// 工具结果压缩配置
|
|
62
|
+
this._maxToolResultSize = config.maxToolResultSize || 3000 // 工具结果超过此大小则压缩
|
|
63
|
+
|
|
61
64
|
// 初始化编码器
|
|
62
65
|
this._initEncoder()
|
|
63
66
|
}
|
|
@@ -201,15 +204,85 @@ ${conversationText}
|
|
|
201
204
|
3. 不要超过 1000 字
|
|
202
205
|
4. 用中文回复`
|
|
203
206
|
|
|
204
|
-
//
|
|
205
|
-
const
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
207
|
+
// 使用 AI SDK 6.x 的 generateText
|
|
208
|
+
const { generateText } = require('ai')
|
|
209
|
+
const { text } = await generateText({
|
|
210
|
+
model: this._aiClient,
|
|
211
|
+
prompt: summarizePrompt,
|
|
209
212
|
...this.providerOptions
|
|
210
213
|
})
|
|
211
214
|
|
|
212
|
-
return
|
|
215
|
+
return text || '(总结生成失败)'
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
/**
|
|
219
|
+
* 检查工具结果是否需要压缩
|
|
220
|
+
* @param {any} result - 工具返回结果
|
|
221
|
+
* @returns {boolean}
|
|
222
|
+
* @private
|
|
223
|
+
*/
|
|
224
|
+
_shouldCompressToolResult(result) {
|
|
225
|
+
if (!result || this._maxToolResultSize <= 0) return false
|
|
226
|
+
|
|
227
|
+
// 计算结果的大小
|
|
228
|
+
let size = 0
|
|
229
|
+
if (typeof result === 'string') {
|
|
230
|
+
size = result.length
|
|
231
|
+
} else if (typeof result === 'object') {
|
|
232
|
+
try {
|
|
233
|
+
size = JSON.stringify(result).length
|
|
234
|
+
} catch {
|
|
235
|
+
size = String(result).length
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
return size > this._maxToolResultSize
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
/**
|
|
243
|
+
* 压缩工具结果
|
|
244
|
+
* @param {any} result - 工具返回结果
|
|
245
|
+
* @returns {Promise<any>} 压缩后的结果
|
|
246
|
+
* @private
|
|
247
|
+
*/
|
|
248
|
+
async _compressToolResult(result) {
|
|
249
|
+
if (!this._shouldCompressToolResult(result)) {
|
|
250
|
+
return result
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
if (!this._aiClient) {
|
|
254
|
+
console.warn('[AgentChat] Cannot compress tool result: no AI client')
|
|
255
|
+
return result
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
try {
|
|
259
|
+
const originalSize = typeof result === 'string' ? result.length : JSON.stringify(result).length
|
|
260
|
+
const resultStr = typeof result === 'string' ? result : JSON.stringify(result, null, 2)
|
|
261
|
+
|
|
262
|
+
// 构建压缩提示
|
|
263
|
+
const compressPrompt = `以下是一个工具执行结果,长度 ${originalSize} 字符。请简洁地总结其核心内容,保留关键数据和信息:
|
|
264
|
+
|
|
265
|
+
${resultStr.substring(0, 8000)}${resultStr.length > 8000 ? '\n...(内容已截断)' : ''}
|
|
266
|
+
|
|
267
|
+
请用简洁的语言总结(不超过 500 字):`
|
|
268
|
+
|
|
269
|
+
// 使用 AI SDK 6.x 的 generateText
|
|
270
|
+
const { generateText } = require('ai')
|
|
271
|
+
const { text } = await generateText({
|
|
272
|
+
model: this._aiClient,
|
|
273
|
+
prompt: compressPrompt,
|
|
274
|
+
...this.providerOptions
|
|
275
|
+
})
|
|
276
|
+
|
|
277
|
+
const summary = text || '(压缩失败,使用原始结果)'
|
|
278
|
+
const compressed = `[工具结果已压缩: 原始大小 ${originalSize} 字符 → ${summary.length} 字符]\n\n${summary}`
|
|
279
|
+
|
|
280
|
+
console.log(`[AgentChat] Tool result compressed: ${originalSize} → ${summary.length} chars`)
|
|
281
|
+
return compressed
|
|
282
|
+
} catch (err) {
|
|
283
|
+
console.warn('[AgentChat] Tool result compression failed:', err.message)
|
|
284
|
+
return result
|
|
285
|
+
}
|
|
213
286
|
}
|
|
214
287
|
|
|
215
288
|
/**
|
|
@@ -280,6 +353,7 @@ ${conversationText}
|
|
|
280
353
|
async chat(message, options = {}) {
|
|
281
354
|
const context = { sessionId: options.sessionId || null, isStream: false }
|
|
282
355
|
const framework = this.agent.framework
|
|
356
|
+
const self = this // 保存引用用于回调
|
|
283
357
|
|
|
284
358
|
// 动态导入 AI SDK
|
|
285
359
|
const { tool, ToolLoopAgent } = await this._importAI()
|
|
@@ -306,6 +380,7 @@ ${conversationText}
|
|
|
306
380
|
model: this._aiClient,
|
|
307
381
|
instructions: this._systemPrompt,
|
|
308
382
|
tools: tools,
|
|
383
|
+
maxOutputTokens: options.maxOutputTokens || 5000,
|
|
309
384
|
stopWhen: (step) => step.stepCount >= maxSteps
|
|
310
385
|
})
|
|
311
386
|
|
|
@@ -319,7 +394,11 @@ ${conversationText}
|
|
|
319
394
|
return agent.generate({ messages, ...this.providerOptions })
|
|
320
395
|
})
|
|
321
396
|
|
|
322
|
-
|
|
397
|
+
// 处理返回的消息历史:只保留 user 和 assistant 消息,让 SDK 自动处理 tool 消息
|
|
398
|
+
if (result.messages && Array.isArray(result.messages)) {
|
|
399
|
+
// 只保留 user 和 assistant 消息,SDK 会自动维护 tool 消息
|
|
400
|
+
this._messages = result.messages.filter(m => m.role === 'user' || m.role === 'assistant')
|
|
401
|
+
} else if (result.text) {
|
|
323
402
|
this._messages.push({
|
|
324
403
|
role: 'assistant',
|
|
325
404
|
content: result.text
|
|
@@ -366,6 +445,7 @@ ${conversationText}
|
|
|
366
445
|
|
|
367
446
|
const maxSteps = options.maxSteps || this._maxSteps
|
|
368
447
|
const tools = this._getAITools(tool)
|
|
448
|
+
const self = this // 保存引用用于回调
|
|
369
449
|
|
|
370
450
|
if (!this._aiClient) {
|
|
371
451
|
throw new Error('AI client not configured.')
|
|
@@ -375,6 +455,8 @@ ${conversationText}
|
|
|
375
455
|
model: this._aiClient,
|
|
376
456
|
instructions: this._systemPrompt,
|
|
377
457
|
tools: tools,
|
|
458
|
+
maxOutputTokens: options.maxOutputTokens || 5000,
|
|
459
|
+
toolChoice:'auto',
|
|
378
460
|
stopWhen: (step) => step.stepCount >= maxSteps
|
|
379
461
|
})
|
|
380
462
|
|
|
@@ -391,7 +473,11 @@ ${conversationText}
|
|
|
391
473
|
const stream = result.fullStream
|
|
392
474
|
let fullText = ''
|
|
393
475
|
|
|
394
|
-
|
|
476
|
+
// 流式迭代器
|
|
477
|
+
const iterator = stream[Symbol.asyncIterator] ? stream : stream.fullStream
|
|
478
|
+
const finalMessages = []
|
|
479
|
+
|
|
480
|
+
for await (const part of (iterator || stream)) {
|
|
395
481
|
if (part.type === 'text-delta') {
|
|
396
482
|
const text = part.text || part.textDelta || ''
|
|
397
483
|
fullText += text
|
|
@@ -401,18 +487,27 @@ ${conversationText}
|
|
|
401
487
|
} else if (part.type === 'tool-call') {
|
|
402
488
|
yield { type: 'tool-call', toolName: part.toolName, args: part.input }
|
|
403
489
|
} else if (part.type === 'tool-result') {
|
|
490
|
+
// 保存到临时消息列表
|
|
491
|
+
finalMessages.push({ role: 'tool', content: part.output, toolName: part.toolName })
|
|
404
492
|
yield { type: 'tool-result', toolName: part.toolName, result: part.output }
|
|
405
493
|
} else if (part.type === 'error') {
|
|
406
494
|
yield { type: 'error', error: part.error }
|
|
407
495
|
}
|
|
408
496
|
}
|
|
409
497
|
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
498
|
+
// 暂时禁用压缩以调试 schema 问题
|
|
499
|
+
// for (const msg of finalMessages) {
|
|
500
|
+
// if (msg.content) {
|
|
501
|
+
// const contentSize = typeof msg.content === 'string' ? msg.content.length : JSON.stringify(msg.content).length
|
|
502
|
+
// if (contentSize > self._maxToolResultSize) {
|
|
503
|
+
// msg.content = await self._compressToolResult(msg.content)
|
|
504
|
+
// }
|
|
505
|
+
// }
|
|
506
|
+
// }
|
|
507
|
+
|
|
508
|
+
// 只保留 user 和 assistant 消息,让 SDK 自动处理 tool 消息
|
|
509
|
+
const assistantMsg = { role: 'assistant', content: fullText }
|
|
510
|
+
this._messages.push(assistantMsg)
|
|
416
511
|
} catch (err) {
|
|
417
512
|
this.emit('error', { error: err.message })
|
|
418
513
|
yield { type: 'error', error: err.message }
|
package/src/core/framework.js
CHANGED
|
@@ -215,7 +215,7 @@ class Framework extends EventEmitter {
|
|
|
215
215
|
name: `session_${sessionId}`,
|
|
216
216
|
...config
|
|
217
217
|
}
|
|
218
|
-
|
|
218
|
+
|
|
219
219
|
// 如果没有提供 AI 相关参数,从 AI 插件获取
|
|
220
220
|
if (!agentConfig.apiKey) {
|
|
221
221
|
const aiPlugin = this.pluginManager.get('ai')
|
|
@@ -239,6 +239,86 @@ class Framework extends EventEmitter {
|
|
|
239
239
|
return agent
|
|
240
240
|
}
|
|
241
241
|
|
|
242
|
+
/**
|
|
243
|
+
* 创建子Agent(具有独立工具集的轻量级Agent)
|
|
244
|
+
* @param {Object} config - 子Agent配置
|
|
245
|
+
* @param {string} config.name - 子Agent名称
|
|
246
|
+
* @param {string} config.role - 角色描述(用于智能选择)
|
|
247
|
+
* @param {string} [config.description] - 供主Agent理解的描述
|
|
248
|
+
* @param {Object} [config.tools] - 自定义工具 { name: toolDef }
|
|
249
|
+
* @param {string[]} [config.parentTools] - 从父Agent继承的工具名称列表
|
|
250
|
+
* @param {Object} [config.llmConfig] - 独立LLM配置
|
|
251
|
+
* @returns {Agent} 创建的子Agent
|
|
252
|
+
*/
|
|
253
|
+
createSubAgent(config) {
|
|
254
|
+
const {
|
|
255
|
+
name,
|
|
256
|
+
role,
|
|
257
|
+
description = '',
|
|
258
|
+
tools = {},
|
|
259
|
+
parentTools, // 如果不传,默认继承主agent所有工具
|
|
260
|
+
llmConfig = null
|
|
261
|
+
} = config
|
|
262
|
+
|
|
263
|
+
// 构建子Agent配置
|
|
264
|
+
const agentConfig = {
|
|
265
|
+
name: `subagent_${name}`,
|
|
266
|
+
role: role,
|
|
267
|
+
systemPrompt: role, // 使用 role 作为 systemPrompt
|
|
268
|
+
...(llmConfig || {}) // 如果提供了独立LLM配置,合并
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
// 如果没有提供 AI 相关参数,从 AI 插件获取
|
|
272
|
+
if (!agentConfig.apiKey) {
|
|
273
|
+
const aiPlugin = this.pluginManager.get('ai')
|
|
274
|
+
if (aiPlugin) {
|
|
275
|
+
agentConfig.apiKey = aiPlugin.config.apiKey
|
|
276
|
+
agentConfig.provider = agentConfig.provider || aiPlugin.config.provider
|
|
277
|
+
agentConfig.model = agentConfig.model || aiPlugin.config.model
|
|
278
|
+
agentConfig.baseURL = agentConfig.baseURL || aiPlugin.config.baseURL
|
|
279
|
+
agentConfig.providerOptions = agentConfig.providerOptions || aiPlugin.config.providerOptions || {}
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
// 创建 Agent
|
|
284
|
+
const agent = new Agent(this, agentConfig)
|
|
285
|
+
|
|
286
|
+
// 注册自定义工具
|
|
287
|
+
for (const [toolName, toolDef] of Object.entries(tools)) {
|
|
288
|
+
if (toolDef && typeof toolDef === 'object') {
|
|
289
|
+
agent.registerTool({
|
|
290
|
+
name: toolName,
|
|
291
|
+
description: toolDef.description || '',
|
|
292
|
+
inputSchema: toolDef.inputSchema,
|
|
293
|
+
execute: toolDef.execute
|
|
294
|
+
})
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
// 从父Agent继承工具
|
|
299
|
+
if (this._mainAgent) {
|
|
300
|
+
const parentToolsDefs = this._mainAgent.getTools()
|
|
301
|
+
if (parentTools === undefined) {
|
|
302
|
+
// 如果没有指定 parentTools,默认继承所有工具
|
|
303
|
+
for (const toolDef of parentToolsDefs) {
|
|
304
|
+
agent.registerTool(toolDef)
|
|
305
|
+
}
|
|
306
|
+
} else if (Array.isArray(parentTools) && parentTools.length > 0) {
|
|
307
|
+
// 指定了要继承的工具列表
|
|
308
|
+
for (const toolName of parentTools) {
|
|
309
|
+
const toolDef = parentToolsDefs.find(t => t.name === toolName)
|
|
310
|
+
if (toolDef) {
|
|
311
|
+
agent.registerTool(toolDef)
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
this._agents.push(agent)
|
|
318
|
+
this.emit('agent:created', agent)
|
|
319
|
+
return agent
|
|
320
|
+
}
|
|
321
|
+
|
|
242
322
|
/**
|
|
243
323
|
* 获取 AI 插件
|
|
244
324
|
* @returns {Object|undefined}
|
|
@@ -1,58 +1,58 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Executor 基类
|
|
3
|
-
* 执行器的基类,定义执行器接口
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
const { EventEmitter } = require('../utils/event-emitter')
|
|
7
|
-
|
|
8
|
-
class ExecutorBase extends EventEmitter {
|
|
9
|
-
/**
|
|
10
|
-
* @param {string} name - 执行器名称
|
|
11
|
-
*/
|
|
12
|
-
constructor(name) {
|
|
13
|
-
super()
|
|
14
|
-
this.name = name
|
|
15
|
-
this._enabled = true
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
/**
|
|
19
|
-
* 执行
|
|
20
|
-
* @param {Object} params - 执行参数
|
|
21
|
-
* @returns {Promise<any>}
|
|
22
|
-
*/
|
|
23
|
-
async execute(params) {
|
|
24
|
-
throw new Error('execute() must be implemented')
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
/**
|
|
28
|
-
* 启用执行器
|
|
29
|
-
*/
|
|
30
|
-
enable() {
|
|
31
|
-
this._enabled = true
|
|
32
|
-
return this
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
/**
|
|
36
|
-
* 禁用执行器
|
|
37
|
-
*/
|
|
38
|
-
disable() {
|
|
39
|
-
this._enabled = false
|
|
40
|
-
return this
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
/**
|
|
44
|
-
* 是否启用
|
|
45
|
-
*/
|
|
46
|
-
isEnabled() {
|
|
47
|
-
return this._enabled
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
/**
|
|
51
|
-
* 销毁
|
|
52
|
-
*/
|
|
53
|
-
destroy() {
|
|
54
|
-
this.removeAllListeners()
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
module.exports = { ExecutorBase }
|
|
1
|
+
/**
|
|
2
|
+
* Executor 基类
|
|
3
|
+
* 执行器的基类,定义执行器接口
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
const { EventEmitter } = require('../utils/event-emitter')
|
|
7
|
+
|
|
8
|
+
class ExecutorBase extends EventEmitter {
|
|
9
|
+
/**
|
|
10
|
+
* @param {string} name - 执行器名称
|
|
11
|
+
*/
|
|
12
|
+
constructor(name) {
|
|
13
|
+
super()
|
|
14
|
+
this.name = name
|
|
15
|
+
this._enabled = true
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* 执行
|
|
20
|
+
* @param {Object} params - 执行参数
|
|
21
|
+
* @returns {Promise<any>}
|
|
22
|
+
*/
|
|
23
|
+
async execute(params) {
|
|
24
|
+
throw new Error('execute() must be implemented')
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* 启用执行器
|
|
29
|
+
*/
|
|
30
|
+
enable() {
|
|
31
|
+
this._enabled = true
|
|
32
|
+
return this
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* 禁用执行器
|
|
37
|
+
*/
|
|
38
|
+
disable() {
|
|
39
|
+
this._enabled = false
|
|
40
|
+
return this
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* 是否启用
|
|
45
|
+
*/
|
|
46
|
+
isEnabled() {
|
|
47
|
+
return this._enabled
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* 销毁
|
|
52
|
+
*/
|
|
53
|
+
destroy() {
|
|
54
|
+
this.removeAllListeners()
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
module.exports = { ExecutorBase }
|
package/test-server.js
DELETED
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
const { serve } = require('@hono/node-server');
|
|
2
|
-
const { Hono } = require('hono');
|
|
3
|
-
|
|
4
|
-
const app = new Hono();
|
|
5
|
-
|
|
6
|
-
// 简单路由
|
|
7
|
-
app.get('/test', (c) => {
|
|
8
|
-
console.log('Handler called');
|
|
9
|
-
return c.json({ message: 'Hello World' });
|
|
10
|
-
});
|
|
11
|
-
|
|
12
|
-
const server = serve({
|
|
13
|
-
fetch: app.fetch,
|
|
14
|
-
port: 3001
|
|
15
|
-
});
|
|
16
|
-
|
|
17
|
-
server.on('request', (req) => {
|
|
18
|
-
console.log('Request:', req.method, req.url);
|
|
19
|
-
});
|
|
20
|
-
|
|
21
|
-
server.on('error', (err) => {
|
|
22
|
-
console.error('Server error:', err);
|
|
23
|
-
});
|
|
24
|
-
|
|
25
|
-
console.log('Server started on port 3001');
|
package/test.txt
DELETED