@tambo-ai/react 0.71.0 → 0.73.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 (167) hide show
  1. package/dist/v1/hooks/use-tambo-v1-component-state.d.ts +44 -0
  2. package/dist/v1/hooks/use-tambo-v1-component-state.d.ts.map +1 -0
  3. package/dist/v1/hooks/use-tambo-v1-component-state.js +134 -0
  4. package/dist/v1/hooks/use-tambo-v1-component-state.js.map +1 -0
  5. package/dist/v1/hooks/use-tambo-v1-component-state.test.d.ts +2 -0
  6. package/dist/v1/hooks/use-tambo-v1-component-state.test.d.ts.map +1 -0
  7. package/dist/v1/hooks/use-tambo-v1-component-state.test.js +292 -0
  8. package/dist/v1/hooks/use-tambo-v1-component-state.test.js.map +1 -0
  9. package/dist/v1/hooks/use-tambo-v1-messages.test.js +22 -9
  10. package/dist/v1/hooks/use-tambo-v1-messages.test.js.map +1 -1
  11. package/dist/v1/hooks/use-tambo-v1-send-message.d.ts +1 -0
  12. package/dist/v1/hooks/use-tambo-v1-send-message.d.ts.map +1 -1
  13. package/dist/v1/hooks/use-tambo-v1-send-message.js +9 -2
  14. package/dist/v1/hooks/use-tambo-v1-send-message.js.map +1 -1
  15. package/dist/v1/hooks/use-tambo-v1-send-message.test.js +22 -9
  16. package/dist/v1/hooks/use-tambo-v1-send-message.test.js.map +1 -1
  17. package/dist/v1/hooks/use-tambo-v1-suggestions.d.ts +91 -0
  18. package/dist/v1/hooks/use-tambo-v1-suggestions.d.ts.map +1 -0
  19. package/dist/v1/hooks/use-tambo-v1-suggestions.js +152 -0
  20. package/dist/v1/hooks/use-tambo-v1-suggestions.js.map +1 -0
  21. package/dist/v1/hooks/use-tambo-v1-suggestions.test.d.ts +2 -0
  22. package/dist/v1/hooks/use-tambo-v1-suggestions.test.d.ts.map +1 -0
  23. package/dist/v1/hooks/use-tambo-v1-suggestions.test.js +511 -0
  24. package/dist/v1/hooks/use-tambo-v1-suggestions.test.js.map +1 -0
  25. package/dist/v1/hooks/use-tambo-v1-thread-input.d.ts +11 -0
  26. package/dist/v1/hooks/use-tambo-v1-thread-input.d.ts.map +1 -0
  27. package/dist/v1/hooks/use-tambo-v1-thread-input.js +16 -0
  28. package/dist/v1/hooks/use-tambo-v1-thread-input.js.map +1 -0
  29. package/dist/v1/hooks/use-tambo-v1-thread-input.test.d.ts +2 -0
  30. package/dist/v1/hooks/use-tambo-v1-thread-input.test.d.ts.map +1 -0
  31. package/dist/v1/hooks/use-tambo-v1-thread-input.test.js +297 -0
  32. package/dist/v1/hooks/use-tambo-v1-thread-input.test.js.map +1 -0
  33. package/dist/v1/hooks/use-tambo-v1-thread-list.d.ts +6 -4
  34. package/dist/v1/hooks/use-tambo-v1-thread-list.d.ts.map +1 -1
  35. package/dist/v1/hooks/use-tambo-v1-thread-list.js +2 -2
  36. package/dist/v1/hooks/use-tambo-v1-thread-list.js.map +1 -1
  37. package/dist/v1/hooks/use-tambo-v1-thread-list.test.js +2 -2
  38. package/dist/v1/hooks/use-tambo-v1-thread-list.test.js.map +1 -1
  39. package/dist/v1/hooks/use-tambo-v1.test.js +16 -7
  40. package/dist/v1/hooks/use-tambo-v1.test.js.map +1 -1
  41. package/dist/v1/index.d.ts +39 -19
  42. package/dist/v1/index.d.ts.map +1 -1
  43. package/dist/v1/index.js +60 -34
  44. package/dist/v1/index.js.map +1 -1
  45. package/dist/v1/providers/tambo-v1-provider.d.ts +61 -1
  46. package/dist/v1/providers/tambo-v1-provider.d.ts.map +1 -1
  47. package/dist/v1/providers/tambo-v1-provider.js +35 -3
  48. package/dist/v1/providers/tambo-v1-provider.js.map +1 -1
  49. package/dist/v1/providers/tambo-v1-provider.test.js +78 -3
  50. package/dist/v1/providers/tambo-v1-provider.test.js.map +1 -1
  51. package/dist/v1/providers/tambo-v1-stream-context.d.ts +19 -10
  52. package/dist/v1/providers/tambo-v1-stream-context.d.ts.map +1 -1
  53. package/dist/v1/providers/tambo-v1-stream-context.js +43 -53
  54. package/dist/v1/providers/tambo-v1-stream-context.js.map +1 -1
  55. package/dist/v1/providers/tambo-v1-stream-context.test.js +94 -19
  56. package/dist/v1/providers/tambo-v1-stream-context.test.js.map +1 -1
  57. package/dist/v1/providers/tambo-v1-stub-provider.d.ts +74 -0
  58. package/dist/v1/providers/tambo-v1-stub-provider.d.ts.map +1 -0
  59. package/dist/v1/providers/tambo-v1-stub-provider.js +212 -0
  60. package/dist/v1/providers/tambo-v1-stub-provider.js.map +1 -0
  61. package/dist/v1/providers/tambo-v1-stub-provider.test.d.ts +2 -0
  62. package/dist/v1/providers/tambo-v1-stub-provider.test.d.ts.map +1 -0
  63. package/dist/v1/providers/tambo-v1-stub-provider.test.js +162 -0
  64. package/dist/v1/providers/tambo-v1-stub-provider.test.js.map +1 -0
  65. package/dist/v1/providers/tambo-v1-thread-input-provider.d.ts +105 -0
  66. package/dist/v1/providers/tambo-v1-thread-input-provider.d.ts.map +1 -0
  67. package/dist/v1/providers/tambo-v1-thread-input-provider.js +191 -0
  68. package/dist/v1/providers/tambo-v1-thread-input-provider.js.map +1 -0
  69. package/dist/v1/types/message.d.ts +27 -2
  70. package/dist/v1/types/message.d.ts.map +1 -1
  71. package/dist/v1/types/message.js.map +1 -1
  72. package/dist/v1/utils/component-renderer.d.ts +37 -0
  73. package/dist/v1/utils/component-renderer.d.ts.map +1 -0
  74. package/dist/v1/utils/component-renderer.js +70 -0
  75. package/dist/v1/utils/component-renderer.js.map +1 -0
  76. package/dist/v1/utils/component-renderer.test.d.ts +2 -0
  77. package/dist/v1/utils/component-renderer.test.d.ts.map +1 -0
  78. package/dist/v1/utils/component-renderer.test.js +45 -0
  79. package/dist/v1/utils/component-renderer.test.js.map +1 -0
  80. package/dist/v1/utils/event-accumulator.js +28 -8
  81. package/dist/v1/utils/event-accumulator.js.map +1 -1
  82. package/dist/v1/utils/event-accumulator.test.js +201 -6
  83. package/dist/v1/utils/event-accumulator.test.js.map +1 -1
  84. package/esm/v1/hooks/use-tambo-v1-component-state.d.ts +44 -0
  85. package/esm/v1/hooks/use-tambo-v1-component-state.d.ts.map +1 -0
  86. package/esm/v1/hooks/use-tambo-v1-component-state.js +131 -0
  87. package/esm/v1/hooks/use-tambo-v1-component-state.js.map +1 -0
  88. package/esm/v1/hooks/use-tambo-v1-component-state.test.d.ts +2 -0
  89. package/esm/v1/hooks/use-tambo-v1-component-state.test.d.ts.map +1 -0
  90. package/esm/v1/hooks/use-tambo-v1-component-state.test.js +290 -0
  91. package/esm/v1/hooks/use-tambo-v1-component-state.test.js.map +1 -0
  92. package/esm/v1/hooks/use-tambo-v1-messages.test.js +22 -9
  93. package/esm/v1/hooks/use-tambo-v1-messages.test.js.map +1 -1
  94. package/esm/v1/hooks/use-tambo-v1-send-message.d.ts +1 -0
  95. package/esm/v1/hooks/use-tambo-v1-send-message.d.ts.map +1 -1
  96. package/esm/v1/hooks/use-tambo-v1-send-message.js +9 -2
  97. package/esm/v1/hooks/use-tambo-v1-send-message.js.map +1 -1
  98. package/esm/v1/hooks/use-tambo-v1-send-message.test.js +22 -9
  99. package/esm/v1/hooks/use-tambo-v1-send-message.test.js.map +1 -1
  100. package/esm/v1/hooks/use-tambo-v1-suggestions.d.ts +91 -0
  101. package/esm/v1/hooks/use-tambo-v1-suggestions.d.ts.map +1 -0
  102. package/esm/v1/hooks/use-tambo-v1-suggestions.js +149 -0
  103. package/esm/v1/hooks/use-tambo-v1-suggestions.js.map +1 -0
  104. package/esm/v1/hooks/use-tambo-v1-suggestions.test.d.ts +2 -0
  105. package/esm/v1/hooks/use-tambo-v1-suggestions.test.d.ts.map +1 -0
  106. package/esm/v1/hooks/use-tambo-v1-suggestions.test.js +506 -0
  107. package/esm/v1/hooks/use-tambo-v1-suggestions.test.js.map +1 -0
  108. package/esm/v1/hooks/use-tambo-v1-thread-input.d.ts +11 -0
  109. package/esm/v1/hooks/use-tambo-v1-thread-input.d.ts.map +1 -0
  110. package/esm/v1/hooks/use-tambo-v1-thread-input.js +12 -0
  111. package/esm/v1/hooks/use-tambo-v1-thread-input.js.map +1 -0
  112. package/esm/v1/hooks/use-tambo-v1-thread-input.test.d.ts +2 -0
  113. package/esm/v1/hooks/use-tambo-v1-thread-input.test.d.ts.map +1 -0
  114. package/esm/v1/hooks/use-tambo-v1-thread-input.test.js +292 -0
  115. package/esm/v1/hooks/use-tambo-v1-thread-input.test.js.map +1 -0
  116. package/esm/v1/hooks/use-tambo-v1-thread-list.d.ts +6 -4
  117. package/esm/v1/hooks/use-tambo-v1-thread-list.d.ts.map +1 -1
  118. package/esm/v1/hooks/use-tambo-v1-thread-list.js +2 -2
  119. package/esm/v1/hooks/use-tambo-v1-thread-list.js.map +1 -1
  120. package/esm/v1/hooks/use-tambo-v1-thread-list.test.js +2 -2
  121. package/esm/v1/hooks/use-tambo-v1-thread-list.test.js.map +1 -1
  122. package/esm/v1/hooks/use-tambo-v1.test.js +16 -7
  123. package/esm/v1/hooks/use-tambo-v1.test.js.map +1 -1
  124. package/esm/v1/index.d.ts +39 -19
  125. package/esm/v1/index.d.ts.map +1 -1
  126. package/esm/v1/index.js +43 -19
  127. package/esm/v1/index.js.map +1 -1
  128. package/esm/v1/providers/tambo-v1-provider.d.ts +61 -1
  129. package/esm/v1/providers/tambo-v1-provider.d.ts.map +1 -1
  130. package/esm/v1/providers/tambo-v1-provider.js +34 -4
  131. package/esm/v1/providers/tambo-v1-provider.js.map +1 -1
  132. package/esm/v1/providers/tambo-v1-provider.test.js +79 -4
  133. package/esm/v1/providers/tambo-v1-provider.test.js.map +1 -1
  134. package/esm/v1/providers/tambo-v1-stream-context.d.ts +19 -10
  135. package/esm/v1/providers/tambo-v1-stream-context.d.ts.map +1 -1
  136. package/esm/v1/providers/tambo-v1-stream-context.js +44 -54
  137. package/esm/v1/providers/tambo-v1-stream-context.js.map +1 -1
  138. package/esm/v1/providers/tambo-v1-stream-context.test.js +95 -20
  139. package/esm/v1/providers/tambo-v1-stream-context.test.js.map +1 -1
  140. package/esm/v1/providers/tambo-v1-stub-provider.d.ts +74 -0
  141. package/esm/v1/providers/tambo-v1-stub-provider.d.ts.map +1 -0
  142. package/esm/v1/providers/tambo-v1-stub-provider.js +176 -0
  143. package/esm/v1/providers/tambo-v1-stub-provider.js.map +1 -0
  144. package/esm/v1/providers/tambo-v1-stub-provider.test.d.ts +2 -0
  145. package/esm/v1/providers/tambo-v1-stub-provider.test.d.ts.map +1 -0
  146. package/esm/v1/providers/tambo-v1-stub-provider.test.js +157 -0
  147. package/esm/v1/providers/tambo-v1-stub-provider.test.js.map +1 -0
  148. package/esm/v1/providers/tambo-v1-thread-input-provider.d.ts +105 -0
  149. package/esm/v1/providers/tambo-v1-thread-input-provider.d.ts.map +1 -0
  150. package/esm/v1/providers/tambo-v1-thread-input-provider.js +153 -0
  151. package/esm/v1/providers/tambo-v1-thread-input-provider.js.map +1 -0
  152. package/esm/v1/types/message.d.ts +27 -2
  153. package/esm/v1/types/message.d.ts.map +1 -1
  154. package/esm/v1/types/message.js.map +1 -1
  155. package/esm/v1/utils/component-renderer.d.ts +37 -0
  156. package/esm/v1/utils/component-renderer.d.ts.map +1 -0
  157. package/esm/v1/utils/component-renderer.js +33 -0
  158. package/esm/v1/utils/component-renderer.js.map +1 -0
  159. package/esm/v1/utils/component-renderer.test.d.ts +2 -0
  160. package/esm/v1/utils/component-renderer.test.d.ts.map +1 -0
  161. package/esm/v1/utils/component-renderer.test.js +40 -0
  162. package/esm/v1/utils/component-renderer.test.js.map +1 -0
  163. package/esm/v1/utils/event-accumulator.js +28 -8
  164. package/esm/v1/utils/event-accumulator.js.map +1 -1
  165. package/esm/v1/utils/event-accumulator.test.js +201 -6
  166. package/esm/v1/utils/event-accumulator.test.js.map +1 -1
  167. package/package.json +2 -2
@@ -1,21 +1,39 @@
1
+ import TamboAI from "@tambo-ai/typescript-sdk";
1
2
  import { QueryClient, useQueryClient } from "@tanstack/react-query";
2
3
  import { renderHook, act } from "@testing-library/react";
3
4
  import React from "react";
4
5
  import { z } from "zod";
5
6
  import { useTamboClient } from "../../providers/tambo-client-provider";
6
7
  import { useTamboRegistry } from "../../providers/tambo-registry-provider";
8
+ import { useTamboContextHelpers } from "../../providers/tambo-context-helpers-provider";
7
9
  import { useStreamState, useThreadManagement } from "./tambo-v1-stream-context";
8
- import { TamboV1Provider } from "./tambo-v1-provider";
10
+ import { TamboV1Provider, useTamboV1Config } from "./tambo-v1-provider";
9
11
  // Mock the client provider to capture the apiKey
10
12
  jest.mock("../../providers/tambo-client-provider", () => ({
11
13
  useTamboClient: jest.fn(),
14
+ useTamboQueryClient: jest.fn(() => new QueryClient()),
12
15
  TamboClientProvider: ({ children }) => children,
13
16
  }));
17
+ // Mock useTamboV1SendMessage to avoid complex dependencies
18
+ jest.mock("../hooks/use-tambo-v1-send-message", () => ({
19
+ useTamboV1SendMessage: jest.fn(() => ({
20
+ mutateAsync: jest.fn(),
21
+ mutate: jest.fn(),
22
+ isPending: false,
23
+ isError: false,
24
+ error: null,
25
+ isSuccess: false,
26
+ reset: jest.fn(),
27
+ })),
28
+ }));
14
29
  describe("TamboV1Provider", () => {
15
- const mockClient = {
16
- apiKey: "test-api-key",
17
- threads: {},
30
+ const mockFetch = async (..._args) => {
31
+ throw new Error("fetch not implemented");
18
32
  };
33
+ const mockClient = new TamboAI({
34
+ apiKey: "test-api-key",
35
+ fetch: mockFetch,
36
+ });
19
37
  beforeEach(() => {
20
38
  jest.mocked(useTamboClient).mockReturnValue(mockClient);
21
39
  });
@@ -114,5 +132,62 @@ describe("TamboV1Provider", () => {
114
132
  const { result } = renderHook(() => useTamboRegistry(), { wrapper });
115
133
  expect(result.current.onCallUnregisteredTool).toBe(onCallUnregisteredTool);
116
134
  });
135
+ it("registers static resources when provided", () => {
136
+ const resources = [
137
+ {
138
+ uri: "resource://test/example",
139
+ name: "Test Resource",
140
+ description: "A test resource",
141
+ mimeType: "text/plain",
142
+ },
143
+ ];
144
+ const wrapper = ({ children }) => (React.createElement(TamboV1Provider, { apiKey: "test-api-key", resources: resources }, children));
145
+ const { result } = renderHook(() => useTamboRegistry(), { wrapper });
146
+ expect(result.current.resources).toHaveLength(1);
147
+ expect(result.current.resources[0].uri).toBe("resource://test/example");
148
+ expect(result.current.resources[0].name).toBe("Test Resource");
149
+ });
150
+ it("registers resource source when listResources and getResource provided", () => {
151
+ const listResources = jest.fn().mockResolvedValue({ resources: [] });
152
+ const getResource = jest.fn().mockResolvedValue({ contents: [] });
153
+ const wrapper = ({ children }) => (React.createElement(TamboV1Provider, { apiKey: "test-api-key", listResources: listResources, getResource: getResource }, children));
154
+ const { result } = renderHook(() => useTamboRegistry(), { wrapper });
155
+ expect(result.current.resourceSource).toBeDefined();
156
+ expect(result.current.resourceSource?.listResources).toBe(listResources);
157
+ expect(result.current.resourceSource?.getResource).toBe(getResource);
158
+ });
159
+ it("provides userKey via useTamboV1Config", () => {
160
+ const wrapper = ({ children }) => (React.createElement(TamboV1Provider, { apiKey: "test-api-key", userKey: "my-user-key" }, children));
161
+ const { result } = renderHook(() => useTamboV1Config(), { wrapper });
162
+ expect(result.current.userKey).toBe("my-user-key");
163
+ });
164
+ it("returns undefined userKey from useTamboV1Config when no userKey provided", () => {
165
+ const wrapper = ({ children }) => (React.createElement(TamboV1Provider, { apiKey: "test-api-key" }, children));
166
+ const { result } = renderHook(() => useTamboV1Config(), { wrapper });
167
+ expect(result.current.userKey).toBeUndefined();
168
+ });
169
+ it("provides context helpers via useTamboContextHelpers hook", async () => {
170
+ const contextHelpers = {
171
+ getUserName: () => "Test User",
172
+ getCurrentTime: () => new Date().toISOString(),
173
+ };
174
+ const wrapper = ({ children }) => (React.createElement(TamboV1Provider, { apiKey: "test-api-key", contextHelpers: contextHelpers }, children));
175
+ const { result, rerender } = renderHook(() => useTamboContextHelpers(), {
176
+ wrapper,
177
+ });
178
+ // Helpers are registered via useEffect, so we need to trigger a rerender
179
+ await act(async () => {
180
+ rerender();
181
+ });
182
+ const helpers = result.current.getContextHelpers();
183
+ expect(helpers.getUserName).toBe(contextHelpers.getUserName);
184
+ expect(helpers.getCurrentTime).toBe(contextHelpers.getCurrentTime);
185
+ });
186
+ it("returns empty contextHelpers when none provided", async () => {
187
+ const wrapper = ({ children }) => (React.createElement(TamboV1Provider, { apiKey: "test-api-key" }, children));
188
+ const { result } = renderHook(() => useTamboContextHelpers(), { wrapper });
189
+ const helpers = result.current.getContextHelpers();
190
+ expect(Object.keys(helpers)).toHaveLength(0);
191
+ });
117
192
  });
118
193
  //# sourceMappingURL=tambo-v1-provider.test.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"tambo-v1-provider.test.js","sourceRoot":"","sources":["../../../src/v1/providers/tambo-v1-provider.test.tsx"],"names":[],"mappings":"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","sourcesContent":["import TamboAI from \"@tambo-ai/typescript-sdk\";\nimport { QueryClient, useQueryClient } from \"@tanstack/react-query\";\nimport { renderHook, act } from \"@testing-library/react\";\nimport React from \"react\";\nimport { z } from \"zod\";\nimport { useTamboClient } from \"../../providers/tambo-client-provider\";\nimport { useTamboRegistry } from \"../../providers/tambo-registry-provider\";\nimport { useStreamState, useThreadManagement } from \"./tambo-v1-stream-context\";\nimport { TamboV1Provider } from \"./tambo-v1-provider\";\n\n// Mock the client provider to capture the apiKey\njest.mock(\"../../providers/tambo-client-provider\", () => ({\n useTamboClient: jest.fn(),\n TamboClientProvider: ({ children }: { children: React.ReactNode }) =>\n children,\n}));\n\ndescribe(\"TamboV1Provider\", () => {\n const mockClient = {\n apiKey: \"test-api-key\",\n threads: {},\n } as unknown as TamboAI;\n\n beforeEach(() => {\n jest.mocked(useTamboClient).mockReturnValue(mockClient);\n });\n\n it(\"provides access to registry context\", () => {\n const wrapper = ({ children }: { children: React.ReactNode }) => (\n <TamboV1Provider apiKey=\"test-api-key\">{children}</TamboV1Provider>\n );\n\n const { result } = renderHook(() => useTamboRegistry(), { wrapper });\n\n expect(result.current.componentList).toBeDefined();\n expect(result.current.toolRegistry).toBeDefined();\n expect(typeof result.current.registerComponent).toBe(\"function\");\n expect(typeof result.current.registerTool).toBe(\"function\");\n });\n\n it(\"provides access to stream context\", () => {\n const wrapper = ({ children }: { children: React.ReactNode }) => (\n <TamboV1Provider apiKey=\"test-api-key\">{children}</TamboV1Provider>\n );\n\n const { result } = renderHook(() => useStreamState(), { wrapper });\n\n expect(result.current.threadMap).toBeDefined();\n expect(result.current.currentThreadId).toBeNull();\n });\n\n it(\"manages threads via useThreadManagement\", () => {\n const wrapper = ({ children }: { children: React.ReactNode }) => (\n <TamboV1Provider apiKey=\"test-api-key\">{children}</TamboV1Provider>\n );\n\n const { result } = renderHook(\n () => ({\n state: useStreamState(),\n management: useThreadManagement(),\n }),\n { wrapper },\n );\n\n // Initially no thread\n expect(result.current.state.currentThreadId).toBeNull();\n\n // Initialize and switch to a thread\n act(() => {\n result.current.management.initThread(\"thread_123\");\n result.current.management.switchThread(\"thread_123\");\n });\n\n expect(result.current.state.currentThreadId).toBe(\"thread_123\");\n expect(result.current.state.threadMap.thread_123).toBeDefined();\n });\n\n it(\"provides access to query client\", () => {\n const wrapper = ({ children }: { children: React.ReactNode }) => (\n <TamboV1Provider apiKey=\"test-api-key\">{children}</TamboV1Provider>\n );\n\n const { result } = renderHook(() => useQueryClient(), { wrapper });\n\n expect(result.current).toBeInstanceOf(QueryClient);\n });\n\n it(\"uses custom query client when provided\", () => {\n const customClient = new QueryClient();\n const wrapper = ({ children }: { children: React.ReactNode }) => (\n <TamboV1Provider apiKey=\"test-api-key\" queryClient={customClient}>\n {children}\n </TamboV1Provider>\n );\n\n const { result } = renderHook(() => useQueryClient(), { wrapper });\n\n expect(result.current).toBe(customClient);\n });\n\n it(\"registers components when provided\", () => {\n const TestComponent = () => <div>Test</div>;\n const propsSchema = z.object({\n title: z.string().describe(\"The title\"),\n });\n const components = [\n {\n name: \"TestComponent\",\n description: \"A test component\",\n component: TestComponent,\n propsSchema,\n },\n ];\n\n const wrapper = ({ children }: { children: React.ReactNode }) => (\n <TamboV1Provider apiKey=\"test-api-key\" components={components}>\n {children}\n </TamboV1Provider>\n );\n\n const { result } = renderHook(() => useTamboRegistry(), { wrapper });\n\n expect(result.current.componentList.TestComponent).toBeDefined();\n expect(result.current.componentList.TestComponent.name).toBe(\n \"TestComponent\",\n );\n });\n\n it(\"registers tools when provided\", () => {\n const inputSchema = z.object({\n query: z.string().describe(\"Search query\"),\n });\n const outputSchema = z.string().describe(\"Result string\");\n const tools = [\n {\n name: \"testTool\",\n description: \"A test tool\",\n tool: async () => \"result\",\n inputSchema,\n outputSchema,\n },\n ];\n\n const wrapper = ({ children }: { children: React.ReactNode }) => (\n <TamboV1Provider apiKey=\"test-api-key\" tools={tools}>\n {children}\n </TamboV1Provider>\n );\n\n const { result } = renderHook(() => useTamboRegistry(), { wrapper });\n\n expect(result.current.toolRegistry.testTool).toBeDefined();\n expect(result.current.toolRegistry.testTool.name).toBe(\"testTool\");\n });\n\n it(\"registers MCP servers when provided\", () => {\n const mcpServers = [\n { url: \"https://mcp.example.com\", name: \"Example MCP\" },\n ];\n\n const wrapper = ({ children }: { children: React.ReactNode }) => (\n <TamboV1Provider apiKey=\"test-api-key\" mcpServers={mcpServers}>\n {children}\n </TamboV1Provider>\n );\n\n const { result } = renderHook(() => useTamboRegistry(), { wrapper });\n\n expect(result.current.mcpServerInfos).toHaveLength(1);\n expect(result.current.mcpServerInfos[0].url).toBe(\n \"https://mcp.example.com\",\n );\n });\n\n it(\"provides onCallUnregisteredTool to registry\", () => {\n const onCallUnregisteredTool = jest\n .fn()\n .mockResolvedValue(\"fallback result\");\n\n const wrapper = ({ children }: { children: React.ReactNode }) => (\n <TamboV1Provider\n apiKey=\"test-api-key\"\n onCallUnregisteredTool={onCallUnregisteredTool}\n >\n {children}\n </TamboV1Provider>\n );\n\n const { result } = renderHook(() => useTamboRegistry(), { wrapper });\n\n expect(result.current.onCallUnregisteredTool).toBe(onCallUnregisteredTool);\n });\n});\n"]}
1
+ {"version":3,"file":"tambo-v1-provider.test.js","sourceRoot":"","sources":["../../../src/v1/providers/tambo-v1-provider.test.tsx"],"names":[],"mappings":"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","sourcesContent":["import TamboAI from \"@tambo-ai/typescript-sdk\";\nimport { QueryClient, useQueryClient } from \"@tanstack/react-query\";\nimport { renderHook, act } from \"@testing-library/react\";\nimport React from \"react\";\nimport { z } from \"zod\";\nimport { useTamboClient } from \"../../providers/tambo-client-provider\";\nimport { useTamboRegistry } from \"../../providers/tambo-registry-provider\";\nimport { useTamboContextHelpers } from \"../../providers/tambo-context-helpers-provider\";\nimport { useStreamState, useThreadManagement } from \"./tambo-v1-stream-context\";\nimport { TamboV1Provider, useTamboV1Config } from \"./tambo-v1-provider\";\n\n// Mock the client provider to capture the apiKey\njest.mock(\"../../providers/tambo-client-provider\", () => ({\n useTamboClient: jest.fn(),\n useTamboQueryClient: jest.fn(() => new QueryClient()),\n TamboClientProvider: ({ children }: { children: React.ReactNode }) =>\n children,\n}));\n\n// Mock useTamboV1SendMessage to avoid complex dependencies\njest.mock(\"../hooks/use-tambo-v1-send-message\", () => ({\n useTamboV1SendMessage: jest.fn(() => ({\n mutateAsync: jest.fn(),\n mutate: jest.fn(),\n isPending: false,\n isError: false,\n error: null,\n isSuccess: false,\n reset: jest.fn(),\n })),\n}));\n\ndescribe(\"TamboV1Provider\", () => {\n const mockFetch: typeof fetch = async (..._args) => {\n throw new Error(\"fetch not implemented\");\n };\n\n const mockClient = new TamboAI({\n apiKey: \"test-api-key\",\n fetch: mockFetch,\n });\n\n beforeEach(() => {\n jest.mocked(useTamboClient).mockReturnValue(mockClient);\n });\n\n it(\"provides access to registry context\", () => {\n const wrapper = ({ children }: { children: React.ReactNode }) => (\n <TamboV1Provider apiKey=\"test-api-key\">{children}</TamboV1Provider>\n );\n\n const { result } = renderHook(() => useTamboRegistry(), { wrapper });\n\n expect(result.current.componentList).toBeDefined();\n expect(result.current.toolRegistry).toBeDefined();\n expect(typeof result.current.registerComponent).toBe(\"function\");\n expect(typeof result.current.registerTool).toBe(\"function\");\n });\n\n it(\"provides access to stream context\", () => {\n const wrapper = ({ children }: { children: React.ReactNode }) => (\n <TamboV1Provider apiKey=\"test-api-key\">{children}</TamboV1Provider>\n );\n\n const { result } = renderHook(() => useStreamState(), { wrapper });\n\n expect(result.current.threadMap).toBeDefined();\n expect(result.current.currentThreadId).toBeNull();\n });\n\n it(\"manages threads via useThreadManagement\", () => {\n const wrapper = ({ children }: { children: React.ReactNode }) => (\n <TamboV1Provider apiKey=\"test-api-key\">{children}</TamboV1Provider>\n );\n\n const { result } = renderHook(\n () => ({\n state: useStreamState(),\n management: useThreadManagement(),\n }),\n { wrapper },\n );\n\n // Initially no thread\n expect(result.current.state.currentThreadId).toBeNull();\n\n // Initialize and switch to a thread\n act(() => {\n result.current.management.initThread(\"thread_123\");\n result.current.management.switchThread(\"thread_123\");\n });\n\n expect(result.current.state.currentThreadId).toBe(\"thread_123\");\n expect(result.current.state.threadMap.thread_123).toBeDefined();\n });\n\n it(\"provides access to query client\", () => {\n const wrapper = ({ children }: { children: React.ReactNode }) => (\n <TamboV1Provider apiKey=\"test-api-key\">{children}</TamboV1Provider>\n );\n\n const { result } = renderHook(() => useQueryClient(), { wrapper });\n\n expect(result.current).toBeInstanceOf(QueryClient);\n });\n\n it(\"uses custom query client when provided\", () => {\n const customClient = new QueryClient();\n const wrapper = ({ children }: { children: React.ReactNode }) => (\n <TamboV1Provider apiKey=\"test-api-key\" queryClient={customClient}>\n {children}\n </TamboV1Provider>\n );\n\n const { result } = renderHook(() => useQueryClient(), { wrapper });\n\n expect(result.current).toBe(customClient);\n });\n\n it(\"registers components when provided\", () => {\n const TestComponent = () => <div>Test</div>;\n const propsSchema = z.object({\n title: z.string().describe(\"The title\"),\n });\n const components = [\n {\n name: \"TestComponent\",\n description: \"A test component\",\n component: TestComponent,\n propsSchema,\n },\n ];\n\n const wrapper = ({ children }: { children: React.ReactNode }) => (\n <TamboV1Provider apiKey=\"test-api-key\" components={components}>\n {children}\n </TamboV1Provider>\n );\n\n const { result } = renderHook(() => useTamboRegistry(), { wrapper });\n\n expect(result.current.componentList.TestComponent).toBeDefined();\n expect(result.current.componentList.TestComponent.name).toBe(\n \"TestComponent\",\n );\n });\n\n it(\"registers tools when provided\", () => {\n const inputSchema = z.object({\n query: z.string().describe(\"Search query\"),\n });\n const outputSchema = z.string().describe(\"Result string\");\n const tools = [\n {\n name: \"testTool\",\n description: \"A test tool\",\n tool: async () => \"result\",\n inputSchema,\n outputSchema,\n },\n ];\n\n const wrapper = ({ children }: { children: React.ReactNode }) => (\n <TamboV1Provider apiKey=\"test-api-key\" tools={tools}>\n {children}\n </TamboV1Provider>\n );\n\n const { result } = renderHook(() => useTamboRegistry(), { wrapper });\n\n expect(result.current.toolRegistry.testTool).toBeDefined();\n expect(result.current.toolRegistry.testTool.name).toBe(\"testTool\");\n });\n\n it(\"registers MCP servers when provided\", () => {\n const mcpServers = [\n { url: \"https://mcp.example.com\", name: \"Example MCP\" },\n ];\n\n const wrapper = ({ children }: { children: React.ReactNode }) => (\n <TamboV1Provider apiKey=\"test-api-key\" mcpServers={mcpServers}>\n {children}\n </TamboV1Provider>\n );\n\n const { result } = renderHook(() => useTamboRegistry(), { wrapper });\n\n expect(result.current.mcpServerInfos).toHaveLength(1);\n expect(result.current.mcpServerInfos[0].url).toBe(\n \"https://mcp.example.com\",\n );\n });\n\n it(\"provides onCallUnregisteredTool to registry\", () => {\n const onCallUnregisteredTool = jest\n .fn()\n .mockResolvedValue(\"fallback result\");\n\n const wrapper = ({ children }: { children: React.ReactNode }) => (\n <TamboV1Provider\n apiKey=\"test-api-key\"\n onCallUnregisteredTool={onCallUnregisteredTool}\n >\n {children}\n </TamboV1Provider>\n );\n\n const { result } = renderHook(() => useTamboRegistry(), { wrapper });\n\n expect(result.current.onCallUnregisteredTool).toBe(onCallUnregisteredTool);\n });\n\n it(\"registers static resources when provided\", () => {\n const resources = [\n {\n uri: \"resource://test/example\",\n name: \"Test Resource\",\n description: \"A test resource\",\n mimeType: \"text/plain\",\n },\n ];\n\n const wrapper = ({ children }: { children: React.ReactNode }) => (\n <TamboV1Provider apiKey=\"test-api-key\" resources={resources}>\n {children}\n </TamboV1Provider>\n );\n\n const { result } = renderHook(() => useTamboRegistry(), { wrapper });\n\n expect(result.current.resources).toHaveLength(1);\n expect(result.current.resources[0].uri).toBe(\"resource://test/example\");\n expect(result.current.resources[0].name).toBe(\"Test Resource\");\n });\n\n it(\"registers resource source when listResources and getResource provided\", () => {\n const listResources = jest.fn().mockResolvedValue({ resources: [] });\n const getResource = jest.fn().mockResolvedValue({ contents: [] });\n\n const wrapper = ({ children }: { children: React.ReactNode }) => (\n <TamboV1Provider\n apiKey=\"test-api-key\"\n listResources={listResources}\n getResource={getResource}\n >\n {children}\n </TamboV1Provider>\n );\n\n const { result } = renderHook(() => useTamboRegistry(), { wrapper });\n\n expect(result.current.resourceSource).toBeDefined();\n expect(result.current.resourceSource?.listResources).toBe(listResources);\n expect(result.current.resourceSource?.getResource).toBe(getResource);\n });\n\n it(\"provides userKey via useTamboV1Config\", () => {\n const wrapper = ({ children }: { children: React.ReactNode }) => (\n <TamboV1Provider apiKey=\"test-api-key\" userKey=\"my-user-key\">\n {children}\n </TamboV1Provider>\n );\n\n const { result } = renderHook(() => useTamboV1Config(), { wrapper });\n\n expect(result.current.userKey).toBe(\"my-user-key\");\n });\n\n it(\"returns undefined userKey from useTamboV1Config when no userKey provided\", () => {\n const wrapper = ({ children }: { children: React.ReactNode }) => (\n <TamboV1Provider apiKey=\"test-api-key\">{children}</TamboV1Provider>\n );\n\n const { result } = renderHook(() => useTamboV1Config(), { wrapper });\n\n expect(result.current.userKey).toBeUndefined();\n });\n\n it(\"provides context helpers via useTamboContextHelpers hook\", async () => {\n const contextHelpers = {\n getUserName: () => \"Test User\",\n getCurrentTime: () => new Date().toISOString(),\n };\n\n const wrapper = ({ children }: { children: React.ReactNode }) => (\n <TamboV1Provider apiKey=\"test-api-key\" contextHelpers={contextHelpers}>\n {children}\n </TamboV1Provider>\n );\n\n const { result, rerender } = renderHook(() => useTamboContextHelpers(), {\n wrapper,\n });\n\n // Helpers are registered via useEffect, so we need to trigger a rerender\n await act(async () => {\n rerender();\n });\n\n const helpers = result.current.getContextHelpers();\n expect(helpers.getUserName).toBe(contextHelpers.getUserName);\n expect(helpers.getCurrentTime).toBe(contextHelpers.getCurrentTime);\n });\n\n it(\"returns empty contextHelpers when none provided\", async () => {\n const wrapper = ({ children }: { children: React.ReactNode }) => (\n <TamboV1Provider apiKey=\"test-api-key\">{children}</TamboV1Provider>\n );\n\n const { result } = renderHook(() => useTamboContextHelpers(), { wrapper });\n\n const helpers = result.current.getContextHelpers();\n expect(Object.keys(helpers)).toHaveLength(0);\n });\n});\n"]}
@@ -5,7 +5,7 @@
5
5
  * Provides state and dispatch to child components via separate contexts
6
6
  * following the split-context pattern for optimal re-render performance.
7
7
  */
8
- import React, { type ReactNode, type Dispatch } from "react";
8
+ import React from "react";
9
9
  import { type StreamState, type StreamAction } from "../utils/event-accumulator";
10
10
  import type { TamboV1Thread } from "../types/thread";
11
11
  /**
@@ -36,27 +36,36 @@ export interface ThreadManagement {
36
36
  * Props for TamboV1StreamProvider
37
37
  */
38
38
  export interface TamboV1StreamProviderProps {
39
- children: ReactNode;
39
+ children: React.ReactNode;
40
40
  /**
41
- * Initial thread state (optional).
42
- * If not provided, an empty thread will be created.
41
+ * Optional override for stream state (primarily for tests).
42
+ * If provided, you must also provide `dispatch`.
43
43
  */
44
- initialThread?: Partial<TamboV1Thread>;
44
+ state?: StreamState;
45
45
  /**
46
- * Thread ID for the stream context.
47
- * Used to initialize the thread if initialThread is not provided.
46
+ * Optional override for stream dispatch (primarily for tests).
47
+ * If provided, you must also provide `state`.
48
48
  */
49
- threadId?: string;
49
+ dispatch?: React.Dispatch<StreamAction>;
50
+ /**
51
+ * Optional override for thread management functions (primarily for tests).
52
+ */
53
+ threadManagement?: ThreadManagement;
50
54
  }
51
55
  /**
52
56
  * Provider component for stream state management.
53
57
  *
54
58
  * Uses useReducer with streamReducer to accumulate AG-UI events into
55
59
  * thread state. Provides state, dispatch, and thread management via separate contexts.
60
+ *
61
+ * Thread management is done programmatically via the hooks:
62
+ * - startNewThread() - Start a new conversation
63
+ * - switchThread(threadId) - Switch to an existing thread
64
+ * - initThread(threadId) - Initialize a thread for receiving events
56
65
  * @returns JSX element wrapping children with stream contexts
57
66
  * @example
58
67
  * ```tsx
59
- * <TamboV1StreamProvider threadId="thread_123">
68
+ * <TamboV1StreamProvider>
60
69
  * <ChatInterface />
61
70
  * </TamboV1StreamProvider>
62
71
  * ```
@@ -107,7 +116,7 @@ export declare function useStreamState(): StreamState;
107
116
  * }
108
117
  * ```
109
118
  */
110
- export declare function useStreamDispatch(): Dispatch<StreamAction>;
119
+ export declare function useStreamDispatch(): React.Dispatch<StreamAction>;
111
120
  /**
112
121
  * Hook to access thread management functions.
113
122
  *
@@ -1 +1 @@
1
- {"version":3,"file":"tambo-v1-stream-context.d.ts","sourceRoot":"","sources":["../../../src/v1/providers/tambo-v1-stream-context.tsx"],"names":[],"mappings":"AAEA;;;;;;GAMG;AAIH,OAAO,KAAK,EAAE,EAMZ,KAAK,SAAS,EACd,KAAK,QAAQ,EACd,MAAM,OAAO,CAAC;AACf,OAAO,EAGL,KAAK,WAAW,EAChB,KAAK,YAAY,EAClB,MAAM,4BAA4B,CAAC;AACpC,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAErD;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B;;;;;OAKG;IACH,UAAU,EAAE,CACV,QAAQ,EAAE,MAAM,EAChB,aAAa,CAAC,EAAE,OAAO,CAAC,aAAa,CAAC,KACnC,IAAI,CAAC;IAEV;;;;OAIG;IACH,YAAY,EAAE,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,KAAK,IAAI,CAAC;IAEhD;;;;OAIG;IACH,cAAc,EAAE,MAAM,MAAM,CAAC;CAC9B;AAsBD;;GAEG;AACH,MAAM,WAAW,0BAA0B;IACzC,QAAQ,EAAE,SAAS,CAAC;IAEpB;;;OAGG;IACH,aAAa,CAAC,EAAE,OAAO,CAAC,aAAa,CAAC,CAAC;IAEvC;;;OAGG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AA0CD;;;;;;;;;;;;GAYG;AACH,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,0BAA0B,qBAkDtE;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,cAAc,IAAI,WAAW,CAQ5C;AAED;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,wBAAgB,iBAAiB,IAAI,QAAQ,CAAC,YAAY,CAAC,CAU1D;AAED;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,wBAAgB,mBAAmB,IAAI,gBAAgB,CAUtD"}
1
+ {"version":3,"file":"tambo-v1-stream-context.d.ts","sourceRoot":"","sources":["../../../src/v1/providers/tambo-v1-stream-context.tsx"],"names":[],"mappings":"AAEA;;;;;;GAMG;AAEH,OAAO,KAMN,MAAM,OAAO,CAAC;AACf,OAAO,EAEL,KAAK,WAAW,EAChB,KAAK,YAAY,EAClB,MAAM,4BAA4B,CAAC;AACpC,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAErD;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B;;;;;OAKG;IACH,UAAU,EAAE,CACV,QAAQ,EAAE,MAAM,EAChB,aAAa,CAAC,EAAE,OAAO,CAAC,aAAa,CAAC,KACnC,IAAI,CAAC;IAEV;;;;OAIG;IACH,YAAY,EAAE,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,KAAK,IAAI,CAAC;IAEhD;;;;OAIG;IACH,cAAc,EAAE,MAAM,MAAM,CAAC;CAC9B;AAqBD;;GAEG;AACH,MAAM,WAAW,0BAA0B;IACzC,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;IAE1B;;;OAGG;IACH,KAAK,CAAC,EAAE,WAAW,CAAC;IAEpB;;;OAGG;IACH,QAAQ,CAAC,EAAE,KAAK,CAAC,QAAQ,CAAC,YAAY,CAAC,CAAC;IAExC;;OAEG;IACH,gBAAgB,CAAC,EAAE,gBAAgB,CAAC;CACrC;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,0BAA0B,qBAiFtE;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,cAAc,IAAI,WAAW,CAQ5C;AAED;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,wBAAgB,iBAAiB,IAAI,KAAK,CAAC,QAAQ,CAAC,YAAY,CAAC,CAUhE;AAED;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,wBAAgB,mBAAmB,IAAI,gBAAgB,CAUtD"}
@@ -6,10 +6,8 @@
6
6
  * Provides state and dispatch to child components via separate contexts
7
7
  * following the split-context pattern for optimal re-render performance.
8
8
  */
9
- // React is used implicitly for JSX transformation (jsx: "react" in tsconfig)
10
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
11
9
  import React, { createContext, useReducer, useContext, useMemo, useCallback, } from "react";
12
- import { streamReducer, createInitialThreadState, } from "../utils/event-accumulator";
10
+ import { streamReducer, } from "../utils/event-accumulator";
13
11
  /**
14
12
  * Context for accessing stream state (read-only).
15
13
  * Separated from dispatch context to prevent unnecessary re-renders.
@@ -25,78 +23,70 @@ const StreamDispatchContext = createContext(null);
25
23
  * Separated from state to prevent unnecessary re-renders.
26
24
  */
27
25
  const ThreadManagementContext = createContext(null);
28
- /**
29
- * Creates initial stream state from props.
30
- * @param props - Provider props
31
- * @returns Initial stream state
32
- */
33
- function createInitialState(props) {
34
- const { initialThread, threadId } = props;
35
- // Initialize with empty threadMap
36
- const threadMap = {};
37
- // If threadId is provided, initialize that thread
38
- if (threadId) {
39
- // Create initial thread state (immutably)
40
- const baseState = createInitialThreadState(threadId);
41
- // If initial thread data provided, merge it immutably
42
- const threadState = initialThread
43
- ? {
44
- ...baseState,
45
- thread: {
46
- ...baseState.thread,
47
- ...initialThread,
48
- id: threadId, // Always use the provided threadId
49
- },
50
- }
51
- : baseState;
52
- threadMap[threadId] = threadState;
53
- }
54
- return {
55
- threadMap,
56
- currentThreadId: threadId ?? null,
57
- };
58
- }
59
26
  /**
60
27
  * Provider component for stream state management.
61
28
  *
62
29
  * Uses useReducer with streamReducer to accumulate AG-UI events into
63
30
  * thread state. Provides state, dispatch, and thread management via separate contexts.
31
+ *
32
+ * Thread management is done programmatically via the hooks:
33
+ * - startNewThread() - Start a new conversation
34
+ * - switchThread(threadId) - Switch to an existing thread
35
+ * - initThread(threadId) - Initialize a thread for receiving events
64
36
  * @returns JSX element wrapping children with stream contexts
65
37
  * @example
66
38
  * ```tsx
67
- * <TamboV1StreamProvider threadId="thread_123">
39
+ * <TamboV1StreamProvider>
68
40
  * <ChatInterface />
69
41
  * </TamboV1StreamProvider>
70
42
  * ```
71
43
  */
72
44
  export function TamboV1StreamProvider(props) {
73
- const { children } = props;
74
- const initialState = useMemo(() => createInitialState(props),
75
- // Only recompute if threadId changes
76
- // eslint-disable-next-line react-hooks/exhaustive-deps
77
- [props.threadId]);
78
- const [state, dispatch] = useReducer(streamReducer, initialState);
45
+ const { children, state: providedState, dispatch: providedDispatch } = props;
46
+ if ((providedState && !providedDispatch) ||
47
+ (!providedState && providedDispatch)) {
48
+ throw new Error("TamboV1StreamProvider requires both state and dispatch when overriding");
49
+ }
50
+ if (props.threadManagement) {
51
+ const { initThread, switchThread, startNewThread } = props.threadManagement;
52
+ if (typeof initThread !== "function" ||
53
+ typeof switchThread !== "function" ||
54
+ typeof startNewThread !== "function") {
55
+ throw new Error("TamboV1StreamProvider: threadManagement override is missing required methods");
56
+ }
57
+ }
58
+ // Create stable initial state - only computed once on mount
59
+ const [state, dispatch] = useReducer(streamReducer, undefined,
60
+ // Lazy initializer function
61
+ () => ({
62
+ threadMap: {},
63
+ currentThreadId: null,
64
+ }));
65
+ const activeState = providedState ?? state;
66
+ const activeDispatch = providedDispatch ?? dispatch;
79
67
  // Thread management functions
80
68
  const initThread = useCallback((threadId, initialThread) => {
81
- dispatch({ type: "INIT_THREAD", threadId, initialThread });
82
- }, []);
69
+ activeDispatch({ type: "INIT_THREAD", threadId, initialThread });
70
+ }, [activeDispatch]);
83
71
  const switchThread = useCallback((threadId) => {
84
- dispatch({ type: "SET_CURRENT_THREAD", threadId });
85
- }, []);
72
+ activeDispatch({ type: "SET_CURRENT_THREAD", threadId });
73
+ }, [activeDispatch]);
86
74
  const startNewThread = useCallback(() => {
87
75
  const tempId = `temp_${crypto.randomUUID()}`;
88
76
  // Use atomic START_NEW_THREAD action to prevent race conditions
89
77
  // when multiple calls happen concurrently (e.g., double-click)
90
- dispatch({ type: "START_NEW_THREAD", threadId: tempId });
78
+ activeDispatch({ type: "START_NEW_THREAD", threadId: tempId });
91
79
  return tempId;
92
- }, []);
93
- const threadManagement = useMemo(() => ({
94
- initThread,
95
- switchThread,
96
- startNewThread,
97
- }), [initThread, switchThread, startNewThread]);
98
- return (React.createElement(StreamStateContext.Provider, { value: state },
99
- React.createElement(StreamDispatchContext.Provider, { value: dispatch },
80
+ }, [activeDispatch]);
81
+ const threadManagement = useMemo(() => {
82
+ return (props.threadManagement ?? {
83
+ initThread,
84
+ switchThread,
85
+ startNewThread,
86
+ });
87
+ }, [props.threadManagement, initThread, switchThread, startNewThread]);
88
+ return (React.createElement(StreamStateContext.Provider, { value: activeState },
89
+ React.createElement(StreamDispatchContext.Provider, { value: activeDispatch },
100
90
  React.createElement(ThreadManagementContext.Provider, { value: threadManagement }, children))));
101
91
  }
102
92
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"tambo-v1-stream-context.js","sourceRoot":"","sources":["../../../src/v1/providers/tambo-v1-stream-context.tsx"],"names":[],"mappings":"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","sourcesContent":["\"use client\";\n\n/**\n * Stream Context Provider for v1 API\n *\n * Manages streaming state using React Context and useReducer.\n * Provides state and dispatch to child components via separate contexts\n * following the split-context pattern for optimal re-render performance.\n */\n\n// React is used implicitly for JSX transformation (jsx: \"react\" in tsconfig)\n// eslint-disable-next-line @typescript-eslint/no-unused-vars\nimport React, {\n createContext,\n useReducer,\n useContext,\n useMemo,\n useCallback,\n type ReactNode,\n type Dispatch,\n} from \"react\";\nimport {\n streamReducer,\n createInitialThreadState,\n type StreamState,\n type StreamAction,\n} from \"../utils/event-accumulator\";\nimport type { TamboV1Thread } from \"../types/thread\";\n\n/**\n * Thread management functions exposed by the stream context.\n */\nexport interface ThreadManagement {\n /**\n * Initialize a new thread in the stream context.\n * Use this before sending messages to a new thread.\n * @param threadId - The thread ID to initialize\n * @param initialThread - Optional initial thread data\n */\n initThread: (\n threadId: string,\n initialThread?: Partial<TamboV1Thread>,\n ) => void;\n\n /**\n * Switch the current active thread.\n * Does not fetch thread data - use useTamboV1Thread for that.\n * @param threadId - The thread ID to switch to, or null to clear\n */\n switchThread: (threadId: string | null) => void;\n\n /**\n * Start a new thread (generates a temporary ID).\n * The actual thread ID will be assigned when the first message is sent.\n * @returns The temporary thread ID\n */\n startNewThread: () => string;\n}\n\n/**\n * Context for accessing stream state (read-only).\n * Separated from dispatch context to prevent unnecessary re-renders.\n */\nconst StreamStateContext = createContext<StreamState | null>(null);\n\n/**\n * Context for dispatching events to the stream reducer.\n * Separated from state context to prevent unnecessary re-renders.\n */\nconst StreamDispatchContext = createContext<Dispatch<StreamAction> | null>(\n null,\n);\n\n/**\n * Context for thread management functions.\n * Separated from state to prevent unnecessary re-renders.\n */\nconst ThreadManagementContext = createContext<ThreadManagement | null>(null);\n\n/**\n * Props for TamboV1StreamProvider\n */\nexport interface TamboV1StreamProviderProps {\n children: ReactNode;\n\n /**\n * Initial thread state (optional).\n * If not provided, an empty thread will be created.\n */\n initialThread?: Partial<TamboV1Thread>;\n\n /**\n * Thread ID for the stream context.\n * Used to initialize the thread if initialThread is not provided.\n */\n threadId?: string;\n}\n\n/**\n * Creates initial stream state from props.\n * @param props - Provider props\n * @returns Initial stream state\n */\nfunction createInitialState(props: TamboV1StreamProviderProps): StreamState {\n const { initialThread, threadId } = props;\n\n // Initialize with empty threadMap\n const threadMap: Record<\n string,\n ReturnType<typeof createInitialThreadState>\n > = {};\n\n // If threadId is provided, initialize that thread\n if (threadId) {\n // Create initial thread state (immutably)\n const baseState = createInitialThreadState(threadId);\n\n // If initial thread data provided, merge it immutably\n const threadState = initialThread\n ? {\n ...baseState,\n thread: {\n ...baseState.thread,\n ...initialThread,\n id: threadId, // Always use the provided threadId\n },\n }\n : baseState;\n\n threadMap[threadId] = threadState;\n }\n\n return {\n threadMap,\n currentThreadId: threadId ?? null,\n };\n}\n\n/**\n * Provider component for stream state management.\n *\n * Uses useReducer with streamReducer to accumulate AG-UI events into\n * thread state. Provides state, dispatch, and thread management via separate contexts.\n * @returns JSX element wrapping children with stream contexts\n * @example\n * ```tsx\n * <TamboV1StreamProvider threadId=\"thread_123\">\n * <ChatInterface />\n * </TamboV1StreamProvider>\n * ```\n */\nexport function TamboV1StreamProvider(props: TamboV1StreamProviderProps) {\n const { children } = props;\n\n const initialState = useMemo(\n () => createInitialState(props),\n // Only recompute if threadId changes\n // eslint-disable-next-line react-hooks/exhaustive-deps\n [props.threadId],\n );\n\n const [state, dispatch] = useReducer(streamReducer, initialState);\n\n // Thread management functions\n const initThread = useCallback(\n (threadId: string, initialThread?: Partial<TamboV1Thread>) => {\n dispatch({ type: \"INIT_THREAD\", threadId, initialThread });\n },\n [],\n );\n\n const switchThread = useCallback((threadId: string | null) => {\n dispatch({ type: \"SET_CURRENT_THREAD\", threadId });\n }, []);\n\n const startNewThread = useCallback(() => {\n const tempId = `temp_${crypto.randomUUID()}`;\n // Use atomic START_NEW_THREAD action to prevent race conditions\n // when multiple calls happen concurrently (e.g., double-click)\n dispatch({ type: \"START_NEW_THREAD\", threadId: tempId });\n return tempId;\n }, []);\n\n const threadManagement = useMemo<ThreadManagement>(\n () => ({\n initThread,\n switchThread,\n startNewThread,\n }),\n [initThread, switchThread, startNewThread],\n );\n\n return (\n <StreamStateContext.Provider value={state}>\n <StreamDispatchContext.Provider value={dispatch}>\n <ThreadManagementContext.Provider value={threadManagement}>\n {children}\n </ThreadManagementContext.Provider>\n </StreamDispatchContext.Provider>\n </StreamStateContext.Provider>\n );\n}\n\n/**\n * Hook to access stream state.\n *\n * Must be used within TamboV1StreamProvider.\n * @returns Current stream state\n * @throws {Error} if used outside TamboV1StreamProvider\n * @example\n * ```tsx\n * function ChatMessages() {\n * const { thread, streaming } = useStreamState();\n *\n * return (\n * <div>\n * {thread.messages.map(msg => <Message key={msg.id} message={msg} />)}\n * {streaming.status === 'streaming' && <LoadingIndicator />}\n * </div>\n * );\n * }\n * ```\n */\nexport function useStreamState(): StreamState {\n const context = useContext(StreamStateContext);\n\n if (!context) {\n throw new Error(\"useStreamState must be used within TamboV1StreamProvider\");\n }\n\n return context;\n}\n\n/**\n * Hook to access stream dispatch function.\n *\n * Must be used within TamboV1StreamProvider.\n * @returns Dispatch function for sending events to reducer\n * @throws {Error} if used outside TamboV1StreamProvider\n * @example\n * ```tsx\n * function StreamHandler() {\n * const dispatch = useStreamDispatch();\n *\n * useEffect(() => {\n * async function handleStream() {\n * for await (const event of streamEvents) {\n * dispatch({ type: 'EVENT', event });\n * }\n * }\n * handleStream();\n * }, [dispatch]);\n *\n * return null;\n * }\n * ```\n */\nexport function useStreamDispatch(): Dispatch<StreamAction> {\n const context = useContext(StreamDispatchContext);\n\n if (!context) {\n throw new Error(\n \"useStreamDispatch must be used within TamboV1StreamProvider\",\n );\n }\n\n return context;\n}\n\n/**\n * Hook to access thread management functions.\n *\n * Must be used within TamboV1StreamProvider.\n * @returns Thread management functions\n * @throws {Error} if used outside TamboV1StreamProvider\n * @example\n * ```tsx\n * function ThreadSwitcher() {\n * const { switchThread, startNewThread } = useThreadManagement();\n *\n * return (\n * <div>\n * <button onClick={() => switchThread('thread_123')}>\n * Load Thread\n * </button>\n * <button onClick={startNewThread}>\n * New Chat\n * </button>\n * </div>\n * );\n * }\n * ```\n */\nexport function useThreadManagement(): ThreadManagement {\n const context = useContext(ThreadManagementContext);\n\n if (!context) {\n throw new Error(\n \"useThreadManagement must be used within TamboV1StreamProvider\",\n );\n }\n\n return context;\n}\n"]}
1
+ {"version":3,"file":"tambo-v1-stream-context.js","sourceRoot":"","sources":["../../../src/v1/providers/tambo-v1-stream-context.tsx"],"names":[],"mappings":"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","sourcesContent":["\"use client\";\n\n/**\n * Stream Context Provider for v1 API\n *\n * Manages streaming state using React Context and useReducer.\n * Provides state and dispatch to child components via separate contexts\n * following the split-context pattern for optimal re-render performance.\n */\n\nimport React, {\n createContext,\n useReducer,\n useContext,\n useMemo,\n useCallback,\n} from \"react\";\nimport {\n streamReducer,\n type StreamState,\n type StreamAction,\n} from \"../utils/event-accumulator\";\nimport type { TamboV1Thread } from \"../types/thread\";\n\n/**\n * Thread management functions exposed by the stream context.\n */\nexport interface ThreadManagement {\n /**\n * Initialize a new thread in the stream context.\n * Use this before sending messages to a new thread.\n * @param threadId - The thread ID to initialize\n * @param initialThread - Optional initial thread data\n */\n initThread: (\n threadId: string,\n initialThread?: Partial<TamboV1Thread>,\n ) => void;\n\n /**\n * Switch the current active thread.\n * Does not fetch thread data - use useTamboV1Thread for that.\n * @param threadId - The thread ID to switch to, or null to clear\n */\n switchThread: (threadId: string | null) => void;\n\n /**\n * Start a new thread (generates a temporary ID).\n * The actual thread ID will be assigned when the first message is sent.\n * @returns The temporary thread ID\n */\n startNewThread: () => string;\n}\n\n/**\n * Context for accessing stream state (read-only).\n * Separated from dispatch context to prevent unnecessary re-renders.\n */\nconst StreamStateContext = createContext<StreamState | null>(null);\n\n/**\n * Context for dispatching events to the stream reducer.\n * Separated from state context to prevent unnecessary re-renders.\n */\nconst StreamDispatchContext =\n createContext<React.Dispatch<StreamAction> | null>(null);\n\n/**\n * Context for thread management functions.\n * Separated from state to prevent unnecessary re-renders.\n */\nconst ThreadManagementContext = createContext<ThreadManagement | null>(null);\n\n/**\n * Props for TamboV1StreamProvider\n */\nexport interface TamboV1StreamProviderProps {\n children: React.ReactNode;\n\n /**\n * Optional override for stream state (primarily for tests).\n * If provided, you must also provide `dispatch`.\n */\n state?: StreamState;\n\n /**\n * Optional override for stream dispatch (primarily for tests).\n * If provided, you must also provide `state`.\n */\n dispatch?: React.Dispatch<StreamAction>;\n\n /**\n * Optional override for thread management functions (primarily for tests).\n */\n threadManagement?: ThreadManagement;\n}\n\n/**\n * Provider component for stream state management.\n *\n * Uses useReducer with streamReducer to accumulate AG-UI events into\n * thread state. Provides state, dispatch, and thread management via separate contexts.\n *\n * Thread management is done programmatically via the hooks:\n * - startNewThread() - Start a new conversation\n * - switchThread(threadId) - Switch to an existing thread\n * - initThread(threadId) - Initialize a thread for receiving events\n * @returns JSX element wrapping children with stream contexts\n * @example\n * ```tsx\n * <TamboV1StreamProvider>\n * <ChatInterface />\n * </TamboV1StreamProvider>\n * ```\n */\nexport function TamboV1StreamProvider(props: TamboV1StreamProviderProps) {\n const { children, state: providedState, dispatch: providedDispatch } = props;\n\n if (\n (providedState && !providedDispatch) ||\n (!providedState && providedDispatch)\n ) {\n throw new Error(\n \"TamboV1StreamProvider requires both state and dispatch when overriding\",\n );\n }\n\n if (props.threadManagement) {\n const { initThread, switchThread, startNewThread } = props.threadManagement;\n if (\n typeof initThread !== \"function\" ||\n typeof switchThread !== \"function\" ||\n typeof startNewThread !== \"function\"\n ) {\n throw new Error(\n \"TamboV1StreamProvider: threadManagement override is missing required methods\",\n );\n }\n }\n\n // Create stable initial state - only computed once on mount\n const [state, dispatch] = useReducer(\n streamReducer,\n undefined,\n // Lazy initializer function\n () => ({\n threadMap: {},\n currentThreadId: null,\n }),\n );\n\n const activeState = providedState ?? state;\n const activeDispatch = providedDispatch ?? dispatch;\n\n // Thread management functions\n const initThread = useCallback(\n (threadId: string, initialThread?: Partial<TamboV1Thread>) => {\n activeDispatch({ type: \"INIT_THREAD\", threadId, initialThread });\n },\n [activeDispatch],\n );\n\n const switchThread = useCallback(\n (threadId: string | null) => {\n activeDispatch({ type: \"SET_CURRENT_THREAD\", threadId });\n },\n [activeDispatch],\n );\n\n const startNewThread = useCallback(() => {\n const tempId = `temp_${crypto.randomUUID()}`;\n // Use atomic START_NEW_THREAD action to prevent race conditions\n // when multiple calls happen concurrently (e.g., double-click)\n activeDispatch({ type: \"START_NEW_THREAD\", threadId: tempId });\n return tempId;\n }, [activeDispatch]);\n\n const threadManagement = useMemo<ThreadManagement>(() => {\n return (\n props.threadManagement ?? {\n initThread,\n switchThread,\n startNewThread,\n }\n );\n }, [props.threadManagement, initThread, switchThread, startNewThread]);\n\n return (\n <StreamStateContext.Provider value={activeState}>\n <StreamDispatchContext.Provider value={activeDispatch}>\n <ThreadManagementContext.Provider value={threadManagement}>\n {children}\n </ThreadManagementContext.Provider>\n </StreamDispatchContext.Provider>\n </StreamStateContext.Provider>\n );\n}\n\n/**\n * Hook to access stream state.\n *\n * Must be used within TamboV1StreamProvider.\n * @returns Current stream state\n * @throws {Error} if used outside TamboV1StreamProvider\n * @example\n * ```tsx\n * function ChatMessages() {\n * const { thread, streaming } = useStreamState();\n *\n * return (\n * <div>\n * {thread.messages.map(msg => <Message key={msg.id} message={msg} />)}\n * {streaming.status === 'streaming' && <LoadingIndicator />}\n * </div>\n * );\n * }\n * ```\n */\nexport function useStreamState(): StreamState {\n const context = useContext(StreamStateContext);\n\n if (!context) {\n throw new Error(\"useStreamState must be used within TamboV1StreamProvider\");\n }\n\n return context;\n}\n\n/**\n * Hook to access stream dispatch function.\n *\n * Must be used within TamboV1StreamProvider.\n * @returns Dispatch function for sending events to reducer\n * @throws {Error} if used outside TamboV1StreamProvider\n * @example\n * ```tsx\n * function StreamHandler() {\n * const dispatch = useStreamDispatch();\n *\n * useEffect(() => {\n * async function handleStream() {\n * for await (const event of streamEvents) {\n * dispatch({ type: 'EVENT', event });\n * }\n * }\n * handleStream();\n * }, [dispatch]);\n *\n * return null;\n * }\n * ```\n */\nexport function useStreamDispatch(): React.Dispatch<StreamAction> {\n const context = useContext(StreamDispatchContext);\n\n if (!context) {\n throw new Error(\n \"useStreamDispatch must be used within TamboV1StreamProvider\",\n );\n }\n\n return context;\n}\n\n/**\n * Hook to access thread management functions.\n *\n * Must be used within TamboV1StreamProvider.\n * @returns Thread management functions\n * @throws {Error} if used outside TamboV1StreamProvider\n * @example\n * ```tsx\n * function ThreadSwitcher() {\n * const { switchThread, startNewThread } = useThreadManagement();\n *\n * return (\n * <div>\n * <button onClick={() => switchThread('thread_123')}>\n * Load Thread\n * </button>\n * <button onClick={startNewThread}>\n * New Chat\n * </button>\n * </div>\n * );\n * }\n * ```\n */\nexport function useThreadManagement(): ThreadManagement {\n const context = useContext(ThreadManagementContext);\n\n if (!context) {\n throw new Error(\n \"useThreadManagement must be used within TamboV1StreamProvider\",\n );\n }\n\n return context;\n}\n"]}
@@ -1,7 +1,7 @@
1
1
  import { EventType } from "@ag-ui/core";
2
2
  import { renderHook, act } from "@testing-library/react";
3
3
  import React from "react";
4
- import { TamboV1StreamProvider, useStreamState, useStreamDispatch, } from "./tambo-v1-stream-context";
4
+ import { TamboV1StreamProvider, useStreamState, useStreamDispatch, useThreadManagement, } from "./tambo-v1-stream-context";
5
5
  describe("TamboV1StreamProvider", () => {
6
6
  describe("useStreamState", () => {
7
7
  it("throws when used outside provider", () => {
@@ -14,33 +14,51 @@ describe("TamboV1StreamProvider", () => {
14
14
  }).toThrow("useStreamState must be used within TamboV1StreamProvider");
15
15
  consoleSpy.mockRestore();
16
16
  });
17
- it("returns initial state with empty threadMap when no threadId", () => {
17
+ it("returns initial state with empty threadMap", () => {
18
18
  const wrapper = ({ children }) => (React.createElement(TamboV1StreamProvider, null, children));
19
19
  const { result } = renderHook(() => useStreamState(), { wrapper });
20
20
  expect(result.current.threadMap).toEqual({});
21
21
  expect(result.current.currentThreadId).toBeNull();
22
22
  });
23
- it("initializes thread when threadId is provided", () => {
24
- const wrapper = ({ children }) => (React.createElement(TamboV1StreamProvider, { threadId: "thread_123" }, children));
25
- const { result } = renderHook(() => useStreamState(), { wrapper });
26
- expect(result.current.currentThreadId).toBe("thread_123");
27
- expect(result.current.threadMap.thread_123).toBeDefined();
28
- expect(result.current.threadMap.thread_123.thread.id).toBe("thread_123");
29
- expect(result.current.threadMap.thread_123.thread.status).toBe("idle");
30
- expect(result.current.threadMap.thread_123.thread.messages).toEqual([]);
23
+ it("initializes thread via dispatch", () => {
24
+ const wrapper = ({ children }) => (React.createElement(TamboV1StreamProvider, null, children));
25
+ const { result } = renderHook(() => ({
26
+ state: useStreamState(),
27
+ dispatch: useStreamDispatch(),
28
+ }), { wrapper });
29
+ act(() => {
30
+ result.current.dispatch({
31
+ type: "INIT_THREAD",
32
+ threadId: "thread_123",
33
+ });
34
+ });
35
+ expect(result.current.state.threadMap.thread_123).toBeDefined();
36
+ expect(result.current.state.threadMap.thread_123.thread.id).toBe("thread_123");
37
+ expect(result.current.state.threadMap.thread_123.thread.status).toBe("idle");
38
+ expect(result.current.state.threadMap.thread_123.thread.messages).toEqual([]);
31
39
  });
32
- it("merges initialThread with default state", () => {
33
- const wrapper = ({ children }) => (React.createElement(TamboV1StreamProvider, { threadId: "thread_123", initialThread: {
34
- title: "Test Thread",
35
- metadata: { key: "value" },
36
- } }, children));
37
- const { result } = renderHook(() => useStreamState(), { wrapper });
38
- expect(result.current.threadMap.thread_123.thread.title).toBe("Test Thread");
39
- expect(result.current.threadMap.thread_123.thread.metadata).toEqual({
40
+ it("initializes thread with initial data via dispatch", () => {
41
+ const wrapper = ({ children }) => (React.createElement(TamboV1StreamProvider, null, children));
42
+ const { result } = renderHook(() => ({
43
+ state: useStreamState(),
44
+ dispatch: useStreamDispatch(),
45
+ }), { wrapper });
46
+ act(() => {
47
+ result.current.dispatch({
48
+ type: "INIT_THREAD",
49
+ threadId: "thread_123",
50
+ initialThread: {
51
+ title: "Test Thread",
52
+ metadata: { key: "value" },
53
+ },
54
+ });
55
+ });
56
+ expect(result.current.state.threadMap.thread_123.thread.title).toBe("Test Thread");
57
+ expect(result.current.state.threadMap.thread_123.thread.metadata).toEqual({
40
58
  key: "value",
41
59
  });
42
60
  // Default values should still be set
43
- expect(result.current.threadMap.thread_123.thread.status).toBe("idle");
61
+ expect(result.current.state.threadMap.thread_123.thread.status).toBe("idle");
44
62
  });
45
63
  });
46
64
  describe("useStreamDispatch", () => {
@@ -54,11 +72,18 @@ describe("TamboV1StreamProvider", () => {
54
72
  consoleSpy.mockRestore();
55
73
  });
56
74
  it("dispatches events to update state", () => {
57
- const wrapper = ({ children }) => (React.createElement(TamboV1StreamProvider, { threadId: "thread_123" }, children));
75
+ const wrapper = ({ children }) => (React.createElement(TamboV1StreamProvider, null, children));
58
76
  const { result } = renderHook(() => ({
59
77
  state: useStreamState(),
60
78
  dispatch: useStreamDispatch(),
61
79
  }), { wrapper });
80
+ // Initialize the thread first
81
+ act(() => {
82
+ result.current.dispatch({
83
+ type: "INIT_THREAD",
84
+ threadId: "thread_123",
85
+ });
86
+ });
62
87
  const runStartedEvent = {
63
88
  type: EventType.RUN_STARTED,
64
89
  runId: "run_1",
@@ -76,5 +101,55 @@ describe("TamboV1StreamProvider", () => {
76
101
  expect(result.current.state.threadMap.thread_123.streaming.runId).toBe("run_1");
77
102
  });
78
103
  });
104
+ describe("useThreadManagement", () => {
105
+ it("throws when used outside provider", () => {
106
+ const consoleSpy = jest
107
+ .spyOn(console, "error")
108
+ .mockImplementation(() => { });
109
+ expect(() => {
110
+ renderHook(() => useThreadManagement());
111
+ }).toThrow("useThreadManagement must be used within TamboV1StreamProvider");
112
+ consoleSpy.mockRestore();
113
+ });
114
+ it("initThread creates a new thread", () => {
115
+ const wrapper = ({ children }) => (React.createElement(TamboV1StreamProvider, null, children));
116
+ const { result } = renderHook(() => ({
117
+ state: useStreamState(),
118
+ management: useThreadManagement(),
119
+ }), { wrapper });
120
+ act(() => {
121
+ result.current.management.initThread("thread_456");
122
+ });
123
+ expect(result.current.state.threadMap.thread_456).toBeDefined();
124
+ expect(result.current.state.threadMap.thread_456.thread.id).toBe("thread_456");
125
+ });
126
+ it("switchThread changes currentThreadId", () => {
127
+ const wrapper = ({ children }) => (React.createElement(TamboV1StreamProvider, null, children));
128
+ const { result } = renderHook(() => ({
129
+ state: useStreamState(),
130
+ management: useThreadManagement(),
131
+ }), { wrapper });
132
+ // Initialize and switch to a thread
133
+ act(() => {
134
+ result.current.management.initThread("thread_789");
135
+ result.current.management.switchThread("thread_789");
136
+ });
137
+ expect(result.current.state.currentThreadId).toBe("thread_789");
138
+ });
139
+ it("startNewThread creates temp thread and switches to it", () => {
140
+ const wrapper = ({ children }) => (React.createElement(TamboV1StreamProvider, null, children));
141
+ const { result } = renderHook(() => ({
142
+ state: useStreamState(),
143
+ management: useThreadManagement(),
144
+ }), { wrapper });
145
+ let tempId;
146
+ act(() => {
147
+ tempId = result.current.management.startNewThread();
148
+ });
149
+ expect(tempId).toMatch(/^temp_/);
150
+ expect(result.current.state.currentThreadId).toBe(tempId);
151
+ expect(result.current.state.threadMap[tempId]).toBeDefined();
152
+ });
153
+ });
79
154
  });
80
155
  //# sourceMappingURL=tambo-v1-stream-context.test.js.map