foliko 1.1.4 → 1.1.6

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 (69) hide show
  1. package/.agent/agents/code-assistant.json +4 -1
  2. package/.agent/agents/file-assistant.json +4 -1
  3. package/.agent/agents/orchestrator-demo.md +53 -0
  4. package/.agent/agents/orchestrator.json +7 -0
  5. package/.agent/data/plugins-state.json +4 -0
  6. package/.agent/mcp_config.json +6 -0
  7. package/.agent/memory/feedback/mnrsiuoc-e1ru74.md +9 -0
  8. package/.agent/memory/feedback/mnrt2mmz-98az6n.md +9 -0
  9. package/.agent/memory/feedback/mnrtqrhm-kxsicz.md +9 -0
  10. package/.agent/memory/feedback/mnrts8vg-i0ngzp.md +15 -0
  11. package/.agent/memory/feedback/mnrtt7jt-c0trb2.md +9 -0
  12. package/.agent/memory/feedback/mnruc2f0-5s52la.md +16 -0
  13. package/.agent/memory/feedback/mnrumbmx-63sa0v.md +9 -0
  14. package/.agent/memory/project/mnrp7p5n-8enm2a.md +31 -0
  15. package/.agent/memory/project/mnrp9ifb-yynks0.md +40 -0
  16. package/.agent/memory/project/mnrpb3b8-f617s4.md +25 -0
  17. package/.agent/memory/project/mnrrmqgg-focprv.md +9 -0
  18. package/.agent/memory/project/mnrtykbh-6atsor.md +9 -0
  19. package/.agent/memory/project/mnru9jiu-kgau16.md +35 -0
  20. package/.agent/memory/reference/mnrnvpwo-rcqv9m.md +52 -0
  21. package/.agent/memory/reference/mnrovxvz-zy9xqm.md +25 -0
  22. package/.agent/memory/reference/mnroxabj-1b3930.md +68 -0
  23. package/.agent/memory/reference/mnrpjtlp-mnb9od.md +35 -0
  24. package/.agent/memory/reference/mnrps1x3-6b8xfm.md +28 -0
  25. package/.agent/memory/reference/mnrpt9ov-15er5w.md +22 -0
  26. package/.agent/memory/reference/mnrq82dn-y9tv9e.md +50 -0
  27. package/.agent/memory/reference/mnrqnr5v-v75drf.md +34 -0
  28. package/.agent/memory/reference/mnrrfzys-urudaf.md +31 -0
  29. package/.agent/memory/reference/mnrrocha-t0027n.md +21 -0
  30. package/.agent/memory/reference/mnrukklc-bxndsb.md +35 -0
  31. package/.agent/memory/user/mnrt39t8-8eosy0.md +9 -0
  32. package/.agent/plugins/marknative/fonts.zip +0 -0
  33. package/.agent/sessions/cli_default.json +4493 -1183
  34. package/.agent/test-agent.js +35 -0
  35. package/cli/src/commands/chat.js +69 -1
  36. package/cli/src/index.js +11 -2
  37. package/foliko-cloud-rising.png +0 -0
  38. package/foliko-dawn-of-ai.png +0 -0
  39. package/foliko-mindful-observation.png +0 -0
  40. package/foliko-stellar-dreams.png +0 -0
  41. package/foliko-zen-jing.png +0 -0
  42. package/foliko-zen-kong.png +0 -0
  43. package/foliko-zen-wu.png +0 -0
  44. package/package.json +2 -2
  45. package/plugins/coordinator-plugin.js +282 -0
  46. package/plugins/default-plugins.js +1 -1
  47. package/plugins/extension-executor-plugin.js +27 -1
  48. package/plugins/memory-plugin.js +228 -57
  49. package/src/core/agent-chat.js +206 -115
  50. package/src/core/agent.js +258 -71
  51. package/src/core/coordinator-manager.js +341 -0
  52. package/src/core/framework.js +50 -0
  53. package/src/core/plugin-base.js +30 -17
  54. package/src/core/sub-agent-config.js +8 -1
  55. package/src/core/worker-agent.js +176 -0
  56. package/src/executors/mcp-executor.js +4 -4
  57. package/zen_karesansui.png +0 -0
  58. package/.agent/plugins/marknative/update-readme.js +0 -134
  59. package/output/emoji-segoe-test-v2.png +0 -0
  60. package/output/emoji-segoe-test.png +0 -0
  61. package/output/emoji-test.png +0 -0
  62. package/output/emoji-windows-test.png +0 -0
  63. package/output/foliko-emoji-poster.png +0 -0
  64. package/output/foliko-muji-poster-final.png +0 -0
  65. package/output/foliko-muji-poster-v2.png +0 -0
  66. package/output/foliko-muji-poster.png +0 -0
  67. package/output/foliko-share.png +0 -0
  68. package/output/progress-circle-test.png +0 -0
  69. package/output/vb-agent-poster.png +0 -0
@@ -31,7 +31,7 @@ const MODEL_CONTEXT_LIMITS = {
31
31
  'deepseek-chat': 28000,
32
32
  'deepseek-coder': 28000,
33
33
  // MiniMax
34
- 'MiniMax-M2.7': 90000,
34
+ 'MiniMax-M2.7': 120000,
35
35
  // OpenAI
36
36
  'gpt-4': 100000,
37
37
  'gpt-4o': 100000,
@@ -41,6 +41,7 @@ const MODEL_CONTEXT_LIMITS = {
41
41
  'claude-3-5-sonnet': 150000,
42
42
  'claude-3-opus': 150000,
43
43
  'claude-3-sonnet': 150000,
44
+ 'glm-5.1': 200000,
44
45
  };
45
46
 
46
47
  /**
@@ -374,6 +375,7 @@ class AgentChatHandler extends EventEmitter {
374
375
  /**
375
376
  * 计算单个内容块的 token 数
376
377
  * 参考 Claude Code 的实现
378
+ * 兼容 AI SDK 6.x 标准格式
377
379
  * @param {Object} block - 内容块
378
380
  * @returns {number}
379
381
  * @private
@@ -390,8 +392,8 @@ class AgentChatHandler extends EventEmitter {
390
392
  return tokenizer.encode(block.text || '');
391
393
  }
392
394
 
393
- // 工具调用块 - 工具名 + 输入参数(JSON 更密集)
394
- if (block.type === 'tool-use' || block.type === 'tool_call') {
395
+ // 工具调用块 - 兼容 tool-call tool-use 两种类型
396
+ if (block.type === 'tool-call' || block.type === 'tool-use' || block.type === 'tool_call') {
395
397
  let count = 0;
396
398
  if (block.name || block.toolName) {
397
399
  count += tokenizer.encode(String(block.name || block.toolName || ''));
@@ -404,7 +406,7 @@ class AgentChatHandler extends EventEmitter {
404
406
  return count;
405
407
  }
406
408
 
407
- // 工具结果块
409
+ // 工具结果块 - 兼容 tool-result 和 tool_result 两种类型
408
410
  if (block.type === 'tool-result' || block.type === 'tool_result') {
409
411
  let count = 0;
410
412
  if (block.content) {
@@ -668,13 +670,19 @@ class AgentChatHandler extends EventEmitter {
668
670
  const assistantToolCalls = new Map(); // toolCallId -> assistant message index
669
671
  const toolResults = new Map(); // toolCallId -> tool result indices
670
672
 
673
+ // 工具类型检查辅助函数(兼容 AI SDK 6.x)
674
+ const isToolCall = (item) =>
675
+ item && (item.type === 'tool-call' || item.type === 'tool-use') && item.toolCallId;
676
+ const isToolResult = (item) =>
677
+ item && (item.type === 'tool-result' || item.type === 'tool_result') && item.toolCallId;
678
+
671
679
  // 第一遍:找出所有 tool call 和它们的 results
672
680
  for (let i = 0; i < recentMessages.length; i++) {
673
681
  const msg = recentMessages[i];
674
682
  if (msg.role === 'assistant' && msg.content) {
675
683
  const content = Array.isArray(msg.content) ? msg.content : [msg.content];
676
684
  for (const item of content) {
677
- if (item.type === 'tool-call' && item.toolCallId) {
685
+ if (isToolCall(item)) {
678
686
  assistantToolCalls.set(item.toolCallId, i);
679
687
  }
680
688
  }
@@ -682,7 +690,7 @@ class AgentChatHandler extends EventEmitter {
682
690
  if (msg.role === 'tool' && Array.isArray(msg.content)) {
683
691
  // 遍历 tool message 中的所有 tool-results
684
692
  for (const item of msg.content) {
685
- if (item && item.type === 'tool-result' && item.toolCallId) {
693
+ if (isToolResult(item)) {
686
694
  if (!toolResults.has(item.toolCallId)) {
687
695
  toolResults.set(item.toolCallId, []);
688
696
  }
@@ -711,7 +719,7 @@ class AgentChatHandler extends EventEmitter {
711
719
  if (msg.content) {
712
720
  const content = Array.isArray(msg.content) ? msg.content : [msg.content];
713
721
  for (const item of content) {
714
- if (item.type === 'tool-call' && item.toolCallId) {
722
+ if (isToolCall(item)) {
715
723
  hasToolCall = true;
716
724
  break;
717
725
  }
@@ -743,7 +751,7 @@ class AgentChatHandler extends EventEmitter {
743
751
  let hasPairedAssistant = false;
744
752
  const content = Array.isArray(msg.content) ? msg.content : [msg.content];
745
753
  for (const item of content) {
746
- if (item && item.type === 'tool-result' && item.toolCallId) {
754
+ if (isToolResult(item)) {
747
755
  // 检查这个 toolCallId 对应的 assistant 消息是否被保留
748
756
  const assistantIdx = assistantToolCalls.get(item.toolCallId);
749
757
  if (assistantIdx !== undefined && assistantIndicesToKeep.has(assistantIdx)) {
@@ -815,6 +823,12 @@ class AgentChatHandler extends EventEmitter {
815
823
  content: summaryContent,
816
824
  };
817
825
 
826
+ // 工具类型检查辅助函数(兼容 AI SDK 6.x)
827
+ const isToolCall = (item) =>
828
+ item && (item.type === 'tool-call' || item.type === 'tool-use') && item.toolCallId;
829
+ const isToolResult = (item) =>
830
+ item && (item.type === 'tool-result' || item.type === 'tool_result') && item.toolCallId;
831
+
818
832
  // 构建保留的消息,确保 tool call 和 tool result 配对不被分离
819
833
  const assistantToolCalls = new Map(); // toolCallId -> assistant message index
820
834
  const toolResults = new Map(); // toolCallId -> tool result indices
@@ -825,7 +839,7 @@ class AgentChatHandler extends EventEmitter {
825
839
  if (msg.role === 'assistant' && msg.content) {
826
840
  const content = Array.isArray(msg.content) ? msg.content : [msg.content];
827
841
  for (const item of content) {
828
- if (item.type === 'tool-call' && item.toolCallId) {
842
+ if (isToolCall(item)) {
829
843
  assistantToolCalls.set(item.toolCallId, i);
830
844
  }
831
845
  }
@@ -833,7 +847,7 @@ class AgentChatHandler extends EventEmitter {
833
847
  if (msg.role === 'tool' && Array.isArray(msg.content)) {
834
848
  // 遍历 tool message 中的所有 tool-results
835
849
  for (const item of msg.content) {
836
- if (item && item.type === 'tool-result' && item.toolCallId) {
850
+ if (isToolResult(item)) {
837
851
  if (!toolResults.has(item.toolCallId)) {
838
852
  toolResults.set(item.toolCallId, []);
839
853
  }
@@ -863,7 +877,7 @@ class AgentChatHandler extends EventEmitter {
863
877
  if (msg.content) {
864
878
  const content = Array.isArray(msg.content) ? msg.content : [msg.content];
865
879
  for (const item of content) {
866
- if (item.type === 'tool-call' && item.toolCallId) {
880
+ if (isToolCall(item)) {
867
881
  hasToolCall = true;
868
882
  break;
869
883
  }
@@ -1216,6 +1230,9 @@ ${truncatedContent}${truncatedNote}
1216
1230
  messages.push(...validatedMessages);
1217
1231
  }
1218
1232
 
1233
+ // 验证并修复消息中的不完整工具调用(在 API 调用前执行,防止开始聊天的时候就报错)
1234
+ this._validateToolCalls(messages);
1235
+
1219
1236
  // 最终检查:在 API 调用前再次验证 token 数
1220
1237
  const finalMessagesTokens = this._countMessagesTokens(messages);
1221
1238
  const finalToolsTokens = this._countToolsTokens();
@@ -1223,9 +1240,9 @@ ${truncatedContent}${truncatedNote}
1223
1240
  const finalTotalTokens = finalMessagesTokens + finalToolsTokens + finalSystemPromptTokens;
1224
1241
  const finalLimit = this._maxContextTokens * 0.5;
1225
1242
 
1226
- logger.info(
1227
- `[API Call Check] messages=${messages.length}, tokens=(${finalMessagesTokens}+${finalToolsTokens}+${finalSystemPromptTokens}=${finalTotalTokens}) vs limit=${finalLimit}`
1228
- );
1243
+ // logger.info(
1244
+ // `[API Call Check] messages=${messages.length}, tokens=(${finalMessagesTokens}+${finalToolsTokens}+${finalSystemPromptTokens}=${finalTotalTokens}) vs limit=${finalLimit}`
1245
+ // );
1229
1246
 
1230
1247
  // 如果仍然超过限制,强制压缩
1231
1248
  if (finalTotalTokens > finalLimit) {
@@ -1241,25 +1258,44 @@ ${truncatedContent}${truncatedNote}
1241
1258
  }
1242
1259
 
1243
1260
  // 准备传给 agent 的消息
1244
- const prepareStepChainStream = async ({ stepNumber, messages }) => {
1261
+ const prepareStepChainStream = async ({ stepNumber, messages: inputMessages }) => {
1245
1262
  try {
1246
1263
  // 1. 验证消息格式
1247
- if (!Array.isArray(messages)) {
1264
+ if (!Array.isArray(inputMessages)) {
1248
1265
  logger.warn('prepareStep received non-array messages');
1249
- return messages;
1266
+ return { messages: inputMessages };
1250
1267
  }
1251
1268
 
1252
- // 2. 消息数量超过阈值才修剪
1253
- if (messages.length <= 50) {
1254
- return messages;
1269
+ // 2. 修复不完整的工具调用(input 只有 { 或其他无效值)
1270
+ this._validateToolCalls(inputMessages);
1271
+
1272
+ // 3. 验证消息配对并更新 inputMessages(防止 API 报 orphaned tool result 错误)
1273
+ const pairedMessages = this._validateMessagesPairing(inputMessages);
1274
+ if (pairedMessages.length !== inputMessages.length) {
1275
+ logger.debug(
1276
+ `prepareStep pairing: ${inputMessages.length} -> ${pairedMessages.length}`
1277
+ );
1255
1278
  }
1279
+ // 重要:用配对验证后的结果替换 inputMessages
1280
+ inputMessages.length = 0;
1281
+ inputMessages.push(...pairedMessages);
1282
+
1283
+ // 4. Token 数检查:超过限制才修剪
1284
+ const tokenCount = this._countMessagesTokens(inputMessages);
1285
+ const tokenLimit = this._maxContextTokens * 0.75; // 75% 阈值,保留缓冲空间
1286
+ if (inputMessages.length <= 30 && tokenCount <= tokenLimit) {
1287
+ return { messages: inputMessages }; // 消息少且 token 未超限,不修剪
1288
+ }
1289
+
1290
+ // 5. 保留配对完整的消息(使用 AI SDK 标准格式)
1291
+ const pruned = this._prepareMessagesForAI(inputMessages);
1292
+ logger.debug(`prepareStep pruned: ${inputMessages.length} -> ${pruned.length} messages`);
1256
1293
 
1257
- // 3. 保留配对完整的消息
1258
- const pruned = this._prepareMessagesForAI(messages);
1259
- return pruned;
1294
+ // 6. 返回 AI SDK 6.x 要求的格式
1295
+ return { messages: pruned };
1260
1296
  } catch (err) {
1261
1297
  logger.error('prepareStep error:', err.message);
1262
- return messages; // 出错时返回原消息
1298
+ return { messages: inputMessages };
1263
1299
  }
1264
1300
  };
1265
1301
 
@@ -1371,6 +1407,9 @@ ${truncatedContent}${truncatedNote}
1371
1407
  messages.push(...validatedMessages);
1372
1408
  }
1373
1409
 
1410
+ // 验证并修复消息中的不完整工具调用(在 API 调用前执行,防止开始聊天的时候就报错)
1411
+ this._validateToolCalls(messages);
1412
+
1374
1413
  // 最终检查:在 API 调用前再次验证 token 数
1375
1414
  const finalMessagesTokens = this._countMessagesTokens(messages);
1376
1415
  const finalToolsTokens = this._countToolsTokens();
@@ -1395,26 +1434,45 @@ ${truncatedContent}${truncatedNote}
1395
1434
  );
1396
1435
  }
1397
1436
 
1398
- // 准备传给 agent 的消息
1399
- const prepareStepChainStream = async ({ stepNumber, messages }) => {
1437
+ // 准备传给 agent 的消息 (流式版本)
1438
+ const prepareStepChainStream = async ({ stepNumber, messages: inputMessages }) => {
1400
1439
  try {
1401
1440
  // 1. 验证消息格式
1402
- if (!Array.isArray(messages)) {
1441
+ if (!Array.isArray(inputMessages)) {
1403
1442
  logger.warn('prepareStep received non-array messages');
1404
- return messages;
1443
+ return { messages: inputMessages };
1405
1444
  }
1406
1445
 
1407
- // 2. 消息数量超过阈值才修剪
1408
- if (messages.length <= 50) {
1409
- return messages;
1446
+ // 2. 修复不完整的工具调用(input 只有 { 或其他无效值)
1447
+ this._validateToolCalls(inputMessages);
1448
+
1449
+ // 3. 验证消息配对并更新 inputMessages(防止 API 报 orphaned tool result 错误)
1450
+ const pairedMessages = this._validateMessagesPairing(inputMessages);
1451
+ if (pairedMessages.length !== inputMessages.length) {
1452
+ logger.debug(
1453
+ `prepareStep pairing: ${inputMessages.length} -> ${pairedMessages.length}`
1454
+ );
1410
1455
  }
1456
+ // 重要:用配对验证后的结果替换 inputMessages
1457
+ inputMessages.length = 0;
1458
+ inputMessages.push(...pairedMessages);
1459
+
1460
+ // 4. Token 数检查:超过限制才修剪
1461
+ const tokenCount = this._countMessagesTokens(inputMessages);
1462
+ const tokenLimit = this._maxContextTokens * 0.75; // 75% 阈值,保留缓冲空间
1463
+ if (inputMessages.length <= 30 && tokenCount <= tokenLimit) {
1464
+ return { messages: inputMessages }; // 消息少且 token 未超限,不修剪
1465
+ }
1466
+
1467
+ // 5. 保留配对完整的消息(使用 AI SDK 标准格式)
1468
+ const pruned = this._prepareMessagesForAI(inputMessages);
1469
+ logger.debug(`prepareStep pruned: ${inputMessages.length} -> ${pruned.length} messages`);
1411
1470
 
1412
- // 3. 保留配对完整的消息
1413
- const pruned = this._prepareMessagesForAI(messages);
1414
- return pruned;
1471
+ // 6. 返回 AI SDK 6.x 要求的格式
1472
+ return { messages: pruned };
1415
1473
  } catch (err) {
1416
1474
  logger.error('prepareStep error:', err.message);
1417
- return messages; // 出错时返回原消息
1475
+ return { messages: inputMessages };
1418
1476
  }
1419
1477
  };
1420
1478
 
@@ -1492,75 +1550,98 @@ ${truncatedContent}${truncatedNote}
1492
1550
  * @private
1493
1551
  */
1494
1552
  _prepareMessagesForAI(messages) {
1495
- if (messages.length <= 10) {
1553
+ if (messages.length <= 15) {
1496
1554
  return messages;
1497
1555
  }
1498
1556
 
1499
- // 收集所有 assistant tool-call
1500
- const assistantToolCalls = new Map(); // toolCallId -> message index
1501
- for (let i = 0; i < messages.length; i++) {
1557
+ // 1. 确定要保留的消息范围(保留最近 N 条)
1558
+ const keepRecentCount = Math.min(30, Math.floor(messages.length * 0.4));
1559
+ const minIndexToKeep = Math.max(0, messages.length - keepRecentCount);
1560
+
1561
+ // 2. 只从保留下来的消息(minIndexToKeep 之后)中收集 assistant 的 tool-call IDs
1562
+ // 重要:这样可以确保只收集保留下来的 assistant 消息的 tool-call IDs
1563
+ // 防止 assistant 消息被修剪但其 tool-call ID 仍然在集合中导致孤儿的 tool-result
1564
+ const assistantToolCallIds = new Set();
1565
+ for (let i = minIndexToKeep; i < messages.length; i++) {
1502
1566
  const msg = messages[i];
1503
1567
  if (msg.role === 'assistant' && Array.isArray(msg.content)) {
1504
1568
  for (const item of msg.content) {
1505
- if (item.type === 'tool-call' && item.toolCallId) {
1506
- assistantToolCalls.set(item.toolCallId, i);
1569
+ if ((item.type === 'tool-call' || item.type === 'tool-use') && item.toolCallId) {
1570
+ assistantToolCallIds.add(item.toolCallId);
1507
1571
  }
1508
1572
  }
1509
1573
  }
1510
1574
  }
1511
1575
 
1512
- // 收集配对的 tool results
1513
- const pairedToolResults = new Set(); // 保留的 tool result indices
1514
- for (let i = 0; i < messages.length; i++) {
1576
+ // 3. 收集有配对的 tool result indices(只在保留下来的消息中检查)
1577
+ const pairedToolResultIndices = new Set();
1578
+ for (let i = minIndexToKeep; i < messages.length; i++) {
1515
1579
  const msg = messages[i];
1516
1580
  if (msg.role === 'tool' && Array.isArray(msg.content)) {
1517
1581
  for (const item of msg.content) {
1518
- if (item.type === 'tool-result' && item.toolCallId) {
1519
- if (assistantToolCalls.has(item.toolCallId)) {
1520
- pairedToolResults.add(i);
1582
+ if ((item.type === 'tool-result' || item.type === 'tool_result') && item.toolCallId) {
1583
+ if (assistantToolCallIds.has(item.toolCallId)) {
1584
+ pairedToolResultIndices.add(i);
1521
1585
  }
1522
1586
  }
1523
1587
  }
1524
1588
  }
1525
1589
  }
1526
1590
 
1527
- // 过滤:保留最近的消息,但确保 tool call/result 配对完整
1528
- const recentCount = Math.max(20, messages.length - 10);
1529
- const minIndexToKeep = messages.length - recentCount;
1591
+ // 4. 构建最终保留索引集合
1592
+ const indicesToKeep = new Set();
1530
1593
 
1531
- const result = [];
1594
+ // minIndexToKeep 开始迭代,和原始代码保持一致
1532
1595
  for (let i = minIndexToKeep; i < messages.length; i++) {
1533
1596
  const msg = messages[i];
1534
-
1535
1597
  if (msg.role === 'tool') {
1536
- // 只保留有配对的 tool messages
1537
- if (pairedToolResults.has(i)) {
1538
- result.push(msg);
1598
+ // 工具消息:只有有配对的才能保留
1599
+ if (pairedToolResultIndices.has(i)) {
1600
+ indicesToKeep.add(i);
1539
1601
  }
1540
- } else if (msg.role === 'assistant') {
1602
+ } else {
1603
+ // 其他角色(user, system, assistant 等)默认保留
1604
+ indicesToKeep.add(i);
1605
+ }
1606
+ }
1607
+
1608
+ // 5. 构建最终结果数组
1609
+ const result = [];
1610
+ for (let i = 0; i < messages.length; i++) {
1611
+ if (!indicesToKeep.has(i)) {
1612
+ continue;
1613
+ }
1614
+
1615
+ const msg = messages[i];
1616
+ if (msg.role === 'assistant' && Array.isArray(msg.content)) {
1541
1617
  // 检查是否有未配对的 tool-call
1542
- if (Array.isArray(msg.content)) {
1543
- const hasUnpairedToolCall = msg.content.some(
1544
- (item) => item.type === 'tool-call' && !assistantToolCalls.has(item.toolCallId)
1545
- );
1546
- if (hasUnpairedToolCall) {
1547
- // 过滤掉未配对的 tool-call
1548
- const filteredContent = msg.content.filter((item) => {
1549
- if (item.type === 'tool-call') {
1550
- return assistantToolCalls.has(item.toolCallId);
1551
- }
1552
- return true;
1553
- });
1554
- if (filteredContent.length > 0) {
1555
- result.push({ ...msg, content: filteredContent });
1618
+ const hasUnpaired = msg.content.some((item) => {
1619
+ if (item.type === 'tool-call' || item.type === 'tool-use') {
1620
+ return !assistantToolCallIds.has(item.toolCallId);
1621
+ }
1622
+ return false;
1623
+ });
1624
+
1625
+ if (hasUnpaired) {
1626
+ // 过滤掉未配对的 tool-call
1627
+ const filteredContent = msg.content.filter((item) => {
1628
+ if (item.type === 'tool-call' || item.type === 'tool-use') {
1629
+ return assistantToolCallIds.has(item.toolCallId);
1556
1630
  }
1557
- } else {
1558
- result.push(msg);
1631
+ return true;
1632
+ });
1633
+
1634
+ if (filteredContent.length > 0) {
1635
+ result.push({ ...msg, content: filteredContent });
1559
1636
  }
1560
1637
  } else {
1561
1638
  result.push(msg);
1562
1639
  }
1640
+ } else if (msg.role === 'tool') {
1641
+ // 工具消息已经在第4步检查过了,直接添加
1642
+ result.push(msg);
1563
1643
  } else {
1644
+ // 其他角色(user, system等)默认保留
1564
1645
  result.push(msg);
1565
1646
  }
1566
1647
  }
@@ -1580,6 +1661,12 @@ ${truncatedContent}${truncatedNote}
1580
1661
  return {};
1581
1662
  }
1582
1663
 
1664
+ // 工具类型检查辅助函数(兼容 AI SDK 6.x)
1665
+ const isToolCall = (item) =>
1666
+ item && (item.type === 'tool-call' || item.type === 'tool-use') && item.toolCallId;
1667
+ const isToolResult = (item) =>
1668
+ item && (item.type === 'tool-result' || item.type === 'tool_result') && item.toolCallId;
1669
+
1583
1670
  // 构建配对索引集合:确保 assistant 的 tool call 和 tool result 不会被单独裁剪
1584
1671
  const assistantIndices = new Map(); // toolCallId -> assistant index
1585
1672
  const toolResultIndices = new Map(); // toolCallId -> tool result indices
@@ -1589,7 +1676,7 @@ ${truncatedContent}${truncatedNote}
1589
1676
  if (msg.role === 'assistant' && msg.content) {
1590
1677
  const content = Array.isArray(msg.content) ? msg.content : [msg.content];
1591
1678
  for (const item of content) {
1592
- if (item.type === 'tool-call' && item.toolCallId) {
1679
+ if (isToolCall(item)) {
1593
1680
  assistantIndices.set(item.toolCallId, i);
1594
1681
  }
1595
1682
  }
@@ -1597,7 +1684,7 @@ ${truncatedContent}${truncatedNote}
1597
1684
  if (msg.role === 'tool' && Array.isArray(msg.content)) {
1598
1685
  // 遍历 tool message 中的所有 tool-results
1599
1686
  for (const item of msg.content) {
1600
- if (item && item.type === 'tool-result' && item.toolCallId) {
1687
+ if (isToolResult(item)) {
1601
1688
  if (!toolResultIndices.has(item.toolCallId)) {
1602
1689
  toolResultIndices.set(item.toolCallId, []);
1603
1690
  }
@@ -1639,16 +1726,18 @@ ${truncatedContent}${truncatedNote}
1639
1726
  /**
1640
1727
  * 验证并修复消息中的 tool call/result 配对
1641
1728
  * 删除没有对应 assistant tool_call 的 tool result(会导致 API 报错)
1729
+ * 兼容 AI SDK 6.x 标准格式
1642
1730
  * @param {Array} messages - 消息列表
1643
1731
  * @private
1644
1732
  */
1645
1733
  _validateMessagesPairing(messages) {
1646
- // 收集所有 assistant 的 tool-call IDs
1734
+ // 收集所有 assistant 的 tool-call IDs(兼容多种类型名)
1647
1735
  const assistantToolCallIds = new Set();
1648
1736
  for (const msg of messages) {
1649
1737
  if (msg.role === 'assistant' && Array.isArray(msg.content)) {
1650
1738
  for (const item of msg.content) {
1651
- if (item.type === 'tool-call' && item.toolCallId) {
1739
+ // 兼容 tool-call tool-use 两种类型
1740
+ if ((item.type === 'tool-call' || item.type === 'tool-use') && item.toolCallId) {
1652
1741
  assistantToolCallIds.add(item.toolCallId);
1653
1742
  }
1654
1743
  }
@@ -1661,7 +1750,12 @@ ${truncatedContent}${truncatedNote}
1661
1750
  if (msg.role === 'tool' && Array.isArray(msg.content)) {
1662
1751
  const originalLength = msg.content.length;
1663
1752
  msg.content = msg.content.filter((item) => {
1664
- if (item && item.type === 'tool-result' && item.toolCallId) {
1753
+ // 兼容 tool-result tool_result 两种类型
1754
+ if (
1755
+ item &&
1756
+ (item.type === 'tool-result' || item.type === 'tool_result') &&
1757
+ item.toolCallId
1758
+ ) {
1665
1759
  if (!assistantToolCallIds.has(item.toolCallId)) {
1666
1760
  removedCount++;
1667
1761
  return false; // 删除没有配对的 tool result
@@ -1694,17 +1788,21 @@ ${truncatedContent}${truncatedNote}
1694
1788
  * 校验并修复消息中的工具调用参数
1695
1789
  * 移除不完整的 JSON(如只有 "{" )的工具调用
1696
1790
  * 同时清理 tool result 中的错误信息
1791
+ * 兼容 AI SDK 6.x 标准格式
1697
1792
  * @param {Array} messages - 消息列表
1698
1793
  * @private
1699
1794
  */
1700
1795
  _validateToolCalls(messages) {
1701
1796
  let fixedCount = 0;
1797
+ // 收集被跳过的 toolCallId,用于清理对应的 tool-result
1798
+ const invalidatedToolCallIds = new Set();
1702
1799
 
1703
1800
  for (const msg of messages) {
1704
1801
  // 清理 assistant 消息中的不完整 tool-call
1705
1802
  if (msg.role === 'assistant' && Array.isArray(msg.content)) {
1706
1803
  for (const item of msg.content) {
1707
- if (item.type !== 'tool-call') {
1804
+ // 兼容 tool-call 和 tool-use 两种类型
1805
+ if (item.type !== 'tool-call' && item.type !== 'tool-use') {
1708
1806
  continue;
1709
1807
  }
1710
1808
 
@@ -1717,6 +1815,13 @@ ${truncatedContent}${truncatedNote}
1717
1815
  const trimmed = input.trim();
1718
1816
  if (trimmed === '{' || trimmed === '' || !trimmed.startsWith('{')) {
1719
1817
  // 不完整的 JSON,移除这个 tool-call
1818
+ // 记录 toolCallId,以便后续清理对应的 tool-result
1819
+ if (item.toolCallId) {
1820
+ invalidatedToolCallIds.add(item.toolCallId);
1821
+ }
1822
+ logger.warn(
1823
+ `_validateToolCalls: invalid tool-call input="${input}", toolCallId=${item.toolCallId}, converting to text`
1824
+ );
1720
1825
  item.type = 'text';
1721
1826
  item.text = `(工具调用 ${item.toolName} 参数不完整,已跳过)`;
1722
1827
  delete item.toolCallId;
@@ -1726,51 +1831,37 @@ ${truncatedContent}${truncatedNote}
1726
1831
  }
1727
1832
  }
1728
1833
  }
1834
+ }
1729
1835
 
1730
- // 清理 tool result 中的无效 JSON 错误信息
1731
- if (msg.role === 'tool' && Array.isArray(msg.content)) {
1732
- for (const item of msg.content) {
1733
- if (item.type !== 'tool-result') {
1734
- continue;
1735
- }
1736
-
1737
- const output = item.output;
1738
- let errorText = null;
1739
-
1740
- // 处理字符串类型 output
1741
- if (typeof output === 'string') {
1742
- errorText = output;
1743
- } else if (typeof output === 'object' && output !== null) {
1744
- // 处理 { type: 'error-text', value: '...' } 结构
1745
- if (output.value && typeof output.value === 'string') {
1746
- errorText = output.value;
1836
+ // 如果有无效的 tool-call,清理对应的 tool-result
1837
+ if (invalidatedToolCallIds.size > 0) {
1838
+ logger.warn(
1839
+ `_validateToolCalls: removing ${invalidatedToolCallIds.size} tool-results with invalidated toolCallIds`
1840
+ );
1841
+ for (const msg of messages) {
1842
+ if (msg.role === 'tool' && Array.isArray(msg.content)) {
1843
+ // 过滤掉引用了无效 toolCallId 的 tool-result
1844
+ const oldLen = msg.content.length;
1845
+ msg.content = msg.content.filter((item) => {
1846
+ if (item.type !== 'tool-result' && item.type !== 'tool_result') {
1847
+ return true;
1747
1848
  }
1748
- }
1749
-
1750
- if (!errorText) {
1751
- continue;
1752
- }
1753
-
1754
- // 检查是否包含 "JSON parsing failed" 或类似的不完整 JSON 错误
1755
- if (errorText.includes('JSON parsing failed') || errorText.includes('Invalid input')) {
1756
- const toolName = item.toolName || 'unknown';
1757
- // 替换为更简洁的错误信息
1758
- if (typeof output === 'string') {
1759
- item.output = `(工具 ${toolName} 参数不完整,已跳过)`;
1760
- } else {
1761
- item.output = {
1762
- type: 'error-text',
1763
- value: `(工具 ${toolName} 参数不完整,已跳过)`,
1764
- };
1849
+ // 如果 tool-result 引用的 toolCallId 已被标记为无效,则移除
1850
+ if (item.toolCallId && invalidatedToolCallIds.has(item.toolCallId)) {
1851
+ logger.warn(
1852
+ `_validateToolCalls: removing orphaned tool-result with toolCallId=${item.toolCallId}`
1853
+ );
1854
+ fixedCount++;
1855
+ return false;
1765
1856
  }
1766
- fixedCount++;
1767
- }
1857
+ return true;
1858
+ });
1768
1859
  }
1769
1860
  }
1770
1861
  }
1771
1862
 
1772
1863
  if (fixedCount > 0) {
1773
- logger.info(`Fixed ${fixedCount} incomplete tool calls/results`);
1864
+ logger.info(`_validateToolCalls: Fixed ${fixedCount} incomplete tool calls/results`);
1774
1865
  }
1775
1866
  }
1776
1867