foliko 1.1.19 → 1.1.20

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.
@@ -0,0 +1,47 @@
1
+ [
2
+ {
3
+ "id": "id_1776246133988_ZqwPPoS3v5c",
4
+ "title": "比特币价格监控",
5
+ "description": "监控BTC_USDT价格,当价格大于74000美元时发送通知",
6
+ "priority": 9,
7
+ "state": "active",
8
+ "actions": [
9
+ {
10
+ "id": "check_price",
11
+ "type": "tool",
12
+ "name": "gate_get_market_info",
13
+ "args": {
14
+ "currency_pair": "BTC_USDT",
15
+ "info_type": "tickers"
16
+ },
17
+ "completed": false,
18
+ "order": 0
19
+ },
20
+ {
21
+ "id": "notify",
22
+ "type": "message",
23
+ "content": "🔔 比特币价格提醒\n\n当前价格已突破 $74,000!\n\n请注意风险控制,合理安排投资。",
24
+ "completed": false,
25
+ "order": 1
26
+ }
27
+ ],
28
+ "conditions": {
29
+ "events": [
30
+ "tool:result"
31
+ ],
32
+ "toolNames": [
33
+ "gate_get_market_info",
34
+ "gate:get_market_info"
35
+ ]
36
+ },
37
+ "persistent": true,
38
+ "createdAt": "2026-04-15T09:42:13.988Z",
39
+ "updatedAt": "2026-04-15T09:42:16.578Z",
40
+ "activatedAt": "2026-04-15T09:42:16.578Z",
41
+ "completedAt": null,
42
+ "failedAt": null,
43
+ "attempts": 0,
44
+ "maxAttempts": 10,
45
+ "eventsReceived": []
46
+ }
47
+ ]
@@ -114,7 +114,7 @@
114
114
  }
115
115
  },
116
116
  "weixin": {
117
- "enabled": true,
117
+ "enabled": false,
118
118
  "config": {
119
119
  "forceLogin": false,
120
120
  "qrcodeTerminal": true,
@@ -0,0 +1,9 @@
1
+ ---
2
+ id: "mnzugpej-esdwsr"
3
+ name: "设计结构反馈"
4
+ type: "feedback"
5
+ project: "null"
6
+ tags: [设计, 反馈, 结构优化, UI]
7
+ created: "2026-04-15"
8
+ ---
9
+ 用户对当前设计(第二张)的结构不满意,希望获得更好看的设计方案。Why:影响用户体验和满意度,需要优化布局结构。How to apply:后续设计时优先考虑更优的排版和布局结构,可参考成功案例或询问用户偏好。
@@ -0,0 +1,9 @@
1
+ ---
2
+ id: "mnztftxj-af82rh"
3
+ name: "团队任务看板Web应用"
4
+ type: "project"
5
+ project: "团队任务看板"
6
+ tags: [Web应用, 团队协作, 任务管理, Team Lead]
7
+ created: "2026-04-15"
8
+ ---
9
+ 用户需要开发一个团队任务看板Web应用,核心功能包括任务分析和分配。Team Lead角色负责进行任务分析和分配工作。项目处于需求启动阶段。
@@ -0,0 +1,13 @@
1
+ ---
2
+ id: "mnzuh4cw-zvee8w"
3
+ name: "比特币价格监控偏好"
4
+ type: "user"
5
+ project: "null"
6
+ tags: [监控, 比特币, 通知策略]
7
+ created: "2026-04-15"
8
+ ---
9
+ 用户设置比特币价格监控,触发条件为价格大于75000元。通知策略:只在条件满足时通知,条件不满足时不通知(零打扰原则)。
10
+
11
+ Why: 明确用户的通知偏好,避免过度通知造成打扰。
12
+
13
+ How to apply: 创建监控目标时,自动应用此通知策略,不需要再次询问用户通知方式。
@@ -3,7 +3,248 @@
3
3
  "messages": [
4
4
  {
5
5
  "role": "user",
6
- "content": "获取今天国内,国外网站最新新闻,包括科技,财经,金融,数字货币等,最后根据选择最适合的设计系统风格,做成一个网页供我预览,要开启markdown获取"
6
+ "content": "创建 一个目标,监控比特币价格,当比特币价格大于74000的时候,发一个通知给我,达不到,不用发送"
7
+ },
8
+ {
9
+ "role": "assistant",
10
+ "content": [
11
+ {
12
+ "type": "text",
13
+ "text": "<think>用户想要创建一个监控比特币价格的目标,使用 Ambient Agent 来实现。当价格大于 74000 时发送通知,否则不发送。\n\n根据记忆中的偏好设置,用户已经有一个比特币价格监控的偏好:触发条件为价格大于75000元,通知策略是只在条件满足时通知(零打扰原则)。\n\n现在用户把条件改成了74000,我需要使用 ambient 插件的 ambient_goals 工具来创建这个目标。\n\n目标需要:\n1. 监听 gate-trading 工具的结果(获取比特币价格)\n2. 当价格大于 74000 时发送通知\n3. 条件不满足时不通知\n\n我需要创建一个持续监控的目标,使用 tool:result 事件来监听 gate_get_market_info 或 gate:get_market_info 工具的结果。\n\n让我创建这个目标:\n- 标题:比特币价格监控\n- 描述:监控BTC_USDT价格,超过74000时通知\n- 优先级:可以设高一些\n- 持久化:true(持续监控)\n- 条件:监听 gate_get_market_info 或 gate:get_market_info 工具结果\n- 动作:当价格 > 74000 时发送通知</think>\n\n\n\n\n"
14
+ },
15
+ {
16
+ "type": "tool-call",
17
+ "toolCallId": "call_01df52eedaa8432ba2668bd2",
18
+ "toolName": "ext_call",
19
+ "input": {
20
+ "plugin": "ambient",
21
+ "tool": "ambient_goals",
22
+ "args": {
23
+ "title": "比特币价格监控",
24
+ "description": "监控BTC_USDT价格,当价格大于74000美元时发送通知",
25
+ "priority": 9,
26
+ "persistent": true,
27
+ "actions": [
28
+ {
29
+ "type": "tool",
30
+ "plugin": "gate-trading",
31
+ "name": "gate_get_market_info",
32
+ "args": {
33
+ "currency_pair": "BTC_USDT"
34
+ }
35
+ },
36
+ {
37
+ "id": "notify",
38
+ "type": "message",
39
+ "content": "🔔 比特币价格提醒\n\n当前价格已突破 $74,000!\n\n请注意风险控制,合理安排投资。"
40
+ }
41
+ ],
42
+ "conditions": {
43
+ "events": [
44
+ "tool:result"
45
+ ],
46
+ "toolNames": [
47
+ "gate_get_market_info",
48
+ "gate:get_market_info"
49
+ ]
50
+ }
51
+ }
52
+ }
53
+ }
54
+ ]
55
+ },
56
+ {
57
+ "role": "tool",
58
+ "content": [
59
+ {
60
+ "type": "tool-result",
61
+ "toolCallId": "call_01df52eedaa8432ba2668bd2",
62
+ "toolName": "ext_call",
63
+ "output": {
64
+ "type": "text",
65
+ "value": "{\"success\":false,\"error\":\"未知操作: undefined\"}"
66
+ }
67
+ }
68
+ ]
69
+ },
70
+ {
71
+ "role": "assistant",
72
+ "content": [
73
+ {
74
+ "type": "text",
75
+ "text": "<think>看起来参数格式有问题,让我调整一下。action 应该是 create,不是这样的格式。让我重新看一下工具定义。\n\nambient_goals 的参数:\n- action: create\n- actions: 要执行的操作列表\n- conditions: 激活条件\n- description: 目标描述\n- persistent: 是否持久化\n- priority: 优先级\n- title: 目标标题\n\n让我重新调用。</think>\n\n\n\n\n"
76
+ },
77
+ {
78
+ "type": "tool-call",
79
+ "toolCallId": "call_ff4efb2b347845078e57cdec",
80
+ "toolName": "ext_call",
81
+ "input": {
82
+ "plugin": "ambient",
83
+ "tool": "ambient_goals",
84
+ "args": {
85
+ "action": "create",
86
+ "actions": [
87
+ {
88
+ "id": "check_price",
89
+ "type": "tool",
90
+ "plugin": "gate-trading",
91
+ "name": "gate_get_market_info",
92
+ "args": {
93
+ "currency_pair": "BTC_USDT",
94
+ "info_type": "tickers"
95
+ }
96
+ },
97
+ {
98
+ "id": "notify",
99
+ "type": "message",
100
+ "content": "🔔 比特币价格提醒\n\n当前价格已突破 $74,000!\n\n请注意风险控制,合理安排投资。"
101
+ }
102
+ ],
103
+ "conditions": {
104
+ "events": [
105
+ "tool:result"
106
+ ],
107
+ "toolNames": [
108
+ "gate_get_market_info",
109
+ "gate:get_market_info"
110
+ ]
111
+ },
112
+ "description": "监控BTC_USDT价格,当价格大于74000美元时发送通知",
113
+ "persistent": true,
114
+ "priority": 9,
115
+ "title": "比特币价格监控"
116
+ }
117
+ }
118
+ }
119
+ ]
120
+ },
121
+ {
122
+ "role": "tool",
123
+ "content": [
124
+ {
125
+ "type": "tool-result",
126
+ "toolCallId": "call_ff4efb2b347845078e57cdec",
127
+ "toolName": "ext_call",
128
+ "output": {
129
+ "type": "text",
130
+ "value": "{\"success\":true,\"result\":{\"success\":true,\"goal\":{\"id\":\"id_1776246133988_ZqwPPoS3v5c\",\"title\":\"比特币价格监控\",\"description\":\"监控BTC_USDT价格,当价格大于74000美元时发送通知\",\"priority\":9,\"state\":\"pending\",\"actions\":[{\"id\":\"check_price\",\"type\":\"tool\",\"plugin\":\"gate-trading\",\"name\":\"gate_get_market_info\",\"args\":{\"currency_pair\":\"BTC_USDT\",\"info_type\":\"tickers\"},\"completed\":false,\"order\":0},{\"id\":\"notify\",\"type\":\"message\",\"content\":\"🔔 比特币价格提醒\\n\\n当前价格已突破 $74,000!\\n\\n请注意风险控制,合理安排投资。\",\"completed\":false,\"order\":1}],\"conditions\":{\"events\":[\"tool:result\"],\"toolNames\":[\"gate_get_market_info\",\"gate:get_market_info\"]},\"persistent\":true,\"createdAt\":\"2026-04-15T09:42:13.988Z\",\"updatedAt\":\"2026-04-15T09:42:13.988Z\",\"activatedAt\":null,\"completedAt\":null,\"failedAt\":null,\"attempts\":0,\"maxAttempts\":10,\"consecutiveSameActions\":0,\"lastActionId\":null,\"eventsReceived\":[]}}}"
131
+ }
132
+ }
133
+ ]
134
+ },
135
+ {
136
+ "role": "assistant",
137
+ "content": [
138
+ {
139
+ "type": "text",
140
+ "text": "<think>目标创建成功了。但是我注意到 Ambient Agent 需要激活目标才会开始执行。让我激活这个目标。</think>\n\n\n\n\n"
141
+ },
142
+ {
143
+ "type": "tool-call",
144
+ "toolCallId": "call_bf56f77bc0174aaab67b1835",
145
+ "toolName": "ext_call",
146
+ "input": {
147
+ "plugin": "ambient",
148
+ "tool": "ambient_goals",
149
+ "args": {
150
+ "action": "activate",
151
+ "goalId": "id_1776246133988_ZqwPPoS3v5c"
152
+ }
153
+ }
154
+ }
155
+ ]
156
+ },
157
+ {
158
+ "role": "tool",
159
+ "content": [
160
+ {
161
+ "type": "tool-result",
162
+ "toolCallId": "call_bf56f77bc0174aaab67b1835",
163
+ "toolName": "ext_call",
164
+ "output": {
165
+ "type": "text",
166
+ "value": "{\"success\":false,\"error\":\"目标未找到或不是待激活状态\"}"
167
+ }
168
+ }
169
+ ]
170
+ },
171
+ {
172
+ "role": "assistant",
173
+ "content": [
174
+ {
175
+ "type": "text",
176
+ "text": "<think>目标可能是状态问题,让我先查看一下当前状态,然后检查 Ambient Agent 状态。</think>\n\n\n\n\n"
177
+ },
178
+ {
179
+ "type": "tool-call",
180
+ "toolCallId": "call_269a1804b5d248e3b22967fe",
181
+ "toolName": "ext_call",
182
+ "input": {
183
+ "plugin": "ambient",
184
+ "tool": "ambient_status",
185
+ "args": {}
186
+ }
187
+ }
188
+ ]
189
+ },
190
+ {
191
+ "role": "tool",
192
+ "content": [
193
+ {
194
+ "type": "tool-result",
195
+ "toolCallId": "call_269a1804b5d248e3b22967fe",
196
+ "toolName": "ext_call",
197
+ "output": {
198
+ "type": "text",
199
+ "value": "{\"success\":true,\"result\":{\"success\":true,\"loop\":{\"running\":true,\"tickCount\":15,\"lastTick\":\"2026-04-15T09:42:21.583Z\",\"tickInterval\":5000,\"cooldownPeriod\":3000},\"activeGoals\":[{\"id\":\"id_1776246133988_ZqwPPoS3v5c\",\"title\":\"比特币价格监控\",\"priority\":9,\"attempts\":0,\"lastActionId\":null}],\"pendingGoals\":[],\"recentActivities\":[{\"type\":\"goal_activated\",\"data\":{\"goalId\":\"id_1776246133988_ZqwPPoS3v5c\",\"reason\":\"事件监听目标已激活\"},\"timestamp\":\"2026-04-15T09:42:16.578Z\"}]}}"
200
+ }
201
+ }
202
+ ]
203
+ },
204
+ {
205
+ "role": "assistant",
206
+ "content": [
207
+ {
208
+ "type": "text",
209
+ "text": "<think>目标已经激活了!状态显示 running: true,目标已经在 activeGoals 中。现在让我获取一下当前的比特币价格,让用户知道当前状态。</think>\n\n\n\n目标已创建并激活成功!\n\n**📊 当前监控状态:**\n- **目标名称:** 比特币价格监控\n- **监控条件:** BTC_USDT 价格 > $74,000\n- **通知策略:** 仅在条件满足时通知(零打扰原则)\n- **当前状态:** ✅ 已激活,持续监控中\n\n让我先查一下当前比特币价格:\n"
210
+ },
211
+ {
212
+ "type": "tool-call",
213
+ "toolCallId": "call_467b5f86ec3a4e369c0b3722",
214
+ "toolName": "ext_call",
215
+ "input": {
216
+ "plugin": "gate-trading",
217
+ "tool": "gate_get_market_info",
218
+ "args": {
219
+ "currency_pair": "BTC_USDT",
220
+ "info_type": "tickers"
221
+ }
222
+ }
223
+ }
224
+ ]
225
+ },
226
+ {
227
+ "role": "tool",
228
+ "content": [
229
+ {
230
+ "type": "tool-result",
231
+ "toolCallId": "call_467b5f86ec3a4e369c0b3722",
232
+ "toolName": "ext_call",
233
+ "output": {
234
+ "type": "text",
235
+ "value": "{\"success\":true,\"result\":{\"success\":true,\"type\":\"ticker\",\"currency_pair\":\"BTC_USDT\",\"last\":\"73957.6\",\"high_24h\":\"76043.6\",\"low_24h\":\"73510.1\",\"volume_24h\":\"7481.237734\",\"quote_volume_24h\":\"557518108.4613161\",\"change_percentage_24h\":\"-0.79\"}}"
236
+ }
237
+ }
238
+ ]
239
+ },
240
+ {
241
+ "role": "assistant",
242
+ "content": [
243
+ {
244
+ "type": "text",
245
+ "text": "<think>当前比特币价格是 $73,957.6,低于 $74,000 的阈值,所以不会发送通知。等价格突破 $74,000 时,Ambient Agent 会自动检测到并发送通知。\n</think>\n\n\n\n**💰 当前比特币价格:$73,957.6**\n\n当前价格尚未达到 $74,000 的触发阈值,暂时不会发送通知。当价格突破 $74,000 时,系统会自动发送通知提醒你。\n\n Ambient Agent 正在后台持续监控,无需额外操作。🔄"
246
+ }
247
+ ]
7
248
  }
8
249
  ],
9
250
  "variables": {},
@@ -202,7 +202,9 @@
202
202
  "Bash(node -e \"\nconst { createTools } = require\\('ai'\\);\nconsole.log\\('createTools exists:', typeof createTools\\);\n\" 2>&1)",
203
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
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\\)\")"
205
+ "Bash(node -e \"const ai = require\\('ai'\\); console.log\\('tool type:', typeof ai.tool\\)\")",
206
+ "Bash(taskkill //PID 26152 //F)",
207
+ "Bash(netstat -ano | grep 3000)"
206
208
  ]
207
209
  }
208
210
  }
@@ -198,9 +198,15 @@ class ChatUI {
198
198
  }
199
199
  }
200
200
  } catch (err) {
201
- console.log(err);
201
+ // 只打印简洁错误消息,不打印堆栈
202
+ const errName = err?.name || '';
203
+ const isRetryError = errName === 'AI_RetryError' || errName === 'RetryError';
204
+ const friendlyMessage = isRetryError
205
+ ? 'AI 服务暂时不可用,请稍后重试'
206
+ : (err.message || '未知错误').split('\n')[0];
207
+
202
208
  if (!interrupted) {
203
- console.error(`\n${colored('[错误]', RED)} ${err.message}\n`);
209
+ console.error(`\n${colored('[错误]', RED)} ${friendlyMessage}\n`);
204
210
  }
205
211
  } finally {
206
212
  process.removeListener('SIGINT', interruptHandler);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "foliko",
3
- "version": "1.1.19",
3
+ "version": "1.1.20",
4
4
  "description": "简约的插件化 Agent 框架",
5
5
  "main": "src/index.js",
6
6
  "type": "commonjs",
@@ -581,11 +581,12 @@ class SchedulerPlugin extends Plugin {
581
581
  // }
582
582
 
583
583
  // 根据 notify 参数决定是否发送通知
584
- if (task.notify !== false) {
584
+ // 只有当 LLM 返回了有效消息时才发送通知
585
+ if (task.notify !== false && responseText && responseText.trim()) {
585
586
  // 发送统一的通知事件
586
587
  this._framework.emit('notification', {
587
588
  title: task.name,
588
- message: responseText || task.message,
589
+ message: responseText,
589
590
  source: 'scheduler',
590
591
  level: 'info',
591
592
  sessionId: task.sessionId,
@@ -556,7 +556,7 @@ class WeixinPlugin extends Plugin {
556
556
  }else{
557
557
  const msg = `继续进行下一步`
558
558
  await this._sendMessageBatch(originalMsg, userId, msg, true)
559
- agent.sendMessage(msg, { sessionId, priority: 1 })
559
+ await agent.sendMessage(msg, { sessionId, priority: 1 })
560
560
  }
561
561
  lineBuffer = ''
562
562
  message=''
@@ -324,11 +324,10 @@ class AgentChatHandler extends EventEmitter {
324
324
  executeFunction: this.chatStream.bind(this),
325
325
  })
326
326
  .catch((err) => {
327
- logger.error('[AgentChat] Queue promise rejected:', err.message);
327
+ // 只设置 streamError,不打印日志(避免重复)
328
328
  if (!streamError) {
329
329
  streamError = err;
330
330
  }
331
- throw err; // 重新抛出,让 await queuePromise 能捕获
332
331
  });
333
332
 
334
333
  try {
@@ -489,6 +488,7 @@ class AgentChatHandler extends EventEmitter {
489
488
  AI_RetryError: 'AI 服务暂时不可用,请稍后重试',
490
489
  AI_APICallError: err?.isRetryable ? 'AI 服务暂时不可用,请稍后重试' : simpleMessage,
491
490
  AI_NoContentGeneratedError: 'AI 未生成有效内容,请重试',
491
+ AI_NoOutputGeneratedError: 'AI 未生成有效内容,请重试',
492
492
  AI_NoSuchModelError: '指定的 AI 模型不存在',
493
493
  AI_NoSuchProviderError: 'AI 提供商配置错误',
494
494
  AI_LoadAPIKeyError: 'AI API 密钥配置错误',
@@ -499,7 +499,7 @@ class AgentChatHandler extends EventEmitter {
499
499
  const friendlyMessage =
500
500
  errorMessages[errName] || (isRetryError ? 'AI 服务暂时不可用,请稍后重试' : simpleMessage);
501
501
 
502
- logger.error(`[AgentChat] Error [${errName}]: ${simpleMessage}`);
502
+ //logger.error(`[AgentChat] Error [${errName}]: ${simpleMessage}`);
503
503
  this.emit('message:error', { sessionId, error: friendlyMessage });
504
504
  yield { type: 'error', error: friendlyMessage };
505
505
  } finally {
@@ -681,7 +681,7 @@ class AgentChatHandler extends EventEmitter {
681
681
 
682
682
  // 执行工具
683
683
  this.emit('tool-call', { name: toolName, args: cleanedArgs });
684
- logger.info(`[AgentChat] Calling tool: ${toolName}`);
684
+ logger.info(`[Tool] Call: ${toolName}`);
685
685
  try {
686
686
  const result = await toolDef.execute(cleanedArgs, this.agent.framework);
687
687
  this.emit('tool-result', { name: toolName, args: cleanedArgs, result });
@@ -6,8 +6,9 @@
6
6
 
7
7
  const { EventEmitter } = require('../utils/event-emitter');
8
8
  const { cleanResponse } = require('../utils');
9
- const { generateText, tool, stepCountIs, isLoopFinished } = require('ai');
9
+ const { generateText, tool, stepCountIs, isLoopFinished, RetryError } = require('ai');
10
10
  const { z } = require('zod');
11
+ const { logger } = require('../utils/logger');
11
12
 
12
13
  class Subagent extends EventEmitter {
13
14
  /**
@@ -217,47 +218,90 @@ class Subagent extends EventEmitter {
217
218
  * @param {Object} [options] - 选项
218
219
  * @param {number} [options.maxSteps] - 最大步数
219
220
  * @param {AbortSignal} [options.signal] - 中止信号
221
+ * @param {number} [options.maxRetries] - 最大重试次数
220
222
  * @returns {Promise<{success: boolean, message: string, steps: number}>}
221
223
  */
222
224
  async chat(task, options = {}) {
223
225
  const maxSteps = options?.maxSteps || 30;
226
+ const maxRetries = options?.maxRetries ?? 2;
224
227
  const aiProvider = this._getAIProvider();
225
228
  const messages = [];
226
229
  messages.push({ role: 'user', content: task });
227
- try {
228
- const tools = this._buildAITools();
229
- const systemPrompt = this._buildSystemPrompt();
230
- const result = await generateText({
231
- model: aiProvider(this.model),
232
- system: systemPrompt,
233
- messages: messages,
234
- tools: tools,
235
- stopWhen: stepCountIs(maxSteps),
236
- ...this.providerOptions,
237
- abortSignal: options.signal,
238
- onChunk: (chunk) => {
239
- this.emit('chunk', chunk);
240
- },
241
- });
242
- messages.push(...result.response.messages);
243
- const full_text = cleanResponse(result.text);
244
- this.emit('complete', { message: full_text, steps: result.steps?.length || 0 });
245
- return {
246
- success: true,
247
- message: full_text,
248
- steps: result.steps?.length || 0,
249
- };
250
- } catch (err) {
251
- this.emit('error', { error: err.message });
252
- return {
253
- success: false,
254
- message: '',
255
- error: err.message,
256
- steps: 0,
257
- };
230
+
231
+ let lastError;
232
+ for (let attempt = 0; attempt <= maxRetries; attempt++) {
233
+ try {
234
+ const tools = this._buildAITools();
235
+ const systemPrompt = this._buildSystemPrompt();
236
+ const result = await generateText({
237
+ model: aiProvider(this.model),
238
+ system: systemPrompt,
239
+ messages: messages,
240
+ tools: tools,
241
+ stopWhen: stepCountIs(maxSteps),
242
+ ...this.providerOptions,
243
+ abortSignal: options.signal,
244
+ onChunk: (chunk) => {
245
+ this.emit('chunk', chunk);
246
+ },
247
+ });
248
+ messages.push(...result.response.messages);
249
+ const full_text = cleanResponse(result.text);
250
+ this.emit('complete', { message: full_text, steps: result.steps?.length || 0 });
251
+ return {
252
+ success: true,
253
+ message: full_text,
254
+ steps: result.steps?.length || 0,
255
+ };
256
+ } catch (err) {
257
+ lastError = err;
258
+ const errName = err?.name || '';
259
+
260
+ // 判断是否是重试错误
261
+ const isRetryError =
262
+ RetryError.isInstance?.(err) ||
263
+ errName === 'AI_RetryError' ||
264
+ errName === 'RetryError' ||
265
+ err?.reason === 'maxRetriesExceeded';
266
+
267
+ if (isRetryError && attempt < maxRetries) {
268
+ logger.warn(`[Subagent:${this.name}] AI 服务不可用,${attempt + 1}/${maxRetries} 次重试`);
269
+ await new Promise((resolve) => setTimeout(resolve, 1000 * Math.pow(2, attempt)));
270
+ continue;
271
+ }
272
+
273
+ // 最终失败,转换为友好消息
274
+ const friendlyMessage = this._getFriendlyError(err);
275
+ this.emit('error', { error: friendlyMessage });
276
+ return {
277
+ success: false,
278
+ message: '',
279
+ error: friendlyMessage,
280
+ steps: 0,
281
+ };
282
+ }
258
283
  }
259
284
  }
260
285
 
286
+ /**
287
+ * 获取友好错误消息
288
+ * @private
289
+ */
290
+ _getFriendlyError(err) {
291
+ const errName = err?.name || '';
292
+ const errorMessages = {
293
+ AI_RetryError: 'AI 服务暂时不可用,请稍后重试',
294
+ AI_APICallError: err?.isRetryable
295
+ ? 'AI 服务暂时不可用,请稍后重试'
296
+ : err.message?.split('\n')[0] || 'AI 请求失败',
297
+ AI_NoContentGeneratedError: 'AI 未生成有效内容,请重试',
298
+ AI_NoSuchModelError: '指定的 AI 模型不存在',
299
+ AI_NoSuchProviderError: 'AI 提供商配置错误',
300
+ AI_LoadAPIKeyError: 'AI API 密钥配置错误',
301
+ };
302
+ return errorMessages[errName] || err.message?.split('\n')[0] || '未知错误';
303
+ }
304
+
261
305
  /**
262
306
  * 设置角色(会清除自定义 systemPrompt)
263
307
  * @param {string} role - 角色描述
@@ -70,13 +70,25 @@ class ChatQueueManager extends EventEmitter {
70
70
 
71
71
  try {
72
72
  const result = await this.executeWithRetry(item);
73
- item.resolve(result);
74
- this.emit('queue:completed', {
75
- requestId: item.id,
76
- sessionId: item.sessionId,
77
- duration: Date.now() - item.timestamp,
78
- });
73
+ // 检查 result 是否有错误(而不是通过 try/catch)
74
+ if (result.error) {
75
+ console.log('[ChatQueue] Rejecting with error from result:', result.error.message);
76
+ item.reject(result.error);
77
+ this.emit('queue:failed', {
78
+ requestId: item.id,
79
+ sessionId: item.sessionId,
80
+ error: result.error.message,
81
+ });
82
+ } else {
83
+ item.resolve(result);
84
+ this.emit('queue:completed', {
85
+ requestId: item.id,
86
+ sessionId: item.sessionId,
87
+ duration: Date.now() - item.timestamp,
88
+ });
89
+ }
79
90
  } catch (error) {
91
+ console.log('[ChatQueue] Rejecting with thrown error:', error.message);
80
92
  item.reject(error);
81
93
  this.emit('queue:failed', {
82
94
  requestId: item.id,
@@ -103,8 +115,24 @@ class ChatQueueManager extends EventEmitter {
103
115
  maxRetries: this.retryAttempts,
104
116
  });
105
117
 
106
- return await this.executeStream(item);
118
+ const result = await this.executeStream(item);
119
+
120
+ // 检查是否有错误(通过返回的 result.error)
121
+ if (result.error) {
122
+ console.log('[ChatQueue] executeWithRetry: attempt', attempt, 'got error:', result.error.message);
123
+ lastError = result.error;
124
+ if (attempt < this.retryAttempts && this.isRetryableError(lastError)) {
125
+ await this.sleep(this.retryDelay * Math.pow(2, attempt - 1));
126
+ continue;
127
+ }
128
+ // 返回 result,让 processQueue 处理错误
129
+ console.log('[ChatQueue] executeWithRetry: returning result with error, no more retries');
130
+ return result;
131
+ }
132
+
133
+ return result;
107
134
  } catch (error) {
135
+ console.log('[ChatQueue] executeWithRetry: attempt', attempt, 'threw error:', error.message);
108
136
  lastError = error;
109
137
  if (attempt < this.retryAttempts && this.isRetryableError(error)) {
110
138
  await this.sleep(this.retryDelay * Math.pow(2, attempt - 1));
@@ -113,7 +141,18 @@ class ChatQueueManager extends EventEmitter {
113
141
  break;
114
142
  }
115
143
  }
116
- throw lastError;
144
+
145
+ // 将最后的错误转换为友好消息
146
+ const errName = lastError?.name || '';
147
+ const isRetryError = errName === 'AI_RetryError' || errName === 'RetryError';
148
+ const friendlyMessage = isRetryError
149
+ ? 'AI 服务暂时不可用,请稍后重试'
150
+ : (lastError?.message || String(lastError)).split('\n')[0];
151
+
152
+ const friendlyError = new Error(friendlyMessage);
153
+ friendlyError.originalError = lastError;
154
+ console.log('[ChatQueue] executeWithRetry: throwing friendly error:', friendlyMessage);
155
+ throw friendlyError;
117
156
  }
118
157
 
119
158
  /**
@@ -128,12 +167,31 @@ class ChatQueueManager extends EventEmitter {
128
167
 
129
168
  const stream = item.executeFunction(item.message, item.options);
130
169
 
131
- for await (const chunk of stream) {
132
- chunks.push(chunk);
170
+ try {
171
+ for await (const chunk of stream) {
172
+ chunks.push(chunk);
173
+ this.emit('stream:chunk', {
174
+ requestId: item.id,
175
+ sessionId: item.sessionId,
176
+ chunk,
177
+ accumulated: chunks.length,
178
+ });
179
+ }
180
+ } catch (err) {
181
+ // SDK 直接抛出错误(没有通过 chunk 传递)
182
+ // 转换为友好错误消息
183
+ const errName = err?.name || '';
184
+ const isRetryError = errName === 'AI_RetryError' || errName === 'RetryError';
185
+ const friendlyMessage = isRetryError
186
+ ? 'AI 服务暂时不可用,请稍后重试'
187
+ : (err.message || err.toString()).split('\n')[0];
188
+
189
+ console.log('[ChatQueue] executeStream caught error, converting to friendly:', friendlyMessage);
190
+ chunks.push({ type: 'error', error: friendlyMessage });
133
191
  this.emit('stream:chunk', {
134
192
  requestId: item.id,
135
193
  sessionId: item.sessionId,
136
- chunk,
194
+ chunk: chunks[chunks.length - 1],
137
195
  accumulated: chunks.length,
138
196
  });
139
197
  }
@@ -141,9 +199,19 @@ class ChatQueueManager extends EventEmitter {
141
199
  // 检查是否有错误 chunk
142
200
  const errorChunk = chunks.find((c) => c.type === 'error');
143
201
  if (errorChunk) {
202
+ // 不再抛出错误,而是返回包含错误的 result
203
+ // 让调用者通过 result 判断是否有错误
144
204
  const error = new Error(errorChunk.error || 'Stream error');
145
205
  error.chunks = chunks;
146
- throw error;
206
+ error.isStreamError = true;
207
+ console.log('[ChatQueue] executeStream returning result with error:', error.message);
208
+ return {
209
+ chunks,
210
+ content: cleanResponse(''),
211
+ sessionId: item.sessionId,
212
+ requestId: item.id,
213
+ error,
214
+ };
147
215
  }
148
216
 
149
217
  const fullText = chunks