@copilotkit/react-core 1.9.2-next.9 → 1.9.3-next.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 (147) hide show
  1. package/CHANGELOG.md +191 -0
  2. package/dist/{chunk-ERXWDCY6.mjs → chunk-36MGCCPZ.mjs} +2 -2
  3. package/dist/{chunk-UBNRUXEK.mjs → chunk-5BSUSFHM.mjs} +2 -2
  4. package/dist/{chunk-G7LYGERN.mjs → chunk-6ZLSC4KB.mjs} +124 -73
  5. package/dist/chunk-6ZLSC4KB.mjs.map +1 -0
  6. package/dist/{chunk-JDEWNLNP.mjs → chunk-BSAVFYRQ.mjs} +11 -11
  7. package/dist/{chunk-JDEWNLNP.mjs.map → chunk-BSAVFYRQ.mjs.map} +1 -1
  8. package/dist/{chunk-JPMIAGI6.mjs → chunk-BVK7PLK6.mjs} +2 -2
  9. package/dist/{chunk-FXK6RQIN.mjs → chunk-CUAFWKTQ.mjs} +4 -4
  10. package/dist/{chunk-XFOTNHYA.mjs → chunk-DKZTPL66.mjs} +2 -2
  11. package/dist/{chunk-XFOTNHYA.mjs.map → chunk-DKZTPL66.mjs.map} +1 -1
  12. package/dist/{chunk-EF5BNM34.mjs → chunk-FN3UA2ZE.mjs} +3 -3
  13. package/dist/{chunk-EXU7GWLC.mjs → chunk-GEE5AMYL.mjs} +9 -9
  14. package/dist/{chunk-WOGURSAL.mjs → chunk-GIMSRCVW.mjs} +64 -23
  15. package/dist/chunk-GIMSRCVW.mjs.map +1 -0
  16. package/dist/{chunk-55QZ2SVJ.mjs → chunk-JWAXDYOW.mjs} +8 -8
  17. package/dist/chunk-JWAXDYOW.mjs.map +1 -0
  18. package/dist/{chunk-3YHYWAHK.mjs → chunk-KIXKBJUV.mjs} +2 -2
  19. package/dist/{chunk-ADIITPD2.mjs → chunk-KLENTCQV.mjs} +34 -8
  20. package/dist/{chunk-ADIITPD2.mjs.map → chunk-KLENTCQV.mjs.map} +1 -1
  21. package/dist/{chunk-OF4SZTLL.mjs → chunk-NGQN3JRJ.mjs} +3 -3
  22. package/dist/{chunk-NQVCZQ5T.mjs → chunk-NJA5ZLAZ.mjs} +27 -8
  23. package/dist/chunk-NJA5ZLAZ.mjs.map +1 -0
  24. package/dist/{chunk-SJJNFYGQ.mjs → chunk-SGF6C7I6.mjs} +4 -4
  25. package/dist/{chunk-CMQV4XNY.mjs → chunk-VDADWRS3.mjs} +2 -2
  26. package/dist/components/copilot-provider/copilot-messages.js +7 -7
  27. package/dist/components/copilot-provider/copilot-messages.js.map +1 -1
  28. package/dist/components/copilot-provider/copilot-messages.mjs +3 -3
  29. package/dist/components/copilot-provider/copilotkit-props.d.ts +14 -9
  30. package/dist/components/copilot-provider/copilotkit-props.js.map +1 -1
  31. package/dist/components/copilot-provider/copilotkit.d.ts +1 -1
  32. package/dist/components/copilot-provider/copilotkit.js +70 -29
  33. package/dist/components/copilot-provider/copilotkit.js.map +1 -1
  34. package/dist/components/copilot-provider/copilotkit.mjs +9 -9
  35. package/dist/components/copilot-provider/index.d.ts +1 -1
  36. package/dist/components/copilot-provider/index.js +70 -29
  37. package/dist/components/copilot-provider/index.js.map +1 -1
  38. package/dist/components/copilot-provider/index.mjs +9 -9
  39. package/dist/components/error-boundary/error-boundary.mjs +2 -2
  40. package/dist/components/index.d.ts +1 -1
  41. package/dist/components/index.js +70 -29
  42. package/dist/components/index.js.map +1 -1
  43. package/dist/components/index.mjs +9 -9
  44. package/dist/context/copilot-context.d.ts +1 -1
  45. package/dist/context/copilot-context.js +1 -1
  46. package/dist/context/copilot-context.js.map +1 -1
  47. package/dist/context/copilot-context.mjs +1 -1
  48. package/dist/context/index.d.ts +1 -1
  49. package/dist/context/index.js +1 -1
  50. package/dist/context/index.js.map +1 -1
  51. package/dist/context/index.mjs +4 -4
  52. package/dist/{copilot-context-3da805ab.d.ts → copilot-context-3ab4fdf5.d.ts} +3 -3
  53. package/dist/hooks/index.d.ts +1 -1
  54. package/dist/hooks/index.js +179 -83
  55. package/dist/hooks/index.js.map +1 -1
  56. package/dist/hooks/index.mjs +23 -23
  57. package/dist/hooks/use-chat.d.ts +1 -1
  58. package/dist/hooks/use-chat.js +220 -169
  59. package/dist/hooks/use-chat.js.map +1 -1
  60. package/dist/hooks/use-chat.mjs +4 -4
  61. package/dist/hooks/use-coagent-state-render.js +1 -1
  62. package/dist/hooks/use-coagent-state-render.js.map +1 -1
  63. package/dist/hooks/use-coagent-state-render.mjs +2 -2
  64. package/dist/hooks/use-coagent.d.ts +1 -1
  65. package/dist/hooks/use-coagent.js +154 -77
  66. package/dist/hooks/use-coagent.js.map +1 -1
  67. package/dist/hooks/use-coagent.mjs +14 -14
  68. package/dist/hooks/use-copilot-action.js +26 -7
  69. package/dist/hooks/use-copilot-action.js.map +1 -1
  70. package/dist/hooks/use-copilot-action.mjs +2 -2
  71. package/dist/hooks/use-copilot-additional-instructions.js +1 -1
  72. package/dist/hooks/use-copilot-additional-instructions.js.map +1 -1
  73. package/dist/hooks/use-copilot-additional-instructions.mjs +2 -2
  74. package/dist/hooks/use-copilot-authenticated-action.js +26 -7
  75. package/dist/hooks/use-copilot-authenticated-action.js.map +1 -1
  76. package/dist/hooks/use-copilot-authenticated-action.mjs +3 -3
  77. package/dist/hooks/use-copilot-chat.d.ts +1 -1
  78. package/dist/hooks/use-copilot-chat.js +128 -77
  79. package/dist/hooks/use-copilot-chat.js.map +1 -1
  80. package/dist/hooks/use-copilot-chat.mjs +13 -13
  81. package/dist/hooks/use-copilot-readable.js +1 -1
  82. package/dist/hooks/use-copilot-readable.js.map +1 -1
  83. package/dist/hooks/use-copilot-readable.mjs +2 -2
  84. package/dist/hooks/use-copilot-runtime-client.d.ts +2 -2
  85. package/dist/hooks/use-copilot-runtime-client.js +7 -7
  86. package/dist/hooks/use-copilot-runtime-client.js.map +1 -1
  87. package/dist/hooks/use-copilot-runtime-client.mjs +1 -1
  88. package/dist/hooks/use-langgraph-interrupt-render.js +1 -1
  89. package/dist/hooks/use-langgraph-interrupt-render.js.map +1 -1
  90. package/dist/hooks/use-langgraph-interrupt-render.mjs +2 -2
  91. package/dist/hooks/use-langgraph-interrupt.d.ts +1 -1
  92. package/dist/hooks/use-langgraph-interrupt.js +128 -77
  93. package/dist/hooks/use-langgraph-interrupt.js.map +1 -1
  94. package/dist/hooks/use-langgraph-interrupt.mjs +14 -14
  95. package/dist/hooks/use-make-copilot-document-readable.js +1 -1
  96. package/dist/hooks/use-make-copilot-document-readable.js.map +1 -1
  97. package/dist/hooks/use-make-copilot-document-readable.mjs +2 -2
  98. package/dist/index.d.ts +1 -1
  99. package/dist/index.js +240 -103
  100. package/dist/index.js.map +1 -1
  101. package/dist/index.mjs +27 -27
  102. package/dist/lib/copilot-task.d.ts +1 -1
  103. package/dist/lib/copilot-task.js.map +1 -1
  104. package/dist/lib/copilot-task.mjs +11 -11
  105. package/dist/lib/index.d.ts +1 -1
  106. package/dist/lib/index.js.map +1 -1
  107. package/dist/lib/index.mjs +11 -11
  108. package/dist/setupTests.d.ts +2 -0
  109. package/dist/setupTests.js +26 -0
  110. package/dist/setupTests.js.map +1 -0
  111. package/dist/setupTests.mjs +24 -0
  112. package/dist/setupTests.mjs.map +1 -0
  113. package/dist/types/interrupt-action.d.ts +1 -1
  114. package/dist/utils/extract.d.ts +1 -1
  115. package/dist/utils/extract.js.map +1 -1
  116. package/dist/utils/extract.mjs +9 -9
  117. package/dist/utils/index.d.ts +1 -1
  118. package/dist/utils/index.js.map +1 -1
  119. package/dist/utils/index.mjs +10 -10
  120. package/jest.config.js +4 -0
  121. package/package.json +6 -3
  122. package/src/components/copilot-provider/__tests__/{copilotkit-trace.test.tsx → copilotkit-error.test.tsx} +17 -17
  123. package/src/components/copilot-provider/copilot-messages.tsx +7 -7
  124. package/src/components/copilot-provider/copilotkit-props.tsx +13 -8
  125. package/src/components/copilot-provider/copilotkit.tsx +61 -19
  126. package/src/context/copilot-context.tsx +4 -4
  127. package/src/hooks/__tests__/use-coagent-config.test.ts +284 -0
  128. package/src/hooks/use-chat.ts +149 -61
  129. package/src/hooks/use-coagent.ts +36 -0
  130. package/src/hooks/use-copilot-action.ts +51 -9
  131. package/src/hooks/use-copilot-runtime-client.ts +11 -12
  132. package/src/setupTests.ts +26 -0
  133. package/tsconfig.json +5 -2
  134. package/dist/chunk-55QZ2SVJ.mjs.map +0 -1
  135. package/dist/chunk-G7LYGERN.mjs.map +0 -1
  136. package/dist/chunk-NQVCZQ5T.mjs.map +0 -1
  137. package/dist/chunk-WOGURSAL.mjs.map +0 -1
  138. /package/dist/{chunk-ERXWDCY6.mjs.map → chunk-36MGCCPZ.mjs.map} +0 -0
  139. /package/dist/{chunk-UBNRUXEK.mjs.map → chunk-5BSUSFHM.mjs.map} +0 -0
  140. /package/dist/{chunk-JPMIAGI6.mjs.map → chunk-BVK7PLK6.mjs.map} +0 -0
  141. /package/dist/{chunk-FXK6RQIN.mjs.map → chunk-CUAFWKTQ.mjs.map} +0 -0
  142. /package/dist/{chunk-EF5BNM34.mjs.map → chunk-FN3UA2ZE.mjs.map} +0 -0
  143. /package/dist/{chunk-EXU7GWLC.mjs.map → chunk-GEE5AMYL.mjs.map} +0 -0
  144. /package/dist/{chunk-3YHYWAHK.mjs.map → chunk-KIXKBJUV.mjs.map} +0 -0
  145. /package/dist/{chunk-OF4SZTLL.mjs.map → chunk-NGQN3JRJ.mjs.map} +0 -0
  146. /package/dist/{chunk-SJJNFYGQ.mjs.map → chunk-SGF6C7I6.mjs.map} +0 -0
  147. /package/dist/{chunk-CMQV4XNY.mjs.map → chunk-VDADWRS3.mjs.map} +0 -0
@@ -272,26 +272,29 @@ export function CopilotKitInternal(cpkProps: CopilotKitProps) {
272
272
  headers,
273
273
  credentials: copilotApiConfig.credentials,
274
274
  showDevConsole: props.showDevConsole ?? false,
275
- onTrace: props.onTrace,
275
+ onError: props.onError,
276
276
  });
277
277
 
278
278
  const [chatSuggestionConfiguration, setChatSuggestionConfiguration] = useState<{
279
279
  [key: string]: CopilotChatSuggestionConfiguration;
280
280
  }>({});
281
281
 
282
- const addChatSuggestionConfiguration = (
283
- id: string,
284
- suggestion: CopilotChatSuggestionConfiguration,
285
- ) => {
286
- setChatSuggestionConfiguration((prev) => ({ ...prev, [id]: suggestion }));
287
- };
282
+ const addChatSuggestionConfiguration = useCallback(
283
+ (id: string, suggestion: CopilotChatSuggestionConfiguration) => {
284
+ setChatSuggestionConfiguration((prev) => ({ ...prev, [id]: suggestion }));
285
+ },
286
+ [setChatSuggestionConfiguration],
287
+ );
288
288
 
289
- const removeChatSuggestionConfiguration = (id: string) => {
290
- setChatSuggestionConfiguration((prev) => {
291
- const { [id]: _, ...rest } = prev;
292
- return rest;
293
- });
294
- };
289
+ const removeChatSuggestionConfiguration = useCallback(
290
+ (id: string) => {
291
+ setChatSuggestionConfiguration((prev) => {
292
+ const { [id]: _, ...rest } = prev;
293
+ return rest;
294
+ });
295
+ },
296
+ [setChatSuggestionConfiguration],
297
+ );
295
298
 
296
299
  const [availableAgents, setAvailableAgents] = useState<Agent[]>([]);
297
300
  const [coagentStates, setCoagentStates] = useState<Record<string, CoagentState>>({});
@@ -389,6 +392,45 @@ export function CopilotKitInternal(cpkProps: CopilotKitProps) {
389
392
 
390
393
  const memoizedChildren = useMemo(() => children, [children]);
391
394
 
395
+ const agentLock = useMemo(() => props.agent ?? null, [props.agent]);
396
+
397
+ const forwardedParameters = useMemo(
398
+ () => props.forwardedParameters ?? {},
399
+ [props.forwardedParameters],
400
+ );
401
+
402
+ const updateExtensions = useCallback(
403
+ (newExtensions: SetStateAction<ExtensionsInput>) => {
404
+ setExtensions((prev: ExtensionsInput) => {
405
+ const resolved = typeof newExtensions === "function" ? newExtensions(prev) : newExtensions;
406
+ const isSameLength = Object.keys(resolved).length === Object.keys(prev).length;
407
+ const isEqual =
408
+ isSameLength &&
409
+ // @ts-ignore
410
+ Object.entries(resolved).every(([key, value]) => prev[key] === value);
411
+
412
+ return isEqual ? prev : resolved;
413
+ });
414
+ },
415
+ [setExtensions],
416
+ );
417
+
418
+ const updateAuthStates = useCallback(
419
+ (newAuthStates: SetStateAction<Record<string, AuthState>>) => {
420
+ setAuthStates((prev) => {
421
+ const resolved = typeof newAuthStates === "function" ? newAuthStates(prev) : newAuthStates;
422
+ const isSameLength = Object.keys(resolved).length === Object.keys(prev).length;
423
+ const isEqual =
424
+ isSameLength &&
425
+ // @ts-ignore
426
+ Object.entries(resolved).every(([key, value]) => prev[key] === value);
427
+
428
+ return isEqual ? prev : resolved;
429
+ });
430
+ },
431
+ [setAuthStates],
432
+ );
433
+
392
434
  return (
393
435
  <CopilotContext.Provider
394
436
  value={{
@@ -425,8 +467,8 @@ export function CopilotKitInternal(cpkProps: CopilotKitProps) {
425
467
  agentSession,
426
468
  setAgentSession,
427
469
  runtimeClient,
428
- forwardedParameters: props.forwardedParameters || {},
429
- agentLock: props.agent || null,
470
+ forwardedParameters,
471
+ agentLock,
430
472
  threadId: internalThreadId,
431
473
  setThreadId,
432
474
  runId,
@@ -435,13 +477,13 @@ export function CopilotKitInternal(cpkProps: CopilotKitProps) {
435
477
  availableAgents,
436
478
  authConfig_c: props.authConfig_c,
437
479
  authStates_c: authStates,
438
- setAuthStates_c: setAuthStates,
480
+ setAuthStates_c: updateAuthStates,
439
481
  extensions,
440
- setExtensions,
482
+ setExtensions: updateExtensions,
441
483
  langGraphInterruptAction,
442
484
  setLangGraphInterruptAction,
443
485
  removeLangGraphInterruptAction,
444
- onTrace: props.onTrace,
486
+ onError: props.onError,
445
487
  }}
446
488
  >
447
489
  <CopilotMessages>{memoizedChildren}</CopilotMessages>
@@ -452,7 +494,7 @@ export function CopilotKitInternal(cpkProps: CopilotKitProps) {
452
494
  export const defaultCopilotContextCategories = ["global"];
453
495
 
454
496
  function entryPointsToFunctionCallHandler(actions: FrontendAction<any>[]): FunctionCallHandler {
455
- return async ({ name, args }) => {
497
+ return async ({ name, args }: { name: string; args: Record<string, any> }) => {
456
498
  let actionsByFunctionName: Record<string, FrontendAction<any>> = {};
457
499
  for (let action of actions) {
458
500
  actionsByFunctionName[action.name] = action;
@@ -1,4 +1,4 @@
1
- import { CopilotCloudConfig, FunctionCallHandler, CopilotTraceHandler } from "@copilotkit/shared";
1
+ import { CopilotCloudConfig, FunctionCallHandler, CopilotErrorHandler } from "@copilotkit/shared";
2
2
  import {
3
3
  ActionRenderProps,
4
4
  CatchAllActionRenderProps,
@@ -195,7 +195,7 @@ export interface CopilotContextParams {
195
195
  /**
196
196
  * The forwarded parameters to use for the task.
197
197
  */
198
- forwardedParameters?: Pick<ForwardedParametersInput, "temperature">;
198
+ forwardedParameters?: Partial<Pick<ForwardedParametersInput, "temperature">>;
199
199
  availableAgents: Agent[];
200
200
 
201
201
  /**
@@ -222,7 +222,7 @@ export interface CopilotContextParams {
222
222
  /**
223
223
  * Optional trace handler for comprehensive debugging and observability.
224
224
  */
225
- onTrace?: CopilotTraceHandler;
225
+ onError?: CopilotErrorHandler;
226
226
  }
227
227
 
228
228
  const emptyCopilotContext: CopilotContextParams = {
@@ -293,7 +293,7 @@ const emptyCopilotContext: CopilotContextParams = {
293
293
  langGraphInterruptAction: null,
294
294
  setLangGraphInterruptAction: () => null,
295
295
  removeLangGraphInterruptAction: () => null,
296
- onTrace: undefined,
296
+ onError: undefined,
297
297
  };
298
298
 
299
299
  export const CopilotContext = React.createContext<CopilotContextParams>(emptyCopilotContext);
@@ -0,0 +1,284 @@
1
+ import { renderHook } from "@testing-library/react";
2
+ import { useCoAgent } from "../use-coagent";
3
+
4
+ // Mock the dependencies
5
+ const mockSetCoagentStatesWithRef = jest.fn();
6
+ const mockAppendMessage = jest.fn();
7
+ const mockRunChatCompletion = jest.fn();
8
+
9
+ jest.mock("../use-copilot-chat", () => ({
10
+ useCopilotChat: () => ({
11
+ appendMessage: mockAppendMessage,
12
+ runChatCompletion: mockRunChatCompletion,
13
+ }),
14
+ }));
15
+
16
+ jest.mock("../use-copilot-runtime-client", () => ({
17
+ useCopilotRuntimeClient: () => ({
18
+ loadAgentState: jest.fn().mockResolvedValue({
19
+ data: { loadAgentState: { state: "{}", threadExists: false } },
20
+ error: null,
21
+ }),
22
+ }),
23
+ }));
24
+
25
+ jest.mock("../../context", () => ({
26
+ useCopilotContext: () => ({
27
+ availableAgents: [],
28
+ coagentStates: {},
29
+ coagentStatesRef: { current: {} },
30
+ setCoagentStatesWithRef: mockSetCoagentStatesWithRef,
31
+ threadId: "test-thread",
32
+ copilotApiConfig: {
33
+ headers: {},
34
+ chatApiEndpoint: "test-endpoint",
35
+ publicApiKey: "test-key",
36
+ },
37
+ showDevConsole: false,
38
+ }),
39
+ useCopilotMessagesContext: () => ({
40
+ messages: [],
41
+ }),
42
+ }));
43
+
44
+ jest.mock("../../components/toast/toast-provider", () => ({
45
+ useToast: () => ({
46
+ setBannerError: jest.fn(),
47
+ }),
48
+ }));
49
+
50
+ jest.mock("../../components/error-boundary/error-utils", () => ({
51
+ useAsyncCallback: (fn: any) => fn,
52
+ }));
53
+
54
+ describe("useCoAgent config synchronization", () => {
55
+ beforeEach(() => {
56
+ jest.clearAllMocks();
57
+ mockSetCoagentStatesWithRef.mockImplementation((updater) => {
58
+ if (typeof updater === "function") {
59
+ updater({});
60
+ }
61
+ });
62
+ });
63
+
64
+ it("should call setCoagentStatesWithRef when config changes", () => {
65
+ const { rerender } = renderHook(
66
+ ({ config }) =>
67
+ useCoAgent({
68
+ name: "test-agent",
69
+ initialState: { count: 0 },
70
+ config,
71
+ }),
72
+ {
73
+ initialProps: { config: { configurable: { model: "gpt-4" } } },
74
+ },
75
+ );
76
+
77
+ // Clear the initial calls
78
+ mockSetCoagentStatesWithRef.mockClear();
79
+
80
+ // Change config
81
+ rerender({ config: { configurable: { model: "gpt-4o" } } });
82
+
83
+ // Should have called setCoagentStatesWithRef with new config
84
+ expect(mockSetCoagentStatesWithRef).toHaveBeenCalledWith(expect.any(Function));
85
+ });
86
+
87
+ it("should not call setCoagentStatesWithRef when config is unchanged", () => {
88
+ const config = { configurable: { model: "gpt-4" } };
89
+
90
+ const { rerender } = renderHook(
91
+ ({ config }) =>
92
+ useCoAgent({
93
+ name: "test-agent",
94
+ initialState: { count: 0 },
95
+ config,
96
+ }),
97
+ {
98
+ initialProps: { config },
99
+ },
100
+ );
101
+
102
+ // Clear the initial calls
103
+ mockSetCoagentStatesWithRef.mockClear();
104
+
105
+ // Re-render with same config
106
+ rerender({ config });
107
+
108
+ // Should not have called setCoagentStatesWithRef
109
+ expect(mockSetCoagentStatesWithRef).not.toHaveBeenCalled();
110
+ });
111
+
112
+ it("should handle backward compatibility with configurable prop", () => {
113
+ const { rerender } = renderHook(
114
+ ({ configurable }) =>
115
+ useCoAgent({
116
+ name: "test-agent",
117
+ initialState: { count: 0 },
118
+ configurable,
119
+ }),
120
+ {
121
+ initialProps: { configurable: { model: "gpt-4" } },
122
+ },
123
+ );
124
+
125
+ // Clear the initial calls
126
+ mockSetCoagentStatesWithRef.mockClear();
127
+
128
+ // Change configurable prop
129
+ rerender({ configurable: { model: "gpt-4o" } });
130
+
131
+ // Should have called setCoagentStatesWithRef
132
+ expect(mockSetCoagentStatesWithRef).toHaveBeenCalledWith(expect.any(Function));
133
+ });
134
+
135
+ it("should update config while preserving other state properties", () => {
136
+ let capturedUpdater: any = null;
137
+
138
+ mockSetCoagentStatesWithRef.mockImplementation((updater) => {
139
+ capturedUpdater = updater;
140
+ return updater;
141
+ });
142
+
143
+ const { rerender } = renderHook(
144
+ ({ config }) =>
145
+ useCoAgent({
146
+ name: "test-agent",
147
+ initialState: { count: 0 },
148
+ config,
149
+ }),
150
+ {
151
+ initialProps: { config: { configurable: { model: "gpt-4" } } },
152
+ },
153
+ );
154
+
155
+ // Clear the initial calls and reset captured updater
156
+ mockSetCoagentStatesWithRef.mockClear();
157
+ capturedUpdater = null;
158
+
159
+ // Change config
160
+ rerender({ config: { configurable: { model: "gpt-4o" } } });
161
+
162
+ // Should have called setCoagentStatesWithRef
163
+ expect(mockSetCoagentStatesWithRef).toHaveBeenCalledWith(expect.any(Function));
164
+ expect(capturedUpdater).toBeTruthy();
165
+
166
+ // Test the updater function behavior
167
+ const prevState = {
168
+ "test-agent": {
169
+ name: "test-agent",
170
+ state: { count: 5 },
171
+ config: { configurable: { model: "gpt-4" } },
172
+ running: true,
173
+ active: true,
174
+ threadId: "thread-123",
175
+ nodeName: "test-node",
176
+ runId: "run-456",
177
+ },
178
+ };
179
+
180
+ const newState = capturedUpdater(prevState);
181
+
182
+ // Verify the state is updated correctly
183
+ expect(newState).toEqual({
184
+ "test-agent": {
185
+ name: "test-agent",
186
+ state: { count: 5 }, // State preserved
187
+ config: { configurable: { model: "gpt-4o" } }, // Config updated
188
+ running: true, // Other properties preserved
189
+ active: true,
190
+ threadId: "thread-123",
191
+ nodeName: "test-node",
192
+ runId: "run-456",
193
+ },
194
+ });
195
+ });
196
+
197
+ it("should create new agent state when agent doesn't exist", () => {
198
+ let capturedUpdater: any = null;
199
+
200
+ mockSetCoagentStatesWithRef.mockImplementation((updater) => {
201
+ capturedUpdater = updater;
202
+ return updater;
203
+ });
204
+
205
+ const { rerender } = renderHook(
206
+ ({ config }) =>
207
+ useCoAgent({
208
+ name: "test-agent",
209
+ initialState: { count: 0 },
210
+ config,
211
+ }),
212
+ {
213
+ initialProps: { config: { configurable: { model: "gpt-4" } } },
214
+ },
215
+ );
216
+
217
+ // Clear the initial calls and reset captured updater
218
+ mockSetCoagentStatesWithRef.mockClear();
219
+ capturedUpdater = null;
220
+
221
+ // Change config
222
+ rerender({ config: { configurable: { model: "gpt-4o" } } });
223
+
224
+ // Should have called setCoagentStatesWithRef
225
+ expect(mockSetCoagentStatesWithRef).toHaveBeenCalledWith(expect.any(Function));
226
+ expect(capturedUpdater).toBeTruthy();
227
+
228
+ // Test the updater function behavior with empty previous state
229
+ const prevState = {}; // No existing agent state
230
+
231
+ const newState = capturedUpdater(prevState);
232
+
233
+ // Verify the state creates a new agent state with default values
234
+ expect(newState).toEqual({
235
+ "test-agent": {
236
+ name: "test-agent",
237
+ state: { count: 0 }, // Uses initialState
238
+ config: { configurable: { model: "gpt-4o" } }, // New config
239
+ running: false, // Default values
240
+ active: false,
241
+ threadId: undefined,
242
+ nodeName: undefined,
243
+ runId: undefined,
244
+ },
245
+ });
246
+ });
247
+
248
+ it("should handle deeply nested config changes", () => {
249
+ const { rerender } = renderHook(
250
+ ({ config }) =>
251
+ useCoAgent({
252
+ name: "test-agent",
253
+ initialState: { count: 0 },
254
+ config,
255
+ }),
256
+ {
257
+ initialProps: {
258
+ config: {
259
+ configurable: {
260
+ model: "gpt-4",
261
+ settings: { temperature: 0.5 },
262
+ },
263
+ },
264
+ },
265
+ },
266
+ );
267
+
268
+ // Clear the initial calls
269
+ mockSetCoagentStatesWithRef.mockClear();
270
+
271
+ // Change nested config
272
+ rerender({
273
+ config: {
274
+ configurable: {
275
+ model: "gpt-4",
276
+ settings: { temperature: 0.7 }, // Only nested property changed
277
+ },
278
+ },
279
+ });
280
+
281
+ // Should detect the nested change
282
+ expect(mockSetCoagentStatesWithRef).toHaveBeenCalledWith(expect.any(Function));
283
+ });
284
+ });