@librechat/agents 3.3.7 → 3.3.9

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 (168) hide show
  1. package/dist/cjs/graphs/Graph.cjs +47 -13
  2. package/dist/cjs/graphs/Graph.cjs.map +1 -1
  3. package/dist/cjs/graphs/MultiAgentGraph.cjs +56 -6
  4. package/dist/cjs/graphs/MultiAgentGraph.cjs.map +1 -1
  5. package/dist/cjs/hitl/askUserQuestion.cjs +3 -2
  6. package/dist/cjs/hitl/askUserQuestion.cjs.map +1 -1
  7. package/dist/cjs/instrumentation.cjs +18 -48
  8. package/dist/cjs/instrumentation.cjs.map +1 -1
  9. package/dist/cjs/langfuse.cjs +174 -29
  10. package/dist/cjs/langfuse.cjs.map +1 -1
  11. package/dist/cjs/langfuseConfig.cjs +12 -0
  12. package/dist/cjs/langfuseConfig.cjs.map +1 -1
  13. package/dist/cjs/langfuseRuntimeContext.cjs +23 -2
  14. package/dist/cjs/langfuseRuntimeContext.cjs.map +1 -1
  15. package/dist/cjs/langfuseRuntimeScope.cjs +39 -8
  16. package/dist/cjs/langfuseRuntimeScope.cjs.map +1 -1
  17. package/dist/cjs/langfuseSpanRegistry.cjs +95 -0
  18. package/dist/cjs/langfuseSpanRegistry.cjs.map +1 -0
  19. package/dist/cjs/langfuseTraceShaping.cjs +121 -4
  20. package/dist/cjs/langfuseTraceShaping.cjs.map +1 -1
  21. package/dist/cjs/llm/anthropic/utils/message_inputs.cjs +39 -15
  22. package/dist/cjs/llm/anthropic/utils/message_inputs.cjs.map +1 -1
  23. package/dist/cjs/llm/bedrock/utils/message_inputs.cjs +25 -5
  24. package/dist/cjs/llm/bedrock/utils/message_inputs.cjs.map +1 -1
  25. package/dist/cjs/llm/init.cjs +3 -3
  26. package/dist/cjs/llm/invoke.cjs +5 -5
  27. package/dist/cjs/llm/openai/index.cjs +1 -1
  28. package/dist/cjs/main.cjs +10 -10
  29. package/dist/cjs/messages/format.cjs +124 -15
  30. package/dist/cjs/messages/format.cjs.map +1 -1
  31. package/dist/cjs/messages/injected.cjs +10 -1
  32. package/dist/cjs/messages/injected.cjs.map +1 -1
  33. package/dist/cjs/messages/prune.cjs +13 -1
  34. package/dist/cjs/messages/prune.cjs.map +1 -1
  35. package/dist/cjs/prompts/activityLabel.cjs +51 -11
  36. package/dist/cjs/prompts/activityLabel.cjs.map +1 -1
  37. package/dist/cjs/run.cjs +54 -24
  38. package/dist/cjs/run.cjs.map +1 -1
  39. package/dist/cjs/session/messageSerialization.cjs +6 -0
  40. package/dist/cjs/session/messageSerialization.cjs.map +1 -1
  41. package/dist/cjs/stream.cjs +21 -10
  42. package/dist/cjs/stream.cjs.map +1 -1
  43. package/dist/cjs/summarization/node.cjs +60 -0
  44. package/dist/cjs/summarization/node.cjs.map +1 -1
  45. package/dist/cjs/tools/ToolNode.cjs +253 -24
  46. package/dist/cjs/tools/ToolNode.cjs.map +1 -1
  47. package/dist/cjs/tools/handlers.cjs +1 -1
  48. package/dist/cjs/tools/search/tool.cjs +1 -1
  49. package/dist/cjs/tools/subagent/SubagentExecutor.cjs +1 -1
  50. package/dist/cjs/utils/index.cjs +2 -2
  51. package/dist/esm/graphs/Graph.mjs +48 -14
  52. package/dist/esm/graphs/Graph.mjs.map +1 -1
  53. package/dist/esm/graphs/MultiAgentGraph.mjs +56 -6
  54. package/dist/esm/graphs/MultiAgentGraph.mjs.map +1 -1
  55. package/dist/esm/hitl/askUserQuestion.mjs +3 -2
  56. package/dist/esm/hitl/askUserQuestion.mjs.map +1 -1
  57. package/dist/esm/instrumentation.mjs +18 -48
  58. package/dist/esm/instrumentation.mjs.map +1 -1
  59. package/dist/esm/langfuse.mjs +176 -28
  60. package/dist/esm/langfuse.mjs.map +1 -1
  61. package/dist/esm/langfuseConfig.mjs +10 -1
  62. package/dist/esm/langfuseConfig.mjs.map +1 -1
  63. package/dist/esm/langfuseRuntimeContext.mjs +21 -3
  64. package/dist/esm/langfuseRuntimeContext.mjs.map +1 -1
  65. package/dist/esm/langfuseRuntimeScope.mjs +39 -10
  66. package/dist/esm/langfuseRuntimeScope.mjs.map +1 -1
  67. package/dist/esm/langfuseSpanRegistry.mjs +91 -0
  68. package/dist/esm/langfuseSpanRegistry.mjs.map +1 -0
  69. package/dist/esm/langfuseTraceShaping.mjs +121 -4
  70. package/dist/esm/langfuseTraceShaping.mjs.map +1 -1
  71. package/dist/esm/llm/anthropic/utils/message_inputs.mjs +39 -15
  72. package/dist/esm/llm/anthropic/utils/message_inputs.mjs.map +1 -1
  73. package/dist/esm/llm/bedrock/utils/message_inputs.mjs +25 -5
  74. package/dist/esm/llm/bedrock/utils/message_inputs.mjs.map +1 -1
  75. package/dist/esm/llm/init.mjs +2 -2
  76. package/dist/esm/llm/invoke.mjs +5 -5
  77. package/dist/esm/llm/openai/index.mjs +1 -1
  78. package/dist/esm/main.mjs +8 -8
  79. package/dist/esm/messages/format.mjs +124 -15
  80. package/dist/esm/messages/format.mjs.map +1 -1
  81. package/dist/esm/messages/injected.mjs +10 -1
  82. package/dist/esm/messages/injected.mjs.map +1 -1
  83. package/dist/esm/messages/prune.mjs +13 -1
  84. package/dist/esm/messages/prune.mjs.map +1 -1
  85. package/dist/esm/prompts/activityLabel.mjs +51 -11
  86. package/dist/esm/prompts/activityLabel.mjs.map +1 -1
  87. package/dist/esm/run.mjs +54 -24
  88. package/dist/esm/run.mjs.map +1 -1
  89. package/dist/esm/session/messageSerialization.mjs +6 -0
  90. package/dist/esm/session/messageSerialization.mjs.map +1 -1
  91. package/dist/esm/stream.mjs +21 -10
  92. package/dist/esm/stream.mjs.map +1 -1
  93. package/dist/esm/summarization/node.mjs +60 -0
  94. package/dist/esm/summarization/node.mjs.map +1 -1
  95. package/dist/esm/tools/ToolNode.mjs +254 -25
  96. package/dist/esm/tools/ToolNode.mjs.map +1 -1
  97. package/dist/esm/tools/handlers.mjs +1 -1
  98. package/dist/esm/tools/search/tool.mjs +1 -1
  99. package/dist/esm/tools/subagent/SubagentExecutor.mjs +1 -1
  100. package/dist/esm/utils/index.mjs +2 -2
  101. package/dist/types/graphs/Graph.d.ts +19 -0
  102. package/dist/types/hitl/askUserQuestion.d.ts +11 -1
  103. package/dist/types/langfuse.d.ts +16 -8
  104. package/dist/types/langfuseConfig.d.ts +6 -0
  105. package/dist/types/langfuseRuntimeContext.d.ts +27 -1
  106. package/dist/types/langfuseRuntimeScope.d.ts +17 -2
  107. package/dist/types/langfuseSpanRegistry.d.ts +17 -0
  108. package/dist/types/langfuseTraceShaping.d.ts +2 -1
  109. package/dist/types/llm/anthropic/utils/message_inputs.d.ts +1 -0
  110. package/dist/types/messages/format.d.ts +9 -8
  111. package/dist/types/prompts/activityLabel.d.ts +8 -1
  112. package/dist/types/run.d.ts +1 -1
  113. package/dist/types/session/types.d.ts +1 -0
  114. package/dist/types/tools/ToolNode.d.ts +7 -1
  115. package/dist/types/types/activityLabel.d.ts +8 -0
  116. package/dist/types/types/hitl.d.ts +8 -0
  117. package/dist/types/types/stream.d.ts +19 -0
  118. package/dist/types/types/tools.d.ts +30 -0
  119. package/package.json +7 -4
  120. package/src/__tests__/stream.eagerArgsDivergence.test.ts +753 -0
  121. package/src/graphs/Graph.ts +69 -20
  122. package/src/graphs/MultiAgentGraph.ts +74 -6
  123. package/src/graphs/__tests__/composition.smoke.test.ts +4 -0
  124. package/src/hitl/askUserQuestion.ts +14 -1
  125. package/src/instrumentation.ts +35 -77
  126. package/src/langfuse.ts +320 -43
  127. package/src/langfuseConfig.ts +24 -0
  128. package/src/langfuseRuntimeContext.ts +43 -1
  129. package/src/langfuseRuntimeScope.ts +94 -21
  130. package/src/langfuseSpanRegistry.ts +131 -0
  131. package/src/langfuseTraceShaping.ts +194 -7
  132. package/src/llm/anthropic/utils/message_inputs.ts +70 -19
  133. package/src/llm/anthropic/utils/streaming-tool-input.test.ts +186 -11
  134. package/src/llm/bedrock/utils/message_inputs.test.ts +120 -4
  135. package/src/llm/bedrock/utils/message_inputs.ts +32 -7
  136. package/src/messages/format.ts +222 -50
  137. package/src/messages/formatAgentMessages.test.ts +308 -6
  138. package/src/messages/injected.test.ts +18 -1
  139. package/src/messages/injected.ts +8 -1
  140. package/src/messages/prune.ts +12 -1
  141. package/src/prompts/activityLabel.ts +67 -2
  142. package/src/run.ts +86 -46
  143. package/src/scripts/activity-labels/captured.json +56 -0
  144. package/src/scripts/activity-labels/checks.cjs +205 -0
  145. package/src/scripts/activity-labels/corpus.cjs +473 -0
  146. package/src/scripts/activity-labels/report.cjs +203 -0
  147. package/src/scripts/activity-labels/rescore.cjs +102 -0
  148. package/src/scripts/activity-labels/run.ts +705 -0
  149. package/src/scripts/activity-labels/variants.ts +71 -0
  150. package/src/session/messageSerialization.ts +12 -1
  151. package/src/session/types.ts +1 -0
  152. package/src/specs/activity-label-prompt.test.ts +109 -0
  153. package/src/specs/agent-handoffs.test.ts +306 -0
  154. package/src/specs/langfuse-callbacks.test.ts +456 -0
  155. package/src/specs/langfuse-routing.integration.test.ts +138 -1
  156. package/src/specs/langfuse-span-registry.test.ts +70 -0
  157. package/src/specs/langfuse-trace-shaping.test.ts +294 -0
  158. package/src/specs/prune.test.ts +38 -1
  159. package/src/stream.ts +70 -6
  160. package/src/summarization/__tests__/node.test.ts +188 -0
  161. package/src/summarization/node.ts +72 -0
  162. package/src/tools/ToolNode.ts +400 -9
  163. package/src/tools/__tests__/ToolNode.invalidToolCalls.test.ts +757 -0
  164. package/src/tools/__tests__/hitl.test.ts +58 -0
  165. package/src/types/activityLabel.ts +8 -0
  166. package/src/types/hitl.ts +8 -0
  167. package/src/types/stream.ts +20 -0
  168. package/src/types/tools.ts +35 -1
@@ -35,6 +35,7 @@ import type * as t from '@/types';
35
35
  import { Providers as providers, GraphEvents } from '@/common';
36
36
  import * as events from '@/utils/events';
37
37
  import { HookRegistry, createToolPolicyHook } from '@/hooks';
38
+ import { askUserQuestion } from '@/hitl';
38
39
  import { ToolNode } from '../ToolNode';
39
40
 
40
41
  async function flushAsyncWork(): Promise<void> {
@@ -4354,6 +4355,63 @@ describe('AskUserQuestion — interrupt + resume', () => {
4354
4355
  expect(String(toolMessage!.content)).toBe('staging');
4355
4356
  });
4356
4357
 
4358
+ it('askUserQuestion surfaces the calling tool_call_id on the interrupt payload when the body supplies it', async () => {
4359
+ /**
4360
+ * LangChain stamps the full ToolCall onto the config a `tool(fn, …)`
4361
+ * body receives, so the body can attribute its interrupt to the exact
4362
+ * call. Hosts use `payload.tool_call_id` to stamp the question/answer
4363
+ * onto the right content part when a model emits several ask calls in
4364
+ * one turn — positional guessing mislabels the cards.
4365
+ */
4366
+ const askTool = tool(
4367
+ async (input: { question: string }, config) => {
4368
+ const resolution = askUserQuestion(input, {
4369
+ toolCallId: config.toolCall?.id,
4370
+ });
4371
+ return resolution.answer;
4372
+ },
4373
+ {
4374
+ name: 'ask_user_question',
4375
+ description: 'Ask the user a clarifying question.',
4376
+ schema: z.object({ question: z.string() }),
4377
+ }
4378
+ ) as unknown as StructuredToolInterface;
4379
+
4380
+ const node = new ToolNode({ tools: [askTool] });
4381
+ const graph = buildHITLGraph(node, [
4382
+ {
4383
+ id: 'call_ask_id_1',
4384
+ name: 'ask_user_question',
4385
+ args: { question: 'Which region?' },
4386
+ },
4387
+ ]);
4388
+ const config = { configurable: { thread_id: 'thread-ask-call-id' } };
4389
+
4390
+ const interrupted = await graph.invoke({ messages: [] }, config);
4391
+ if (!isInterrupted<t.HumanInterruptPayload>(interrupted)) {
4392
+ throw new Error('expected interrupt');
4393
+ }
4394
+ const payload = interrupted.__interrupt__[0].value!;
4395
+ if (payload.type !== 'ask_user_question') {
4396
+ throw new Error('expected ask_user_question payload');
4397
+ }
4398
+ expect(payload.tool_call_id).toBe('call_ask_id_1');
4399
+ expect(payload.question.question).toBe('Which region?');
4400
+
4401
+ const resumed = (await resumeGraph(
4402
+ graph,
4403
+ interrupted,
4404
+ { answer: 'us-east' } satisfies t.AskUserQuestionResolution,
4405
+ config
4406
+ )) as MessagesUpdate;
4407
+ const toolMessage = resumed.messages.find(
4408
+ (m): m is ToolMessage =>
4409
+ m._getType() === 'tool' &&
4410
+ (m as ToolMessage).tool_call_id === 'call_ask_id_1'
4411
+ );
4412
+ expect(String(toolMessage!.content)).toBe('us-east');
4413
+ });
4414
+
4357
4415
  it('isAskUserQuestionInterrupt narrows the payload union correctly', async () => {
4358
4416
  const { isAskUserQuestionInterrupt, isToolApprovalInterrupt } =
4359
4417
  await import('@/types/hitl');
@@ -38,6 +38,14 @@ export type RunActivityLabelOptions = {
38
38
  thinkingExcerpts?: string[];
39
39
  /** Assistant's last text before the block (~200 chars), as intent context. */
40
40
  lastAssistantText?: string;
41
+ /**
42
+ * Headers already committed for earlier batches in this run (run order,
43
+ * most recent last). Continuity context: the prompt shows them so the new
44
+ * header extends the run's story instead of restating a line already on
45
+ * screen. Hosts should pass only COMMITTED labels — a pending slot's text
46
+ * is empty and a dropped fill never surfaced to the user.
47
+ */
48
+ previousLabels?: string[];
41
49
  /** Override for the default label system prompt. */
42
50
  prompt?: string;
43
51
  /** Per-entry serialization cap for the prompt. Default 600. */
package/src/types/hitl.ts CHANGED
@@ -158,6 +158,14 @@ export interface AskUserQuestionRequest {
158
158
  export interface AskUserQuestionInterruptPayload {
159
159
  type: 'ask_user_question';
160
160
  question: AskUserQuestionRequest;
161
+ /**
162
+ * The `tool_call_id` of the ask-tool call that raised this interrupt,
163
+ * when the tool body supplied it (see `askUserQuestion`'s `options`).
164
+ * Lets hosts attribute the question — and later the answer — to the
165
+ * exact tool-call content part instead of guessing by position, which
166
+ * mislabels cards when a model emits several ask calls in one turn.
167
+ */
168
+ tool_call_id?: string;
161
169
  }
162
170
 
163
171
  /**
@@ -301,10 +301,30 @@ export type SummaryBoundary = {
301
301
  contentIndex: number;
302
302
  };
303
303
 
304
+ /**
305
+ * Semantic extent of a summary: the first source message compaction retained
306
+ * verbatim, meaning everything before it is covered. Distinct from `boundary`,
307
+ * which records where the block was emitted — a retained recency tail sits
308
+ * *before* the block's own position, so position alone cannot say what the
309
+ * summary replaced.
310
+ *
311
+ * Anchored to the retained side rather than the covered side so that a source
312
+ * message expanding into several messages (a steer splits an assistant entry
313
+ * into pre-steer, steer, and post-steer entries sharing one ID) stays whole:
314
+ * such a message is the retained anchor and survives intact.
315
+ */
316
+ export type SummaryCoverage = {
317
+ retainedFromMessageId: string;
318
+ };
319
+
304
320
  export type SummaryContentBlock = {
305
321
  type: ContentTypes.SUMMARY;
306
322
  content?: MessageContentComplex[];
323
+ /** Injection budget: provider output-token space when usage was reported, plus
324
+ * the wrapper added at injection time. Not comparable with per-message counts
325
+ * such as `indexTokenCountMap`, which are in the consumer's own tokenizer. */
307
326
  tokenCount?: number;
327
+ coverage?: SummaryCoverage;
308
328
  boundary?: SummaryBoundary;
309
329
  summaryVersion?: number;
310
330
  model?: string;
@@ -75,6 +75,28 @@ export type EagerEventToolCallChunkState = {
75
75
  argsText: string;
76
76
  index?: number;
77
77
  lastArgsFragment?: string;
78
+ /**
79
+ * Cumulative length of every observed args fragment — the length of the
80
+ * plain in-order concatenation that LangChain's `AIMessageChunk.concat`
81
+ * performs to build the final tool call. Every reconciliation branch in
82
+ * `mergeToolCallArgsText` (and the repeat-fragment dedupe) produces text no
83
+ * longer than plain concatenation, with equality exactly when every merge
84
+ * was a pure append — so `argsText.length === rawArgsLength` proves
85
+ * `argsText` IS the canonical accumulation the final request will carry.
86
+ * Tracking only the length keeps cumulative/restating streams from
87
+ * retaining every prefix (quadratic growth) while still letting seal-time
88
+ * prestart verify the snapshot.
89
+ */
90
+ rawArgsLength?: number;
91
+ /**
92
+ * The non-empty args fragment carried by a chunk whose explicit adapter
93
+ * seal covered this call. Some adapters restate the finished call's full
94
+ * args on the seal chunk (OpenAI Responses `function_call_arguments.done`);
95
+ * only such a restatement may override the canonical-accumulation check at
96
+ * seal time. Pure-signal seals (Bedrock `contentBlockStop`, `args: ''`)
97
+ * never set this.
98
+ */
99
+ sealedArgsFragment?: string;
78
100
  };
79
101
 
80
102
  export type ToolNodeOptions = {
@@ -156,6 +178,14 @@ export type ToolNodeOptions = {
156
178
  eagerEventToolExecutions?: Map<string, EagerEventToolExecution>;
157
179
  /** Shared per-run per-tool turn counter used by eager and normal event dispatch. */
158
180
  eagerEventToolUsageCount?: Map<string, number>;
181
+ /**
182
+ * Shared per-run circuit breaker for eager prestart. When a prestarted
183
+ * execution's args turn out to differ from the final request ("changed
184
+ * after eager execution started"), the ToolNode adds the tool name here
185
+ * and the stream handler stops prestarting that tool for the remainder of
186
+ * the run, so the model's retry executes normally instead of looping.
187
+ */
188
+ eagerEventToolSuppressions?: Set<string>;
159
189
  /**
160
190
  * Hook registry for PreToolUse/PostToolUse/PostToolUseFailure/
161
191
  * PermissionDenied lifecycle hooks. Fires for **every** tool the
@@ -241,7 +271,11 @@ export type ToolEndEvent = {
241
271
  * present (see `ProcessedToolCall.outcome`) so `ON_RUN_STEP_COMPLETED`
242
272
  * consumers can read it without an unsafe cast.
243
273
  */
244
- tool_call: ToolCall & { output?: string; progress?: number; outcome?: string };
274
+ tool_call: ToolCall & {
275
+ output?: string;
276
+ progress?: number;
277
+ outcome?: string;
278
+ };
245
279
  /** The content index of the tool call */
246
280
  index: number;
247
281
  type?: 'tool_call';