@copilotkit/runtime 1.8.12-next.2 → 1.8.12-next.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 (47) hide show
  1. package/CHANGELOG.md +13 -0
  2. package/dist/{chunk-OZLQ2A5E.mjs → chunk-FA3E4I4W.mjs} +4 -3
  3. package/dist/chunk-FA3E4I4W.mjs.map +1 -0
  4. package/dist/{chunk-FDGTTGQU.mjs → chunk-KGZF7KSR.mjs} +2 -2
  5. package/dist/{chunk-VQSVMSXZ.mjs → chunk-MG576PIZ.mjs} +2 -2
  6. package/dist/{chunk-Y4H3U52G.mjs → chunk-MVKCCH5U.mjs} +216 -173
  7. package/dist/chunk-MVKCCH5U.mjs.map +1 -0
  8. package/dist/{chunk-V6IQU4D2.mjs → chunk-S5U6J5X2.mjs} +2 -2
  9. package/dist/index.js +217 -173
  10. package/dist/index.js.map +1 -1
  11. package/dist/index.mjs +5 -5
  12. package/dist/lib/index.js +109 -82
  13. package/dist/lib/index.js.map +1 -1
  14. package/dist/lib/index.mjs +5 -5
  15. package/dist/lib/integrations/index.js +2 -1
  16. package/dist/lib/integrations/index.js.map +1 -1
  17. package/dist/lib/integrations/index.mjs +5 -5
  18. package/dist/lib/integrations/nest/index.js +2 -1
  19. package/dist/lib/integrations/nest/index.js.map +1 -1
  20. package/dist/lib/integrations/nest/index.mjs +3 -3
  21. package/dist/lib/integrations/node-express/index.js +2 -1
  22. package/dist/lib/integrations/node-express/index.js.map +1 -1
  23. package/dist/lib/integrations/node-express/index.mjs +3 -3
  24. package/dist/lib/integrations/node-http/index.js +2 -1
  25. package/dist/lib/integrations/node-http/index.js.map +1 -1
  26. package/dist/lib/integrations/node-http/index.mjs +2 -2
  27. package/dist/service-adapters/index.js +215 -172
  28. package/dist/service-adapters/index.js.map +1 -1
  29. package/dist/service-adapters/index.mjs +1 -1
  30. package/jest.config.js +8 -3
  31. package/package.json +3 -2
  32. package/src/service-adapters/anthropic/anthropic-adapter.ts +124 -66
  33. package/src/service-adapters/anthropic/utils.ts +0 -19
  34. package/src/service-adapters/openai/openai-adapter.ts +107 -69
  35. package/tests/global.d.ts +13 -0
  36. package/tests/service-adapters/anthropic/allowlist-approach.test.ts +226 -0
  37. package/tests/service-adapters/anthropic/anthropic-adapter.test.ts +604 -0
  38. package/tests/service-adapters/openai/allowlist-approach.test.ts +238 -0
  39. package/tests/service-adapters/openai/openai-adapter.test.ts +301 -0
  40. package/tests/setup.jest.ts +21 -0
  41. package/tests/tsconfig.json +10 -0
  42. package/tsconfig.json +1 -1
  43. package/dist/chunk-OZLQ2A5E.mjs.map +0 -1
  44. package/dist/chunk-Y4H3U52G.mjs.map +0 -1
  45. /package/dist/{chunk-FDGTTGQU.mjs.map → chunk-KGZF7KSR.mjs.map} +0 -0
  46. /package/dist/{chunk-VQSVMSXZ.mjs.map → chunk-MG576PIZ.mjs.map} +0 -0
  47. /package/dist/{chunk-V6IQU4D2.mjs.map → chunk-S5U6J5X2.mjs.map} +0 -0
@@ -0,0 +1,226 @@
1
+ /**
2
+ * @jest-environment node
3
+ */
4
+
5
+ import { describe, it, expect } from "@jest/globals";
6
+
7
+ describe("Anthropic Adapter - Allowlist Approach", () => {
8
+ it("should filter out tool_result messages with no corresponding tool_use ID", () => {
9
+ // Setup test data
10
+ const validToolUseIds = new Set<string>(["valid-id-1", "valid-id-2"]);
11
+
12
+ // Messages to filter - valid and invalid ones
13
+ const messages = [
14
+ { type: "text", role: "user", content: "Hello" },
15
+ { type: "tool_result", actionExecutionId: "valid-id-1", result: "result1" },
16
+ { type: "tool_result", actionExecutionId: "invalid-id", result: "invalid" },
17
+ { type: "tool_result", actionExecutionId: "valid-id-2", result: "result2" },
18
+ { type: "tool_result", actionExecutionId: "valid-id-1", result: "duplicate" }, // Duplicate ID
19
+ ];
20
+
21
+ // Apply the allowlist filter approach
22
+ const filteredMessages = [];
23
+ const processedIds = new Set<string>();
24
+
25
+ for (const message of messages) {
26
+ if (message.type === "tool_result") {
27
+ // Skip if no corresponding valid tool_use ID
28
+ if (!validToolUseIds.has(message.actionExecutionId)) {
29
+ continue;
30
+ }
31
+
32
+ // Skip if we've already processed this ID
33
+ if (processedIds.has(message.actionExecutionId)) {
34
+ continue;
35
+ }
36
+
37
+ // Mark this ID as processed
38
+ processedIds.add(message.actionExecutionId);
39
+ }
40
+
41
+ // Include all non-tool-result messages and valid tool results
42
+ filteredMessages.push(message);
43
+ }
44
+
45
+ // Verify results
46
+ expect(filteredMessages.length).toBe(3); // text + 2 valid tool results (no duplicates or invalid)
47
+
48
+ // Valid results should be included
49
+ expect(
50
+ filteredMessages.some(
51
+ (m) => m.type === "tool_result" && m.actionExecutionId === "valid-id-1",
52
+ ),
53
+ ).toBe(true);
54
+
55
+ expect(
56
+ filteredMessages.some(
57
+ (m) => m.type === "tool_result" && m.actionExecutionId === "valid-id-2",
58
+ ),
59
+ ).toBe(true);
60
+
61
+ // Invalid result should be excluded
62
+ expect(
63
+ filteredMessages.some(
64
+ (m) => m.type === "tool_result" && m.actionExecutionId === "invalid-id",
65
+ ),
66
+ ).toBe(false);
67
+
68
+ // Duplicate should be excluded
69
+ const validId1Count = filteredMessages.filter(
70
+ (m) => m.type === "tool_result" && m.actionExecutionId === "valid-id-1",
71
+ ).length;
72
+
73
+ expect(validId1Count).toBe(1);
74
+ });
75
+
76
+ it("should maintain correct order of messages when filtering", () => {
77
+ // Setup test data with specific ordering
78
+ const validToolUseIds = new Set<string>(["tool-1", "tool-2", "tool-3"]);
79
+
80
+ // Messages in a specific order, with some invalid/duplicate results
81
+ const messages = [
82
+ { type: "text", role: "user", content: "Initial message" },
83
+ { type: "text", role: "assistant", content: "I'll help with that" },
84
+ { type: "tool_use", id: "tool-1", name: "firstTool" },
85
+ { type: "tool_result", actionExecutionId: "tool-1", result: "result1" },
86
+ { type: "text", role: "assistant", content: "Got the first result" },
87
+ { type: "tool_use", id: "tool-2", name: "secondTool" },
88
+ { type: "tool_result", actionExecutionId: "tool-2", result: "result2" },
89
+ { type: "tool_result", actionExecutionId: "invalid-id", result: "invalid-result" },
90
+ { type: "tool_use", id: "tool-3", name: "thirdTool" },
91
+ { type: "tool_result", actionExecutionId: "tool-1", result: "duplicate-result" }, // Duplicate
92
+ { type: "tool_result", actionExecutionId: "tool-3", result: "result3" },
93
+ { type: "text", role: "user", content: "Final message" },
94
+ ];
95
+
96
+ // Apply the allowlist filter approach
97
+ const filteredMessages = [];
98
+ const processedIds = new Set<string>();
99
+
100
+ for (const message of messages) {
101
+ if (message.type === "tool_result") {
102
+ // Skip if no corresponding valid tool_use ID
103
+ if (!validToolUseIds.has(message.actionExecutionId)) {
104
+ continue;
105
+ }
106
+
107
+ // Skip if we've already processed this ID
108
+ if (processedIds.has(message.actionExecutionId)) {
109
+ continue;
110
+ }
111
+
112
+ // Mark this ID as processed
113
+ processedIds.add(message.actionExecutionId);
114
+ }
115
+
116
+ // Include all non-tool-result messages and valid tool results
117
+ filteredMessages.push(message);
118
+ }
119
+
120
+ // Verify results
121
+ expect(filteredMessages.length).toBe(10); // 12 original - 2 filtered out
122
+
123
+ // Check that the order is preserved
124
+ expect(filteredMessages[0].type).toBe("text"); // Initial user message
125
+ expect(filteredMessages[1].type).toBe("text"); // Assistant response
126
+ expect(filteredMessages[2].type).toBe("tool_use"); // First tool
127
+ expect(filteredMessages[3].type).toBe("tool_result"); // First result
128
+ expect(filteredMessages[3].actionExecutionId).toBe("tool-1"); // First result
129
+ expect(filteredMessages[4].type).toBe("text"); // Assistant comment
130
+ expect(filteredMessages[5].type).toBe("tool_use"); // Second tool
131
+ expect(filteredMessages[6].type).toBe("tool_result"); // Second result
132
+ expect(filteredMessages[6].actionExecutionId).toBe("tool-2"); // Second result
133
+ expect(filteredMessages[7].type).toBe("tool_use"); // Third tool
134
+ expect(filteredMessages[8].type).toBe("tool_result"); // Third result
135
+ expect(filteredMessages[8].actionExecutionId).toBe("tool-3"); // Third result
136
+ expect(filteredMessages[9].type).toBe("text"); // Final user message
137
+
138
+ // Each valid tool ID should appear exactly once in the results
139
+ const toolResultCounts = {
140
+ "tool-1": 0,
141
+ "tool-2": 0,
142
+ "tool-3": 0,
143
+ };
144
+
145
+ filteredMessages.forEach((message) => {
146
+ if (message.type === "tool_result" && message.actionExecutionId in toolResultCounts) {
147
+ toolResultCounts[message.actionExecutionId]++;
148
+ }
149
+ });
150
+
151
+ expect(toolResultCounts["tool-1"]).toBe(1);
152
+ expect(toolResultCounts["tool-2"]).toBe(1);
153
+ expect(toolResultCounts["tool-3"]).toBe(1);
154
+ });
155
+
156
+ it("should handle an empty message array", () => {
157
+ const validToolUseIds = new Set<string>(["valid-id-1", "valid-id-2"]);
158
+ const messages = [];
159
+
160
+ // Apply the filtering logic
161
+ const filteredMessages = [];
162
+ const processedIds = new Set<string>();
163
+
164
+ for (const message of messages) {
165
+ if (message.type === "tool_result") {
166
+ if (
167
+ !validToolUseIds.has(message.actionExecutionId) ||
168
+ processedIds.has(message.actionExecutionId)
169
+ ) {
170
+ continue;
171
+ }
172
+ processedIds.add(message.actionExecutionId);
173
+ }
174
+ filteredMessages.push(message);
175
+ }
176
+
177
+ expect(filteredMessages.length).toBe(0);
178
+ });
179
+
180
+ it("should handle edge cases with mixed message types", () => {
181
+ // Setup with mixed message types
182
+ const validToolUseIds = new Set<string>(["valid-id-1"]);
183
+
184
+ const messages = [
185
+ { type: "text", role: "user", content: "Hello" },
186
+ { type: "image", url: "https://example.com/image.jpg" }, // Non-tool message type
187
+ { type: "tool_result", actionExecutionId: "valid-id-1", result: "result1" },
188
+ { type: "custom", data: { key: "value" } }, // Another custom type
189
+ { type: "tool_result", actionExecutionId: "valid-id-1", result: "duplicate" }, // Duplicate
190
+ { type: "null", value: null }, // Edge case
191
+ { type: "undefined" }, // Edge case
192
+ ];
193
+
194
+ // Apply the filtering logic
195
+ const filteredMessages = [];
196
+ const processedIds = new Set<string>();
197
+
198
+ for (const message of messages) {
199
+ if (message.type === "tool_result") {
200
+ if (
201
+ !validToolUseIds.has(message.actionExecutionId) ||
202
+ processedIds.has(message.actionExecutionId)
203
+ ) {
204
+ continue;
205
+ }
206
+ processedIds.add(message.actionExecutionId);
207
+ }
208
+ filteredMessages.push(message);
209
+ }
210
+
211
+ // Should have all non-tool_result messages + 1 valid tool_result
212
+ expect(filteredMessages.length).toBe(6); // 7 original - 1 duplicate
213
+
214
+ // Valid tool_result should be included exactly once
215
+ const toolResults = filteredMessages.filter((m) => m.type === "tool_result");
216
+ expect(toolResults.length).toBe(1);
217
+ expect(toolResults[0].actionExecutionId).toBe("valid-id-1");
218
+
219
+ // All other message types should be preserved
220
+ expect(filteredMessages.filter((m) => m.type === "text").length).toBe(1);
221
+ expect(filteredMessages.filter((m) => m.type === "image").length).toBe(1);
222
+ expect(filteredMessages.filter((m) => m.type === "custom").length).toBe(1);
223
+ expect(filteredMessages.filter((m) => m.type === "null").length).toBe(1);
224
+ expect(filteredMessages.filter((m) => m.type === "undefined").length).toBe(1);
225
+ });
226
+ });