@librechat/agents 3.2.46 → 3.2.52

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 (57) hide show
  1. package/dist/cjs/graphs/MultiAgentGraph.cjs +6 -6
  2. package/dist/cjs/graphs/MultiAgentGraph.cjs.map +1 -1
  3. package/dist/cjs/llm/anthropic/index.cjs +4 -3
  4. package/dist/cjs/llm/anthropic/index.cjs.map +1 -1
  5. package/dist/cjs/llm/anthropic/utils/tools.cjs +2 -1
  6. package/dist/cjs/llm/anthropic/utils/tools.cjs.map +1 -1
  7. package/dist/cjs/llm/bedrock/cachePoints.cjs +28 -0
  8. package/dist/cjs/llm/bedrock/cachePoints.cjs.map +1 -0
  9. package/dist/cjs/llm/bedrock/index.cjs +10 -1
  10. package/dist/cjs/llm/bedrock/index.cjs.map +1 -1
  11. package/dist/cjs/llm/openrouter/index.cjs.map +1 -1
  12. package/dist/cjs/run.cjs +21 -3
  13. package/dist/cjs/run.cjs.map +1 -1
  14. package/dist/cjs/tools/ToolNode.cjs +26 -5
  15. package/dist/cjs/tools/ToolNode.cjs.map +1 -1
  16. package/dist/esm/graphs/MultiAgentGraph.mjs +7 -7
  17. package/dist/esm/graphs/MultiAgentGraph.mjs.map +1 -1
  18. package/dist/esm/llm/anthropic/index.mjs +4 -3
  19. package/dist/esm/llm/anthropic/index.mjs.map +1 -1
  20. package/dist/esm/llm/anthropic/utils/tools.mjs +2 -1
  21. package/dist/esm/llm/anthropic/utils/tools.mjs.map +1 -1
  22. package/dist/esm/llm/bedrock/cachePoints.mjs +28 -0
  23. package/dist/esm/llm/bedrock/cachePoints.mjs.map +1 -0
  24. package/dist/esm/llm/bedrock/index.mjs +10 -1
  25. package/dist/esm/llm/bedrock/index.mjs.map +1 -1
  26. package/dist/esm/llm/openrouter/index.mjs.map +1 -1
  27. package/dist/esm/run.mjs +21 -3
  28. package/dist/esm/run.mjs.map +1 -1
  29. package/dist/esm/tools/ToolNode.mjs +26 -5
  30. package/dist/esm/tools/ToolNode.mjs.map +1 -1
  31. package/dist/types/llm/anthropic/utils/tools.d.ts +1 -1
  32. package/dist/types/llm/bedrock/cachePoints.d.ts +9 -0
  33. package/dist/types/run.d.ts +15 -1
  34. package/dist/types/tools/ToolNode.d.ts +9 -2
  35. package/package.json +16 -21
  36. package/src/graphs/MultiAgentGraph.ts +7 -12
  37. package/src/llm/anthropic/index.ts +13 -2
  38. package/src/llm/anthropic/inherited-content-utils.spec.ts +244 -0
  39. package/src/llm/anthropic/inherited-stream-events.spec.ts +752 -0
  40. package/src/llm/anthropic/inherited-strict.spec.ts +302 -0
  41. package/src/llm/anthropic/llm.spec.ts +65 -0
  42. package/src/llm/anthropic/utils/tools.ts +7 -1
  43. package/src/llm/bedrock/cachePoints.ts +86 -0
  44. package/src/llm/bedrock/index.ts +9 -0
  45. package/src/llm/bedrock/inherited-cache.spec.ts +144 -0
  46. package/src/llm/bedrock/inherited.spec.ts +724 -0
  47. package/src/llm/google/inherited-stream-events.spec.ts +350 -0
  48. package/src/llm/openai/inherited-deepseek.spec.ts +347 -0
  49. package/src/llm/openai/inherited-xai.spec.ts +416 -0
  50. package/src/llm/openai/llm.spec.ts +1568 -0
  51. package/src/llm/openrouter/index.ts +1 -3
  52. package/src/llm/vertexai/inherited-stream-events.spec.ts +271 -0
  53. package/src/run.ts +31 -3
  54. package/src/scripts/handoff-test.ts +5 -6
  55. package/src/tools/ToolNode.ts +43 -5
  56. package/src/tools/__tests__/ToolNode.runtimeState.test.ts +120 -0
  57. package/src/tools/__tests__/hitl.test.ts +162 -0
@@ -761,6 +761,54 @@ describe('Run integration — HITL fallback checkpointer + resume', () => {
761
761
  expect(run.Graph?.compileOptions?.checkpointer).toBe(hostCheckpointer);
762
762
  });
763
763
 
764
+ it('Run.resume forwards update + goto into the resume Command (langgraph 1.4.5)', async () => {
765
+ const { Run } = await import('@/run');
766
+ const { Providers } = await import('@/common');
767
+
768
+ const run = await Run.create<t.IState>({
769
+ runId: 'hitl-resume-update-goto',
770
+ graphConfig: {
771
+ type: 'standard',
772
+ agents: [
773
+ {
774
+ agentId: 'a',
775
+ provider: Providers.OPENAI,
776
+ clientOptions: { modelName: 'gpt-4o-mini', apiKey: 'test-key' },
777
+ instructions: 'noop',
778
+ maxContextTokens: 8000,
779
+ },
780
+ ],
781
+ },
782
+ });
783
+
784
+ const spy = jest.spyOn(run, 'processStream').mockResolvedValue(undefined);
785
+
786
+ const decision = [{ type: 'approve' as const }];
787
+ const update = { messages: [new AIMessage('host-edit')] };
788
+ await run.resume(
789
+ decision,
790
+ { version: 'v1', configurable: { thread_id: 't' } },
791
+ undefined,
792
+ { update, goto: 'agent' }
793
+ );
794
+
795
+ const cmd = spy.mock.calls[0]?.[0] as Command;
796
+ expect(cmd).toBeInstanceOf(Command);
797
+ // No interrupt was captured, so the resume value passes through unscoped.
798
+ expect(cmd.resume).toEqual(decision);
799
+ expect(cmd.update).toEqual(update);
800
+ expect(cmd.goto).toEqual(['agent']); // langgraph normalizes goto to an array
801
+
802
+ // Backward-compat: omitting commandOptions leaves update unset, goto empty.
803
+ await run.resume(decision, {
804
+ version: 'v1',
805
+ configurable: { thread_id: 't' },
806
+ });
807
+ const cmd2 = spy.mock.calls[1]?.[0] as Command;
808
+ expect(cmd2.update).toBeUndefined();
809
+ expect(cmd2.goto).toEqual([]);
810
+ });
811
+
764
812
  it('re-exports langgraph HITL primitives from the SDK barrel for host use', async () => {
765
813
  const indexExports = await import('@/index');
766
814
  expect(indexExports.MemorySaver).toBe(MemorySaver);
@@ -903,6 +951,120 @@ describe('Run integration — HITL fallback checkpointer + resume', () => {
903
951
  expect(run.getHaltReason()).toBeUndefined();
904
952
  });
905
953
 
954
+ it('Run.resume() forwards `update` so langgraph applies the channel edit through streamEvents', async () => {
955
+ /** Executing proof (not a spy): interrupt on a tool, resume with an
956
+ * injected message via `update`, then read the committed checkpoint.
957
+ * langgraph 1.4.5 maps an INPUT resume Command through `mapCommand`
958
+ * (pregel/io.js), which applies resume AND update AND goto, so the
959
+ * injected message must land in the messages channel. */
960
+ jest
961
+ .spyOn(events, 'safeDispatchCustomEvent')
962
+ .mockImplementation(async (event, data) => {
963
+ if (event !== 'on_tool_execute') {
964
+ return;
965
+ }
966
+ const request = data as {
967
+ toolCalls: t.ToolCallRequest[];
968
+ resolve: (r: t.ToolExecuteResult[]) => void;
969
+ };
970
+ request.resolve(
971
+ request.toolCalls.map((c) => ({
972
+ toolCallId: c.id,
973
+ content: 'host-result',
974
+ status: 'success' as const,
975
+ }))
976
+ );
977
+ });
978
+
979
+ const registry = new HookRegistry();
980
+ registry.register('PreToolUse', {
981
+ hooks: [
982
+ async (): Promise<PreToolUseHookOutput> => ({
983
+ decision: 'ask',
984
+ reason: 'review',
985
+ }),
986
+ ],
987
+ });
988
+
989
+ const hexToolCallId = '0123456789abcdef0123456789abcdef';
990
+ const node = new ToolNode({
991
+ tools: [createSchemaStub('echo')],
992
+ eventDrivenMode: true,
993
+ agentId: 'agent-x',
994
+ toolCallStepIds: new Map([[hexToolCallId, 'step_1']]),
995
+ hookRegistry: registry,
996
+ humanInTheLoop: { enabled: true },
997
+ });
998
+
999
+ const builder = new StateGraph(MessagesAnnotation)
1000
+ .addNode(
1001
+ 'agent',
1002
+ (): MessagesUpdate => ({
1003
+ messages: [
1004
+ new AIMessage({
1005
+ content: '',
1006
+ tool_calls: [
1007
+ { id: hexToolCallId, name: 'echo', args: { command: 'x' } },
1008
+ ],
1009
+ }),
1010
+ ],
1011
+ })
1012
+ )
1013
+ .addNode('tools', node)
1014
+ .addEdge(START, 'agent')
1015
+ .addEdge('agent', 'tools')
1016
+ .addEdge('tools', END);
1017
+ const graph = builder.compile({ checkpointer: new MemorySaver() });
1018
+
1019
+ const { Run } = await import('@/run');
1020
+ const { HumanMessage } = await import('@langchain/core/messages');
1021
+ const run = await Run.create<t.IState>({
1022
+ runId: 'run-resume-update',
1023
+ graphConfig: {
1024
+ type: 'standard',
1025
+ agents: [
1026
+ {
1027
+ agentId: 'a',
1028
+ provider: providers.OPENAI,
1029
+ clientOptions: { modelName: 'gpt-4o-mini', apiKey: 'test-key' },
1030
+ instructions: 'noop',
1031
+ maxContextTokens: 8000,
1032
+ },
1033
+ ],
1034
+ },
1035
+ hooks: registry,
1036
+ humanInTheLoop: { enabled: true },
1037
+ });
1038
+ run.graphRunnable = graph as unknown as t.CompiledStateWorkflow;
1039
+
1040
+ const callerConfig = {
1041
+ configurable: { thread_id: 'run-resume-update-thread' },
1042
+ version: 'v2' as const,
1043
+ };
1044
+
1045
+ await run.processStream({ messages: [] }, callerConfig);
1046
+ expect(run.getInterrupt()).toBeDefined();
1047
+
1048
+ const injected = new HumanMessage({ content: 'human-injected-on-resume' });
1049
+ await run.resume(
1050
+ { [hexToolCallId]: { type: 'approve' } },
1051
+ callerConfig,
1052
+ undefined,
1053
+ { update: { messages: [injected] } }
1054
+ );
1055
+
1056
+ expect(run.getInterrupt()).toBeUndefined();
1057
+
1058
+ const state = await graph.getState(callerConfig);
1059
+ const contents = (state.values.messages as BaseMessage[]).map(
1060
+ (m) => m.content
1061
+ );
1062
+ /** Proves langgraph honored `update` on the INPUT resume Command. */
1063
+ expect(contents).toContain('human-injected-on-resume');
1064
+ /** Resume itself still completed: the approved tool produced its result. */
1065
+ expect(contents).toContain('host-result');
1066
+ });
1067
+
906
1068
  it('Run.getHaltReason() reports prompt_denied when UserPromptSubmit denies the prompt', async () => {
907
1069
  const registry = new HookRegistry();
908
1070
  registry.register('UserPromptSubmit', {