@librechat/agents 3.3.0 → 3.3.1

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 (129) hide show
  1. package/dist/cjs/agents/AgentContext.cjs +116 -2
  2. package/dist/cjs/agents/AgentContext.cjs.map +1 -1
  3. package/dist/cjs/common/enum.cjs +2 -0
  4. package/dist/cjs/common/enum.cjs.map +1 -1
  5. package/dist/cjs/graphs/Graph.cjs +229 -48
  6. package/dist/cjs/graphs/Graph.cjs.map +1 -1
  7. package/dist/cjs/graphs/MultiAgentGraph.cjs +102 -27
  8. package/dist/cjs/graphs/MultiAgentGraph.cjs.map +1 -1
  9. package/dist/cjs/llm/bedrock/index.cjs +11 -2
  10. package/dist/cjs/llm/bedrock/index.cjs.map +1 -1
  11. package/dist/cjs/llm/contextOverflowRecovery.cjs +132 -0
  12. package/dist/cjs/llm/contextOverflowRecovery.cjs.map +1 -0
  13. package/dist/cjs/llm/google/index.cjs +1 -1
  14. package/dist/cjs/llm/invoke.cjs +60 -3
  15. package/dist/cjs/llm/invoke.cjs.map +1 -1
  16. package/dist/cjs/main.cjs +20 -7
  17. package/dist/cjs/messages/prune.cjs +16 -5
  18. package/dist/cjs/messages/prune.cjs.map +1 -1
  19. package/dist/cjs/messages/recency.cjs +2 -0
  20. package/dist/cjs/messages/recency.cjs.map +1 -1
  21. package/dist/cjs/run.cjs +15 -2
  22. package/dist/cjs/run.cjs.map +1 -1
  23. package/dist/cjs/stream.cjs +205 -49
  24. package/dist/cjs/stream.cjs.map +1 -1
  25. package/dist/cjs/summarization/node.cjs +55 -7
  26. package/dist/cjs/summarization/node.cjs.map +1 -1
  27. package/dist/cjs/tools/BashExecutor.cjs +2 -2
  28. package/dist/cjs/tools/BashExecutor.cjs.map +1 -1
  29. package/dist/cjs/tools/BashProgrammaticToolCalling.cjs +2 -2
  30. package/dist/cjs/tools/BashProgrammaticToolCalling.cjs.map +1 -1
  31. package/dist/cjs/tools/CodeExecutor.cjs +69 -8
  32. package/dist/cjs/tools/CodeExecutor.cjs.map +1 -1
  33. package/dist/cjs/tools/ProgrammaticToolCalling.cjs +19 -15
  34. package/dist/cjs/tools/ProgrammaticToolCalling.cjs.map +1 -1
  35. package/dist/cjs/tools/ToolNode.cjs +32 -3
  36. package/dist/cjs/tools/ToolNode.cjs.map +1 -1
  37. package/dist/cjs/utils/errors.cjs +317 -52
  38. package/dist/cjs/utils/errors.cjs.map +1 -1
  39. package/dist/esm/agents/AgentContext.mjs +117 -3
  40. package/dist/esm/agents/AgentContext.mjs.map +1 -1
  41. package/dist/esm/common/enum.mjs +2 -0
  42. package/dist/esm/common/enum.mjs.map +1 -1
  43. package/dist/esm/graphs/Graph.mjs +227 -46
  44. package/dist/esm/graphs/Graph.mjs.map +1 -1
  45. package/dist/esm/graphs/MultiAgentGraph.mjs +102 -27
  46. package/dist/esm/graphs/MultiAgentGraph.mjs.map +1 -1
  47. package/dist/esm/llm/bedrock/index.mjs +11 -2
  48. package/dist/esm/llm/bedrock/index.mjs.map +1 -1
  49. package/dist/esm/llm/contextOverflowRecovery.mjs +130 -0
  50. package/dist/esm/llm/contextOverflowRecovery.mjs.map +1 -0
  51. package/dist/esm/llm/google/index.mjs +1 -1
  52. package/dist/esm/llm/invoke.mjs +59 -4
  53. package/dist/esm/llm/invoke.mjs.map +1 -1
  54. package/dist/esm/main.mjs +9 -9
  55. package/dist/esm/messages/prune.mjs +14 -6
  56. package/dist/esm/messages/prune.mjs.map +1 -1
  57. package/dist/esm/messages/recency.mjs +2 -1
  58. package/dist/esm/messages/recency.mjs.map +1 -1
  59. package/dist/esm/run.mjs +15 -2
  60. package/dist/esm/run.mjs.map +1 -1
  61. package/dist/esm/stream.mjs +205 -49
  62. package/dist/esm/stream.mjs.map +1 -1
  63. package/dist/esm/summarization/node.mjs +55 -7
  64. package/dist/esm/summarization/node.mjs.map +1 -1
  65. package/dist/esm/tools/BashExecutor.mjs +3 -3
  66. package/dist/esm/tools/BashExecutor.mjs.map +1 -1
  67. package/dist/esm/tools/BashProgrammaticToolCalling.mjs +3 -3
  68. package/dist/esm/tools/BashProgrammaticToolCalling.mjs.map +1 -1
  69. package/dist/esm/tools/CodeExecutor.mjs +62 -9
  70. package/dist/esm/tools/CodeExecutor.mjs.map +1 -1
  71. package/dist/esm/tools/ProgrammaticToolCalling.mjs +20 -16
  72. package/dist/esm/tools/ProgrammaticToolCalling.mjs.map +1 -1
  73. package/dist/esm/tools/ToolNode.mjs +32 -3
  74. package/dist/esm/tools/ToolNode.mjs.map +1 -1
  75. package/dist/esm/utils/errors.mjs +317 -53
  76. package/dist/esm/utils/errors.mjs.map +1 -1
  77. package/dist/types/agents/AgentContext.d.ts +62 -3
  78. package/dist/types/common/enum.d.ts +2 -0
  79. package/dist/types/graphs/Graph.d.ts +16 -2
  80. package/dist/types/graphs/MultiAgentGraph.d.ts +1 -0
  81. package/dist/types/llm/contextOverflowRecovery.d.ts +85 -0
  82. package/dist/types/llm/invoke.d.ts +35 -6
  83. package/dist/types/messages/prune.d.ts +10 -2
  84. package/dist/types/messages/recency.d.ts +1 -0
  85. package/dist/types/run.d.ts +2 -0
  86. package/dist/types/tools/CodeExecutor.d.ts +14 -1
  87. package/dist/types/types/llm.d.ts +7 -4
  88. package/dist/types/types/stream.d.ts +5 -4
  89. package/dist/types/types/summarize.d.ts +22 -0
  90. package/dist/types/utils/__tests__/fixtures/contextOverflowSignatures.d.ts +40 -0
  91. package/dist/types/utils/errors.d.ts +65 -16
  92. package/dist/types/utils/redactSecrets.d.ts +3 -0
  93. package/package.json +7 -8
  94. package/src/agents/AgentContext.ts +188 -7
  95. package/src/agents/__tests__/AgentContext.overflow.test.ts +205 -0
  96. package/src/common/enum.ts +2 -0
  97. package/src/graphs/Graph.ts +389 -58
  98. package/src/graphs/MultiAgentGraph.ts +184 -46
  99. package/src/graphs/__tests__/Graph.contextOverflow.test.ts +631 -0
  100. package/src/llm/__tests__/contextOverflowRecovery.test.ts +401 -0
  101. package/src/llm/__tests__/fallbackOverflow.test.ts +287 -0
  102. package/src/llm/bedrock/index.ts +25 -12
  103. package/src/llm/contextOverflowRecovery.ts +292 -0
  104. package/src/llm/invoke.ts +119 -4
  105. package/src/messages/prune.ts +24 -11
  106. package/src/messages/recency.ts +3 -1
  107. package/src/run.ts +24 -1
  108. package/src/scripts/context-overflow-probe.ts +997 -0
  109. package/src/specs/agent-handoffs.test.ts +903 -1
  110. package/src/specs/context-overflow-recovery.live.test.ts +213 -0
  111. package/src/splitStream.test.ts +882 -0
  112. package/src/stream.ts +315 -51
  113. package/src/summarization/__tests__/aggregator.test.ts +83 -0
  114. package/src/summarization/__tests__/node.test.ts +139 -0
  115. package/src/summarization/node.ts +99 -14
  116. package/src/tools/BashExecutor.ts +4 -2
  117. package/src/tools/BashProgrammaticToolCalling.ts +4 -7
  118. package/src/tools/CodeExecutor.ts +119 -8
  119. package/src/tools/ProgrammaticToolCalling.ts +29 -27
  120. package/src/tools/ToolNode.ts +50 -8
  121. package/src/tools/__tests__/CodeApiAuthHeaders.test.ts +297 -3
  122. package/src/types/llm.ts +8 -1
  123. package/src/types/stream.ts +5 -4
  124. package/src/types/summarize.ts +22 -0
  125. package/src/utils/__tests__/errors.test.ts +270 -0
  126. package/src/utils/__tests__/fixtures/contextOverflowSignatures.ts +336 -0
  127. package/src/utils/__tests__/redactSecrets.test.ts +56 -0
  128. package/src/utils/errors.ts +484 -66
  129. package/src/utils/redactSecrets.ts +61 -0
@@ -18,6 +18,7 @@ const require_anthropicToolCache = require("../messages/anthropicToolCache.cjs")
18
18
  const require_content = require("../messages/content.cjs");
19
19
  const require_tools = require("../messages/tools.cjs");
20
20
  const require_reducer = require("../messages/reducer.cjs");
21
+ const require_recency = require("../messages/recency.cjs");
21
22
  require("../messages/index.cjs");
22
23
  const require_graph = require("../utils/graph.cjs");
23
24
  const require_llm = require("../utils/llm.cjs");
@@ -26,7 +27,11 @@ const require_toolOutputReferences = require("../tools/toolOutputReferences.cjs"
26
27
  const require_events$1 = require("../events.cjs");
27
28
  const require_run = require("../utils/run.cjs");
28
29
  require("../utils/index.cjs");
29
- const require_toolCache = require("../llm/openrouter/toolCache.cjs");
30
+ const require_contextOverflowRecovery = require("../llm/contextOverflowRecovery.cjs");
31
+ const require_toolCache = require("../llm/bedrock/toolCache.cjs");
32
+ const require_init = require("../llm/init.cjs");
33
+ const require_invoke = require("../llm/invoke.cjs");
34
+ const require_toolCache$1 = require("../llm/openrouter/toolCache.cjs");
30
35
  const require_LocalCodingTools = require("../tools/local/LocalCodingTools.cjs");
31
36
  const require_CloudflareSandboxTools = require("../tools/cloudflare/CloudflareSandboxTools.cjs");
32
37
  require("../tools/cloudflare/index.cjs");
@@ -35,9 +40,6 @@ require("../tools/local/index.cjs");
35
40
  const require_ToolNode = require("../tools/ToolNode.cjs");
36
41
  const require_SubagentExecutor = require("../tools/subagent/SubagentExecutor.cjs");
37
42
  require("../tools/subagent/index.cjs");
38
- const require_toolCache$1 = require("../llm/bedrock/toolCache.cjs");
39
- const require_init = require("../llm/init.cjs");
40
- const require_invoke = require("../llm/invoke.cjs");
41
43
  const require_SubagentTool = require("../tools/SubagentTool.cjs");
42
44
  const require_index$6 = require("../summarization/index.cjs");
43
45
  const require_request = require("../llm/request.cjs");
@@ -161,6 +163,33 @@ function clearCurrentDeltaStepMarkers({ graph, metadata }) {
161
163
  graph.reasoningStepHasDeltas.delete(stepId);
162
164
  }
163
165
  }
166
+ /**
167
+ * The completion allowance the caller configured, under whichever key the
168
+ * provider's client uses. Providers count it against the same ceiling as the
169
+ * prompt, so overflow recovery has to reserve it when the error did not
170
+ * itemize the total.
171
+ */
172
+ function getConfiguredCompletionTokens(clientOptions) {
173
+ const options = clientOptions;
174
+ for (const value of [options?.maxTokens, options?.maxOutputTokens]) if (typeof value === "number" && Number.isFinite(value) && value > 0) return value;
175
+ }
176
+ /**
177
+ * Our own estimate of the prompt that was actually sent, derived from the
178
+ * pre-invoke usage snapshot. Used to corroborate ambiguous provider errors
179
+ * and to measure how far our token accounting sits from the provider's.
180
+ */
181
+ function getEstimatedPromptTokens(contextUsage) {
182
+ const budget = contextUsage?.contextBudget;
183
+ const remaining = contextUsage?.remainingContextTokens;
184
+ if (budget == null || remaining == null || !Number.isFinite(budget) || !Number.isFinite(remaining)) return;
185
+ const used = budget - remaining;
186
+ return used > 0 ? used : void 0;
187
+ }
188
+ function minDefined(left, right) {
189
+ if (left == null) return right;
190
+ if (right == null) return left;
191
+ return Math.min(left, right);
192
+ }
164
193
  async function dispatchMessageCreationStep({ graph, stepKey, messageId, metadata }) {
165
194
  await graph.dispatchRunStep(stepKey, {
166
195
  type: "message_creation",
@@ -457,9 +486,13 @@ var StandardGraph = class StandardGraph extends Graph {
457
486
  overrideModel;
458
487
  /** Optional compile options passed into workflow.compile() */
459
488
  compileOptions;
489
+ /** Whether the workflow was actually compiled with a checkpointer. */
490
+ hasCompiledCheckpointer = false;
460
491
  messages = [];
461
492
  /** Cached run messages preserved before clearHeavyState() so getRunMessages() works after cleanup. */
462
493
  cachedRunMessages;
494
+ /** Checkpoint scope whose messages match index-keyed tool snapshots. */
495
+ originalToolContentCheckpointScope;
463
496
  runId;
464
497
  /**
465
498
  * Boundary between historical messages (loaded from conversation state)
@@ -498,7 +531,7 @@ var StandardGraph = class StandardGraph extends Graph {
498
531
  }
499
532
  this.defaultAgentId = agents[0].agentId;
500
533
  }
501
- resetValues(keepContent) {
534
+ resetValues(keepContent, checkpointScope) {
502
535
  this.messages = [];
503
536
  this.cachedRunMessages = void 0;
504
537
  this.config = require_graph.resetIfNotEmpty(this.config, void 0);
@@ -525,14 +558,18 @@ var StandardGraph = class StandardGraph extends Graph {
525
558
  this.reasoningStepHasDeltas = require_graph.resetIfNotEmpty(this.reasoningStepHasDeltas, /* @__PURE__ */ new Set());
526
559
  this.prelimMessageIdsByStepKey = require_graph.resetIfNotEmpty(this.prelimMessageIdsByStepKey, /* @__PURE__ */ new Map());
527
560
  this.invokedToolIds = require_graph.resetIfNotEmpty(this.invokedToolIds, void 0);
528
- for (const context of this.agentContexts.values()) context.reset();
561
+ const hasScopedCheckpoint = this.hasCompiledCheckpointer && checkpointScope != null && checkpointScope !== "";
562
+ const preserveOriginalToolContent = hasScopedCheckpoint && this.originalToolContentCheckpointScope === checkpointScope;
563
+ for (const context of this.agentContexts.values()) context.reset({ preserveOriginalToolContent });
564
+ this.originalToolContentCheckpointScope = hasScopedCheckpoint ? checkpointScope : void 0;
529
565
  }
530
566
  clearHeavyState() {
531
567
  this.cachedRunMessages = this.messages.slice(this.startIndex);
532
568
  super.clearHeavyState();
533
569
  this.messages = [];
534
570
  this.overrideModel = void 0;
535
- for (const context of this.agentContexts.values()) context.reset();
571
+ const preserveOriginalToolContent = this.hasCompiledCheckpointer && this.originalToolContentCheckpointScope != null;
572
+ for (const context of this.agentContexts.values()) context.reset({ preserveOriginalToolContent });
536
573
  }
537
574
  getRunStep(stepId) {
538
575
  const index = this.contentIndexMap.get(stepId);
@@ -767,6 +804,49 @@ var StandardGraph = class StandardGraph extends Graph {
767
804
  this.signal.removeEventListener("abort", client.abortHandler);
768
805
  client.abortHandler = void 0;
769
806
  }
807
+ /**
808
+ * Applies a context-overflow recovery plan and hands control to the
809
+ * summarize node, which compacts and then routes straight back here for a
810
+ * retry against the corrected budget.
811
+ *
812
+ * Returning the detour rather than rethrowing is the whole point: the
813
+ * caller never sees the provider's rejection, only a slightly longer turn.
814
+ */
815
+ beginOverflowRecovery({ recovery, agentContext, agentId, config, originalToolContent, estimatedPromptTokens }) {
816
+ const previousBudget = agentContext.maxContextTokens;
817
+ /**
818
+ * Deterministic compaction first. Re-pruning against the corrected budget
819
+ * raises context pressure, which is what drives the pruner's tool-output
820
+ * truncation and observation masking — no model call, no cost, and no
821
+ * message content lost. A summarization call is held back until that has
822
+ * been tried and the provider rejected the prompt again.
823
+ */
824
+ const allowSummarization = agentContext.shouldSummarizeOverflow();
825
+ agentContext.preserveOriginalToolContent(originalToolContent);
826
+ agentContext.applyContextBudgetCorrection(recovery.budgetTokens, estimatedPromptTokens);
827
+ agentContext.applyObservedOverflowCalibration(recovery.info.provider, recovery.observedCalibrationRatio);
828
+ require_events.emitAgentLog(config, "warn", "graph", "Provider rejected the prompt as too large — compacting and retrying", {
829
+ kind: recovery.info.kind,
830
+ previousBudget,
831
+ recoveredBudget: recovery.budgetTokens,
832
+ providerReportedLimit: recovery.info.limitTokens,
833
+ providerReportedTokens: recovery.info.requestedTokens,
834
+ providerReportedPromptTokens: recovery.info.promptTokens,
835
+ observedCalibrationRatio: recovery.observedCalibrationRatio,
836
+ detectedBy: recovery.info.source,
837
+ attempt: agentContext.overflowRecoveryAttempts,
838
+ compaction: allowSummarization ? "summarize" : "compress"
839
+ }, {
840
+ runId: this.runId,
841
+ agentId
842
+ }, { force: true });
843
+ return { summarizationRequest: {
844
+ remainingContextTokens: 0,
845
+ agentId: agentId || agentContext.agentId,
846
+ reason: "overflow",
847
+ allowSummarization
848
+ } };
849
+ }
770
850
  createCallModel(agentId = "default") {
771
851
  return async (state, config) => {
772
852
  const agentContext = this.agentContexts.get(agentId);
@@ -794,10 +874,10 @@ var StandardGraph = class StandardGraph extends Graph {
794
874
  */
795
875
  let toolsForBinding = rawToolsForBinding;
796
876
  if (agentContext.provider === "anthropic" && agentContext.clientOptions?.promptCache === true) toolsForBinding = require_anthropicToolCache.partitionAndMarkAnthropicToolCache(rawToolsForBinding, require_anthropicToolCache.makeIsDeferred(agentContext.toolDefinitions), require_cache.resolvePromptCacheTtl(agentContext.clientOptions?.promptCacheTtl)) ?? rawToolsForBinding;
797
- else if (agentContext.provider === "openrouter" && agentContext.clientOptions?.promptCache === true) toolsForBinding = require_toolCache.partitionAndMarkOpenRouterToolCache(rawToolsForBinding, require_anthropicToolCache.makeIsDeferred(agentContext.toolDefinitions), require_cache.resolvePromptCacheTtl(agentContext.clientOptions?.promptCacheTtl)) ?? rawToolsForBinding;
877
+ else if (agentContext.provider === "openrouter" && agentContext.clientOptions?.promptCache === true) toolsForBinding = require_toolCache$1.partitionAndMarkOpenRouterToolCache(rawToolsForBinding, require_anthropicToolCache.makeIsDeferred(agentContext.toolDefinitions), require_cache.resolvePromptCacheTtl(agentContext.clientOptions?.promptCacheTtl)) ?? rawToolsForBinding;
798
878
  else if (agentContext.provider === "bedrock" && agentContext.clientOptions?.promptCache === true) {
799
879
  const bedrockModel = agentContext.clientOptions?.model;
800
- if (bedrockModel == null || require_cache.supportsBedrockToolCache(bedrockModel)) toolsForBinding = require_toolCache$1.partitionAndMarkBedrockToolCache(rawToolsForBinding, require_anthropicToolCache.makeIsDeferred(agentContext.toolDefinitions)) ?? rawToolsForBinding;
880
+ if (bedrockModel == null || require_cache.supportsBedrockToolCache(bedrockModel)) toolsForBinding = require_toolCache.partitionAndMarkBedrockToolCache(rawToolsForBinding, require_anthropicToolCache.makeIsDeferred(agentContext.toolDefinitions)) ?? rawToolsForBinding;
801
881
  }
802
882
  let model = this.overrideModel ?? require_init.initializeModel({
803
883
  tools: toolsForBinding,
@@ -810,6 +890,12 @@ var StandardGraph = class StandardGraph extends Graph {
810
890
  this.config = config;
811
891
  let messagesToUse = messages;
812
892
  let contextUsage = null;
893
+ /**
894
+ * Held outside the prune block so overflow recovery — which detours to
895
+ * the summarize node from the invoke catch below — can preserve the
896
+ * same masking record the configured trigger preserves.
897
+ */
898
+ let prunedOriginalToolContent;
813
899
  if (!agentContext.pruneMessages && agentContext.tokenCounter && agentContext.maxContextTokens != null) agentContext.pruneMessages = require_prune.createPruneMessages({
814
900
  startIndex: agentContext.indexTokenCountMap[0] != null ? this.startIndex : 0,
815
901
  provider: agentContext.provider,
@@ -830,12 +916,22 @@ var StandardGraph = class StandardGraph extends Graph {
830
916
  }
831
917
  });
832
918
  if (agentContext.pruneMessages) {
833
- const { context, indexTokenCountMap, messagesToRefine, prePruneContextTokens, remainingContextTokens, originalToolContent, calibrationRatio, resolvedInstructionOverhead, contextBudget, effectiveInstructionTokens } = agentContext.pruneMessages({
919
+ const { context, indexTokenCountMap, messagesToRefine, prePruneContextTokens, remainingContextTokens, newOriginalToolContent, calibrationRatio, resolvedInstructionOverhead, contextBudget, effectiveInstructionTokens } = agentContext.pruneMessages({
834
920
  messages,
835
921
  usageMetadata: agentContext.currentUsage,
836
922
  lastCallUsage: agentContext.lastCallUsage,
837
923
  totalTokensFresh: agentContext.totalTokensFresh
838
924
  });
925
+ prunedOriginalToolContent = newOriginalToolContent;
926
+ /**
927
+ * Masking rewrites tool content in `state.messages` in place, so this
928
+ * map is the only surviving copy of the full output. Persist it on
929
+ * every prune, not just when a summary is about to be written — the
930
+ * pruner closure that produced it is discarded on the next reset, and
931
+ * with it any chance of a later summary restoring the real content.
932
+ * AgentContext bounds what accumulates.
933
+ */
934
+ agentContext.preserveOriginalToolContent(newOriginalToolContent);
839
935
  agentContext.indexTokenCountMap = indexTokenCountMap;
840
936
  if (calibrationRatio != null && calibrationRatio > 0) agentContext.calibrationRatio = calibrationRatio;
841
937
  if (resolvedInstructionOverhead != null) {
@@ -882,31 +978,6 @@ var StandardGraph = class StandardGraph extends Graph {
882
978
  remainingContextTokens,
883
979
  messagesToRefineCount: messagesToRefine.length
884
980
  })) {
885
- if (originalToolContent != null && originalToolContent.size > 0)
886
- /**
887
- * Merge — never overwrite — the pruner's masking record
888
- * into pendingOriginalToolContent. Carry-over entries
889
- * from a prior summarize (preserved by the recency
890
- * window for masked tool messages still in the tail) and
891
- * the current pruner's new entries are both keyed by
892
- * indices in the current `state.messages`, so a key-wise
893
- * union is correct. Overwriting would discard the
894
- * carry-over and reduce summary fidelity when those
895
- * masked tail messages eventually move into the head.
896
- */
897
- if (agentContext.pendingOriginalToolContent == null) agentContext.pendingOriginalToolContent = originalToolContent;
898
- else {
899
- for (const [idx, content] of originalToolContent) agentContext.pendingOriginalToolContent.set(idx, content);
900
- /**
901
- * Re-apply the per-store char cap after the union. The
902
- * pruner enforces ORIGINAL_CONTENT_MAX_CHARS inside its
903
- * own map via the onContentStored callback, but a
904
- * key-wise merge with recency carry-over bypasses that
905
- * accounting and could let the merged map grow without
906
- * bound across long sessions.
907
- */
908
- require_prune.enforceOriginalContentCap(agentContext.pendingOriginalToolContent);
909
- }
910
981
  require_events.emitAgentLog(config, "info", "graph", "Summarization triggered", void 0, {
911
982
  runId: this.runId,
912
983
  agentId
@@ -1137,17 +1208,115 @@ var StandardGraph = class StandardGraph extends Graph {
1137
1208
  graph: this,
1138
1209
  metadata
1139
1210
  });
1140
- result = await require_langfuseRuntimeScope.withLangfuseRuntimeScope(require_langfuseRuntimeScope.resolveLangfuseRuntimeScope({
1141
- runLangfuse: this.langfuse,
1142
- langfuseOverlay: agentContext.langfuse
1143
- }), () => require_invoke.tryFallbackProviders({
1144
- fallbacks,
1145
- tools: agentContext.tools,
1146
- messages: finalMessages,
1147
- config: invokeConfig,
1148
- primaryError,
1149
- context: this
1150
- }));
1211
+ /**
1212
+ * A context overflow is a deterministic consequence of the payload,
1213
+ * not a provider being unavailable — so it is answered by compacting
1214
+ * and retrying rather than by re-sending the same oversized prompt
1215
+ * down the fallback chain. Fallbacks still run for every other
1216
+ * failure, and for an overflow whose recovery budget is spent.
1217
+ */
1218
+ /**
1219
+ * Compaction has to have something to work with. Without a token
1220
+ * counter there is no pruner, and with summarization disabled the
1221
+ * summarize node deliberately no-ops — so in that combination the
1222
+ * retry would resend a byte-identical prompt. Skipping the detour
1223
+ * keeps the original error and one round trip instead of three.
1224
+ */
1225
+ 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
+ const canSummarizeOverflow = agentContext.summarizationEnabled === true && require_recency.splitAtRecencyBoundary(messages, {
1234
+ turns: agentContext.summarizationConfig?.retainRecent?.turns ?? 2,
1235
+ tokens: agentContext.summarizationConfig?.retainRecent?.tokens,
1236
+ tokenCounter: agentContext.tokenCounter
1237
+ }).head.length > 0;
1238
+ const planRecovery = (error, attributedFallbackContext) => {
1239
+ if (recoveryStalled) return null;
1240
+ /**
1241
+ * When the rejection came from a fallback, plan against *that*
1242
+ * client: its window and output allowance are why it was configured
1243
+ * as an alternative in the first place.
1244
+ */
1245
+ const fallbackContext = attributedFallbackContext ?? require_invoke.getFallbackErrorContext(error);
1246
+ const recovery = require_contextOverflowRecovery.planContextOverflowRecovery({
1247
+ error,
1248
+ provider: fallbackContext?.provider ?? agentContext.provider,
1249
+ maxContextTokens: fallbackContext?.maxContextTokens ?? agentContext.maxContextTokens,
1250
+ estimatedPromptTokens,
1251
+ calibrationRatio: agentContext.calibrationRatio,
1252
+ instructionTokens: agentContext.instructionTokens,
1253
+ canSummarize: agentContext.summarizationEnabled === true,
1254
+ configuredCompletionTokens: getConfiguredCompletionTokens(fallbackContext?.clientOptions ?? agentContext.clientOptions),
1255
+ attemptsSoFar: agentContext.overflowRecoveryAttempts
1256
+ });
1257
+ if (recovery == null) return null;
1258
+ const translatedRecovery = fallbackContext != null ? {
1259
+ ...recovery,
1260
+ budgetTokens: minDefined(require_contextOverflowRecovery.getBlindRecoveryBudget(agentContext.maxContextTokens), require_contextOverflowRecovery.translateRecoveryBudget(recovery.budgetTokens, recovery.observedCalibrationRatio ?? 5, agentContext.calibrationRatio)),
1261
+ observedCalibrationRatio: void 0
1262
+ } : recovery;
1263
+ return canSummarizeOverflow || agentContext.tokenCounter != null && translatedRecovery.budgetTokens != null ? translatedRecovery : null;
1264
+ };
1265
+ 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
+ });
1274
+ /**
1275
+ * A fallback can reject the same prompt as too large even when the
1276
+ * primary failed for an unrelated reason — a fallback with a smaller
1277
+ * window is the obvious case. Planning against the exhausted-chain
1278
+ * error keeps that path recoverable instead of surfacing it.
1279
+ */
1280
+ try {
1281
+ result = await require_langfuseRuntimeScope.withLangfuseRuntimeScope(require_langfuseRuntimeScope.resolveLangfuseRuntimeScope({
1282
+ runLangfuse: this.langfuse,
1283
+ langfuseOverlay: agentContext.langfuse
1284
+ }), () => require_invoke.tryFallbackProviders({
1285
+ fallbacks,
1286
+ tools: agentContext.tools,
1287
+ messages: finalMessages,
1288
+ config: invokeConfig,
1289
+ primaryError,
1290
+ context: this,
1291
+ /**
1292
+ * Lets the chain recognise a fallback overflow whose signature
1293
+ * carries no reason of its own (Vertex AI's bare 400) and
1294
+ * surface it rather than a later unrelated failure.
1295
+ */
1296
+ overflowContext: {
1297
+ provider: agentContext.provider,
1298
+ estimatedPromptTokens: getEstimatedPromptTokens(contextUsage),
1299
+ maxContextTokens: agentContext.maxContextTokens
1300
+ }
1301
+ }));
1302
+ } catch (fallbackError) {
1303
+ const overflowCandidates = require_invoke.getFallbackOverflowCandidates(fallbackError);
1304
+ let fallbackRecovery = null;
1305
+ for (const candidate of overflowCandidates) {
1306
+ fallbackRecovery = planRecovery(candidate.error, candidate.context);
1307
+ if (fallbackRecovery != null) break;
1308
+ }
1309
+ if (overflowCandidates.length === 0) fallbackRecovery = planRecovery(fallbackError);
1310
+ if (fallbackRecovery == null) throw fallbackError;
1311
+ return this.beginOverflowRecovery({
1312
+ recovery: fallbackRecovery,
1313
+ agentContext,
1314
+ agentId,
1315
+ config,
1316
+ originalToolContent: prunedOriginalToolContent,
1317
+ estimatedPromptTokens
1318
+ });
1319
+ }
1151
1320
  } finally {
1152
1321
  await require_langfuse.disposeLangfuseHandler(langfuseHandler);
1153
1322
  }
@@ -1386,9 +1555,13 @@ var StandardGraph = class StandardGraph extends Graph {
1386
1555
  isMultiAgent: this.isMultiAgentGraph(),
1387
1556
  hookRegistry: this.hookRegistry,
1388
1557
  dispatchRunStep: async (runStep, nodeConfig) => {
1558
+ const resolvedConfig = nodeConfig ?? this.config;
1559
+ if (runStep.agentId != null) {
1560
+ const groupId = this.resolveParallelGroupId(runStep.agentId, resolvedConfig?.metadata);
1561
+ if (groupId != null) runStep.groupId = groupId;
1562
+ }
1389
1563
  this.contentData.push(runStep);
1390
1564
  this.contentIndexMap.set(runStep.id, runStep.index);
1391
- const resolvedConfig = nodeConfig ?? this.config;
1392
1565
  const handler = this.handlerRegistry?.getHandler("on_run_step");
1393
1566
  if (handler) {
1394
1567
  await handler.handle("on_run_step", runStep, resolvedConfig?.configurable, this);
@@ -1416,6 +1589,7 @@ var StandardGraph = class StandardGraph extends Graph {
1416
1589
  })).addEdge(_langchain_langgraph.START, agentNode).addConditionalEdges(agentNode, routeMessage).addEdge(summarizeNode, agentNode).addEdge(toolNode, agentContext.toolEnd ? _langchain_langgraph.END : agentNode).compile();
1417
1590
  }
1418
1591
  createWorkflow() {
1592
+ this.hasCompiledCheckpointer = this.compileOptions?.checkpointer != null;
1419
1593
  const agentNode = this.createAgentNode(this.defaultAgentId);
1420
1594
  return new _langchain_langgraph.StateGraph(_langchain_langgraph.Annotation.Root({ messages: (0, _langchain_langgraph.Annotation)({
1421
1595
  reducer: (a, b) => {
@@ -1443,6 +1617,13 @@ var StandardGraph = class StandardGraph extends Graph {
1443
1617
  * @returns undefined for StandardGraph (no parallel groups), or group number for MultiAgentGraph
1444
1618
  */
1445
1619
  getParallelGroupIdForAgent(_agentId) {}
1620
+ resolveParallelGroupId(agentId, metadata) {
1621
+ if (metadata == null || !Object.prototype.hasOwnProperty.call(metadata, "__handoff_group_id")) return this.getParallelGroupIdForAgent(agentId);
1622
+ const runtimeGroupId = metadata["__handoff_group_id"];
1623
+ if (runtimeGroupId === null) return;
1624
+ if (typeof runtimeGroupId === "number" && Number.isSafeInteger(runtimeGroupId) && runtimeGroupId > 0) return runtimeGroupId;
1625
+ return this.getParallelGroupIdForAgent(agentId);
1626
+ }
1446
1627
  /**
1447
1628
  * Dispatches a run step to the client, returns the step ID
1448
1629
  */
@@ -1468,7 +1649,7 @@ var StandardGraph = class StandardGraph extends Graph {
1468
1649
  const agentContext = this.getAgentContext(metadata);
1469
1650
  if (this.isMultiAgentGraph() && agentContext.agentId) {
1470
1651
  runStep.agentId = agentContext.agentId;
1471
- const groupId = this.getParallelGroupIdForAgent(agentContext.agentId);
1652
+ const groupId = this.resolveParallelGroupId(agentContext.agentId, metadata);
1472
1653
  if (groupId != null) runStep.groupId = groupId;
1473
1654
  }
1474
1655
  } catch (_e) {}