@tambo-ai/react 0.58.0 → 0.59.0

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 (85) hide show
  1. package/dist/hooks/use-tambo-stream-status.js +1 -1
  2. package/dist/hooks/use-tambo-stream-status.js.map +1 -1
  3. package/dist/mcp/__tests__/mcp-client.test.js +0 -266
  4. package/dist/mcp/__tests__/mcp-client.test.js.map +1 -1
  5. package/dist/mcp/__tests__/tambo-mcp-provider.test.js +218 -12
  6. package/dist/mcp/__tests__/tambo-mcp-provider.test.js.map +1 -1
  7. package/dist/mcp/__tests__/use-mcp-servers.test.js +34 -9
  8. package/dist/mcp/__tests__/use-mcp-servers.test.js.map +1 -1
  9. package/dist/mcp/index.d.ts +3 -1
  10. package/dist/mcp/index.d.ts.map +1 -1
  11. package/dist/mcp/index.js +4 -1
  12. package/dist/mcp/index.js.map +1 -1
  13. package/dist/mcp/mcp-client.d.ts +12 -79
  14. package/dist/mcp/mcp-client.d.ts.map +1 -1
  15. package/dist/mcp/mcp-client.js +22 -159
  16. package/dist/mcp/mcp-client.js.map +1 -1
  17. package/dist/mcp/mcp-hooks.d.ts +93 -0
  18. package/dist/mcp/mcp-hooks.d.ts.map +1 -0
  19. package/dist/mcp/mcp-hooks.js +56 -0
  20. package/dist/mcp/mcp-hooks.js.map +1 -0
  21. package/dist/mcp/tambo-mcp-provider.d.ts +30 -2
  22. package/dist/mcp/tambo-mcp-provider.d.ts.map +1 -1
  23. package/dist/mcp/tambo-mcp-provider.js +127 -17
  24. package/dist/mcp/tambo-mcp-provider.js.map +1 -1
  25. package/dist/model/component-metadata.d.ts +1 -1
  26. package/dist/model/component-metadata.d.ts.map +1 -1
  27. package/dist/model/component-metadata.js.map +1 -1
  28. package/dist/providers/__tests__/tambo-thread-provider-initial-messages.test.js +3 -1
  29. package/dist/providers/__tests__/tambo-thread-provider-initial-messages.test.js.map +1 -1
  30. package/dist/providers/__tests__/tambo-thread-provider.test.js +395 -8
  31. package/dist/providers/__tests__/tambo-thread-provider.test.js.map +1 -1
  32. package/dist/providers/tambo-mcp-token-provider.d.ts +34 -0
  33. package/dist/providers/tambo-mcp-token-provider.d.ts.map +1 -0
  34. package/dist/providers/tambo-mcp-token-provider.js +69 -0
  35. package/dist/providers/tambo-mcp-token-provider.js.map +1 -0
  36. package/dist/providers/tambo-provider.d.ts.map +1 -1
  37. package/dist/providers/tambo-provider.js +7 -5
  38. package/dist/providers/tambo-provider.js.map +1 -1
  39. package/dist/providers/tambo-thread-provider.d.ts.map +1 -1
  40. package/dist/providers/tambo-thread-provider.js +20 -8
  41. package/dist/providers/tambo-thread-provider.js.map +1 -1
  42. package/dist/testing/tools.d.ts +1 -1
  43. package/esm/hooks/use-tambo-stream-status.js +1 -1
  44. package/esm/hooks/use-tambo-stream-status.js.map +1 -1
  45. package/esm/mcp/__tests__/mcp-client.test.js +0 -266
  46. package/esm/mcp/__tests__/mcp-client.test.js.map +1 -1
  47. package/esm/mcp/__tests__/tambo-mcp-provider.test.js +218 -12
  48. package/esm/mcp/__tests__/tambo-mcp-provider.test.js.map +1 -1
  49. package/esm/mcp/__tests__/use-mcp-servers.test.js +34 -9
  50. package/esm/mcp/__tests__/use-mcp-servers.test.js.map +1 -1
  51. package/esm/mcp/index.d.ts +3 -1
  52. package/esm/mcp/index.d.ts.map +1 -1
  53. package/esm/mcp/index.js +1 -0
  54. package/esm/mcp/index.js.map +1 -1
  55. package/esm/mcp/mcp-client.d.ts +12 -79
  56. package/esm/mcp/mcp-client.d.ts.map +1 -1
  57. package/esm/mcp/mcp-client.js +22 -159
  58. package/esm/mcp/mcp-client.js.map +1 -1
  59. package/esm/mcp/mcp-hooks.d.ts +93 -0
  60. package/esm/mcp/mcp-hooks.d.ts.map +1 -0
  61. package/esm/mcp/mcp-hooks.js +52 -0
  62. package/esm/mcp/mcp-hooks.js.map +1 -0
  63. package/esm/mcp/tambo-mcp-provider.d.ts +30 -2
  64. package/esm/mcp/tambo-mcp-provider.d.ts.map +1 -1
  65. package/esm/mcp/tambo-mcp-provider.js +128 -18
  66. package/esm/mcp/tambo-mcp-provider.js.map +1 -1
  67. package/esm/model/component-metadata.d.ts +1 -1
  68. package/esm/model/component-metadata.d.ts.map +1 -1
  69. package/esm/model/component-metadata.js.map +1 -1
  70. package/esm/providers/__tests__/tambo-thread-provider-initial-messages.test.js +3 -1
  71. package/esm/providers/__tests__/tambo-thread-provider-initial-messages.test.js.map +1 -1
  72. package/esm/providers/__tests__/tambo-thread-provider.test.js +395 -8
  73. package/esm/providers/__tests__/tambo-thread-provider.test.js.map +1 -1
  74. package/esm/providers/tambo-mcp-token-provider.d.ts +34 -0
  75. package/esm/providers/tambo-mcp-token-provider.d.ts.map +1 -0
  76. package/esm/providers/tambo-mcp-token-provider.js +31 -0
  77. package/esm/providers/tambo-mcp-token-provider.js.map +1 -0
  78. package/esm/providers/tambo-provider.d.ts.map +1 -1
  79. package/esm/providers/tambo-provider.js +7 -5
  80. package/esm/providers/tambo-provider.js.map +1 -1
  81. package/esm/providers/tambo-thread-provider.d.ts.map +1 -1
  82. package/esm/providers/tambo-thread-provider.js +20 -8
  83. package/esm/providers/tambo-thread-provider.js.map +1 -1
  84. package/esm/testing/tools.d.ts +1 -1
  85. package/package.json +1 -1
@@ -6,6 +6,7 @@ import { GenerationStage, } from "../../model/generate-component-response";
6
6
  import { serializeRegistry } from "../../testing/tools";
7
7
  import { useTamboClient, useTamboQueryClient } from "../tambo-client-provider";
8
8
  import { TamboContextHelpersProvider } from "../tambo-context-helpers-provider";
9
+ import { TamboMcpTokenProvider } from "../tambo-mcp-token-provider";
9
10
  import { TamboRegistryProvider } from "../tambo-registry-provider";
10
11
  import { TamboThreadProvider, useTamboThread } from "../tambo-thread-provider";
11
12
  // Mock crypto.randomUUID
@@ -111,7 +112,8 @@ describe("TamboThreadProvider", () => {
111
112
  currentTimeContextHelper: () => null,
112
113
  currentPageContextHelper: () => null,
113
114
  } },
114
- React.createElement(TamboThreadProvider, { streaming: false }, children))));
115
+ React.createElement(TamboMcpTokenProvider, null,
116
+ React.createElement(TamboThreadProvider, { streaming: false }, children)))));
115
117
  beforeEach(() => {
116
118
  jest.clearAllMocks();
117
119
  // Setup mock query client
@@ -327,7 +329,8 @@ describe("TamboThreadProvider", () => {
327
329
  currentTimeContextHelper: () => null,
328
330
  currentPageContextHelper: () => null,
329
331
  } },
330
- React.createElement(TamboThreadProvider, { streaming: false }, children))));
332
+ React.createElement(TamboMcpTokenProvider, null,
333
+ React.createElement(TamboThreadProvider, { streaming: false }, children)))));
331
334
  const mockUnregisteredToolCallResponse = {
332
335
  responseMessageDto: {
333
336
  id: "unregistered-tool-call-1",
@@ -425,7 +428,8 @@ describe("TamboThreadProvider", () => {
425
428
  currentTimeContextHelper: () => null,
426
429
  currentPageContextHelper: () => null,
427
430
  } },
428
- React.createElement(TamboThreadProvider, { streaming: true }, children))));
431
+ React.createElement(TamboMcpTokenProvider, null,
432
+ React.createElement(TamboThreadProvider, { streaming: true }, children)))));
429
433
  const mockStreamResponse = {
430
434
  responseMessageDto: {
431
435
  id: "stream-response",
@@ -486,7 +490,8 @@ describe("TamboThreadProvider", () => {
486
490
  currentTimeContextHelper: () => null,
487
491
  currentPageContextHelper: () => null,
488
492
  } },
489
- React.createElement(TamboThreadProvider, { streaming: true }, children))));
493
+ React.createElement(TamboMcpTokenProvider, null,
494
+ React.createElement(TamboThreadProvider, { streaming: true }, children)))));
490
495
  const { result } = renderHook(() => useTamboThread(), {
491
496
  wrapper: wrapperWithStreaming,
492
497
  });
@@ -528,7 +533,8 @@ describe("TamboThreadProvider", () => {
528
533
  currentTimeContextHelper: () => null,
529
534
  currentPageContextHelper: () => null,
530
535
  } },
531
- React.createElement(TamboThreadProvider, { streaming: false }, children))));
536
+ React.createElement(TamboMcpTokenProvider, null,
537
+ React.createElement(TamboThreadProvider, { streaming: false }, children)))));
532
538
  const { result } = renderHook(() => useTamboThread(), {
533
539
  wrapper: wrapperWithoutStreaming,
534
540
  });
@@ -570,7 +576,8 @@ describe("TamboThreadProvider", () => {
570
576
  currentTimeContextHelper: () => null,
571
577
  currentPageContextHelper: () => null,
572
578
  } },
573
- React.createElement(TamboThreadProvider, null, children))));
579
+ React.createElement(TamboMcpTokenProvider, null,
580
+ React.createElement(TamboThreadProvider, null, children)))));
574
581
  const mockStreamResponse = {
575
582
  responseMessageDto: {
576
583
  id: "stream-response",
@@ -631,7 +638,8 @@ describe("TamboThreadProvider", () => {
631
638
  currentTimeContextHelper: () => null,
632
639
  currentPageContextHelper: () => null,
633
640
  } },
634
- React.createElement(TamboThreadProvider, { streaming: true }, children))));
641
+ React.createElement(TamboMcpTokenProvider, null,
642
+ React.createElement(TamboThreadProvider, { streaming: true }, children)))));
635
643
  const { result } = renderHook(() => useTamboThread(), {
636
644
  wrapper: wrapperWithStreaming,
637
645
  });
@@ -675,7 +683,8 @@ describe("TamboThreadProvider", () => {
675
683
  currentTimeContextHelper: () => null,
676
684
  currentPageContextHelper: () => null,
677
685
  } },
678
- React.createElement(TamboThreadProvider, { streaming: false }, children))));
686
+ React.createElement(TamboMcpTokenProvider, null,
687
+ React.createElement(TamboThreadProvider, { streaming: false }, children)))));
679
688
  const mockStreamResponse = {
680
689
  responseMessageDto: {
681
690
  id: "stream-response",
@@ -844,5 +853,383 @@ describe("TamboThreadProvider", () => {
844
853
  expect(result.current.thread.id).toBe("existing-thread-123");
845
854
  });
846
855
  });
856
+ describe("transformToContent", () => {
857
+ it("should use custom transformToContent when provided (non-streaming)", async () => {
858
+ const mockTransformToContent = jest.fn().mockReturnValue([
859
+ { type: "text", text: "Custom transformed content" },
860
+ {
861
+ type: "image_url",
862
+ image_url: { url: "https://example.com/image.png" },
863
+ },
864
+ ]);
865
+ const customToolRegistry = [
866
+ {
867
+ name: "TestComponent",
868
+ component: () => React.createElement("div", null, "Test"),
869
+ description: "Test",
870
+ propsSchema: z.object({ test: z.string() }),
871
+ associatedTools: [
872
+ {
873
+ name: "custom-tool",
874
+ tool: jest.fn().mockResolvedValue({ data: "tool result" }),
875
+ description: "Tool with custom transform",
876
+ toolSchema: z
877
+ .function()
878
+ .args(z.string())
879
+ .returns(z.object({ data: z.string() })),
880
+ transformToContent: mockTransformToContent,
881
+ },
882
+ ],
883
+ },
884
+ ];
885
+ const wrapperWithCustomTool = ({ children, }) => (React.createElement(TamboRegistryProvider, { components: customToolRegistry },
886
+ React.createElement(TamboContextHelpersProvider, { contextHelpers: {
887
+ currentTimeContextHelper: () => null,
888
+ currentPageContextHelper: () => null,
889
+ } },
890
+ React.createElement(TamboMcpTokenProvider, null,
891
+ React.createElement(TamboThreadProvider, { streaming: false }, children)))));
892
+ const mockToolCallResponse = {
893
+ responseMessageDto: {
894
+ id: "tool-call-1",
895
+ content: [{ type: "text", text: "Tool response" }],
896
+ role: "tool",
897
+ threadId: "test-thread-1",
898
+ toolCallRequest: {
899
+ toolName: "custom-tool",
900
+ parameters: [{ parameterName: "input", parameterValue: "test" }],
901
+ },
902
+ componentState: {},
903
+ createdAt: new Date().toISOString(),
904
+ },
905
+ generationStage: GenerationStage.COMPLETE,
906
+ mcpAccessToken: "test-mcp-access-token",
907
+ };
908
+ jest
909
+ .mocked(mockThreadsApi.advanceByID)
910
+ .mockResolvedValueOnce(mockToolCallResponse)
911
+ .mockResolvedValueOnce({
912
+ responseMessageDto: {
913
+ id: "final-response",
914
+ content: [{ type: "text", text: "Final response" }],
915
+ role: "assistant",
916
+ threadId: "test-thread-1",
917
+ componentState: {},
918
+ createdAt: new Date().toISOString(),
919
+ },
920
+ generationStage: GenerationStage.COMPLETE,
921
+ mcpAccessToken: "test-mcp-access-token",
922
+ });
923
+ const { result } = renderHook(() => useTamboThread(), {
924
+ wrapper: wrapperWithCustomTool,
925
+ });
926
+ await act(async () => {
927
+ await result.current.sendThreadMessage("Use custom tool", {
928
+ threadId: "test-thread-1",
929
+ streamResponse: false,
930
+ });
931
+ });
932
+ // Verify the tool was called
933
+ expect(customToolRegistry[0]?.associatedTools?.[0]?.tool).toHaveBeenCalledWith("test");
934
+ // Verify transformToContent was called with the tool result
935
+ expect(mockTransformToContent).toHaveBeenCalledWith({
936
+ data: "tool result",
937
+ });
938
+ // Verify the second advance call included the transformed content
939
+ expect(mockThreadsApi.advanceByID).toHaveBeenCalledTimes(2);
940
+ expect(mockThreadsApi.advanceByID).toHaveBeenLastCalledWith("test-thread-1", expect.objectContaining({
941
+ messageToAppend: expect.objectContaining({
942
+ content: [
943
+ { type: "text", text: "Custom transformed content" },
944
+ {
945
+ type: "image_url",
946
+ image_url: { url: "https://example.com/image.png" },
947
+ },
948
+ ],
949
+ role: "tool",
950
+ }),
951
+ }));
952
+ });
953
+ it("should use custom async transformToContent when provided (streaming)", async () => {
954
+ const mockTransformToContent = jest
955
+ .fn()
956
+ .mockResolvedValue([
957
+ { type: "text", text: "Async transformed content" },
958
+ ]);
959
+ const customToolRegistry = [
960
+ {
961
+ name: "TestComponent",
962
+ component: () => React.createElement("div", null, "Test"),
963
+ description: "Test",
964
+ propsSchema: z.object({ test: z.string() }),
965
+ associatedTools: [
966
+ {
967
+ name: "async-tool",
968
+ tool: jest.fn().mockResolvedValue({ data: "async tool result" }),
969
+ description: "Tool with async transform",
970
+ toolSchema: z
971
+ .function()
972
+ .args(z.string())
973
+ .returns(z.object({ data: z.string() })),
974
+ transformToContent: mockTransformToContent,
975
+ },
976
+ ],
977
+ },
978
+ ];
979
+ const wrapperWithAsyncTool = ({ children, }) => (React.createElement(TamboRegistryProvider, { components: customToolRegistry },
980
+ React.createElement(TamboContextHelpersProvider, { contextHelpers: {
981
+ currentTimeContextHelper: () => null,
982
+ currentPageContextHelper: () => null,
983
+ } },
984
+ React.createElement(TamboMcpTokenProvider, null,
985
+ React.createElement(TamboThreadProvider, { streaming: true }, children)))));
986
+ const mockToolCallChunk = {
987
+ responseMessageDto: {
988
+ id: "tool-call-chunk",
989
+ content: [{ type: "text", text: "Tool call" }],
990
+ role: "tool",
991
+ threadId: "test-thread-1",
992
+ toolCallRequest: {
993
+ toolName: "async-tool",
994
+ parameters: [
995
+ { parameterName: "input", parameterValue: "async-test" },
996
+ ],
997
+ },
998
+ componentState: {},
999
+ createdAt: new Date().toISOString(),
1000
+ },
1001
+ generationStage: GenerationStage.COMPLETE,
1002
+ mcpAccessToken: "test-mcp-access-token",
1003
+ };
1004
+ const mockFinalChunk = {
1005
+ responseMessageDto: {
1006
+ id: "final-chunk",
1007
+ content: [{ type: "text", text: "Final streaming response" }],
1008
+ role: "assistant",
1009
+ threadId: "test-thread-1",
1010
+ componentState: {},
1011
+ createdAt: new Date().toISOString(),
1012
+ },
1013
+ generationStage: GenerationStage.COMPLETE,
1014
+ mcpAccessToken: "test-mcp-access-token",
1015
+ };
1016
+ const mockAsyncIterator = {
1017
+ [Symbol.asyncIterator]: async function* () {
1018
+ yield mockToolCallChunk;
1019
+ yield mockFinalChunk;
1020
+ },
1021
+ };
1022
+ jest
1023
+ .mocked(advanceStream)
1024
+ .mockResolvedValueOnce(mockAsyncIterator)
1025
+ .mockResolvedValueOnce({
1026
+ [Symbol.asyncIterator]: async function* () {
1027
+ yield mockFinalChunk;
1028
+ },
1029
+ });
1030
+ const { result } = renderHook(() => useTamboThread(), {
1031
+ wrapper: wrapperWithAsyncTool,
1032
+ });
1033
+ await act(async () => {
1034
+ await result.current.sendThreadMessage("Use async tool", {
1035
+ threadId: "test-thread-1",
1036
+ streamResponse: true,
1037
+ });
1038
+ });
1039
+ // Verify the tool was called
1040
+ expect(customToolRegistry[0]?.associatedTools?.[0]?.tool).toHaveBeenCalledWith("async-test");
1041
+ // Verify transformToContent was called
1042
+ expect(mockTransformToContent).toHaveBeenCalledWith({
1043
+ data: "async tool result",
1044
+ });
1045
+ // Verify advanceStream was called twice (initial request and tool response)
1046
+ expect(advanceStream).toHaveBeenCalledTimes(2);
1047
+ // Verify the second advanceStream call included the transformed content
1048
+ expect(advanceStream).toHaveBeenLastCalledWith(mockTamboAI, expect.objectContaining({
1049
+ messageToAppend: expect.objectContaining({
1050
+ content: [{ type: "text", text: "Async transformed content" }],
1051
+ role: "tool",
1052
+ }),
1053
+ }), "test-thread-1");
1054
+ });
1055
+ it("should fallback to stringified text when transformToContent is not provided", async () => {
1056
+ const toolWithoutTransform = [
1057
+ {
1058
+ name: "TestComponent",
1059
+ component: () => React.createElement("div", null, "Test"),
1060
+ description: "Test",
1061
+ propsSchema: z.object({ test: z.string() }),
1062
+ associatedTools: [
1063
+ {
1064
+ name: "no-transform-tool",
1065
+ tool: jest
1066
+ .fn()
1067
+ .mockResolvedValue({ complex: "data", nested: { value: 42 } }),
1068
+ description: "Tool without custom transform",
1069
+ toolSchema: z
1070
+ .function()
1071
+ .args(z.string())
1072
+ .returns(z.object({
1073
+ complex: z.string(),
1074
+ nested: z.object({ value: z.number() }),
1075
+ })),
1076
+ // No transformToContent provided
1077
+ },
1078
+ ],
1079
+ },
1080
+ ];
1081
+ const wrapperWithoutTransform = ({ children, }) => (React.createElement(TamboRegistryProvider, { components: toolWithoutTransform },
1082
+ React.createElement(TamboContextHelpersProvider, { contextHelpers: {
1083
+ currentTimeContextHelper: () => null,
1084
+ currentPageContextHelper: () => null,
1085
+ } },
1086
+ React.createElement(TamboMcpTokenProvider, null,
1087
+ React.createElement(TamboThreadProvider, { streaming: false }, children)))));
1088
+ const mockToolCallResponse = {
1089
+ responseMessageDto: {
1090
+ id: "tool-call-1",
1091
+ content: [{ type: "text", text: "Tool call" }],
1092
+ role: "tool",
1093
+ threadId: "test-thread-1",
1094
+ toolCallRequest: {
1095
+ toolName: "no-transform-tool",
1096
+ parameters: [{ parameterName: "input", parameterValue: "test" }],
1097
+ },
1098
+ componentState: {},
1099
+ createdAt: new Date().toISOString(),
1100
+ },
1101
+ generationStage: GenerationStage.COMPLETE,
1102
+ mcpAccessToken: "test-mcp-access-token",
1103
+ };
1104
+ jest
1105
+ .mocked(mockThreadsApi.advanceByID)
1106
+ .mockResolvedValueOnce(mockToolCallResponse)
1107
+ .mockResolvedValueOnce({
1108
+ responseMessageDto: {
1109
+ id: "final-response",
1110
+ content: [{ type: "text", text: "Final response" }],
1111
+ role: "assistant",
1112
+ threadId: "test-thread-1",
1113
+ componentState: {},
1114
+ createdAt: new Date().toISOString(),
1115
+ },
1116
+ generationStage: GenerationStage.COMPLETE,
1117
+ mcpAccessToken: "test-mcp-access-token",
1118
+ });
1119
+ const { result } = renderHook(() => useTamboThread(), {
1120
+ wrapper: wrapperWithoutTransform,
1121
+ });
1122
+ await act(async () => {
1123
+ await result.current.sendThreadMessage("Use tool without transform", {
1124
+ threadId: "test-thread-1",
1125
+ streamResponse: false,
1126
+ });
1127
+ });
1128
+ // Verify the tool was called
1129
+ expect(toolWithoutTransform[0]?.associatedTools?.[0]?.tool).toHaveBeenCalledWith("test");
1130
+ // Verify the second advance call used stringified content
1131
+ expect(mockThreadsApi.advanceByID).toHaveBeenLastCalledWith("test-thread-1", expect.objectContaining({
1132
+ messageToAppend: expect.objectContaining({
1133
+ content: [
1134
+ {
1135
+ type: "text",
1136
+ text: '{"complex":"data","nested":{"value":42}}',
1137
+ },
1138
+ ],
1139
+ role: "tool",
1140
+ }),
1141
+ }));
1142
+ });
1143
+ it("should always return text for error responses even with transformToContent", async () => {
1144
+ const mockTransformToContent = jest.fn().mockReturnValue([
1145
+ {
1146
+ type: "image_url",
1147
+ image_url: { url: "https://example.com/error.png" },
1148
+ },
1149
+ ]);
1150
+ const toolWithTransform = [
1151
+ {
1152
+ name: "TestComponent",
1153
+ component: () => React.createElement("div", null, "Test"),
1154
+ description: "Test",
1155
+ propsSchema: z.object({ test: z.string() }),
1156
+ associatedTools: [
1157
+ {
1158
+ name: "error-tool",
1159
+ tool: jest
1160
+ .fn()
1161
+ .mockRejectedValue(new Error("Tool execution failed")),
1162
+ description: "Tool that errors",
1163
+ toolSchema: z.function().args(z.string()).returns(z.string()),
1164
+ transformToContent: mockTransformToContent,
1165
+ },
1166
+ ],
1167
+ },
1168
+ ];
1169
+ const wrapperWithErrorTool = ({ children, }) => (React.createElement(TamboRegistryProvider, { components: toolWithTransform },
1170
+ React.createElement(TamboContextHelpersProvider, { contextHelpers: {
1171
+ currentTimeContextHelper: () => null,
1172
+ currentPageContextHelper: () => null,
1173
+ } },
1174
+ React.createElement(TamboMcpTokenProvider, null,
1175
+ React.createElement(TamboThreadProvider, { streaming: false }, children)))));
1176
+ const mockToolCallResponse = {
1177
+ responseMessageDto: {
1178
+ id: "tool-call-1",
1179
+ content: [{ type: "text", text: "Tool call" }],
1180
+ role: "tool",
1181
+ threadId: "test-thread-1",
1182
+ toolCallRequest: {
1183
+ toolName: "error-tool",
1184
+ parameters: [{ parameterName: "input", parameterValue: "test" }],
1185
+ },
1186
+ componentState: {},
1187
+ createdAt: new Date().toISOString(),
1188
+ },
1189
+ generationStage: GenerationStage.COMPLETE,
1190
+ mcpAccessToken: "test-mcp-access-token",
1191
+ };
1192
+ jest
1193
+ .mocked(mockThreadsApi.advanceByID)
1194
+ .mockResolvedValueOnce(mockToolCallResponse)
1195
+ .mockResolvedValueOnce({
1196
+ responseMessageDto: {
1197
+ id: "final-response",
1198
+ content: [{ type: "text", text: "Final response" }],
1199
+ role: "assistant",
1200
+ threadId: "test-thread-1",
1201
+ componentState: {},
1202
+ createdAt: new Date().toISOString(),
1203
+ },
1204
+ generationStage: GenerationStage.COMPLETE,
1205
+ mcpAccessToken: "test-mcp-access-token",
1206
+ });
1207
+ const { result } = renderHook(() => useTamboThread(), {
1208
+ wrapper: wrapperWithErrorTool,
1209
+ });
1210
+ await act(async () => {
1211
+ await result.current.sendThreadMessage("Use error tool", {
1212
+ threadId: "test-thread-1",
1213
+ streamResponse: false,
1214
+ });
1215
+ });
1216
+ // Verify the tool was called
1217
+ expect(toolWithTransform[0]?.associatedTools?.[0]?.tool).toHaveBeenCalledWith("test");
1218
+ // Verify transformToContent was NOT called for error responses
1219
+ expect(mockTransformToContent).not.toHaveBeenCalled();
1220
+ // Verify the second advance call used text content with the error message
1221
+ expect(mockThreadsApi.advanceByID).toHaveBeenLastCalledWith("test-thread-1", expect.objectContaining({
1222
+ messageToAppend: expect.objectContaining({
1223
+ content: [
1224
+ expect.objectContaining({
1225
+ type: "text",
1226
+ // Error message should be in text format
1227
+ }),
1228
+ ],
1229
+ role: "tool",
1230
+ }),
1231
+ }));
1232
+ });
1233
+ });
847
1234
  });
848
1235
  //# sourceMappingURL=tambo-thread-provider.test.js.map