@librechat/agents 3.3.11 → 3.3.13

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 (205) hide show
  1. package/dist/cjs/graphs/Graph.cjs +246 -25
  2. package/dist/cjs/graphs/Graph.cjs.map +1 -1
  3. package/dist/cjs/instrumentation.cjs +1 -0
  4. package/dist/cjs/instrumentation.cjs.map +1 -1
  5. package/dist/cjs/langfuseSpanRegistry.cjs +6 -3
  6. package/dist/cjs/langfuseSpanRegistry.cjs.map +1 -1
  7. package/dist/cjs/llm/bedrock/index.cjs +13 -2
  8. package/dist/cjs/llm/bedrock/index.cjs.map +1 -1
  9. package/dist/cjs/llm/invoke.cjs +146 -36
  10. package/dist/cjs/llm/invoke.cjs.map +1 -1
  11. package/dist/cjs/llm/openai/index.cjs +2 -2
  12. package/dist/cjs/llm/openai/index.cjs.map +1 -1
  13. package/dist/cjs/llm/streamLimits.cjs +723 -0
  14. package/dist/cjs/llm/streamLimits.cjs.map +1 -0
  15. package/dist/cjs/main.cjs +8 -0
  16. package/dist/cjs/run.cjs +6 -2
  17. package/dist/cjs/run.cjs.map +1 -1
  18. package/dist/cjs/session/AgentSession.cjs +4 -1
  19. package/dist/cjs/session/AgentSession.cjs.map +1 -1
  20. package/dist/cjs/stream.cjs +103 -12
  21. package/dist/cjs/stream.cjs.map +1 -1
  22. package/dist/cjs/summarization/node.cjs +157 -37
  23. package/dist/cjs/summarization/node.cjs.map +1 -1
  24. package/dist/cjs/tools/BashExecutor.cjs +3 -2
  25. package/dist/cjs/tools/BashExecutor.cjs.map +1 -1
  26. package/dist/cjs/tools/CodeExecutor.cjs +4 -3
  27. package/dist/cjs/tools/CodeExecutor.cjs.map +1 -1
  28. package/dist/cjs/tools/ProgrammaticToolCalling.cjs +5 -3
  29. package/dist/cjs/tools/ProgrammaticToolCalling.cjs.map +1 -1
  30. package/dist/cjs/tools/ToolNode.cjs +76 -3
  31. package/dist/cjs/tools/ToolNode.cjs.map +1 -1
  32. package/dist/cjs/tools/ToolSearch.cjs +3 -2
  33. package/dist/cjs/tools/ToolSearch.cjs.map +1 -1
  34. package/dist/cjs/tools/search/crw-scraper.cjs +7 -1
  35. package/dist/cjs/tools/search/crw-scraper.cjs.map +1 -1
  36. package/dist/cjs/tools/search/crw-search.cjs +3 -1
  37. package/dist/cjs/tools/search/crw-search.cjs.map +1 -1
  38. package/dist/cjs/tools/search/firecrawl.cjs +7 -1
  39. package/dist/cjs/tools/search/firecrawl.cjs.map +1 -1
  40. package/dist/cjs/tools/search/keenable-scraper.cjs +7 -1
  41. package/dist/cjs/tools/search/keenable-scraper.cjs.map +1 -1
  42. package/dist/cjs/tools/search/keenable-search.cjs +3 -1
  43. package/dist/cjs/tools/search/keenable-search.cjs.map +1 -1
  44. package/dist/cjs/tools/search/rerankers.cjs +26 -8
  45. package/dist/cjs/tools/search/rerankers.cjs.map +1 -1
  46. package/dist/cjs/tools/search/search.cjs +30 -10
  47. package/dist/cjs/tools/search/search.cjs.map +1 -1
  48. package/dist/cjs/tools/search/serper-scraper.cjs +7 -1
  49. package/dist/cjs/tools/search/serper-scraper.cjs.map +1 -1
  50. package/dist/cjs/tools/search/tavily-scraper.cjs +7 -1
  51. package/dist/cjs/tools/search/tavily-scraper.cjs.map +1 -1
  52. package/dist/cjs/tools/search/tavily-search.cjs +3 -1
  53. package/dist/cjs/tools/search/tavily-search.cjs.map +1 -1
  54. package/dist/cjs/tools/search/tool.cjs +16 -2
  55. package/dist/cjs/tools/search/tool.cjs.map +1 -1
  56. package/dist/cjs/tools/subagent/SubagentExecutor.cjs +54 -3
  57. package/dist/cjs/tools/subagent/SubagentExecutor.cjs.map +1 -1
  58. package/dist/cjs/utils/index.cjs +1 -0
  59. package/dist/cjs/utils/misc.cjs +12 -0
  60. package/dist/cjs/utils/misc.cjs.map +1 -1
  61. package/dist/cjs/utils/proxy.cjs +63 -0
  62. package/dist/cjs/utils/proxy.cjs.map +1 -0
  63. package/dist/esm/graphs/Graph.mjs +245 -24
  64. package/dist/esm/graphs/Graph.mjs.map +1 -1
  65. package/dist/esm/instrumentation.mjs +1 -0
  66. package/dist/esm/instrumentation.mjs.map +1 -1
  67. package/dist/esm/langfuseSpanRegistry.mjs +6 -3
  68. package/dist/esm/langfuseSpanRegistry.mjs.map +1 -1
  69. package/dist/esm/llm/bedrock/index.mjs +13 -2
  70. package/dist/esm/llm/bedrock/index.mjs.map +1 -1
  71. package/dist/esm/llm/invoke.mjs +146 -36
  72. package/dist/esm/llm/invoke.mjs.map +1 -1
  73. package/dist/esm/llm/openai/index.mjs +2 -2
  74. package/dist/esm/llm/openai/index.mjs.map +1 -1
  75. package/dist/esm/llm/streamLimits.mjs +704 -0
  76. package/dist/esm/llm/streamLimits.mjs.map +1 -0
  77. package/dist/esm/main.mjs +4 -2
  78. package/dist/esm/run.mjs +6 -2
  79. package/dist/esm/run.mjs.map +1 -1
  80. package/dist/esm/session/AgentSession.mjs +4 -1
  81. package/dist/esm/session/AgentSession.mjs.map +1 -1
  82. package/dist/esm/stream.mjs +103 -12
  83. package/dist/esm/stream.mjs.map +1 -1
  84. package/dist/esm/summarization/node.mjs +157 -37
  85. package/dist/esm/summarization/node.mjs.map +1 -1
  86. package/dist/esm/tools/BashExecutor.mjs +3 -2
  87. package/dist/esm/tools/BashExecutor.mjs.map +1 -1
  88. package/dist/esm/tools/CodeExecutor.mjs +4 -3
  89. package/dist/esm/tools/CodeExecutor.mjs.map +1 -1
  90. package/dist/esm/tools/ProgrammaticToolCalling.mjs +5 -3
  91. package/dist/esm/tools/ProgrammaticToolCalling.mjs.map +1 -1
  92. package/dist/esm/tools/ToolNode.mjs +76 -3
  93. package/dist/esm/tools/ToolNode.mjs.map +1 -1
  94. package/dist/esm/tools/ToolSearch.mjs +3 -2
  95. package/dist/esm/tools/ToolSearch.mjs.map +1 -1
  96. package/dist/esm/tools/search/crw-scraper.mjs +7 -1
  97. package/dist/esm/tools/search/crw-scraper.mjs.map +1 -1
  98. package/dist/esm/tools/search/crw-search.mjs +3 -1
  99. package/dist/esm/tools/search/crw-search.mjs.map +1 -1
  100. package/dist/esm/tools/search/firecrawl.mjs +7 -1
  101. package/dist/esm/tools/search/firecrawl.mjs.map +1 -1
  102. package/dist/esm/tools/search/keenable-scraper.mjs +7 -1
  103. package/dist/esm/tools/search/keenable-scraper.mjs.map +1 -1
  104. package/dist/esm/tools/search/keenable-search.mjs +3 -1
  105. package/dist/esm/tools/search/keenable-search.mjs.map +1 -1
  106. package/dist/esm/tools/search/rerankers.mjs +26 -8
  107. package/dist/esm/tools/search/rerankers.mjs.map +1 -1
  108. package/dist/esm/tools/search/search.mjs +30 -10
  109. package/dist/esm/tools/search/search.mjs.map +1 -1
  110. package/dist/esm/tools/search/serper-scraper.mjs +7 -1
  111. package/dist/esm/tools/search/serper-scraper.mjs.map +1 -1
  112. package/dist/esm/tools/search/tavily-scraper.mjs +7 -1
  113. package/dist/esm/tools/search/tavily-scraper.mjs.map +1 -1
  114. package/dist/esm/tools/search/tavily-search.mjs +3 -1
  115. package/dist/esm/tools/search/tavily-search.mjs.map +1 -1
  116. package/dist/esm/tools/search/tool.mjs +16 -2
  117. package/dist/esm/tools/search/tool.mjs.map +1 -1
  118. package/dist/esm/tools/subagent/SubagentExecutor.mjs +54 -3
  119. package/dist/esm/tools/subagent/SubagentExecutor.mjs.map +1 -1
  120. package/dist/esm/utils/index.mjs +1 -0
  121. package/dist/esm/utils/misc.mjs +12 -1
  122. package/dist/esm/utils/misc.mjs.map +1 -1
  123. package/dist/esm/utils/proxy.mjs +62 -0
  124. package/dist/esm/utils/proxy.mjs.map +1 -0
  125. package/dist/types/graphs/Graph.d.ts +53 -1
  126. package/dist/types/index.d.ts +2 -0
  127. package/dist/types/llm/invoke.d.ts +25 -4
  128. package/dist/types/llm/openai/index.d.ts +3 -0
  129. package/dist/types/llm/streamLimits.d.ts +314 -0
  130. package/dist/types/run.d.ts +1 -0
  131. package/dist/types/summarization/node.d.ts +27 -2
  132. package/dist/types/tools/BashExecutor.d.ts +2 -2
  133. package/dist/types/tools/CodeExecutor.d.ts +3 -3
  134. package/dist/types/tools/ToolNode.d.ts +11 -1
  135. package/dist/types/tools/search/crw-scraper.d.ts +2 -0
  136. package/dist/types/tools/search/firecrawl.d.ts +2 -0
  137. package/dist/types/tools/search/keenable-scraper.d.ts +2 -0
  138. package/dist/types/tools/search/rerankers.d.ts +9 -5
  139. package/dist/types/tools/search/serper-scraper.d.ts +2 -0
  140. package/dist/types/tools/search/tavily-scraper.d.ts +2 -0
  141. package/dist/types/tools/search/types.d.ts +28 -33
  142. package/dist/types/tools/subagent/SubagentExecutor.d.ts +44 -0
  143. package/dist/types/types/graph.d.ts +12 -1
  144. package/dist/types/types/run.d.ts +46 -1
  145. package/dist/types/types/tools.d.ts +21 -0
  146. package/dist/types/utils/index.d.ts +1 -0
  147. package/dist/types/utils/misc.d.ts +7 -0
  148. package/dist/types/utils/proxy.d.ts +31 -0
  149. package/package.json +2 -1
  150. package/src/__tests__/stream.eagerArgsDivergence.test.ts +158 -0
  151. package/src/__tests__/stream.eagerEventExecution.test.ts +1 -0
  152. package/src/__tests__/stream.streamLimits.test.ts +1982 -0
  153. package/src/graphs/Graph.ts +302 -27
  154. package/src/graphs/__tests__/Graph.breakerLifecycle.test.ts +234 -0
  155. package/src/index.ts +11 -0
  156. package/src/instrumentation.ts +1 -0
  157. package/src/langfuseSpanRegistry.ts +9 -0
  158. package/src/llm/bedrock/index.ts +22 -2
  159. package/src/llm/invoke.streamLimits.test.ts +142 -0
  160. package/src/llm/invoke.test.ts +89 -1
  161. package/src/llm/invoke.ts +197 -20
  162. package/src/llm/openai/cacheWriteTokens.test.ts +112 -0
  163. package/src/llm/openai/index.ts +14 -6
  164. package/src/llm/streamLimits.test.ts +450 -0
  165. package/src/llm/streamLimits.ts +1158 -0
  166. package/src/run.ts +4 -0
  167. package/src/session/AgentSession.ts +5 -0
  168. package/src/specs/langfuse-instrumentation.test.ts +26 -0
  169. package/src/specs/langfuse-span-registry.test.ts +17 -0
  170. package/src/specs/summarization.test.ts +5 -2
  171. package/src/stream.ts +141 -6
  172. package/src/summarization/__tests__/node.test.ts +269 -0
  173. package/src/summarization/chunkHandler.test.ts +196 -0
  174. package/src/summarization/node.ts +203 -6
  175. package/src/tools/BashExecutor.ts +4 -3
  176. package/src/tools/CodeExecutor.ts +5 -4
  177. package/src/tools/ProgrammaticToolCalling.ts +7 -5
  178. package/src/tools/ToolNode.ts +109 -6
  179. package/src/tools/ToolSearch.ts +4 -3
  180. package/src/tools/__tests__/BashExecutor.test.ts +2 -2
  181. package/src/tools/__tests__/ProgrammaticToolCalling.test.ts +2 -4
  182. package/src/tools/__tests__/SubagentExecutor.test.ts +126 -0
  183. package/src/tools/__tests__/ToolNode.breakerSignal.test.ts +389 -0
  184. package/src/tools/__tests__/ToolNode.streamLimits.test.ts +69 -0
  185. package/src/tools/search/crw-scraper.ts +6 -0
  186. package/src/tools/search/crw-search.ts +6 -1
  187. package/src/tools/search/firecrawl.ts +6 -0
  188. package/src/tools/search/http-agent.test.ts +133 -0
  189. package/src/tools/search/keenable-scraper.ts +6 -0
  190. package/src/tools/search/keenable-search.ts +6 -1
  191. package/src/tools/search/rerankers.ts +36 -10
  192. package/src/tools/search/search.ts +29 -11
  193. package/src/tools/search/serper-scraper.ts +6 -0
  194. package/src/tools/search/tavily-scraper.ts +6 -0
  195. package/src/tools/search/tavily-search.ts +2 -0
  196. package/src/tools/search/tool.ts +16 -0
  197. package/src/tools/search/types.ts +31 -33
  198. package/src/tools/subagent/SubagentExecutor.ts +96 -3
  199. package/src/types/graph.ts +12 -0
  200. package/src/types/run.ts +49 -1
  201. package/src/types/tools.ts +21 -0
  202. package/src/utils/index.ts +1 -0
  203. package/src/utils/misc.ts +19 -0
  204. package/src/utils/proxy.test.ts +176 -0
  205. package/src/utils/proxy.ts +93 -0
@@ -1,3 +1,4 @@
1
+ const require_misc = require("../utils/misc.cjs");
1
2
  const require_langfuseConfig = require("../langfuseConfig.cjs");
2
3
  const require_langfuseRuntimeScope = require("../langfuseRuntimeScope.cjs");
3
4
  const require_langfuse = require("../langfuse.cjs");
@@ -27,6 +28,7 @@ const require_reducer = require("../messages/reducer.cjs");
27
28
  const require_recency = require("../messages/recency.cjs");
28
29
  require("../messages/index.cjs");
29
30
  const require_handlers = require("../tools/handlers.cjs");
31
+ const require_streamLimits = require("../llm/streamLimits.cjs");
30
32
  const require_toolOutputReferences = require("../tools/toolOutputReferences.cjs");
31
33
  const require_llm = require("../utils/llm.cjs");
32
34
  const require_toolCache = require("../llm/bedrock/toolCache.cjs");
@@ -68,17 +70,6 @@ const EMPTY_PREEMPT_BOUNDARY = {
68
70
  messages: [],
69
71
  preventContinuation: false
70
72
  };
71
- /**
72
- * One signal that fires when either input fires. `AbortSignal.any` is skipped
73
- * when the inputs collapse to a single signal — the composite is a fresh
74
- * object per call, and the common cases (one channel, or the host reusing the
75
- * same controller for both) don't need one.
76
- */
77
- function composeAbortSignals(a, b) {
78
- if (a == null || a === b) return b;
79
- if (b == null) return a;
80
- return AbortSignal.any([a, b]);
81
- }
82
73
  /** Minimum relative variance before calibrated toolSchemaTokens overrides current value. */
83
74
  const CALIBRATION_VARIANCE_THRESHOLD = .15;
84
75
  function createToolHandlerRegistry(source) {
@@ -575,6 +566,62 @@ var StandardGraph = class StandardGraph extends Graph {
575
566
  /** See {@link t.StandardGraphInput.preemption}. */
576
567
  preemption;
577
568
  /**
569
+ * Stream circuit breakers, resolved once from
570
+ * {@link t.StandardGraphInput.streamLimits}. The stream handler enforces
571
+ * these on every streamed chunk event.
572
+ */
573
+ streamLimits;
574
+ /**
575
+ * Cumulative streamed argument bytes per in-flight tool call, keyed by
576
+ * generation key + chunk index (see `resolveGenerationKey`). Per-run
577
+ * accumulation state, cleared by both reset paths.
578
+ */
579
+ streamedToolCallArgTallies = /* @__PURE__ */ new Map();
580
+ /** Streamed chunk events per model generation, keyed by generation key. */
581
+ streamDeltaEventCounts = /* @__PURE__ */ new Map();
582
+ /** Per-chunk-object, per-generation charge balances (lazily created; see
583
+ * `StreamLimitState`). Reinitialized by both reset paths: a model may
584
+ * retain and re-yield one mutable chunk object, whose nested map would
585
+ * otherwise grow by one attempt-stamped entry per model call for the
586
+ * graph's lifetime. */
587
+ streamLimitChargeCredits;
588
+ /** Run-scoped breaker abort: composed into every model invocation and
589
+ * every SubagentExecutor child signal, and tripped when a stream circuit
590
+ * breaker fires anywhere in the run, so parallel agent nodes' in-flight
591
+ * provider calls and subagents stop consuming quota while the rejection
592
+ * propagates. Recreated by both reset paths — the abort is one-way within
593
+ * a run, and a reused graph must start its next run unaborted. */
594
+ breakerAbort = new AbortController();
595
+ /** Incremented whenever `breakerAbort` is replaced. Stamped into each
596
+ * model attempt's metadata ({@link STREAM_LIMIT_EPOCH_KEY}) so the stream
597
+ * handler's consumer-side trip binds to the run that produced the event
598
+ * rather than whichever controller is live when a straggling chunk is
599
+ * finally handled. */
600
+ breakerEpoch = 0;
601
+ /** Immutable snapshot of the run's breaker identity (epoch + controller),
602
+ * replaced as ONE object whenever `resetValues` installs a fresh
603
+ * controller. Sites that pause across awaits capture it at entry and
604
+ * revalidate by REFERENCE afterwards — a single identity comparison
605
+ * proves no reset happened while suspended, where separate epoch and
606
+ * controller reads could interleave with one. */
607
+ runScope = Object.freeze({
608
+ epoch: 0,
609
+ controller: this.breakerAbort
610
+ });
611
+ /** Generation keys of model attempts still in flight (see
612
+ * `StreamLimitState.activeStreamLimitGenerations`). Lazily created by the
613
+ * attempt lease; spans resets on purpose. */
614
+ activeStreamLimitGenerations;
615
+ /** The stream-limit error behind an already-fired breaker, whether this
616
+ * graph's own controller tripped or a parent run's breaker arrived through
617
+ * the composed constructor signal (child graphs own separate controllers).
618
+ * Providers can translate either abort into a generic error, and recovery
619
+ * paths must not run in that state. */
620
+ resolveTrippedBreakerReason(breakerSignal = this.breakerAbort.signal) {
621
+ if (breakerSignal.aborted && breakerSignal.reason instanceof require_streamLimits.StreamLimitExceededError) return breakerSignal.reason;
622
+ if (this.signal?.aborted === true && this.signal.reason instanceof require_streamLimits.StreamLimitExceededError) return this.signal.reason;
623
+ }
624
+ /**
578
625
  * Seals charged against `preemption.maxSeals`. Per-turn: cleared by both
579
626
  * reset paths so a fresh turn gets a fresh budget, while a HITL resume —
580
627
  * which skips `resetValues` — keeps what it had left.
@@ -619,7 +666,7 @@ var StandardGraph = class StandardGraph extends Graph {
619
666
  * another's turn.
620
667
  */
621
668
  pendingPreemptReturn = /* @__PURE__ */ new Set();
622
- constructor({ runId, signal, agents, langfuse, tokenCounter, indexTokenCountMap, calibrationRatio, subagentUsageSink, subagentScope, preemption }) {
669
+ constructor({ runId, signal, agents, langfuse, tokenCounter, indexTokenCountMap, calibrationRatio, subagentUsageSink, subagentScope, preemption, streamLimits }) {
623
670
  super();
624
671
  this.runId = runId;
625
672
  this.langfuseScopeRunId = `${runId ?? "graph"}:${(0, nanoid.nanoid)()}`;
@@ -628,6 +675,7 @@ var StandardGraph = class StandardGraph extends Graph {
628
675
  this.subagentUsageSink = subagentUsageSink;
629
676
  this.subagentScope = subagentScope === true;
630
677
  this.preemption = preemption;
678
+ this.streamLimits = require_streamLimits.resolveStreamLimits(streamLimits);
631
679
  if (agents.length === 0) throw new Error("At least one agent configuration is required");
632
680
  for (const agentConfig of agents) {
633
681
  const agentContext = require_AgentContext.AgentContext.fromConfig(agentConfig, tokenCounter, indexTokenCountMap);
@@ -658,6 +706,28 @@ var StandardGraph = class StandardGraph extends Graph {
658
706
  this.clearEagerEventToolUsageCounts();
659
707
  this.eagerEventToolCallChunks.clear();
660
708
  this.eagerEventToolSuppressions.clear();
709
+ /** Grace sweep instead of a clear: producer loops of straggling
710
+ * attempts use these maps directly and sit outside the consumer-only
711
+ * epoch gate — clearing would hand a cancellation-ignoring provider a
712
+ * fresh allowance at every run start. Entries from the epoch that is
713
+ * ending survive exactly one reset so those stragglers stay on their
714
+ * original budgets; older entries are removed. */
715
+ require_streamLimits.sweepStaleStreamLimitEntries(this.streamedToolCallArgTallies, this.breakerEpoch, this.activeStreamLimitGenerations);
716
+ require_streamLimits.sweepStaleStreamLimitEntries(this.streamDeltaEventCounts, this.breakerEpoch, this.activeStreamLimitGenerations);
717
+ this.streamLimitChargeCredits = void 0;
718
+ /** Run-start is the only safe replacement point for the breaker:
719
+ * end-of-run cleanup must leave it in place so straggling parallel
720
+ * children from the failed run cannot start on a fresh signal.
721
+ * Replaced UNCONDITIONALLY here — a run that failed on an ordinary
722
+ * error leaves the controller un-aborted, and stragglers still settling
723
+ * hold their entry-time capture of it; a late stream-limit trip on that
724
+ * old controller must not cancel the run starting now. */
725
+ this.breakerAbort = new AbortController();
726
+ this.breakerEpoch += 1;
727
+ this.runScope = Object.freeze({
728
+ epoch: this.breakerEpoch,
729
+ controller: this.breakerAbort
730
+ });
661
731
  this.handlerDispatchedStepIds = require_graph.resetIfNotEmpty(this.handlerDispatchedStepIds, /* @__PURE__ */ new Set());
662
732
  this.handlerDispatchedEventCounts = require_graph.resetIfNotEmpty(this.handlerDispatchedEventCounts, /* @__PURE__ */ new Map());
663
733
  this.messageIdsByStepKey = require_graph.resetIfNotEmpty(this.messageIdsByStepKey, /* @__PURE__ */ new Map());
@@ -679,6 +749,21 @@ var StandardGraph = class StandardGraph extends Graph {
679
749
  super.clearHeavyState();
680
750
  this.messages = [];
681
751
  this.overrideModel = void 0;
752
+ /** Stream-limit accounting (argument tallies, event counts, charge
753
+ * credits) deliberately SURVIVES cleanup: this runs in `processStream`'s
754
+ * finally, which an ordinary parallel-branch failure reaches while
755
+ * sibling attempts are still unwinding on the retained breaker — a
756
+ * cancellation-ignoring provider's late chunks would otherwise recreate
757
+ * their budgets from zero and stream another full allowance. The maps
758
+ * are bounded by in-flight call sizes and `resetValues` clears them at
759
+ * the next run start, where the epoch bump already drops stamped
760
+ * straggler events before accounting. */
761
+ /** Deliberately NOT recreating a tripped breakerAbort here: this runs in
762
+ * `processStream`'s cleanup, which a rejected parallel batch reaches
763
+ * while sibling subagents can still be pre-invoke — a fresh controller
764
+ * would hand them an un-aborted signal and let provider requests start
765
+ * after the run already failed. `resetValues` recreates it when the next
766
+ * run begins. */
682
767
  /**
683
768
  * Turn state only. The reported totals must outlive cleanup — this runs
684
769
  * in `processStream`'s `finally`, and the host reads `getPreemptStats()`
@@ -850,9 +935,13 @@ var StandardGraph = class StandardGraph extends Graph {
850
935
  return keyList.some((key) => key === void 0);
851
936
  }
852
937
  getRunMessages() {
853
- if (this.messages == null) return this.cachedRunMessages;
854
- if (this.messages.length === 0 && this.cachedRunMessages != null) return this.cachedRunMessages;
855
- return this.messages.slice(this.startIndex);
938
+ /** Runtime-honest widening: a disposed-but-cached graph (HITL
939
+ * resume/reconnect through a WeakRef cache) can carry null here despite
940
+ * the field type. */
941
+ const messages = this.messages;
942
+ if (messages == null) return this.cachedRunMessages;
943
+ if (messages.length === 0 && this.cachedRunMessages != null) return this.cachedRunMessages;
944
+ return messages.slice(this.startIndex);
856
945
  }
857
946
  getDiscoveredTools(agentId) {
858
947
  if (agentId != null) {
@@ -880,8 +969,9 @@ var StandardGraph = class StandardGraph extends Graph {
880
969
  return typeof id === "string" && id !== "" && this.runProducedAiMessageIds.has(id);
881
970
  }
882
971
  getContentParts() {
883
- if (this.messages == null) return;
884
- return require_core.convertMessagesToContent(this.messages.slice(this.startIndex));
972
+ const messages = this.messages;
973
+ if (messages == null) return;
974
+ return require_core.convertMessagesToContent(messages.slice(this.startIndex));
885
975
  }
886
976
  getCalibrationRatio() {
887
977
  return this.agentContexts.get(this.defaultAgentId)?.calibrationRatio ?? 1;
@@ -897,9 +987,10 @@ var StandardGraph = class StandardGraph extends Graph {
897
987
  * Get all run steps, optionally filtered by agent ID
898
988
  */
899
989
  getRunSteps(agentId) {
900
- if (this.contentData == null) return [];
901
- if (agentId == null || agentId === "") return [...this.contentData];
902
- return this.contentData.filter((step) => step.agentId === agentId);
990
+ const contentData = this.contentData;
991
+ if (contentData == null) return [];
992
+ if (agentId == null || agentId === "") return [...contentData];
993
+ return contentData.filter((step) => step.agentId === agentId);
903
994
  }
904
995
  /**
905
996
  * Get run steps grouped by agent ID
@@ -979,6 +1070,8 @@ var StandardGraph = class StandardGraph extends Graph {
979
1070
  maxToolResultChars: agentContext?.maxToolResultChars,
980
1071
  toolOutputRegistry: this.getOrCreateToolOutputRegistry(),
981
1072
  fileCheckpointer: this.getOrCreateFileCheckpointer(),
1073
+ getBreakerSignal: () => this.breakerAbort.signal,
1074
+ getRunScope: () => this.runScope,
982
1075
  errorHandler: (data, metadata) => StandardGraph.handleToolCallErrorStatic(this, data, metadata)
983
1076
  });
984
1077
  this.registerCompiledToolNode(node);
@@ -1006,7 +1099,9 @@ var StandardGraph = class StandardGraph extends Graph {
1006
1099
  maxContextTokens: agentContext?.maxContextTokens,
1007
1100
  maxToolResultChars: agentContext?.maxToolResultChars,
1008
1101
  toolOutputRegistry: this.getOrCreateToolOutputRegistry(),
1009
- fileCheckpointer: this.getOrCreateFileCheckpointer()
1102
+ fileCheckpointer: this.getOrCreateFileCheckpointer(),
1103
+ getBreakerSignal: () => this.breakerAbort.signal,
1104
+ getRunScope: () => this.runScope
1010
1105
  });
1011
1106
  this.registerCompiledToolNode(node);
1012
1107
  return node;
@@ -1075,6 +1170,21 @@ var StandardGraph = class StandardGraph extends Graph {
1075
1170
  }
1076
1171
  createCallModel(agentId = "default") {
1077
1172
  return async (state, config) => {
1173
+ /** Captured at node ENTRY, before any host-facing await (context-usage
1174
+ * dispatch, hooks): a sibling's trip can fail the run and a prompt
1175
+ * next run can reset the controller while this node is paused in one
1176
+ * of those awaits, and a later capture would bind this attempt to the
1177
+ * fresh controller. Trips and reason reads below stay on this
1178
+ * capture. */
1179
+ const attemptBreaker = this.breakerAbort;
1180
+ const attemptBreakerEpoch = this.breakerEpoch;
1181
+ /** Already-tripped-at-entry: a parallel sibling's breach has failed
1182
+ * the run before this node was scheduled. Rethrow before hooks or the
1183
+ * provider call — a custom provider that doesn't synchronously reject
1184
+ * an aborted signal would otherwise start another model request on a
1185
+ * failed run. */
1186
+ const entryTripReason = this.resolveTrippedBreakerReason(attemptBreaker.signal);
1187
+ if (entryTripReason != null) throw entryTripReason;
1078
1188
  const agentContext = this.agentContexts.get(agentId);
1079
1189
  if (!agentContext) throw new Error(`Agent context not found for agentId: ${agentId}`);
1080
1190
  if (!config) throw new Error("No config provided");
@@ -1670,6 +1780,10 @@ var StandardGraph = class StandardGraph extends Graph {
1670
1780
  let langfuseHandler;
1671
1781
  let invokeConfig = {
1672
1782
  ...config,
1783
+ /** The run-scoped breaker composed in, so a stream-limit trip in one
1784
+ * parallel agent node also cancels sibling nodes' in-flight model
1785
+ * calls, not only their subagents. */
1786
+ signal: require_misc.composeAbortSignals(config.signal, attemptBreaker.signal),
1673
1787
  metadata: {
1674
1788
  ...config.metadata ?? {},
1675
1789
  ...traceMetadata,
@@ -1677,7 +1791,8 @@ var StandardGraph = class StandardGraph extends Graph {
1677
1791
  * filtering: `createLangfuseTraceMetadata` drops values over its
1678
1792
  * length cap, but scope trust (`isForeignScope`) needs the id
1679
1793
  * verbatim regardless of length. */
1680
- agentId
1794
+ agentId,
1795
+ [require_streamLimits.STREAM_LIMIT_EPOCH_KEY]: attemptBreakerEpoch
1681
1796
  }
1682
1797
  };
1683
1798
  require_instrumentation.initializeLangfuseTracing(langfuse);
@@ -1700,6 +1815,14 @@ var StandardGraph = class StandardGraph extends Graph {
1700
1815
  const metadata = config.metadata;
1701
1816
  try {
1702
1817
  if (preInvokeContextOverflowError != null) throw preInvokeContextOverflowError;
1818
+ /** Rechecked after the pre-invoke awaits (context-usage dispatch,
1819
+ * hooks): a sibling can trip the breaker while this node is paused
1820
+ * in one of them, and a provider that doesn't synchronously reject
1821
+ * an aborted signal would still start the request. */
1822
+ {
1823
+ const preInvokeTrip = this.resolveTrippedBreakerReason(attemptBreaker.signal);
1824
+ if (preInvokeTrip != null) throw preInvokeTrip;
1825
+ }
1703
1826
  result = await require_langfuseRuntimeScope.withLangfuseRuntimeScope(require_langfuseRuntimeScope.resolveLangfuseRuntimeScope({
1704
1827
  runLangfuse: this.langfuse,
1705
1828
  langfuseOverlay: agentContext.langfuse,
@@ -1717,6 +1840,28 @@ var StandardGraph = class StandardGraph extends Graph {
1717
1840
  metadata
1718
1841
  });
1719
1842
  /**
1843
+ * A tripped stream circuit breaker is a deliberate abort, not a
1844
+ * provider failure: entering overflow recovery or the fallback chain
1845
+ * would spend more provider work after the safety limit fired, and a
1846
+ * succeeding fallback would resolve a run the public contract says
1847
+ * must reject. Rethrow before any recovery path.
1848
+ */
1849
+ if (primaryError instanceof require_streamLimits.StreamLimitExceededError) {
1850
+ /** Tripped before rethrowing so parallel agent nodes' in-flight
1851
+ * model calls and subagents stop while the rejection propagates. */
1852
+ attemptBreaker.abort(primaryError);
1853
+ throw primaryError;
1854
+ }
1855
+ /** A sibling that tripped the shared breaker aborts this branch's
1856
+ * composed signal, and some providers surface that as a generic
1857
+ * abort error; entering overflow planning or the fallback chain
1858
+ * would start new provider work after the run-wide breaker fired.
1859
+ * Rethrow the breaker's own stream-limit reason instead. */
1860
+ {
1861
+ const trippedReason = this.resolveTrippedBreakerReason(attemptBreaker.signal);
1862
+ if (trippedReason != null) throw trippedReason;
1863
+ }
1864
+ /**
1720
1865
  * A context overflow is a deterministic consequence of the payload,
1721
1866
  * not a provider being unavailable — so it is answered by compacting
1722
1867
  * and retrying rather than by re-sending the same oversized prompt
@@ -1848,6 +1993,18 @@ var StandardGraph = class StandardGraph extends Graph {
1848
1993
  }
1849
1994
  }));
1850
1995
  } catch (fallbackError) {
1996
+ if (fallbackError instanceof require_streamLimits.StreamLimitExceededError) {
1997
+ /** Same treatment as the primary path: a fallback stream that
1998
+ * trips the breaker must stop parallel agent nodes' model calls
1999
+ * and subagents before the rejection propagates. */
2000
+ attemptBreaker.abort(fallbackError);
2001
+ throw fallbackError;
2002
+ }
2003
+ {
2004
+ /** Same sibling-abort translation guard as the primary catch. */
2005
+ const trippedReason = this.resolveTrippedBreakerReason(attemptBreaker.signal);
2006
+ if (trippedReason != null) throw trippedReason;
2007
+ }
1851
2008
  const overflowCandidates = require_invoke.getFallbackOverflowCandidates(fallbackError);
1852
2009
  let fallbackRecovery = null;
1853
2010
  let fallbackRecoveryPromptEstimate;
@@ -2083,7 +2240,7 @@ var StandardGraph = class StandardGraph extends Graph {
2083
2240
  * construction signal. When both exist they may be different
2084
2241
  * controllers, and a drain must stop when EITHER fires.
2085
2242
  */
2086
- signal: composeAbortSignals(this.signal, this.callerSignal)
2243
+ signal: require_misc.composeAbortSignals(this.signal, this.callerSignal)
2087
2244
  }).catch(() => void 0);
2088
2245
  if (result == null) return EMPTY_PREEMPT_BOUNDARY;
2089
2246
  /**
@@ -2166,6 +2323,7 @@ var StandardGraph = class StandardGraph extends Graph {
2166
2323
  const executor = new require_SubagentExecutor.SubagentExecutor({
2167
2324
  configs: new Map(resolvedConfigs.map((c) => [c.type, c])),
2168
2325
  parentSignal: this.signal,
2326
+ breakerScope: { controller: () => this.breakerAbort },
2169
2327
  hookRegistry: this.hookRegistry,
2170
2328
  /** Lazy — Run wires the registry onto the graph AFTER
2171
2329
  * `createWorkflow()` runs, so a direct capture here would be
@@ -2176,6 +2334,7 @@ var StandardGraph = class StandardGraph extends Graph {
2176
2334
  langfuse: this.langfuse,
2177
2335
  tokenCounter: agentContext.tokenCounter,
2178
2336
  usageSink: this.subagentUsageSink,
2337
+ streamLimits: this.streamLimits,
2179
2338
  maxDepth: effectiveSubagentDepth,
2180
2339
  createChildGraph: (input) => {
2181
2340
  const childGraph = new StandardGraph(input);
@@ -2215,11 +2374,17 @@ var StandardGraph = class StandardGraph extends Graph {
2215
2374
  */
2216
2375
  const toolCall = config.toolCall;
2217
2376
  const parentToolCallId = typeof toolCall?.id === "string" ? toolCall.id : void 0;
2377
+ /** The parent tool batch's entry-captured scope (stamped by
2378
+ * ToolNode before PreToolUse hooks) — binds this child to the
2379
+ * run that dispatched it, not to whatever controller a reset
2380
+ * installed while the hooks were awaited. */
2381
+ const batchScope = config.configurable?.[require_streamLimits.RUN_BREAKER_SCOPE_CONFIG_KEY];
2218
2382
  return (await executor.execute({
2219
2383
  description,
2220
2384
  subagentType,
2221
2385
  threadId,
2222
2386
  parentToolCallId,
2387
+ breaker: batchScope?.controller,
2223
2388
  /**
2224
2389
  * Forward the parent's `configurable` so host-set fields
2225
2390
  * (`requestBody`, `user`, etc.) propagate into the child
@@ -2262,7 +2427,7 @@ var StandardGraph = class StandardGraph extends Graph {
2262
2427
  if (state.summarizationRequest != null) return summarizeNode;
2263
2428
  return require_ToolNode.toolsCondition(state, toolNode, this.invokedToolIds);
2264
2429
  };
2265
- return new _langchain_langgraph.StateGraph(_langchain_langgraph.Annotation.Root({
2430
+ const StateAnnotation = _langchain_langgraph.Annotation.Root({
2266
2431
  messages: (0, _langchain_langgraph.Annotation)({
2267
2432
  reducer: require_reducer.messagesStateReducer,
2268
2433
  default: () => []
@@ -2271,7 +2436,17 @@ var StandardGraph = class StandardGraph extends Graph {
2271
2436
  reducer: (_, b) => b,
2272
2437
  default: () => void 0
2273
2438
  })
2274
- })).addNode(agentNode, this.createCallModel(agentId)).addNode(toolNode, this.initializeTools({
2439
+ });
2440
+ const readChargeCredits = () => this.streamLimitChargeCredits;
2441
+ const readBreakerEpoch = () => this.breakerEpoch;
2442
+ const readActiveGenerations = () => this.activeStreamLimitGenerations;
2443
+ const writeActiveGenerations = (value) => {
2444
+ this.activeStreamLimitGenerations = value;
2445
+ };
2446
+ const writeChargeCredits = (value) => {
2447
+ this.streamLimitChargeCredits = value;
2448
+ };
2449
+ return new _langchain_langgraph.StateGraph(StateAnnotation).addNode(agentNode, this.createCallModel(agentId)).addNode(toolNode, this.initializeTools({
2275
2450
  currentTools: agentContext.tools,
2276
2451
  currentToolMap: agentContext.toolMap,
2277
2452
  agentContext
@@ -2286,6 +2461,52 @@ var StandardGraph = class StandardGraph extends Graph {
2286
2461
  runId: this.runId,
2287
2462
  isMultiAgent: this.isMultiAgentGraph(),
2288
2463
  hookRegistry: this.hookRegistry,
2464
+ /**
2465
+ * Live references (both maps are cleared in place, never
2466
+ * replaced), so summarization streams share the run's event
2467
+ * budget accounting under their own generation key.
2468
+ */
2469
+ streamLimits: this.streamLimits,
2470
+ streamDeltaEventCounts: this.streamDeltaEventCounts,
2471
+ streamedToolCallArgTallies: this.streamedToolCallArgTallies,
2472
+ /** Accessor pair, not a value copy: unlike the two maps above,
2473
+ * the credit map is REPLACED by graph resets rather than cleared
2474
+ * in place, and the guards' lazy `??=` must install onto the
2475
+ * graph — a copy held here would survive resets and grow one
2476
+ * attempt-stamped entry per compaction for a retained reused
2477
+ * chunk object. */
2478
+ get streamLimitChargeCredits() {
2479
+ return readChargeCredits();
2480
+ },
2481
+ set streamLimitChargeCredits(value) {
2482
+ writeChargeCredits(value);
2483
+ },
2484
+ /** Live epoch, so summary tallies are creation-tagged and the
2485
+ * resetValues grace sweep treats them like model-attempt
2486
+ * entries. */
2487
+ get breakerEpoch() {
2488
+ return readBreakerEpoch();
2489
+ },
2490
+ /** Accessor pair like the charge credits: summary attempts
2491
+ * lease their generations on the GRAPH's active set, and the
2492
+ * lazy `??=` in the lease helper must install there. */
2493
+ get activeStreamLimitGenerations() {
2494
+ return readActiveGenerations();
2495
+ },
2496
+ set activeStreamLimitGenerations(value) {
2497
+ writeActiveGenerations(value);
2498
+ },
2499
+ /** Read per attempt: a sibling branch tripping the run breaker
2500
+ * must also cancel in-flight summarization model calls. */
2501
+ getBreakerSignal: () => this.breakerAbort.signal,
2502
+ /** The node captures this at entry so its own chunk handler's
2503
+ * breach trips the run that STARTED the summarization, not a
2504
+ * controller installed by a later reset. */
2505
+ getBreakerController: () => this.breakerAbort,
2506
+ /** Captured at node entry and stamped into the summary attempt
2507
+ * metadata, so the wire consumer epoch-gates old-run summary
2508
+ * chunks exactly like model-attempt chunks. */
2509
+ getBreakerEpoch: () => this.breakerEpoch,
2289
2510
  dispatchRunStep: async (runStep, nodeConfig) => {
2290
2511
  const resolvedConfig = nodeConfig ?? this.config;
2291
2512
  if (runStep.agentId != null) {