@librechat/agents 3.1.85 → 3.1.87

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 (166) hide show
  1. package/README.md +69 -0
  2. package/dist/cjs/agents/AgentContext.cjs +7 -2
  3. package/dist/cjs/agents/AgentContext.cjs.map +1 -1
  4. package/dist/cjs/events.cjs +23 -0
  5. package/dist/cjs/events.cjs.map +1 -1
  6. package/dist/cjs/graphs/Graph.cjs +133 -18
  7. package/dist/cjs/graphs/Graph.cjs.map +1 -1
  8. package/dist/cjs/graphs/MultiAgentGraph.cjs +1 -1
  9. package/dist/cjs/graphs/MultiAgentGraph.cjs.map +1 -1
  10. package/dist/cjs/llm/anthropic/index.cjs +251 -53
  11. package/dist/cjs/llm/anthropic/index.cjs.map +1 -1
  12. package/dist/cjs/llm/init.cjs +1 -5
  13. package/dist/cjs/llm/init.cjs.map +1 -1
  14. package/dist/cjs/llm/openai/index.cjs +113 -24
  15. package/dist/cjs/llm/openai/index.cjs.map +1 -1
  16. package/dist/cjs/llm/openai/utils/index.cjs.map +1 -1
  17. package/dist/cjs/llm/openrouter/index.cjs +3 -1
  18. package/dist/cjs/llm/openrouter/index.cjs.map +1 -1
  19. package/dist/cjs/main.cjs +18 -5
  20. package/dist/cjs/main.cjs.map +1 -1
  21. package/dist/cjs/openai/index.cjs +253 -0
  22. package/dist/cjs/openai/index.cjs.map +1 -0
  23. package/dist/cjs/responses/index.cjs +448 -0
  24. package/dist/cjs/responses/index.cjs.map +1 -0
  25. package/dist/cjs/run.cjs +108 -7
  26. package/dist/cjs/run.cjs.map +1 -1
  27. package/dist/cjs/session/AgentSession.cjs +1057 -0
  28. package/dist/cjs/session/AgentSession.cjs.map +1 -0
  29. package/dist/cjs/session/JsonlSessionStore.cjs +425 -0
  30. package/dist/cjs/session/JsonlSessionStore.cjs.map +1 -0
  31. package/dist/cjs/session/handlers.cjs +221 -0
  32. package/dist/cjs/session/handlers.cjs.map +1 -0
  33. package/dist/cjs/session/ids.cjs +22 -0
  34. package/dist/cjs/session/ids.cjs.map +1 -0
  35. package/dist/cjs/session/messageSerialization.cjs +179 -0
  36. package/dist/cjs/session/messageSerialization.cjs.map +1 -0
  37. package/dist/cjs/stream.cjs +472 -11
  38. package/dist/cjs/stream.cjs.map +1 -1
  39. package/dist/cjs/summarization/node.cjs +1 -1
  40. package/dist/cjs/summarization/node.cjs.map +1 -1
  41. package/dist/cjs/tools/ToolNode.cjs +177 -59
  42. package/dist/cjs/tools/ToolNode.cjs.map +1 -1
  43. package/dist/cjs/tools/eagerEventExecution.cjs +113 -0
  44. package/dist/cjs/tools/eagerEventExecution.cjs.map +1 -0
  45. package/dist/cjs/tools/handlers.cjs +1 -1
  46. package/dist/cjs/tools/handlers.cjs.map +1 -1
  47. package/dist/cjs/tools/streamedToolCallSeals.cjs +42 -0
  48. package/dist/cjs/tools/streamedToolCallSeals.cjs.map +1 -0
  49. package/dist/esm/agents/AgentContext.mjs +7 -2
  50. package/dist/esm/agents/AgentContext.mjs.map +1 -1
  51. package/dist/esm/events.mjs +23 -1
  52. package/dist/esm/events.mjs.map +1 -1
  53. package/dist/esm/graphs/Graph.mjs +133 -18
  54. package/dist/esm/graphs/Graph.mjs.map +1 -1
  55. package/dist/esm/graphs/MultiAgentGraph.mjs +1 -1
  56. package/dist/esm/graphs/MultiAgentGraph.mjs.map +1 -1
  57. package/dist/esm/llm/anthropic/index.mjs +251 -53
  58. package/dist/esm/llm/anthropic/index.mjs.map +1 -1
  59. package/dist/esm/llm/init.mjs +1 -5
  60. package/dist/esm/llm/init.mjs.map +1 -1
  61. package/dist/esm/llm/openai/index.mjs +113 -25
  62. package/dist/esm/llm/openai/index.mjs.map +1 -1
  63. package/dist/esm/llm/openai/utils/index.mjs.map +1 -1
  64. package/dist/esm/llm/openrouter/index.mjs +4 -2
  65. package/dist/esm/llm/openrouter/index.mjs.map +1 -1
  66. package/dist/esm/main.mjs +5 -1
  67. package/dist/esm/main.mjs.map +1 -1
  68. package/dist/esm/openai/index.mjs +246 -0
  69. package/dist/esm/openai/index.mjs.map +1 -0
  70. package/dist/esm/responses/index.mjs +440 -0
  71. package/dist/esm/responses/index.mjs.map +1 -0
  72. package/dist/esm/run.mjs +108 -7
  73. package/dist/esm/run.mjs.map +1 -1
  74. package/dist/esm/session/AgentSession.mjs +1054 -0
  75. package/dist/esm/session/AgentSession.mjs.map +1 -0
  76. package/dist/esm/session/JsonlSessionStore.mjs +422 -0
  77. package/dist/esm/session/JsonlSessionStore.mjs.map +1 -0
  78. package/dist/esm/session/handlers.mjs +219 -0
  79. package/dist/esm/session/handlers.mjs.map +1 -0
  80. package/dist/esm/session/ids.mjs +17 -0
  81. package/dist/esm/session/ids.mjs.map +1 -0
  82. package/dist/esm/session/messageSerialization.mjs +173 -0
  83. package/dist/esm/session/messageSerialization.mjs.map +1 -0
  84. package/dist/esm/stream.mjs +473 -12
  85. package/dist/esm/stream.mjs.map +1 -1
  86. package/dist/esm/summarization/node.mjs +1 -1
  87. package/dist/esm/summarization/node.mjs.map +1 -1
  88. package/dist/esm/tools/ToolNode.mjs +177 -59
  89. package/dist/esm/tools/ToolNode.mjs.map +1 -1
  90. package/dist/esm/tools/eagerEventExecution.mjs +107 -0
  91. package/dist/esm/tools/eagerEventExecution.mjs.map +1 -0
  92. package/dist/esm/tools/handlers.mjs +1 -1
  93. package/dist/esm/tools/handlers.mjs.map +1 -1
  94. package/dist/esm/tools/streamedToolCallSeals.mjs +36 -0
  95. package/dist/esm/tools/streamedToolCallSeals.mjs.map +1 -0
  96. package/dist/types/events.d.ts +1 -0
  97. package/dist/types/graphs/Graph.d.ts +24 -9
  98. package/dist/types/index.d.ts +1 -0
  99. package/dist/types/llm/openai/index.d.ts +1 -0
  100. package/dist/types/openai/index.d.ts +75 -0
  101. package/dist/types/responses/index.d.ts +97 -0
  102. package/dist/types/run.d.ts +2 -0
  103. package/dist/types/session/AgentSession.d.ts +32 -0
  104. package/dist/types/session/JsonlSessionStore.d.ts +67 -0
  105. package/dist/types/session/handlers.d.ts +8 -0
  106. package/dist/types/session/ids.d.ts +4 -0
  107. package/dist/types/session/index.d.ts +5 -0
  108. package/dist/types/session/messageSerialization.d.ts +7 -0
  109. package/dist/types/session/types.d.ts +191 -0
  110. package/dist/types/tools/ToolNode.d.ts +12 -1
  111. package/dist/types/tools/eagerEventExecution.d.ts +23 -0
  112. package/dist/types/tools/streamedToolCallSeals.d.ts +13 -0
  113. package/dist/types/types/hitl.d.ts +4 -0
  114. package/dist/types/types/run.d.ts +11 -1
  115. package/dist/types/types/tools.d.ts +36 -0
  116. package/package.json +19 -2
  117. package/src/__tests__/stream.eagerEventExecution.test.ts +2458 -0
  118. package/src/agents/AgentContext.ts +7 -2
  119. package/src/agents/__tests__/AgentContext.test.ts +254 -5
  120. package/src/events.ts +29 -0
  121. package/src/graphs/Graph.ts +224 -50
  122. package/src/graphs/MultiAgentGraph.ts +1 -1
  123. package/src/graphs/__tests__/composition.smoke.test.ts +30 -0
  124. package/src/index.ts +3 -0
  125. package/src/llm/anthropic/index.ts +356 -84
  126. package/src/llm/anthropic/llm.spec.ts +64 -0
  127. package/src/llm/custom-chat-models.smoke.test.ts +175 -4
  128. package/src/llm/openai/contentBlocks.test.ts +35 -0
  129. package/src/llm/openai/deepseek.test.ts +201 -2
  130. package/src/llm/openai/index.ts +171 -26
  131. package/src/llm/openai/utils/index.ts +22 -0
  132. package/src/llm/openrouter/index.ts +4 -2
  133. package/src/openai/__tests__/openai.test.ts +337 -0
  134. package/src/openai/index.ts +404 -0
  135. package/src/responses/__tests__/responses.test.ts +652 -0
  136. package/src/responses/index.ts +677 -0
  137. package/src/run.ts +158 -8
  138. package/src/scripts/compare_pi_vs_ours.ts +592 -173
  139. package/src/scripts/session_live.ts +548 -0
  140. package/src/session/AgentSession.ts +1432 -0
  141. package/src/session/JsonlSessionStore.ts +572 -0
  142. package/src/session/__tests__/JsonlSessionStore.test.ts +1410 -0
  143. package/src/session/__tests__/handlers.test.ts +161 -0
  144. package/src/session/handlers.ts +272 -0
  145. package/src/session/ids.ts +17 -0
  146. package/src/session/index.ts +44 -0
  147. package/src/session/messageSerialization.ts +207 -0
  148. package/src/session/types.ts +275 -0
  149. package/src/specs/custom-event-await.test.ts +89 -0
  150. package/src/specs/summarization.test.ts +1 -1
  151. package/src/stream.ts +755 -48
  152. package/src/summarization/node.ts +1 -1
  153. package/src/tools/ToolNode.ts +299 -126
  154. package/src/tools/__tests__/ToolNode.eagerEventExecution.test.ts +373 -0
  155. package/src/tools/__tests__/handlers.test.ts +2 -1
  156. package/src/tools/__tests__/hitl.test.ts +206 -110
  157. package/src/tools/eagerEventExecution.ts +153 -0
  158. package/src/tools/handlers.ts +8 -4
  159. package/src/tools/streamedToolCallSeals.ts +57 -0
  160. package/src/types/hitl.ts +4 -0
  161. package/src/types/run.ts +11 -0
  162. package/src/types/tools.ts +36 -0
  163. package/dist/cjs/llm/text.cjs +0 -69
  164. package/dist/cjs/llm/text.cjs.map +0 -1
  165. package/dist/esm/llm/text.mjs +0 -67
  166. package/dist/esm/llm/text.mjs.map +0 -1
@@ -70,6 +70,13 @@ const { AGENT, TOOLS, SUMMARIZE } = GraphNodeKeys;
70
70
  /** Minimum relative variance before calibrated toolSchemaTokens overrides current value. */
71
71
  const CALIBRATION_VARIANCE_THRESHOLD = 0.15;
72
72
 
73
+ function getHandlerDispatchedEventKey(
74
+ eventName: string,
75
+ stepId: string
76
+ ): string {
77
+ return `${eventName}:${stepId}`;
78
+ }
79
+
73
80
  export abstract class Graph<
74
81
  T extends t.BaseGraphState = t.BaseGraphState,
75
82
  _TNodeName extends string = string,
@@ -99,15 +106,18 @@ export abstract class Graph<
99
106
  ): Promise<string>;
100
107
  abstract dispatchRunStepDelta(
101
108
  id: string,
102
- delta: t.ToolCallDelta
109
+ delta: t.ToolCallDelta,
110
+ metadata?: Record<string, unknown>
103
111
  ): Promise<void>;
104
112
  abstract dispatchMessageDelta(
105
113
  id: string,
106
- delta: t.MessageDelta
114
+ delta: t.MessageDelta,
115
+ metadata?: Record<string, unknown>
107
116
  ): Promise<void>;
108
117
  abstract dispatchReasoningDelta(
109
118
  stepId: string,
110
- delta: t.ReasoningDelta
119
+ delta: t.ReasoningDelta,
120
+ metadata?: Record<string, unknown>
111
121
  ): Promise<void>;
112
122
  abstract createCallModel(
113
123
  agentId?: string,
@@ -125,11 +135,12 @@ export abstract class Graph<
125
135
  contentIndexMap: Map<string, number> = new Map();
126
136
  toolCallStepIds: Map<string, string> = new Map();
127
137
  /**
128
- * Step IDs that have been dispatched via handler registry directly
129
- * (in dispatchRunStep). Used by the custom event callback to skip
130
- * duplicate dispatch through the LangGraph callback chain.
138
+ * Step IDs dispatched through the handler registry during this run.
139
+ * Event echo suppression is tracked separately so repeated deltas for
140
+ * the same step are scoped to the active custom event dispatch.
131
141
  */
132
142
  handlerDispatchedStepIds: Set<string> = new Set();
143
+ protected handlerDispatchedEventCounts: Map<string, number> = new Map();
133
144
  signal?: AbortSignal;
134
145
  /** Set of invoked tool call IDs from non-message run steps completed mid-run, if any */
135
146
  invokedToolIds?: Set<string>;
@@ -148,6 +159,20 @@ export abstract class Graph<
148
159
  * graph compiles.
149
160
  */
150
161
  toolOutputReferences: t.ToolOutputReferencesConfig | undefined;
162
+ /**
163
+ * Run-scoped opt-in for eager event-driven tool execution. The stream
164
+ * handler may prestart eligible event-driven tools; ToolNode later
165
+ * consumes the settled promises while preserving final ToolMessage order.
166
+ */
167
+ eagerEventToolExecution: t.EagerEventToolExecutionConfig | undefined;
168
+ eagerEventToolExecutions: Map<string, t.EagerEventToolExecution> = new Map();
169
+ eagerEventToolUsageCount: Map<string, number> = new Map();
170
+ private eagerEventToolUsageCountsByAgentId: Map<
171
+ string,
172
+ Map<string, number>
173
+ > = new Map();
174
+ eagerEventToolCallChunks: Map<string, t.EagerEventToolCallChunkState> =
175
+ new Map();
151
176
  /**
152
177
  * Run-scoped execution backend for built-in code tools. Defaults to the
153
178
  * remote Code API sandbox when unset.
@@ -187,7 +212,12 @@ export abstract class Graph<
187
212
  this.hookRegistry = undefined;
188
213
  this.humanInTheLoop = undefined;
189
214
  this.toolOutputReferences = undefined;
215
+ this.eagerEventToolExecution = undefined;
216
+ this.eagerEventToolExecutions.clear();
217
+ this.clearEagerEventToolUsageCounts();
218
+ this.eagerEventToolCallChunks.clear();
190
219
  this.toolExecution = undefined;
220
+ this.handlerDispatchedEventCounts.clear();
191
221
  /**
192
222
  * ToolNodes compiled from this graph captured the registry
193
223
  * instance at construction time, so simply dropping the Graph's
@@ -218,6 +248,46 @@ export abstract class Graph<
218
248
  this.sessions.clear();
219
249
  }
220
250
 
251
+ getEagerEventToolUsageCount(agentId?: string): Map<string, number> {
252
+ if (agentId == null || agentId === '') {
253
+ return this.eagerEventToolUsageCount;
254
+ }
255
+ let usageCount = this.eagerEventToolUsageCountsByAgentId.get(agentId);
256
+ if (usageCount == null) {
257
+ usageCount = new Map<string, number>();
258
+ this.eagerEventToolUsageCountsByAgentId.set(agentId, usageCount);
259
+ }
260
+ return usageCount;
261
+ }
262
+
263
+ protected clearEagerEventToolUsageCounts(): void {
264
+ this.eagerEventToolUsageCount.clear();
265
+ for (const usageCount of this.eagerEventToolUsageCountsByAgentId.values()) {
266
+ usageCount.clear();
267
+ }
268
+ }
269
+
270
+ markHandlerDispatchedEvent(eventName: string, stepId: string): () => void {
271
+ const key = getHandlerDispatchedEventKey(eventName, stepId);
272
+ this.handlerDispatchedEventCounts.set(
273
+ key,
274
+ (this.handlerDispatchedEventCounts.get(key) ?? 0) + 1
275
+ );
276
+ return () => {
277
+ const count = this.handlerDispatchedEventCounts.get(key) ?? 0;
278
+ if (count <= 1) {
279
+ this.handlerDispatchedEventCounts.delete(key);
280
+ return;
281
+ }
282
+ this.handlerDispatchedEventCounts.set(key, count - 1);
283
+ };
284
+ }
285
+
286
+ hasHandlerDispatchedEvent(eventName: string, stepId: string): boolean {
287
+ const key = getHandlerDispatchedEventKey(eventName, stepId);
288
+ return (this.handlerDispatchedEventCounts.get(key) ?? 0) > 0;
289
+ }
290
+
221
291
  /**
222
292
  * Subclass hook to register a freshly compiled ToolNode so
223
293
  * `clearHeavyState` can flush its per-Run direct-path turn cache
@@ -279,9 +349,7 @@ export abstract class Graph<
279
349
  private _compiledToolNodes: Set<{
280
350
  clearDirectPathTurns(): void;
281
351
  }> = new Set();
282
- public getOrCreateFileCheckpointer():
283
- | t.LocalFileCheckpointer
284
- | undefined {
352
+ public getOrCreateFileCheckpointer(): t.LocalFileCheckpointer | undefined {
285
353
  // Return the cached instance unconditionally if one exists. The
286
354
  // toolExecution check below decides whether to *create* a new
287
355
  // one — `clearHeavyState` nulls `this.toolExecution` at end-of-
@@ -301,9 +369,7 @@ export abstract class Graph<
301
369
  // cleanup hooks fire). The bundle factory itself accepts a pre-
302
370
  // supplied checkpointer when present, so re-injecting this one
303
371
  // into every ToolNode is idempotent.
304
- const bundle = createLocalCodingToolBundle(
305
- this.toolExecution.local ?? {}
306
- );
372
+ const bundle = createLocalCodingToolBundle(this.toolExecution.local ?? {});
307
373
  this._fileCheckpointer = bundle.checkpointer;
308
374
  return this._fileCheckpointer;
309
375
  }
@@ -381,10 +447,17 @@ export class StandardGraph extends Graph<t.BaseGraphState, t.GraphNode> {
381
447
  * a stale reference on 2nd+ processStream calls.
382
448
  */
383
449
  this.toolCallStepIds.clear();
450
+ this.eagerEventToolExecutions.clear();
451
+ this.clearEagerEventToolUsageCounts();
452
+ this.eagerEventToolCallChunks.clear();
384
453
  this.handlerDispatchedStepIds = resetIfNotEmpty(
385
454
  this.handlerDispatchedStepIds,
386
455
  new Set()
387
456
  );
457
+ this.handlerDispatchedEventCounts = resetIfNotEmpty(
458
+ this.handlerDispatchedEventCounts,
459
+ new Map()
460
+ );
388
461
  this.messageIdsByStepKey = resetIfNotEmpty(
389
462
  this.messageIdsByStepKey,
390
463
  new Map()
@@ -679,6 +752,11 @@ export class StandardGraph extends Graph<t.BaseGraphState, t.GraphNode> {
679
752
  toolRegistry: agentContext?.toolRegistry,
680
753
  hookRegistry: this.hookRegistry,
681
754
  humanInTheLoop: this.humanInTheLoop,
755
+ eagerEventToolExecution: this.eagerEventToolExecution,
756
+ eagerEventToolExecutions: this.eagerEventToolExecutions,
757
+ eagerEventToolUsageCount: this.getEagerEventToolUsageCount(
758
+ agentContext?.agentId
759
+ ),
682
760
  toolExecution: this.toolExecution,
683
761
  directToolNames: directToolNames.size > 0 ? directToolNames : undefined,
684
762
  maxContextTokens: agentContext?.maxContextTokens,
@@ -1307,9 +1385,13 @@ export class StandardGraph extends Graph<t.BaseGraphState, t.GraphNode> {
1307
1385
  );
1308
1386
  const stepId = this.getStepIdByKey(stepKey);
1309
1387
  if (typeof content === 'string') {
1310
- await this.dispatchMessageDelta(stepId, {
1311
- content: [{ type: ContentTypes.TEXT, text: content }],
1312
- });
1388
+ await this.dispatchMessageDelta(
1389
+ stepId,
1390
+ {
1391
+ content: [{ type: ContentTypes.TEXT, text: content }],
1392
+ },
1393
+ metadata
1394
+ );
1313
1395
  } else if (
1314
1396
  Array.isArray(content) &&
1315
1397
  content.every(
@@ -1320,9 +1402,13 @@ export class StandardGraph extends Graph<t.BaseGraphState, t.GraphNode> {
1320
1402
  c.type.startsWith('text')
1321
1403
  )
1322
1404
  ) {
1323
- await this.dispatchMessageDelta(stepId, {
1324
- content: content as t.MessageDelta['content'],
1325
- });
1405
+ await this.dispatchMessageDelta(
1406
+ stepId,
1407
+ {
1408
+ content: content as t.MessageDelta['content'],
1409
+ },
1410
+ metadata
1411
+ );
1326
1412
  }
1327
1413
  }
1328
1414
  }
@@ -1360,9 +1446,13 @@ export class StandardGraph extends Graph<t.BaseGraphState, t.GraphNode> {
1360
1446
  const stepId = this.getStepIdByKey(stepKey);
1361
1447
  const content = responseMessage.content;
1362
1448
  if (typeof content === 'string') {
1363
- await this.dispatchMessageDelta(stepId, {
1364
- content: [{ type: ContentTypes.TEXT, text: content }],
1365
- });
1449
+ await this.dispatchMessageDelta(
1450
+ stepId,
1451
+ {
1452
+ content: [{ type: ContentTypes.TEXT, text: content }],
1453
+ },
1454
+ metadata
1455
+ );
1366
1456
  } else if (
1367
1457
  Array.isArray(content) &&
1368
1458
  content.every(
@@ -1373,9 +1463,13 @@ export class StandardGraph extends Graph<t.BaseGraphState, t.GraphNode> {
1373
1463
  c.type.startsWith('text')
1374
1464
  )
1375
1465
  ) {
1376
- await this.dispatchMessageDelta(stepId, {
1377
- content: content as t.MessageDelta['content'],
1378
- });
1466
+ await this.dispatchMessageDelta(
1467
+ stepId,
1468
+ {
1469
+ content: content as t.MessageDelta['content'],
1470
+ },
1471
+ metadata
1472
+ );
1379
1473
  }
1380
1474
  }
1381
1475
  }
@@ -1613,12 +1707,22 @@ export class StandardGraph extends Graph<t.BaseGraphState, t.GraphNode> {
1613
1707
  this.handlerDispatchedStepIds.add(runStep.id);
1614
1708
  }
1615
1709
 
1616
- if (resolvedConfig) {
1617
- await safeDispatchCustomEvent(
1710
+ const unmarkHandlerDispatchedEvent = handler
1711
+ ? this.markHandlerDispatchedEvent(
1618
1712
  GraphEvents.ON_RUN_STEP,
1619
- runStep,
1620
- resolvedConfig
1621
- );
1713
+ runStep.id
1714
+ )
1715
+ : undefined;
1716
+ try {
1717
+ if (resolvedConfig) {
1718
+ await safeDispatchCustomEvent(
1719
+ GraphEvents.ON_RUN_STEP,
1720
+ runStep,
1721
+ resolvedConfig
1722
+ );
1723
+ }
1724
+ } finally {
1725
+ unmarkHandlerDispatchedEvent?.();
1622
1726
  }
1623
1727
  },
1624
1728
  dispatchRunStepCompleted: async (
@@ -1663,7 +1767,7 @@ export class StandardGraph extends Graph<t.BaseGraphState, t.GraphNode> {
1663
1767
  const StateAnnotation = Annotation.Root({
1664
1768
  messages: Annotation<BaseMessage[]>({
1665
1769
  reducer: (a, b) => {
1666
- if (!a.length) {
1770
+ if (!this.messages.length) {
1667
1771
  this.startIndex = a.length + b.length;
1668
1772
  }
1669
1773
  const result = messagesStateReducer(a, b);
@@ -1782,11 +1886,18 @@ export class StandardGraph extends Graph<t.BaseGraphState, t.GraphNode> {
1782
1886
  // but the primary dispatch above guarantees the event reaches the handler.
1783
1887
  // The customEventCallback in run.ts skips events already dispatched above
1784
1888
  // to prevent double handling.
1785
- await safeDispatchCustomEvent(
1786
- GraphEvents.ON_RUN_STEP,
1787
- runStep,
1788
- this.config
1789
- );
1889
+ const unmarkHandlerDispatchedEvent = handler
1890
+ ? this.markHandlerDispatchedEvent(GraphEvents.ON_RUN_STEP, stepId)
1891
+ : undefined;
1892
+ try {
1893
+ await safeDispatchCustomEvent(
1894
+ GraphEvents.ON_RUN_STEP,
1895
+ runStep,
1896
+ this.config
1897
+ );
1898
+ } finally {
1899
+ unmarkHandlerDispatchedEvent?.();
1900
+ }
1790
1901
  return stepId;
1791
1902
  }
1792
1903
 
@@ -1858,7 +1969,8 @@ export class StandardGraph extends Graph<t.BaseGraphState, t.GraphNode> {
1858
1969
 
1859
1970
  async dispatchRunStepDelta(
1860
1971
  id: string,
1861
- delta: t.ToolCallDelta
1972
+ delta: t.ToolCallDelta,
1973
+ metadata?: Record<string, unknown>
1862
1974
  ): Promise<void> {
1863
1975
  if (!this.config) {
1864
1976
  throw new Error('No config provided');
@@ -1869,14 +1981,37 @@ export class StandardGraph extends Graph<t.BaseGraphState, t.GraphNode> {
1869
1981
  id,
1870
1982
  delta,
1871
1983
  };
1872
- await safeDispatchCustomEvent(
1873
- GraphEvents.ON_RUN_STEP_DELTA,
1874
- runStepDelta,
1875
- this.config
1984
+ const handler = this.handlerRegistry?.getHandler(
1985
+ GraphEvents.ON_RUN_STEP_DELTA
1876
1986
  );
1987
+ if (handler) {
1988
+ await handler.handle(
1989
+ GraphEvents.ON_RUN_STEP_DELTA,
1990
+ runStepDelta,
1991
+ metadata,
1992
+ this
1993
+ );
1994
+ this.handlerDispatchedStepIds.add(id);
1995
+ }
1996
+ const unmarkHandlerDispatchedEvent = handler
1997
+ ? this.markHandlerDispatchedEvent(GraphEvents.ON_RUN_STEP_DELTA, id)
1998
+ : undefined;
1999
+ try {
2000
+ await safeDispatchCustomEvent(
2001
+ GraphEvents.ON_RUN_STEP_DELTA,
2002
+ runStepDelta,
2003
+ this.config
2004
+ );
2005
+ } finally {
2006
+ unmarkHandlerDispatchedEvent?.();
2007
+ }
1877
2008
  }
1878
2009
 
1879
- async dispatchMessageDelta(id: string, delta: t.MessageDelta): Promise<void> {
2010
+ async dispatchMessageDelta(
2011
+ id: string,
2012
+ delta: t.MessageDelta,
2013
+ metadata?: Record<string, unknown>
2014
+ ): Promise<void> {
1880
2015
  if (!this.config) {
1881
2016
  throw new Error('No config provided');
1882
2017
  }
@@ -1884,16 +2019,36 @@ export class StandardGraph extends Graph<t.BaseGraphState, t.GraphNode> {
1884
2019
  id,
1885
2020
  delta,
1886
2021
  };
1887
- await safeDispatchCustomEvent(
1888
- GraphEvents.ON_MESSAGE_DELTA,
1889
- messageDelta,
1890
- this.config
2022
+ const handler = this.handlerRegistry?.getHandler(
2023
+ GraphEvents.ON_MESSAGE_DELTA
1891
2024
  );
2025
+ if (handler) {
2026
+ await handler.handle(
2027
+ GraphEvents.ON_MESSAGE_DELTA,
2028
+ messageDelta,
2029
+ metadata,
2030
+ this
2031
+ );
2032
+ this.handlerDispatchedStepIds.add(id);
2033
+ }
2034
+ const unmarkHandlerDispatchedEvent = handler
2035
+ ? this.markHandlerDispatchedEvent(GraphEvents.ON_MESSAGE_DELTA, id)
2036
+ : undefined;
2037
+ try {
2038
+ await safeDispatchCustomEvent(
2039
+ GraphEvents.ON_MESSAGE_DELTA,
2040
+ messageDelta,
2041
+ this.config
2042
+ );
2043
+ } finally {
2044
+ unmarkHandlerDispatchedEvent?.();
2045
+ }
1892
2046
  }
1893
2047
 
1894
2048
  dispatchReasoningDelta = async (
1895
2049
  stepId: string,
1896
- delta: t.ReasoningDelta
2050
+ delta: t.ReasoningDelta,
2051
+ metadata?: Record<string, unknown>
1897
2052
  ): Promise<void> => {
1898
2053
  if (!this.config) {
1899
2054
  throw new Error('No config provided');
@@ -1902,10 +2057,29 @@ export class StandardGraph extends Graph<t.BaseGraphState, t.GraphNode> {
1902
2057
  id: stepId,
1903
2058
  delta,
1904
2059
  };
1905
- await safeDispatchCustomEvent(
1906
- GraphEvents.ON_REASONING_DELTA,
1907
- reasoningDelta,
1908
- this.config
2060
+ const handler = this.handlerRegistry?.getHandler(
2061
+ GraphEvents.ON_REASONING_DELTA
1909
2062
  );
2063
+ if (handler) {
2064
+ await handler.handle(
2065
+ GraphEvents.ON_REASONING_DELTA,
2066
+ reasoningDelta,
2067
+ metadata,
2068
+ this
2069
+ );
2070
+ this.handlerDispatchedStepIds.add(stepId);
2071
+ }
2072
+ const unmarkHandlerDispatchedEvent = handler
2073
+ ? this.markHandlerDispatchedEvent(GraphEvents.ON_REASONING_DELTA, stepId)
2074
+ : undefined;
2075
+ try {
2076
+ await safeDispatchCustomEvent(
2077
+ GraphEvents.ON_REASONING_DELTA,
2078
+ reasoningDelta,
2079
+ this.config
2080
+ );
2081
+ } finally {
2082
+ unmarkHandlerDispatchedEvent?.();
2083
+ }
1910
2084
  };
1911
2085
  }
@@ -723,7 +723,7 @@ export class MultiAgentGraph extends StandardGraph {
723
723
  const StateAnnotation = Annotation.Root({
724
724
  messages: Annotation<BaseMessage[]>({
725
725
  reducer: (a, b) => {
726
- if (!a.length) {
726
+ if (!this.messages.length) {
727
727
  this.startIndex = a.length + b.length;
728
728
  }
729
729
  const result = messagesStateReducer(a, b);
@@ -73,6 +73,36 @@ const expectCompiledWorkflow = (
73
73
  };
74
74
 
75
75
  describe('LangGraph composition smoke tests', () => {
76
+ it('clears run-scoped eager tool state on reset', () => {
77
+ const graph = new StandardGraph({
78
+ runId: 'standard-eager-reset',
79
+ agents: [makeAgent('agent')],
80
+ });
81
+ const executions = graph.eagerEventToolExecutions;
82
+ const usageCount = graph.eagerEventToolUsageCount;
83
+ const scopedUsageCount = graph.getEagerEventToolUsageCount('agent');
84
+ const chunks = graph.eagerEventToolCallChunks;
85
+
86
+ graph.eagerEventToolExecutions.set(
87
+ 'call_weather',
88
+ {} as t.EagerEventToolExecution
89
+ );
90
+ graph.eagerEventToolUsageCount.set('weather', 1);
91
+ scopedUsageCount.set('weather', 1);
92
+ graph.eagerEventToolCallChunks.set('0', { argsText: '{"city":"NYC"}' });
93
+
94
+ graph.resetValues();
95
+
96
+ expect(graph.eagerEventToolExecutions).toBe(executions);
97
+ expect(graph.eagerEventToolUsageCount).toBe(usageCount);
98
+ expect(graph.getEagerEventToolUsageCount('agent')).toBe(scopedUsageCount);
99
+ expect(graph.eagerEventToolCallChunks).toBe(chunks);
100
+ expect(graph.eagerEventToolExecutions.size).toBe(0);
101
+ expect(graph.eagerEventToolUsageCount.size).toBe(0);
102
+ expect(scopedUsageCount.size).toBe(0);
103
+ expect(graph.eagerEventToolCallChunks.size).toBe(0);
104
+ });
105
+
76
106
  it('compiles and invokes the standard single-agent graph', async () => {
77
107
  const graph = new StandardGraph({
78
108
  runId: 'standard-smoke',
package/src/index.ts CHANGED
@@ -36,6 +36,9 @@ export * from './utils';
36
36
  /* Hooks */
37
37
  export * from './hooks';
38
38
 
39
+ /* Programmatic sessions */
40
+ export * from './session';
41
+
39
42
  /* HITL helpers */
40
43
  export * from './hitl';
41
44