@librechat/agents 3.3.3 → 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 (156) 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/llm/anthropic/utils/message_inputs.cjs +79 -6
  8. package/dist/cjs/llm/anthropic/utils/message_inputs.cjs.map +1 -1
  9. package/dist/cjs/llm/bedrock/utils/message_inputs.cjs +37 -10
  10. package/dist/cjs/llm/bedrock/utils/message_inputs.cjs.map +1 -1
  11. package/dist/cjs/llm/google/utils/common.cjs +19 -7
  12. package/dist/cjs/llm/google/utils/common.cjs.map +1 -1
  13. package/dist/cjs/llm/invoke.cjs +64 -3
  14. package/dist/cjs/llm/invoke.cjs.map +1 -1
  15. package/dist/cjs/llm/openai/index.cjs +41 -5
  16. package/dist/cjs/llm/openai/index.cjs.map +1 -1
  17. package/dist/cjs/llm/openai/utils/index.cjs +25 -4
  18. package/dist/cjs/llm/openai/utils/index.cjs.map +1 -1
  19. package/dist/cjs/llm/openrouter/index.cjs +4 -1
  20. package/dist/cjs/llm/openrouter/index.cjs.map +1 -1
  21. package/dist/cjs/main.cjs +19 -2
  22. package/dist/cjs/messages/cache.cjs +54 -33
  23. package/dist/cjs/messages/cache.cjs.map +1 -1
  24. package/dist/cjs/messages/contextPruning.cjs +17 -43
  25. package/dist/cjs/messages/contextPruning.cjs.map +1 -1
  26. package/dist/cjs/messages/core.cjs +315 -23
  27. package/dist/cjs/messages/core.cjs.map +1 -1
  28. package/dist/cjs/messages/format.cjs +218 -60
  29. package/dist/cjs/messages/format.cjs.map +1 -1
  30. package/dist/cjs/messages/index.cjs +1 -1
  31. package/dist/cjs/messages/prune.cjs +503 -127
  32. package/dist/cjs/messages/prune.cjs.map +1 -1
  33. package/dist/cjs/stream.cjs +7 -3
  34. package/dist/cjs/stream.cjs.map +1 -1
  35. package/dist/cjs/summarization/node.cjs +28 -14
  36. package/dist/cjs/summarization/node.cjs.map +1 -1
  37. package/dist/cjs/tools/ToolNode.cjs +54 -41
  38. package/dist/cjs/tools/ToolNode.cjs.map +1 -1
  39. package/dist/cjs/tools/toolOutputReferences.cjs +6 -0
  40. package/dist/cjs/tools/toolOutputReferences.cjs.map +1 -1
  41. package/dist/cjs/utils/index.cjs +1 -1
  42. package/dist/cjs/utils/llm.cjs +1 -1
  43. package/dist/cjs/utils/llm.cjs.map +1 -1
  44. package/dist/cjs/utils/tokens.cjs +307 -20
  45. package/dist/cjs/utils/tokens.cjs.map +1 -1
  46. package/dist/cjs/utils/toolContent.cjs +1514 -0
  47. package/dist/cjs/utils/toolContent.cjs.map +1 -0
  48. package/dist/esm/agents/AgentContext.mjs +2 -1
  49. package/dist/esm/agents/AgentContext.mjs.map +1 -1
  50. package/dist/esm/graphs/Graph.mjs +349 -116
  51. package/dist/esm/graphs/Graph.mjs.map +1 -1
  52. package/dist/esm/graphs/MultiAgentGraph.mjs +3 -1
  53. package/dist/esm/graphs/MultiAgentGraph.mjs.map +1 -1
  54. package/dist/esm/llm/anthropic/utils/message_inputs.mjs +79 -6
  55. package/dist/esm/llm/anthropic/utils/message_inputs.mjs.map +1 -1
  56. package/dist/esm/llm/bedrock/utils/message_inputs.mjs +37 -10
  57. package/dist/esm/llm/bedrock/utils/message_inputs.mjs.map +1 -1
  58. package/dist/esm/llm/google/utils/common.mjs +19 -7
  59. package/dist/esm/llm/google/utils/common.mjs.map +1 -1
  60. package/dist/esm/llm/invoke.mjs +65 -5
  61. package/dist/esm/llm/invoke.mjs.map +1 -1
  62. package/dist/esm/llm/openai/index.mjs +41 -5
  63. package/dist/esm/llm/openai/index.mjs.map +1 -1
  64. package/dist/esm/llm/openai/utils/index.mjs +25 -4
  65. package/dist/esm/llm/openai/utils/index.mjs.map +1 -1
  66. package/dist/esm/llm/openrouter/index.mjs +4 -1
  67. package/dist/esm/llm/openrouter/index.mjs.map +1 -1
  68. package/dist/esm/main.mjs +7 -7
  69. package/dist/esm/messages/cache.mjs +55 -34
  70. package/dist/esm/messages/cache.mjs.map +1 -1
  71. package/dist/esm/messages/contextPruning.mjs +17 -43
  72. package/dist/esm/messages/contextPruning.mjs.map +1 -1
  73. package/dist/esm/messages/core.mjs +305 -24
  74. package/dist/esm/messages/core.mjs.map +1 -1
  75. package/dist/esm/messages/format.mjs +218 -61
  76. package/dist/esm/messages/format.mjs.map +1 -1
  77. package/dist/esm/messages/index.mjs +1 -1
  78. package/dist/esm/messages/prune.mjs +500 -128
  79. package/dist/esm/messages/prune.mjs.map +1 -1
  80. package/dist/esm/stream.mjs +7 -3
  81. package/dist/esm/stream.mjs.map +1 -1
  82. package/dist/esm/summarization/node.mjs +28 -14
  83. package/dist/esm/summarization/node.mjs.map +1 -1
  84. package/dist/esm/tools/ToolNode.mjs +54 -41
  85. package/dist/esm/tools/ToolNode.mjs.map +1 -1
  86. package/dist/esm/tools/toolOutputReferences.mjs +6 -0
  87. package/dist/esm/tools/toolOutputReferences.mjs.map +1 -1
  88. package/dist/esm/utils/index.mjs +1 -1
  89. package/dist/esm/utils/llm.mjs +1 -1
  90. package/dist/esm/utils/llm.mjs.map +1 -1
  91. package/dist/esm/utils/tokens.mjs +307 -21
  92. package/dist/esm/utils/tokens.mjs.map +1 -1
  93. package/dist/esm/utils/toolContent.mjs +1503 -0
  94. package/dist/esm/utils/toolContent.mjs.map +1 -0
  95. package/dist/types/graphs/Graph.d.ts +1 -1
  96. package/dist/types/llm/invoke.d.ts +27 -1
  97. package/dist/types/llm/openai/index.d.ts +4 -0
  98. package/dist/types/llm/openai/utils/index.d.ts +2 -0
  99. package/dist/types/llm/openrouter/index.d.ts +5 -0
  100. package/dist/types/messages/contextPruning.d.ts +2 -1
  101. package/dist/types/messages/core.d.ts +36 -0
  102. package/dist/types/messages/format.d.ts +6 -0
  103. package/dist/types/messages/prune.d.ts +16 -12
  104. package/dist/types/types/stream.d.ts +1 -1
  105. package/dist/types/utils/tokens.d.ts +7 -0
  106. package/dist/types/utils/toolContent.d.ts +107 -0
  107. package/package.json +1 -1
  108. package/src/__tests__/stream.eagerEventExecution.test.ts +74 -0
  109. package/src/agents/AgentContext.ts +1 -0
  110. package/src/graphs/Graph.ts +698 -200
  111. package/src/graphs/MultiAgentGraph.ts +6 -1
  112. package/src/graphs/__tests__/Graph.contextOverflow.test.ts +1128 -4
  113. package/src/llm/anthropic/utils/message_inputs.ts +158 -12
  114. package/src/llm/anthropic/utils/tool-id-normalization.test.ts +109 -0
  115. package/src/llm/bedrock/utils/message_inputs.test.ts +49 -1
  116. package/src/llm/bedrock/utils/message_inputs.ts +86 -13
  117. package/src/llm/google/utils/common.test.ts +75 -9
  118. package/src/llm/google/utils/common.ts +34 -3
  119. package/src/llm/invoke.test.ts +488 -0
  120. package/src/llm/invoke.ts +196 -2
  121. package/src/llm/openai/index.ts +106 -6
  122. package/src/llm/openai/utils/index.ts +109 -53
  123. package/src/llm/openai/utils/messages.test.ts +330 -1
  124. package/src/llm/openrouter/index.ts +12 -2
  125. package/src/llm/openrouter/reasoning.test.ts +314 -0
  126. package/src/messages/__tests__/observationMasking.test.ts +93 -2
  127. package/src/messages/cache.tail.test.ts +193 -0
  128. package/src/messages/cache.test.ts +113 -0
  129. package/src/messages/cache.ts +92 -60
  130. package/src/messages/contextPruning.test.ts +184 -0
  131. package/src/messages/contextPruning.ts +49 -42
  132. package/src/messages/core.ts +653 -40
  133. package/src/messages/ensureThinkingBlock.test.ts +49 -1
  134. package/src/messages/foldToollessToolBlocks.test.ts +163 -5
  135. package/src/messages/format.ts +363 -91
  136. package/src/messages/formatAgentMessages.test.ts +604 -14
  137. package/src/messages/formatAgentMessages.tools.test.ts +103 -0
  138. package/src/messages/prune.ts +996 -183
  139. package/src/specs/prune.test.ts +1083 -6
  140. package/src/specs/summarization-unit.test.ts +105 -0
  141. package/src/specs/tokens.test.ts +609 -32
  142. package/src/stream.ts +23 -15
  143. package/src/summarization/__tests__/node.test.ts +77 -0
  144. package/src/summarization/node.ts +46 -18
  145. package/src/tools/ToolNode.ts +147 -68
  146. package/src/tools/__tests__/ToolNode.onResultCompletion.test.ts +148 -1
  147. package/src/tools/__tests__/ToolNode.outputReferences.test.ts +112 -3
  148. package/src/tools/__tests__/annotateMessagesForLLM.test.ts +32 -0
  149. package/src/tools/__tests__/directToolHooks.test.ts +255 -7
  150. package/src/tools/toolOutputReferences.ts +6 -0
  151. package/src/types/stream.ts +1 -1
  152. package/src/utils/llm.test.ts +18 -0
  153. package/src/utils/llm.ts +4 -1
  154. package/src/utils/tokens.ts +586 -31
  155. package/src/utils/toolContent.test.ts +1432 -0
  156. package/src/utils/toolContent.ts +2707 -0
@@ -5,15 +5,17 @@ const require_langfuseToolOutputTracing = require("../langfuseToolOutputTracing.
5
5
  const require_callbacks = require("../utils/callbacks.cjs");
6
6
  const require_enum = require("../common/enum.cjs");
7
7
  require("../common/index.cjs");
8
+ const require_truncation = require("../utils/truncation.cjs");
9
+ const require_toolContent = require("../utils/toolContent.cjs");
8
10
  const require_tokens = require("../utils/tokens.cjs");
9
11
  const require_instrumentation = require("../instrumentation.cjs");
12
+ const require_cache = require("../messages/cache.cjs");
10
13
  const require_core = require("../messages/core.cjs");
11
14
  const require_ids = require("../messages/ids.cjs");
12
15
  const require_prune = require("../messages/prune.cjs");
13
16
  const require_budget = require("../messages/budget.cjs");
14
17
  const require_events = require("../utils/events.cjs");
15
18
  const require_format = require("../messages/format.cjs");
16
- const require_cache = require("../messages/cache.cjs");
17
19
  const require_anthropicToolCache = require("../messages/anthropicToolCache.cjs");
18
20
  const require_content = require("../messages/content.cjs");
19
21
  const require_tools = require("../messages/tools.cjs");
@@ -27,10 +29,10 @@ const require_toolOutputReferences = require("../tools/toolOutputReferences.cjs"
27
29
  const require_events$1 = require("../events.cjs");
28
30
  const require_run = require("../utils/run.cjs");
29
31
  require("../utils/index.cjs");
30
- const require_contextOverflowRecovery = require("../llm/contextOverflowRecovery.cjs");
31
32
  const require_toolCache = require("../llm/bedrock/toolCache.cjs");
32
33
  const require_init = require("../llm/init.cjs");
33
34
  const require_invoke = require("../llm/invoke.cjs");
35
+ const require_contextOverflowRecovery = require("../llm/contextOverflowRecovery.cjs");
34
36
  const require_toolCache$1 = require("../llm/openrouter/toolCache.cjs");
35
37
  const require_LocalCodingTools = require("../tools/local/LocalCodingTools.cjs");
36
38
  const require_CloudflareSandboxTools = require("../tools/cloudflare/CloudflareSandboxTools.cjs");
@@ -51,6 +53,7 @@ let _langchain_core_messages = require("@langchain/core/messages");
51
53
  let _langchain_langgraph = require("@langchain/langgraph");
52
54
  let _langchain_core_tools = require("@langchain/core/tools");
53
55
  let nanoid = require("nanoid");
56
+ let _langchain_core_errors = require("@langchain/core/errors");
54
57
  //#region src/graphs/Graph.ts
55
58
  const { AGENT, TOOLS, SUMMARIZE } = require_enum.GraphNodeKeys;
56
59
  /** Minimum relative variance before calibrated toolSchemaTokens overrides current value. */
@@ -62,19 +65,6 @@ function createToolHandlerRegistry(source) {
62
65
  registry.register("on_tool_execute", toolHandler);
63
66
  return registry;
64
67
  }
65
- /**
66
- * Start index of the span post-prune formatters can mutate in place: the
67
- * trailing tool batch plus its owning AI message (artifact formatting touches
68
- * every tool result after the last AI tool call; Bedrock rewrites the AI
69
- * message before a trailing tool result). Capped so the usage-snapshot
70
- * recount stays constant-cost.
71
- */
72
- function trailingMutationStart(messages) {
73
- const MAX_SPAN = 16;
74
- let index = messages.length - 1;
75
- while (index >= 0 && messages[index]?.getType() === "tool" && messages.length - index < MAX_SPAN) index--;
76
- return Math.max(0, Math.min(index, messages.length - 2));
77
- }
78
68
  function getHandlerDispatchedEventKey(eventName, stepId) {
79
69
  return `${eventName}:${stepId}`;
80
70
  }
@@ -901,6 +891,7 @@ var StandardGraph = class StandardGraph extends Graph {
901
891
  provider: agentContext.provider,
902
892
  tokenCounter: agentContext.tokenCounter,
903
893
  maxTokens: agentContext.maxContextTokens,
894
+ maxToolResultChars: agentContext.maxToolResultChars,
904
895
  thinkingEnabled: require_request.isThinkingEnabled(agentContext.provider, agentContext.clientOptions),
905
896
  indexTokenCountMap: agentContext.indexTokenCountMap,
906
897
  contextPruningConfig: agentContext.contextPruningConfig,
@@ -1010,28 +1001,73 @@ var StandardGraph = class StandardGraph extends Graph {
1010
1001
  }
1011
1002
  }
1012
1003
  let finalMessages = messagesToUse;
1013
- /** Tail snapshot for the dispatch-time usage delta: in-place
1014
- * formatters (artifact appends, Bedrock content rewrites, legacy
1015
- * string conversion) mutate without changing length or identity
1016
- * capture before they run. Legacy string conversion can also touch
1017
- * messages before the tail, so those convertible indices are
1018
- * tracked separately (none exist in the common case). */
1019
- const tailStart = trailingMutationStart(messagesToUse);
1020
- let preFormatTailTokens = null;
1021
- let legacyIndices = null;
1022
- let preFormatLegacyTokens = 0;
1004
+ /**
1005
+ * Keep the pruner's provider-grounded aggregate as the authoritative
1006
+ * baseline, then attribute it across retained messages. Provider
1007
+ * transforms can shrink one message while expanding or adding another;
1008
+ * per-origin accounting prevents that unrelated shrink from canceling
1009
+ * the expansion. Raw counts are frozen before in-place formatters run.
1010
+ */
1011
+ let providerMessageBaseline;
1012
+ const providerMessageOrigins = /* @__PURE__ */ new WeakMap();
1023
1013
  if (contextUsage != null && agentContext.tokenCounter != null) {
1024
- preFormatTailTokens = 0;
1025
- for (const message of messagesToUse.slice(tailStart)) preFormatTailTokens += agentContext.tokenCounter(message);
1026
- if (agentContext.useLegacyContent) {
1027
- legacyIndices = [];
1028
- for (let i = 0; i < tailStart; i++) if (require_content.isLegacyConvertible(messagesToUse[i])) {
1029
- legacyIndices.push(i);
1030
- preFormatLegacyTokens += agentContext.tokenCounter(messagesToUse[i]);
1014
+ const sourceIndices = /* @__PURE__ */ new WeakMap();
1015
+ for (let i = 0; i < messages.length; i++) sourceIndices.set(messages[i], i);
1016
+ providerMessageBaseline = messagesToUse.map((message, index) => {
1017
+ const rawTokens = agentContext.tokenCounter(message);
1018
+ const sourceIndex = sourceIndices.get(message);
1019
+ const indexedTokens = sourceIndex != null ? agentContext.indexTokenCountMap[sourceIndex] : void 0;
1020
+ const accountingWeight = indexedTokens != null && Number.isFinite(indexedTokens) && indexedTokens >= 0 ? indexedTokens : rawTokens;
1021
+ if (!providerMessageOrigins.has(message)) providerMessageOrigins.set(message, index);
1022
+ return {
1023
+ rawTokens,
1024
+ accountingWeight
1025
+ };
1026
+ });
1027
+ }
1028
+ const getProviderMessageOriginKey = (message) => {
1029
+ const type = message.getType();
1030
+ if (message instanceof _langchain_core_messages.ToolMessage && typeof message.tool_call_id === "string" && message.tool_call_id.length > 0) return `tool:call:${message.tool_call_id}`;
1031
+ if (typeof message.id === "string" && message.id.length > 0) return `${type}:id:${message.id}`;
1032
+ };
1033
+ /**
1034
+ * Provider projections clone messages. Preserve their baseline origin
1035
+ * without writing tracking metadata onto the wire. Synthetic fold
1036
+ * messages intentionally remain unattributed and are charged in full.
1037
+ */
1038
+ const trackProviderMessageOrigins = (before, after) => {
1039
+ if (providerMessageBaseline == null || before === after) return after;
1040
+ if (before.length === after.length) {
1041
+ for (let i = 0; i < after.length; i++) {
1042
+ const origin = providerMessageOrigins.get(before[i]);
1043
+ if (origin != null && !providerMessageOrigins.has(after[i]) && before[i].getType() === after[i].getType() && !require_format.isSyntheticProviderContextMessage(after[i])) providerMessageOrigins.set(after[i], origin);
1031
1044
  }
1045
+ return after;
1046
+ }
1047
+ const keyedOrigins = /* @__PURE__ */ new Map();
1048
+ for (const message of before) {
1049
+ const origin = providerMessageOrigins.get(message);
1050
+ const key = getProviderMessageOriginKey(message);
1051
+ if (origin == null || key == null) continue;
1052
+ keyedOrigins.set(key, keyedOrigins.has(key) ? null : origin);
1053
+ }
1054
+ for (const message of after) {
1055
+ if (providerMessageOrigins.has(message) || require_format.isSyntheticProviderContextMessage(message)) continue;
1056
+ const key = getProviderMessageOriginKey(message);
1057
+ const origin = key != null ? keyedOrigins.get(key) : void 0;
1058
+ if (origin != null) providerMessageOrigins.set(message, origin);
1032
1059
  }
1060
+ return after;
1061
+ };
1062
+ if (agentContext.useLegacyContent) {
1063
+ const before = finalMessages;
1064
+ finalMessages = trackProviderMessageOrigins(before, require_content.formatContentStrings(before));
1033
1065
  }
1034
- if (agentContext.useLegacyContent) finalMessages = require_content.formatContentStrings(finalMessages);
1066
+ const maxProviderToolResultChars = agentContext.maxToolResultChars ?? require_truncation.calculateMaxToolResultChars(agentContext.maxContextTokens);
1067
+ const beforeToolStreamProjection = finalMessages;
1068
+ finalMessages = trackProviderMessageOrigins(beforeToolStreamProjection, require_core.projectToolStreamContentForProvider(beforeToolStreamProjection));
1069
+ const beforeToolInputProjection = finalMessages;
1070
+ finalMessages = trackProviderMessageOrigins(beforeToolInputProjection, require_prune.projectToolCallInputs(beforeToolInputProjection, require_prune.calculateMaxToolCallInputChars(agentContext.maxContextTokens)));
1035
1071
  const lastMessageX = finalMessages.length >= 2 ? finalMessages[finalMessages.length - 2] : null;
1036
1072
  const lastMessageY = finalMessages.length >= 1 ? finalMessages[finalMessages.length - 1] : null;
1037
1073
  const anthropicLike = require_llm.isAnthropicLike(agentContext.provider, agentContext.clientOptions);
@@ -1042,34 +1078,186 @@ var StandardGraph = class StandardGraph extends Graph {
1042
1078
  text: trimmed
1043
1079
  }] : "";
1044
1080
  }
1081
+ const localProviderOverflowMeasurements = /* @__PURE__ */ new WeakMap();
1082
+ const measureProviderPayload = (candidate, contextBudgetOverride, forceRawRecount = false) => {
1083
+ const contextBudget = contextBudgetOverride ?? contextUsage?.contextBudget;
1084
+ const effectiveInstructionTokens = contextUsage?.effectiveInstructionTokens ?? (forceRawRecount ? agentContext.instructionTokens : void 0);
1085
+ if (agentContext.tokenCounter == null || contextBudget == null || effectiveInstructionTokens == null) return { fits: true };
1086
+ const availableMessageTokens = Math.max(0, contextBudget - effectiveInstructionTokens);
1087
+ let usageRatio = agentContext.calibrationRatio > 0 ? agentContext.calibrationRatio : 1;
1088
+ if (contextUsage?.calibrationRatio != null && contextUsage.calibrationRatio > 0) usageRatio = contextUsage.calibrationRatio;
1089
+ if (forceRawRecount) usageRatio = Math.max(1, usageRatio);
1090
+ const baselineRemaining = contextUsage?.remainingContextTokens;
1091
+ const accountedMessageTokens = !forceRawRecount && providerMessageBaseline != null && baselineRemaining != null && Number.isFinite(baselineRemaining) ? availableMessageTokens - Math.min(availableMessageTokens, Math.max(0, baselineRemaining)) : void 0;
1092
+ let projectedMessageTokens;
1093
+ if (accountedMessageTokens != null && providerMessageBaseline != null) {
1094
+ const replyPrimerTokens = Math.round(3 * usageRatio);
1095
+ const rawWeights = {};
1096
+ let totalWeight = 0;
1097
+ for (let i = 0; i < providerMessageBaseline.length; i++) {
1098
+ const weight = providerMessageBaseline[i].accountingWeight;
1099
+ rawWeights[i] = weight;
1100
+ totalWeight += weight;
1101
+ }
1102
+ const attributableTokens = totalWeight > 0 ? Math.min(Math.max(0, accountedMessageTokens - replyPrimerTokens), Math.round(totalWeight * usageRatio)) : 0;
1103
+ const apportionedTokens = totalWeight > 0 ? require_tokens.apportionTokenCounts(rawWeights, attributableTokens / totalWeight, attributableTokens) : {};
1104
+ const attributedByOrigin = providerMessageBaseline.map((_, origin) => apportionedTokens[origin] || 0);
1105
+ projectedMessageTokens = Math.max(replyPrimerTokens, accountedMessageTokens - attributableTokens);
1106
+ let newRawTokens = 0;
1107
+ const usedOrigins = /* @__PURE__ */ new Set();
1108
+ for (const message of candidate) {
1109
+ const rawTokens = agentContext.tokenCounter(message);
1110
+ const origin = providerMessageOrigins.get(message);
1111
+ if (origin == null || usedOrigins.has(origin)) {
1112
+ newRawTokens += rawTokens;
1113
+ continue;
1114
+ }
1115
+ usedOrigins.add(origin);
1116
+ projectedMessageTokens += Math.max(0, attributedByOrigin[origin] + Math.round((rawTokens - providerMessageBaseline[origin].rawTokens) * usageRatio));
1117
+ }
1118
+ projectedMessageTokens += Math.round(newRawTokens * usageRatio);
1119
+ } else {
1120
+ let rawTokens = 3;
1121
+ for (const message of candidate) rawTokens += agentContext.tokenCounter(message);
1122
+ projectedMessageTokens = Math.round(rawTokens * usageRatio);
1123
+ }
1124
+ return {
1125
+ fits: projectedMessageTokens <= availableMessageTokens,
1126
+ projectedMessageTokens,
1127
+ availableMessageTokens,
1128
+ contextBudget,
1129
+ effectiveInstructionTokens
1130
+ };
1131
+ };
1132
+ const createProviderPayloadOverflowError = ({ projection, provider, info }) => {
1133
+ const error = new _langchain_core_errors.ContextOverflowError(JSON.stringify({
1134
+ type: "final_context_overflow",
1135
+ info,
1136
+ provider,
1137
+ projectedMessageTokens: projection.projectedMessageTokens,
1138
+ availableMessageTokens: projection.availableMessageTokens
1139
+ }));
1140
+ if (projection.projectedMessageTokens != null && projection.contextBudget != null && projection.effectiveInstructionTokens != null) localProviderOverflowMeasurements.set(error, {
1141
+ contextBudget: projection.contextBudget,
1142
+ estimatedPromptTokens: projection.projectedMessageTokens + projection.effectiveInstructionTokens
1143
+ });
1144
+ return error;
1145
+ };
1146
+ const applyProviderMessageTransforms = (candidate) => {
1147
+ let transformed = candidate;
1148
+ if (require_request.isThinkingEnabled(agentContext.provider, agentContext.clientOptions)) {
1149
+ /**
1150
+ * Current-run AI messages may validly omit a thinking block. The
1151
+ * boundary prevents them from being mistaken for foreign history.
1152
+ */
1153
+ const before = transformed;
1154
+ transformed = trackProviderMessageOrigins(before, require_format.ensureThinkingBlockInMessages(before, agentContext.provider, config, this.startIndex));
1155
+ }
1156
+ /**
1157
+ * Tool-less destinations cannot send inherited tool blocks without a
1158
+ * tool schema, so fold those interactions into provider-valid content.
1159
+ */
1160
+ if (toolsForBinding == null || toolsForBinding.length === 0) {
1161
+ const before = transformed;
1162
+ transformed = trackProviderMessageOrigins(before, require_format.foldToolBlocksForToollessAgent(before, config));
1163
+ if (agentContext.useLegacyContent) {
1164
+ const beforeLegacyFormat = transformed;
1165
+ transformed = trackProviderMessageOrigins(beforeLegacyFormat, require_content.formatContentStrings(beforeLegacyFormat));
1166
+ }
1167
+ }
1168
+ return transformed;
1169
+ };
1170
+ const toolOutputRegistry = this.getOrCreateToolOutputRegistry();
1171
+ const providerRunId = config.configurable?.run_id;
1172
+ const projectProviderReferences = (candidate) => trackProviderMessageOrigins(candidate, require_toolOutputReferences.annotateMessagesForLLM(candidate, toolOutputRegistry, providerRunId));
1173
+ const compactSyntheticProviderContext = (candidate) => {
1174
+ const synthetic = [];
1175
+ for (let i = 0; i < candidate.length; i++) {
1176
+ const message = candidate[i];
1177
+ if (!(message instanceof _langchain_core_messages.HumanMessage) || !require_format.isSyntheticProviderContextMessage(message)) continue;
1178
+ const content = message.content;
1179
+ synthetic.push({
1180
+ index: i,
1181
+ message,
1182
+ chars: require_toolContent.getToolContentCharLength(content)
1183
+ });
1184
+ }
1185
+ if (synthetic.length === 0) return candidate;
1186
+ const buildCandidate = (scale) => {
1187
+ const compacted = [...candidate];
1188
+ for (const { index, message, chars } of synthetic) {
1189
+ const content = require_toolContent.compactToolContent(message.content, Math.floor(chars * scale)).content;
1190
+ compacted[index] = new _langchain_core_messages.HumanMessage({
1191
+ content,
1192
+ id: message.id,
1193
+ name: message.name,
1194
+ additional_kwargs: message.additional_kwargs,
1195
+ response_metadata: message.response_metadata
1196
+ });
1197
+ }
1198
+ return compacted;
1199
+ };
1200
+ let best = buildCandidate(0);
1201
+ if (!measureProviderPayload(best).fits) return candidate;
1202
+ let low = 0;
1203
+ let high = 1;
1204
+ for (let i = 0; i < 12; i++) {
1205
+ const scale = (low + high) / 2;
1206
+ const attempt = buildCandidate(scale);
1207
+ if (measureProviderPayload(attempt).fits) {
1208
+ best = attempt;
1209
+ low = scale;
1210
+ } else high = scale;
1211
+ }
1212
+ return best;
1213
+ };
1214
+ let artifactBaseMessages;
1045
1215
  if (lastMessageY instanceof _langchain_core_messages.ToolMessage) {
1046
- if (anthropicLike) require_core.formatAnthropicArtifactContent(finalMessages);
1047
- else if (require_llm.isOpenAILike(agentContext.provider) && agentContext.provider !== "deepseek" || require_llm.isGoogleLike(agentContext.provider)) require_core.formatArtifactPayload(finalMessages);
1216
+ let artifactCandidate = finalMessages;
1217
+ if (anthropicLike) artifactCandidate = trackProviderMessageOrigins(finalMessages, require_core.projectAnthropicArtifactContent(finalMessages, maxProviderToolResultChars));
1218
+ else if (require_llm.isOpenAILike(agentContext.provider) && agentContext.provider !== "deepseek" || require_llm.isGoogleLike(agentContext.provider)) artifactCandidate = trackProviderMessageOrigins(finalMessages, require_core.projectArtifactPayload(finalMessages, maxProviderToolResultChars));
1219
+ if (artifactCandidate !== finalMessages) {
1220
+ const projection = measureProviderPayload(artifactCandidate);
1221
+ if (projection.fits) {
1222
+ artifactBaseMessages = finalMessages;
1223
+ finalMessages = artifactCandidate;
1224
+ } else require_events.emitAgentLog(config, "warn", "graph", "Artifact payload omitted because it exceeds the remaining context budget", {
1225
+ projectedMessageTokens: projection.projectedMessageTokens,
1226
+ availableMessageTokens: projection.availableMessageTokens
1227
+ }, {
1228
+ runId: this.runId,
1229
+ agentId
1230
+ });
1231
+ }
1048
1232
  }
1049
- if (require_request.isThinkingEnabled(agentContext.provider, agentContext.clientOptions))
1050
- /**
1051
- * Pass `this.startIndex` so the function can distinguish CURRENT-run
1052
- * AI messages (the agent's own iterations — possibly without a
1053
- * leading thinking block, which Claude is allowed to skip) from
1054
- * historical context that genuinely needs the
1055
- * `[Previous agent context]` placeholder. Without this signal the
1056
- * function would convert the agent's own in-run tool_use messages,
1057
- * polluting the next iteration's prompt with a placeholder the
1058
- * model treats as suspicious injected content.
1059
- */
1060
- finalMessages = require_format.ensureThinkingBlockInMessages(finalMessages, agentContext.provider, config, this.startIndex);
1061
- /**
1062
- * A destination that binds no tools is invoked without a tool schema, but
1063
- * in a multi-agent graph it can still inherit a prior agent's toolUse/
1064
- * toolResult history. Bedrock's Converse API (and other tool-schema-strict
1065
- * providers) reject such a request when no top-level toolConfig is sent.
1066
- * Fold that historical tool content into plain text so the tool-less agent
1067
- * receives valid, context-preserving messages. Handoff tools count as
1068
- * bound tools, so a tool-less router mid-handoff is not affected.
1069
- */
1070
- if (toolsForBinding == null || toolsForBinding.length === 0) {
1071
- finalMessages = require_format.foldToolBlocksForToollessAgent(finalMessages, config);
1072
- if (agentContext.useLegacyContent) finalMessages = require_content.formatContentStrings(finalMessages);
1233
+ finalMessages = projectProviderReferences(applyProviderMessageTransforms(finalMessages));
1234
+ let finalProjection = measureProviderPayload(finalMessages);
1235
+ if (artifactBaseMessages != null) {
1236
+ if (!finalProjection.fits) {
1237
+ finalMessages = projectProviderReferences(applyProviderMessageTransforms(artifactBaseMessages));
1238
+ finalProjection = measureProviderPayload(finalMessages);
1239
+ require_events.emitAgentLog(config, "warn", "graph", "Artifact payload omitted after final provider formatting exceeded the remaining context budget", {
1240
+ projectedMessageTokens: finalProjection.projectedMessageTokens,
1241
+ availableMessageTokens: finalProjection.availableMessageTokens
1242
+ }, {
1243
+ runId: this.runId,
1244
+ agentId
1245
+ });
1246
+ }
1247
+ }
1248
+ if (!finalProjection.fits) {
1249
+ const compacted = compactSyntheticProviderContext(finalMessages);
1250
+ if (compacted !== finalMessages) {
1251
+ finalMessages = compacted;
1252
+ finalProjection = measureProviderPayload(finalMessages);
1253
+ require_events.emitAgentLog(config, finalProjection.fits ? "warn" : "error", "graph", finalProjection.fits ? "Synthetic provider context compacted to fit the final payload budget" : "Final provider payload still exceeds budget after synthetic context compaction", {
1254
+ projectedMessageTokens: finalProjection.projectedMessageTokens,
1255
+ availableMessageTokens: finalProjection.availableMessageTokens
1256
+ }, {
1257
+ runId: this.runId,
1258
+ agentId
1259
+ });
1260
+ }
1073
1261
  }
1074
1262
  const anthropicPromptCacheEnabled = agentContext.provider === "anthropic" && agentContext.clientOptions?.promptCache === true;
1075
1263
  const openRouterPromptCacheEnabled = agentContext.provider === "openrouter" && agentContext.clientOptions?.promptCache === true;
@@ -1077,7 +1265,11 @@ var StandardGraph = class StandardGraph extends Graph {
1077
1265
  const providerPromptCacheEnabled = anthropicPromptCacheEnabled || openRouterPromptCacheEnabled || bedrockPromptCacheEnabled;
1078
1266
  if (anthropicLike && (!agentContext.pruneMessages || finalMessages !== messagesToUse || providerPromptCacheEnabled)) {
1079
1267
  const beforeSanitize = finalMessages.length;
1080
- finalMessages = require_prune.sanitizeOrphanToolBlocks(finalMessages);
1268
+ const beforeSanitizeMessages = finalMessages;
1269
+ finalMessages = trackProviderMessageOrigins(beforeSanitizeMessages, require_prune.sanitizeOrphanToolBlocks(beforeSanitizeMessages, (source, clone) => {
1270
+ const origin = providerMessageOrigins.get(source);
1271
+ if (origin != null) providerMessageOrigins.set(clone, origin);
1272
+ }));
1081
1273
  if (finalMessages.length !== beforeSanitize) require_events.emitAgentLog(config, "warn", "sanitize", "Orphan tool blocks removed", {
1082
1274
  before: beforeSanitize,
1083
1275
  after: finalMessages.length,
@@ -1087,11 +1279,34 @@ var StandardGraph = class StandardGraph extends Graph {
1087
1279
  agentId
1088
1280
  });
1089
1281
  }
1090
- if ((anthropicPromptCacheEnabled || openRouterPromptCacheEnabled) && !agentContext.systemRunnable) finalMessages = require_cache.addTailCacheControl(finalMessages, require_cache.resolvePromptCacheTtl(anthropicPromptCacheEnabled ? agentContext.clientOptions?.promptCacheTtl : agentContext.clientOptions?.promptCacheTtl));
1091
- else if (bedrockPromptCacheEnabled) {
1282
+ if ((anthropicPromptCacheEnabled || openRouterPromptCacheEnabled) && !agentContext.systemRunnable) {
1283
+ const beforeCacheControl = finalMessages;
1284
+ finalMessages = trackProviderMessageOrigins(beforeCacheControl, require_cache.addTailCacheControl(beforeCacheControl, require_cache.resolvePromptCacheTtl(anthropicPromptCacheEnabled ? agentContext.clientOptions?.promptCacheTtl : agentContext.clientOptions?.promptCacheTtl)));
1285
+ } else if (bedrockPromptCacheEnabled) {
1092
1286
  const bedrockOptions = agentContext.clientOptions;
1093
- finalMessages = require_cache.addBedrockTailCacheControl(finalMessages, require_cache.resolveBedrockPromptCacheTtl(bedrockOptions?.promptCacheTtl, bedrockOptions?.model));
1287
+ const beforeCacheControl = finalMessages;
1288
+ finalMessages = trackProviderMessageOrigins(beforeCacheControl, require_cache.addBedrockTailCacheControl(beforeCacheControl, require_cache.resolveBedrockPromptCacheTtl(bedrockOptions?.promptCacheTtl, bedrockOptions?.model)));
1094
1289
  }
1290
+ const fallbackBaseMessages = finalMessages;
1291
+ const beforeFinalProviderProjection = fallbackBaseMessages;
1292
+ finalMessages = trackProviderMessageOrigins(beforeFinalProviderProjection, require_invoke.projectMessagesForProvider({
1293
+ model: this.overrideModel ?? model,
1294
+ messages: beforeFinalProviderProjection,
1295
+ provider: agentContext.provider,
1296
+ maxToolResultChars: maxProviderToolResultChars,
1297
+ callOptions: config
1298
+ }));
1299
+ /**
1300
+ * Prompt-cache placement and orphan sanitization are provider-wire
1301
+ * transforms too. Re-measure after both so no content added after the
1302
+ * earlier artifact/synthetic compaction decision can bypass the guard.
1303
+ */
1304
+ finalProjection = measureProviderPayload(finalMessages);
1305
+ const preInvokeContextOverflowError = !finalProjection.fits ? createProviderPayloadOverflowError({
1306
+ projection: finalProjection,
1307
+ provider: agentContext.provider,
1308
+ info: "Provider message formatting exceeded the context budget and no safe synthetic-context compaction could make it fit."
1309
+ }) : void 0;
1095
1310
  if (agentContext.lastStreamCall != null && agentContext.streamBuffer != null) {
1096
1311
  const timeSinceLastCall = Date.now() - agentContext.lastStreamCall;
1097
1312
  if (timeSinceLastCall < agentContext.streamBuffer) await require_run.sleep(Math.ceil((agentContext.streamBuffer - timeSinceLastCall) / 1e3) * 1e3);
@@ -1125,28 +1340,9 @@ var StandardGraph = class StandardGraph extends Graph {
1125
1340
  }
1126
1341
  /** Past the empty-prompt guard — a model call is now guaranteed */
1127
1342
  if (contextUsage != null) {
1128
- const usageRatio = contextUsage.calibrationRatio != null && contextUsage.calibrationRatio > 0 ? contextUsage.calibrationRatio : 1;
1129
- if (agentContext.tokenCounter != null && finalMessages.length !== messagesToUse.length) {
1130
- /** Post-prune formatting restructured the payload (e.g. thinking
1131
- * placeholder collapse, orphan drops) — recount so the gauge
1132
- * reflects what is actually sent */
1133
- let rawTokens = 0;
1134
- for (const message of finalMessages) rawTokens += agentContext.tokenCounter(message);
1343
+ if (finalProjection.projectedMessageTokens != null && finalProjection.availableMessageTokens != null) {
1135
1344
  contextUsage.breakdown.messageCount = finalMessages.length;
1136
- if (contextUsage.contextBudget != null && contextUsage.effectiveInstructionTokens != null) contextUsage.remainingContextTokens = Math.max(0, contextUsage.contextBudget - contextUsage.effectiveInstructionTokens - Math.round(rawTokens * usageRatio));
1137
- } else if (preFormatTailTokens != null && agentContext.tokenCounter != null && contextUsage.remainingContextTokens != null) {
1138
- /** Same-length formatting can still mutate in place — the trailing
1139
- * tool batch (artifacts, Bedrock rewrites) and any legacy-converted
1140
- * messages before it — adjust remaining by the calibrated delta */
1141
- let postFormatTailTokens = 0;
1142
- for (const message of finalMessages.slice(tailStart)) postFormatTailTokens += agentContext.tokenCounter(message);
1143
- let formatDelta = postFormatTailTokens - preFormatTailTokens;
1144
- if (legacyIndices != null && legacyIndices.length > 0) {
1145
- let postFormatLegacyTokens = 0;
1146
- for (const index of legacyIndices) postFormatLegacyTokens += agentContext.tokenCounter(finalMessages[index]);
1147
- formatDelta += postFormatLegacyTokens - preFormatLegacyTokens;
1148
- }
1149
- if (formatDelta !== 0) contextUsage.remainingContextTokens = Math.max(0, Math.min(contextUsage.contextBudget ?? Number.MAX_SAFE_INTEGER, contextUsage.remainingContextTokens - Math.round(formatDelta * usageRatio)));
1345
+ contextUsage.remainingContextTokens = Math.max(0, finalProjection.availableMessageTokens - finalProjection.projectedMessageTokens);
1150
1346
  }
1151
1347
  require_budget.syncBudgetDerivedFields(contextUsage);
1152
1348
  /** Awaited so async host handlers receive the pre-invoke snapshot
@@ -1194,6 +1390,7 @@ var StandardGraph = class StandardGraph extends Graph {
1194
1390
  }
1195
1391
  const metadata = config.metadata;
1196
1392
  try {
1393
+ if (preInvokeContextOverflowError != null) throw preInvokeContextOverflowError;
1197
1394
  result = await require_langfuseRuntimeScope.withLangfuseRuntimeScope(require_langfuseRuntimeScope.resolveLangfuseRuntimeScope({
1198
1395
  runLangfuse: this.langfuse,
1199
1396
  langfuseOverlay: agentContext.langfuse
@@ -1223,31 +1420,37 @@ var StandardGraph = class StandardGraph extends Graph {
1223
1420
  * keeps the original error and one round trip instead of three.
1224
1421
  */
1225
1422
  const estimatedPromptTokens = getEstimatedPromptTokens(contextUsage);
1226
- /**
1227
- * A previous correction that left the prompt no smaller proves this
1228
- * state has nothing left to compact — an emptied message list whose
1229
- * content rides along in an injected summary, for instance. Measuring
1230
- * that beats trying to predict every such configuration.
1231
- */
1232
- const recoveryStalled = agentContext.overflowRecoveryStalled(estimatedPromptTokens);
1233
1423
  const canSummarizeOverflow = agentContext.summarizationEnabled === true && require_recency.splitAtRecencyBoundary(messages, {
1234
1424
  turns: agentContext.summarizationConfig?.retainRecent?.turns ?? 2,
1235
1425
  tokens: agentContext.summarizationConfig?.retainRecent?.tokens,
1236
1426
  tokenCounter: agentContext.tokenCounter
1237
1427
  }).head.length > 0;
1428
+ const getLocalProviderOverflowMeasurement = (error) => typeof error === "object" && error !== null ? localProviderOverflowMeasurements.get(error) : void 0;
1429
+ const getRecoveryPromptEstimate = (error, fallbackContext) => {
1430
+ const resolvedFallbackContext = fallbackContext ?? require_invoke.getFallbackErrorContext(error);
1431
+ return getLocalProviderOverflowMeasurement(error)?.estimatedPromptTokens ?? (resolvedFallbackContext == null ? estimatedPromptTokens : void 0);
1432
+ };
1238
1433
  const planRecovery = (error, attributedFallbackContext) => {
1239
- if (recoveryStalled) return null;
1240
1434
  /**
1241
1435
  * When the rejection came from a fallback, plan against *that*
1242
1436
  * client: its window and output allowance are why it was configured
1243
1437
  * as an alternative in the first place.
1244
1438
  */
1245
1439
  const fallbackContext = attributedFallbackContext ?? require_invoke.getFallbackErrorContext(error);
1440
+ const localMeasurement = getLocalProviderOverflowMeasurement(error);
1441
+ const recoveryPromptEstimate = getRecoveryPromptEstimate(error, fallbackContext);
1442
+ /**
1443
+ * A previous correction that left the rejected prompt no smaller
1444
+ * proves this state has nothing left to compact. Use the fallback
1445
+ * projection when one exists so unlike provider formats are never
1446
+ * compared through the primary's cheaper pre-projection estimate.
1447
+ */
1448
+ if (agentContext.overflowRecoveryStalled(recoveryPromptEstimate)) return null;
1246
1449
  const recovery = require_contextOverflowRecovery.planContextOverflowRecovery({
1247
1450
  error,
1248
1451
  provider: fallbackContext?.provider ?? agentContext.provider,
1249
- maxContextTokens: fallbackContext?.maxContextTokens ?? agentContext.maxContextTokens,
1250
- estimatedPromptTokens,
1452
+ maxContextTokens: localMeasurement?.contextBudget ?? fallbackContext?.maxContextTokens ?? agentContext.maxContextTokens,
1453
+ estimatedPromptTokens: recoveryPromptEstimate,
1251
1454
  calibrationRatio: agentContext.calibrationRatio,
1252
1455
  instructionTokens: agentContext.instructionTokens,
1253
1456
  canSummarize: agentContext.summarizationEnabled === true,
@@ -1257,20 +1460,23 @@ var StandardGraph = class StandardGraph extends Graph {
1257
1460
  if (recovery == null) return null;
1258
1461
  const translatedRecovery = fallbackContext != null ? {
1259
1462
  ...recovery,
1260
- budgetTokens: minDefined(require_contextOverflowRecovery.getBlindRecoveryBudget(agentContext.maxContextTokens), require_contextOverflowRecovery.translateRecoveryBudget(recovery.budgetTokens, recovery.observedCalibrationRatio ?? 5, agentContext.calibrationRatio)),
1463
+ budgetTokens: minDefined(require_contextOverflowRecovery.getBlindRecoveryBudget(agentContext.maxContextTokens), localMeasurement != null ? recovery.budgetTokens : require_contextOverflowRecovery.translateRecoveryBudget(recovery.budgetTokens, recovery.observedCalibrationRatio ?? 5, agentContext.calibrationRatio)),
1261
1464
  observedCalibrationRatio: void 0
1262
1465
  } : recovery;
1263
1466
  return canSummarizeOverflow || agentContext.tokenCounter != null && translatedRecovery.budgetTokens != null ? translatedRecovery : null;
1264
1467
  };
1265
1468
  const recovery = planRecovery(primaryError);
1266
- if (recovery != null) return this.beginOverflowRecovery({
1267
- recovery,
1268
- agentContext,
1269
- agentId,
1270
- config,
1271
- originalToolContent: prunedOriginalToolContent,
1272
- estimatedPromptTokens
1273
- });
1469
+ if (recovery != null) {
1470
+ const recoveryPromptEstimate = getRecoveryPromptEstimate(primaryError);
1471
+ return this.beginOverflowRecovery({
1472
+ recovery,
1473
+ agentContext,
1474
+ agentId,
1475
+ config,
1476
+ originalToolContent: prunedOriginalToolContent,
1477
+ estimatedPromptTokens: recoveryPromptEstimate
1478
+ });
1479
+ }
1274
1480
  /**
1275
1481
  * A fallback can reject the same prompt as too large even when the
1276
1482
  * primary failed for an unrelated reason — a fallback with a smaller
@@ -1284,7 +1490,7 @@ var StandardGraph = class StandardGraph extends Graph {
1284
1490
  }), () => require_invoke.tryFallbackProviders({
1285
1491
  fallbacks,
1286
1492
  tools: agentContext.tools,
1287
- messages: finalMessages,
1493
+ messages: fallbackBaseMessages,
1288
1494
  config: invokeConfig,
1289
1495
  primaryError,
1290
1496
  context: this,
@@ -1297,16 +1503,40 @@ var StandardGraph = class StandardGraph extends Graph {
1297
1503
  provider: agentContext.provider,
1298
1504
  estimatedPromptTokens: getEstimatedPromptTokens(contextUsage),
1299
1505
  maxContextTokens: agentContext.maxContextTokens
1506
+ },
1507
+ prepareProviderMessages: ({ model: fallbackModel, messages: fallbackMessages, provider: fallbackProvider, maxContextTokens: fallbackMaxContextTokens, config: fallbackConfig }) => {
1508
+ const projectedFallbackMessages = trackProviderMessageOrigins(fallbackMessages, require_invoke.projectMessagesForProvider({
1509
+ model: fallbackModel,
1510
+ messages: fallbackMessages,
1511
+ provider: fallbackProvider,
1512
+ maxToolResultChars: agentContext.maxToolResultChars ?? require_truncation.calculateMaxToolResultChars(fallbackMaxContextTokens ?? agentContext.maxContextTokens),
1513
+ callOptions: fallbackConfig
1514
+ }));
1515
+ const primaryContextBudget = contextUsage?.contextBudget;
1516
+ const projection = measureProviderPayload(projectedFallbackMessages, fallbackMaxContextTokens == null ? primaryContextBudget : Math.min(primaryContextBudget ?? fallbackMaxContextTokens, fallbackMaxContextTokens), true);
1517
+ if (!projection.fits) throw createProviderPayloadOverflowError({
1518
+ projection,
1519
+ provider: fallbackProvider,
1520
+ info: "Fallback provider message formatting exceeded the context budget before invocation."
1521
+ });
1522
+ return projectedFallbackMessages;
1300
1523
  }
1301
1524
  }));
1302
1525
  } catch (fallbackError) {
1303
1526
  const overflowCandidates = require_invoke.getFallbackOverflowCandidates(fallbackError);
1304
1527
  let fallbackRecovery = null;
1528
+ let fallbackRecoveryPromptEstimate;
1305
1529
  for (const candidate of overflowCandidates) {
1306
1530
  fallbackRecovery = planRecovery(candidate.error, candidate.context);
1307
- if (fallbackRecovery != null) break;
1531
+ if (fallbackRecovery != null) {
1532
+ fallbackRecoveryPromptEstimate = getRecoveryPromptEstimate(candidate.error, candidate.context);
1533
+ break;
1534
+ }
1535
+ }
1536
+ if (overflowCandidates.length === 0) {
1537
+ fallbackRecovery = planRecovery(fallbackError);
1538
+ fallbackRecoveryPromptEstimate = getRecoveryPromptEstimate(fallbackError);
1308
1539
  }
1309
- if (overflowCandidates.length === 0) fallbackRecovery = planRecovery(fallbackError);
1310
1540
  if (fallbackRecovery == null) throw fallbackError;
1311
1541
  return this.beginOverflowRecovery({
1312
1542
  recovery: fallbackRecovery,
@@ -1314,7 +1544,7 @@ var StandardGraph = class StandardGraph extends Graph {
1314
1544
  agentId,
1315
1545
  config,
1316
1546
  originalToolContent: prunedOriginalToolContent,
1317
- estimatedPromptTokens
1547
+ estimatedPromptTokens: fallbackRecoveryPromptEstimate
1318
1548
  });
1319
1549
  }
1320
1550
  } finally {
@@ -1690,13 +1920,16 @@ var StandardGraph = class StandardGraph extends Graph {
1690
1920
  const stepId = graph.toolCallStepIds.get(data.id) ?? "";
1691
1921
  if (!stepId) return false;
1692
1922
  const { name, input: args, error } = data;
1923
+ const eventValueLimit = require_truncation.calculateMaxToolResultChars();
1924
+ const errorOutputPrefix = "Error processing tool";
1925
+ const errorDetail = error?.message != null ? `: ${require_toolContent.serializeToolContentBounded(error.message, Math.max(0, eventValueLimit - 21 - 2))}` : "";
1693
1926
  const runStep = graph.getRunStep(stepId);
1694
1927
  if (!runStep) return false;
1695
1928
  const tool_call = {
1696
1929
  id: data.id,
1697
1930
  name: name || "",
1698
- args: typeof args === "string" ? args : JSON.stringify(args),
1699
- output: `Error processing tool${error?.message != null ? `: ${error.message}` : ""}`,
1931
+ args: require_toolContent.serializeToolContentBounded(args, eventValueLimit),
1932
+ output: `${errorOutputPrefix}${errorDetail}`,
1700
1933
  progress: 1
1701
1934
  };
1702
1935
  const handler = graph.handlerRegistry?.getHandler("on_run_step_completed");