@librechat/agents 3.3.2 → 3.3.4

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 (162) hide show
  1. package/dist/cjs/agents/AgentContext.cjs +2 -1
  2. package/dist/cjs/agents/AgentContext.cjs.map +1 -1
  3. package/dist/cjs/graphs/Graph.cjs +342 -109
  4. package/dist/cjs/graphs/Graph.cjs.map +1 -1
  5. package/dist/cjs/graphs/MultiAgentGraph.cjs +3 -1
  6. package/dist/cjs/graphs/MultiAgentGraph.cjs.map +1 -1
  7. package/dist/cjs/langfuse.cjs +6 -0
  8. package/dist/cjs/langfuse.cjs.map +1 -1
  9. package/dist/cjs/llm/anthropic/utils/message_inputs.cjs +79 -6
  10. package/dist/cjs/llm/anthropic/utils/message_inputs.cjs.map +1 -1
  11. package/dist/cjs/llm/bedrock/utils/message_inputs.cjs +37 -10
  12. package/dist/cjs/llm/bedrock/utils/message_inputs.cjs.map +1 -1
  13. package/dist/cjs/llm/google/utils/common.cjs +19 -7
  14. package/dist/cjs/llm/google/utils/common.cjs.map +1 -1
  15. package/dist/cjs/llm/invoke.cjs +64 -3
  16. package/dist/cjs/llm/invoke.cjs.map +1 -1
  17. package/dist/cjs/llm/openai/index.cjs +41 -5
  18. package/dist/cjs/llm/openai/index.cjs.map +1 -1
  19. package/dist/cjs/llm/openai/utils/index.cjs +25 -4
  20. package/dist/cjs/llm/openai/utils/index.cjs.map +1 -1
  21. package/dist/cjs/llm/openrouter/index.cjs +4 -1
  22. package/dist/cjs/llm/openrouter/index.cjs.map +1 -1
  23. package/dist/cjs/main.cjs +19 -2
  24. package/dist/cjs/messages/cache.cjs +54 -33
  25. package/dist/cjs/messages/cache.cjs.map +1 -1
  26. package/dist/cjs/messages/contextPruning.cjs +17 -43
  27. package/dist/cjs/messages/contextPruning.cjs.map +1 -1
  28. package/dist/cjs/messages/core.cjs +315 -23
  29. package/dist/cjs/messages/core.cjs.map +1 -1
  30. package/dist/cjs/messages/format.cjs +218 -60
  31. package/dist/cjs/messages/format.cjs.map +1 -1
  32. package/dist/cjs/messages/index.cjs +1 -1
  33. package/dist/cjs/messages/prune.cjs +503 -127
  34. package/dist/cjs/messages/prune.cjs.map +1 -1
  35. package/dist/cjs/stream.cjs +7 -3
  36. package/dist/cjs/stream.cjs.map +1 -1
  37. package/dist/cjs/summarization/node.cjs +28 -14
  38. package/dist/cjs/summarization/node.cjs.map +1 -1
  39. package/dist/cjs/tools/ToolNode.cjs +54 -41
  40. package/dist/cjs/tools/ToolNode.cjs.map +1 -1
  41. package/dist/cjs/tools/toolOutputReferences.cjs +6 -0
  42. package/dist/cjs/tools/toolOutputReferences.cjs.map +1 -1
  43. package/dist/cjs/utils/index.cjs +1 -1
  44. package/dist/cjs/utils/llm.cjs +1 -1
  45. package/dist/cjs/utils/llm.cjs.map +1 -1
  46. package/dist/cjs/utils/tokens.cjs +307 -20
  47. package/dist/cjs/utils/tokens.cjs.map +1 -1
  48. package/dist/cjs/utils/toolContent.cjs +1514 -0
  49. package/dist/cjs/utils/toolContent.cjs.map +1 -0
  50. package/dist/esm/agents/AgentContext.mjs +2 -1
  51. package/dist/esm/agents/AgentContext.mjs.map +1 -1
  52. package/dist/esm/graphs/Graph.mjs +349 -116
  53. package/dist/esm/graphs/Graph.mjs.map +1 -1
  54. package/dist/esm/graphs/MultiAgentGraph.mjs +3 -1
  55. package/dist/esm/graphs/MultiAgentGraph.mjs.map +1 -1
  56. package/dist/esm/langfuse.mjs +6 -0
  57. package/dist/esm/langfuse.mjs.map +1 -1
  58. package/dist/esm/llm/anthropic/utils/message_inputs.mjs +79 -6
  59. package/dist/esm/llm/anthropic/utils/message_inputs.mjs.map +1 -1
  60. package/dist/esm/llm/bedrock/utils/message_inputs.mjs +37 -10
  61. package/dist/esm/llm/bedrock/utils/message_inputs.mjs.map +1 -1
  62. package/dist/esm/llm/google/utils/common.mjs +19 -7
  63. package/dist/esm/llm/google/utils/common.mjs.map +1 -1
  64. package/dist/esm/llm/invoke.mjs +65 -5
  65. package/dist/esm/llm/invoke.mjs.map +1 -1
  66. package/dist/esm/llm/openai/index.mjs +41 -5
  67. package/dist/esm/llm/openai/index.mjs.map +1 -1
  68. package/dist/esm/llm/openai/utils/index.mjs +25 -4
  69. package/dist/esm/llm/openai/utils/index.mjs.map +1 -1
  70. package/dist/esm/llm/openrouter/index.mjs +4 -1
  71. package/dist/esm/llm/openrouter/index.mjs.map +1 -1
  72. package/dist/esm/main.mjs +7 -7
  73. package/dist/esm/messages/cache.mjs +55 -34
  74. package/dist/esm/messages/cache.mjs.map +1 -1
  75. package/dist/esm/messages/contextPruning.mjs +17 -43
  76. package/dist/esm/messages/contextPruning.mjs.map +1 -1
  77. package/dist/esm/messages/core.mjs +305 -24
  78. package/dist/esm/messages/core.mjs.map +1 -1
  79. package/dist/esm/messages/format.mjs +218 -61
  80. package/dist/esm/messages/format.mjs.map +1 -1
  81. package/dist/esm/messages/index.mjs +1 -1
  82. package/dist/esm/messages/prune.mjs +500 -128
  83. package/dist/esm/messages/prune.mjs.map +1 -1
  84. package/dist/esm/stream.mjs +7 -3
  85. package/dist/esm/stream.mjs.map +1 -1
  86. package/dist/esm/summarization/node.mjs +28 -14
  87. package/dist/esm/summarization/node.mjs.map +1 -1
  88. package/dist/esm/tools/ToolNode.mjs +54 -41
  89. package/dist/esm/tools/ToolNode.mjs.map +1 -1
  90. package/dist/esm/tools/toolOutputReferences.mjs +6 -0
  91. package/dist/esm/tools/toolOutputReferences.mjs.map +1 -1
  92. package/dist/esm/utils/index.mjs +1 -1
  93. package/dist/esm/utils/llm.mjs +1 -1
  94. package/dist/esm/utils/llm.mjs.map +1 -1
  95. package/dist/esm/utils/tokens.mjs +307 -21
  96. package/dist/esm/utils/tokens.mjs.map +1 -1
  97. package/dist/esm/utils/toolContent.mjs +1503 -0
  98. package/dist/esm/utils/toolContent.mjs.map +1 -0
  99. package/dist/types/graphs/Graph.d.ts +1 -1
  100. package/dist/types/llm/invoke.d.ts +27 -1
  101. package/dist/types/llm/openai/index.d.ts +4 -0
  102. package/dist/types/llm/openai/utils/index.d.ts +2 -0
  103. package/dist/types/llm/openrouter/index.d.ts +5 -0
  104. package/dist/types/messages/contextPruning.d.ts +2 -1
  105. package/dist/types/messages/core.d.ts +36 -0
  106. package/dist/types/messages/format.d.ts +6 -0
  107. package/dist/types/messages/prune.d.ts +16 -12
  108. package/dist/types/types/stream.d.ts +1 -1
  109. package/dist/types/utils/tokens.d.ts +7 -0
  110. package/dist/types/utils/toolContent.d.ts +107 -0
  111. package/package.json +13 -9
  112. package/src/__tests__/stream.eagerEventExecution.test.ts +74 -0
  113. package/src/agents/AgentContext.ts +1 -0
  114. package/src/graphs/Graph.ts +698 -200
  115. package/src/graphs/MultiAgentGraph.ts +6 -1
  116. package/src/graphs/__tests__/Graph.contextOverflow.test.ts +1128 -4
  117. package/src/langfuse.ts +15 -0
  118. package/src/llm/anthropic/utils/message_inputs.ts +158 -12
  119. package/src/llm/anthropic/utils/tool-id-normalization.test.ts +109 -0
  120. package/src/llm/bedrock/utils/message_inputs.test.ts +49 -1
  121. package/src/llm/bedrock/utils/message_inputs.ts +86 -13
  122. package/src/llm/google/utils/common.test.ts +75 -9
  123. package/src/llm/google/utils/common.ts +34 -3
  124. package/src/llm/invoke.test.ts +488 -0
  125. package/src/llm/invoke.ts +196 -2
  126. package/src/llm/openai/index.ts +106 -6
  127. package/src/llm/openai/utils/index.ts +109 -53
  128. package/src/llm/openai/utils/messages.test.ts +330 -1
  129. package/src/llm/openrouter/index.ts +12 -2
  130. package/src/llm/openrouter/reasoning.test.ts +314 -0
  131. package/src/messages/__tests__/observationMasking.test.ts +93 -2
  132. package/src/messages/cache.tail.test.ts +193 -0
  133. package/src/messages/cache.test.ts +113 -0
  134. package/src/messages/cache.ts +92 -60
  135. package/src/messages/contextPruning.test.ts +184 -0
  136. package/src/messages/contextPruning.ts +49 -42
  137. package/src/messages/core.ts +653 -40
  138. package/src/messages/ensureThinkingBlock.test.ts +49 -1
  139. package/src/messages/foldToollessToolBlocks.test.ts +163 -5
  140. package/src/messages/format.ts +363 -91
  141. package/src/messages/formatAgentMessages.test.ts +604 -14
  142. package/src/messages/formatAgentMessages.tools.test.ts +103 -0
  143. package/src/messages/prune.ts +996 -183
  144. package/src/specs/langfuse-callbacks.test.ts +203 -1
  145. package/src/specs/prune.test.ts +1083 -6
  146. package/src/specs/summarization-unit.test.ts +105 -0
  147. package/src/specs/tokens.test.ts +609 -32
  148. package/src/stream.ts +23 -15
  149. package/src/summarization/__tests__/node.test.ts +77 -0
  150. package/src/summarization/node.ts +46 -18
  151. package/src/tools/ToolNode.ts +147 -68
  152. package/src/tools/__tests__/ToolNode.onResultCompletion.test.ts +148 -1
  153. package/src/tools/__tests__/ToolNode.outputReferences.test.ts +112 -3
  154. package/src/tools/__tests__/annotateMessagesForLLM.test.ts +32 -0
  155. package/src/tools/__tests__/directToolHooks.test.ts +255 -7
  156. package/src/tools/toolOutputReferences.ts +6 -0
  157. package/src/types/stream.ts +1 -1
  158. package/src/utils/llm.test.ts +18 -0
  159. package/src/utils/llm.ts +4 -1
  160. package/src/utils/tokens.ts +586 -31
  161. package/src/utils/toolContent.test.ts +1432 -0
  162. package/src/utils/toolContent.ts +2707 -0
@@ -1,5 +1,7 @@
1
1
  import "../common/enum.mjs";
2
2
  import "../common/index.mjs";
3
+ import { HARD_MAX_TOOL_RESULT_CHARS } from "../utils/truncation.mjs";
4
+ import { compactToolContent, getToolContentCharLength, isAtomicToolContentBlock, serializeStructuredValueBounded } from "../utils/toolContent.mjs";
3
5
  import { toLangChainContent, toLangChainMessageFields } from "./langchain.mjs";
4
6
  import { normalizeAnthropicToolCallId } from "../llm/anthropic/utils/message_inputs.mjs";
5
7
  import { emitAgentLog } from "../utils/events.mjs";
@@ -175,6 +177,10 @@ function hasToolCallOutput(part) {
175
177
  const output = part.tool_call?.output;
176
178
  return output != null && output !== "";
177
179
  }
180
+ function formatToolCallOutput(output) {
181
+ if (output == null) return "";
182
+ return compactToolContent(output, HARD_MAX_TOOL_RESULT_CHARS).content;
183
+ }
178
184
  /**
179
185
  * Helper function to format an assistant message
180
186
  * @param message The message to format
@@ -312,7 +318,7 @@ function formatAssistantMessage(message, options) {
312
318
  formattedMessages.push(withMessageRole(new ToolMessage({
313
319
  tool_call_id: tool_call.id ?? "",
314
320
  name: tool_call.name,
315
- content: output != null ? output : ""
321
+ content: formatToolCallOutput(output)
316
322
  }), "tool"));
317
323
  } else if (part.type === "think" || part.type === "thinking" || part.type === "reasoning" || part.type === "reasoning_content" || part.type === "redacted_thinking") {
318
324
  hasReasoning = true;
@@ -583,7 +589,10 @@ function contentPartCharLength(part) {
583
589
  if (typeof record.thinking === "string") len += record.thinking.length;
584
590
  const { input } = record;
585
591
  if (typeof input === "string") len += input.length;
586
- else if (input != null && typeof input === "object") len += JSON.stringify(input).length;
592
+ else if (input != null && typeof input === "object") {
593
+ const measured = serializeStructuredValueBounded(input, 0).originalChars;
594
+ len += measured === Number.MAX_SAFE_INTEGER ? HARD_MAX_TOOL_RESULT_CHARS : Math.min(measured, HARD_MAX_TOOL_RESULT_CHARS);
595
+ }
587
596
  return len;
588
597
  }
589
598
  /** Extracts the skillName from a skill tool_call's args (string or object). */
@@ -821,7 +830,10 @@ const formatAgentMessages = (payload, indexTokenCountMap, tools, skills, options
821
830
  if (typeof tc.name === "string") len += tc.name.length;
822
831
  const { args } = tc;
823
832
  if (typeof args === "string") len += args.length;
824
- else if (args != null) len += JSON.stringify(args).length;
833
+ else if (args != null) {
834
+ const measured = serializeStructuredValueBounded(args, 0).originalChars;
835
+ len += measured === Number.MAX_SAFE_INTEGER ? HARD_MAX_TOOL_RESULT_CHARS : Math.min(measured, HARD_MAX_TOOL_RESULT_CHARS);
836
+ }
825
837
  }
826
838
  lengths[k] = len;
827
839
  totalLength += len;
@@ -867,10 +879,99 @@ function shiftIndexTokenCountMap(indexTokenCountMap, instructionsTokenCount) {
867
879
  }
868
880
  return shiftedMap;
869
881
  }
870
- /** Block types that contain binary image data and must be preserved structurally. */
871
- const IMAGE_BLOCK_TYPES = new Set(["image_url", "image"]);
872
882
  /** Checks whether a BaseMessage is a tool-role message. */
873
883
  const isToolMessage$1 = (m) => m instanceof ToolMessage || "role" in m && m.role === "tool";
884
+ const PORTABLE_FOLDED_MEDIA_TYPES = new Set(["image", "image_url"]);
885
+ const MAX_FOLDED_BLOCK_CHARS = 8e3;
886
+ const MAX_FOLDED_CONTEXT_CHARS = HARD_MAX_TOOL_RESULT_CHARS;
887
+ const MAX_FOLDED_CONTEXT_WORK = 1e5;
888
+ const FOLDED_CONTEXT_TRUNCATION_NOTICE = "… [additional folded context omitted]";
889
+ const syntheticProviderContextMessages = /* @__PURE__ */ new WeakSet();
890
+ function createFoldContextBudget() {
891
+ return {
892
+ remainingChars: MAX_FOLDED_CONTEXT_CHARS,
893
+ remainingWork: MAX_FOLDED_CONTEXT_WORK,
894
+ truncated: false
895
+ };
896
+ }
897
+ function readFoldedDataProperty(value, key) {
898
+ if (value == null || typeof value !== "object") return;
899
+ try {
900
+ const descriptor = Object.getOwnPropertyDescriptor(value, key);
901
+ return descriptor != null && "value" in descriptor ? descriptor.value : void 0;
902
+ } catch {
903
+ return;
904
+ }
905
+ }
906
+ /**
907
+ * Adds one logical line without first concatenating caller-controlled strings.
908
+ * The shared budget covers every synthetic message emitted by one transform,
909
+ * so many individually bounded blocks cannot build an unbounded aggregate.
910
+ */
911
+ function appendFoldedLine(textChunks, budget, pieces) {
912
+ if (budget.remainingChars <= 0) {
913
+ budget.truncated = true;
914
+ return false;
915
+ }
916
+ const separatorChars = textChunks.length > 0 ? 1 : 0;
917
+ const available = budget.remainingChars - separatorChars;
918
+ if (available <= 0) {
919
+ budget.remainingChars = 0;
920
+ budget.truncated = true;
921
+ return false;
922
+ }
923
+ let totalChars = 0;
924
+ for (const piece of pieces) totalChars = Math.min(Number.MAX_SAFE_INTEGER, totalChars + piece.length);
925
+ if (totalChars <= available) {
926
+ const line = pieces.join("");
927
+ textChunks.push(line);
928
+ budget.remainingChars -= separatorChars + line.length;
929
+ return true;
930
+ }
931
+ const notice = FOLDED_CONTEXT_TRUNCATION_NOTICE.slice(0, available);
932
+ let remainingHeadChars = Math.max(0, available - notice.length);
933
+ const boundedPieces = [];
934
+ for (const piece of pieces) {
935
+ if (remainingHeadChars <= 0) break;
936
+ const retained = piece.slice(0, remainingHeadChars);
937
+ boundedPieces.push(retained);
938
+ remainingHeadChars -= retained.length;
939
+ }
940
+ boundedPieces.push(notice);
941
+ textChunks.push(boundedPieces.join(""));
942
+ budget.remainingChars = 0;
943
+ budget.truncated = true;
944
+ return false;
945
+ }
946
+ function consumeFoldedWork(textChunks, budget) {
947
+ if (budget.remainingChars <= 0) return false;
948
+ if (budget.remainingWork-- > 0) return true;
949
+ appendFoldedLine(textChunks, budget, [FOLDED_CONTEXT_TRUNCATION_NOTICE]);
950
+ budget.remainingChars = 0;
951
+ budget.truncated = true;
952
+ return false;
953
+ }
954
+ function serializeFoldedValue(value) {
955
+ const compacted = compactToolContent(value, MAX_FOLDED_BLOCK_CHARS).content;
956
+ return typeof compacted === "string" ? compacted : serializeStructuredValueBounded(compacted, MAX_FOLDED_BLOCK_CHARS).content;
957
+ }
958
+ function markSyntheticProviderContext(message) {
959
+ syntheticProviderContextMessages.add(message);
960
+ return message;
961
+ }
962
+ function appendSyntheticProviderContextMessage(result, parts) {
963
+ if (parts.length === 0) return false;
964
+ result.push(markSyntheticProviderContext(withMessageRole(new HumanMessage({ content: toLangChainContent(parts) }), "user")));
965
+ return true;
966
+ }
967
+ /**
968
+ * Identifies provider-context placeholders created by this module without
969
+ * trusting user-controlled content prefixes or leaking marker metadata onto
970
+ * the provider wire.
971
+ */
972
+ function isSyntheticProviderContextMessage(message) {
973
+ return syntheticProviderContextMessages.has(message);
974
+ }
874
975
  /** Flushes accumulated text chunks into `parts` as a single text block. */
875
976
  function flushTextChunks(textChunks, parts) {
876
977
  if (textChunks.length === 0) return;
@@ -882,88 +983,139 @@ function flushTextChunks(textChunks, parts) {
882
983
  }
883
984
  /**
884
985
  * Appends a single message's content to the running `textChunks` / `parts`
885
- * accumulators. Image blocks are shallow-copied into `parts` as-is so that
886
- * binary data (base64 images) never becomes text tokens. All other block
887
- * types are serialized to text unrecognized types are JSON-serialized
888
- * rather than silently dropped.
986
+ * accumulators. Portable image blocks are shallow-copied into `parts` so
987
+ * binary data never becomes text tokens. Provider-specific media/resource
988
+ * blocks are retained as bounded text so a folded cross-provider history
989
+ * cannot contain an unsupported native block or JSON-expand without limit.
889
990
  *
890
991
  * When `content` is an array containing tool_use blocks, `tool_calls` is NOT
891
992
  * additionally serialized (avoiding double output). `tool_calls` is used as
892
993
  * a fallback when `content` is a plain string or an array with no tool_use.
893
994
  */
894
- function appendMessageContent(msg, role, textChunks, parts) {
995
+ function appendMessageContent(msg, role, textChunks, parts, budget) {
895
996
  const { content } = msg;
896
997
  if (typeof content === "string") {
897
- if (content) textChunks.push(`${role}: ${content}`);
898
- appendToolCalls(msg, role, textChunks);
998
+ if (content && consumeFoldedWork(textChunks, budget)) appendFoldedLine(textChunks, budget, [`${role}: `, content]);
999
+ appendToolCalls(msg, role, textChunks, budget);
899
1000
  return;
900
1001
  }
901
1002
  if (!Array.isArray(content)) {
902
- appendToolCalls(msg, role, textChunks);
1003
+ appendToolCalls(msg, role, textChunks, budget);
903
1004
  return;
904
1005
  }
905
1006
  let hasToolUseBlock = false;
906
1007
  for (const block of content) {
907
- if (IMAGE_BLOCK_TYPES.has(block.type ?? "")) {
908
- flushTextChunks(textChunks, parts);
909
- parts.push({ ...block });
1008
+ if (!consumeFoldedWork(textChunks, budget)) {
1009
+ hasToolUseBlock = true;
1010
+ break;
1011
+ }
1012
+ const blockTypeValue = readFoldedDataProperty(block, "type");
1013
+ const blockType = typeof blockTypeValue === "string" ? blockTypeValue : void 0;
1014
+ if (blockType !== "tool_use" && blockType !== "tool_call" && blockType !== "tool_result" && isAtomicToolContentBlock(block)) {
1015
+ if (PORTABLE_FOLDED_MEDIA_TYPES.has(blockType ?? "")) {
1016
+ const blockChars = getToolContentCharLength([block]);
1017
+ if (blockChars > budget.remainingChars) {
1018
+ appendFoldedLine(textChunks, budget, [`${role}: [${blockType ?? "media"} omitted: folded context limit]`]);
1019
+ continue;
1020
+ }
1021
+ flushTextChunks(textChunks, parts);
1022
+ parts.push({ ...block });
1023
+ budget.remainingChars -= blockChars;
1024
+ } else appendFoldedLine(textChunks, budget, [`${role}: [${blockType ?? "media"}] `, serializeFoldedValue(block)]);
910
1025
  continue;
911
1026
  }
912
- if (block.type === "tool_use") {
1027
+ if (blockType === "tool_use") {
913
1028
  hasToolUseBlock = true;
914
- textChunks.push(`${role}: [tool_use] ${String(block.name ?? "")} ${JSON.stringify(block.input ?? {})}`);
1029
+ const name = readFoldedDataProperty(block, "name");
1030
+ const input = readFoldedDataProperty(block, "input");
1031
+ appendFoldedLine(textChunks, budget, [`${role}: [tool_use] ${typeof name === "string" ? name : ""} `, serializeFoldedValue(input ?? {})]);
915
1032
  continue;
916
1033
  }
917
- if (block.type === "tool_call") {
1034
+ if (blockType === "tool_call") {
918
1035
  hasToolUseBlock = true;
919
- const nested = block.tool_call;
920
- const name = String(nested?.name ?? block.name ?? "");
921
- const rawArgs = nested?.args ?? block.args ?? {};
922
- const argsText = typeof rawArgs === "string" ? rawArgs : JSON.stringify(rawArgs);
923
- textChunks.push(`${role}: [tool_use] ${name} ${argsText}`.trimEnd());
924
- const output = nested?.output;
925
- if (output != null && output !== "") textChunks.push(`Tool: ${String(output)}`);
1036
+ const nested = readFoldedDataProperty(block, "tool_call");
1037
+ const nestedName = readFoldedDataProperty(nested, "name");
1038
+ const topLevelName = readFoldedDataProperty(block, "name");
1039
+ let name = "";
1040
+ if (typeof nestedName === "string") name = nestedName;
1041
+ else if (typeof topLevelName === "string") name = topLevelName;
1042
+ const nestedArgs = readFoldedDataProperty(nested, "args");
1043
+ const topLevelArgs = readFoldedDataProperty(block, "args");
1044
+ const rawArgs = nestedArgs ?? topLevelArgs ?? {};
1045
+ const argsText = typeof rawArgs === "string" ? rawArgs : serializeFoldedValue(rawArgs);
1046
+ appendFoldedLine(textChunks, budget, [`${role}: [tool_use] ${name}${argsText ? " " : ""}`, argsText]);
1047
+ const output = readFoldedDataProperty(nested, "output");
1048
+ if (output != null && output !== "") appendFoldedLine(textChunks, budget, ["Tool: ", typeof output === "string" ? output : serializeFoldedValue(output)]);
926
1049
  continue;
927
1050
  }
928
- if (block.type === "tool_result") {
1051
+ if (blockType === "tool_result") {
929
1052
  hasToolUseBlock = true;
930
- const inner = block.content;
1053
+ const inner = readFoldedDataProperty(block, "content");
931
1054
  if (typeof inner === "string") {
932
- if (inner) textChunks.push(`${role}: [tool_result] ${inner}`);
933
- } else if (Array.isArray(inner)) for (const innerBlock of inner) if (typeof innerBlock === "string") {
934
- if (innerBlock) textChunks.push(`${role}: [tool_result] ${innerBlock}`);
935
- } else if (IMAGE_BLOCK_TYPES.has(innerBlock.type ?? "")) {
936
- flushTextChunks(textChunks, parts);
937
- parts.push({ ...innerBlock });
938
- } else {
939
- const innerText = innerBlock.text ?? innerBlock.input;
940
- textChunks.push(`${role}: [tool_result] ${typeof innerText === "string" && innerText ? innerText : JSON.stringify(innerBlock)}`);
1055
+ if (inner) appendFoldedLine(textChunks, budget, [`${role}: [tool_result] `, inner]);
1056
+ } else if (Array.isArray(inner)) for (const innerBlock of inner) {
1057
+ if (!consumeFoldedWork(textChunks, budget)) break;
1058
+ if (typeof innerBlock === "string") {
1059
+ if (innerBlock) appendFoldedLine(textChunks, budget, [`${role}: [tool_result] `, innerBlock]);
1060
+ } else {
1061
+ const innerTypeValue = readFoldedDataProperty(innerBlock, "type");
1062
+ const innerType = typeof innerTypeValue === "string" ? innerTypeValue : void 0;
1063
+ if (isAtomicToolContentBlock(innerBlock) && PORTABLE_FOLDED_MEDIA_TYPES.has(innerType ?? "")) {
1064
+ const blockChars = getToolContentCharLength([innerBlock]);
1065
+ if (blockChars <= budget.remainingChars) {
1066
+ flushTextChunks(textChunks, parts);
1067
+ parts.push({ ...innerBlock });
1068
+ budget.remainingChars -= blockChars;
1069
+ } else appendFoldedLine(textChunks, budget, [`${role}: [${innerType ?? "media"} omitted: folded context limit]`]);
1070
+ } else {
1071
+ const textValue = readFoldedDataProperty(innerBlock, "text");
1072
+ const inputValue = readFoldedDataProperty(innerBlock, "input");
1073
+ const innerText = textValue ?? inputValue;
1074
+ appendFoldedLine(textChunks, budget, [`${role}: [tool_result] `, typeof innerText === "string" && innerText ? innerText : serializeFoldedValue(innerBlock)]);
1075
+ }
1076
+ }
941
1077
  }
942
- else if (inner != null) textChunks.push(`${role}: [tool_result] ${JSON.stringify(inner)}`);
1078
+ else if (inner != null) appendFoldedLine(textChunks, budget, [`${role}: [tool_result] `, serializeFoldedValue(inner)]);
943
1079
  continue;
944
1080
  }
945
- const text = block.text ?? block.input;
1081
+ const text = readFoldedDataProperty(block, "text") ?? readFoldedDataProperty(block, "input");
946
1082
  if (typeof text === "string" && text) {
947
- textChunks.push(`${role}: ${text}`);
1083
+ appendFoldedLine(textChunks, budget, [`${role}: `, text]);
948
1084
  continue;
949
1085
  }
950
- if (block.type != null && block.type !== "") textChunks.push(`${role}: [${block.type}] ${JSON.stringify(block)}`);
1086
+ if (blockType != null && blockType !== "") appendFoldedLine(textChunks, budget, [`${role}: [${blockType}] `, serializeFoldedValue(block)]);
951
1087
  }
952
- if (!hasToolUseBlock) appendToolCalls(msg, role, textChunks);
1088
+ if (!hasToolUseBlock) appendToolCalls(msg, role, textChunks, budget);
953
1089
  }
954
- function appendToolCalls(msg, role, textChunks) {
1090
+ function appendToolCalls(msg, role, textChunks, budget) {
955
1091
  if (role !== "AI") return;
956
1092
  const aiMsg = msg;
957
1093
  if (aiMsg.tool_calls && aiMsg.tool_calls.length > 0) {
958
- for (const tc of aiMsg.tool_calls) textChunks.push(`AI: [tool_call] ${tc.name}(${JSON.stringify(tc.args)})`);
1094
+ for (const tc of aiMsg.tool_calls) {
1095
+ if (!consumeFoldedWork(textChunks, budget)) break;
1096
+ const name = readFoldedDataProperty(tc, "name");
1097
+ const args = readFoldedDataProperty(tc, "args");
1098
+ appendFoldedLine(textChunks, budget, [
1099
+ `AI: [tool_call] ${typeof name === "string" ? name : ""}(`,
1100
+ serializeFoldedValue(args),
1101
+ ")"
1102
+ ]);
1103
+ }
959
1104
  return;
960
1105
  }
961
1106
  const rawToolCalls = aiMsg.additional_kwargs.tool_calls;
962
1107
  if (!Array.isArray(rawToolCalls)) return;
963
1108
  for (const tc of rawToolCalls) {
964
- const fn = tc.function;
1109
+ if (!consumeFoldedWork(textChunks, budget)) break;
1110
+ const fn = readFoldedDataProperty(tc, "function");
965
1111
  if (fn == null) continue;
966
- textChunks.push(`AI: [tool_call] ${String(fn.name ?? "")}(${String(fn.arguments ?? "")})`);
1112
+ const name = readFoldedDataProperty(fn, "name");
1113
+ const args = readFoldedDataProperty(fn, "arguments");
1114
+ appendFoldedLine(textChunks, budget, [
1115
+ `AI: [tool_call] ${typeof name === "string" ? name : ""}(`,
1116
+ typeof args === "string" ? args : "",
1117
+ ")"
1118
+ ]);
967
1119
  }
968
1120
  }
969
1121
  /**
@@ -1004,6 +1156,7 @@ function ensureThinkingBlockInMessages(messages, _provider, config, runStartInde
1004
1156
  }
1005
1157
  if (lastHumanIndex === messages.length - 1) return messages;
1006
1158
  const result = lastHumanIndex >= 0 ? messages.slice(0, lastHumanIndex + 1) : [];
1159
+ const foldBudget = createFoldContextBudget();
1007
1160
  let i = lastHumanIndex + 1;
1008
1161
  while (i < messages.length) {
1009
1162
  const msg = messages[i];
@@ -1019,8 +1172,9 @@ function ensureThinkingBlockInMessages(messages, _provider, config, runStartInde
1019
1172
  let hasThinkingBlock = false;
1020
1173
  if (contentIsArray && aiMsg.content.length > 0) for (const c of aiMsg.content) {
1021
1174
  if (typeof c !== "object") continue;
1022
- if (c.type === "tool_use") hasToolUse = true;
1023
- else if (c.type === "thinking" || c.type === "reasoning_content" || c.type === "reasoning" || c.type === "redacted_thinking") hasThinkingBlock = true;
1175
+ const type = readFoldedDataProperty(c, "type");
1176
+ if (type === "tool_use") hasToolUse = true;
1177
+ else if (type === "thinking" || type === "reasoning_content" || type === "reasoning" || type === "redacted_thinking") hasThinkingBlock = true;
1024
1178
  if (hasToolUse && hasThinkingBlock) break;
1025
1179
  }
1026
1180
  if (!hasThinkingBlock && aiMsg.additional_kwargs.reasoning_content != null) hasThinkingBlock = true;
@@ -1036,16 +1190,16 @@ function ensureThinkingBlockInMessages(messages, _provider, config, runStartInde
1036
1190
  continue;
1037
1191
  }
1038
1192
  const parts = [];
1039
- const textChunks = ["[Previous agent context]"];
1040
- appendMessageContent(msg, "AI", textChunks, parts);
1193
+ const textChunks = [];
1194
+ appendFoldedLine(textChunks, foldBudget, ["[Previous agent context]"]);
1195
+ appendMessageContent(msg, "AI", textChunks, parts, foldBudget);
1041
1196
  let j = i + 1;
1042
1197
  while (j < messages.length && isToolMessage$1(messages[j])) {
1043
- appendMessageContent(messages[j], "Tool", textChunks, parts);
1198
+ appendMessageContent(messages[j], "Tool", textChunks, parts, foldBudget);
1044
1199
  j++;
1045
1200
  }
1046
1201
  flushTextChunks(textChunks, parts);
1047
- emitAgentLog(config, "warn", "format", `ensureThinkingBlockInMessages: injecting [Previous agent context] HumanMessage (${parts.length} msgs at index ${i}, no thinking block in chain)`);
1048
- result.push(withMessageRole(new HumanMessage({ content: toLangChainContent(parts) }), "user"));
1202
+ if (appendSyntheticProviderContextMessage(result, parts)) emitAgentLog(config, "warn", "format", `ensureThinkingBlockInMessages: injecting [Previous agent context] HumanMessage (${parts.length} msgs at index ${i}, no thinking block in chain)`);
1049
1203
  i = j;
1050
1204
  } else {
1051
1205
  result.push(msg);
@@ -1069,8 +1223,9 @@ function messageHasToolContent(msg) {
1069
1223
  if (aiMsg.tool_calls != null && aiMsg.tool_calls.length > 0) return true;
1070
1224
  const rawToolCalls = aiMsg.additional_kwargs.tool_calls;
1071
1225
  if (Array.isArray(rawToolCalls) && rawToolCalls.length > 0) return true;
1072
- if (Array.isArray(msg.content)) {
1073
- for (const block of msg.content) if (typeof block === "object" && (block.type === "tool_use" || block.type === "tool_call" || block.type === "tool_result")) return true;
1226
+ if (Array.isArray(msg.content)) for (const block of msg.content) {
1227
+ const type = readFoldedDataProperty(block, "type");
1228
+ if (typeof block === "object" && (type === "tool_use" || type === "tool_call" || type === "tool_result")) return true;
1074
1229
  }
1075
1230
  return false;
1076
1231
  }
@@ -1081,7 +1236,7 @@ function messageHasToolContent(msg) {
1081
1236
  * and labelled as tool output. */
1082
1237
  function isToolResultMessage(msg) {
1083
1238
  if (isToolMessage$1(msg)) return true;
1084
- if (Array.isArray(msg.content)) return msg.content.some((block) => typeof block === "object" && block.type === "tool_result");
1239
+ if (Array.isArray(msg.content)) return msg.content.some((block) => typeof block === "object" && readFoldedDataProperty(block, "type") === "tool_result");
1085
1240
  return false;
1086
1241
  }
1087
1242
  /**
@@ -1108,6 +1263,7 @@ function isToolResultMessage(msg) {
1108
1263
  function foldToolBlocksForToollessAgent(messages, config) {
1109
1264
  let result = null;
1110
1265
  let foldedCount = 0;
1266
+ const foldBudget = createFoldContextBudget();
1111
1267
  let i = 0;
1112
1268
  while (i < messages.length) {
1113
1269
  const msg = messages[i];
@@ -1119,17 +1275,18 @@ function foldToolBlocksForToollessAgent(messages, config) {
1119
1275
  /** First fold — copy the untouched prefix once, then append from here. */
1120
1276
  if (result === null) result = messages.slice(0, i);
1121
1277
  const parts = [];
1122
- const textChunks = ["[Previous tool interaction]"];
1123
- appendMessageContent(msg, isToolResultMessage(msg) ? "Tool" : "AI", textChunks, parts);
1278
+ const textChunks = [];
1279
+ appendFoldedLine(textChunks, foldBudget, ["[Previous tool interaction]"]);
1280
+ appendMessageContent(msg, isToolResultMessage(msg) ? "Tool" : "AI", textChunks, parts, foldBudget);
1124
1281
  foldedCount++;
1125
1282
  let j = i + 1;
1126
1283
  while (j < messages.length && isToolResultMessage(messages[j])) {
1127
- appendMessageContent(messages[j], "Tool", textChunks, parts);
1284
+ appendMessageContent(messages[j], "Tool", textChunks, parts, foldBudget);
1128
1285
  foldedCount++;
1129
1286
  j++;
1130
1287
  }
1131
1288
  flushTextChunks(textChunks, parts);
1132
- result.push(withMessageRole(new HumanMessage({ content: toLangChainContent(parts) }), "user"));
1289
+ appendSyntheticProviderContextMessage(result, parts);
1133
1290
  i = j;
1134
1291
  }
1135
1292
  if (result === null) return messages;
@@ -1162,6 +1319,6 @@ function chainHasThinkingBlock(messages, currentIndex) {
1162
1319
  return false;
1163
1320
  }
1164
1321
  //#endregion
1165
- export { ensureThinkingBlockInMessages, foldToolBlocksForToollessAgent, formatAgentMessages, formatFromLangChain, formatLangChainMessages, formatMediaMessage, formatMessage, labelContentByAgent, shiftIndexTokenCountMap, withMessageRole };
1322
+ export { ensureThinkingBlockInMessages, foldToolBlocksForToollessAgent, formatAgentMessages, formatFromLangChain, formatLangChainMessages, formatMediaMessage, formatMessage, isSyntheticProviderContextMessage, labelContentByAgent, shiftIndexTokenCountMap, withMessageRole };
1166
1323
 
1167
1324
  //# sourceMappingURL=format.mjs.map