foliko 1.0.87 → 1.1.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 (104) hide show
  1. package/.agent/data/default.json +3 -108
  2. package/.agent/data/plugins-state.json +34 -1
  3. package/.agent/mcp_config.json +0 -1
  4. package/.agent/memory/core.md +1 -0
  5. package/.agent/memory/project/mnn93ogy-ypjn27.md +9 -0
  6. package/.agent/memory/project/mnn98fqy-5nhc1u.md +25 -0
  7. package/.agent/memory/user/mnm67t9m-x8rekk.md +9 -0
  8. package/.agent/memory/user/mnn5mmqh-w6aktx.md +11 -0
  9. package/.agent/memory/user/mnnbfhhn-dk1bd1.md +22 -0
  10. package/.agent/plugins/__pycache__/file_writer.cpython-312.pyc +0 -0
  11. package/.agent/plugins/poster-plugin/README.md +304 -0
  12. package/.agent/plugins/poster-plugin/fonts/PatuaOne-Regular.ttf +0 -0
  13. package/.agent/plugins/poster-plugin/fonts//345/276/256/350/275/257/351/233/205/351/273/221.ttf +0 -0
  14. package/.agent/plugins/poster-plugin/fonts//345/276/256/350/275/257/351/233/205/351/273/221/347/262/227/344/275/223.ttf +0 -0
  15. package/.agent/plugins/poster-plugin/index.js +13 -0
  16. package/.agent/plugins/poster-plugin/package.json +28 -0
  17. package/.agent/plugins/poster-plugin/src/canvas.js +161 -0
  18. package/.agent/plugins/poster-plugin/src/components/arrow.js +84 -0
  19. package/.agent/plugins/poster-plugin/src/components/avatar.js +71 -0
  20. package/.agent/plugins/poster-plugin/src/components/badge.js +85 -0
  21. package/.agent/plugins/poster-plugin/src/components/card.js +88 -0
  22. package/.agent/plugins/poster-plugin/src/components/chart.js +127 -0
  23. package/.agent/plugins/poster-plugin/src/components/chip.js +88 -0
  24. package/.agent/plugins/poster-plugin/src/components/columns.js +107 -0
  25. package/.agent/plugins/poster-plugin/src/components/cta.js +85 -0
  26. package/.agent/plugins/poster-plugin/src/components/divider.js +55 -0
  27. package/.agent/plugins/poster-plugin/src/components/feature.js +85 -0
  28. package/.agent/plugins/poster-plugin/src/components/featureGrid.js +112 -0
  29. package/.agent/plugins/poster-plugin/src/components/grid.js +118 -0
  30. package/.agent/plugins/poster-plugin/src/components/imageFrame.js +155 -0
  31. package/.agent/plugins/poster-plugin/src/components/index.js +62 -0
  32. package/.agent/plugins/poster-plugin/src/components/listItem.js +146 -0
  33. package/.agent/plugins/poster-plugin/src/components/notification.js +123 -0
  34. package/.agent/plugins/poster-plugin/src/components/progress.js +79 -0
  35. package/.agent/plugins/poster-plugin/src/components/progressCircle.js +117 -0
  36. package/.agent/plugins/poster-plugin/src/components/quote.js +97 -0
  37. package/.agent/plugins/poster-plugin/src/components/rating.js +85 -0
  38. package/.agent/plugins/poster-plugin/src/components/star.js +70 -0
  39. package/.agent/plugins/poster-plugin/src/components/statCard.js +105 -0
  40. package/.agent/plugins/poster-plugin/src/components/stepper.js +118 -0
  41. package/.agent/plugins/poster-plugin/src/components/table.js +159 -0
  42. package/.agent/plugins/poster-plugin/src/components/tagCloud.js +78 -0
  43. package/.agent/plugins/poster-plugin/src/components/timeline.js +105 -0
  44. package/.agent/plugins/poster-plugin/src/components/watermark.js +52 -0
  45. package/.agent/plugins/poster-plugin/src/composer.js +1904 -0
  46. package/.agent/plugins/poster-plugin/src/elements/artText.js +60 -0
  47. package/.agent/plugins/poster-plugin/src/elements/background.js +52 -0
  48. package/.agent/plugins/poster-plugin/src/elements/circle.js +31 -0
  49. package/.agent/plugins/poster-plugin/src/elements/image.js +71 -0
  50. package/.agent/plugins/poster-plugin/src/elements/index.js +26 -0
  51. package/.agent/plugins/poster-plugin/src/elements/line.js +23 -0
  52. package/.agent/plugins/poster-plugin/src/elements/polygon.js +32 -0
  53. package/.agent/plugins/poster-plugin/src/elements/rectangle.js +32 -0
  54. package/.agent/plugins/poster-plugin/src/elements/svg.js +92 -0
  55. package/.agent/plugins/poster-plugin/src/elements/text.js +38 -0
  56. package/.agent/plugins/poster-plugin/src/fonts.js +118 -0
  57. package/.agent/plugins/poster-plugin/src/index.js +1659 -0
  58. package/.agent/plugins/poster-plugin/src/presets.js +36 -0
  59. package/.agent/plugins/poster-plugin/src/templates/business.js +60 -0
  60. package/.agent/plugins/poster-plugin/src/templates/gradient.js +64 -0
  61. package/.agent/plugins/poster-plugin/src/templates/index.js +43 -0
  62. package/.agent/plugins/poster-plugin/src/templates/modern.js +69 -0
  63. package/.agent/plugins/poster-plugin/src/templates/simple.js +58 -0
  64. package/.agent/plugins/poster-plugin/src/templates/social.js +62 -0
  65. package/.agent/plugins/poster-plugin/src/templates/tech.js +84 -0
  66. package/.agent/sessions/cli_default.json +24265 -0
  67. package/.agent/weixin.json +6 -0
  68. package/.claude/settings.local.json +5 -8
  69. package/CLAUDE.md +144 -108
  70. package/docs/CONTEXT_DESIGN.md +1596 -0
  71. package/examples/test-concurrent-chat.js +60 -60
  72. package/output/beef-love-poster.png +0 -0
  73. package/package.json +2 -2
  74. package/plugins/default-plugins.js +2 -1
  75. package/plugins/extension-executor-plugin.js +11 -0
  76. package/plugins/memory-plugin.js +984 -0
  77. package/plugins/session-plugin.js +57 -1
  78. package/plugins/weixin-plugin.js +24 -22
  79. package/skills/poster-guide/SKILL.md +743 -0
  80. package/skills/python-plugin-dev/SKILL.md +238 -238
  81. package/skills/skill-guide/SKILL.md +130 -108
  82. package/src/capabilities/skill-manager.js +99 -0
  83. package/src/core/agent-chat.js +538 -138
  84. package/src/core/agent-context.js +188 -0
  85. package/src/core/agent.js +6 -2
  86. package/src/core/context-manager.js +283 -0
  87. package/src/core/framework.js +264 -3
  88. package/src/core/plugin-manager.js +79 -2
  89. package/src/core/request-context.js +98 -0
  90. package/src/core/session-context.js +341 -0
  91. package/src/core/session-storage.js +274 -0
  92. package/src/executors/mcp-executor.js +2 -2
  93. package/src/utils/index.js +239 -67
  94. package/src/utils/plugin-helpers.js +17 -0
  95. package//346/265/267/346/212/245/346/217/222/344/273/266.md +621 -0
  96. package/.agent/plugins/__pycache__/test_plugin.cpython-312.pyc +0 -0
  97. package/.agent/plugins/temp-repo/LICENSE +0 -201
  98. package/.agent/plugins/temp-repo/puppeteer-plugin/README.md +0 -147
  99. package/.agent/plugins/temp-repo/puppeteer-plugin/index.js +0 -1418
  100. package/.agent/plugins/temp-repo/puppeteer-plugin/package.json +0 -9
  101. package/.agent/plugins/test_plugin.py +0 -304
  102. package/examples/test-chat-debug.js +0 -102
  103. package/examples/test-chat-result.js +0 -76
  104. package/examples/test-chat-stream-diff.js +0 -63
@@ -7,6 +7,23 @@ const { EventEmitter } = require('../utils/event-emitter');
7
7
  const { logger } = require('../utils/logger');
8
8
  const { generateText, stepCountIs } = require('ai');
9
9
  const { prepareMessagesForAPI } = require('../utils');
10
+ const fs = require('fs/promises');
11
+
12
+ /**
13
+ * 从消息中提取 toolCallId(兼容两种格式)
14
+ * 格式1: msg.toolCallId (旧格式)
15
+ * 格式2: msg.content[0].toolCallId (AI SDK CoreMessage 格式)
16
+ * @param {Object} msg - 消息对象
17
+ * @returns {string|null}
18
+ */
19
+ function extractToolCallId(msg) {
20
+ if (msg.toolCallId) return msg.toolCallId;
21
+ if (Array.isArray(msg.content) && msg.content.length > 0) {
22
+ const firstContent = msg.content[0];
23
+ if (firstContent && firstContent.toolCallId) return firstContent.toolCallId;
24
+ }
25
+ return null;
26
+ }
10
27
 
11
28
  // 模型上下文限制表(留 15-20% 余量给 system prompt 和输出)
12
29
  const MODEL_CONTEXT_LIMITS = {
@@ -96,7 +113,8 @@ class AgentChatHandler extends EventEmitter {
96
113
  this.providerOptions = config.providerOptions || {};
97
114
 
98
115
  this._systemPrompt = config.systemPrompt || 'You are a helpful assistant.';
99
- this._messages = [];
116
+ // this._messages = []; // 移除共享消息存储!改用 Per-Session
117
+ this._sessionMessageStores = new Map(); // sessionId → { messages: [], historyLoaded: false, compressionState: {} }
100
118
  this._tools = new Map();
101
119
  this._maxSteps = 20; // 降低默认步骤数,减少上下文消耗
102
120
 
@@ -127,10 +145,58 @@ class AgentChatHandler extends EventEmitter {
127
145
  // Per-Session 队列管理
128
146
  this._sessionQueues = new Map(); // sessionId -> Queue of {message, options, resolve, reject}
129
147
  this._processingSessions = new Set(); // sessionId -> processing flag
148
+
149
+ // Session Memory prepareStep(从 memory 插件获取)
150
+ this._sessionMemoryPrepareStep = null;
151
+ if (agent?.framework) {
152
+ const memoryPlugin = agent.framework.pluginManager.get('memory');
153
+ if (memoryPlugin?.instance?.getPrepareStep) {
154
+ this._sessionMemoryPrepareStep = memoryPlugin.instance.getPrepareStep();
155
+ logger.debug('Session memory prepareStep loaded');
156
+ }
157
+ }
158
+ }
159
+
160
+ /**
161
+ * 获取 Per-Session 的消息存储
162
+ * @param {string} sessionId - 会话 ID
163
+ * @returns {Object} 消息存储对象
164
+ * @private
165
+ */
166
+ _getSessionMessageStore(sessionId) {
167
+ if (!sessionId) {
168
+ // 无 session 的单次请求,使用临时存储
169
+ return { messages: [], historyLoaded: false, compressionState: {}, save: () => {} };
170
+ }
171
+
172
+ if (!this._sessionMessageStores.has(sessionId)) {
173
+ // 自动从 session 加载消息
174
+ const savedMessages = this._loadHistoryFromSession(sessionId);
175
+ this._sessionMessageStores.set(sessionId, {
176
+ sessionId, // 保存 sessionId 引用
177
+ messages: savedMessages,
178
+ historyLoaded: true, // 已加载
179
+ compressionState: {
180
+ lastCompressedAt: null,
181
+ lastTokenCount: 0,
182
+ count: 0,
183
+ },
184
+ save: () => {
185
+ // 简洁的保存方法
186
+ if (this.agent?.framework) {
187
+ const sessionCtx = this.agent.framework.getOrCreateSessionContext(sessionId);
188
+ if (sessionCtx) {
189
+ sessionCtx.replaceMessages(this._sessionMessageStores.get(sessionId).messages);
190
+ }
191
+ }
192
+ },
193
+ });
194
+ }
195
+ return this._sessionMessageStores.get(sessionId);
130
196
  }
131
197
 
132
198
  /**
133
- * 从 session 存储加载聊天历史
199
+ * 从 SessionContext 加载聊天历史
134
200
  * @param {string} sessionId - 会话 ID
135
201
  * @returns {Array} 消息数组
136
202
  * @private
@@ -139,11 +205,10 @@ class AgentChatHandler extends EventEmitter {
139
205
  if (!sessionId || !this.agent?.framework) return [];
140
206
 
141
207
  try {
142
- const sessionPlugin = this.agent.framework.pluginManager.get('session');
143
- if (!sessionPlugin) return [];
208
+ const sessionCtx = this.agent.framework.getOrCreateSessionContext(sessionId);
209
+ if (!sessionCtx) return [];
144
210
 
145
- const messages = sessionPlugin.getHistory(sessionId);
146
- // 直接返回,保存时已清理,数据应该是干净的
211
+ const messages = sessionCtx.getMessages();
147
212
  return messages || [];
148
213
  } catch (err) {
149
214
  // 忽略加载错误
@@ -152,7 +217,7 @@ class AgentChatHandler extends EventEmitter {
152
217
  }
153
218
 
154
219
  /**
155
- * 保存聊天历史到 session 存储
220
+ * 保存聊天历史到 SessionContext
156
221
  * @param {string} sessionId - 会话 ID
157
222
  * @param {Array} messages - 消息数组
158
223
  * @private
@@ -161,12 +226,12 @@ class AgentChatHandler extends EventEmitter {
161
226
  if (!sessionId || !this.agent?.framework || !messages) return;
162
227
 
163
228
  try {
164
- const sessionPlugin = this.agent.framework.pluginManager.get('session');
165
- if (!sessionPlugin) return;
229
+ const sessionCtx = this.agent.framework.getOrCreateSessionContext(sessionId);
230
+ if (!sessionCtx) return;
166
231
 
167
232
  // 清理消息格式后整体替换
168
233
  const cleanedMessages = prepareMessagesForAPI(messages);
169
- sessionPlugin.replaceMessages(sessionId, cleanedMessages);
234
+ sessionCtx.replaceMessages(cleanedMessages);
170
235
  } catch (err) {
171
236
  // 忽略保存错误
172
237
  }
@@ -287,16 +352,6 @@ class AgentChatHandler extends EventEmitter {
287
352
  return total;
288
353
  }
289
354
 
290
- /**
291
- * 检查是否需要压缩上下文
292
- * @returns {boolean}
293
- * @private
294
- */
295
- _shouldCompress() {
296
- const totalTokens = this._countMessagesTokens(this._messages);
297
- return totalTokens > this._maxContextTokens * this._compressionThreshold;
298
- }
299
-
300
355
  /**
301
356
  * 计算工具定义的 token 数(估算)
302
357
  * @returns {number}
@@ -321,21 +376,6 @@ class AgentChatHandler extends EventEmitter {
321
376
  return total;
322
377
  }
323
378
 
324
- /**
325
- * 检查是否需要压缩(包括工具定义)
326
- * @returns {boolean}
327
- * @private
328
- */
329
- _shouldCompressWithTools() {
330
- const messagesTokens = this._countMessagesTokens(this._messages);
331
- const toolsTokens = this._countToolsTokens();
332
- const systemPromptTokens = this._countTokens(this._systemPrompt);
333
- const total = messagesTokens + toolsTokens + systemPromptTokens;
334
-
335
- // 如果总token数超过上下文限制的 85%,就压缩
336
- return total > this._maxContextTokens * 0.85;
337
- }
338
-
339
379
  /**
340
380
  * 压缩上下文消息(智能摘要模式,支持超时控制)
341
381
  * 策略:
@@ -343,23 +383,29 @@ class AgentChatHandler extends EventEmitter {
343
383
  * 2. 否则使用简单裁剪 + 标记
344
384
  * 3. 支持超时控制,防止压缩阻塞太久
345
385
  * @param {string} sessionId - 会话 ID(用于压缩后同步)
386
+ * @param {Array} messages - 消息数组引用
387
+ * @param {Object} messageStore - 消息存储对象
346
388
  * @private
347
389
  */
348
- async _compressContext(sessionId) {
390
+ async _compressContext(sessionId, messages, messageStore) {
349
391
  // 如果已经有压缩在进行,返回现有的 Promise
350
392
  if (this._compressionInProgress && this._compressionPromise) {
351
393
  logger.debug('Compression already in progress, waiting for it...');
352
394
  return this._compressionPromise;
353
395
  }
354
396
 
355
- if (this._messages.length <= this._keepRecentMessages) {
397
+ if (messages.length <= this._keepRecentMessages) {
356
398
  return;
357
399
  }
358
400
 
359
401
  this._compressionInProgress = true;
360
402
 
361
403
  // 创建压缩 Promise,带超时控制
362
- this._compressionPromise = this._executeCompressionWithTimeout(sessionId).finally(() => {
404
+ this._compressionPromise = this._executeCompressionWithTimeout(
405
+ sessionId,
406
+ messages,
407
+ messageStore
408
+ ).finally(() => {
363
409
  this._compressionInProgress = false;
364
410
  this._compressionPromise = null;
365
411
  });
@@ -370,15 +416,20 @@ class AgentChatHandler extends EventEmitter {
370
416
  /**
371
417
  * 执行压缩(带超时)
372
418
  * @param {string} sessionId - 会话 ID
419
+ * @param {Array} messages - 消息数组引用
420
+ * @param {Object} messageStore - 消息存储对象
373
421
  * @private
374
422
  */
375
- async _executeCompressionWithTimeout(sessionId) {
423
+ async _executeCompressionWithTimeout(sessionId, messages, messageStore) {
376
424
  try {
377
- return await Promise.race([this._doCompress(sessionId), this._createTimeoutPromise()]);
425
+ return await Promise.race([
426
+ this._doCompress(sessionId, messages, messageStore),
427
+ this._createTimeoutPromise(),
428
+ ]);
378
429
  } catch (err) {
379
430
  logger.warn('Compression failed:', err.message);
380
431
  // 压缩失败时使用简单的截断策略
381
- this._simpleCompress(sessionId);
432
+ this._simpleCompress(sessionId, messages, messageStore);
382
433
  }
383
434
  }
384
435
 
@@ -416,11 +467,13 @@ class AgentChatHandler extends EventEmitter {
416
467
  /**
417
468
  * 执行实际压缩逻辑
418
469
  * @param {string} sessionId - 会话 ID
470
+ * @param {Array} messages - 消息数组引用
471
+ * @param {Object} messageStore - 消息存储对象
419
472
  * @private
420
473
  */
421
- async _doCompress(sessionId) {
422
- const systemMessages = this._messages.filter((m) => m.role === 'system');
423
- const otherMessages = this._messages.filter((m) => m.role !== 'system');
474
+ async _doCompress(sessionId, messages, messageStore) {
475
+ const systemMessages = messages.filter((m) => m.role === 'system');
476
+ const otherMessages = messages.filter((m) => m.role !== 'system');
424
477
 
425
478
  // 保留最近的 N 条非系统消息
426
479
  const recentMessages = otherMessages.slice(-this._keepRecentMessages);
@@ -448,29 +501,117 @@ class AgentChatHandler extends EventEmitter {
448
501
  content: summaryContent,
449
502
  };
450
503
 
451
- this._messages = [...systemMessages, summary, ...recentMessages].filter(
452
- (item) => item.role !== 'tool'
453
- );
504
+ // 构建保留的消息,确保 tool call 和 tool result 配对不被分离
505
+ const assistantToolCalls = new Map(); // toolCallId -> assistant message index
506
+ const toolResults = new Map(); // toolCallId -> tool result indices
507
+
508
+ // 第一遍:找出所有 tool call 和它们的 results
509
+ for (let i = 0; i < recentMessages.length; i++) {
510
+ const msg = recentMessages[i];
511
+ if (msg.role === 'assistant' && msg.content) {
512
+ const content = Array.isArray(msg.content) ? msg.content : [msg.content];
513
+ for (const item of content) {
514
+ if (item.type === 'tool-call' && item.toolCallId) {
515
+ assistantToolCalls.set(item.toolCallId, i);
516
+ }
517
+ }
518
+ }
519
+ if (msg.role === 'tool' && Array.isArray(msg.content)) {
520
+ // 遍历 tool message 中的所有 tool-results
521
+ for (const item of msg.content) {
522
+ if (item && item.type === 'tool-result' && item.toolCallId) {
523
+ if (!toolResults.has(item.toolCallId)) {
524
+ toolResults.set(item.toolCallId, []);
525
+ }
526
+ toolResults.get(item.toolCallId).push(i);
527
+ }
528
+ }
529
+ }
530
+ }
531
+
532
+ // 第二遍:找出哪些 tool call 没有对应的 result(需要保留)
533
+ const orphanToolCalls = new Set();
534
+ for (const [toolCallId, assistantIdx] of assistantToolCalls) {
535
+ if (!toolResults.has(toolCallId)) {
536
+ orphanToolCalls.add(toolCallId);
537
+ }
538
+ }
539
+
540
+ // 过滤时保留:有 result 的 tool call,以及没有 result 的 tool call(但要附带其 result)
541
+ const indicesToKeep = new Set();
542
+ for (let i = 0; i < recentMessages.length; i++) {
543
+ const msg = recentMessages[i];
544
+ if (msg.role === 'tool') {
545
+ // 保留所有 tool result(它们是必要的)
546
+ indicesToKeep.add(i);
547
+ } else if (msg.role === 'assistant') {
548
+ // 检查这个 assistant 消息是否有 tool call
549
+ let hasToolCall = false;
550
+ if (msg.content) {
551
+ const content = Array.isArray(msg.content) ? msg.content : [msg.content];
552
+ for (const item of content) {
553
+ if (item.type === 'tool-call' && item.toolCallId) {
554
+ hasToolCall = true;
555
+ break;
556
+ }
557
+ }
558
+ }
559
+ if (hasToolCall) {
560
+ indicesToKeep.add(i);
561
+ } else if (i >= recentMessages.length - 3) {
562
+ // 保留最近几条 assistant 消息(它们可能包含重要上下文)
563
+ indicesToKeep.add(i);
564
+ }
565
+ } else {
566
+ // user, system 等角色默认保留
567
+ indicesToKeep.add(i);
568
+ }
569
+ }
570
+
571
+ // 如果有孤儿的 tool call(没有 result),保留该 assistant 消息
572
+ for (const [toolCallId, assistantIdx] of assistantToolCalls) {
573
+ if (!toolResults.has(toolCallId)) {
574
+ indicesToKeep.add(assistantIdx);
575
+ }
576
+ }
577
+
578
+ // 按索引排序并重建消息数组
579
+ const sortedIndices = Array.from(indicesToKeep).sort((a, b) => a - b);
580
+ const filteredRecentMessages = sortedIndices.map((i) => recentMessages[i]);
581
+
582
+ // 直接修改传入的 messages 数组
583
+ messages.length = 0;
584
+ messages.push(...systemMessages, summary, ...filteredRecentMessages);
585
+
586
+ // 更新压缩状态
454
587
  this._compressionCount++;
455
- const totalTokens = this._countMessagesTokens(this._messages);
588
+ if (messageStore.compressionState) {
589
+ messageStore.compressionState.count++;
590
+ messageStore.compressionState.lastCompressedAt = Date.now();
591
+ messageStore.compressionState.lastTokenCount = this._countMessagesTokens(messages);
592
+ }
593
+
594
+ const totalTokens = this._countMessagesTokens(messages);
456
595
  logger.info(
457
- `Context compressed (${this._compressionCount} times). Messages: ${this._messages.length}, Est. tokens: ${totalTokens}`
596
+ `Context compressed (${this._compressionCount} times). Messages: ${messages.length}, Est. tokens: ${totalTokens}`
458
597
  );
459
598
 
460
599
  // 压缩后立即同步到 session
461
600
  if (sessionId) {
462
- this._saveHistoryToSession(sessionId, this._messages);
601
+ this._saveHistoryToSession(sessionId, messages);
463
602
  }
464
603
  }
465
604
 
466
605
  /**
467
606
  * 简单压缩(当 AI 压缩失败时使用)
468
607
  * @param {string} sessionId - 会话 ID
608
+ * @param {Array} messages - 消息数组引用
609
+ * @param {Object} messageStore - 消息存储对象
469
610
  * @private
470
611
  */
471
- _simpleCompress(sessionId) {
472
- const systemMessages = this._messages.filter((m) => m.role === 'system');
473
- const otherMessages = this._messages.filter((m) => m.role !== 'system');
612
+ _simpleCompress(sessionId, messages, messageStore) {
613
+ const systemMessages = messages.filter((m) => m.role === 'system');
614
+ const otherMessages = messages.filter((m) => m.role !== 'system');
474
615
  const recentMessages = otherMessages.slice(-this._keepRecentMessages);
475
616
  const compressedCount = otherMessages.length - this._keepRecentMessages;
476
617
 
@@ -481,18 +622,102 @@ class AgentChatHandler extends EventEmitter {
481
622
  content: summaryContent,
482
623
  };
483
624
 
484
- this._messages = [...systemMessages, summary, ...recentMessages].filter(
485
- (item) => item.role !== 'tool'
486
- );
625
+ // 构建保留的消息,确保 tool call 和 tool result 配对不被分离
626
+ const assistantToolCalls = new Map(); // toolCallId -> assistant message index
627
+ const toolResults = new Map(); // toolCallId -> tool result indices
628
+
629
+ // 第一遍:找出所有 tool call 和它们的 results
630
+ for (let i = 0; i < recentMessages.length; i++) {
631
+ const msg = recentMessages[i];
632
+ if (msg.role === 'assistant' && msg.content) {
633
+ const content = Array.isArray(msg.content) ? msg.content : [msg.content];
634
+ for (const item of content) {
635
+ if (item.type === 'tool-call' && item.toolCallId) {
636
+ assistantToolCalls.set(item.toolCallId, i);
637
+ }
638
+ }
639
+ }
640
+ if (msg.role === 'tool' && Array.isArray(msg.content)) {
641
+ // 遍历 tool message 中的所有 tool-results
642
+ for (const item of msg.content) {
643
+ if (item && item.type === 'tool-result' && item.toolCallId) {
644
+ if (!toolResults.has(item.toolCallId)) {
645
+ toolResults.set(item.toolCallId, []);
646
+ }
647
+ toolResults.get(item.toolCallId).push(i);
648
+ }
649
+ }
650
+ }
651
+ }
652
+
653
+ // 第二遍:找出哪些 tool call 没有对应的 result(需要保留)
654
+ const orphanToolCalls = new Set();
655
+ for (const [toolCallId, assistantIdx] of assistantToolCalls) {
656
+ if (!toolResults.has(toolCallId)) {
657
+ orphanToolCalls.add(toolCallId);
658
+ }
659
+ }
660
+
661
+ // 过滤时保留:有 result 的 tool call,以及没有 result 的 tool call
662
+ const indicesToKeep = new Set();
663
+ for (let i = 0; i < recentMessages.length; i++) {
664
+ const msg = recentMessages[i];
665
+ if (msg.role === 'tool') {
666
+ // 保留所有 tool result
667
+ indicesToKeep.add(i);
668
+ } else if (msg.role === 'assistant') {
669
+ let hasToolCall = false;
670
+ if (msg.content) {
671
+ const content = Array.isArray(msg.content) ? msg.content : [msg.content];
672
+ for (const item of content) {
673
+ if (item.type === 'tool-call' && item.toolCallId) {
674
+ hasToolCall = true;
675
+ break;
676
+ }
677
+ }
678
+ }
679
+ if (hasToolCall) {
680
+ indicesToKeep.add(i);
681
+ } else if (i >= recentMessages.length - 3) {
682
+ // 保留最近几条 assistant 消息
683
+ indicesToKeep.add(i);
684
+ }
685
+ } else {
686
+ // user, system 等角色默认保留
687
+ indicesToKeep.add(i);
688
+ }
689
+ }
690
+
691
+ // 如果有孤儿的 tool call(没有 result),保留该 assistant 消息
692
+ for (const [toolCallId, assistantIdx] of assistantToolCalls) {
693
+ if (!toolResults.has(toolCallId)) {
694
+ indicesToKeep.add(assistantIdx);
695
+ }
696
+ }
697
+
698
+ // 按索引排序并重建消息数组
699
+ const sortedIndices = Array.from(indicesToKeep).sort((a, b) => a - b);
700
+ const filteredRecentMessages = sortedIndices.map((i) => recentMessages[i]);
701
+
702
+ // 直接修改传入的 messages 数组
703
+ messages.length = 0;
704
+ messages.push(...systemMessages, summary, ...filteredRecentMessages);
705
+
706
+ // 更新压缩状态
487
707
  this._compressionCount++;
708
+ if (messageStore.compressionState) {
709
+ messageStore.compressionState.count++;
710
+ messageStore.compressionState.lastCompressedAt = Date.now();
711
+ messageStore.compressionState.lastTokenCount = this._countMessagesTokens(messages);
712
+ }
488
713
 
489
714
  logger.info(
490
- `Context simple compressed (${this._compressionCount} times). Messages: ${this._messages.length}`
715
+ `Context simple compressed (${this._compressionCount} times). Messages: ${messages.length}`
491
716
  );
492
717
 
493
718
  // 压缩后立即同步到 session
494
719
  if (sessionId) {
495
- this._saveHistoryToSession(sessionId, this._messages);
720
+ this._saveHistoryToSession(sessionId, messages);
496
721
  }
497
722
  }
498
723
 
@@ -677,23 +902,34 @@ ${truncatedContent}${truncatedNote}
677
902
  * @param {string} sessionId - 可选,指定 session 则同时清除 session 中的历史
678
903
  */
679
904
  clearHistory(sessionId) {
680
- this._messages = [];
681
- this._compressionCount = 0;
682
-
683
- // 同时清除 session 中的历史
684
- if (sessionId && this.agent?.framework) {
685
- try {
686
- const sessionPlugin = this.agent.framework.pluginManager.get('session');
687
- if (sessionPlugin) {
688
- const session = sessionPlugin.getSession(sessionId);
689
- if (session) {
690
- session.messages = [];
905
+ if (sessionId) {
906
+ // 清除 Per-Session 消息存储
907
+ const messageStore = this._sessionMessageStores.get(sessionId);
908
+ if (messageStore) {
909
+ messageStore.messages = [];
910
+ messageStore.historyLoaded = false;
911
+ messageStore.compressionState = {
912
+ lastCompressedAt: null,
913
+ lastTokenCount: 0,
914
+ count: 0,
915
+ };
916
+ }
917
+ // 清除 SessionContext 中的历史
918
+ if (this.agent?.framework) {
919
+ try {
920
+ const sessionCtx = this.agent.framework.getSessionContext(sessionId);
921
+ if (sessionCtx) {
922
+ sessionCtx.clearMessages();
691
923
  }
924
+ } catch (err) {
925
+ // 忽略错误
692
926
  }
693
- } catch (err) {
694
- // 忽略错误
695
927
  }
928
+ } else {
929
+ // 清除所有 Per-Session 消息存储
930
+ this._sessionMessageStores.clear();
696
931
  }
932
+ this._compressionCount = 0;
697
933
  return this;
698
934
  }
699
935
 
@@ -739,19 +975,13 @@ ${truncatedContent}${truncatedNote}
739
975
  */
740
976
  async _doChat(message, options = {}) {
741
977
  const sessionId = options.sessionId || null;
742
- const context = { sessionId, isStream: false };
743
978
  const framework = this.agent.framework;
744
979
 
745
- try {
746
- // session 加载聊天历史
747
- if (sessionId) {
748
- const savedMessages = this._loadHistoryFromSession(sessionId);
749
- if (savedMessages.length > 0) {
750
- this._messages = savedMessages;
751
- logger.info(`Loaded ${savedMessages.length} messages from session ${sessionId}`);
752
- }
753
- }
980
+ // 获取 Per-Session 的消息存储(自动从 session 加载)
981
+ const messageStore = this._getSessionMessageStore(sessionId);
982
+ const messages = messageStore.messages;
754
983
 
984
+ try {
755
985
  // 关键:每次 chat 调用时刷新系统提示词,确保包含最新的工具/技能描述
756
986
  // 解决上下文过长时 LLM 不调用工具的问题
757
987
  this._systemPrompt = this.agent._buildSystemPrompt();
@@ -760,11 +990,10 @@ ${truncatedContent}${truncatedNote}
760
990
 
761
991
  const userMessage =
762
992
  typeof message === 'string' ? { role: 'user', content: message } : message;
763
- //console.log('System Prompt:', this._systemPrompt);
764
- this._messages.push(userMessage);
993
+ messages.push(userMessage);
765
994
 
766
995
  // 检查是否需要压缩上下文(包括工具定义)
767
- const messagesTokens = this._countMessagesTokens(this._messages);
996
+ const messagesTokens = this._countMessagesTokens(messages);
768
997
  const toolsTokens = this._countToolsTokens();
769
998
  const systemPromptTokens = this._countTokens(this._systemPrompt);
770
999
  const totalTokens = messagesTokens + toolsTokens + systemPromptTokens;
@@ -774,55 +1003,75 @@ ${truncatedContent}${truncatedNote}
774
1003
  logger.info(
775
1004
  `Context large (${totalTokens}/${this._maxContextTokens} tokens = msgs:${messagesTokens} + tools:${toolsTokens} + sys:${systemPromptTokens}), compressing...`
776
1005
  );
777
- // 使用带超时控制的压缩
778
- await this._compressContext(sessionId);
1006
+ // 使用带超时控制的压缩(传入 messages 引用)
1007
+ await this._compressContext(sessionId, messages, messageStore);
779
1008
  }
780
1009
 
781
1010
  const maxSteps = options.maxSteps || this._maxSteps;
782
1011
  const tools = this._getAITools(tool);
783
1012
 
1013
+ // 准备传给 agent 的消息
1014
+ const prepareStepChainStream = async ({ stepNumber, messages }) => {
1015
+ try {
1016
+ // 1. 验证消息格式
1017
+ if (!Array.isArray(messages)) {
1018
+ logger.warn('prepareStep received non-array messages');
1019
+ return messages;
1020
+ }
1021
+
1022
+ // 2. 消息数量超过阈值才修剪
1023
+ if (messages.length <= 50) {
1024
+ return messages;
1025
+ }
1026
+
1027
+ // 3. 保留配对完整的消息
1028
+ const pruned = this._prepareMessagesForAI(messages);
1029
+ return pruned;
1030
+ } catch (err) {
1031
+ logger.error('prepareStep error:', err.message);
1032
+ return messages; // 出错时返回原消息
1033
+ }
1034
+ };
1035
+
784
1036
  if (!this._aiClient) {
785
1037
  throw new Error('AI client not configured.');
786
1038
  }
787
1039
 
788
- // 准备传给 agent 的消息
789
- // ToolLoopAgent 会自动处理消息格式,不需要手动 prune
1040
+ // 不使用 prepareStep,消息格式已在 _saveHistoryToSession 中处理
790
1041
  const agent = new ToolLoopAgent({
791
1042
  model: this._aiClient,
792
1043
  instructions: this._systemPrompt,
793
1044
  tools: tools,
794
- //stopWhen: stepCountIs(maxSteps),
795
- prepareStep: async ({ stepNumber, messages }) =>
796
- this._prepareStepForPruning(stepNumber, messages),
1045
+ prepareStep: prepareStepChainStream,
797
1046
  });
798
1047
 
799
- // 使用 runWithContext 让工具执行时能获取 sessionId
800
- const result = await framework.runWithContext(context, async () => {
801
- return agent.generate({ messages: this._messages, ...this.providerOptions });
1048
+ // 使用 runInSession 让工具执行时能获取 sessionId(Per-Session 隔离)
1049
+ const result = await framework.runInSession(sessionId, {}, async () => {
1050
+ return agent.generate({ messages, ...this.providerOptions });
802
1051
  });
803
1052
 
804
- this._messages.push(...result.response.messages);
1053
+ messages.push(...result.response.messages);
1054
+
1055
+ // 触发 agent:message 事件,让 memory 插件可以自动提取记忆
1056
+ const userMsg = messages[messages.length - result.response.messages.length - 1];
1057
+ this.emit('message', { userMessage: userMsg, assistantResponse: result.text });
805
1058
 
806
1059
  // 生成后检查:如果消息太长,下次需要压缩
807
- const afterTokens = this._countMessagesTokens(this._messages);
1060
+ const afterTokens = this._countMessagesTokens(messages);
808
1061
  if (afterTokens > this._maxContextTokens * 0.8) {
809
1062
  logger.info(`After generation: ${afterTokens} tokens, will compress on next turn`);
810
1063
  }
811
1064
 
812
- // 保存聊天历史到 session
813
- if (sessionId) {
814
- this._saveHistoryToSession(sessionId, this._messages);
815
- }
816
-
817
1065
  return {
818
1066
  success: true,
819
1067
  message: result.text || '',
820
1068
  stepCount: result.stepCount || 1,
821
1069
  };
822
- } catch (err) {
823
- this.emit('error', { error: err.message });
824
- // 抛出错误而不是返回错误响应,让 Agent 能捕获
825
- throw err;
1070
+ } finally {
1071
+ // 校验并修复消息中的不完整工具调用
1072
+ this._validateToolCalls(messages);
1073
+ // 确保保存聊天历史到 session(无论成功还是失败)
1074
+ messageStore.save();
826
1075
  }
827
1076
  }
828
1077
 
@@ -833,19 +1082,13 @@ ${truncatedContent}${truncatedNote}
833
1082
  */
834
1083
  async *chatStream(message, options = {}) {
835
1084
  const sessionId = options.sessionId || null;
836
- const context = { sessionId, isStream: true };
837
1085
  const framework = this.agent.framework;
838
1086
 
839
- try {
840
- //从 session 加载聊天历史
841
- if (sessionId) {
842
- const savedMessages = this._loadHistoryFromSession(sessionId);
843
- if (savedMessages.length > 0) {
844
- this._messages = savedMessages;
845
- logger.info(`Loaded ${savedMessages.length} messages from session ${sessionId}`);
846
- }
847
- }
1087
+ // 获取 Per-Session 的消息存储
1088
+ const messageStore = this._getSessionMessageStore(sessionId);
1089
+ const messages = messageStore.messages;
848
1090
 
1091
+ try {
849
1092
  // 关键:每次 chat 调用时刷新系统提示词,确保包含最新的工具/技能描述
850
1093
  // 解决上下文过长时 LLM 不调用工具的问题
851
1094
  this._systemPrompt = this.agent._buildSystemPrompt();
@@ -854,23 +1097,21 @@ ${truncatedContent}${truncatedNote}
854
1097
 
855
1098
  const userMessage =
856
1099
  typeof message === 'string' ? { role: 'user', content: message } : message;
857
- //console.log('System Prompt:', this._systemPrompt);
858
- this._messages.push(userMessage);
1100
+ messages.push(userMessage);
859
1101
 
860
1102
  // 检查是否需要压缩上下文(包括工具定义)
861
- const messagesTokens = this._countMessagesTokens(this._messages);
1103
+ const messagesTokens = this._countMessagesTokens(messages);
862
1104
  const toolsTokens = this._countToolsTokens();
863
1105
  const systemPromptTokens = this._countTokens(this._systemPrompt);
864
1106
  const totalTokens = messagesTokens + toolsTokens + systemPromptTokens;
865
1107
  const limit = this._maxContextTokens * 0.7; // 降低到 70%
866
- //console.log(messagesTokens,toolsTokens,systemPromptTokens,totalTokens,this._maxContextTokens)
867
1108
  // 对于流式调用,如果上下文太大,先压缩再开始
868
1109
  if (totalTokens > limit) {
869
1110
  logger.info(
870
1111
  `Context large (${totalTokens}/${this._maxContextTokens} tokens), compressing...`
871
1112
  );
872
1113
  // 流式调用时等待压缩完成(使用带超时控制的压缩)
873
- await this._compressContext(sessionId);
1114
+ await this._compressContext(sessionId, messages, messageStore);
874
1115
  }
875
1116
 
876
1117
  const maxSteps = options.maxSteps || this._maxSteps;
@@ -880,20 +1121,39 @@ ${truncatedContent}${truncatedNote}
880
1121
  }
881
1122
 
882
1123
  // 准备传给 agent 的消息
883
- // ToolLoopAgent 会自动处理消息格式,不需要手动 prune
1124
+ const prepareStepChainStream = async ({ stepNumber, messages }) => {
1125
+ try {
1126
+ // 1. 验证消息格式
1127
+ if (!Array.isArray(messages)) {
1128
+ logger.warn('prepareStep received non-array messages');
1129
+ return messages;
1130
+ }
1131
+
1132
+ // 2. 消息数量超过阈值才修剪
1133
+ if (messages.length <= 50) {
1134
+ return messages;
1135
+ }
1136
+
1137
+ // 3. 保留配对完整的消息
1138
+ const pruned = this._prepareMessagesForAI(messages);
1139
+ return pruned;
1140
+ } catch (err) {
1141
+ logger.error('prepareStep error:', err.message);
1142
+ return messages; // 出错时返回原消息
1143
+ }
1144
+ };
1145
+
884
1146
  const agent = new ToolLoopAgent({
885
1147
  model: this._aiClient,
886
1148
  instructions: this._systemPrompt,
887
1149
  tools: tools,
888
- //stopWhen: stepCountIs(maxSteps),
889
- prepareStep: async ({ stepNumber, messages }) =>
890
- this._prepareStepForPruning(stepNumber, messages),
1150
+ prepareStep: prepareStepChainStream,
891
1151
  });
892
1152
 
893
- // 使用 runWithContext 让工具执行时能获取 sessionId(支持并行)
894
- const result = await framework.runWithContext(context, async () => {
1153
+ // 使用 runInSession 让工具执行时能获取 sessionId(Per-Session 隔离)
1154
+ const result = await framework.runInSession(sessionId, {}, async () => {
895
1155
  return agent.stream({
896
- messages: this._messages,
1156
+ messages,
897
1157
  ...this.providerOptions,
898
1158
  });
899
1159
  });
@@ -921,18 +1181,106 @@ ${truncatedContent}${truncatedNote}
921
1181
  }
922
1182
 
923
1183
  const finishMessages = (await result.response).messages;
924
- this._messages.push(...finishMessages);
1184
+ messages.push(...finishMessages);
925
1185
 
926
- // 保存聊天历史到 session(使用实际传给 agent 的消息一致)
927
- if (sessionId) {
928
- this._saveHistoryToSession(sessionId, this._messages);
929
- }
1186
+ // 触发 agent:message 事件,让 memory 插件可以自动提取记忆
1187
+ const userMsg = messages[messages.length - finishMessages.length - 1];
1188
+ this.emit('message', { userMessage: userMsg, assistantResponse: fullText });
930
1189
  } catch (err) {
931
1190
  this.emit('error', { error: err.message });
932
1191
  yield { type: 'error', error: err.message };
1192
+ } finally {
1193
+ // 校验并修复消息中的不完整工具调用
1194
+ this._validateToolCalls(messages);
1195
+ // 确保保存聊天历史到 session(无论成功还是失败)
1196
+ messageStore.save();
933
1197
  }
934
1198
  }
935
1199
 
1200
+ /**
1201
+ * 为 AI SDK prepareStep 准备消息
1202
+ * 确保 tool call 和 tool result 正确配对
1203
+ * @param {Array} messages - 消息列表
1204
+ * @returns {Array} 过滤后的消息数组
1205
+ * @private
1206
+ */
1207
+ _prepareMessagesForAI(messages) {
1208
+ if (messages.length <= 10) {
1209
+ return messages;
1210
+ }
1211
+
1212
+ // 收集所有 assistant 的 tool-call
1213
+ const assistantToolCalls = new Map(); // toolCallId -> message index
1214
+ for (let i = 0; i < messages.length; i++) {
1215
+ const msg = messages[i];
1216
+ if (msg.role === 'assistant' && Array.isArray(msg.content)) {
1217
+ for (const item of msg.content) {
1218
+ if (item.type === 'tool-call' && item.toolCallId) {
1219
+ assistantToolCalls.set(item.toolCallId, i);
1220
+ }
1221
+ }
1222
+ }
1223
+ }
1224
+
1225
+ // 收集配对的 tool results
1226
+ const pairedToolResults = new Set(); // 保留的 tool result indices
1227
+ for (let i = 0; i < messages.length; i++) {
1228
+ const msg = messages[i];
1229
+ if (msg.role === 'tool' && Array.isArray(msg.content)) {
1230
+ for (const item of msg.content) {
1231
+ if (item.type === 'tool-result' && item.toolCallId) {
1232
+ if (assistantToolCalls.has(item.toolCallId)) {
1233
+ pairedToolResults.add(i);
1234
+ }
1235
+ }
1236
+ }
1237
+ }
1238
+ }
1239
+
1240
+ // 过滤:保留最近的消息,但确保 tool call/result 配对完整
1241
+ const recentCount = Math.max(20, messages.length - 10);
1242
+ const minIndexToKeep = messages.length - recentCount;
1243
+
1244
+ const result = [];
1245
+ for (let i = minIndexToKeep; i < messages.length; i++) {
1246
+ const msg = messages[i];
1247
+
1248
+ if (msg.role === 'tool') {
1249
+ // 只保留有配对的 tool messages
1250
+ if (pairedToolResults.has(i)) {
1251
+ result.push(msg);
1252
+ }
1253
+ } else if (msg.role === 'assistant') {
1254
+ // 检查是否有未配对的 tool-call
1255
+ if (Array.isArray(msg.content)) {
1256
+ const hasUnpairedToolCall = msg.content.some(
1257
+ (item) => item.type === 'tool-call' && !assistantToolCalls.has(item.toolCallId)
1258
+ );
1259
+ if (hasUnpairedToolCall) {
1260
+ // 过滤掉未配对的 tool-call
1261
+ const filteredContent = msg.content.filter((item) => {
1262
+ if (item.type === 'tool-call') {
1263
+ return assistantToolCalls.has(item.toolCallId);
1264
+ }
1265
+ return true;
1266
+ });
1267
+ if (filteredContent.length > 0) {
1268
+ result.push({ ...msg, content: filteredContent });
1269
+ }
1270
+ } else {
1271
+ result.push(msg);
1272
+ }
1273
+ } else {
1274
+ result.push(msg);
1275
+ }
1276
+ } else {
1277
+ result.push(msg);
1278
+ }
1279
+ }
1280
+
1281
+ return result;
1282
+ }
1283
+
936
1284
  /**
937
1285
  * 修剪消息确保配对的 tool call/result 不被拆分
938
1286
  * @param {number} stepNumber - 当前步骤号
@@ -959,11 +1307,16 @@ ${truncatedContent}${truncatedNote}
959
1307
  }
960
1308
  }
961
1309
  }
962
- if (msg.role === 'tool' && msg.toolCallId) {
963
- if (!toolResultIndices.has(msg.toolCallId)) {
964
- toolResultIndices.set(msg.toolCallId, []);
1310
+ if (msg.role === 'tool' && Array.isArray(msg.content)) {
1311
+ // 遍历 tool message 中的所有 tool-results
1312
+ for (const item of msg.content) {
1313
+ if (item && item.type === 'tool-result' && item.toolCallId) {
1314
+ if (!toolResultIndices.has(item.toolCallId)) {
1315
+ toolResultIndices.set(item.toolCallId, []);
1316
+ }
1317
+ toolResultIndices.get(item.toolCallId).push(i);
1318
+ }
965
1319
  }
966
- toolResultIndices.get(msg.toolCallId).push(i);
967
1320
  }
968
1321
  }
969
1322
 
@@ -996,6 +1349,49 @@ ${truncatedContent}${truncatedNote}
996
1349
  return { messages: pruned };
997
1350
  }
998
1351
 
1352
+ /**
1353
+ * 校验并修复消息中的工具调用参数
1354
+ * 移除不完整的 JSON(如只有 "{" )的工具调用
1355
+ * @param {Array} messages - 消息列表
1356
+ * @private
1357
+ */
1358
+ _validateToolCalls(messages) {
1359
+ let fixedCount = 0;
1360
+
1361
+ for (const msg of messages) {
1362
+ if (msg.role !== 'assistant' || !Array.isArray(msg.content)) {
1363
+ continue;
1364
+ }
1365
+
1366
+ for (const item of msg.content) {
1367
+ if (item.type !== 'tool-call') {
1368
+ continue;
1369
+ }
1370
+
1371
+ const input = item.input;
1372
+ if (typeof input !== 'string') {
1373
+ continue;
1374
+ }
1375
+
1376
+ // 检查 input 是否是有效的 JSON(不是不完整的)
1377
+ const trimmed = input.trim();
1378
+ if (trimmed === '{' || trimmed === '' || !trimmed.startsWith('{')) {
1379
+ // 不完整的 JSON,移除这个 tool-call
1380
+ item.type = 'text';
1381
+ item.text = `(工具调用 ${item.toolName} 参数不完整,已跳过)`;
1382
+ delete item.toolCallId;
1383
+ delete item.toolName;
1384
+ delete item.input;
1385
+ fixedCount++;
1386
+ }
1387
+ }
1388
+ }
1389
+
1390
+ if (fixedCount > 0) {
1391
+ logger.info(`Fixed ${fixedCount} incomplete tool calls`);
1392
+ }
1393
+ }
1394
+
999
1395
  /**
1000
1396
  * 获取 AI SDK 格式的工具
1001
1397
  * AI SDK 6.x 需要对象形式 { toolName: tool }
@@ -1022,7 +1418,10 @@ ${truncatedContent}${truncatedNote}
1022
1418
  try {
1023
1419
  const result = await toolDef.execute(cleanedArgs, this.agent.framework);
1024
1420
  this.emit('tool-result', { name: toolName, args: cleanedArgs, result });
1025
- return result;
1421
+ if (result !== null && typeof result === 'object') {
1422
+ return JSON.stringify(result);
1423
+ }
1424
+ return String(result);
1026
1425
  } catch (err) {
1027
1426
  this.emit('tool-error', { name: toolName, args: cleanedArgs, error: err.message });
1028
1427
  return { error: err.message };
@@ -1084,7 +1483,8 @@ ${truncatedContent}${truncatedNote}
1084
1483
  * 销毁
1085
1484
  */
1086
1485
  destroy() {
1087
- this._messages = [];
1486
+ this._sessionMessageStores.clear();
1487
+ this._sessionMessageStores = new Map();
1088
1488
  this._tools.clear();
1089
1489
  this._encoder = null;
1090
1490
  this.removeAllListeners();