@copilotkit/runtime 1.10.6 → 1.50.0-beta.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 (133) hide show
  1. package/dist/index.d.ts +1655 -27
  2. package/dist/index.js +2172 -5049
  3. package/dist/index.js.map +1 -1
  4. package/dist/index.mjs +5441 -99
  5. package/dist/index.mjs.map +1 -1
  6. package/dist/v2/index.d.ts +1 -0
  7. package/dist/v2/index.js +15 -0
  8. package/dist/v2/index.js.map +1 -0
  9. package/dist/v2/index.mjs +4 -0
  10. package/dist/v2/index.mjs.map +1 -0
  11. package/package.json +33 -21
  12. package/src/graphql/message-conversion/agui-to-gql.test.ts +1263 -0
  13. package/src/graphql/message-conversion/agui-to-gql.ts +333 -0
  14. package/src/graphql/message-conversion/gql-to-agui.test.ts +1578 -0
  15. package/src/graphql/message-conversion/gql-to-agui.ts +278 -0
  16. package/src/graphql/message-conversion/index.ts +2 -0
  17. package/src/graphql/message-conversion/roundtrip-conversion.test.ts +526 -0
  18. package/src/graphql/resolvers/copilot.resolver.ts +3 -48
  19. package/src/graphql/resolvers/state.resolver.ts +3 -2
  20. package/src/graphql/types/converted/index.ts +32 -6
  21. package/src/graphql/types/enums.ts +2 -2
  22. package/src/graphql/types/message-status.type.ts +3 -1
  23. package/src/lib/index.ts +1 -1
  24. package/src/lib/integrations/nextjs/app-router.ts +10 -11
  25. package/src/lib/integrations/nextjs/pages-router.ts +4 -11
  26. package/src/lib/integrations/node-http/index.ts +64 -5
  27. package/src/lib/integrations/shared.ts +1 -1
  28. package/src/lib/observability.ts +87 -0
  29. package/src/lib/runtime/{langgraph/langgraph-agent.ts → agent-integrations/langgraph.agent.ts} +5 -0
  30. package/src/lib/runtime/copilot-runtime.ts +346 -1333
  31. package/src/lib/runtime/types.ts +49 -0
  32. package/src/lib/runtime/utils.ts +87 -0
  33. package/src/lib/telemetry-client.ts +6 -5
  34. package/src/service-adapters/anthropic/anthropic-adapter.ts +5 -1
  35. package/src/service-adapters/bedrock/bedrock-adapter.ts +6 -1
  36. package/src/service-adapters/empty/empty-adapter.ts +3 -0
  37. package/src/service-adapters/events.ts +0 -254
  38. package/src/service-adapters/experimental/ollama/ollama-adapter.ts +5 -1
  39. package/src/service-adapters/google/google-genai-adapter.ts +7 -1
  40. package/src/service-adapters/groq/groq-adapter.ts +5 -1
  41. package/src/service-adapters/langchain/langchain-adapter.ts +3 -0
  42. package/src/service-adapters/openai/openai-adapter.ts +5 -1
  43. package/src/service-adapters/openai/openai-assistant-adapter.ts +4 -0
  44. package/src/service-adapters/service-adapter.ts +3 -0
  45. package/src/service-adapters/unify/unify-adapter.ts +6 -1
  46. package/src/v2/index.ts +2 -0
  47. package/tsup.config.ts +2 -1
  48. package/dist/chunk-27JKTS6P.mjs +0 -1704
  49. package/dist/chunk-27JKTS6P.mjs.map +0 -1
  50. package/dist/chunk-2OZAGFV3.mjs +0 -43
  51. package/dist/chunk-2OZAGFV3.mjs.map +0 -1
  52. package/dist/chunk-AMUJQ6IR.mjs +0 -50
  53. package/dist/chunk-AMUJQ6IR.mjs.map +0 -1
  54. package/dist/chunk-CEOMFPJU.mjs +0 -6020
  55. package/dist/chunk-CEOMFPJU.mjs.map +0 -1
  56. package/dist/chunk-DTPRUTNV.mjs +0 -25
  57. package/dist/chunk-DTPRUTNV.mjs.map +0 -1
  58. package/dist/chunk-FHD4JECV.mjs +0 -33
  59. package/dist/chunk-FHD4JECV.mjs.map +0 -1
  60. package/dist/chunk-I27F2UPA.mjs +0 -175
  61. package/dist/chunk-I27F2UPA.mjs.map +0 -1
  62. package/dist/chunk-LPEPX6NH.mjs +0 -25
  63. package/dist/chunk-LPEPX6NH.mjs.map +0 -1
  64. package/dist/chunk-PTYRVXXP.mjs +0 -80
  65. package/dist/chunk-PTYRVXXP.mjs.map +0 -1
  66. package/dist/chunk-SHBDMA63.mjs +0 -141
  67. package/dist/chunk-SHBDMA63.mjs.map +0 -1
  68. package/dist/chunk-XWBDEXDA.mjs +0 -153
  69. package/dist/chunk-XWBDEXDA.mjs.map +0 -1
  70. package/dist/graphql/types/base/index.d.ts +0 -6
  71. package/dist/graphql/types/base/index.js +0 -63
  72. package/dist/graphql/types/base/index.js.map +0 -1
  73. package/dist/graphql/types/base/index.mjs +0 -8
  74. package/dist/graphql/types/base/index.mjs.map +0 -1
  75. package/dist/graphql/types/converted/index.d.ts +0 -2
  76. package/dist/graphql/types/converted/index.js +0 -200
  77. package/dist/graphql/types/converted/index.js.map +0 -1
  78. package/dist/graphql/types/converted/index.mjs +0 -19
  79. package/dist/graphql/types/converted/index.mjs.map +0 -1
  80. package/dist/groq-adapter-c8aec5c5.d.ts +0 -321
  81. package/dist/index-96b330da.d.ts +0 -119
  82. package/dist/langserve-0c6100e3.d.ts +0 -257
  83. package/dist/lib/cloud/index.d.ts +0 -6
  84. package/dist/lib/cloud/index.js +0 -18
  85. package/dist/lib/cloud/index.js.map +0 -1
  86. package/dist/lib/cloud/index.mjs +0 -1
  87. package/dist/lib/cloud/index.mjs.map +0 -1
  88. package/dist/lib/index.d.ts +0 -212
  89. package/dist/lib/index.js +0 -7843
  90. package/dist/lib/index.js.map +0 -1
  91. package/dist/lib/index.mjs +0 -76
  92. package/dist/lib/index.mjs.map +0 -1
  93. package/dist/lib/integrations/index.d.ts +0 -34
  94. package/dist/lib/integrations/index.js +0 -3052
  95. package/dist/lib/integrations/index.js.map +0 -1
  96. package/dist/lib/integrations/index.mjs +0 -37
  97. package/dist/lib/integrations/index.mjs.map +0 -1
  98. package/dist/lib/integrations/nest/index.d.ts +0 -15
  99. package/dist/lib/integrations/nest/index.js +0 -2959
  100. package/dist/lib/integrations/nest/index.js.map +0 -1
  101. package/dist/lib/integrations/nest/index.mjs +0 -14
  102. package/dist/lib/integrations/nest/index.mjs.map +0 -1
  103. package/dist/lib/integrations/node-express/index.d.ts +0 -15
  104. package/dist/lib/integrations/node-express/index.js +0 -2959
  105. package/dist/lib/integrations/node-express/index.js.map +0 -1
  106. package/dist/lib/integrations/node-express/index.mjs +0 -14
  107. package/dist/lib/integrations/node-express/index.mjs.map +0 -1
  108. package/dist/lib/integrations/node-http/index.d.ts +0 -15
  109. package/dist/lib/integrations/node-http/index.js +0 -2945
  110. package/dist/lib/integrations/node-http/index.js.map +0 -1
  111. package/dist/lib/integrations/node-http/index.mjs +0 -13
  112. package/dist/lib/integrations/node-http/index.mjs.map +0 -1
  113. package/dist/service-adapters/index.d.ts +0 -162
  114. package/dist/service-adapters/index.js +0 -1787
  115. package/dist/service-adapters/index.js.map +0 -1
  116. package/dist/service-adapters/index.mjs +0 -34
  117. package/dist/service-adapters/index.mjs.map +0 -1
  118. package/dist/service-adapters/shared/index.d.ts +0 -9
  119. package/dist/service-adapters/shared/index.js +0 -72
  120. package/dist/service-adapters/shared/index.js.map +0 -1
  121. package/dist/service-adapters/shared/index.mjs +0 -8
  122. package/dist/service-adapters/shared/index.mjs.map +0 -1
  123. package/dist/shared-0a7346ce.d.ts +0 -466
  124. package/dist/utils/index.d.ts +0 -65
  125. package/dist/utils/index.js +0 -175
  126. package/dist/utils/index.js.map +0 -1
  127. package/dist/utils/index.mjs +0 -12
  128. package/dist/utils/index.mjs.map +0 -1
  129. package/src/lib/runtime/__tests__/remote-action-constructors.test.ts +0 -246
  130. package/src/lib/runtime/agui-action.ts +0 -180
  131. package/src/lib/runtime/remote-action-constructors.ts +0 -331
  132. package/src/lib/runtime/remote-actions.ts +0 -217
  133. package/src/lib/runtime/remote-lg-action.ts +0 -1006
@@ -0,0 +1,526 @@
1
+ import { describe, test, expect, vi } from "vitest";
2
+ import * as gql from "../types/converted/index";
3
+ import agui from "@copilotkit/shared";
4
+ import { aguiToGQL } from "./agui-to-gql";
5
+ import { gqlToAGUI } from "./gql-to-agui";
6
+
7
+ // Helper to strip functions for deep equality
8
+ function stripFunctions(obj: any): any {
9
+ if (typeof obj === "function") return undefined;
10
+ if (Array.isArray(obj)) return obj.map(stripFunctions);
11
+ if (obj && typeof obj === "object") {
12
+ const out: any = {};
13
+ for (const k in obj) {
14
+ if (typeof obj[k] !== "function") {
15
+ out[k] = stripFunctions(obj[k]);
16
+ }
17
+ }
18
+ return out;
19
+ }
20
+ return obj;
21
+ }
22
+
23
+ describe("roundtrip message conversion", () => {
24
+ test("text message AGUI -> GQL -> AGUI", () => {
25
+ const aguiMsg: agui.Message = {
26
+ id: "user-1",
27
+ role: "user",
28
+ content: "Hello!",
29
+ };
30
+ const gqlMsgs = aguiToGQL(aguiMsg);
31
+ const aguiMsgs2 = gqlToAGUI(gqlMsgs);
32
+ expect(stripFunctions(aguiMsgs2[0])).toEqual(stripFunctions(aguiMsg));
33
+ });
34
+
35
+ test("text message GQL -> AGUI -> GQL", () => {
36
+ const gqlMsg = new gql.TextMessage({
37
+ id: "assistant-1",
38
+ content: "Hi!",
39
+ role: gql.Role.assistant,
40
+ });
41
+ const aguiMsgs = gqlToAGUI(gqlMsg);
42
+ const gqlMsgs2 = aguiToGQL(aguiMsgs);
43
+ // Should be equivalent in content, id, and role
44
+ expect(gqlMsgs2[0].id).toBe(gqlMsg.id);
45
+ expect((gqlMsgs2[0] as any).content).toBe(gqlMsg.content);
46
+ expect((gqlMsgs2[0] as any).role).toBe(gqlMsg.role);
47
+ });
48
+
49
+ test("tool message AGUI -> GQL -> AGUI", () => {
50
+ const aguiMsg: agui.Message = {
51
+ id: "tool-1",
52
+ role: "tool",
53
+ content: "Tool result",
54
+ toolCallId: "tool-call-1",
55
+ toolName: "testAction",
56
+ };
57
+ const gqlMsgs = aguiToGQL(aguiMsg);
58
+ const aguiMsgs2 = gqlToAGUI(gqlMsgs);
59
+ expect(stripFunctions(aguiMsgs2[0])).toEqual(stripFunctions(aguiMsg));
60
+ });
61
+
62
+ test("tool message GQL -> AGUI -> GQL", () => {
63
+ const gqlMsg = new gql.ResultMessage({
64
+ id: "tool-1",
65
+ result: "Tool result",
66
+ actionExecutionId: "tool-call-1",
67
+ actionName: "testAction",
68
+ });
69
+ const aguiMsgs = gqlToAGUI(gqlMsg);
70
+ const gqlMsgs2 = aguiToGQL(aguiMsgs);
71
+ expect(gqlMsgs2[0].id).toBe(gqlMsg.id);
72
+ expect((gqlMsgs2[0] as any).result).toBe(gqlMsg.result);
73
+ expect((gqlMsgs2[0] as any).actionExecutionId).toBe(gqlMsg.actionExecutionId);
74
+ });
75
+
76
+ test("action execution AGUI -> GQL -> AGUI", () => {
77
+ const aguiMsg: agui.Message = {
78
+ id: "assistant-1",
79
+ role: "assistant",
80
+ content: "Running action",
81
+ toolCalls: [
82
+ {
83
+ id: "tool-call-1",
84
+ type: "function",
85
+ function: {
86
+ name: "doSomething",
87
+ arguments: JSON.stringify({ foo: "bar" }),
88
+ },
89
+ },
90
+ ],
91
+ };
92
+ const gqlMsgs = aguiToGQL(aguiMsg);
93
+ const aguiMsgs2 = gqlToAGUI(gqlMsgs);
94
+ // Should have an assistant message and an action execution message
95
+ expect(aguiMsgs2[0].role).toBe("assistant");
96
+ expect(aguiMsgs2[1].role).toBe("assistant");
97
+ // Only check toolCalls if present
98
+ if ("toolCalls" in aguiMsgs2[1]) {
99
+ expect((aguiMsgs2[1] as any).toolCalls[0].function.name).toBe("doSomething");
100
+ }
101
+ });
102
+
103
+ test("action execution GQL -> AGUI -> GQL", () => {
104
+ const actionExecMsg = new gql.ActionExecutionMessage({
105
+ id: "tool-call-1",
106
+ name: "doSomething",
107
+ arguments: { foo: "bar" },
108
+ parentMessageId: "assistant-1",
109
+ });
110
+ const aguiMsgs = gqlToAGUI([actionExecMsg]);
111
+ const gqlMsgs2 = aguiToGQL(aguiMsgs);
112
+ // The ActionExecutionMessage is at index 1, not index 0
113
+ expect(gqlMsgs2[1].id).toBe("tool-call-1");
114
+ // The name should be extracted from the toolCall function name
115
+ expect((gqlMsgs2[1] as any).name).toBe("doSomething");
116
+ expect((gqlMsgs2[1] as any).arguments).toEqual({ foo: "bar" });
117
+ });
118
+
119
+ test("agent state GQL -> AGUI -> GQL", () => {
120
+ const agentStateMsg = new gql.AgentStateMessage({
121
+ id: "agent-state-1",
122
+ agentName: "testAgent",
123
+ state: { status: "running" },
124
+ role: gql.Role.assistant,
125
+ });
126
+ const aguiMsgs = gqlToAGUI([agentStateMsg]);
127
+ const gqlMsgs2 = aguiToGQL(aguiMsgs);
128
+ expect(gqlMsgs2[0].id).toBe("agent-state-1");
129
+ // The agentName should be preserved in the roundtrip
130
+ expect((gqlMsgs2[0] as any).agentName).toBe("testAgent");
131
+ });
132
+
133
+ test("action execution with render function roundtrip", () => {
134
+ const mockRender = vi.fn();
135
+ const aguiMsg: agui.Message = {
136
+ id: "assistant-1",
137
+ role: "assistant",
138
+ content: "Running action",
139
+ toolCalls: [
140
+ {
141
+ id: "tool-call-1",
142
+ type: "function",
143
+ function: {
144
+ name: "doSomething",
145
+ arguments: JSON.stringify({ foo: "bar" }),
146
+ },
147
+ },
148
+ ],
149
+ generativeUI: mockRender,
150
+ };
151
+ const actions: Record<string, any> = { doSomething: { name: "doSomething" } };
152
+ const gqlMsgs = aguiToGQL(aguiMsg, actions);
153
+ const aguiMsgs2 = gqlToAGUI(gqlMsgs, actions);
154
+ // The render function should be preserved in actions context
155
+ expect(typeof actions.doSomething.render).toBe("function");
156
+ // The roundtripped message should have the same tool call
157
+ if ("toolCalls" in aguiMsgs2[1]) {
158
+ expect((aguiMsgs2[1] as any).toolCalls[0].function.name).toBe("doSomething");
159
+ }
160
+ });
161
+
162
+ test("image message GQL -> AGUI -> GQL", () => {
163
+ const gqlMsg = new gql.ImageMessage({
164
+ id: "img-1",
165
+ format: "jpeg",
166
+ bytes: "somebase64string",
167
+ role: gql.Role.user,
168
+ });
169
+ const aguiMsgs = gqlToAGUI(gqlMsg);
170
+ const gqlMsgs2 = aguiToGQL(aguiMsgs);
171
+ expect(gqlMsgs2[0].id).toBe(gqlMsg.id);
172
+ expect((gqlMsgs2[0] as any).format).toBe(gqlMsg.format);
173
+ expect((gqlMsgs2[0] as any).bytes).toBe(gqlMsg.bytes);
174
+ expect((gqlMsgs2[0] as any).role).toBe(gqlMsg.role);
175
+ });
176
+
177
+ test("image message AGUI -> GQL -> AGUI (assistant and user)", () => {
178
+ // Assistant image message
179
+ const aguiAssistantImageMsg: agui.Message = {
180
+ id: "img-assistant-1",
181
+ role: "assistant",
182
+ image: {
183
+ format: "jpeg",
184
+ bytes: "assistantbase64data",
185
+ },
186
+ content: "", // required for type
187
+ };
188
+ const gqlAssistantMsgs = aguiToGQL(aguiAssistantImageMsg);
189
+ const aguiAssistantMsgs2 = gqlToAGUI(gqlAssistantMsgs);
190
+ expect(aguiAssistantMsgs2[0].id).toBe(aguiAssistantImageMsg.id);
191
+ expect(aguiAssistantMsgs2[0].role).toBe("assistant");
192
+ expect((aguiAssistantMsgs2[0] as any).image.format).toBe("jpeg");
193
+ expect((aguiAssistantMsgs2[0] as any).image.bytes).toBe("assistantbase64data");
194
+
195
+ // User image message
196
+ const aguiUserImageMsg: agui.Message = {
197
+ id: "img-user-1",
198
+ role: "user",
199
+ image: {
200
+ format: "png",
201
+ bytes: "userbase64data",
202
+ },
203
+ content: "", // required for type
204
+ };
205
+ const gqlUserMsgs = aguiToGQL(aguiUserImageMsg);
206
+ const aguiUserMsgs2 = gqlToAGUI(gqlUserMsgs);
207
+ expect(aguiUserMsgs2[0].id).toBe(aguiUserImageMsg.id);
208
+ expect(aguiUserMsgs2[0].role).toBe("user");
209
+ expect((aguiUserMsgs2[0] as any).image.format).toBe("png");
210
+ expect((aguiUserMsgs2[0] as any).image.bytes).toBe("userbase64data");
211
+ });
212
+
213
+ test("wild card action roundtrip conversion", () => {
214
+ const mockRender = vi.fn((props) => `Wildcard rendered: ${props.args.test}`);
215
+ const aguiMsg: agui.Message = {
216
+ id: "assistant-wildcard-1",
217
+ role: "assistant",
218
+ content: "Running wild card action",
219
+ toolCalls: [
220
+ {
221
+ id: "tool-call-wildcard-1",
222
+ type: "function",
223
+ function: {
224
+ name: "unknownAction",
225
+ arguments: JSON.stringify({ test: "wildcard-value" }),
226
+ },
227
+ },
228
+ ],
229
+ generativeUI: mockRender,
230
+ };
231
+
232
+ const actions: Record<string, any> = {
233
+ "*": { name: "*" },
234
+ };
235
+
236
+ // AGUI -> GQL -> AGUI roundtrip
237
+ const gqlMsgs = aguiToGQL(aguiMsg, actions);
238
+ const aguiMsgs2 = gqlToAGUI(gqlMsgs, actions);
239
+
240
+ // Verify the wild card action preserved the render function
241
+ expect(typeof actions["*"].render).toBe("function");
242
+ expect(actions["*"].render).toBe(mockRender);
243
+
244
+ // Verify the roundtripped message structure
245
+ expect(aguiMsgs2).toHaveLength(2);
246
+ expect(aguiMsgs2[0].role).toBe("assistant");
247
+ expect(aguiMsgs2[1].role).toBe("assistant");
248
+
249
+ // Check that the tool call is preserved
250
+ if ("toolCalls" in aguiMsgs2[1]) {
251
+ expect((aguiMsgs2[1] as any).toolCalls[0].function.name).toBe("unknownAction");
252
+ expect((aguiMsgs2[1] as any).toolCalls[0].function.arguments).toBe(
253
+ '{"test":"wildcard-value"}',
254
+ );
255
+ }
256
+ });
257
+
258
+ test("wild card action with specific action priority roundtrip", () => {
259
+ const mockRender = vi.fn((props) => `Specific action rendered: ${props.args.test}`);
260
+ const aguiMsg: agui.Message = {
261
+ id: "assistant-priority-1",
262
+ role: "assistant",
263
+ content: "Running specific action",
264
+ toolCalls: [
265
+ {
266
+ id: "tool-call-priority-1",
267
+ type: "function",
268
+ function: {
269
+ name: "specificAction",
270
+ arguments: JSON.stringify({ test: "specific-value" }),
271
+ },
272
+ },
273
+ ],
274
+ generativeUI: mockRender,
275
+ };
276
+
277
+ const actions: Record<string, any> = {
278
+ specificAction: { name: "specificAction" },
279
+ "*": { name: "*" },
280
+ };
281
+
282
+ // AGUI -> GQL -> AGUI roundtrip
283
+ const gqlMsgs = aguiToGQL(aguiMsg, actions);
284
+ const aguiMsgs2 = gqlToAGUI(gqlMsgs, actions);
285
+
286
+ // Verify the specific action preserved the render function (not wild card)
287
+ expect(typeof actions.specificAction.render).toBe("function");
288
+ expect(actions.specificAction.render).toBe(mockRender);
289
+ expect(actions["*"].render).toBeUndefined();
290
+
291
+ // Verify the roundtripped message structure
292
+ expect(aguiMsgs2).toHaveLength(2);
293
+ expect(aguiMsgs2[0].role).toBe("assistant");
294
+ expect(aguiMsgs2[1].role).toBe("assistant");
295
+
296
+ // Check that the tool call is preserved
297
+ if ("toolCalls" in aguiMsgs2[1]) {
298
+ expect((aguiMsgs2[1] as any).toolCalls[0].function.name).toBe("specificAction");
299
+ expect((aguiMsgs2[1] as any).toolCalls[0].function.arguments).toBe(
300
+ '{"test":"specific-value"}',
301
+ );
302
+ }
303
+ });
304
+
305
+ test("wild card action GQL -> AGUI -> GQL roundtrip", () => {
306
+ const actionExecMsg = new gql.ActionExecutionMessage({
307
+ id: "wildcard-action-1",
308
+ name: "unknownAction",
309
+ arguments: { test: "wildcard-gql-value" },
310
+ parentMessageId: "assistant-1",
311
+ });
312
+
313
+ const actions: Record<string, any> = {
314
+ "*": {
315
+ name: "*",
316
+ render: vi.fn((props) => `GQL wildcard rendered: ${props.args.test}`),
317
+ },
318
+ };
319
+
320
+ // GQL -> AGUI -> GQL roundtrip
321
+ const aguiMsgs = gqlToAGUI([actionExecMsg], actions);
322
+ const gqlMsgs2 = aguiToGQL(aguiMsgs, actions);
323
+
324
+ // When converting ActionExecutionMessage to AGUI and back, we get:
325
+ // 1. A TextMessage (assistant message with toolCalls)
326
+ // 2. An ActionExecutionMessage (the tool call itself)
327
+ expect(gqlMsgs2).toHaveLength(2);
328
+ expect(gqlMsgs2[0].id).toBe("wildcard-action-1");
329
+ expect((gqlMsgs2[0] as any).role).toBe(gql.Role.assistant);
330
+ expect(gqlMsgs2[1].id).toBe("wildcard-action-1");
331
+ expect((gqlMsgs2[1] as any).name).toBe("unknownAction");
332
+ expect((gqlMsgs2[1] as any).arguments).toEqual({ test: "wildcard-gql-value" });
333
+ });
334
+
335
+ test("roundtrip conversion with result parsing edge cases", () => {
336
+ // Test with a tool result that contains a JSON string
337
+ const toolResultMsg: agui.Message = {
338
+ id: "tool-result-json",
339
+ role: "tool",
340
+ content: '{"status": "success", "data": {"value": 42}}',
341
+ toolCallId: "tool-call-json",
342
+ toolName: "jsonAction",
343
+ };
344
+
345
+ // Convert AGUI -> GQL -> AGUI
346
+ const gqlMsgs = aguiToGQL(toolResultMsg);
347
+ const aguiMsgs = gqlToAGUI(gqlMsgs);
348
+
349
+ expect(gqlMsgs).toHaveLength(1);
350
+ expect(gqlMsgs[0]).toBeInstanceOf(gql.ResultMessage);
351
+ expect((gqlMsgs[0] as any).result).toBe('{"status": "success", "data": {"value": 42}}');
352
+
353
+ expect(aguiMsgs).toHaveLength(1);
354
+ expect(aguiMsgs[0].role).toBe("tool");
355
+ expect(aguiMsgs[0].content).toBe('{"status": "success", "data": {"value": 42}}');
356
+ });
357
+
358
+ test("roundtrip conversion with object content in tool results", () => {
359
+ // Test with a tool result that has object content (edge case)
360
+ const toolResultMsg: agui.Message = {
361
+ id: "tool-result-object",
362
+ role: "tool",
363
+ content: { status: "success", data: { value: 42 } } as any,
364
+ toolCallId: "tool-call-object",
365
+ toolName: "objectAction",
366
+ };
367
+
368
+ // Convert AGUI -> GQL -> AGUI
369
+ const gqlMsgs = aguiToGQL(toolResultMsg);
370
+ const aguiMsgs = gqlToAGUI(gqlMsgs);
371
+
372
+ expect(gqlMsgs).toHaveLength(1);
373
+ expect(gqlMsgs[0]).toBeInstanceOf(gql.ResultMessage);
374
+ expect((gqlMsgs[0] as any).result).toBe('{"status":"success","data":{"value":42}}');
375
+
376
+ expect(aguiMsgs).toHaveLength(1);
377
+ expect(aguiMsgs[0].role).toBe("tool");
378
+ expect(aguiMsgs[0].content).toBe('{"status":"success","data":{"value":42}}');
379
+ });
380
+
381
+ test("roundtrip conversion with action execution and result parsing", () => {
382
+ const mockRender = vi.fn((props) => `Rendered: ${JSON.stringify(props.result)}`);
383
+
384
+ // Create action execution message
385
+ const actionExecMsg = new gql.ActionExecutionMessage({
386
+ id: "action-with-result",
387
+ name: "testAction",
388
+ arguments: { input: "test-value" },
389
+ parentMessageId: "parent-result",
390
+ });
391
+
392
+ // Create result message
393
+ const resultMsg = new gql.ResultMessage({
394
+ id: "result-with-json",
395
+ result: '{"output": "processed", "count": 5}',
396
+ actionExecutionId: "action-with-result",
397
+ actionName: "testAction",
398
+ });
399
+
400
+ const actions = {
401
+ testAction: {
402
+ name: "testAction",
403
+ render: mockRender,
404
+ },
405
+ };
406
+
407
+ // Convert GQL -> AGUI
408
+ const aguiMsgs = gqlToAGUI([actionExecMsg, resultMsg], actions);
409
+
410
+ // The action execution should have a generativeUI function that parses string results
411
+ expect(aguiMsgs).toHaveLength(2);
412
+ expect(aguiMsgs[0].role).toBe("assistant");
413
+ expect("generativeUI" in aguiMsgs[0]).toBe(true);
414
+ expect(aguiMsgs[1].role).toBe("tool");
415
+ expect(aguiMsgs[1].content).toBe('{"output": "processed", "count": 5}');
416
+
417
+ // Test that the render function receives parsed results
418
+ if ("generativeUI" in aguiMsgs[0] && aguiMsgs[0].generativeUI) {
419
+ aguiMsgs[0].generativeUI({ result: '{"parsed": true}' });
420
+ expect(mockRender).toHaveBeenCalledWith(
421
+ expect.objectContaining({
422
+ result: { parsed: true }, // Should be parsed from string
423
+ }),
424
+ );
425
+ }
426
+
427
+ // Convert back AGUI -> GQL
428
+ const gqlMsgs2 = aguiToGQL(aguiMsgs, actions);
429
+
430
+ // Should have 3 messages: TextMessage, ActionExecutionMessage, ResultMessage
431
+ expect(gqlMsgs2).toHaveLength(3);
432
+ expect(gqlMsgs2[0]).toBeInstanceOf(gql.TextMessage);
433
+ expect(gqlMsgs2[1]).toBeInstanceOf(gql.ActionExecutionMessage);
434
+ expect(gqlMsgs2[2]).toBeInstanceOf(gql.ResultMessage);
435
+
436
+ // Check that arguments roundtripped correctly
437
+ expect((gqlMsgs2[1] as any).arguments).toEqual({ input: "test-value" });
438
+ expect((gqlMsgs2[2] as any).result).toBe('{"output": "processed", "count": 5}');
439
+ });
440
+
441
+ test("roundtrip conversion verifies correct property distribution for regular actions", () => {
442
+ const mockRender = vi.fn((props) => `Regular action: ${JSON.stringify(props.args)}`);
443
+
444
+ const actionExecMsg = new gql.ActionExecutionMessage({
445
+ id: "regular-action-test",
446
+ name: "regularAction",
447
+ arguments: { test: "regular-value" },
448
+ parentMessageId: "parent-regular",
449
+ });
450
+
451
+ const actions = {
452
+ regularAction: {
453
+ name: "regularAction",
454
+ render: mockRender,
455
+ },
456
+ };
457
+
458
+ // GQL -> AGUI -> GQL roundtrip
459
+ const aguiMsgs = gqlToAGUI([actionExecMsg], actions);
460
+ const gqlMsgs2 = aguiToGQL(aguiMsgs, actions);
461
+
462
+ // Verify the roundtrip preserved the action
463
+ expect(gqlMsgs2).toHaveLength(2);
464
+ expect(gqlMsgs2[1]).toBeInstanceOf(gql.ActionExecutionMessage);
465
+ expect((gqlMsgs2[1] as any).name).toBe("regularAction");
466
+
467
+ // Test that regular actions do NOT receive the name property in render props
468
+ if ("generativeUI" in aguiMsgs[0] && aguiMsgs[0].generativeUI) {
469
+ aguiMsgs[0].generativeUI();
470
+ expect(mockRender).toHaveBeenCalledWith(
471
+ expect.objectContaining({
472
+ args: { test: "regular-value" },
473
+ // name property should NOT be present for regular actions
474
+ }),
475
+ );
476
+
477
+ // Verify name property is NOT present
478
+ const callArgs = mockRender.mock.calls[0][0];
479
+ expect(callArgs).not.toHaveProperty("name");
480
+ }
481
+ });
482
+
483
+ test("roundtrip conversion verifies correct property distribution for wildcard actions", () => {
484
+ const mockRender = vi.fn(
485
+ (props) => `Wildcard action: ${props.name} with ${JSON.stringify(props.args)}`,
486
+ );
487
+
488
+ const actionExecMsg = new gql.ActionExecutionMessage({
489
+ id: "wildcard-action-test",
490
+ name: "unknownAction",
491
+ arguments: { test: "wildcard-value" },
492
+ parentMessageId: "parent-wildcard",
493
+ });
494
+
495
+ const actions = {
496
+ "*": {
497
+ name: "*",
498
+ render: mockRender,
499
+ },
500
+ };
501
+
502
+ // GQL -> AGUI -> GQL roundtrip
503
+ const aguiMsgs = gqlToAGUI([actionExecMsg], actions);
504
+ const gqlMsgs2 = aguiToGQL(aguiMsgs, actions);
505
+
506
+ // Verify the roundtrip preserved the action
507
+ expect(gqlMsgs2).toHaveLength(2);
508
+ expect(gqlMsgs2[1]).toBeInstanceOf(gql.ActionExecutionMessage);
509
+ expect((gqlMsgs2[1] as any).name).toBe("unknownAction");
510
+
511
+ // Test that wildcard actions DO receive the name property in render props
512
+ if ("generativeUI" in aguiMsgs[0] && aguiMsgs[0].generativeUI) {
513
+ aguiMsgs[0].generativeUI();
514
+ expect(mockRender).toHaveBeenCalledWith(
515
+ expect.objectContaining({
516
+ args: { test: "wildcard-value" },
517
+ name: "unknownAction", // name property SHOULD be present for wildcard actions
518
+ }),
519
+ );
520
+
521
+ // Verify name property IS present
522
+ const callArgs = mockRender.mock.calls[0][0];
523
+ expect(callArgs).toHaveProperty("name", "unknownAction");
524
+ }
525
+ });
526
+ });
@@ -59,6 +59,7 @@ import {
59
59
  CopilotKitLowLevelError,
60
60
  isStructuredCopilotKitError,
61
61
  } from "@copilotkit/shared";
62
+ import { CopilotRuntime } from "../../lib";
62
63
 
63
64
  const invokeGuardrails = async ({
64
65
  baseUrl,
@@ -128,7 +129,7 @@ export class CopilotResolver {
128
129
  let logger = ctx.logger.child({ component: "CopilotResolver.availableAgents" });
129
130
 
130
131
  logger.debug("Processing");
131
- const agentsWithEndpoints = await ctx._copilotkit.runtime.discoverAgentsFromEndpoints(ctx);
132
+ const agentsWithEndpoints = [];
132
133
 
133
134
  logger.debug("Event source created, creating response");
134
135
 
@@ -172,7 +173,7 @@ export class CopilotResolver {
172
173
  ctx.properties = { ...ctx.properties, ...properties };
173
174
  }
174
175
 
175
- const copilotRuntime = ctx._copilotkit.runtime;
176
+ const copilotRuntime = ctx._copilotkit.runtime as unknown as CopilotRuntime;
176
177
  const serviceAdapter = ctx._copilotkit.serviceAdapter;
177
178
 
178
179
  let copilotCloudPublicApiKey: string | null = null;
@@ -192,19 +193,6 @@ export class CopilotResolver {
192
193
  if (!copilotCloudPublicApiKey) {
193
194
  logger.error("Public API key not found in headers");
194
195
 
195
- await copilotRuntime.errorGraphQLError(
196
- {
197
- message: "X-CopilotCloud-Public-API-Key header is required",
198
- code: "MISSING_PUBLIC_API_KEY",
199
- type: "GraphQLError",
200
- },
201
- {
202
- operation: "generateCopilotResponse",
203
- cloudConfigPresent: Boolean(data.cloud),
204
- guardrailsEnabled: Boolean(data.cloud?.guardrails),
205
- },
206
- );
207
-
208
196
  throw new GraphQLError("X-CopilotCloud-Public-API-Key header is required");
209
197
  }
210
198
 
@@ -239,39 +227,6 @@ export class CopilotResolver {
239
227
 
240
228
  logger.debug("Processing");
241
229
  let runtimeResponse;
242
- try {
243
- runtimeResponse = await copilotRuntime.processRuntimeRequest({
244
- serviceAdapter,
245
- messages: data.messages,
246
- actions: data.frontend.actions.filter(
247
- (action) => action.available !== ActionInputAvailability.disabled,
248
- ),
249
- threadId: data.threadId,
250
- runId: data.runId,
251
- publicApiKey: copilotCloudPublicApiKey,
252
- outputMessagesPromise,
253
- graphqlContext: ctx,
254
- forwardedParameters: data.forwardedParameters,
255
- agentSession: data.agentSession,
256
- agentStates: data.agentStates,
257
- url: data.frontend.url,
258
- extensions: data.extensions,
259
- metaEvents: data.metaEvents,
260
- context: data.context,
261
- });
262
- } catch (error) {
263
- // Catch structured CopilotKit errors at the main mutation level and re-throw as GraphQL errors
264
- if (isStructuredCopilotKitError(error) || (error as any)?.extensions?.visibility) {
265
- throw new GraphQLError(error.message || "Agent error occurred", {
266
- extensions: {
267
- ...(error as any).extensions,
268
- code: (error as any).code || (error as any).extensions?.code || "AGENT_ERROR",
269
- originalError: error,
270
- },
271
- });
272
- }
273
- throw error; // Re-throw non-CopilotKit errors as-is
274
- }
275
230
 
276
231
  const {
277
232
  eventSource,
@@ -5,12 +5,13 @@ import { LoadAgentStateResponse } from "../types/load-agent-state-response.type"
5
5
  import type { GraphQLContext } from "../../lib/integrations";
6
6
  import { LoadAgentStateInput } from "../inputs/load-agent-state.input";
7
7
  import { CopilotKitAgentDiscoveryError } from "@copilotkit/shared";
8
+ import { CopilotRuntime } from "../../lib";
8
9
 
9
10
  @Resolver(() => LoadAgentStateResponse)
10
11
  export class StateResolver {
11
12
  @Query(() => LoadAgentStateResponse)
12
13
  async loadAgentState(@Ctx() ctx: GraphQLContext, @Arg("data") data: LoadAgentStateInput) {
13
- const agents = await ctx._copilotkit.runtime.getAllAgents(ctx);
14
+ const agents = [];
14
15
  const hasAgent = agents.some((agent) => agent.name === data.agentName);
15
16
  if (!hasAgent) {
16
17
  throw new CopilotKitAgentDiscoveryError({
@@ -19,7 +20,7 @@ export class StateResolver {
19
20
  });
20
21
  }
21
22
 
22
- const state = await ctx._copilotkit.runtime.loadAgentState(ctx, data.threadId, data.agentName);
23
+ const state = {};
23
24
 
24
25
  return state;
25
26
  }
@@ -1,3 +1,4 @@
1
+ import { randomId } from "@copilotkit/shared";
1
2
  import {
2
3
  ActionExecutionMessageInput,
3
4
  ResultMessageInput,
@@ -6,7 +7,9 @@ import {
6
7
  ImageMessageInput,
7
8
  } from "../../inputs/message.input";
8
9
  import { BaseMessageInput } from "../base";
10
+ import { BaseMessageOutput } from "../copilot-response.type";
9
11
  import { MessageRole } from "../enums";
12
+ import { MessageStatus, MessageStatusCode } from "../message-status.type";
10
13
 
11
14
  export type MessageType =
12
15
  | "TextMessage"
@@ -15,8 +18,18 @@ export type MessageType =
15
18
  | "AgentStateMessage"
16
19
  | "ImageMessage";
17
20
 
18
- export class Message extends BaseMessageInput {
21
+ export class Message {
19
22
  type: MessageType;
23
+ id: BaseMessageOutput["id"];
24
+ createdAt: BaseMessageOutput["createdAt"];
25
+ status: MessageStatus;
26
+
27
+ constructor(props: any) {
28
+ props.id ??= randomId();
29
+ props.status ??= { code: MessageStatusCode.Success };
30
+ props.createdAt ??= new Date();
31
+ Object.assign(this, props);
32
+ }
20
33
 
21
34
  isTextMessage(): this is TextMessage {
22
35
  return this.type === "TextMessage";
@@ -39,11 +52,24 @@ export class Message extends BaseMessageInput {
39
52
  }
40
53
  }
41
54
 
42
- export class TextMessage extends Message implements TextMessageInput {
43
- type: MessageType = "TextMessage";
44
- content: string;
45
- role: MessageRole;
46
- parentMessageId?: string;
55
+ // alias Role to MessageRole
56
+ export const Role = MessageRole;
57
+
58
+ // when constructing any message, the base fields are optional
59
+ type MessageConstructorOptions = Partial<Message>;
60
+
61
+ type TextMessageConstructorOptions = MessageConstructorOptions & TextMessageInput;
62
+
63
+ export class TextMessage extends Message implements TextMessageConstructorOptions {
64
+ content: TextMessageInput["content"];
65
+ parentMessageId: TextMessageInput["parentMessageId"];
66
+ role: TextMessageInput["role"];
67
+ type = "TextMessage" as const;
68
+
69
+ constructor(props: TextMessageConstructorOptions) {
70
+ super(props);
71
+ this.type = "TextMessage";
72
+ }
47
73
  }
48
74
 
49
75
  export class ActionExecutionMessage