@librechat/agents 3.8.0 → 3.8.2

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 (248) hide show
  1. package/dist/cjs/agents/AgentContext.cjs +13 -3
  2. package/dist/cjs/agents/AgentContext.cjs.map +1 -1
  3. package/dist/cjs/graphs/Graph.cjs +176 -55
  4. package/dist/cjs/graphs/Graph.cjs.map +1 -1
  5. package/dist/cjs/graphs/MultiAgentGraph.cjs +16 -6
  6. package/dist/cjs/graphs/MultiAgentGraph.cjs.map +1 -1
  7. package/dist/cjs/hitl/approvalReview.cjs +89 -0
  8. package/dist/cjs/hitl/approvalReview.cjs.map +1 -0
  9. package/dist/cjs/langfuse.cjs +42 -0
  10. package/dist/cjs/langfuse.cjs.map +1 -1
  11. package/dist/cjs/langfuseToolOutputTracing.cjs +42 -0
  12. package/dist/cjs/langfuseToolOutputTracing.cjs.map +1 -1
  13. package/dist/cjs/langfuseTraceShaping.cjs +7 -1
  14. package/dist/cjs/langfuseTraceShaping.cjs.map +1 -1
  15. package/dist/cjs/llm/anthropic/utils/message_inputs.cjs +6 -10
  16. package/dist/cjs/llm/anthropic/utils/message_inputs.cjs.map +1 -1
  17. package/dist/cjs/llm/bedrock/utils/message_inputs.cjs +2 -7
  18. package/dist/cjs/llm/bedrock/utils/message_inputs.cjs.map +1 -1
  19. package/dist/cjs/llm/contextPressureMeter.cjs +51 -10
  20. package/dist/cjs/llm/contextPressureMeter.cjs.map +1 -1
  21. package/dist/cjs/llm/init.cjs +1 -1
  22. package/dist/cjs/llm/invoke.cjs +2 -2
  23. package/dist/cjs/llm/openai/index.cjs +4 -3
  24. package/dist/cjs/llm/openai/index.cjs.map +1 -1
  25. package/dist/cjs/llm/openai/utils/index.cjs +5 -4
  26. package/dist/cjs/llm/openai/utils/index.cjs.map +1 -1
  27. package/dist/cjs/llm/preempt.cjs +3 -2
  28. package/dist/cjs/llm/preempt.cjs.map +1 -1
  29. package/dist/cjs/llm/prepareProviderRequest.cjs +9 -7
  30. package/dist/cjs/llm/prepareProviderRequest.cjs.map +1 -1
  31. package/dist/cjs/llm/providers.cjs +1 -1
  32. package/dist/cjs/main.cjs +14 -8
  33. package/dist/cjs/messages/alternation.cjs +1 -5
  34. package/dist/cjs/messages/alternation.cjs.map +1 -1
  35. package/dist/cjs/messages/budget.cjs +206 -7
  36. package/dist/cjs/messages/budget.cjs.map +1 -1
  37. package/dist/cjs/messages/cache.cjs +3 -9
  38. package/dist/cjs/messages/cache.cjs.map +1 -1
  39. package/dist/cjs/messages/core.cjs +26 -38
  40. package/dist/cjs/messages/core.cjs.map +1 -1
  41. package/dist/cjs/messages/format.cjs +132 -43
  42. package/dist/cjs/messages/format.cjs.map +1 -1
  43. package/dist/cjs/messages/index.cjs +2 -0
  44. package/dist/cjs/messages/prune.cjs +14 -9
  45. package/dist/cjs/messages/prune.cjs.map +1 -1
  46. package/dist/cjs/messages/reasoningTypes.cjs +21 -0
  47. package/dist/cjs/messages/reasoningTypes.cjs.map +1 -0
  48. package/dist/cjs/messages/recency.cjs +15 -94
  49. package/dist/cjs/messages/recency.cjs.map +1 -1
  50. package/dist/cjs/messages/toolHistoryProjection.cjs +243 -0
  51. package/dist/cjs/messages/toolHistoryProjection.cjs.map +1 -0
  52. package/dist/cjs/messages/toolResultTypes.cjs +145 -4
  53. package/dist/cjs/messages/toolResultTypes.cjs.map +1 -1
  54. package/dist/cjs/run.cjs +22 -10
  55. package/dist/cjs/run.cjs.map +1 -1
  56. package/dist/cjs/session/AgentSession.cjs +7 -5
  57. package/dist/cjs/session/AgentSession.cjs.map +1 -1
  58. package/dist/cjs/session/JsonlSessionStore.cjs +3 -0
  59. package/dist/cjs/session/JsonlSessionStore.cjs.map +1 -1
  60. package/dist/cjs/session/index.cjs +1 -1
  61. package/dist/cjs/session/sessionProjection.cjs +75 -0
  62. package/dist/cjs/session/sessionProjection.cjs.map +1 -0
  63. package/dist/cjs/stream.cjs +20 -19
  64. package/dist/cjs/stream.cjs.map +1 -1
  65. package/dist/cjs/summarization/index.cjs.map +1 -1
  66. package/dist/cjs/summarization/node.cjs +19 -9
  67. package/dist/cjs/summarization/node.cjs.map +1 -1
  68. package/dist/cjs/summarization/shared.cjs +9 -0
  69. package/dist/cjs/summarization/shared.cjs.map +1 -1
  70. package/dist/cjs/tools/ToolNode.cjs +256 -84
  71. package/dist/cjs/tools/ToolNode.cjs.map +1 -1
  72. package/dist/cjs/tools/local/CompileCheckTool.cjs +1 -1
  73. package/dist/cjs/tools/local/LocalCodingTools.cjs +1 -1
  74. package/dist/cjs/tools/subagent/SubagentExecutor.cjs +24 -9
  75. package/dist/cjs/tools/subagent/SubagentExecutor.cjs.map +1 -1
  76. package/dist/cjs/tools/subagent/SubagentReplay.cjs +2 -0
  77. package/dist/cjs/tools/subagent/SubagentReplay.cjs.map +1 -1
  78. package/dist/cjs/tools/toolBatchReplay.cjs +187 -0
  79. package/dist/cjs/tools/toolBatchReplay.cjs.map +1 -0
  80. package/dist/cjs/tools/toolOutputReferences.cjs +12 -0
  81. package/dist/cjs/tools/toolOutputReferences.cjs.map +1 -1
  82. package/dist/cjs/types/hitl.cjs +4 -0
  83. package/dist/cjs/types/hitl.cjs.map +1 -1
  84. package/dist/cjs/utils/events.cjs +13 -0
  85. package/dist/cjs/utils/events.cjs.map +1 -1
  86. package/dist/cjs/utils/tokens.cjs +105 -0
  87. package/dist/cjs/utils/tokens.cjs.map +1 -1
  88. package/dist/esm/agents/AgentContext.mjs +13 -3
  89. package/dist/esm/agents/AgentContext.mjs.map +1 -1
  90. package/dist/esm/graphs/Graph.mjs +178 -57
  91. package/dist/esm/graphs/Graph.mjs.map +1 -1
  92. package/dist/esm/graphs/MultiAgentGraph.mjs +16 -6
  93. package/dist/esm/graphs/MultiAgentGraph.mjs.map +1 -1
  94. package/dist/esm/hitl/approvalReview.mjs +83 -0
  95. package/dist/esm/hitl/approvalReview.mjs.map +1 -0
  96. package/dist/esm/langfuse.mjs +43 -2
  97. package/dist/esm/langfuse.mjs.map +1 -1
  98. package/dist/esm/langfuseToolOutputTracing.mjs +41 -1
  99. package/dist/esm/langfuseToolOutputTracing.mjs.map +1 -1
  100. package/dist/esm/langfuseTraceShaping.mjs +7 -1
  101. package/dist/esm/langfuseTraceShaping.mjs.map +1 -1
  102. package/dist/esm/llm/anthropic/utils/message_inputs.mjs +6 -10
  103. package/dist/esm/llm/anthropic/utils/message_inputs.mjs.map +1 -1
  104. package/dist/esm/llm/bedrock/utils/message_inputs.mjs +2 -7
  105. package/dist/esm/llm/bedrock/utils/message_inputs.mjs.map +1 -1
  106. package/dist/esm/llm/contextPressureMeter.mjs +52 -11
  107. package/dist/esm/llm/contextPressureMeter.mjs.map +1 -1
  108. package/dist/esm/llm/init.mjs +1 -1
  109. package/dist/esm/llm/invoke.mjs +2 -2
  110. package/dist/esm/llm/openai/index.mjs +2 -1
  111. package/dist/esm/llm/openai/index.mjs.map +1 -1
  112. package/dist/esm/llm/openai/utils/index.mjs +5 -4
  113. package/dist/esm/llm/openai/utils/index.mjs.map +1 -1
  114. package/dist/esm/llm/preempt.mjs +2 -1
  115. package/dist/esm/llm/preempt.mjs.map +1 -1
  116. package/dist/esm/llm/prepareProviderRequest.mjs +9 -7
  117. package/dist/esm/llm/prepareProviderRequest.mjs.map +1 -1
  118. package/dist/esm/llm/providers.mjs +1 -1
  119. package/dist/esm/main.mjs +13 -10
  120. package/dist/esm/messages/alternation.mjs +2 -6
  121. package/dist/esm/messages/alternation.mjs.map +1 -1
  122. package/dist/esm/messages/budget.mjs +206 -8
  123. package/dist/esm/messages/budget.mjs.map +1 -1
  124. package/dist/esm/messages/cache.mjs +3 -9
  125. package/dist/esm/messages/cache.mjs.map +1 -1
  126. package/dist/esm/messages/core.mjs +24 -34
  127. package/dist/esm/messages/core.mjs.map +1 -1
  128. package/dist/esm/messages/format.mjs +133 -44
  129. package/dist/esm/messages/format.mjs.map +1 -1
  130. package/dist/esm/messages/index.mjs +2 -0
  131. package/dist/esm/messages/prune.mjs +14 -9
  132. package/dist/esm/messages/prune.mjs.map +1 -1
  133. package/dist/esm/messages/reasoningTypes.mjs +20 -0
  134. package/dist/esm/messages/reasoningTypes.mjs.map +1 -0
  135. package/dist/esm/messages/recency.mjs +16 -95
  136. package/dist/esm/messages/recency.mjs.map +1 -1
  137. package/dist/esm/messages/toolHistoryProjection.mjs +237 -0
  138. package/dist/esm/messages/toolHistoryProjection.mjs.map +1 -0
  139. package/dist/esm/messages/toolResultTypes.mjs +141 -4
  140. package/dist/esm/messages/toolResultTypes.mjs.map +1 -1
  141. package/dist/esm/run.mjs +23 -11
  142. package/dist/esm/run.mjs.map +1 -1
  143. package/dist/esm/session/AgentSession.mjs +7 -5
  144. package/dist/esm/session/AgentSession.mjs.map +1 -1
  145. package/dist/esm/session/JsonlSessionStore.mjs +3 -0
  146. package/dist/esm/session/JsonlSessionStore.mjs.map +1 -1
  147. package/dist/esm/session/index.mjs +1 -1
  148. package/dist/esm/session/sessionProjection.mjs +72 -0
  149. package/dist/esm/session/sessionProjection.mjs.map +1 -0
  150. package/dist/esm/stream.mjs +17 -16
  151. package/dist/esm/stream.mjs.map +1 -1
  152. package/dist/esm/summarization/index.mjs.map +1 -1
  153. package/dist/esm/summarization/node.mjs +20 -10
  154. package/dist/esm/summarization/node.mjs.map +1 -1
  155. package/dist/esm/summarization/shared.mjs +9 -1
  156. package/dist/esm/summarization/shared.mjs.map +1 -1
  157. package/dist/esm/tools/ToolNode.mjs +256 -84
  158. package/dist/esm/tools/ToolNode.mjs.map +1 -1
  159. package/dist/esm/tools/local/CompileCheckTool.mjs +1 -1
  160. package/dist/esm/tools/local/LocalCodingTools.mjs +1 -1
  161. package/dist/esm/tools/subagent/SubagentExecutor.mjs +24 -9
  162. package/dist/esm/tools/subagent/SubagentExecutor.mjs.map +1 -1
  163. package/dist/esm/tools/subagent/SubagentReplay.mjs +1 -1
  164. package/dist/esm/tools/subagent/SubagentReplay.mjs.map +1 -1
  165. package/dist/esm/tools/toolBatchReplay.mjs +176 -0
  166. package/dist/esm/tools/toolBatchReplay.mjs.map +1 -0
  167. package/dist/esm/tools/toolOutputReferences.mjs +12 -0
  168. package/dist/esm/tools/toolOutputReferences.mjs.map +1 -1
  169. package/dist/esm/types/hitl.mjs +4 -1
  170. package/dist/esm/types/hitl.mjs.map +1 -1
  171. package/dist/esm/utils/events.mjs +13 -0
  172. package/dist/esm/utils/events.mjs.map +1 -1
  173. package/dist/esm/utils/tokens.mjs +105 -1
  174. package/dist/esm/utils/tokens.mjs.map +1 -1
  175. package/dist/types/agents/AgentContext.d.ts +13 -1
  176. package/dist/types/graphs/Graph.d.ts +27 -0
  177. package/dist/types/hitl/approvalReview.d.ts +32 -0
  178. package/dist/types/hooks/types.d.ts +4 -2
  179. package/dist/types/index.d.ts +1 -0
  180. package/dist/types/langfuse.d.ts +5 -0
  181. package/dist/types/langfuseToolOutputTracing.d.ts +2 -0
  182. package/dist/types/llm/contextPressureMeter.d.ts +2 -1
  183. package/dist/types/llm/prepareProviderRequest.d.ts +7 -1
  184. package/dist/types/messages/budget.d.ts +15 -10
  185. package/dist/types/messages/core.d.ts +8 -17
  186. package/dist/types/messages/format.d.ts +3 -2
  187. package/dist/types/messages/index.d.ts +1 -1
  188. package/dist/types/messages/prune.d.ts +1 -1
  189. package/dist/types/messages/reasoningTypes.d.ts +7 -0
  190. package/dist/types/messages/recency.d.ts +3 -0
  191. package/dist/types/messages/toolHistoryProjection.d.ts +65 -0
  192. package/dist/types/messages/toolResultTypes.d.ts +31 -1
  193. package/dist/types/session/sessionProjection.d.ts +10 -0
  194. package/dist/types/summarization/index.d.ts +2 -1
  195. package/dist/types/summarization/node.d.ts +1 -0
  196. package/dist/types/summarization/shared.d.ts +12 -0
  197. package/dist/types/tools/ToolNode.d.ts +12 -6
  198. package/dist/types/tools/subagent/SubagentReplay.d.ts +2 -0
  199. package/dist/types/tools/toolBatchReplay.d.ts +54 -0
  200. package/dist/types/tools/toolOutputReferences.d.ts +2 -0
  201. package/dist/types/types/graph.d.ts +20 -0
  202. package/dist/types/types/run.d.ts +4 -0
  203. package/dist/types/types/summarize.d.ts +4 -1
  204. package/dist/types/utils/tokens.d.ts +2 -1
  205. package/package.json +1 -1
  206. package/src/agents/AgentContext.ts +34 -3
  207. package/src/graphs/Graph.ts +465 -147
  208. package/src/graphs/MultiAgentGraph.ts +28 -6
  209. package/src/hitl/approvalReview.ts +209 -0
  210. package/src/hooks/types.ts +4 -1
  211. package/src/index.ts +1 -0
  212. package/src/langfuse.ts +67 -9
  213. package/src/langfuseToolOutputTracing.ts +91 -0
  214. package/src/langfuseTraceShaping.ts +17 -4
  215. package/src/llm/anthropic/utils/message_inputs.ts +5 -10
  216. package/src/llm/bedrock/utils/message_inputs.ts +2 -8
  217. package/src/llm/contextPressureMeter.ts +80 -12
  218. package/src/llm/openai/utils/index.ts +5 -4
  219. package/src/llm/prepareProviderRequest.ts +21 -16
  220. package/src/messages/alternation.ts +2 -15
  221. package/src/messages/budget.ts +439 -23
  222. package/src/messages/cache.ts +8 -9
  223. package/src/messages/core.ts +65 -95
  224. package/src/messages/format.ts +262 -78
  225. package/src/messages/index.ts +1 -1
  226. package/src/messages/prune.ts +31 -12
  227. package/src/messages/reasoningTypes.ts +23 -0
  228. package/src/messages/recency.ts +35 -179
  229. package/src/messages/toolHistoryProjection.ts +460 -0
  230. package/src/messages/toolResultTypes.ts +331 -100
  231. package/src/run.ts +44 -22
  232. package/src/session/AgentSession.ts +33 -16
  233. package/src/session/JsonlSessionStore.ts +6 -0
  234. package/src/session/sessionProjection.ts +126 -0
  235. package/src/stream.ts +14 -19
  236. package/src/summarization/index.ts +2 -0
  237. package/src/summarization/node.ts +62 -13
  238. package/src/summarization/shared.ts +23 -0
  239. package/src/tools/ToolNode.ts +518 -184
  240. package/src/tools/subagent/SubagentExecutor.ts +35 -6
  241. package/src/tools/subagent/SubagentReplay.ts +2 -2
  242. package/src/tools/toolBatchReplay.ts +395 -0
  243. package/src/tools/toolOutputReferences.ts +16 -0
  244. package/src/types/graph.ts +20 -0
  245. package/src/types/run.ts +4 -0
  246. package/src/types/summarize.ts +4 -1
  247. package/src/utils/events.ts +19 -0
  248. package/src/utils/tokens.ts +183 -0
package/src/run.ts CHANGED
@@ -22,7 +22,6 @@ import type {
22
22
  } from '@langchain/core/messages';
23
23
  import type { StringPromptValue } from '@langchain/core/prompt_values';
24
24
  import type { RunnableConfig } from '@langchain/core/runnables';
25
- import { isFadingTier } from '@/messages/fading';
26
25
  import type { AggregatedHookResult, HookRegistry } from '@/hooks';
27
26
  import type { MultiAgentGraph } from '@/graphs/MultiAgentGraph';
28
27
  import type { StandardGraph } from '@/graphs/Graph';
@@ -40,7 +39,6 @@ import {
40
39
  } from '@/common';
41
40
  import {
42
41
  requireValidSubagentResumeManifest,
43
- stripSubagentResumeManifest,
44
42
  SUBAGENT_RESUME_ATTEMPT_CONFIG_KEY,
45
43
  SUBAGENT_RESUME_MANIFEST_CONFIG_KEY,
46
44
  } from '@/tools/subagent/SubagentReplay';
@@ -70,6 +68,16 @@ import {
70
68
  resolveLangfuseConfig,
71
69
  resolveToolOutputTracingConfig,
72
70
  } from '@/langfuseConfig';
71
+ import {
72
+ cloneToolApprovalInterruptPayload,
73
+ TOOL_APPROVAL_REVIEW_CONFIG_KEY,
74
+ } from '@/hitl/approvalReview';
75
+ import {
76
+ TOOL_BATCH_REPLAY_KEY,
77
+ restoreToolReplayConfig,
78
+ stripToolBatchReplayState,
79
+ getPublicToolInterruptPayload,
80
+ } from '@/tools/toolBatchReplay';
73
81
  import {
74
82
  resolveLangfuseDestinationKey,
75
83
  resolveLangfuseTraceAnchorParent,
@@ -104,6 +112,7 @@ import { seedRunInitialSessions } from '@/utils/toolSessions';
104
112
  import { getTraceIdSeed } from '@/langfuseRuntimeContext';
105
113
  import { resolveClientOptionsModel } from '@/llm/request';
106
114
  import { createGraph } from '@/graphs/createGraph';
115
+ import { isFadingTier } from '@/messages/fading';
107
116
  import { resolveMaxSeals } from '@/llm/preempt';
108
117
  import { isBuiltRuntime } from '@/lazyRequire';
109
118
  import { initializeModel } from '@/llm/init';
@@ -169,9 +178,7 @@ function materializeStopContinuation(
169
178
  return messages;
170
179
  }
171
180
 
172
- function advanceCheckpointCursor(
173
- config: t.RunStreamConfig
174
- ): t.RunStreamConfig {
181
+ function advanceCheckpointCursor(config: t.RunStreamConfig): t.RunStreamConfig {
175
182
  const configurable = { ...config.configurable };
176
183
  delete configurable.checkpoint_id;
177
184
  delete configurable.checkpoint_map;
@@ -186,9 +193,7 @@ function assertFinalAdmissionSucceeded(result: AggregatedHookResult): void {
186
193
  result.errors.length > 0
187
194
  ? result.errors.join('; ')
188
195
  : 'one or more internal finalizers failed';
189
- throw new Error(
190
- `StopFinalize terminal admission failed: ${detail}`
191
- );
196
+ throw new Error(`StopFinalize terminal admission failed: ${detail}`);
192
197
  }
193
198
 
194
199
  const CUSTOM_GRAPH_EVENTS = new Set<string>([
@@ -268,9 +273,7 @@ function isLangGraphResumeMapForInterrupt(
268
273
  }
269
274
 
270
275
  function getInterruptHookSessionId(payload: unknown): string | undefined {
271
- const publicPayload = stripSubagentResumeManifest(
272
- stripRunStepResumeState(payload)
273
- );
276
+ const publicPayload = getPublicToolInterruptPayload(payload);
274
277
  if (
275
278
  publicPayload == null ||
276
279
  typeof publicPayload !== 'object' ||
@@ -423,7 +426,7 @@ function overwriteResumeRunStepState(
423
426
  const update = Array.isArray(command.update)
424
427
  ? [
425
428
  ...command.update.filter(([key]) => key !== 'runStepState'),
426
- ['runStepState', overwrite] as [string, unknown],
429
+ ['runStepState', overwrite] as [string, unknown],
427
430
  ]
428
431
  : { ...(command.update ?? {}), runStepState: overwrite };
429
432
  const resumed = new Command({
@@ -1266,6 +1269,8 @@ export class Run<_T extends t.BaseGraphState> {
1266
1269
  recursionLimit,
1267
1270
  configurable: { ...callerConfig.configurable },
1268
1271
  };
1272
+ delete config.configurable?.[TOOL_APPROVAL_REVIEW_CONFIG_KEY];
1273
+ delete config.configurable?.[TOOL_BATCH_REPLAY_KEY];
1269
1274
  if (!isResume) {
1270
1275
  delete config.configurable?.[SUBAGENT_RESUME_ATTEMPT_CONFIG_KEY];
1271
1276
  delete config.configurable?.[SUBAGENT_RESUME_MANIFEST_CONFIG_KEY];
@@ -1276,6 +1281,10 @@ export class Run<_T extends t.BaseGraphState> {
1276
1281
  config,
1277
1282
  (inputs as Command).update
1278
1283
  );
1284
+ const publicPayload = stripRunStepResumeState(this._interrupt?.payload);
1285
+ if (config.configurable != null) {
1286
+ restoreToolReplayConfig(config.configurable, this._interrupt?.interruptId, publicPayload);
1287
+ }
1279
1288
  if (graph.getStopContinuationExecutionId() === '') {
1280
1289
  graph.startStopContinuationExecution(nanoid());
1281
1290
  overwriteLegacyResumeState = this.hasCheckpointer;
@@ -1514,7 +1523,7 @@ export class Run<_T extends t.BaseGraphState> {
1514
1523
  this._interrupt = {
1515
1524
  interruptId: first.id ?? '',
1516
1525
  threadId,
1517
- payload: first.value,
1526
+ payload: cloneToolApprovalInterruptPayload(first.value),
1518
1527
  };
1519
1528
  }
1520
1529
  }
@@ -1558,7 +1567,8 @@ export class Run<_T extends t.BaseGraphState> {
1558
1567
  stopReason = OUTPUT_TRUNCATED_HALT_REASON;
1559
1568
  }
1560
1569
 
1561
- const stopMessages = graph.getRunMessages() ?? stateInputs?.messages ?? [];
1570
+ const stopMessages =
1571
+ graph.getRunMessages() ?? stateInputs?.messages ?? [];
1562
1572
  const stopContinuationCount = graph.getStopContinuationCount();
1563
1573
  const continuationBudgetRemaining = Math.max(
1564
1574
  0,
@@ -1584,11 +1594,17 @@ export class Run<_T extends t.BaseGraphState> {
1584
1594
  }).catch((): undefined => undefined);
1585
1595
  }
1586
1596
 
1597
+ /** A summarize-only run is complete once its summary exists: there
1598
+ * is no assistant reply for a Stop hook to demand, and its one
1599
+ * summarization claim is spent, so it admits no continuation. */
1600
+ const continuationAdmissible = graph.summarizeOnlyAgentId == null;
1587
1601
  if (this.hookRegistry?.hasHookFor('StopFinalize', this.id) === true) {
1588
1602
  const stopInjected =
1589
1603
  stopResult == null ? [] : materializeStopContinuation(stopResult);
1590
1604
  const continuationPrevented =
1591
- stopReason != null || stopResult?.preventContinuation === true;
1605
+ !continuationAdmissible ||
1606
+ stopReason != null ||
1607
+ stopResult?.preventContinuation === true;
1592
1608
  const finalized = await executeHooks({
1593
1609
  registry: this.hookRegistry,
1594
1610
  input: {
@@ -1622,7 +1638,9 @@ export class Run<_T extends t.BaseGraphState> {
1622
1638
  }
1623
1639
 
1624
1640
  const requestsContinuation =
1625
- stopReason == null && stopResult?.stopDecision === 'block';
1641
+ continuationAdmissible &&
1642
+ stopReason == null &&
1643
+ stopResult?.stopDecision === 'block';
1626
1644
  if (requestsContinuation && stopResult != null) {
1627
1645
  const injected = materializeStopContinuation(stopResult);
1628
1646
  if (injected.length > 0) {
@@ -1873,8 +1891,8 @@ export class Run<_T extends t.BaseGraphState> {
1873
1891
  }
1874
1892
  return {
1875
1893
  ...this._interrupt,
1876
- payload: stripSubagentResumeManifest(
1877
- stripRunStepResumeState(this._interrupt.payload)
1894
+ payload: cloneToolApprovalInterruptPayload(
1895
+ getPublicToolInterruptPayload(this._interrupt.payload)
1878
1896
  ),
1879
1897
  } as t.RunInterruptResult<TPayload>;
1880
1898
  }
@@ -1996,16 +2014,20 @@ export class Run<_T extends t.BaseGraphState> {
1996
2014
  ): Promise<t.RunStreamConfig> {
1997
2015
  await this.restoreInterruptFromCheckpoint(callerConfig, resumeUpdate);
1998
2016
  const interrupt = this._interrupt;
1999
- const resumeManifest = requireValidSubagentResumeManifest(
2000
- stripRunStepResumeState(interrupt?.payload)
2001
- );
2017
+ const replayPayload = stripRunStepResumeState(interrupt?.payload);
2018
+ const resumeManifest = requireValidSubagentResumeManifest(replayPayload) ??
2019
+ requireValidSubagentResumeManifest(stripToolBatchReplayState(replayPayload));
2002
2020
  const resumeConfigurable = { ...callerConfig.configurable };
2021
+ delete resumeConfigurable[TOOL_APPROVAL_REVIEW_CONFIG_KEY];
2022
+ delete resumeConfigurable[TOOL_BATCH_REPLAY_KEY];
2003
2023
  delete resumeConfigurable[SUBAGENT_RESUME_ATTEMPT_CONFIG_KEY];
2004
2024
  delete resumeConfigurable[SUBAGENT_RESUME_MANIFEST_CONFIG_KEY];
2005
2025
  resumeConfigurable[SUBAGENT_RESUME_ATTEMPT_CONFIG_KEY] = nanoid();
2006
2026
  if (resumeManifest != null) {
2007
2027
  resumeConfigurable[SUBAGENT_RESUME_MANIFEST_CONFIG_KEY] = resumeManifest;
2008
2028
  }
2029
+ const publicPayload = stripRunStepResumeState(interrupt?.payload);
2030
+ restoreToolReplayConfig(resumeConfigurable, interrupt?.interruptId, publicPayload);
2009
2031
  const manifestConfig = {
2010
2032
  ...callerConfig,
2011
2033
  configurable: resumeConfigurable,
@@ -2122,7 +2144,7 @@ export class Run<_T extends t.BaseGraphState> {
2122
2144
  const threadId = callerConfig.configurable?.thread_id;
2123
2145
  this._interrupt = {
2124
2146
  interruptId: persistedInterrupt.id,
2125
- payload: persistedInterrupt.value,
2147
+ payload: cloneToolApprovalInterruptPayload(persistedInterrupt.value),
2126
2148
  ...(typeof threadId === 'string' ? { threadId } : {}),
2127
2149
  ...(typeof checkpointId === 'string' ? { checkpointId } : {}),
2128
2150
  ...(typeof checkpointNs === 'string' ? { checkpointNs } : {}),
@@ -21,9 +21,12 @@ import type {
21
21
  SessionEntry,
22
22
  SessionForkOptions,
23
23
  } from './types';
24
- import { isFadingTier } from '@/messages/fading';
25
24
  import type { HookRegistry } from '@/hooks';
26
25
  import type * as t from '@/types';
26
+ import {
27
+ deriveSessionMessages,
28
+ releaseSessionProjection,
29
+ } from './sessionProjection';
27
30
  import { createSummarizeNode } from '@/summarization/node';
28
31
  import { resolveStreamLimits } from '@/llm/streamLimits';
29
32
  import { JsonlSessionStore } from './JsonlSessionStore';
@@ -31,6 +34,7 @@ import { AgentContext } from '@/agents/AgentContext';
31
34
  import { ContentTypes, GraphEvents } from '@/common';
32
35
  import { createRunId, createSessionId } from './ids';
33
36
  import { deriveMessages } from './deriveMessages';
37
+ import { isFadingTier } from '@/messages/fading';
34
38
  import { createRunHandlers } from './handlers';
35
39
  import { Run } from '@/run';
36
40
 
@@ -377,7 +381,9 @@ function fadingGenerationKey(scopeKey: string, agentKey: string): string {
377
381
 
378
382
  function fadingGenerationScope(generationKey: string): string {
379
383
  const parsed: unknown = JSON.parse(generationKey);
380
- return Array.isArray(parsed) && typeof parsed[0] === 'string' ? parsed[0] : '';
384
+ return Array.isArray(parsed) && typeof parsed[0] === 'string'
385
+ ? parsed[0]
386
+ : '';
381
387
  }
382
388
 
383
389
  /** What a run saw when it started, so its capture can tell stale from current. */
@@ -404,10 +410,7 @@ function mergeFadingTier(
404
410
  if (incoming == null) {
405
411
  return { ...current };
406
412
  }
407
- const budgetTokens = Math.min(
408
- current.budgetTokens,
409
- incoming.budgetTokens
410
- );
413
+ const budgetTokens = Math.min(current.budgetTokens, incoming.budgetTokens);
411
414
  const masked = current.masked || incoming.masked;
412
415
  const latched =
413
416
  current.latched === true ||
@@ -1010,6 +1013,9 @@ export class AgentSession {
1010
1013
  }
1011
1014
 
1012
1015
  getSessionStore(): JsonlSessionStore | undefined {
1016
+ if (this.store) {
1017
+ releaseSessionProjection(this.store);
1018
+ }
1013
1019
  return this.store;
1014
1020
  }
1015
1021
 
@@ -1017,7 +1023,10 @@ export class AgentSession {
1017
1023
  return this.checkpointing.checkpointer;
1018
1024
  }
1019
1025
 
1020
- private getFadingState(threadId: string, checkpointNs = ''): {
1026
+ private getFadingState(
1027
+ threadId: string,
1028
+ checkpointNs = ''
1029
+ ): {
1021
1030
  fadingTier?: t.FadingTier;
1022
1031
  fadingTiers?: t.FadingTiers;
1023
1032
  } {
@@ -1058,8 +1067,7 @@ export class AgentSession {
1058
1067
 
1059
1068
  private trimAlternateFadingStates(): void {
1060
1069
  while (
1061
- this.alternateThreadFadingState.size >
1062
- MAX_ALTERNATE_THREAD_FADING_STATES
1070
+ this.alternateThreadFadingState.size > MAX_ALTERNATE_THREAD_FADING_STATES
1063
1071
  ) {
1064
1072
  const oldestThreadId = this.alternateThreadFadingState.keys().next();
1065
1073
  if (oldestThreadId.done === true) {
@@ -1172,7 +1180,10 @@ export class AgentSession {
1172
1180
  };
1173
1181
  const advanceGeneration = (agentKey: string): void => {
1174
1182
  const key = fadingGenerationKey(scopeKey, agentKey);
1175
- this.fadingGenerations.set(key, (this.fadingGenerations.get(key) ?? 0) + 1);
1183
+ this.fadingGenerations.set(
1184
+ key,
1185
+ (this.fadingGenerations.get(key) ?? 0) + 1
1186
+ );
1176
1187
  };
1177
1188
  const mergeCapturedTier = (
1178
1189
  existing: t.FadingTier | undefined,
@@ -1218,7 +1229,11 @@ export class AgentSession {
1218
1229
  if (didResetTier) {
1219
1230
  advanceGeneration('');
1220
1231
  }
1221
- nextTier = mergeCapturedTier(current.fadingTier, incomingTier, didResetTier);
1232
+ nextTier = mergeCapturedTier(
1233
+ current.fadingTier,
1234
+ incomingTier,
1235
+ didResetTier
1236
+ );
1222
1237
  }
1223
1238
  if (threadId === this.threadId && checkpointNs === '') {
1224
1239
  this.fadingTier = nextTier;
@@ -1408,8 +1423,8 @@ export class AgentSession {
1408
1423
  handlerResult.events.push(streamEvent);
1409
1424
  onEvent?.(streamEvent);
1410
1425
  };
1411
- const sessionState = deriveMessages(
1412
- isSessionThread ? (this.store?.getPath() ?? []) : []
1426
+ const sessionState = deriveSessionMessages(
1427
+ isSessionThread ? this.store : undefined
1413
1428
  );
1414
1429
  const fadingState = this.getFadingState(threadId, checkpointNs);
1415
1430
  let run: Run<t.IState> | undefined;
@@ -1690,7 +1705,9 @@ export class AgentSession {
1690
1705
  throw new Error('Cannot compact an ephemeral session');
1691
1706
  }
1692
1707
  const activePath = path ?? store.getPath();
1693
- const sessionState = deriveMessages(activePath);
1708
+ const sessionState = path
1709
+ ? deriveMessages(path)
1710
+ : deriveSessionMessages(store);
1694
1711
  const messageEntries = activePath.filter(isMessageEntry);
1695
1712
  if (sessionState.messages.length === 0) {
1696
1713
  return undefined;
@@ -1819,8 +1836,8 @@ export class AgentSession {
1819
1836
  threadId,
1820
1837
  userHandlers: this.runConfig.customHandlers,
1821
1838
  });
1822
- const sessionState = deriveMessages(
1823
- isSessionThread ? (this.store?.getPath() ?? []) : []
1839
+ const sessionState = deriveSessionMessages(
1840
+ isSessionThread ? this.store : undefined
1824
1841
  );
1825
1842
  const fadingState = this.getFadingState(threadId, checkpointNs);
1826
1843
  let run: Run<t.IState> | undefined;
@@ -35,6 +35,10 @@ import {
35
35
  serializeMessage,
36
36
  toJsonValue,
37
37
  } from './messageSerialization';
38
+ import {
39
+ initializeSessionProjection,
40
+ shareSessionProjectionOwnership,
41
+ } from './sessionProjection';
38
42
  import { createEntryId, createSessionId, createTimestamp } from './ids';
39
43
 
40
44
  const SESSION_VERSION = 1;
@@ -126,6 +130,7 @@ export class JsonlSessionStore {
126
130
  this.path = params.path;
127
131
  this.header = params.header;
128
132
  this.entries = sortEntries(params.entries);
133
+ initializeSessionProjection(this, this.entries);
129
134
  }
130
135
 
131
136
  static getDefaultRoot(): string {
@@ -573,6 +578,7 @@ export class JsonlSessionStore {
573
578
  name: options.name ?? this.header.name,
574
579
  parentSession: this.path,
575
580
  });
581
+ shareSessionProjectionOwnership(this, newStore);
576
582
  const pathEntries = target ? this.getPath(target.id) : [];
577
583
  for (const entry of pathEntries) {
578
584
  await newStore.appendExistingEntry(entry);
@@ -0,0 +1,126 @@
1
+ import type { DerivedSessionMessages } from './deriveMessages';
2
+ import type { SessionEntry, SessionStateEntry } from './types';
3
+ import type { JsonlSessionStore } from './JsonlSessionStore';
4
+ import { deriveMessages } from './deriveMessages';
5
+
6
+ interface SessionProjection {
7
+ entries: readonly SessionEntry[];
8
+ ownership: { exposed: boolean };
9
+ indexedCount: number;
10
+ byId: Map<string, SessionEntry>;
11
+ lastState?: SessionStateEntry;
12
+ lastMessage?: SessionEntry;
13
+ leaf?: SessionEntry;
14
+ projected: SessionEntry[];
15
+ }
16
+
17
+ const projections = new WeakMap<JsonlSessionStore, SessionProjection>();
18
+
19
+ /** Internal: only AgentSession's unexposed stores may reuse log topology. */
20
+ export function initializeSessionProjection(
21
+ store: JsonlSessionStore,
22
+ entries: readonly SessionEntry[]
23
+ ): void {
24
+ projections.set(store, {
25
+ entries,
26
+ ownership: { exposed: false },
27
+ indexedCount: 0,
28
+ byId: new Map(),
29
+ projected: [],
30
+ });
31
+ }
32
+
33
+ /** Forks share entry references, so exposing either disables the whole family. */
34
+ export function shareSessionProjectionOwnership(
35
+ source: JsonlSessionStore,
36
+ target: JsonlSessionStore
37
+ ): void {
38
+ const sourceProjection = projections.get(source);
39
+ const targetProjection = projections.get(target);
40
+ if (sourceProjection && targetProjection) {
41
+ targetProjection.ownership = sourceProjection.ownership;
42
+ }
43
+ }
44
+
45
+ export function releaseSessionProjection(store: JsonlSessionStore): void {
46
+ const projection = projections.get(store);
47
+ if (projection) {
48
+ projection.ownership.exposed = true;
49
+ projection.byId.clear();
50
+ projection.projected = [];
51
+ projection.leaf = undefined;
52
+ }
53
+ }
54
+
55
+ function indexAppends(projection: SessionProjection): boolean {
56
+ for (
57
+ ;
58
+ projection.indexedCount < projection.entries.length;
59
+ projection.indexedCount++
60
+ ) {
61
+ const entry = projection.entries[projection.indexedCount];
62
+ if (projection.byId.has(entry.id)) {
63
+ return false;
64
+ }
65
+ projection.byId.set(entry.id, entry);
66
+ if (entry.type === 'session_state') {
67
+ projection.lastState = entry;
68
+ } else if (entry.type === 'message' || entry.type === 'summary') {
69
+ projection.lastMessage = entry;
70
+ }
71
+ }
72
+ return true;
73
+ }
74
+
75
+ function projectLeaf(projection: SessionProjection): void {
76
+ const leafId = projection.lastState
77
+ ? projection.lastState.data.leafId
78
+ : projection.lastMessage?.id;
79
+ const leaf =
80
+ leafId != null && leafId !== '' ? projection.byId.get(leafId) : undefined;
81
+ if (leaf === projection.leaf) {
82
+ return;
83
+ }
84
+ const delta: SessionEntry[] = [];
85
+ let current = leaf;
86
+ while (current && current !== projection.leaf) {
87
+ if (current.type === 'message' || current.type === 'summary') {
88
+ delta.push(current);
89
+ }
90
+ if (current.type === 'summary') {
91
+ current = undefined;
92
+ break;
93
+ }
94
+ current =
95
+ current.parentId == null
96
+ ? undefined
97
+ : projection.byId.get(current.parentId);
98
+ }
99
+ if (!current) {
100
+ projection.projected = [];
101
+ }
102
+ for (let i = delta.length - 1; i >= 0; i--) {
103
+ projection.projected.push(delta[i]);
104
+ }
105
+ projection.leaf = leaf;
106
+ }
107
+
108
+ /** Reuses log topology, but materializes fresh mutable messages for every run. */
109
+ export function deriveSessionMessages(
110
+ store: JsonlSessionStore | undefined
111
+ ): DerivedSessionMessages {
112
+ if (!store) {
113
+ return { messages: [] };
114
+ }
115
+ const projection = projections.get(store);
116
+ if (!projection || projection.ownership.exposed) {
117
+ releaseSessionProjection(store);
118
+ return deriveMessages(store.getPath());
119
+ }
120
+ if (!indexAppends(projection)) {
121
+ releaseSessionProjection(store);
122
+ return deriveMessages(store.getPath());
123
+ }
124
+ projectLeaf(projection);
125
+ return deriveMessages(projection.projected);
126
+ }
package/src/stream.ts CHANGED
@@ -1014,18 +1014,6 @@ function pruneEagerToolCallChunkStates(args: {
1014
1014
  }
1015
1015
  }
1016
1016
 
1017
- function isEagerToolChunkStateComplete(
1018
- state: t.EagerEventToolCallChunkState
1019
- ): boolean {
1020
- return (
1021
- state.id != null &&
1022
- state.id !== '' &&
1023
- state.name != null &&
1024
- state.name !== '' &&
1025
- coerceRecordArgs(state.argsText) != null
1026
- );
1027
- }
1028
-
1029
1017
  function mergeToolCallArgsText(existing: string, incoming: string): string {
1030
1018
  if (incoming === '') {
1031
1019
  return existing;
@@ -1180,6 +1168,7 @@ function getStreamedReadyToolCalls(args: {
1180
1168
  key: string;
1181
1169
  state: t.EagerEventToolCallChunkState;
1182
1170
  sealedByAdapter: boolean;
1171
+ parsedArgs: ToolCall['args'];
1183
1172
  }> = [];
1184
1173
 
1185
1174
  for (const [key, state] of graph.eagerEventToolCallChunks) {
@@ -1190,7 +1179,12 @@ function getStreamedReadyToolCalls(args: {
1190
1179
  graph.eagerEventToolCallChunks.delete(key);
1191
1180
  continue;
1192
1181
  }
1193
- if (!isEagerToolChunkStateComplete(state)) {
1182
+ if (
1183
+ state.id == null ||
1184
+ state.id === '' ||
1185
+ state.name == null ||
1186
+ state.name === ''
1187
+ ) {
1194
1188
  continue;
1195
1189
  }
1196
1190
  const isSealedByLaterChunk =
@@ -1209,10 +1203,15 @@ function getStreamedReadyToolCalls(args: {
1209
1203
  isSealedByLaterChunk ||
1210
1204
  isSealedExplicitly
1211
1205
  ) {
1206
+ const parsedArgs = coerceRecordArgs(state.argsText);
1207
+ if (parsedArgs == null) {
1208
+ continue;
1209
+ }
1212
1210
  readyEntries.push({
1213
1211
  key,
1214
1212
  state,
1215
1213
  sealedByAdapter: isSealedExplicitly || seal?.kind === 'all',
1214
+ parsedArgs,
1216
1215
  });
1217
1216
  }
1218
1217
  }
@@ -1232,11 +1231,7 @@ function getStreamedReadyToolCalls(args: {
1232
1231
 
1233
1232
  return readyEntries
1234
1233
  .sort((left, right) => (left.state.index ?? 0) - (right.state.index ?? 0))
1235
- .flatMap(({ state, sealedByAdapter }) => {
1236
- const args = coerceRecordArgs(state.argsText);
1237
- if (args == null) {
1238
- return [];
1239
- }
1234
+ .flatMap(({ state, sealedByAdapter, parsedArgs }) => {
1240
1235
  // The final request's args come from LangChain's canonical verbatim
1241
1236
  // concatenation of fragments, while `argsText` reconciles provider
1242
1237
  // quirks with lossy heuristics that can also swallow legitimately
@@ -1267,7 +1262,7 @@ function getStreamedReadyToolCalls(args: {
1267
1262
  {
1268
1263
  id: state.id,
1269
1264
  name: state.name ?? '',
1270
- args,
1265
+ args: parsedArgs,
1271
1266
  },
1272
1267
  ];
1273
1268
  });
@@ -9,7 +9,9 @@ export {
9
9
  buildSummarizationInstruction,
10
10
  buildSummaryCarrierText,
11
11
  separateSummarizationParameters,
12
+ ManualSummarizationSkippedError,
12
13
  } from './shared';
14
+ export type { ManualSummarizationSkipReason } from './shared';
13
15
 
14
16
  const VALID_TRIGGER_TYPES = [
15
17
  'token_ratio',