@copilotkitnext/angular 0.0.2 → 0.0.5

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 (173) hide show
  1. package/README.md +3 -3
  2. package/dist/README.md +3 -3
  3. package/dist/components/chat/copilot-chat-assistant-message.component.d.ts +10 -10
  4. package/dist/components/chat/copilot-chat-message-view.component.d.ts +42 -42
  5. package/dist/components/chat/copilot-chat-view.component.d.ts +14 -14
  6. package/dist/core/copilotkit.providers.d.ts +1 -1
  7. package/dist/core/copilotkit.service.d.ts +5 -5
  8. package/dist/core/copilotkit.types.d.ts +8 -10
  9. package/dist/directives/copilotkit-frontend-tool.directive.d.ts +1 -1
  10. package/dist/esm2022/components/chat/copilot-chat-assistant-message-buttons.component.mjs +384 -0
  11. package/dist/esm2022/components/chat/copilot-chat-assistant-message-renderer.component.mjs +286 -0
  12. package/dist/esm2022/components/chat/copilot-chat-assistant-message-toolbar.component.mjs +27 -0
  13. package/dist/esm2022/components/chat/copilot-chat-assistant-message.component.mjs +433 -0
  14. package/dist/esm2022/components/chat/copilot-chat-assistant-message.types.mjs +2 -0
  15. package/dist/esm2022/components/chat/copilot-chat-audio-recorder.component.mjs +202 -0
  16. package/dist/esm2022/components/chat/copilot-chat-buttons.component.mjs +321 -0
  17. package/dist/esm2022/components/chat/copilot-chat-input-defaults.mjs +38 -0
  18. package/dist/esm2022/components/chat/copilot-chat-input.component.mjs +666 -0
  19. package/dist/esm2022/components/chat/copilot-chat-input.types.mjs +10 -0
  20. package/dist/esm2022/components/chat/copilot-chat-message-view-cursor.component.mjs +45 -0
  21. package/dist/esm2022/components/chat/copilot-chat-message-view.component.mjs +296 -0
  22. package/dist/esm2022/components/chat/copilot-chat-message-view.types.mjs +2 -0
  23. package/dist/esm2022/components/chat/copilot-chat-textarea.component.mjs +188 -0
  24. package/dist/esm2022/components/chat/copilot-chat-tool-calls-view.component.mjs +216 -0
  25. package/dist/esm2022/components/chat/copilot-chat-toolbar.component.mjs +25 -0
  26. package/dist/esm2022/components/chat/copilot-chat-tools-menu.component.mjs +199 -0
  27. package/dist/esm2022/components/chat/copilot-chat-user-message-branch-navigation.component.mjs +137 -0
  28. package/dist/esm2022/components/chat/copilot-chat-user-message-buttons.component.mjs +207 -0
  29. package/dist/esm2022/components/chat/copilot-chat-user-message-renderer.component.mjs +35 -0
  30. package/dist/esm2022/components/chat/copilot-chat-user-message-toolbar.component.mjs +34 -0
  31. package/dist/esm2022/components/chat/copilot-chat-user-message.component.mjs +341 -0
  32. package/dist/esm2022/components/chat/copilot-chat-user-message.types.mjs +2 -0
  33. package/dist/esm2022/components/chat/copilot-chat-view-disclaimer.component.mjs +52 -0
  34. package/dist/esm2022/components/chat/copilot-chat-view-feather.component.mjs +55 -0
  35. package/dist/esm2022/components/chat/copilot-chat-view-handlers.service.mjs +19 -0
  36. package/dist/esm2022/components/chat/copilot-chat-view-input-container.component.mjs +110 -0
  37. package/dist/esm2022/components/chat/copilot-chat-view-scroll-to-bottom-button.component.mjs +93 -0
  38. package/dist/esm2022/components/chat/copilot-chat-view-scroll-view.component.mjs +443 -0
  39. package/dist/esm2022/components/chat/copilot-chat-view.component.mjs +479 -0
  40. package/dist/esm2022/components/chat/copilot-chat-view.types.mjs +2 -0
  41. package/dist/esm2022/components/chat/copilot-chat.component.mjs +214 -0
  42. package/dist/esm2022/components/copilotkit-tool-render.component.mjs +153 -0
  43. package/dist/esm2022/copilotkitnext-angular.mjs +5 -0
  44. package/dist/esm2022/core/chat-configuration/chat-configuration.providers.mjs +65 -0
  45. package/dist/esm2022/core/chat-configuration/chat-configuration.service.mjs +145 -0
  46. package/dist/esm2022/core/chat-configuration/chat-configuration.types.mjs +26 -0
  47. package/dist/esm2022/core/copilotkit.providers.mjs +34 -0
  48. package/dist/esm2022/core/copilotkit.service.mjs +426 -0
  49. package/dist/esm2022/core/copilotkit.types.mjs +13 -0
  50. package/dist/esm2022/directives/copilotkit-agent-context.directive.mjs +130 -0
  51. package/dist/esm2022/directives/copilotkit-agent.directive.mjs +217 -0
  52. package/dist/esm2022/directives/copilotkit-chat-config.directive.mjs +218 -0
  53. package/dist/esm2022/directives/copilotkit-config.directive.mjs +94 -0
  54. package/dist/esm2022/directives/copilotkit-frontend-tool.directive.mjs +128 -0
  55. package/dist/esm2022/directives/copilotkit-human-in-the-loop.directive.mjs +265 -0
  56. package/dist/esm2022/directives/stick-to-bottom.directive.mjs +181 -0
  57. package/dist/esm2022/index.mjs +70 -0
  58. package/dist/esm2022/lib/directives/tooltip.directive.mjs +211 -0
  59. package/dist/esm2022/lib/slots/copilot-slot.component.mjs +144 -0
  60. package/dist/esm2022/lib/slots/slot.types.mjs +6 -0
  61. package/dist/esm2022/lib/slots/slot.utils.mjs +222 -0
  62. package/dist/esm2022/lib/utils.mjs +10 -0
  63. package/dist/esm2022/services/resize-observer.service.mjs +152 -0
  64. package/dist/esm2022/services/scroll-position.service.mjs +124 -0
  65. package/dist/esm2022/types/frontend-tool.mjs +2 -0
  66. package/dist/esm2022/types/human-in-the-loop.mjs +2 -0
  67. package/dist/esm2022/utils/agent-context.utils.mjs +114 -0
  68. package/dist/esm2022/utils/agent.utils.mjs +204 -0
  69. package/dist/esm2022/utils/chat-config.utils.mjs +186 -0
  70. package/dist/esm2022/utils/copilotkit.utils.mjs +20 -0
  71. package/dist/esm2022/utils/frontend-tool.utils.mjs +224 -0
  72. package/dist/esm2022/utils/human-in-the-loop.utils.mjs +293 -0
  73. package/dist/fesm2022/copilotkitnext-angular.mjs +174 -187
  74. package/dist/fesm2022/copilotkitnext-angular.mjs.map +1 -1
  75. package/dist/utils/frontend-tool.utils.d.ts +1 -1
  76. package/package.json +23 -20
  77. package/vitest.config.mts +32 -21
  78. package/.turbo/turbo-build.log +0 -38
  79. package/.turbo/turbo-check-types.log +0 -0
  80. package/.turbo/turbo-test.log +0 -71
  81. package/ng-package.json +0 -19
  82. package/src/components/chat/__tests__/copilot-chat-assistant-message.component.spec.ts +0 -282
  83. package/src/components/chat/__tests__/copilot-chat-input.component.spec.ts +0 -419
  84. package/src/components/chat/__tests__/copilot-chat-message-view.component.spec.ts +0 -372
  85. package/src/components/chat/__tests__/copilot-chat-user-message.component.spec.ts +0 -249
  86. package/src/components/chat/copilot-chat-assistant-message-buttons.component.ts +0 -292
  87. package/src/components/chat/copilot-chat-assistant-message-renderer.component.ts +0 -472
  88. package/src/components/chat/copilot-chat-assistant-message-toolbar.component.ts +0 -29
  89. package/src/components/chat/copilot-chat-assistant-message.component.ts +0 -463
  90. package/src/components/chat/copilot-chat-assistant-message.types.ts +0 -50
  91. package/src/components/chat/copilot-chat-audio-recorder.component.ts +0 -241
  92. package/src/components/chat/copilot-chat-buttons.component.ts +0 -308
  93. package/src/components/chat/copilot-chat-buttons.component.ts.bak +0 -471
  94. package/src/components/chat/copilot-chat-input-defaults.ts +0 -47
  95. package/src/components/chat/copilot-chat-input.component.ts +0 -512
  96. package/src/components/chat/copilot-chat-input.types.ts +0 -148
  97. package/src/components/chat/copilot-chat-message-view-cursor.component.ts +0 -51
  98. package/src/components/chat/copilot-chat-message-view.component.ts +0 -233
  99. package/src/components/chat/copilot-chat-message-view.types.ts +0 -39
  100. package/src/components/chat/copilot-chat-textarea.component.ts +0 -220
  101. package/src/components/chat/copilot-chat-tool-calls-view.component.ts +0 -261
  102. package/src/components/chat/copilot-chat-toolbar.component.ts +0 -35
  103. package/src/components/chat/copilot-chat-tools-menu.component.ts +0 -185
  104. package/src/components/chat/copilot-chat-user-message-branch-navigation.component.ts +0 -121
  105. package/src/components/chat/copilot-chat-user-message-buttons.component.ts +0 -170
  106. package/src/components/chat/copilot-chat-user-message-renderer.component.ts +0 -37
  107. package/src/components/chat/copilot-chat-user-message-toolbar.component.ts +0 -37
  108. package/src/components/chat/copilot-chat-user-message.component.ts +0 -247
  109. package/src/components/chat/copilot-chat-user-message.types.ts +0 -42
  110. package/src/components/chat/copilot-chat-view-disclaimer.component.ts +0 -51
  111. package/src/components/chat/copilot-chat-view-feather.component.ts +0 -47
  112. package/src/components/chat/copilot-chat-view-handlers.service.ts +0 -14
  113. package/src/components/chat/copilot-chat-view-input-container.component.ts +0 -87
  114. package/src/components/chat/copilot-chat-view-scroll-to-bottom-button.component.ts +0 -79
  115. package/src/components/chat/copilot-chat-view-scroll-view.component.ts +0 -322
  116. package/src/components/chat/copilot-chat-view.component.ts +0 -420
  117. package/src/components/chat/copilot-chat-view.types.ts +0 -52
  118. package/src/components/chat/copilot-chat.component.ts +0 -232
  119. package/src/components/copilotkit-tool-render.component.ts +0 -169
  120. package/src/core/__tests__/copilotkit.service.spec.ts +0 -1051
  121. package/src/core/__tests__/copilotkit.service.wildcard.spec.ts +0 -316
  122. package/src/core/chat-configuration/__tests__/chat-configuration.service.spec.ts +0 -287
  123. package/src/core/chat-configuration/chat-configuration.providers.ts +0 -71
  124. package/src/core/chat-configuration/chat-configuration.service.ts +0 -162
  125. package/src/core/chat-configuration/chat-configuration.types.ts +0 -57
  126. package/src/core/copilotkit.providers.ts +0 -59
  127. package/src/core/copilotkit.service.ts +0 -542
  128. package/src/core/copilotkit.types.ts +0 -132
  129. package/src/directives/__tests__/copilotkit-agent-context.directive.spec.ts +0 -384
  130. package/src/directives/__tests__/copilotkit-agent.directive.spec.ts +0 -253
  131. package/src/directives/__tests__/copilotkit-chat-config.directive.spec.ts +0 -385
  132. package/src/directives/__tests__/copilotkit-config.directive.spec.ts +0 -69
  133. package/src/directives/__tests__/copilotkit-frontend-tool-simple.directive.spec.ts +0 -60
  134. package/src/directives/__tests__/copilotkit-frontend-tool.directive.spec.ts +0 -108
  135. package/src/directives/__tests__/copilotkit-human-in-the-loop.directive.spec.ts +0 -452
  136. package/src/directives/copilotkit-agent-context.directive.ts +0 -138
  137. package/src/directives/copilotkit-agent.directive.ts +0 -225
  138. package/src/directives/copilotkit-chat-config.directive.ts +0 -241
  139. package/src/directives/copilotkit-config.directive.ts +0 -81
  140. package/src/directives/copilotkit-frontend-tool.directive.ts +0 -145
  141. package/src/directives/copilotkit-human-in-the-loop.directive.ts +0 -281
  142. package/src/directives/stick-to-bottom.directive.ts +0 -204
  143. package/src/index.ts +0 -105
  144. package/src/lib/directives/tooltip.directive.ts +0 -292
  145. package/src/lib/slots/__tests__/slot.utils.spec.ts +0 -377
  146. package/src/lib/slots/copilot-slot.component.ts +0 -135
  147. package/src/lib/slots/index.ts +0 -3
  148. package/src/lib/slots/slot.types.ts +0 -64
  149. package/src/lib/slots/slot.utils.ts +0 -289
  150. package/src/lib/utils.ts +0 -10
  151. package/src/public-api.ts +0 -1
  152. package/src/services/resize-observer.service.ts +0 -181
  153. package/src/services/scroll-position.service.ts +0 -169
  154. package/src/styles/globals.css +0 -266
  155. package/src/styles/index.css +0 -3
  156. package/src/test-setup.ts +0 -15
  157. package/src/testing/index.ts +0 -3
  158. package/src/testing/testing.utils.ts +0 -248
  159. package/src/types/frontend-tool.ts +0 -44
  160. package/src/types/human-in-the-loop.ts +0 -52
  161. package/src/utils/__tests__/agent.utils.spec.ts +0 -234
  162. package/src/utils/__tests__/chat-config.utils.spec.ts +0 -306
  163. package/src/utils/__tests__/frontend-tool-inject.spec.ts +0 -350
  164. package/src/utils/__tests__/frontend-tool-integration.spec.ts +0 -199
  165. package/src/utils/__tests__/frontend-tool.utils.spec.ts +0 -272
  166. package/src/utils/__tests__/human-in-the-loop.utils.spec.ts +0 -365
  167. package/src/utils/agent-context.utils.ts +0 -133
  168. package/src/utils/agent.utils.ts +0 -239
  169. package/src/utils/chat-config.utils.ts +0 -221
  170. package/src/utils/copilotkit.utils.ts +0 -20
  171. package/src/utils/frontend-tool.utils.ts +0 -266
  172. package/src/utils/human-in-the-loop.utils.ts +0 -359
  173. package/tsconfig.spec.json +0 -12
@@ -1,272 +0,0 @@
1
- import { TestBed } from "@angular/core/testing";
2
- import { Component } from "@angular/core";
3
- import { describe, it, expect, beforeEach, vi, afterEach } from "vitest";
4
- import { addFrontendTool, removeFrontendTool } from "../frontend-tool.utils";
5
- import { CopilotKitService } from "../../core/copilotkit.service";
6
- import { provideCopilotKit } from "../../core/copilotkit.providers";
7
- import { z } from "zod";
8
-
9
- // Mock CopilotKitCore
10
- vi.mock("@copilotkitnext/core", () => ({
11
- CopilotKitCore: vi.fn().mockImplementation(() => ({
12
- addTool: vi.fn(),
13
- removeTool: vi.fn(),
14
- setRuntimeUrl: vi.fn(),
15
- setHeaders: vi.fn(),
16
- setProperties: vi.fn(),
17
- setAgents: vi.fn(),
18
- subscribe: vi.fn(() => () => {}),
19
- })),
20
- }));
21
-
22
- // Mock component for testing
23
- @Component({
24
- template: `<div>Mock Tool Render</div>`,
25
- standalone: true,
26
- })
27
- class MockRenderComponent {}
28
-
29
- describe("Frontend Tool Utils", () => {
30
- let service: CopilotKitService;
31
- let addToolSpy: any;
32
- let removeToolSpy: any;
33
-
34
- beforeEach(() => {
35
- TestBed.configureTestingModule({
36
- providers: [provideCopilotKit({})],
37
- });
38
-
39
- service = TestBed.inject(CopilotKitService);
40
- addToolSpy = vi.spyOn(service.copilotkit, "addTool");
41
- removeToolSpy = vi.spyOn(service.copilotkit, "removeTool");
42
- });
43
-
44
- afterEach(() => {
45
- vi.clearAllMocks();
46
- });
47
-
48
- describe("addFrontendTool", () => {
49
- it("should add tool to CopilotKit", () => {
50
- const tool = {
51
- name: "testTool",
52
- description: "Test tool",
53
- parameters: z.object({ value: z.string() }),
54
- handler: vi.fn(async () => "result"),
55
- };
56
-
57
- const cleanup = addFrontendTool(service, tool);
58
-
59
- expect(addToolSpy).toHaveBeenCalledWith(tool);
60
- expect(typeof cleanup).toBe("function");
61
-
62
- cleanup();
63
- });
64
-
65
- it("should register render when provided", () => {
66
- const tool = {
67
- name: "renderTool",
68
- description: "Tool with render",
69
- render: MockRenderComponent,
70
- };
71
-
72
- const cleanup = addFrontendTool(service, tool);
73
-
74
- const renders = service.currentRenderToolCalls();
75
- const renderTool = renders.find((r) => r.name === "renderTool");
76
- expect(renderTool).toBeDefined();
77
- expect(renderTool?.render).toBe(MockRenderComponent);
78
-
79
- cleanup();
80
- });
81
-
82
- it("should return cleanup function that removes tool and render", () => {
83
- const tool = {
84
- name: "cleanupTool",
85
- description: "Tool to cleanup",
86
- render: MockRenderComponent,
87
- };
88
-
89
- const cleanup = addFrontendTool(service, tool);
90
-
91
- // Verify tool was added
92
- expect(addToolSpy).toHaveBeenCalledWith(tool);
93
- let renders = service.currentRenderToolCalls();
94
- expect(renders.find((r) => r.name === "cleanupTool")).toBeDefined();
95
-
96
- // Execute cleanup
97
- cleanup();
98
-
99
- // Verify tool was removed
100
- expect(removeToolSpy).toHaveBeenCalledWith("cleanupTool");
101
- renders = service.currentRenderToolCalls();
102
- expect(renders.find((r) => r.name === "cleanupTool")).toBeUndefined();
103
- });
104
-
105
- it("should handle tool without parameters", () => {
106
- const tool = {
107
- name: "noParams",
108
- description: "No parameters tool",
109
- handler: vi.fn(async () => "result"),
110
- };
111
-
112
- const cleanup = addFrontendTool(service, tool);
113
-
114
- expect(addToolSpy).toHaveBeenCalledWith(tool);
115
-
116
- cleanup();
117
- });
118
-
119
- it("should warn about duplicate render names", () => {
120
- const consoleSpy = vi
121
- .spyOn(console, "error")
122
- .mockImplementation(() => {});
123
-
124
- // Pre-register a render
125
- service.setCurrentRenderToolCalls([
126
- {
127
- name: "duplicateTool",
128
- args: z.object({}),
129
- render: MockRenderComponent,
130
- },
131
- ]);
132
-
133
- const tool = {
134
- name: "duplicateTool",
135
- render: MockRenderComponent,
136
- };
137
-
138
- const cleanup = addFrontendTool(service, tool);
139
-
140
- expect(consoleSpy).toHaveBeenCalledWith(
141
- expect.stringContaining("already has a render")
142
- );
143
-
144
- consoleSpy.mockRestore();
145
- cleanup();
146
- });
147
-
148
- it("should handle complex parameter schemas", () => {
149
- const complexSchema = z.object({
150
- user: z.object({
151
- name: z.string(),
152
- age: z.number(),
153
- }),
154
- settings: z.object({
155
- theme: z.enum(["light", "dark"]),
156
- notifications: z.boolean(),
157
- }),
158
- items: z.array(z.string()),
159
- });
160
-
161
- const tool = {
162
- name: "complexTool",
163
- parameters: complexSchema,
164
- handler: vi.fn(),
165
- };
166
-
167
- const cleanup = addFrontendTool(service, tool);
168
-
169
- expect(addToolSpy).toHaveBeenCalledWith(
170
- expect.objectContaining({
171
- name: "complexTool",
172
- parameters: complexSchema,
173
- })
174
- );
175
-
176
- cleanup();
177
- });
178
- });
179
-
180
- describe("removeFrontendTool", () => {
181
- it("should remove tool from CopilotKit", () => {
182
- removeFrontendTool(service, "testTool");
183
- expect(removeToolSpy).toHaveBeenCalledWith("testTool");
184
- });
185
-
186
- it("should remove render if exists", () => {
187
- // Setup a tool with render
188
- service.setCurrentRenderToolCalls([
189
- {
190
- name: "toolWithRender",
191
- args: z.object({}),
192
- render: MockRenderComponent,
193
- },
194
- ]);
195
-
196
- removeFrontendTool(service, "toolWithRender");
197
-
198
- expect(removeToolSpy).toHaveBeenCalledWith("toolWithRender");
199
- const renders = service.currentRenderToolCalls();
200
- expect(renders.find((r) => r.name === "toolWithRender")).toBeUndefined();
201
- });
202
-
203
- it("should handle removing non-existent tool gracefully", () => {
204
- expect(() => {
205
- removeFrontendTool(service, "nonExistent");
206
- }).not.toThrow();
207
-
208
- expect(removeToolSpy).toHaveBeenCalledWith("nonExistent");
209
- });
210
-
211
- it("should only remove specified tool", () => {
212
- // Setup multiple tools
213
- service.setCurrentRenderToolCalls([
214
- { name: "tool1", args: z.object({}), render: MockRenderComponent },
215
- { name: "tool2", args: z.object({}), render: MockRenderComponent },
216
- { name: "tool3", args: z.object({}), render: MockRenderComponent },
217
- ]);
218
-
219
- removeFrontendTool(service, "tool2");
220
-
221
- const renders = service.currentRenderToolCalls();
222
- expect(renders.find((r) => r.name === "tool1")).toBeDefined();
223
- expect(renders.find((r) => r.name === "tool2")).toBeUndefined();
224
- expect(renders.find((r) => r.name === "tool3")).toBeDefined();
225
- });
226
- });
227
-
228
- describe("Service Integration", () => {
229
- it("should work with service render methods", () => {
230
- const tool = {
231
- name: "serviceTool",
232
- render: MockRenderComponent,
233
- };
234
-
235
- // Test registerToolRender
236
- service.registerToolRender("serviceTool", {
237
- name: "serviceTool",
238
- args: z.object({}),
239
- render: MockRenderComponent,
240
- });
241
-
242
- expect(service.getToolRender("serviceTool")).toBeDefined();
243
-
244
- // Test unregisterToolRender
245
- service.unregisterToolRender("serviceTool");
246
- expect(service.getToolRender("serviceTool")).toBeUndefined();
247
- });
248
-
249
- it("should handle multiple tools with renders", () => {
250
- const tools = [
251
- { name: "tool1", render: MockRenderComponent },
252
- { name: "tool2", render: MockRenderComponent },
253
- { name: "tool3", render: MockRenderComponent },
254
- ];
255
-
256
- const cleanups = tools.map((tool) => addFrontendTool(service, tool));
257
-
258
- // All tools should be registered
259
- expect(addToolSpy).toHaveBeenCalledTimes(3);
260
- const renders = service.currentRenderToolCalls();
261
- expect(renders.find((r) => r.name === "tool1")).toBeDefined();
262
- expect(renders.find((r) => r.name === "tool2")).toBeDefined();
263
- expect(renders.find((r) => r.name === "tool3")).toBeDefined();
264
-
265
- // Cleanup all
266
- cleanups.forEach((cleanup) => cleanup());
267
-
268
- // All tools should be removed
269
- expect(removeToolSpy).toHaveBeenCalledTimes(3);
270
- });
271
- });
272
- });
@@ -1,365 +0,0 @@
1
- import { TestBed } from "@angular/core/testing";
2
- import { Component } from "@angular/core";
3
- import { describe, it, expect, beforeEach, vi, afterEach } from "vitest";
4
- import {
5
- registerHumanInTheLoop,
6
- addHumanInTheLoop,
7
- createHumanInTheLoop,
8
- enhancePropsForHumanInTheLoop,
9
- } from "../human-in-the-loop.utils";
10
- import { CopilotKitService } from "../../core/copilotkit.service";
11
- import { provideCopilotKit } from "../../core/copilotkit.providers";
12
- import { z } from "zod";
13
- import {
14
- HumanInTheLoopProps,
15
- ToolCallStatus,
16
- } from "../../core/copilotkit.types";
17
-
18
- // Mock CopilotKitCore
19
- const mockCopilotKitCore = {
20
- addTool: vi.fn(() => "tool-id-123"),
21
- removeTool: vi.fn(),
22
- setRuntimeUrl: vi.fn(),
23
- setHeaders: vi.fn(),
24
- setProperties: vi.fn(),
25
- setAgents: vi.fn(),
26
- getAgent: vi.fn(),
27
- subscribe: vi.fn(() => vi.fn()),
28
- };
29
-
30
- vi.mock("@copilotkitnext/core", () => ({
31
- CopilotKitCore: vi.fn().mockImplementation(() => mockCopilotKitCore),
32
- ToolCallStatus: {
33
- InProgress: "inProgress",
34
- Executing: "executing",
35
- Complete: "complete",
36
- },
37
- }));
38
-
39
- // Test component for rendering
40
- @Component({
41
- selector: "test-approval",
42
- template: `
43
- <div class="approval-dialog">
44
- <p>{{ props.args.action }}</p>
45
- <button *ngIf="props.respond" (click)="approve()">Approve</button>
46
- <button *ngIf="props.respond" (click)="reject()">Reject</button>
47
- <span class="status">{{ props.status }}</span>
48
- </div>
49
- `,
50
- standalone: true,
51
- })
52
- class TestApprovalComponent {
53
- props!: HumanInTheLoopProps<{ action: string }>;
54
-
55
- approve() {
56
- this.props.respond?.("approved");
57
- }
58
-
59
- reject() {
60
- this.props.respond?.("rejected");
61
- }
62
- }
63
-
64
- describe("Human-in-the-Loop Utilities", () => {
65
- let service: CopilotKitService;
66
-
67
- beforeEach(() => {
68
- TestBed.configureTestingModule({
69
- providers: [provideCopilotKit({})],
70
- declarations: [],
71
- });
72
-
73
- service = TestBed.inject(CopilotKitService);
74
- vi.clearAllMocks();
75
- });
76
-
77
- afterEach(() => {
78
- vi.clearAllMocks();
79
- });
80
-
81
- describe("registerHumanInTheLoop", () => {
82
- it("should register a tool with handler that returns a Promise", () => {
83
- @Component({
84
- template: "",
85
- standalone: true,
86
- providers: [provideCopilotKit({})],
87
- })
88
- class TestComponent {
89
- toolId = registerHumanInTheLoop({
90
- name: "requireApproval",
91
- description: "Requires user approval",
92
- parameters: z.object({ action: z.string() }),
93
- render: TestApprovalComponent,
94
- });
95
- }
96
-
97
- const fixture = TestBed.createComponent(TestComponent);
98
- fixture.detectChanges();
99
-
100
- expect(mockCopilotKitCore.addTool).toHaveBeenCalled();
101
- expect(fixture.componentInstance.toolId).toBe("requireApproval");
102
-
103
- // Verify the tool was added with a handler
104
- const addToolCall = mockCopilotKitCore.addTool.mock.calls[0][0];
105
- expect(addToolCall.name).toBe("requireApproval");
106
- expect(addToolCall.description).toBe("Requires user approval");
107
- expect(typeof addToolCall.handler).toBe("function");
108
- });
109
-
110
- it("should cleanup on component destroy", () => {
111
- @Component({
112
- template: "",
113
- standalone: true,
114
- providers: [provideCopilotKit({})],
115
- })
116
- class TestComponent {
117
- toolId = registerHumanInTheLoop({
118
- name: "requireApproval",
119
- description: "Requires user approval",
120
- parameters: z.object({ action: z.string() }),
121
- render: TestApprovalComponent,
122
- });
123
- }
124
-
125
- const fixture = TestBed.createComponent(TestComponent);
126
- fixture.detectChanges();
127
-
128
- const toolId = fixture.componentInstance.toolId;
129
-
130
- fixture.destroy();
131
-
132
- expect(mockCopilotKitCore.removeTool).toHaveBeenCalledWith(toolId);
133
- });
134
-
135
- it("should handle Promise resolution when respond is called", async () => {
136
- @Component({
137
- template: "",
138
- standalone: true,
139
- providers: [provideCopilotKit({})],
140
- })
141
- class TestComponent {
142
- toolId = registerHumanInTheLoop({
143
- name: "requireApproval",
144
- description: "Requires user approval",
145
- parameters: z.object({ action: z.string() }),
146
- render: TestApprovalComponent,
147
- });
148
- }
149
-
150
- const fixture = TestBed.createComponent(TestComponent);
151
- fixture.detectChanges();
152
-
153
- // Get the handler from the mock call
154
- const addToolCall = mockCopilotKitCore.addTool.mock.calls[0][0];
155
- const handler = addToolCall.handler;
156
-
157
- // Call the handler - it should return a Promise
158
- const resultPromise = handler({ action: "delete" });
159
- expect(resultPromise).toBeDefined();
160
- expect(typeof resultPromise.then).toBe("function");
161
-
162
- // The Promise should remain pending until respond is called
163
- // This would require access to the respond function which is internal
164
- // For now, we just verify the Promise is created
165
- });
166
- });
167
-
168
- describe("addHumanInTheLoop", () => {
169
- it("should add a tool and return cleanup function", () => {
170
- const cleanup = addHumanInTheLoop(service, {
171
- name: "requireApproval",
172
- description: "Requires user approval",
173
- args: z.object({ action: z.string() }),
174
- render: TestApprovalComponent,
175
- });
176
-
177
- expect(mockCopilotKitCore.addTool).toHaveBeenCalled();
178
- expect(typeof cleanup).toBe("function");
179
-
180
- // Call cleanup
181
- cleanup();
182
- expect(mockCopilotKitCore.removeTool).toHaveBeenCalledWith(
183
- "requireApproval"
184
- );
185
- });
186
-
187
- it("should register tool render", () => {
188
- const registerSpy = vi.spyOn(service, "registerToolRender");
189
-
190
- addHumanInTheLoop(service, {
191
- name: "requireApproval",
192
- description: "Requires user approval",
193
- parameters: z.object({ action: z.string() }),
194
- render: TestApprovalComponent,
195
- });
196
-
197
- expect(registerSpy).toHaveBeenCalledWith("requireApproval", {
198
- name: "requireApproval",
199
- args: expect.any(Object), // Note: registerToolRender expects 'args', not 'parameters'
200
- render: TestApprovalComponent,
201
- });
202
- });
203
-
204
- it("should unregister tool render on cleanup", () => {
205
- const unregisterSpy = vi.spyOn(service, "unregisterToolRender");
206
-
207
- const cleanup = addHumanInTheLoop(service, {
208
- name: "requireApproval",
209
- description: "Requires user approval",
210
- args: z.object({ action: z.string() }),
211
- render: TestApprovalComponent,
212
- });
213
-
214
- cleanup();
215
-
216
- expect(unregisterSpy).toHaveBeenCalledWith("requireApproval");
217
- });
218
- });
219
-
220
- describe("createHumanInTheLoop", () => {
221
- it("should create tool with status signal and control methods", () => {
222
- const result = createHumanInTheLoop(service, {
223
- name: "requireApproval",
224
- description: "Requires user approval",
225
- args: z.object({ action: z.string() }),
226
- render: TestApprovalComponent,
227
- });
228
-
229
- expect(result.status).toBeDefined();
230
- expect(result.toolId).toBe("requireApproval");
231
- expect(typeof result.update).toBe("function");
232
- expect(typeof result.destroy).toBe("function");
233
-
234
- // Check initial status
235
- expect(result.status()).toBe(ToolCallStatus.InProgress);
236
- });
237
-
238
- it("should allow updating tool configuration", () => {
239
- const result = createHumanInTheLoop(service, {
240
- name: "requireApproval",
241
- description: "Requires user approval",
242
- args: z.object({ action: z.string() }),
243
- render: TestApprovalComponent,
244
- });
245
-
246
- // Clear previous calls
247
- vi.clearAllMocks();
248
-
249
- // Update the tool
250
- result.update({
251
- description: "Updated description",
252
- });
253
-
254
- // Should remove old tool and add new one
255
- expect(mockCopilotKitCore.removeTool).toHaveBeenCalledWith(
256
- "requireApproval"
257
- );
258
- expect(mockCopilotKitCore.addTool).toHaveBeenCalled();
259
-
260
- const addToolCall = mockCopilotKitCore.addTool.mock.calls[0][0];
261
- expect(addToolCall.description).toBe("Updated description");
262
- });
263
-
264
- it("should cleanup when destroy is called", () => {
265
- const result = createHumanInTheLoop(service, {
266
- name: "requireApproval",
267
- description: "Requires user approval",
268
- args: z.object({ action: z.string() }),
269
- render: TestApprovalComponent,
270
- });
271
-
272
- result.destroy();
273
-
274
- expect(mockCopilotKitCore.removeTool).toHaveBeenCalledWith(
275
- "requireApproval"
276
- );
277
- });
278
-
279
- it("should track status changes through handler lifecycle", async () => {
280
- const result = createHumanInTheLoop(service, {
281
- name: "requireApproval",
282
- description: "Requires user approval",
283
- args: z.object({ action: z.string() }),
284
- render: TestApprovalComponent,
285
- });
286
-
287
- // Initial status
288
- expect(result.status()).toBe(ToolCallStatus.InProgress);
289
-
290
- // Get the handler
291
- const addToolCall = mockCopilotKitCore.addTool.mock.calls[0][0];
292
- const handler = addToolCall.handler;
293
-
294
- // Call handler - should change status to executing
295
- const promise = handler({ action: "delete" });
296
-
297
- // Note: We can't directly test the status change to 'executing'
298
- // without access to internal state, but we verify the Promise is created
299
- expect(promise).toBeDefined();
300
- expect(typeof promise.then).toBe("function");
301
- });
302
- });
303
-
304
- describe("enhancePropsForHumanInTheLoop", () => {
305
- it("should add respond function when status is executing", () => {
306
- const respond = vi.fn();
307
- const props: HumanInTheLoopProps = {
308
- name: "test",
309
- description: "Test tool",
310
- args: { action: "delete" },
311
- status: ToolCallStatus.Executing,
312
- result: undefined,
313
- };
314
-
315
- const enhanced = enhancePropsForHumanInTheLoop(
316
- props,
317
- ToolCallStatus.Executing,
318
- respond
319
- );
320
-
321
- expect(enhanced.respond).toBe(respond);
322
- expect(enhanced.status).toBe(ToolCallStatus.Executing);
323
- });
324
-
325
- it("should not add respond function when status is inProgress", () => {
326
- const respond = vi.fn();
327
- const props: HumanInTheLoopProps = {
328
- name: "test",
329
- description: "Test tool",
330
- args: { action: "delete" },
331
- status: ToolCallStatus.InProgress,
332
- result: undefined,
333
- };
334
-
335
- const enhanced = enhancePropsForHumanInTheLoop(
336
- props,
337
- ToolCallStatus.InProgress,
338
- respond
339
- );
340
-
341
- expect(enhanced.respond).toBeUndefined();
342
- expect(enhanced.status).toBe(ToolCallStatus.InProgress);
343
- });
344
-
345
- it("should not add respond function when status is complete", () => {
346
- const respond = vi.fn();
347
- const props: HumanInTheLoopProps = {
348
- name: "test",
349
- description: "Test tool",
350
- args: { action: "delete" },
351
- status: "complete",
352
- result: "approved",
353
- };
354
-
355
- const enhanced = enhancePropsForHumanInTheLoop(
356
- props,
357
- "complete",
358
- respond
359
- );
360
-
361
- expect(enhanced.respond).toBeUndefined();
362
- expect(enhanced.status).toBe("complete");
363
- });
364
- });
365
- });