@tambo-ai/react 0.71.0 → 0.72.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 (83) 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-thread-input.d.ts +62 -0
  10. package/dist/v1/hooks/use-tambo-v1-thread-input.d.ts.map +1 -0
  11. package/dist/v1/hooks/use-tambo-v1-thread-input.js +76 -0
  12. package/dist/v1/hooks/use-tambo-v1-thread-input.js.map +1 -0
  13. package/dist/v1/hooks/use-tambo-v1-thread-input.test.d.ts +2 -0
  14. package/dist/v1/hooks/use-tambo-v1-thread-input.test.d.ts.map +1 -0
  15. package/dist/v1/hooks/use-tambo-v1-thread-input.test.js +168 -0
  16. package/dist/v1/hooks/use-tambo-v1-thread-input.test.js.map +1 -0
  17. package/dist/v1/index.d.ts +23 -12
  18. package/dist/v1/index.d.ts.map +1 -1
  19. package/dist/v1/index.js +48 -14
  20. package/dist/v1/index.js.map +1 -1
  21. package/dist/v1/providers/tambo-v1-provider.d.ts +43 -1
  22. package/dist/v1/providers/tambo-v1-provider.d.ts.map +1 -1
  23. package/dist/v1/providers/tambo-v1-provider.js +24 -3
  24. package/dist/v1/providers/tambo-v1-provider.js.map +1 -1
  25. package/dist/v1/providers/tambo-v1-provider.test.js +58 -0
  26. package/dist/v1/providers/tambo-v1-provider.test.js.map +1 -1
  27. package/dist/v1/types/message.d.ts +27 -2
  28. package/dist/v1/types/message.d.ts.map +1 -1
  29. package/dist/v1/types/message.js.map +1 -1
  30. package/dist/v1/utils/component-renderer.d.ts +89 -0
  31. package/dist/v1/utils/component-renderer.d.ts.map +1 -0
  32. package/dist/v1/utils/component-renderer.js +216 -0
  33. package/dist/v1/utils/component-renderer.js.map +1 -0
  34. package/dist/v1/utils/component-renderer.test.d.ts +2 -0
  35. package/dist/v1/utils/component-renderer.test.d.ts.map +1 -0
  36. package/dist/v1/utils/component-renderer.test.js +380 -0
  37. package/dist/v1/utils/component-renderer.test.js.map +1 -0
  38. package/dist/v1/utils/event-accumulator.js +28 -8
  39. package/dist/v1/utils/event-accumulator.js.map +1 -1
  40. package/dist/v1/utils/event-accumulator.test.js +201 -6
  41. package/dist/v1/utils/event-accumulator.test.js.map +1 -1
  42. package/esm/v1/hooks/use-tambo-v1-component-state.d.ts +44 -0
  43. package/esm/v1/hooks/use-tambo-v1-component-state.d.ts.map +1 -0
  44. package/esm/v1/hooks/use-tambo-v1-component-state.js +131 -0
  45. package/esm/v1/hooks/use-tambo-v1-component-state.js.map +1 -0
  46. package/esm/v1/hooks/use-tambo-v1-component-state.test.d.ts +2 -0
  47. package/esm/v1/hooks/use-tambo-v1-component-state.test.d.ts.map +1 -0
  48. package/esm/v1/hooks/use-tambo-v1-component-state.test.js +290 -0
  49. package/esm/v1/hooks/use-tambo-v1-component-state.test.js.map +1 -0
  50. package/esm/v1/hooks/use-tambo-v1-thread-input.d.ts +62 -0
  51. package/esm/v1/hooks/use-tambo-v1-thread-input.d.ts.map +1 -0
  52. package/esm/v1/hooks/use-tambo-v1-thread-input.js +73 -0
  53. package/esm/v1/hooks/use-tambo-v1-thread-input.js.map +1 -0
  54. package/esm/v1/hooks/use-tambo-v1-thread-input.test.d.ts +2 -0
  55. package/esm/v1/hooks/use-tambo-v1-thread-input.test.d.ts.map +1 -0
  56. package/esm/v1/hooks/use-tambo-v1-thread-input.test.js +166 -0
  57. package/esm/v1/hooks/use-tambo-v1-thread-input.test.js.map +1 -0
  58. package/esm/v1/index.d.ts +23 -12
  59. package/esm/v1/index.d.ts.map +1 -1
  60. package/esm/v1/index.js +31 -12
  61. package/esm/v1/index.js.map +1 -1
  62. package/esm/v1/providers/tambo-v1-provider.d.ts +43 -1
  63. package/esm/v1/providers/tambo-v1-provider.d.ts.map +1 -1
  64. package/esm/v1/providers/tambo-v1-provider.js +24 -4
  65. package/esm/v1/providers/tambo-v1-provider.js.map +1 -1
  66. package/esm/v1/providers/tambo-v1-provider.test.js +59 -1
  67. package/esm/v1/providers/tambo-v1-provider.test.js.map +1 -1
  68. package/esm/v1/types/message.d.ts +27 -2
  69. package/esm/v1/types/message.d.ts.map +1 -1
  70. package/esm/v1/types/message.js.map +1 -1
  71. package/esm/v1/utils/component-renderer.d.ts +89 -0
  72. package/esm/v1/utils/component-renderer.d.ts.map +1 -0
  73. package/esm/v1/utils/component-renderer.js +175 -0
  74. package/esm/v1/utils/component-renderer.js.map +1 -0
  75. package/esm/v1/utils/component-renderer.test.d.ts +2 -0
  76. package/esm/v1/utils/component-renderer.test.d.ts.map +1 -0
  77. package/esm/v1/utils/component-renderer.test.js +375 -0
  78. package/esm/v1/utils/component-renderer.test.js.map +1 -0
  79. package/esm/v1/utils/event-accumulator.js +28 -8
  80. package/esm/v1/utils/event-accumulator.js.map +1 -1
  81. package/esm/v1/utils/event-accumulator.test.js +201 -6
  82. package/esm/v1/utils/event-accumulator.test.js.map +1 -1
  83. package/package.json +1 -1
@@ -0,0 +1,375 @@
1
+ // React import needed for JSX transform (jsxImportSource is not set to react-jsx)
2
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
3
+ import React from "react";
4
+ import { render, screen } from "@testing-library/react";
5
+ import { isComponentContent, renderComponentContent, renderMessageContent, renderMessageComponents, useV1ComponentContent, useV1ComponentContentOptional, } from "./component-renderer";
6
+ // Test component that displays its props
7
+ function TestComponent({ title, count }) {
8
+ return (React.createElement("div", { "data-testid": "test-component" },
9
+ React.createElement("span", { "data-testid": "title" }, title),
10
+ React.createElement("span", { "data-testid": "count" }, count)));
11
+ }
12
+ // Test loading component
13
+ function TestLoadingComponent() {
14
+ return React.createElement("div", { "data-testid": "loading" }, "Loading...");
15
+ }
16
+ // Test component that uses the content context
17
+ function ContextAwareComponent() {
18
+ const context = useV1ComponentContent();
19
+ return (React.createElement("div", { "data-testid": "context-aware" },
20
+ React.createElement("span", { "data-testid": "componentId" }, context.componentId),
21
+ React.createElement("span", { "data-testid": "threadId" }, context.threadId),
22
+ React.createElement("span", { "data-testid": "messageId" }, context.messageId)));
23
+ }
24
+ const mockRegistry = {
25
+ TestComponent: {
26
+ component: TestComponent,
27
+ name: "TestComponent",
28
+ description: "A test component",
29
+ props: {},
30
+ contextTools: [],
31
+ },
32
+ TestWithLoading: {
33
+ component: TestComponent,
34
+ loadingComponent: TestLoadingComponent,
35
+ name: "TestWithLoading",
36
+ description: "A test component with loading state",
37
+ props: {},
38
+ contextTools: [],
39
+ },
40
+ ContextAware: {
41
+ component: ContextAwareComponent,
42
+ name: "ContextAware",
43
+ description: "A context-aware component",
44
+ props: {},
45
+ contextTools: [],
46
+ },
47
+ };
48
+ describe("isComponentContent", () => {
49
+ it("returns true for component content", () => {
50
+ const content = {
51
+ type: "component",
52
+ id: "comp_1",
53
+ name: "Test",
54
+ props: {},
55
+ streamingState: "done",
56
+ };
57
+ expect(isComponentContent(content)).toBe(true);
58
+ });
59
+ it("returns false for text content", () => {
60
+ const content = { type: "text", text: "hello" };
61
+ expect(isComponentContent(content)).toBe(false);
62
+ });
63
+ });
64
+ describe("renderComponentContent", () => {
65
+ it("renders a component from registry", () => {
66
+ const content = {
67
+ type: "component",
68
+ id: "comp_1",
69
+ name: "TestComponent",
70
+ props: { title: "Hello", count: 42 },
71
+ streamingState: "done",
72
+ };
73
+ const result = renderComponentContent(content, {
74
+ threadId: "thread_1",
75
+ messageId: "msg_1",
76
+ componentList: mockRegistry,
77
+ });
78
+ expect(result.renderedComponent).not.toBeNull();
79
+ // Render and check output
80
+ render(React.createElement(React.Fragment, null, result.renderedComponent));
81
+ expect(screen.getByTestId("title")).toHaveTextContent("Hello");
82
+ expect(screen.getByTestId("count")).toHaveTextContent("42");
83
+ });
84
+ it("returns null renderedComponent for unknown component", () => {
85
+ const content = {
86
+ type: "component",
87
+ id: "comp_1",
88
+ name: "UnknownComponent",
89
+ props: {},
90
+ streamingState: "done",
91
+ };
92
+ const consoleWarn = jest.spyOn(console, "warn").mockImplementation();
93
+ const result = renderComponentContent(content, {
94
+ threadId: "thread_1",
95
+ messageId: "msg_1",
96
+ componentList: mockRegistry,
97
+ });
98
+ expect(result.renderedComponent).toBeNull();
99
+ expect(consoleWarn).toHaveBeenCalledWith('Component "UnknownComponent" not found in registry');
100
+ consoleWarn.mockRestore();
101
+ });
102
+ it("shows loading component when streaming", () => {
103
+ const content = {
104
+ type: "component",
105
+ id: "comp_1",
106
+ name: "TestWithLoading",
107
+ props: { title: "Loading Test", count: 0 },
108
+ streamingState: "streaming",
109
+ };
110
+ const result = renderComponentContent(content, {
111
+ threadId: "thread_1",
112
+ messageId: "msg_1",
113
+ componentList: mockRegistry,
114
+ });
115
+ render(React.createElement(React.Fragment, null, result.renderedComponent));
116
+ expect(screen.getByTestId("loading")).toHaveTextContent("Loading...");
117
+ });
118
+ it("shows main component when done streaming", () => {
119
+ const content = {
120
+ type: "component",
121
+ id: "comp_1",
122
+ name: "TestWithLoading",
123
+ props: { title: "Done Test", count: 99 },
124
+ streamingState: "done",
125
+ };
126
+ const result = renderComponentContent(content, {
127
+ threadId: "thread_1",
128
+ messageId: "msg_1",
129
+ componentList: mockRegistry,
130
+ });
131
+ render(React.createElement(React.Fragment, null, result.renderedComponent));
132
+ expect(screen.getByTestId("title")).toHaveTextContent("Done Test");
133
+ expect(screen.getByTestId("count")).toHaveTextContent("99");
134
+ });
135
+ it("provides component context to rendered components", () => {
136
+ const content = {
137
+ type: "component",
138
+ id: "comp_123",
139
+ name: "ContextAware",
140
+ props: {},
141
+ streamingState: "done",
142
+ };
143
+ const result = renderComponentContent(content, {
144
+ threadId: "thread_456",
145
+ messageId: "msg_789",
146
+ componentList: mockRegistry,
147
+ });
148
+ render(React.createElement(React.Fragment, null, result.renderedComponent));
149
+ expect(screen.getByTestId("componentId")).toHaveTextContent("comp_123");
150
+ expect(screen.getByTestId("threadId")).toHaveTextContent("thread_456");
151
+ expect(screen.getByTestId("messageId")).toHaveTextContent("msg_789");
152
+ });
153
+ });
154
+ describe("renderMessageContent", () => {
155
+ it("renders component content blocks and passes through others", () => {
156
+ const content = [
157
+ { type: "text", text: "Hello" },
158
+ {
159
+ type: "component",
160
+ id: "comp_1",
161
+ name: "TestComponent",
162
+ props: { title: "Test", count: 1 },
163
+ streamingState: "done",
164
+ },
165
+ { type: "text", text: "World" },
166
+ ];
167
+ const result = renderMessageContent(content, {
168
+ threadId: "thread_1",
169
+ messageId: "msg_1",
170
+ componentList: mockRegistry,
171
+ });
172
+ expect(result).toHaveLength(3);
173
+ expect(result[0]).toEqual({ type: "text", text: "Hello" });
174
+ expect(result[1].renderedComponent).not.toBeNull();
175
+ expect(result[2]).toEqual({ type: "text", text: "World" });
176
+ });
177
+ it("handles multiple component blocks in a message", () => {
178
+ const content = [
179
+ {
180
+ type: "component",
181
+ id: "comp_1",
182
+ name: "TestComponent",
183
+ props: { title: "First", count: 1 },
184
+ streamingState: "done",
185
+ },
186
+ {
187
+ type: "component",
188
+ id: "comp_2",
189
+ name: "TestComponent",
190
+ props: { title: "Second", count: 2 },
191
+ streamingState: "done",
192
+ },
193
+ ];
194
+ const result = renderMessageContent(content, {
195
+ threadId: "thread_1",
196
+ messageId: "msg_1",
197
+ componentList: mockRegistry,
198
+ });
199
+ expect(result).toHaveLength(2);
200
+ expect(result[0].renderedComponent).not.toBeNull();
201
+ expect(result[1].renderedComponent).not.toBeNull();
202
+ });
203
+ it("handles tool_use content blocks unchanged", () => {
204
+ const content = [
205
+ {
206
+ type: "tool_use",
207
+ id: "tool_1",
208
+ name: "search",
209
+ input: { query: "test" },
210
+ },
211
+ ];
212
+ const result = renderMessageContent(content, {
213
+ threadId: "thread_1",
214
+ messageId: "msg_1",
215
+ componentList: mockRegistry,
216
+ });
217
+ expect(result).toHaveLength(1);
218
+ expect(result[0]).toEqual(content[0]);
219
+ });
220
+ });
221
+ describe("renderComponentContent edge cases", () => {
222
+ it("shows main component when streamingState is 'started' (no loading component)", () => {
223
+ const content = {
224
+ type: "component",
225
+ id: "comp_1",
226
+ name: "TestComponent",
227
+ props: { title: "Started", count: 0 },
228
+ streamingState: "started",
229
+ };
230
+ const result = renderComponentContent(content, {
231
+ threadId: "thread_1",
232
+ messageId: "msg_1",
233
+ componentList: mockRegistry,
234
+ });
235
+ // TestComponent has no loading component, so it shows main even when streaming
236
+ render(React.createElement(React.Fragment, null, result.renderedComponent));
237
+ expect(screen.getByTestId("title")).toHaveTextContent("Started");
238
+ });
239
+ it("shows loading component when streamingState is 'started' and loading exists", () => {
240
+ const content = {
241
+ type: "component",
242
+ id: "comp_1",
243
+ name: "TestWithLoading",
244
+ props: { title: "Started", count: 0 },
245
+ streamingState: "started",
246
+ };
247
+ const result = renderComponentContent(content, {
248
+ threadId: "thread_1",
249
+ messageId: "msg_1",
250
+ componentList: mockRegistry,
251
+ });
252
+ render(React.createElement(React.Fragment, null, result.renderedComponent));
253
+ expect(screen.getByTestId("loading")).toHaveTextContent("Loading...");
254
+ });
255
+ it("passes state as initialState prop", () => {
256
+ // Component that reads initialState
257
+ function StatefulComponent({ initialState, }) {
258
+ return (React.createElement("div", { "data-testid": "initial-count" }, initialState?.count ?? "none"));
259
+ }
260
+ const registryWithStateful = {
261
+ StatefulComponent: {
262
+ component: StatefulComponent,
263
+ name: "StatefulComponent",
264
+ description: "A stateful component",
265
+ props: {},
266
+ contextTools: [],
267
+ },
268
+ };
269
+ const content = {
270
+ type: "component",
271
+ id: "comp_1",
272
+ name: "StatefulComponent",
273
+ props: {},
274
+ state: { count: 42 },
275
+ streamingState: "done",
276
+ };
277
+ const result = renderComponentContent(content, {
278
+ threadId: "thread_1",
279
+ messageId: "msg_1",
280
+ componentList: registryWithStateful,
281
+ });
282
+ render(React.createElement(React.Fragment, null, result.renderedComponent));
283
+ expect(screen.getByTestId("initial-count")).toHaveTextContent("42");
284
+ });
285
+ it("preserves original content properties in returned object", () => {
286
+ const content = {
287
+ type: "component",
288
+ id: "comp_custom",
289
+ name: "TestComponent",
290
+ props: { title: "Test", count: 5 },
291
+ state: { value: "preserved" },
292
+ streamingState: "done",
293
+ };
294
+ const result = renderComponentContent(content, {
295
+ threadId: "thread_1",
296
+ messageId: "msg_1",
297
+ componentList: mockRegistry,
298
+ });
299
+ expect(result.id).toBe("comp_custom");
300
+ expect(result.name).toBe("TestComponent");
301
+ expect(result.props).toEqual({ title: "Test", count: 5 });
302
+ expect(result.state).toEqual({ value: "preserved" });
303
+ expect(result.streamingState).toBe("done");
304
+ });
305
+ });
306
+ describe("useV1ComponentContentOptional", () => {
307
+ it("returns null when used outside rendered component", () => {
308
+ function TestConsumer() {
309
+ const context = useV1ComponentContentOptional();
310
+ return (React.createElement("div", { "data-testid": "context" }, context ? "has context" : "no context"));
311
+ }
312
+ render(React.createElement(TestConsumer, null));
313
+ expect(screen.getByTestId("context")).toHaveTextContent("no context");
314
+ });
315
+ });
316
+ describe("renderMessageComponents", () => {
317
+ it("renders all components in a message", () => {
318
+ const message = {
319
+ id: "msg_1",
320
+ role: "assistant",
321
+ content: [
322
+ { type: "text", text: "Here is the weather:" },
323
+ {
324
+ type: "component",
325
+ id: "comp_1",
326
+ name: "TestComponent",
327
+ props: { title: "Weather", count: 72 },
328
+ streamingState: "done",
329
+ },
330
+ ],
331
+ createdAt: "2024-01-01T00:00:00.000Z",
332
+ };
333
+ const result = renderMessageComponents(message, {
334
+ threadId: "thread_1",
335
+ componentList: mockRegistry,
336
+ });
337
+ expect(result.id).toBe("msg_1");
338
+ expect(result.content).toHaveLength(2);
339
+ expect(result.content[0]).toEqual({
340
+ type: "text",
341
+ text: "Here is the weather:",
342
+ });
343
+ expect(result.content[1].renderedComponent).not.toBeNull();
344
+ });
345
+ it("preserves message metadata", () => {
346
+ const message = {
347
+ id: "msg_123",
348
+ role: "assistant",
349
+ content: [],
350
+ createdAt: "2024-01-01T00:00:00.000Z",
351
+ metadata: { custom: "value" },
352
+ };
353
+ const result = renderMessageComponents(message, {
354
+ threadId: "thread_1",
355
+ componentList: mockRegistry,
356
+ });
357
+ expect(result.id).toBe("msg_123");
358
+ expect(result.role).toBe("assistant");
359
+ expect(result.createdAt).toBe("2024-01-01T00:00:00.000Z");
360
+ expect(result.metadata).toEqual({ custom: "value" });
361
+ });
362
+ });
363
+ describe("useV1ComponentContent error handling", () => {
364
+ it("throws when used outside rendered component", () => {
365
+ function TestConsumer() {
366
+ useV1ComponentContent();
367
+ return React.createElement("div", null, "Should not render");
368
+ }
369
+ // Suppress React error boundary logs
370
+ const consoleSpy = jest.spyOn(console, "error").mockImplementation();
371
+ expect(() => render(React.createElement(TestConsumer, null))).toThrow("useV1ComponentContent must be used within a rendered component");
372
+ consoleSpy.mockRestore();
373
+ });
374
+ });
375
+ //# sourceMappingURL=component-renderer.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"component-renderer.test.js","sourceRoot":"","sources":["../../../src/v1/utils/component-renderer.test.tsx"],"names":[],"mappings":"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","sourcesContent":["// React import needed for JSX transform (jsxImportSource is not set to react-jsx)\n// eslint-disable-next-line @typescript-eslint/no-unused-vars\nimport React from \"react\";\nimport { render, screen } from \"@testing-library/react\";\nimport type { ComponentRegistry } from \"../../model/component-metadata\";\nimport type { V1ComponentContent } from \"../types/message\";\nimport {\n isComponentContent,\n renderComponentContent,\n renderMessageContent,\n renderMessageComponents,\n useV1ComponentContent,\n useV1ComponentContentOptional,\n} from \"./component-renderer\";\n\n// Test component that displays its props\nfunction TestComponent({ title, count }: { title: string; count: number }) {\n return (\n <div data-testid=\"test-component\">\n <span data-testid=\"title\">{title}</span>\n <span data-testid=\"count\">{count}</span>\n </div>\n );\n}\n\n// Test loading component\nfunction TestLoadingComponent() {\n return <div data-testid=\"loading\">Loading...</div>;\n}\n\n// Test component that uses the content context\nfunction ContextAwareComponent() {\n const context = useV1ComponentContent();\n return (\n <div data-testid=\"context-aware\">\n <span data-testid=\"componentId\">{context.componentId}</span>\n <span data-testid=\"threadId\">{context.threadId}</span>\n <span data-testid=\"messageId\">{context.messageId}</span>\n </div>\n );\n}\n\nconst mockRegistry: ComponentRegistry = {\n TestComponent: {\n component: TestComponent,\n name: \"TestComponent\",\n description: \"A test component\",\n props: {},\n contextTools: [],\n },\n TestWithLoading: {\n component: TestComponent,\n loadingComponent: TestLoadingComponent,\n name: \"TestWithLoading\",\n description: \"A test component with loading state\",\n props: {},\n contextTools: [],\n },\n ContextAware: {\n component: ContextAwareComponent,\n name: \"ContextAware\",\n description: \"A context-aware component\",\n props: {},\n contextTools: [],\n },\n};\n\ndescribe(\"isComponentContent\", () => {\n it(\"returns true for component content\", () => {\n const content: V1ComponentContent = {\n type: \"component\",\n id: \"comp_1\",\n name: \"Test\",\n props: {},\n streamingState: \"done\",\n };\n expect(isComponentContent(content)).toBe(true);\n });\n\n it(\"returns false for text content\", () => {\n const content = { type: \"text\", text: \"hello\" };\n expect(isComponentContent(content as any)).toBe(false);\n });\n});\n\ndescribe(\"renderComponentContent\", () => {\n it(\"renders a component from registry\", () => {\n const content: V1ComponentContent = {\n type: \"component\",\n id: \"comp_1\",\n name: \"TestComponent\",\n props: { title: \"Hello\", count: 42 },\n streamingState: \"done\",\n };\n\n const result = renderComponentContent(content, {\n threadId: \"thread_1\",\n messageId: \"msg_1\",\n componentList: mockRegistry,\n });\n\n expect(result.renderedComponent).not.toBeNull();\n\n // Render and check output\n render(<>{result.renderedComponent}</>);\n expect(screen.getByTestId(\"title\")).toHaveTextContent(\"Hello\");\n expect(screen.getByTestId(\"count\")).toHaveTextContent(\"42\");\n });\n\n it(\"returns null renderedComponent for unknown component\", () => {\n const content: V1ComponentContent = {\n type: \"component\",\n id: \"comp_1\",\n name: \"UnknownComponent\",\n props: {},\n streamingState: \"done\",\n };\n\n const consoleWarn = jest.spyOn(console, \"warn\").mockImplementation();\n\n const result = renderComponentContent(content, {\n threadId: \"thread_1\",\n messageId: \"msg_1\",\n componentList: mockRegistry,\n });\n\n expect(result.renderedComponent).toBeNull();\n expect(consoleWarn).toHaveBeenCalledWith(\n 'Component \"UnknownComponent\" not found in registry',\n );\n\n consoleWarn.mockRestore();\n });\n\n it(\"shows loading component when streaming\", () => {\n const content: V1ComponentContent = {\n type: \"component\",\n id: \"comp_1\",\n name: \"TestWithLoading\",\n props: { title: \"Loading Test\", count: 0 },\n streamingState: \"streaming\",\n };\n\n const result = renderComponentContent(content, {\n threadId: \"thread_1\",\n messageId: \"msg_1\",\n componentList: mockRegistry,\n });\n\n render(<>{result.renderedComponent}</>);\n expect(screen.getByTestId(\"loading\")).toHaveTextContent(\"Loading...\");\n });\n\n it(\"shows main component when done streaming\", () => {\n const content: V1ComponentContent = {\n type: \"component\",\n id: \"comp_1\",\n name: \"TestWithLoading\",\n props: { title: \"Done Test\", count: 99 },\n streamingState: \"done\",\n };\n\n const result = renderComponentContent(content, {\n threadId: \"thread_1\",\n messageId: \"msg_1\",\n componentList: mockRegistry,\n });\n\n render(<>{result.renderedComponent}</>);\n expect(screen.getByTestId(\"title\")).toHaveTextContent(\"Done Test\");\n expect(screen.getByTestId(\"count\")).toHaveTextContent(\"99\");\n });\n\n it(\"provides component context to rendered components\", () => {\n const content: V1ComponentContent = {\n type: \"component\",\n id: \"comp_123\",\n name: \"ContextAware\",\n props: {},\n streamingState: \"done\",\n };\n\n const result = renderComponentContent(content, {\n threadId: \"thread_456\",\n messageId: \"msg_789\",\n componentList: mockRegistry,\n });\n\n render(<>{result.renderedComponent}</>);\n expect(screen.getByTestId(\"componentId\")).toHaveTextContent(\"comp_123\");\n expect(screen.getByTestId(\"threadId\")).toHaveTextContent(\"thread_456\");\n expect(screen.getByTestId(\"messageId\")).toHaveTextContent(\"msg_789\");\n });\n});\n\ndescribe(\"renderMessageContent\", () => {\n it(\"renders component content blocks and passes through others\", () => {\n const content = [\n { type: \"text\", text: \"Hello\" },\n {\n type: \"component\",\n id: \"comp_1\",\n name: \"TestComponent\",\n props: { title: \"Test\", count: 1 },\n streamingState: \"done\",\n } as V1ComponentContent,\n { type: \"text\", text: \"World\" },\n ];\n\n const result = renderMessageContent(content as any, {\n threadId: \"thread_1\",\n messageId: \"msg_1\",\n componentList: mockRegistry,\n });\n\n expect(result).toHaveLength(3);\n expect(result[0]).toEqual({ type: \"text\", text: \"Hello\" });\n expect((result[1] as any).renderedComponent).not.toBeNull();\n expect(result[2]).toEqual({ type: \"text\", text: \"World\" });\n });\n\n it(\"handles multiple component blocks in a message\", () => {\n const content = [\n {\n type: \"component\",\n id: \"comp_1\",\n name: \"TestComponent\",\n props: { title: \"First\", count: 1 },\n streamingState: \"done\",\n } as V1ComponentContent,\n {\n type: \"component\",\n id: \"comp_2\",\n name: \"TestComponent\",\n props: { title: \"Second\", count: 2 },\n streamingState: \"done\",\n } as V1ComponentContent,\n ];\n\n const result = renderMessageContent(content as any, {\n threadId: \"thread_1\",\n messageId: \"msg_1\",\n componentList: mockRegistry,\n });\n\n expect(result).toHaveLength(2);\n expect((result[0] as V1ComponentContent).renderedComponent).not.toBeNull();\n expect((result[1] as V1ComponentContent).renderedComponent).not.toBeNull();\n });\n\n it(\"handles tool_use content blocks unchanged\", () => {\n const content = [\n {\n type: \"tool_use\",\n id: \"tool_1\",\n name: \"search\",\n input: { query: \"test\" },\n },\n ];\n\n const result = renderMessageContent(content as any, {\n threadId: \"thread_1\",\n messageId: \"msg_1\",\n componentList: mockRegistry,\n });\n\n expect(result).toHaveLength(1);\n expect(result[0]).toEqual(content[0]);\n });\n});\n\ndescribe(\"renderComponentContent edge cases\", () => {\n it(\"shows main component when streamingState is 'started' (no loading component)\", () => {\n const content: V1ComponentContent = {\n type: \"component\",\n id: \"comp_1\",\n name: \"TestComponent\",\n props: { title: \"Started\", count: 0 },\n streamingState: \"started\",\n };\n\n const result = renderComponentContent(content, {\n threadId: \"thread_1\",\n messageId: \"msg_1\",\n componentList: mockRegistry,\n });\n\n // TestComponent has no loading component, so it shows main even when streaming\n render(<>{result.renderedComponent}</>);\n expect(screen.getByTestId(\"title\")).toHaveTextContent(\"Started\");\n });\n\n it(\"shows loading component when streamingState is 'started' and loading exists\", () => {\n const content: V1ComponentContent = {\n type: \"component\",\n id: \"comp_1\",\n name: \"TestWithLoading\",\n props: { title: \"Started\", count: 0 },\n streamingState: \"started\",\n };\n\n const result = renderComponentContent(content, {\n threadId: \"thread_1\",\n messageId: \"msg_1\",\n componentList: mockRegistry,\n });\n\n render(<>{result.renderedComponent}</>);\n expect(screen.getByTestId(\"loading\")).toHaveTextContent(\"Loading...\");\n });\n\n it(\"passes state as initialState prop\", () => {\n // Component that reads initialState\n function StatefulComponent({\n initialState,\n }: {\n initialState?: { count: number };\n }) {\n return (\n <div data-testid=\"initial-count\">{initialState?.count ?? \"none\"}</div>\n );\n }\n\n const registryWithStateful: ComponentRegistry = {\n StatefulComponent: {\n component: StatefulComponent,\n name: \"StatefulComponent\",\n description: \"A stateful component\",\n props: {},\n contextTools: [],\n },\n };\n\n const content: V1ComponentContent = {\n type: \"component\",\n id: \"comp_1\",\n name: \"StatefulComponent\",\n props: {},\n state: { count: 42 },\n streamingState: \"done\",\n };\n\n const result = renderComponentContent(content, {\n threadId: \"thread_1\",\n messageId: \"msg_1\",\n componentList: registryWithStateful,\n });\n\n render(<>{result.renderedComponent}</>);\n expect(screen.getByTestId(\"initial-count\")).toHaveTextContent(\"42\");\n });\n\n it(\"preserves original content properties in returned object\", () => {\n const content: V1ComponentContent = {\n type: \"component\",\n id: \"comp_custom\",\n name: \"TestComponent\",\n props: { title: \"Test\", count: 5 },\n state: { value: \"preserved\" },\n streamingState: \"done\",\n };\n\n const result = renderComponentContent(content, {\n threadId: \"thread_1\",\n messageId: \"msg_1\",\n componentList: mockRegistry,\n });\n\n expect(result.id).toBe(\"comp_custom\");\n expect(result.name).toBe(\"TestComponent\");\n expect(result.props).toEqual({ title: \"Test\", count: 5 });\n expect(result.state).toEqual({ value: \"preserved\" });\n expect(result.streamingState).toBe(\"done\");\n });\n});\n\ndescribe(\"useV1ComponentContentOptional\", () => {\n it(\"returns null when used outside rendered component\", () => {\n function TestConsumer() {\n const context = useV1ComponentContentOptional();\n return (\n <div data-testid=\"context\">\n {context ? \"has context\" : \"no context\"}\n </div>\n );\n }\n\n render(<TestConsumer />);\n expect(screen.getByTestId(\"context\")).toHaveTextContent(\"no context\");\n });\n});\n\ndescribe(\"renderMessageComponents\", () => {\n it(\"renders all components in a message\", () => {\n const message = {\n id: \"msg_1\",\n role: \"assistant\" as const,\n content: [\n { type: \"text\" as const, text: \"Here is the weather:\" },\n {\n type: \"component\",\n id: \"comp_1\",\n name: \"TestComponent\",\n props: { title: \"Weather\", count: 72 },\n streamingState: \"done\",\n } as V1ComponentContent,\n ],\n createdAt: \"2024-01-01T00:00:00.000Z\",\n };\n\n const result = renderMessageComponents(message, {\n threadId: \"thread_1\",\n componentList: mockRegistry,\n });\n\n expect(result.id).toBe(\"msg_1\");\n expect(result.content).toHaveLength(2);\n expect(result.content[0]).toEqual({\n type: \"text\",\n text: \"Here is the weather:\",\n });\n expect(\n (result.content[1] as V1ComponentContent).renderedComponent,\n ).not.toBeNull();\n });\n\n it(\"preserves message metadata\", () => {\n const message = {\n id: \"msg_123\",\n role: \"assistant\" as const,\n content: [],\n createdAt: \"2024-01-01T00:00:00.000Z\",\n metadata: { custom: \"value\" },\n };\n\n const result = renderMessageComponents(message, {\n threadId: \"thread_1\",\n componentList: mockRegistry,\n });\n\n expect(result.id).toBe(\"msg_123\");\n expect(result.role).toBe(\"assistant\");\n expect(result.createdAt).toBe(\"2024-01-01T00:00:00.000Z\");\n expect(result.metadata).toEqual({ custom: \"value\" });\n });\n});\n\ndescribe(\"useV1ComponentContent error handling\", () => {\n it(\"throws when used outside rendered component\", () => {\n function TestConsumer() {\n useV1ComponentContent();\n return <div>Should not render</div>;\n }\n\n // Suppress React error boundary logs\n const consoleSpy = jest.spyOn(console, \"error\").mockImplementation();\n\n expect(() => render(<TestConsumer />)).toThrow(\n \"useV1ComponentContent must be used within a rendered component\",\n );\n\n consoleSpy.mockRestore();\n });\n});\n"]}
@@ -610,7 +610,7 @@ function handleCustomEvent(threadState, event) {
610
610
  }
611
611
  /**
612
612
  * Handle tambo.component.start event.
613
- * Adds a component content block to the message.
613
+ * Adds a component content block to the message with 'started' streaming state.
614
614
  * @param threadState - Current thread state
615
615
  * @param event - Component start event
616
616
  * @returns Updated thread state
@@ -624,12 +624,13 @@ function handleComponentStart(threadState, event) {
624
624
  throw new Error(`Message ${messageId} not found for tambo.component.start event`);
625
625
  }
626
626
  const message = messages[messageIndex];
627
- // Add component content block
627
+ // Add component content block with 'started' streaming state
628
628
  const newContent = {
629
629
  type: "component",
630
630
  id: event.value.componentId,
631
631
  name: event.value.componentName,
632
632
  props: {},
633
+ streamingState: "started",
633
634
  };
634
635
  const updatedMessage = {
635
636
  ...message,
@@ -639,7 +640,7 @@ function handleComponentStart(threadState, event) {
639
640
  }
640
641
  /**
641
642
  * Handle component delta events (both props_delta and state_delta).
642
- * Applies JSON Patch to the specified field.
643
+ * Applies JSON Patch to the specified field and sets streamingState to 'streaming'.
643
644
  * @param threadState - Current thread state
644
645
  * @param event - Component delta event (props or state)
645
646
  * @param field - Which field to update ('props' or 'state')
@@ -663,9 +664,11 @@ function handleComponentDelta(threadState, event, field) {
663
664
  : (componentContent.state ?? {});
664
665
  // Apply JSON Patch
665
666
  const updatedValue = applyJsonPatch(currentValue, operations);
667
+ // Update field and set streaming state to 'streaming'
666
668
  const updatedContent = {
667
669
  ...componentContent,
668
670
  [field]: updatedValue,
671
+ streamingState: "streaming",
669
672
  };
670
673
  const updatedMessage = {
671
674
  ...message,
@@ -675,14 +678,31 @@ function handleComponentDelta(threadState, event, field) {
675
678
  }
676
679
  /**
677
680
  * Handle tambo.component.end event.
678
- * Marks component as complete.
681
+ * Sets component streaming state to 'done'.
679
682
  * @param threadState - Current thread state
680
- * @param _event - Component end event (unused)
683
+ * @param event - Component end event
681
684
  * @returns Updated thread state
682
685
  */
683
- function handleComponentEnd(threadState, _event) {
684
- // For now, this doesn't change state
685
- return threadState;
686
+ function handleComponentEnd(threadState, event) {
687
+ const componentId = event.value.componentId;
688
+ const messages = threadState.thread.messages;
689
+ // Find the component content block
690
+ const { messageIndex, contentIndex } = findContentById(messages, "component", componentId, "tambo.component.end event");
691
+ const message = messages[messageIndex];
692
+ const componentContent = message.content[contentIndex];
693
+ if (componentContent.type !== "component") {
694
+ throw new Error(`Content at index ${contentIndex} is not a component block for tambo.component.end event`);
695
+ }
696
+ // Set streaming state to 'done'
697
+ const updatedContent = {
698
+ ...componentContent,
699
+ streamingState: "done",
700
+ };
701
+ const updatedMessage = {
702
+ ...message,
703
+ content: updateContentAtIndex(message.content, contentIndex, updatedContent),
704
+ };
705
+ return updateThreadMessages(threadState, updateMessageAtIndex(messages, messageIndex, updatedMessage));
686
706
  }
687
707
  /**
688
708
  * Handle tambo.run.awaiting_input event.