@copilotkitnext/angular 0.0.1 → 0.0.4

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 (171) hide show
  1. package/README.md +248 -0
  2. package/dist/README.md +248 -0
  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/esm2022/components/chat/copilot-chat-assistant-message-buttons.component.mjs +384 -0
  7. package/dist/esm2022/components/chat/copilot-chat-assistant-message-renderer.component.mjs +286 -0
  8. package/dist/esm2022/components/chat/copilot-chat-assistant-message-toolbar.component.mjs +27 -0
  9. package/dist/esm2022/components/chat/copilot-chat-assistant-message.component.mjs +433 -0
  10. package/dist/esm2022/components/chat/copilot-chat-assistant-message.types.mjs +2 -0
  11. package/dist/esm2022/components/chat/copilot-chat-audio-recorder.component.mjs +202 -0
  12. package/dist/esm2022/components/chat/copilot-chat-buttons.component.mjs +321 -0
  13. package/dist/esm2022/components/chat/copilot-chat-input-defaults.mjs +38 -0
  14. package/dist/esm2022/components/chat/copilot-chat-input.component.mjs +666 -0
  15. package/dist/esm2022/components/chat/copilot-chat-input.types.mjs +10 -0
  16. package/dist/esm2022/components/chat/copilot-chat-message-view-cursor.component.mjs +45 -0
  17. package/dist/esm2022/components/chat/copilot-chat-message-view.component.mjs +296 -0
  18. package/dist/esm2022/components/chat/copilot-chat-message-view.types.mjs +2 -0
  19. package/dist/esm2022/components/chat/copilot-chat-textarea.component.mjs +188 -0
  20. package/dist/esm2022/components/chat/copilot-chat-tool-calls-view.component.mjs +216 -0
  21. package/dist/esm2022/components/chat/copilot-chat-toolbar.component.mjs +25 -0
  22. package/dist/esm2022/components/chat/copilot-chat-tools-menu.component.mjs +199 -0
  23. package/dist/esm2022/components/chat/copilot-chat-user-message-branch-navigation.component.mjs +137 -0
  24. package/dist/esm2022/components/chat/copilot-chat-user-message-buttons.component.mjs +207 -0
  25. package/dist/esm2022/components/chat/copilot-chat-user-message-renderer.component.mjs +35 -0
  26. package/dist/esm2022/components/chat/copilot-chat-user-message-toolbar.component.mjs +34 -0
  27. package/dist/esm2022/components/chat/copilot-chat-user-message.component.mjs +341 -0
  28. package/dist/esm2022/components/chat/copilot-chat-user-message.types.mjs +2 -0
  29. package/dist/esm2022/components/chat/copilot-chat-view-disclaimer.component.mjs +52 -0
  30. package/dist/esm2022/components/chat/copilot-chat-view-feather.component.mjs +55 -0
  31. package/dist/esm2022/components/chat/copilot-chat-view-handlers.service.mjs +19 -0
  32. package/dist/esm2022/components/chat/copilot-chat-view-input-container.component.mjs +110 -0
  33. package/dist/esm2022/components/chat/copilot-chat-view-scroll-to-bottom-button.component.mjs +93 -0
  34. package/dist/esm2022/components/chat/copilot-chat-view-scroll-view.component.mjs +443 -0
  35. package/dist/esm2022/components/chat/copilot-chat-view.component.mjs +479 -0
  36. package/dist/esm2022/components/chat/copilot-chat-view.types.mjs +2 -0
  37. package/dist/esm2022/components/chat/copilot-chat.component.mjs +214 -0
  38. package/dist/esm2022/components/copilotkit-tool-render.component.mjs +153 -0
  39. package/dist/esm2022/copilotkitnext-angular.mjs +5 -0
  40. package/dist/esm2022/core/chat-configuration/chat-configuration.providers.mjs +65 -0
  41. package/dist/esm2022/core/chat-configuration/chat-configuration.service.mjs +145 -0
  42. package/dist/esm2022/core/chat-configuration/chat-configuration.types.mjs +26 -0
  43. package/dist/esm2022/core/copilotkit.providers.mjs +34 -0
  44. package/dist/esm2022/core/copilotkit.service.mjs +430 -0
  45. package/dist/esm2022/core/copilotkit.types.mjs +12 -0
  46. package/dist/esm2022/directives/copilotkit-agent-context.directive.mjs +130 -0
  47. package/dist/esm2022/directives/copilotkit-agent.directive.mjs +217 -0
  48. package/dist/esm2022/directives/copilotkit-chat-config.directive.mjs +218 -0
  49. package/dist/esm2022/directives/copilotkit-config.directive.mjs +94 -0
  50. package/dist/esm2022/directives/copilotkit-frontend-tool.directive.mjs +130 -0
  51. package/dist/esm2022/directives/copilotkit-human-in-the-loop.directive.mjs +266 -0
  52. package/dist/esm2022/directives/stick-to-bottom.directive.mjs +181 -0
  53. package/dist/esm2022/index.mjs +70 -0
  54. package/dist/esm2022/lib/directives/tooltip.directive.mjs +211 -0
  55. package/dist/esm2022/lib/slots/copilot-slot.component.mjs +144 -0
  56. package/dist/esm2022/lib/slots/slot.types.mjs +6 -0
  57. package/dist/esm2022/lib/slots/slot.utils.mjs +222 -0
  58. package/dist/esm2022/lib/utils.mjs +10 -0
  59. package/dist/esm2022/services/resize-observer.service.mjs +152 -0
  60. package/dist/esm2022/services/scroll-position.service.mjs +124 -0
  61. package/dist/esm2022/types/frontend-tool.mjs +2 -0
  62. package/dist/esm2022/types/human-in-the-loop.mjs +2 -0
  63. package/dist/esm2022/utils/agent-context.utils.mjs +114 -0
  64. package/dist/esm2022/utils/agent.utils.mjs +204 -0
  65. package/dist/esm2022/utils/chat-config.utils.mjs +186 -0
  66. package/dist/esm2022/utils/copilotkit.utils.mjs +20 -0
  67. package/dist/esm2022/utils/frontend-tool.utils.mjs +228 -0
  68. package/dist/esm2022/utils/human-in-the-loop.utils.mjs +296 -0
  69. package/dist/fesm2022/copilotkitnext-angular.mjs +163 -164
  70. package/dist/fesm2022/copilotkitnext-angular.mjs.map +1 -1
  71. package/dist/styles.css +0 -27
  72. package/package.json +23 -20
  73. package/vitest.config.mts +32 -21
  74. package/.turbo/turbo-build.log +0 -39
  75. package/.turbo/turbo-check-types.log +0 -0
  76. package/.turbo/turbo-test.log +0 -71
  77. package/README-agent-context.md +0 -310
  78. package/ng-package.json +0 -19
  79. package/slots.md +0 -331
  80. package/src/components/chat/__tests__/copilot-chat-assistant-message.component.spec.ts +0 -282
  81. package/src/components/chat/__tests__/copilot-chat-input.component.spec.ts +0 -419
  82. package/src/components/chat/__tests__/copilot-chat-message-view.component.spec.ts +0 -372
  83. package/src/components/chat/__tests__/copilot-chat-user-message.component.spec.ts +0 -249
  84. package/src/components/chat/copilot-chat-assistant-message-buttons.component.ts +0 -292
  85. package/src/components/chat/copilot-chat-assistant-message-renderer.component.ts +0 -472
  86. package/src/components/chat/copilot-chat-assistant-message-toolbar.component.ts +0 -29
  87. package/src/components/chat/copilot-chat-assistant-message.component.ts +0 -463
  88. package/src/components/chat/copilot-chat-assistant-message.types.ts +0 -50
  89. package/src/components/chat/copilot-chat-audio-recorder.component.ts +0 -241
  90. package/src/components/chat/copilot-chat-buttons.component.ts +0 -308
  91. package/src/components/chat/copilot-chat-buttons.component.ts.bak +0 -471
  92. package/src/components/chat/copilot-chat-input-defaults.ts +0 -47
  93. package/src/components/chat/copilot-chat-input.component.ts +0 -512
  94. package/src/components/chat/copilot-chat-input.types.ts +0 -148
  95. package/src/components/chat/copilot-chat-message-view-cursor.component.ts +0 -51
  96. package/src/components/chat/copilot-chat-message-view.component.ts +0 -233
  97. package/src/components/chat/copilot-chat-message-view.types.ts +0 -39
  98. package/src/components/chat/copilot-chat-textarea.component.ts +0 -220
  99. package/src/components/chat/copilot-chat-tool-calls-view.component.ts +0 -261
  100. package/src/components/chat/copilot-chat-toolbar.component.ts +0 -35
  101. package/src/components/chat/copilot-chat-tools-menu.component.ts +0 -185
  102. package/src/components/chat/copilot-chat-user-message-branch-navigation.component.ts +0 -121
  103. package/src/components/chat/copilot-chat-user-message-buttons.component.ts +0 -170
  104. package/src/components/chat/copilot-chat-user-message-renderer.component.ts +0 -37
  105. package/src/components/chat/copilot-chat-user-message-toolbar.component.ts +0 -37
  106. package/src/components/chat/copilot-chat-user-message.component.ts +0 -247
  107. package/src/components/chat/copilot-chat-user-message.types.ts +0 -42
  108. package/src/components/chat/copilot-chat-view-disclaimer.component.ts +0 -51
  109. package/src/components/chat/copilot-chat-view-feather.component.ts +0 -47
  110. package/src/components/chat/copilot-chat-view-handlers.service.ts +0 -14
  111. package/src/components/chat/copilot-chat-view-input-container.component.ts +0 -87
  112. package/src/components/chat/copilot-chat-view-scroll-to-bottom-button.component.ts +0 -79
  113. package/src/components/chat/copilot-chat-view-scroll-view.component.ts +0 -322
  114. package/src/components/chat/copilot-chat-view.component.ts +0 -420
  115. package/src/components/chat/copilot-chat-view.types.ts +0 -52
  116. package/src/components/chat/copilot-chat.component.ts +0 -232
  117. package/src/components/copilotkit-tool-render.component.ts +0 -169
  118. package/src/core/__tests__/copilotkit.service.spec.ts +0 -1051
  119. package/src/core/__tests__/copilotkit.service.wildcard.spec.ts +0 -316
  120. package/src/core/chat-configuration/__tests__/chat-configuration.service.spec.ts +0 -287
  121. package/src/core/chat-configuration/chat-configuration.providers.ts +0 -71
  122. package/src/core/chat-configuration/chat-configuration.service.ts +0 -162
  123. package/src/core/chat-configuration/chat-configuration.types.ts +0 -57
  124. package/src/core/copilotkit.providers.ts +0 -59
  125. package/src/core/copilotkit.service.ts +0 -542
  126. package/src/core/copilotkit.types.ts +0 -132
  127. package/src/directives/__tests__/copilotkit-agent-context.directive.spec.ts +0 -384
  128. package/src/directives/__tests__/copilotkit-agent.directive.spec.ts +0 -253
  129. package/src/directives/__tests__/copilotkit-chat-config.directive.spec.ts +0 -385
  130. package/src/directives/__tests__/copilotkit-config.directive.spec.ts +0 -69
  131. package/src/directives/__tests__/copilotkit-frontend-tool-simple.directive.spec.ts +0 -60
  132. package/src/directives/__tests__/copilotkit-frontend-tool.directive.spec.ts +0 -108
  133. package/src/directives/__tests__/copilotkit-human-in-the-loop.directive.spec.ts +0 -452
  134. package/src/directives/copilotkit-agent-context.directive.ts +0 -138
  135. package/src/directives/copilotkit-agent.directive.ts +0 -225
  136. package/src/directives/copilotkit-chat-config.directive.ts +0 -241
  137. package/src/directives/copilotkit-config.directive.ts +0 -81
  138. package/src/directives/copilotkit-frontend-tool.directive.ts +0 -145
  139. package/src/directives/copilotkit-human-in-the-loop.directive.ts +0 -281
  140. package/src/directives/stick-to-bottom.directive.ts +0 -204
  141. package/src/index.ts +0 -105
  142. package/src/lib/directives/tooltip.directive.ts +0 -292
  143. package/src/lib/slots/__tests__/slot.utils.spec.ts +0 -377
  144. package/src/lib/slots/copilot-slot.component.ts +0 -135
  145. package/src/lib/slots/index.ts +0 -3
  146. package/src/lib/slots/slot.types.ts +0 -64
  147. package/src/lib/slots/slot.utils.ts +0 -289
  148. package/src/lib/utils.ts +0 -10
  149. package/src/public-api.ts +0 -1
  150. package/src/services/resize-observer.service.ts +0 -181
  151. package/src/services/scroll-position.service.ts +0 -169
  152. package/src/styles/globals.css +0 -266
  153. package/src/styles/index.css +0 -3
  154. package/src/test-setup.ts +0 -15
  155. package/src/testing/index.ts +0 -3
  156. package/src/testing/testing.utils.ts +0 -248
  157. package/src/types/frontend-tool.ts +0 -44
  158. package/src/types/human-in-the-loop.ts +0 -52
  159. package/src/utils/__tests__/agent.utils.spec.ts +0 -234
  160. package/src/utils/__tests__/chat-config.utils.spec.ts +0 -306
  161. package/src/utils/__tests__/frontend-tool-inject.spec.ts +0 -350
  162. package/src/utils/__tests__/frontend-tool-integration.spec.ts +0 -199
  163. package/src/utils/__tests__/frontend-tool.utils.spec.ts +0 -272
  164. package/src/utils/__tests__/human-in-the-loop.utils.spec.ts +0 -365
  165. package/src/utils/agent-context.utils.ts +0 -133
  166. package/src/utils/agent.utils.ts +0 -239
  167. package/src/utils/chat-config.utils.ts +0 -221
  168. package/src/utils/copilotkit.utils.ts +0 -20
  169. package/src/utils/frontend-tool.utils.ts +0 -266
  170. package/src/utils/human-in-the-loop.utils.ts +0 -359
  171. 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
- });