foliko 1.1.13 → 1.1.15

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 (102) hide show
  1. package/.agent/data/plugins-state.json +1 -1
  2. package/.agent/data/weixin/images/file_1776188148383jpg +0 -0
  3. package/.agent/data/weixin/images/file_1776188458326.jpg +0 -0
  4. package/.agent/data/weixin/images/file_1776188689423.jpg +0 -0
  5. package/.agent/data/weixin/images/file_1776188813604.jpg +0 -0
  6. package/.agent/data/weixin/images/file_1776189097450.jpg +0 -0
  7. package/.agent/data/weixin/videos/file_1776188318431.mp4 +0 -0
  8. package/.agent/mcp_config.json +7 -0
  9. package/.agent/memory/feedback/mnxe0cxc-14l6q5.md +17 -0
  10. package/.agent/memory/feedback/mnxe11pa-nxf577.md +9 -0
  11. package/.agent/memory/feedback/mnxe1an2-84faff.md +9 -0
  12. package/.agent/memory/feedback/mnxgcfj0-qg3wjc.md +9 -0
  13. package/.agent/memory/feedback/mnxgcn3y-40mqss.md +9 -0
  14. package/.agent/memory/feedback/mnxgcxq9-jm7ydl.md +9 -0
  15. package/.agent/memory/feedback/mnxgdyfj-pzjvkb.md +9 -0
  16. package/.agent/memory/feedback/mnxge3z1-7vyit1.md +9 -0
  17. package/.agent/memory/feedback/mnxhrg28-41hhjr.md +9 -0
  18. package/.agent/memory/feedback/mnxhrx0e-yth94k.md +9 -0
  19. package/.agent/memory/feedback/mnxhs3jd-rvx8aq.md +9 -0
  20. package/.agent/memory/feedback/mnxhs7p7-g5rtn9.md +9 -0
  21. package/.agent/memory/feedback/mnxhslx5-oqwuhr.md +9 -0
  22. package/.agent/memory/feedback/mnxhsvd6-nuyvvc.md +9 -0
  23. package/.agent/memory/project/mnxegq6z-5fc64w.md +22 -0
  24. package/.agent/memory/project/mnxh2w4r-le9hur.md +17 -0
  25. package/.agent/memory/project/mnxhq2yv-9qa8ay.md +31 -0
  26. package/.agent/memory/project/mnxhql11-iaun2o.md +34 -0
  27. package/.agent/memory/project/mnxhr78p-jpg7eq.md +23 -0
  28. package/.agent/memory/reference/mnxe0oa9-p6wzk6.md +27 -0
  29. package/.agent/memory/reference/mnxehcll-kcrmpf.md +29 -0
  30. package/.agent/memory/reference/mnxei0ts-jw091y.md +18 -0
  31. package/.agent/memory/reference/mnxfnrr4-rski36.md +40 -0
  32. package/.agent/memory/reference/mnxfo6n5-af9zls.md +18 -0
  33. package/.agent/memory/reference/mnxh2ady-u6cmvk.md +61 -0
  34. package/.agent/memory/reference/mnxhqdqh-ucsbsk.md +31 -0
  35. package/.agent/memory/reference/mnxiixyp-rz2gvw.md +34 -0
  36. package/.agent/memory/user/mnxhqxk3-vjjhlf.md +23 -0
  37. package/.agent/sessions/cli_default.json +11 -639
  38. package/.agent/sessions/weixin_o9cq80zgZqKPA2-s59PN43GdDy1w@im.wechat.json +25 -0
  39. package/.claude/settings.local.json +23 -1
  40. package/cli/src/commands/chat.js +9 -15
  41. package/cli/src/ui/chat-ui.js +40 -71
  42. package/package.json +4 -2
  43. package/plugins/default-plugins.js +5 -5
  44. package/plugins/file-system-plugin.js +1 -1
  45. package/plugins/memory-plugin.js +12 -12
  46. package/plugins/plugin-manager-plugin.js +1 -0
  47. package/plugins/subagent-plugin.js +55 -1
  48. package/plugins/telegram-plugin.js +9 -6
  49. package/plugins/weixin-plugin.js +75 -78
  50. package/src/core/agent-chat.js +468 -1612
  51. package/src/core/agent.js +53 -134
  52. package/src/core/chat-session.js +423 -0
  53. package/src/core/context-compressor.js +473 -0
  54. package/src/core/context-manager.js +0 -48
  55. package/src/core/framework.js +95 -68
  56. package/src/core/index.js +11 -0
  57. package/src/core/notification-manager.js +125 -0
  58. package/src/core/subagent.js +295 -0
  59. package/src/core/token-counter.js +190 -0
  60. package/src/core/tool-executor.js +270 -0
  61. package/src/executors/mcp-executor.js +14 -1
  62. package/src/utils/download.js +596 -0
  63. package/system.md +312 -2373
  64. package/.agent/agents/code-assistant.json +0 -17
  65. package/.agent/agents/email-assistant.json +0 -14
  66. package/.agent/agents/file-assistant.json +0 -18
  67. package/.agent/agents/orchestrator-demo.md +0 -53
  68. package/.agent/agents/orchestrator.json +0 -7
  69. package/.agent/agents/poster-expert.md +0 -228
  70. package/.agent/agents/system-assistant.json +0 -15
  71. package/.agent/agents/web-assistant.json +0 -12
  72. package/.agent/memory/feedback/mnv3nu27-3o15pf.md +0 -9
  73. package/.agent/memory/feedback/mnv3o078-b959yj.md +0 -9
  74. package/.agent/memory/feedback/mnv3o6ej-u0fif5.md +0 -9
  75. package/.agent/memory/feedback/mnv3obgl-bkkjoj.md +0 -9
  76. package/.agent/memory/feedback/mnv4a3js-dv6onx.md +0 -9
  77. package/.agent/memory/feedback/mnv4aacm-sxxowp.md +0 -9
  78. package/.agent/memory/feedback/mnv4ahto-w40ffm.md +0 -9
  79. package/.agent/memory/feedback/mnv4anvp-3cs06y.md +0 -9
  80. package/.agent/memory/feedback/mnvzgvtd-0o2900.md +0 -9
  81. package/.agent/memory/feedback/mnvzhajn-swbx61.md +0 -15
  82. package/.agent/memory/feedback/mnvzhgsp-p5vog3.md +0 -9
  83. package/.agent/memory/feedback/mnvzho0c-fgql7q.md +0 -14
  84. package/.agent/memory/feedback/mnvzhtzq-ufr5at.md +0 -9
  85. package/.agent/memory/feedback/mnvzhyb3-9byq2z.md +0 -9
  86. package/.agent/memory/feedback/mnvzi7hp-hyeafp.md +0 -9
  87. package/.agent/memory/feedback/mnvzibph-z7rwp5.md +0 -9
  88. package/.agent/memory/feedback/mnvzilys-7h176w.md +0 -14
  89. package/.agent/memory/feedback/mnvziuh5-zjshci.md +0 -9
  90. package/.agent/memory/feedback/mnw07wde-6zqsc8.md +0 -9
  91. package/.agent/memory/feedback/mnw084bp-j0ba2a.md +0 -9
  92. package/.agent/memory/user/mnv3n62r-y0h79j.md +0 -21
  93. package/.agent/memory/user/mnv3n9yf-ead4g8.md +0 -13
  94. package/.agent/memory/user/mnv3ne3j-82tq1k.md +0 -19
  95. package/.agent/memory/user/mnv3nhgm-g2s2us.md +0 -11
  96. package/.agent/memory/user/mnv3nl9u-ejd998.md +0 -16
  97. package/.agent/memory/user/mnv3nofp-ya5szl.md +0 -10
  98. package/.agent/memory/user/mnv49qne-bhk0ki.md +0 -9
  99. package/.agent/memory/user/mnv49w3y-rzr8ju.md +0 -13
  100. package/.agent/sessions/test.json +0 -16
  101. package/plugins/python-plugin-loader.js.bak +0 -856
  102. package/src/core/agent-context.js +0 -188
@@ -0,0 +1,25 @@
1
+ {
2
+ "sessionId": "weixin_o9cq80zgZqKPA2-s59PN43GdDy1w@im.wechat",
3
+ "messages": [
4
+ {
5
+ "role": "user",
6
+ "content": "\n [用户发送了图片消息]\n\n 文件列表:\n\n [\n \"D:\\\\Code\\\\foliko\\\\.agent\\\\data\\\\weixin\\\\images\\\\file_1776189097450.jpg\"\n] \n\n \n *重要:* 不要试图读取文件内容!\n "
7
+ },
8
+ {
9
+ "role": "assistant",
10
+ "content": [
11
+ {
12
+ "type": "text",
13
+ "text": "<think>\n用户发送了一张图片,路径是 \"D:\\\\Code\\\\foliko\\\\.agent\\\\data\\\\weixin\\\\images\\\\file_1776189097450.jpg\"。我需要帮助用户查看这张图片的内容。\n\n我可以直接使用 read_file 工具来读取图片文件,但用户说不要试图读取文件内容。不过,用户可能是想让我帮忙识别图片内容或进行其他处理。\n\n让我先看看这张图片是什么类型的文件,然后尝试理解用户的需求。由于这是一个图片文件,我可以考虑使用 Python 来处理它,或者直接向用户询问他们想对这张图片做什么。\n\n实际上,从工具列表来看,我没有专门处理图片的工具。我应该告诉用户我收到了图片,并询问他们想对这张图片做什么。\n\n等等,用户说的是\"不要试图读取文件内容\",这可能是因为:\n1. 图片是私密内容,不想让我知道\n2. 只是想让我识别图片内容\n3. 担心读取大文件会有问题\n\n无论如何,我应该询问用户他们希望我对这张图片做什么操作。\n</think>\n\n我收到了你发送的图片文件。请问你想对这张图片做什么处理?比如:\n\n- **识别图片内容**(描述图片中的内容)\n- **提取图片文字**(OCR)\n- **查看图片信息**(尺寸、格式、大小等)\n- **其他处理**\n\n请告诉我你的需求,我会帮你处理。"
14
+ }
15
+ ]
16
+ }
17
+ ],
18
+ "variables": {},
19
+ "metadata": {
20
+ "createdAt": 1776188690731,
21
+ "lastActive": 1776188690731,
22
+ "messageCount": 0,
23
+ "compressionCount": 0
24
+ }
25
+ }
@@ -180,7 +180,29 @@
180
180
  "Bash(cd \"D:/Code/foliko\" && node -e \"require\\('./plugins/extension-executor-plugin'\\); console.log\\('OK'\\)\")",
181
181
  "Bash(cd D:/Code/foliko && timeout 8s node cli/bin/foliko.js chat 2>&1 || true)",
182
182
  "Bash(cd /d/Code/foliko-plugins/poster-plugin && node -e \"\nconst LayoutManager = require\\('./src/layout-manager'\\);\nconsole.log\\('LayoutManager loaded successfully'\\);\nconsole.log\\('Methods:', Object.getOwnPropertyNames\\(LayoutManager.prototype\\).join\\(', '\\)\\);\n\")",
183
- "Bash(node test-ctx-push.js 2>&1 | head -50)"
183
+ "Bash(node test-ctx-push.js 2>&1 | head -50)",
184
+ "Bash(cd D:/code/vb-agent && node -e \"const { Framework } = require\\('./src/core/framework'\\); const f = new Framework\\({ debug: true }\\); console.log\\('Framework loaded OK'\\); console.log\\('ContextManager stats:', f._contextManager.getStats\\(\\)\\);\")",
185
+ "Bash(cd D:/code/vb-agent && node -e \"\nconst { Framework } = require\\('./src/core/framework'\\);\nconst { NotificationManager } = require\\('./src/core/notification-manager'\\);\nconst f = new Framework\\({ debug: true }\\);\nconst agent = f.createAgent\\({ name: 'test' }\\);\nconsole.log\\('Agent created OK'\\);\nconsole.log\\('Has notificationManager:', !!agent._notificationManager\\);\nconsole.log\\('NotificationManager type:', agent._notificationManager.constructor.name\\);\n\")",
186
+ "Bash(cd D:/code/vb-agent && node -e \"\nconst { Framework } = require\\('./src/core/framework'\\);\nconst { NotificationManager } = require\\('./src/core/notification-manager'\\);\nconst { TokenCounter } = require\\('./src/core/token-counter'\\);\n\nconst f = new Framework\\({ debug: true }\\);\nconst agent = f.createAgent\\({ name: 'test' }\\);\n\nconsole.log\\('=== 验证结果 ==='\\);\nconsole.log\\('Framework: OK'\\);\nconsole.log\\('NotificationManager: OK \\(has enhanceMessage:', typeof agent._notificationManager.enhanceMessage === 'function', '\\)'\\);\nconsole.log\\('TokenCounter: OK \\(has countMessages:', typeof TokenCounter.prototype.countMessages === 'function', '\\)'\\);\n\n// 检查 AgentContext 是否被删除\ntry {\n require\\('./src/core/agent-context'\\);\n console.log\\('AgentContext: FAILED \\(should not exist\\)'\\);\n} catch \\(e\\) {\n console.log\\('AgentContext: OK \\(deleted\\)'\\);\n}\n\n// 检查 ContextManager 是否正常工作\nconsole.log\\('ContextManager stats:', f._contextManager.getStats\\(\\)\\);\n\")",
187
+ "Bash(cd D:/code/vb-agent && node -e \"\nconst { Framework } = require\\('./src/core/framework'\\);\nconst f = new Framework\\({ debug: true }\\);\n\n// 创建 Agent\nconst agent = f.createAgent\\({ \n name: 'TestAgent',\n systemPrompt: '你是一个测试助手'\n}\\);\n\nconsole.log\\('Agent created:', agent.name\\);\nconsole.log\\('Agent has _chatHandler:', !!agent._chatHandler\\);\nconsole.log\\('AgentChatHandler type:', agent._chatHandler?.constructor?.name\\);\n\n// 测试 chat 方法是否存在\nconsole.log\\('Agent.chat is function:', typeof agent.chat === 'function'\\);\nconsole.log\\('Agent._chatHandler.chat is function:', typeof agent._chatHandler?.chat === 'function'\\);\n\n// 测试 Agent._buildSystemPrompt\nconsole.log\\('Agent._buildSystemPrompt is function:', typeof agent._buildSystemPrompt === 'function'\\);\nconsole.log\\('System prompt length:', agent.systemPrompt?.length || 0\\);\n\")",
188
+ "Bash(cd D:/code/vb-agent && node -e \"const { generateText, streamText } = require\\('ai'\\); console.log\\(typeof streamText\\)\" 2>&1)",
189
+ "Bash(cd D:/code/vb-agent && node -e \"\nconst { createAI, createModel } = require\\('./src/core/provider'\\);\nconst { streamText } = require\\('ai'\\);\n\nconst aiProvider = createAI\\({\n provider: 'minimax',\n model: 'MiniMax-text-01',\n apiKey: process.env.MINIMAX_API_KEY || 'test',\n baseURL: 'https://api.minimaxi.com/v1',\n}\\);\nconst model = createModel\\('MiniMax-text-01', aiProvider\\);\n\nconst result = streamText\\({\n model,\n system: 'You are helpful',\n messages: [{ role: 'user', content: 'hi' }],\n}\\);\n\nconsole.log\\('baseStream type:', typeof result.baseStream\\);\nconsole.log\\('baseStream:', result.baseStream\\);\n\n// 尝试直接遍历\n\\(async \\(\\) => {\n try {\n for await \\(const chunk of result\\) {\n console.log\\('chunk:', chunk\\);\n }\n } catch \\(e\\) {\n console.error\\('error:', e.message\\);\n }\n}\\)\\(\\);\n\" 2>&1)",
190
+ "Bash(cd D:/code/vb-agent && node -e \"\nrequire\\('dotenv'\\).config\\(\\);\nconsole.log\\('MINIMAX_API_KEY:', process.env.MINIMAX_API_KEY ? 'set \\(' + process.env.MINIMAX_API_KEY.substring\\(0, 10\\) + '...\\)' : 'NOT SET'\\);\nconsole.log\\('DEEPSEEK_API_KEY:', process.env.DEEPSEEK_API_KEY ? 'set \\(' + process.env.DEEPSEEK_API_KEY.substring\\(0, 10\\) + '...\\)' : 'NOT SET'\\);\n\" 2>&1)",
191
+ "Bash(cd D:/code/vb-agent && node -e \"\nrequire\\('dotenv'\\).config\\(\\);\nconst { Framework } = require\\('./src/core/framework'\\);\n\n\\(async \\(\\) => {\n const framework = new Framework\\({ debug: false }\\);\n await framework.bootstrap\\({\n agentDir: 'D:/code/vb-agent/.agent',\n aiConfig: {\n provider: 'minimax',\n model: 'MiniMax-M2.7',\n baseURL: 'https://api.minimaxi.com/v1',\n apiKey: process.env.FOLIKO_API_KEY,\n },\n }\\);\n \n const agent = framework._mainAgent;\n console.log\\('Agent provider:', agent.provider\\);\n console.log\\('Agent apiKey:', agent.apiKey ? agent.apiKey.substring\\(0, 15\\) : 'undefined'\\);\n console.log\\('Agent baseURL:', agent.baseURL\\);\n process.exit\\(0\\);\n}\\)\\(\\);\n\" 2>&1)",
192
+ "Bash(cd D:/code/vb-agent && node -e \"const ai = require\\('ai'\\); console.log\\(Object.keys\\(ai\\).filter\\(k => k.toLowerCase\\(\\).includes\\('tool'\\) || k.toLowerCase\\(\\).includes\\('loop'\\)\\)\\)\" 2>&1)",
193
+ "Bash(cd D:/code/vb-agent && node -e \"\nrequire\\('dotenv'\\).config\\(\\);\nconst { Framework } = require\\('./src/core/framework'\\);\n\n\\(async \\(\\) => {\n const framework = new Framework\\({ debug: false }\\);\n await framework.bootstrap\\({\n agentDir: 'D:/code/vb-agent/.agent',\n aiConfig: {\n provider: 'minimax',\n model: 'MiniMax-M2.7',\n baseURL: 'https://api.minimaxi.com/v1',\n apiKey: process.env.FOLIKO_API_KEY,\n },\n }\\);\n \n const agent = framework._mainAgent;\n console.log\\('Testing chat...'\\);\n \n try {\n const result = await agent.chat\\('say hi in 3 words', {}\\);\n console.log\\('Result:', result.message?.substring\\(0, 50\\)\\);\n } catch \\(e\\) {\n console.error\\('Error:', e.message\\);\n }\n process.exit\\(0\\);\n}\\)\\(\\);\n\" 2>&1)",
194
+ "Bash(cd D:/code/vb-agent && node -e \"\nrequire\\('dotenv'\\).config\\(\\);\nconst { Framework } = require\\('./src/core/framework'\\);\n\n\\(async \\(\\) => {\n const framework = new Framework\\({ debug: false }\\);\n await framework.bootstrap\\({\n agentDir: 'D:/code/vb-agent/.agent',\n aiConfig: {\n provider: 'minimax',\n model: 'MiniMax-M2.7',\n baseURL: 'https://api.minimaxi.com/v1',\n apiKey: process.env.FOLIKO_API_KEY,\n },\n }\\);\n \n const agent = framework._mainAgent;\n let full = '';\n try {\n for await \\(const chunk of agent.chatStream\\('say hi', {}\\)\\) {\n if \\(chunk.type === 'text'\\) {\n full += chunk.text;\n process.stdout.write\\(chunk.text\\);\n }\n }\n console.log\\('\\\\\\\\nFull:', full.length\\);\n } catch \\(e\\) {\n console.error\\('Error:', e.message\\);\n }\n process.exit\\(0\\);\n}\\)\\(\\);\n\" 2>&1)",
195
+ "Bash(node -e \"require\\('./src/core/chat-session.js'\\); require\\('./src/core/agent-chat.js'\\); console.log\\('OK'\\)\" 2>&1)",
196
+ "Bash(node -e \"require\\('./src/core/chat-session.js'\\); console.log\\('OK'\\)\" 2>&1)",
197
+ "Bash(node -e \"require\\('./cli/src/ui/chat-ui.js'\\); console.log\\('OK'\\)\" 2>&1)",
198
+ "Bash(node -e \"\nconst { ToolLoopAgent, isLoopFinished, generateText, streamText } = require\\('ai'\\);\nconsole.log\\('ToolLoopAgent imported:', typeof ToolLoopAgent\\);\nconsole.log\\('isLoopFinished:', typeof isLoopFinished\\);\nconsole.log\\('ToolLoopAgent methods:', Object.getOwnPropertyNames\\(ToolLoopAgent.prototype\\)\\);\n\" 2>&1)",
199
+ "Bash(node -e \"\nconst { isLoopFinished } = require\\('ai'\\);\nconsole.log\\('isLoopFinished:', isLoopFinished.toString\\(\\).slice\\(0, 500\\)\\);\n\" 2>&1)",
200
+ "Bash(node -e \"require\\('./plugins/weixin-plugin.js'\\); console.log\\('OK'\\)\" 2>&1)",
201
+ "Bash(node -e \"require\\('./src/core/tool-executor.js'\\); console.log\\('OK'\\)\" 2>&1)",
202
+ "Bash(node -e \"\nconst { createTools } = require\\('ai'\\);\nconsole.log\\('createTools exists:', typeof createTools\\);\n\" 2>&1)",
203
+ "Bash(node -e \"\nconst poster = require\\('/d/code/foliko-plugins/poster-plugin'\\);\nconsole.log\\('poster.tools:', typeof poster.tools\\);\nconsole.log\\('poster.tools keys:', Object.keys\\(poster.tools || {}\\).slice\\(0, 5\\)\\);\n\" 2>&1)",
204
+ "Bash(node -e \"require\\('./src/core/agent-chat.js'\\); console.log\\('OK'\\)\" 2>&1)",
205
+ "Bash(node -e \"const ai = require\\('ai'\\); console.log\\('tool type:', typeof ai.tool\\)\")"
184
206
  ]
185
207
  }
186
208
  }
@@ -112,8 +112,6 @@ async function runContinuousTest(agent, options) {
112
112
  console.log(`命令数量: ${commands.length}`);
113
113
  console.log('');
114
114
 
115
- const runWithContext = agent.framework?.runWithContext.bind(agent.framework);
116
-
117
115
  for (let i = 0; i < commands.length; i++) {
118
116
  const cmd = commands[i];
119
117
  console.log(`\n[${i + 1}/${commands.length}] >>> ${cmd}`);
@@ -121,26 +119,22 @@ async function runContinuousTest(agent, options) {
121
119
 
122
120
  try {
123
121
  let fullResponse = '';
124
- await runWithContext({ sessionId }, async () => {
125
- for await (const chunk of agent.chatStream(cmd, { sessionId })) {
126
- if (chunk.type === 'text') {
127
- fullResponse += chunk.text;
128
- process.stdout.write(chunk.text);
129
- } else if (chunk.type === 'tool-call') {
130
- console.log(`\n[工具调用] ${chunk.toolName} ${JSON.stringify(chunk.input)}`);
131
- } else if (chunk.type === 'error') {
132
- console.error(`\n[错误] ${chunk.error}`);
133
- }
122
+ for await (const chunk of agent.chatStream(cmd, { sessionId })) {
123
+ if (chunk.type === 'text') {
124
+ fullResponse += chunk.text;
125
+ process.stdout.write(chunk.text);
126
+ } else if (chunk.type === 'tool-call') {
127
+ console.log(`\n[工具调用] ${chunk.toolName} ${JSON.stringify(chunk.input)}`);
128
+ } else if (chunk.type === 'error') {
129
+ console.error(`\n[错误] ${chunk.error}`);
134
130
  }
135
- });
131
+ }
136
132
  console.log('\n' + '-'.repeat(50));
137
133
  console.log(`[完成] 回复长度: ${fullResponse.length}`);
138
134
  } catch (err) {
139
135
  console.error(`\n[执行错误] ${err.message}`);
140
- // 继续执行下一条命令
141
136
  }
142
137
 
143
- // 命令间隔 1 秒
144
138
  await new Promise((r) => setTimeout(r, 1000));
145
139
  }
146
140
 
@@ -125,10 +125,8 @@ class ChatUI {
125
125
  * 发送消息并显示响应
126
126
  */
127
127
  async sendMessage(message) {
128
- // 用于打断的标志
129
128
  let interrupted = false;
130
129
 
131
- // 设置打断处理
132
130
  const interruptHandler = () => {
133
131
  if (!interrupted) {
134
132
  interrupted = true;
@@ -136,78 +134,58 @@ class ChatUI {
136
134
  }
137
135
  };
138
136
 
139
- // 监听 Ctrl+C
140
137
  process.on('SIGINT', interruptHandler);
141
138
 
142
- let fullResponse = '';
143
- let sessionScope = null;
144
-
145
139
  try {
146
140
  const renderState = { inThink: false, inCodeBlock: false };
147
- const runWithContext = this.agent.framework?.runWithContext.bind(this.agent.framework);
148
141
  const { sessionId } = this;
149
142
 
150
- // 创建当前 session 的事件作用域,自动过滤其他 session 的事件
151
- sessionScope = this.agent.createSessionScope(sessionId);
152
-
153
143
  if (this.stream) {
154
- // 流式模式
144
+ // 流式模式 - 使用事件方式
155
145
  console.log(colored('● ', GREEN));
156
146
 
157
147
  let lineBuffer = '';
158
- await runWithContext({ sessionId }, async () => {
159
- // 使用 sessionScope 监听事件,自动只接收当前 session 的事件
160
- if (sessionScope) {
161
- sessionScope.on('stream:chunk', (data) => {
162
- const { chunk } = data;
163
- if (interrupted) return;
164
-
165
- if (chunk.type === 'text') {
166
- lineBuffer += chunk.text;
167
-
168
- while (lineBuffer.includes('\n')) {
169
- if (interrupted) break;
170
-
171
- const nlIndex = lineBuffer.indexOf('\n');
172
- const line = lineBuffer.substring(0, nlIndex);
173
- lineBuffer = lineBuffer.substring(nlIndex + 1);
174
- if (line.trim()) {
175
- console.log(renderLine(line, renderState));
176
- }
177
- }
178
- } else if (chunk.type === 'tool-call') {
179
- console.log(
180
- `\n${colored('[工具调用]', YELLOW)} ${chunk.toolName}`,
181
- `args=`,
182
- JSON.stringify(chunk.input)
183
- );
184
- } else if (chunk.type === 'error') {
185
- console.error(`\n${colored('[错误]', RED)} ${chunk.error}`);
186
- }
187
- });
188
-
189
- sessionScope.on('message:complete', ({ sessionId, content }) => {
190
- const message = '继续下一步';
191
- if (!content) {
192
- console.log(colored(`● ${message}...`, DIM));
193
- this.agent.sendMessage(message, {
194
- sessionId: sessionId,
195
- priority: 1,
196
- });
148
+ let hasToolCall = false;
149
+
150
+ // 创建 session scope 过滤事件
151
+ const sessionScope = this.agent.createSessionScope(sessionId);
152
+
153
+ // 监听流式数据
154
+ sessionScope.on('stream:chunk', ({ chunk }) => {
155
+ if (interrupted) return;
156
+
157
+ if (chunk.type === 'text') {
158
+ lineBuffer += chunk.text;
159
+
160
+ while (lineBuffer.includes('\n')) {
161
+ if (interrupted) break;
162
+
163
+ const nlIndex = lineBuffer.indexOf('\n');
164
+ const line = lineBuffer.substring(0, nlIndex);
165
+ lineBuffer = lineBuffer.substring(nlIndex + 1);
166
+ if (line.trim()) {
167
+ console.log(renderLine(line, renderState));
197
168
  }
198
- });
169
+ }
170
+ } else if (chunk.type === 'tool-call') {
171
+ if (!hasToolCall) {
172
+ hasToolCall = true;
173
+ console.log();
174
+ }
175
+ console.log(
176
+ `${colored('[工具调用]', YELLOW)} ${chunk.toolName}`,
177
+ `args=`,
178
+ JSON.stringify(chunk.input)
179
+ );
180
+ } else if (chunk.type === 'error') {
181
+ console.error(`\n${colored('[错误]', RED)} ${chunk.error}`);
199
182
  }
200
-
201
- const result = await this.agent.sendMessage(message, {
202
- sessionId: sessionId,
203
- priority: 1,
204
- });
205
183
  });
206
184
 
207
- // 清除 session scope 的监听器
208
- if (sessionScope) {
209
- sessionScope.removeAllListeners();
210
- }
185
+ // 调用 sendMessage 触发事件
186
+ const res = await this.agent.sendMessage(message, { sessionId });
187
+ // 清理
188
+ sessionScope.removeAllListeners();
211
189
 
212
190
  if (lineBuffer.trim() && !interrupted) {
213
191
  console.log(renderLine(lineBuffer, renderState));
@@ -216,16 +194,11 @@ class ChatUI {
216
194
  // 非流式模式
217
195
  console.log(colored('○ ', CYAN) + '处理中...\n');
218
196
 
219
- const result = await runWithContext({ sessionId }, async () => {
220
- return await this.agent.chat(message, { sessionId });
221
- });
197
+ const result = await this.agent.chat(message, { sessionId });
222
198
 
223
199
  if (interrupted) return;
224
200
 
225
- fullResponse = result.message;
226
-
227
- // 一次性渲染完整响应
228
- const lines = fullResponse.split('\n');
201
+ const lines = result.message.split('\n');
229
202
  for (const line of lines) {
230
203
  if (line.trim()) {
231
204
  console.log(renderLine(line, renderState));
@@ -240,10 +213,6 @@ class ChatUI {
240
213
  console.error(`\n${colored('[错误]', RED)} ${err.message}\n`);
241
214
  }
242
215
  } finally {
243
- // 确保清理 session scope 的监听器
244
- if (sessionScope) {
245
- sessionScope.removeAllListeners();
246
- }
247
216
  process.removeListener('SIGINT', interruptHandler);
248
217
  }
249
218
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "foliko",
3
- "version": "1.1.13",
3
+ "version": "1.1.15",
4
4
  "description": "简约的插件化 Agent 框架",
5
5
  "main": "src/index.js",
6
6
  "type": "commonjs",
@@ -53,13 +53,15 @@
53
53
  "@ai-sdk/openai": "^3.0.41",
54
54
  "@ai-sdk/openai-compatible": "^2.0.35",
55
55
  "@anthropic-ai/sdk": "^0.39.0",
56
- "@chnak/weixin-bot": "^1.2.7",
56
+ "@chnak/weixin-bot": "^1.2.9",
57
57
  "@chnak/zod-to-markdown": "1.0.7",
58
58
  "@hono/node-server": "^1.19.11",
59
59
  "@larksuiteoapi/node-sdk": "^1.59.0",
60
60
  "@modelcontextprotocol/sdk": "^1.27.1",
61
61
  "ai": "^6.0.146",
62
+ "crypto": "^1.0.1",
62
63
  "dotenv": "^17.3.1",
64
+ "file-type": "^22.0.1",
63
65
  "gate-api": "^7.2.57",
64
66
  "hono": "^4.12.9",
65
67
  "imap-mkl": "^1.0.2",
@@ -279,13 +279,13 @@ async function bootstrapDefaults(framework, config = {}) {
279
279
 
280
280
  framework._debug&&bootstrapLog.debug(' Loading default plugins...')
281
281
  // AI 插件(如果已禁用则跳过)
282
- if (!shouldLoad('ai') || !(aiConfig.provider || aiConfig.model || aiConfig.apiKey)) {
282
+ // 根据 provider 获取对应的 API key
283
+ const upperProvider = (aiConfig.provider || 'deepseek').toUpperCase().replace(/-/g, '_')
284
+ const envApiKey = process.env[`${upperProvider}_API_KEY`] || process.env.FOLIKO_API_KEY
285
+ if (!shouldLoad('ai') || !(aiConfig.provider || aiConfig.model || aiConfig.apiKey || envApiKey)) {
283
286
  // 跳过或已禁用
284
287
  } else {
285
288
  const AIPlugin = require('./ai-plugin')
286
- // 根据 provider 获取对应的 API key
287
- const upperProvider = (aiConfig.provider || 'deepseek').toUpperCase().replace(/-/g, '_')
288
- const envApiKey = process.env[`${upperProvider}_API_KEY`] || process.env.FOLIKO_API_KEY
289
289
  const aiPlugin = new AIPlugin({
290
290
  provider: aiConfig.provider || 'deepseek',
291
291
  model: aiConfig.model || 'deepseek-chat',
@@ -309,7 +309,7 @@ async function bootstrapDefaults(framework, config = {}) {
309
309
  systemPrompt: '你是一个智能助手。当用户提出问题或任务时,你会主动分析需求,选择合适的工具来获取信息或执行操作。你善于将复杂任务拆解为多个步骤,通过工具协作完成。',
310
310
  model: aiConfig.model || 'deepseek-chat',
311
311
  provider: aiConfig.provider || 'deepseek',
312
- apiKey: aiConfig.apiKey,
312
+ apiKey: aiPlugin ? aiPlugin.config.apiKey : (aiConfig.apiKey || envApiKey),
313
313
  baseURL: aiConfig.baseURL
314
314
  })
315
315
  framework._agents.push(framework._mainAgent)
@@ -13,7 +13,7 @@ class FileSystemPlugin extends Plugin {
13
13
  this.name = 'file-system'
14
14
  this.version = '1.0.0'
15
15
  this.description = '文件系统工具插件'
16
- this.priority = 5
16
+ this.priority = 12
17
17
  this.system = true
18
18
  }
19
19
 
@@ -977,18 +977,18 @@ class MemoryPlugin extends Plugin {
977
977
  }
978
978
 
979
979
  // 批量处理错误
980
- if (this._pendingErrors.length > 0) {
981
- const errors = this._pendingErrors.splice(0, 10) // 每次最多处理 10 条
982
- log.debug(`Processing ${errors.length} pending errors`)
983
-
984
- for (const errorData of errors) {
985
- try {
986
- await this._extractErrorLesson(errorData)
987
- } catch (err) {
988
- log.warn('Failed to extract error lesson:', err.message)
989
- }
990
- }
991
- }
980
+ // if (this._pendingErrors.length > 0) {
981
+ // const errors = this._pendingErrors.splice(0, 10) // 每次最多处理 10 条
982
+ // log.debug(`Processing ${errors.length} pending errors`)
983
+
984
+ // for (const errorData of errors) {
985
+ // try {
986
+ // await this._extractErrorLesson(errorData)
987
+ // } catch (err) {
988
+ // log.warn('Failed to extract error lesson:', err.message)
989
+ // }
990
+ // }
991
+ // }
992
992
  } finally {
993
993
  this._isExtracting = false
994
994
  }
@@ -79,6 +79,7 @@ class PluginManagerPlugin extends Plugin {
79
79
  this.description = '管理远程插件:列表、发布、安装';
80
80
  this._repo = config.repo || process.env.FOLIKO_PLUGIN_REPO || DEFAULT_REPO;
81
81
  this.system = true;
82
+ this.tools = {}; // ext_call 需要通过 this.tools 访问工具
82
83
  }
83
84
 
84
85
  install(framework) {
@@ -110,7 +110,7 @@ class SubAgentPlugin extends Plugin {
110
110
  tools:{}, // 自定义工具
111
111
  parentTools: all_tools
112
112
  })
113
-
113
+
114
114
 
115
115
  // 注册从父Agent继承的工具
116
116
  // for (const tool of parentTools) {
@@ -662,6 +662,60 @@ class SubAgentManagerPlugin extends Plugin {
662
662
  return config
663
663
  }
664
664
 
665
+
666
+ /**
667
+ * 构建子Agent描述
668
+ * 使用数组收集 + join 模式优化字符串拼接
669
+ * 动态从配置文件读取每个子Agent的专业领域
670
+ */
671
+ _buildDescription() {
672
+ const allSubAgents = this.getAllSubAgents();
673
+ if (!allSubAgents || allSubAgents.length === 0) {
674
+ return '';
675
+ }
676
+
677
+ // 使用数组收集 + join 模式优化字符串拼接
678
+ const lines = ['## 子 Agent 匹配表', ''];
679
+ lines.push('');
680
+ lines.push('> **提示**:根据任务类型选择最匹配的子 Agent 处理');
681
+ lines.push('');
682
+
683
+ // 动态从配置文件读取每个子Agent的核心能力
684
+ for (const plugin of allSubAgents) {
685
+ const name = plugin.name;
686
+ const role = plugin.role;
687
+ const goal = plugin.description || '';
688
+
689
+ // 优先使用 SubAgentConfigManager 获取详细信息
690
+ const config = this._framework._subAgentConfigManager?.get(name);
691
+ if (config) {
692
+ const agentDesc = config.getDescription();
693
+ const skills = config.getSkills();
694
+ let info = agentDesc || role || goal || '子代理';
695
+ if (skills && skills.length > 0) {
696
+ info += ` (技能: ${skills.join(', ')})`;
697
+ }
698
+ lines.push(`- **${name}**:${info}`);
699
+ } else {
700
+ // 回退到旧逻辑
701
+ lines.push(`- **${name}**:${role || goal || '子代理'}`);
702
+ }
703
+ }
704
+
705
+ // 通用规则
706
+ lines.push('');
707
+ lines.push('### 子 Agent 调用规则');
708
+ lines.push('');
709
+ lines.push('> **重要**:必须严格遵守以下规则');
710
+ lines.push('');
711
+ lines.push('1. 根据上述「子 Agent 匹配表」,将任务委托给最匹配的子 Agent 处理');
712
+ lines.push('2. 使用 `subagent_call` 工具并指定 `agentName` 来委托任务');
713
+ lines.push('3. 只有当没有匹配的子 Agent 时,才直接调用工具');
714
+ lines.push('4. 子 Agent 是没有记忆的,有需要的时候,有需要的时候,需要传递给上下文给子 Agent');
715
+
716
+ return lines.join('\n');
717
+ }
718
+
665
719
  /**
666
720
  * 获取所有 subAgent 的基本信息列表
667
721
  */
@@ -311,24 +311,27 @@ class TelegramPlugin extends Plugin {
311
311
  }
312
312
 
313
313
  try {
314
+ // 使用事件方式实时处理流式响应
315
+ const sessionScope = agent.createSessionScope(sessionId)
314
316
  let fullResponse = ''
315
- for await (const chunk of agent.chatStream(text, { sessionId })) {
317
+
318
+ sessionScope.on('stream:chunk', ({ chunk }) => {
316
319
  if (chunk.type === 'text' && chunk.text) {
317
320
  fullResponse += chunk.text
318
321
  if (fullResponse.length % 100 === 0) {
319
322
  try {
320
- await this._bot.editMessageText(`📝 ${escapeMarkdown(fullResponse)}▌`, {
323
+ this._bot.editMessageText(`📝 ${escapeMarkdown(fullResponse)}▌`, {
321
324
  chat_id: chatId,
322
325
  message_id: thinkingMsg.message_id
323
326
  })
324
327
  } catch (e) { /* ignore */ }
325
328
  }
326
329
  }
327
- }
330
+ })
331
+
332
+ await agent.sendMessage(text, { sessionId })
328
333
 
329
- // if (this._sessionPlugin) {
330
- // this._sessionPlugin.addMessage(sessionId, { role: 'assistant', content: fullResponse })
331
- // }
334
+ sessionScope.removeAllListeners()
332
335
 
333
336
  const safeResponse = escapeMarkdown(fullResponse) || '抱歉,我没有收到有效的回复。'
334
337
  await this._bot.editMessageText(safeResponse, {