@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
@@ -43,7 +43,16 @@ function convertInjectedMessages(messages) {
43
43
  const converted = [];
44
44
  for (const msg of messages) {
45
45
  if (isEmptyInjectedContent(msg.content)) continue;
46
- const additional_kwargs = { role: msg.role };
46
+ /** Provenance, recorded here because this is the only place that knows it.
47
+ * `isMeta` and `source` are both optional on `InjectedMessage`, so a bare
48
+ * entry is otherwise indistinguishable from a message replayed out of the
49
+ * payload — and downstream consumers such as compaction coverage need to
50
+ * know that this message has no persisted source ID to name. Kept separate
51
+ * from `isMeta`, which carries UI and cache meaning of its own. */
52
+ const additional_kwargs = {
53
+ role: msg.role,
54
+ injected: true
55
+ };
47
56
  if (msg.isMeta != null) additional_kwargs.isMeta = msg.isMeta;
48
57
  if (msg.source != null) additional_kwargs.source = msg.source;
49
58
  if (msg.skillName != null) additional_kwargs.skillName = msg.skillName;
@@ -1 +1 @@
1
- {"version":3,"file":"injected.cjs","names":["HumanMessage","toLangChainContent"],"sources":["../../../src/messages/injected.ts"],"sourcesContent":["// src/messages/injected.ts\nimport { HumanMessage } from '@langchain/core/messages';\nimport type { BaseMessage } from '@langchain/core/messages';\nimport type { InjectedMessage } from '@/types/tools';\nimport { ContentTypes } from '@/common';\nimport { toLangChainContent } from './langchain';\n\n/**\n * Converts `InjectedMessage` instances to LangChain `HumanMessage` objects.\n * Both 'user' and 'system' roles become `HumanMessage` to avoid provider\n * rejections (Anthropic/Google reject non-leading SystemMessages). The\n * original role is preserved in `additional_kwargs` for downstream consumers.\n *\n * Shared by both injection boundaries — `ToolNode`'s tool-batch dispatch and\n * `StandardGraph`'s preempt-boundary dispatch. Keeping one implementation is\n * load-bearing rather than tidy: the provider-safety argument for sealing a\n * stream mid-generation rests on the injected turn having byte-identical\n * shape to the one the already-shipped tool boundary emits, so the two sites\n * must not be able to drift.\n */\n/**\n * True when an entry carries nothing a provider will accept as a turn.\n *\n * The public `InjectedMessage` type admits `content: ''` and `content: []`,\n * and a host hook is free to return one. Converting it anyway produces a\n * `HumanMessage` that passes the caller's `length > 0` test, so the graph\n * resumes and sends a trailing EMPTY user turn — which Anthropic and other\n * strict providers reject outright, turning a cooperative seal into a failed\n * run. Permissive providers merely burn a model call.\n *\n * Whitespace counts as empty, matching the standard `canSealPreempt` applies\n * to the assistant side of the same pair. Non-text blocks count as content: a\n * media-only steer is a real turn and must survive.\n */\nfunction isEmptyInjectedContent(content: InjectedMessage['content']): boolean {\n if (typeof content === 'string') {\n return content.trim() === '';\n }\n if (content.length === 0) {\n return true;\n }\n return content.every((block) => {\n if (block.type !== ContentTypes.TEXT) {\n return false;\n }\n const text = block[ContentTypes.TEXT];\n return typeof text !== 'string' || text.trim() === '';\n });\n}\n\nexport function convertInjectedMessages(\n messages: InjectedMessage[]\n): BaseMessage[] {\n const converted: BaseMessage[] = [];\n for (const msg of messages) {\n if (isEmptyInjectedContent(msg.content)) {\n continue;\n }\n const additional_kwargs: Record<string, unknown> = {\n role: msg.role,\n };\n if (msg.isMeta != null) additional_kwargs.isMeta = msg.isMeta;\n if (msg.source != null) additional_kwargs.source = msg.source;\n if (msg.skillName != null) additional_kwargs.skillName = msg.skillName;\n\n converted.push(\n new HumanMessage({\n content: toLangChainContent(msg.content),\n additional_kwargs,\n })\n );\n }\n return converted;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAkCA,SAAS,uBAAuB,SAA8C;CAC5E,IAAI,OAAO,YAAY,UACrB,OAAO,QAAQ,KAAK,MAAM;CAE5B,IAAI,QAAQ,WAAW,GACrB,OAAO;CAET,OAAO,QAAQ,OAAO,UAAU;EAC9B,IAAI,MAAM,SAAA,QACR,OAAO;EAET,MAAM,OAAO,MAAA;EACb,OAAO,OAAO,SAAS,YAAY,KAAK,KAAK,MAAM;CACrD,CAAC;AACH;AAEA,SAAgB,wBACd,UACe;CACf,MAAM,YAA2B,CAAC;CAClC,KAAK,MAAM,OAAO,UAAU;EAC1B,IAAI,uBAAuB,IAAI,OAAO,GACpC;EAEF,MAAM,oBAA6C,EACjD,MAAM,IAAI,KACZ;EACA,IAAI,IAAI,UAAU,MAAM,kBAAkB,SAAS,IAAI;EACvD,IAAI,IAAI,UAAU,MAAM,kBAAkB,SAAS,IAAI;EACvD,IAAI,IAAI,aAAa,MAAM,kBAAkB,YAAY,IAAI;EAE7D,UAAU,KACR,IAAIA,yBAAAA,aAAa;GACf,SAASC,kBAAAA,mBAAmB,IAAI,OAAO;GACvC;EACF,CAAC,CACH;CACF;CACA,OAAO;AACT"}
1
+ {"version":3,"file":"injected.cjs","names":["HumanMessage","toLangChainContent"],"sources":["../../../src/messages/injected.ts"],"sourcesContent":["// src/messages/injected.ts\nimport { HumanMessage } from '@langchain/core/messages';\nimport type { BaseMessage } from '@langchain/core/messages';\nimport type { InjectedMessage } from '@/types/tools';\nimport { toLangChainContent } from './langchain';\nimport { ContentTypes } from '@/common';\n\n/**\n * Converts `InjectedMessage` instances to LangChain `HumanMessage` objects.\n * Both 'user' and 'system' roles become `HumanMessage` to avoid provider\n * rejections (Anthropic/Google reject non-leading SystemMessages). The\n * original role is preserved in `additional_kwargs` for downstream consumers.\n *\n * Shared by both injection boundaries — `ToolNode`'s tool-batch dispatch and\n * `StandardGraph`'s preempt-boundary dispatch. Keeping one implementation is\n * load-bearing rather than tidy: the provider-safety argument for sealing a\n * stream mid-generation rests on the injected turn having byte-identical\n * shape to the one the already-shipped tool boundary emits, so the two sites\n * must not be able to drift.\n */\n/**\n * True when an entry carries nothing a provider will accept as a turn.\n *\n * The public `InjectedMessage` type admits `content: ''` and `content: []`,\n * and a host hook is free to return one. Converting it anyway produces a\n * `HumanMessage` that passes the caller's `length > 0` test, so the graph\n * resumes and sends a trailing EMPTY user turn — which Anthropic and other\n * strict providers reject outright, turning a cooperative seal into a failed\n * run. Permissive providers merely burn a model call.\n *\n * Whitespace counts as empty, matching the standard `canSealPreempt` applies\n * to the assistant side of the same pair. Non-text blocks count as content: a\n * media-only steer is a real turn and must survive.\n */\nfunction isEmptyInjectedContent(content: InjectedMessage['content']): boolean {\n if (typeof content === 'string') {\n return content.trim() === '';\n }\n if (content.length === 0) {\n return true;\n }\n return content.every((block) => {\n if (block.type !== ContentTypes.TEXT) {\n return false;\n }\n const text = block[ContentTypes.TEXT];\n return typeof text !== 'string' || text.trim() === '';\n });\n}\n\nexport function convertInjectedMessages(\n messages: InjectedMessage[]\n): BaseMessage[] {\n const converted: BaseMessage[] = [];\n for (const msg of messages) {\n if (isEmptyInjectedContent(msg.content)) {\n continue;\n }\n /** Provenance, recorded here because this is the only place that knows it.\n * `isMeta` and `source` are both optional on `InjectedMessage`, so a bare\n * entry is otherwise indistinguishable from a message replayed out of the\n * payload — and downstream consumers such as compaction coverage need to\n * know that this message has no persisted source ID to name. Kept separate\n * from `isMeta`, which carries UI and cache meaning of its own. */\n const additional_kwargs: Record<string, unknown> = {\n role: msg.role,\n injected: true,\n };\n if (msg.isMeta != null) additional_kwargs.isMeta = msg.isMeta;\n if (msg.source != null) additional_kwargs.source = msg.source;\n if (msg.skillName != null) additional_kwargs.skillName = msg.skillName;\n\n converted.push(\n new HumanMessage({\n content: toLangChainContent(msg.content),\n additional_kwargs,\n })\n );\n }\n return converted;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAkCA,SAAS,uBAAuB,SAA8C;CAC5E,IAAI,OAAO,YAAY,UACrB,OAAO,QAAQ,KAAK,MAAM;CAE5B,IAAI,QAAQ,WAAW,GACrB,OAAO;CAET,OAAO,QAAQ,OAAO,UAAU;EAC9B,IAAI,MAAM,SAAA,QACR,OAAO;EAET,MAAM,OAAO,MAAA;EACb,OAAO,OAAO,SAAS,YAAY,KAAK,KAAK,MAAM;CACrD,CAAC;AACH;AAEA,SAAgB,wBACd,UACe;CACf,MAAM,YAA2B,CAAC;CAClC,KAAK,MAAM,OAAO,UAAU;EAC1B,IAAI,uBAAuB,IAAI,OAAO,GACpC;;;;;;;EAQF,MAAM,oBAA6C;GACjD,MAAM,IAAI;GACV,UAAU;EACZ;EACA,IAAI,IAAI,UAAU,MAAM,kBAAkB,SAAS,IAAI;EACvD,IAAI,IAAI,UAAU,MAAM,kBAAkB,SAAS,IAAI;EACvD,IAAI,IAAI,aAAa,MAAM,kBAAkB,YAAY,IAAI;EAE7D,UAAU,KACR,IAAIA,yBAAAA,aAAa;GACf,SAASC,kBAAAA,mBAAmB,IAAI,OAAO;GACvC;EACF,CAAC,CACH;CACF;CACA,OAAO;AACT"}
@@ -716,7 +716,19 @@ function createBoundedTruncationValue(preview, originalChars, maxChars) {
716
716
  if (JSON.stringify({
717
717
  _truncated: "",
718
718
  _originalChars: originalChars
719
- }).length > normalizedMaxChars) return null;
719
+ }).length > normalizedMaxChars)
720
+ /**
721
+ * Even the empty envelope overflows the cap, so no preview survives —
722
+ * but the result must still be a JSON OBJECT, never `null`. This value
723
+ * replaces a `tool_use.input` / tool-call `args` on messages that are
724
+ * mutated IN PLACE into graph state (`preFlightTruncateToolCallInputs`),
725
+ * and Anthropic rejects a replayed non-object input with a 400
726
+ * (`tool_use.input: Input should be an object`). Observed live: a tight
727
+ * summarization budget shrank the cap below the envelope, nulled a
728
+ * retained calculator call's input and args, and the next model call
729
+ * failed on replay.
730
+ */
731
+ return {};
720
732
  let low = 0;
721
733
  let high = Math.min(preview.length, normalizedMaxChars);
722
734
  while (low < high) {