@librechat/agents 3.3.4 → 3.3.5

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 (159) hide show
  1. package/dist/cjs/common/constants.cjs +21 -0
  2. package/dist/cjs/common/constants.cjs.map +1 -1
  3. package/dist/cjs/events.cjs +10 -1
  4. package/dist/cjs/events.cjs.map +1 -1
  5. package/dist/cjs/graphs/Graph.cjs +456 -7
  6. package/dist/cjs/graphs/Graph.cjs.map +1 -1
  7. package/dist/cjs/hooks/HookRegistry.cjs +25 -0
  8. package/dist/cjs/hooks/HookRegistry.cjs.map +1 -1
  9. package/dist/cjs/hooks/index.cjs +12 -0
  10. package/dist/cjs/hooks/index.cjs.map +1 -1
  11. package/dist/cjs/hooks/types.cjs +1 -0
  12. package/dist/cjs/hooks/types.cjs.map +1 -1
  13. package/dist/cjs/langfuse.cjs +8 -0
  14. package/dist/cjs/langfuse.cjs.map +1 -1
  15. package/dist/cjs/llm/bedrock/utils/message_inputs.cjs +1 -3
  16. package/dist/cjs/llm/bedrock/utils/message_inputs.cjs.map +1 -1
  17. package/dist/cjs/llm/invoke.cjs +268 -3
  18. package/dist/cjs/llm/invoke.cjs.map +1 -1
  19. package/dist/cjs/llm/preempt.cjs +132 -0
  20. package/dist/cjs/llm/preempt.cjs.map +1 -0
  21. package/dist/cjs/main.cjs +17 -3
  22. package/dist/cjs/messages/alternation.cjs +92 -0
  23. package/dist/cjs/messages/alternation.cjs.map +1 -0
  24. package/dist/cjs/messages/format.cjs +72 -0
  25. package/dist/cjs/messages/format.cjs.map +1 -1
  26. package/dist/cjs/messages/handoffCue.cjs +63 -0
  27. package/dist/cjs/messages/handoffCue.cjs.map +1 -0
  28. package/dist/cjs/messages/index.cjs +3 -0
  29. package/dist/cjs/messages/injected.cjs +60 -0
  30. package/dist/cjs/messages/injected.cjs.map +1 -0
  31. package/dist/cjs/run.cjs +80 -7
  32. package/dist/cjs/run.cjs.map +1 -1
  33. package/dist/cjs/session/handlers.cjs +18 -9
  34. package/dist/cjs/session/handlers.cjs.map +1 -1
  35. package/dist/cjs/stream.cjs +28 -7
  36. package/dist/cjs/stream.cjs.map +1 -1
  37. package/dist/cjs/tools/ToolNode.cjs +3 -23
  38. package/dist/cjs/tools/ToolNode.cjs.map +1 -1
  39. package/dist/cjs/tools/search/keenable-scraper.cjs +90 -0
  40. package/dist/cjs/tools/search/keenable-scraper.cjs.map +1 -0
  41. package/dist/cjs/tools/search/tool.cjs +9 -1
  42. package/dist/cjs/tools/search/tool.cjs.map +1 -1
  43. package/dist/esm/common/constants.mjs +19 -1
  44. package/dist/esm/common/constants.mjs.map +1 -1
  45. package/dist/esm/events.mjs +10 -1
  46. package/dist/esm/events.mjs.map +1 -1
  47. package/dist/esm/graphs/Graph.mjs +455 -6
  48. package/dist/esm/graphs/Graph.mjs.map +1 -1
  49. package/dist/esm/hooks/HookRegistry.mjs +25 -0
  50. package/dist/esm/hooks/HookRegistry.mjs.map +1 -1
  51. package/dist/esm/hooks/index.mjs +12 -1
  52. package/dist/esm/hooks/index.mjs.map +1 -1
  53. package/dist/esm/hooks/types.mjs +1 -0
  54. package/dist/esm/hooks/types.mjs.map +1 -1
  55. package/dist/esm/langfuse.mjs +9 -1
  56. package/dist/esm/langfuse.mjs.map +1 -1
  57. package/dist/esm/llm/bedrock/utils/message_inputs.mjs +1 -3
  58. package/dist/esm/llm/bedrock/utils/message_inputs.mjs.map +1 -1
  59. package/dist/esm/llm/invoke.mjs +270 -6
  60. package/dist/esm/llm/invoke.mjs.map +1 -1
  61. package/dist/esm/llm/preempt.mjs +131 -0
  62. package/dist/esm/llm/preempt.mjs.map +1 -0
  63. package/dist/esm/main.mjs +8 -5
  64. package/dist/esm/messages/alternation.mjs +91 -0
  65. package/dist/esm/messages/alternation.mjs.map +1 -0
  66. package/dist/esm/messages/format.mjs +72 -0
  67. package/dist/esm/messages/format.mjs.map +1 -1
  68. package/dist/esm/messages/handoffCue.mjs +61 -0
  69. package/dist/esm/messages/handoffCue.mjs.map +1 -0
  70. package/dist/esm/messages/index.mjs +3 -0
  71. package/dist/esm/messages/injected.mjs +60 -0
  72. package/dist/esm/messages/injected.mjs.map +1 -0
  73. package/dist/esm/run.mjs +80 -7
  74. package/dist/esm/run.mjs.map +1 -1
  75. package/dist/esm/session/handlers.mjs +19 -10
  76. package/dist/esm/session/handlers.mjs.map +1 -1
  77. package/dist/esm/stream.mjs +27 -8
  78. package/dist/esm/stream.mjs.map +1 -1
  79. package/dist/esm/tools/ToolNode.mjs +3 -23
  80. package/dist/esm/tools/ToolNode.mjs.map +1 -1
  81. package/dist/esm/tools/search/keenable-scraper.mjs +88 -0
  82. package/dist/esm/tools/search/keenable-scraper.mjs.map +1 -0
  83. package/dist/esm/tools/search/tool.mjs +9 -1
  84. package/dist/esm/tools/search/tool.mjs.map +1 -1
  85. package/dist/types/common/constants.d.ts +18 -0
  86. package/dist/types/graphs/Graph.d.ts +138 -1
  87. package/dist/types/hooks/HookRegistry.d.ts +15 -0
  88. package/dist/types/hooks/index.d.ts +12 -1
  89. package/dist/types/hooks/types.d.ts +45 -6
  90. package/dist/types/index.d.ts +1 -1
  91. package/dist/types/llm/invoke.d.ts +7 -0
  92. package/dist/types/llm/preempt.d.ts +40 -0
  93. package/dist/types/messages/alternation.d.ts +23 -0
  94. package/dist/types/messages/handoffCue.d.ts +40 -0
  95. package/dist/types/messages/index.d.ts +3 -0
  96. package/dist/types/messages/injected.d.ts +3 -0
  97. package/dist/types/run.d.ts +7 -0
  98. package/dist/types/stream.d.ts +14 -0
  99. package/dist/types/tools/ToolNode.d.ts +0 -7
  100. package/dist/types/tools/search/keenable-scraper.d.ts +15 -0
  101. package/dist/types/tools/search/types.d.ts +31 -2
  102. package/dist/types/types/graph.d.ts +8 -1
  103. package/dist/types/types/run.d.ts +65 -0
  104. package/dist/types/types/stream.d.ts +0 -25
  105. package/package.json +1 -1
  106. package/src/{splitStream.test.ts → aggregator.test.ts} +59 -666
  107. package/src/common/constants.ts +21 -0
  108. package/src/events.ts +15 -1
  109. package/src/graphs/Graph.ts +568 -3
  110. package/src/graphs/__tests__/Graph.contextOverflow.test.ts +18 -7
  111. package/src/graphs/__tests__/Graph.preemptSignal.test.ts +126 -0
  112. package/src/hooks/HookRegistry.ts +40 -0
  113. package/src/hooks/__tests__/preemptBoundary.test.ts +152 -0
  114. package/src/hooks/index.ts +16 -2
  115. package/src/hooks/types.ts +47 -3
  116. package/src/index.ts +1 -1
  117. package/src/langfuse.ts +26 -1
  118. package/src/llm/bedrock/utils/message_inputs.test.ts +82 -0
  119. package/src/llm/bedrock/utils/message_inputs.ts +14 -11
  120. package/src/llm/bedrock/utils/toolResultCachePoint.test.ts +15 -0
  121. package/src/llm/invoke.alternation.test.ts +87 -0
  122. package/src/llm/invoke.handoffCue.test.ts +112 -0
  123. package/src/llm/invoke.ts +417 -8
  124. package/src/llm/preempt.test.ts +323 -0
  125. package/src/llm/preempt.ts +178 -0
  126. package/src/messages/alternation.test.ts +212 -0
  127. package/src/messages/alternation.ts +112 -0
  128. package/src/messages/format.ts +91 -0
  129. package/src/messages/formatAgentMessages.steer.test.ts +267 -0
  130. package/src/messages/handoffCue.test.ts +96 -0
  131. package/src/messages/handoffCue.ts +78 -0
  132. package/src/messages/index.ts +3 -0
  133. package/src/messages/injected.test.ts +90 -0
  134. package/src/messages/injected.ts +74 -0
  135. package/src/run.ts +91 -6
  136. package/src/scripts/preempt-probe.ts +330 -0
  137. package/src/scripts/preempt-scenarios.ts +388 -0
  138. package/src/session/handlers.ts +32 -12
  139. package/src/specs/handoffCue.test.ts +165 -0
  140. package/src/specs/langfuse-callbacks.test.ts +352 -2
  141. package/src/specs/preemptSeal.test.ts +309 -0
  142. package/src/stream.dispatch.test.ts +63 -0
  143. package/src/stream.ts +36 -8
  144. package/src/tools/ToolNode.ts +3 -31
  145. package/src/tools/search/keenable-scraper.test.ts +153 -0
  146. package/src/tools/search/keenable-scraper.ts +137 -0
  147. package/src/tools/search/tool.ts +13 -2
  148. package/src/tools/search/types.ts +50 -3
  149. package/src/types/graph.ts +12 -1
  150. package/src/types/run.ts +67 -0
  151. package/src/types/stream.ts +0 -41
  152. package/dist/cjs/splitStream.cjs +0 -151
  153. package/dist/cjs/splitStream.cjs.map +0 -1
  154. package/dist/esm/splitStream.mjs +0 -150
  155. package/dist/esm/splitStream.mjs.map +0 -1
  156. package/dist/types/mockStream.d.ts +0 -32
  157. package/dist/types/splitStream.d.ts +0 -37
  158. package/src/mockStream.ts +0 -99
  159. package/src/splitStream.ts +0 -234
@@ -182,6 +182,19 @@ function formatToolCallOutput(output) {
182
182
  return require_toolContent.compactToolContent(output, require_truncation.HARD_MAX_TOOL_RESULT_CHARS).content;
183
183
  }
184
184
  /**
185
+ * Content for the synthetic assistant turn that separates a trailing steer
186
+ * from the next user turn. Non-empty by necessity — see the push site.
187
+ */
188
+ const STEER_ANCHOR_PLACEHOLDER = "_";
189
+ /**
190
+ * True when an assistant message replayed as a steer and nothing followed it,
191
+ * so the emitted run ends on the steer's `HumanMessage`.
192
+ */
193
+ function endsWithSteerMessage(formatted) {
194
+ if (formatted.length === 0) return false;
195
+ return formatted[formatted.length - 1].additional_kwargs.source === "steer";
196
+ }
197
+ /**
185
198
  * Helper function to format an assistant message
186
199
  * @param message The message to format
187
200
  * @param options Optional formatting options
@@ -617,6 +630,27 @@ function extractSkillName(args) {
617
630
  */
618
631
  const formatAgentMessages = (payload, indexTokenCountMap, tools, skills, options) => {
619
632
  const messages = [];
633
+ /**
634
+ * A steer ended the previous payload entry, so the next message emitted —
635
+ * whichever entry finally produces one — must be separated from it by an
636
+ * assistant turn. Held rather than emitted so an entry that produces
637
+ * nothing cannot leave the anchor stranded as the final turn.
638
+ */
639
+ let pendingSteerAnchor = false;
640
+ /**
641
+ * Emits the deferred anchor ahead of `next` — the message about to be
642
+ * pushed. When that message is itself an assistant turn, it already IS the
643
+ * separation the anchor exists to synthesize, so the intent is simply
644
+ * discharged: emitting the placeholder anyway would put two assistant turns
645
+ * back to back, which strict-alternation providers can reject and nothing downstream
646
+ * repairs (`coalesceAdjacentUserTurns` merges user turns only).
647
+ */
648
+ const flushSteerAnchor = (next) => {
649
+ if (!pendingSteerAnchor) return;
650
+ pendingSteerAnchor = false;
651
+ if (next.role === "assistant") return;
652
+ messages.push(withMessageRole(new _langchain_core_messages.AIMessage({ content: STEER_ANCHOR_PLACEHOLDER }), "assistant"));
653
+ };
620
654
  const updatedIndexTokenCountMap = {};
621
655
  let boundaryTokenAdjustment;
622
656
  const indexMapping = {};
@@ -652,6 +686,7 @@ const formatAgentMessages = (payload, indexTokenCountMap, tools, skills, options
652
686
  langChain: true
653
687
  });
654
688
  if (sourceMessageId != null && sourceMessageId !== "") formattedMessage.id = sourceMessageId;
689
+ flushSteerAnchor(formattedMessage);
655
690
  messages.push(formattedMessage);
656
691
  indexMapping[i] = [messages.length - 1];
657
692
  continue;
@@ -755,7 +790,44 @@ const formatAgentMessages = (payload, indexTokenCountMap, tools, skills, options
755
790
  provider: options?.provider
756
791
  });
757
792
  if (sourceMessageId != null && sourceMessageId !== "") for (const formattedMessage of formattedMessages) formattedMessage.id = sourceMessageId;
793
+ /**
794
+ * A steer that ends an assistant message leaves the replay on a
795
+ * `HumanMessage`. The next payload message is itself a user turn, so the
796
+ * sequence would reach the provider as two adjacent user turns — rejected
797
+ * by strict-alternation providers. Anchor it with a placeholder assistant
798
+ * turn.
799
+ *
800
+ * The placeholder must be NON-EMPTY. A string-content assistant message
801
+ * with no tool calls passes through `_convertMessagesToAnthropicPayload`
802
+ * verbatim — the empty-text repair there only covers array content and
803
+ * tool-call turns — so an empty anchor would reach Anthropic as
804
+ * `{role: 'assistant', content: ''}` and trade one invalid sequence for
805
+ * another. Same single-underscore convention the Anthropic converter
806
+ * already uses when it has to synthesize a non-empty block.
807
+ *
808
+ * Deferred rather than decided by lookahead. `i < payload.length - 1` only
809
+ * proves a later ENTRY exists, not that it EMITS: entries with empty
810
+ * content, and entries dropped by `applySummaryBoundary`, are skipped
811
+ * silently. A trailing steer followed only by those would get the anchor
812
+ * as the FINAL turn — an assistant prefill with no request after it, which
813
+ * the model may simply never answer. So the intent is recorded and flushed
814
+ * only when a message actually follows.
815
+ *
816
+ * Pushed AFTER the id stamping above, deliberately. `messagesStateReducer`
817
+ * treats a repeated id as replace-in-place, so an anchor carrying the
818
+ * shared `sourceMessageId` would overwrite the steer it exists to protect.
819
+ * Left unstamped, it reaches the reducer with a null id and is assigned a
820
+ * fresh one. `endsWithSteerMessage` reads only `additional_kwargs.source`,
821
+ * so the deferral cannot change which messages get anchored.
822
+ */
823
+ /**
824
+ * Guarded on emission: an assistant entry whose blocks all filtered away
825
+ * emits nothing, and flushing for it would strand the anchor as the final
826
+ * turn — the pending flag stays set for whichever entry emits next.
827
+ */
828
+ if (formattedMessages.length > 0) flushSteerAnchor(formattedMessages[0]);
758
829
  messages.push(...formattedMessages);
830
+ if (endsWithSteerMessage(formattedMessages)) pendingSteerAnchor = true;
759
831
  const endMessageIndex = messages.length;
760
832
  if (pendingSkillNames?.size != null && pendingSkillNames.size > 0) {
761
833
  const skipSkillBodyNames = options?.skipSkillBodyNames;