@copilotkit/runtime 1.56.5 → 1.57.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 (62) hide show
  1. package/dist/package.cjs +1 -1
  2. package/dist/package.mjs +1 -1
  3. package/dist/v2/index.d.cts +2 -2
  4. package/dist/v2/index.d.mts +2 -2
  5. package/dist/v2/runtime/core/fetch-handler.cjs +16 -0
  6. package/dist/v2/runtime/core/fetch-handler.cjs.map +1 -1
  7. package/dist/v2/runtime/core/fetch-handler.d.cts.map +1 -1
  8. package/dist/v2/runtime/core/fetch-handler.d.mts.map +1 -1
  9. package/dist/v2/runtime/core/fetch-handler.mjs +17 -1
  10. package/dist/v2/runtime/core/fetch-handler.mjs.map +1 -1
  11. package/dist/v2/runtime/core/fetch-router.cjs +18 -1
  12. package/dist/v2/runtime/core/fetch-router.cjs.map +1 -1
  13. package/dist/v2/runtime/core/fetch-router.mjs +18 -1
  14. package/dist/v2/runtime/core/fetch-router.mjs.map +1 -1
  15. package/dist/v2/runtime/core/hooks.cjs.map +1 -1
  16. package/dist/v2/runtime/core/hooks.d.cts +8 -0
  17. package/dist/v2/runtime/core/hooks.d.cts.map +1 -1
  18. package/dist/v2/runtime/core/hooks.d.mts +8 -0
  19. package/dist/v2/runtime/core/hooks.d.mts.map +1 -1
  20. package/dist/v2/runtime/core/hooks.mjs.map +1 -1
  21. package/dist/v2/runtime/handlers/handle-run.cjs +1 -0
  22. package/dist/v2/runtime/handlers/handle-run.cjs.map +1 -1
  23. package/dist/v2/runtime/handlers/handle-run.mjs +1 -0
  24. package/dist/v2/runtime/handlers/handle-run.mjs.map +1 -1
  25. package/dist/v2/runtime/handlers/intelligence/threads.cjs +124 -12
  26. package/dist/v2/runtime/handlers/intelligence/threads.cjs.map +1 -1
  27. package/dist/v2/runtime/handlers/intelligence/threads.mjs +122 -13
  28. package/dist/v2/runtime/handlers/intelligence/threads.mjs.map +1 -1
  29. package/dist/v2/runtime/index.d.cts +1 -1
  30. package/dist/v2/runtime/index.d.mts +1 -1
  31. package/dist/v2/runtime/intelligence-platform/client.cjs +30 -0
  32. package/dist/v2/runtime/intelligence-platform/client.cjs.map +1 -1
  33. package/dist/v2/runtime/intelligence-platform/client.d.cts +66 -0
  34. package/dist/v2/runtime/intelligence-platform/client.d.cts.map +1 -1
  35. package/dist/v2/runtime/intelligence-platform/client.d.mts +66 -0
  36. package/dist/v2/runtime/intelligence-platform/client.d.mts.map +1 -1
  37. package/dist/v2/runtime/intelligence-platform/client.mjs +30 -0
  38. package/dist/v2/runtime/intelligence-platform/client.mjs.map +1 -1
  39. package/dist/v2/runtime/runner/in-memory.cjs +94 -22
  40. package/dist/v2/runtime/runner/in-memory.cjs.map +1 -1
  41. package/dist/v2/runtime/runner/in-memory.d.cts +65 -2
  42. package/dist/v2/runtime/runner/in-memory.d.cts.map +1 -1
  43. package/dist/v2/runtime/runner/in-memory.d.mts +65 -2
  44. package/dist/v2/runtime/runner/in-memory.d.mts.map +1 -1
  45. package/dist/v2/runtime/runner/in-memory.mjs +94 -22
  46. package/dist/v2/runtime/runner/in-memory.mjs.map +1 -1
  47. package/dist/v2/runtime/runner/index.d.cts +1 -1
  48. package/dist/v2/runtime/runner/index.d.mts +1 -1
  49. package/package.json +2 -2
  50. package/src/v2/runtime/__tests__/fetch-handler-validation.test.ts +68 -0
  51. package/src/v2/runtime/__tests__/fetch-router.test.ts +46 -0
  52. package/src/v2/runtime/__tests__/handle-run.test.ts +97 -1
  53. package/src/v2/runtime/__tests__/handle-threads.test.ts +493 -13
  54. package/src/v2/runtime/core/fetch-handler.ts +19 -0
  55. package/src/v2/runtime/core/fetch-router.ts +33 -1
  56. package/src/v2/runtime/core/hooks.ts +3 -0
  57. package/src/v2/runtime/handlers/handle-run.ts +4 -0
  58. package/src/v2/runtime/handlers/handle-threads.ts +3 -0
  59. package/src/v2/runtime/handlers/intelligence/threads.ts +200 -41
  60. package/src/v2/runtime/intelligence-platform/client.ts +76 -0
  61. package/src/v2/runtime/runner/__tests__/in-memory-runner.test.ts +417 -3
  62. package/src/v2/runtime/runner/in-memory.ts +137 -51
@@ -35,6 +35,10 @@ export async function handleRunAgent({
35
35
  return agent;
36
36
  }
37
37
 
38
+ // Ensure the clone carries the registry key so InMemoryAgentRunner can
39
+ // tag historic runs with the correct agentId for filtering.
40
+ agent.agentId = agentId;
41
+
38
42
  configureAgentForRequest({ runtime, request, agentId, agent });
39
43
 
40
44
  if (
@@ -1,7 +1,10 @@
1
1
  export {
2
2
  handleArchiveThread,
3
+ handleClearThreads,
3
4
  handleDeleteThread,
5
+ handleGetThreadEvents,
4
6
  handleGetThreadMessages,
7
+ handleGetThreadState,
5
8
  handleListThreads,
6
9
  handleSubscribeToThreads,
7
10
  handleUpdateThread,
@@ -7,6 +7,7 @@ import { logger } from "@copilotkit/shared";
7
7
  import { errorResponse, isHandlerResponse } from "../shared/json-response";
8
8
  import { isValidIdentifier } from "../shared/intelligence-utils";
9
9
  import { resolveIntelligenceUser } from "../shared/resolve-intelligence-user";
10
+ import { InMemoryAgentRunner } from "../../runner/in-memory";
10
11
 
11
12
  interface ThreadsHandlerParams {
12
13
  runtime: CopilotRuntimeLike;
@@ -73,40 +74,70 @@ export async function handleListThreads({
73
74
  runtime,
74
75
  request,
75
76
  }: ThreadsHandlerParams): Promise<Response> {
76
- const intelligenceRuntime = requireIntelligenceRuntime(runtime);
77
- if (isHandlerResponse(intelligenceRuntime)) {
78
- return intelligenceRuntime;
77
+ // Intelligence platform path
78
+ if (isIntelligenceRuntime(runtime)) {
79
+ try {
80
+ const url = new URL(request.url);
81
+ const agentId = url.searchParams.get("agentId");
82
+ const includeArchived =
83
+ url.searchParams.get("includeArchived") === "true";
84
+ const limitParam = url.searchParams.get("limit");
85
+ const cursor = url.searchParams.get("cursor");
86
+ const user = await resolveIntelligenceUser({ runtime, request });
87
+ if (isHandlerResponse(user)) return user;
88
+
89
+ if (!isValidIdentifier(agentId)) {
90
+ return errorResponse("Valid agentId query param is required", 400);
91
+ }
92
+
93
+ const data = await runtime.intelligence.listThreads({
94
+ userId: user.id,
95
+ agentId,
96
+ ...(includeArchived ? { includeArchived: true } : {}),
97
+ ...(limitParam ? { limit: Number(limitParam) } : {}),
98
+ ...(cursor ? { cursor } : {}),
99
+ });
100
+
101
+ return Response.json(data);
102
+ } catch (error) {
103
+ logger.error({ err: error }, "Error listing threads");
104
+ return errorResponse("Failed to list threads", 500);
105
+ }
79
106
  }
80
107
 
81
- try {
108
+ // Local in-memory fallback — useful for local development without Intelligence
109
+ if (runtime.runner instanceof InMemoryAgentRunner) {
82
110
  const url = new URL(request.url);
83
111
  const agentId = url.searchParams.get("agentId");
84
- const includeArchived = url.searchParams.get("includeArchived") === "true";
85
- const limitParam = url.searchParams.get("limit");
86
- const cursor = url.searchParams.get("cursor");
87
- const user = await resolveIntelligenceUser({
88
- runtime: intelligenceRuntime,
89
- request,
90
- });
91
- if (isHandlerResponse(user)) return user;
92
-
93
- if (!isValidIdentifier(agentId)) {
94
- return errorResponse("Valid agentId query param is required", 400);
112
+ let threads = runtime.runner.listThreads();
113
+ if (agentId) {
114
+ threads = threads.filter((t) => t.agentId === agentId);
95
115
  }
116
+ return Response.json({ threads, nextCursor: null });
117
+ }
96
118
 
97
- const data = await intelligenceRuntime.intelligence.listThreads({
98
- userId: user.id,
99
- agentId,
100
- ...(includeArchived ? { includeArchived: true } : {}),
101
- ...(limitParam ? { limit: Number(limitParam) } : {}),
102
- ...(cursor ? { cursor } : {}),
103
- });
119
+ return errorResponse(
120
+ "Missing CopilotKitIntelligence configuration. Thread operations require a CopilotKitIntelligence instance to be provided in CopilotRuntime options.",
121
+ 422,
122
+ );
123
+ }
104
124
 
105
- return Response.json(data);
106
- } catch (error) {
107
- logger.error({ err: error }, "Error listing threads");
108
- return errorResponse("Failed to list threads", 500);
125
+ /**
126
+ * Clears all in-memory thread history for the local-dev InMemory fallback.
127
+ *
128
+ * The local-dev fallback exposes this so consumers (e.g. the demo's Clear
129
+ * button) can wipe in-memory thread history without restarting the runtime.
130
+ * Intentionally a no-op when the Intelligence platform is configured: real
131
+ * thread history lives in the database and must not be wiped by a
132
+ * client-side page load.
133
+ */
134
+ export function handleClearThreads({
135
+ runtime,
136
+ }: ThreadsHandlerParams): Response {
137
+ if (runtime.runner instanceof InMemoryAgentRunner) {
138
+ runtime.runner.clearThreads();
109
139
  }
140
+ return new Response(null, { status: 204 });
110
141
  }
111
142
 
112
143
  export async function handleUpdateThread({
@@ -237,25 +268,153 @@ export async function handleGetThreadMessages({
237
268
  request,
238
269
  threadId,
239
270
  }: ThreadMutationParams): Promise<Response> {
240
- const intelligenceRuntime = requireIntelligenceRuntime(runtime);
241
- if (isHandlerResponse(intelligenceRuntime)) {
242
- return intelligenceRuntime;
271
+ // Intelligence platform path
272
+ if (isIntelligenceRuntime(runtime)) {
273
+ try {
274
+ const user = await resolveIntelligenceUser({ runtime, request });
275
+ if (isHandlerResponse(user)) return user;
276
+
277
+ const data = await runtime.intelligence.getThreadMessages({ threadId });
278
+ return Response.json(data);
279
+ } catch (error) {
280
+ logger.error({ err: error, threadId }, "Error fetching thread messages");
281
+ return errorResponse("Failed to fetch thread messages", 500);
282
+ }
243
283
  }
244
284
 
245
- try {
246
- const user = await resolveIntelligenceUser({
247
- runtime: intelligenceRuntime,
248
- request,
285
+ // Local in-memory fallback — useful for local development without Intelligence
286
+ if (runtime.runner instanceof InMemoryAgentRunner) {
287
+ const messages = runtime.runner.getThreadMessages(threadId);
288
+ // Map ag-ui Message objects to the same shape the Intelligence platform
289
+ // returns. Switching on the discriminant `role` lets each branch read
290
+ // the narrowed message arm directly, instead of laundering through
291
+ // `Record<string, unknown>` and chained `as` casts.
292
+ const mapped = messages.map((msg) => {
293
+ switch (msg.role) {
294
+ case "assistant": {
295
+ const toolCalls = msg.toolCalls ?? [];
296
+ return {
297
+ id: msg.id,
298
+ role: msg.role,
299
+ ...(msg.content !== undefined ? { content: msg.content } : {}),
300
+ ...(toolCalls.length > 0
301
+ ? {
302
+ toolCalls: toolCalls.map((tc) => ({
303
+ id: tc.id,
304
+ name: tc.function.name,
305
+ args: tc.function.arguments,
306
+ })),
307
+ }
308
+ : {}),
309
+ };
310
+ }
311
+ case "tool":
312
+ return {
313
+ id: msg.id,
314
+ role: msg.role,
315
+ content: msg.content,
316
+ toolCallId: msg.toolCallId,
317
+ };
318
+ default:
319
+ return {
320
+ id: msg.id,
321
+ role: msg.role,
322
+ ...("content" in msg && msg.content !== undefined
323
+ ? { content: msg.content }
324
+ : {}),
325
+ };
326
+ }
249
327
  });
250
- if (isHandlerResponse(user)) return user;
328
+ return Response.json({ messages: mapped });
329
+ }
251
330
 
252
- const data = await intelligenceRuntime.intelligence.getThreadMessages({
253
- threadId,
254
- });
331
+ return errorResponse(
332
+ "Missing CopilotKitIntelligence configuration. Thread operations require a CopilotKitIntelligence instance to be provided in CopilotRuntime options.",
333
+ 422,
334
+ );
335
+ }
255
336
 
256
- return Response.json(data);
257
- } catch (error) {
258
- logger.error({ err: error, threadId }, "Error getting thread messages");
259
- return errorResponse("Failed to get thread messages", 500);
337
+ export async function handleGetThreadEvents({
338
+ runtime,
339
+ request,
340
+ threadId,
341
+ }: ThreadMutationParams): Promise<Response> {
342
+ // Intelligence platform path. Delegates to the platform's `_inspect`
343
+ // endpoint (Intelligence PR #144). Auth still flows through the standard
344
+ // identifyUser → API key path; threadId scoping happens server-side.
345
+ if (isIntelligenceRuntime(runtime)) {
346
+ try {
347
+ const user = await resolveIntelligenceUser({ runtime, request });
348
+ if (isHandlerResponse(user)) return user;
349
+
350
+ const data = await runtime.intelligence.getThreadEvents({ threadId });
351
+ // Strip platform-internal fields (`decodeErrorRowIds`, `truncated`)
352
+ // before returning to the inspector — those describe persistence-side
353
+ // concerns the inspector currently has no UI for. The shape becomes
354
+ // `{ events }`, matching the in-memory branch below.
355
+ return Response.json({ events: data.events });
356
+ } catch (error) {
357
+ logger.error({ err: error, threadId }, "Error fetching thread events");
358
+ return errorResponse("Failed to fetch thread events", 500);
359
+ }
360
+ }
361
+
362
+ // Local in-memory fallback
363
+ if (runtime.runner instanceof InMemoryAgentRunner) {
364
+ try {
365
+ const events = runtime.runner.getThreadEvents(threadId);
366
+ return Response.json({ events });
367
+ } catch (error) {
368
+ logger.error({ err: error, threadId }, "Error fetching thread events");
369
+ return errorResponse("Failed to fetch thread events", 500);
370
+ }
260
371
  }
372
+
373
+ return errorResponse(
374
+ "Missing CopilotKitIntelligence configuration. Thread operations require a CopilotKitIntelligence instance to be provided in CopilotRuntime options.",
375
+ 422,
376
+ );
377
+ }
378
+
379
+ export async function handleGetThreadState({
380
+ runtime,
381
+ request,
382
+ threadId,
383
+ }: ThreadMutationParams): Promise<Response> {
384
+ // Intelligence platform path. Delegates to the platform's `_inspect`
385
+ // state endpoint, which folds STATE_DELTA events onto the latest
386
+ // STATE_SNAPSHOT to return the thread's current state.
387
+ if (isIntelligenceRuntime(runtime)) {
388
+ try {
389
+ const user = await resolveIntelligenceUser({ runtime, request });
390
+ if (isHandlerResponse(user)) return user;
391
+
392
+ const data = await runtime.intelligence.getThreadState({ threadId });
393
+ // Flatten the discriminated `ThreadStateResult` to the wire shape the
394
+ // inspector consumes (`{ state: <value> | null }`). Missing snapshot
395
+ // and decode-error both surface as `null`; the inspector renders an
396
+ // empty state branch for null and the platform's decode-error case is
397
+ // already logged platform-side.
398
+ const state = data.kind === "snapshot" ? data.state : null;
399
+ return Response.json({ state });
400
+ } catch (error) {
401
+ logger.error({ err: error, threadId }, "Error fetching thread state");
402
+ return errorResponse("Failed to fetch thread state", 500);
403
+ }
404
+ }
405
+
406
+ if (runtime.runner instanceof InMemoryAgentRunner) {
407
+ try {
408
+ const state = runtime.runner.getThreadState(threadId);
409
+ return Response.json({ state });
410
+ } catch (error) {
411
+ logger.error({ err: error, threadId }, "Error fetching thread state");
412
+ return errorResponse("Failed to fetch thread state", 500);
413
+ }
414
+ }
415
+
416
+ return errorResponse(
417
+ "Missing CopilotKitIntelligence configuration. Thread operations require a CopilotKitIntelligence instance to be provided in CopilotRuntime options.",
418
+ 422,
419
+ );
261
420
  }
@@ -198,6 +198,40 @@ export interface ThreadMessagesResponse {
198
198
  messages: ThreadMessage[];
199
199
  }
200
200
 
201
+ /**
202
+ * Persisted AG-UI event for the inspector's debugging views. The platform
203
+ * stores raw events keyed by run; the `_inspect` route returns them in
204
+ * replay order (oldest first) across every run that targeted the thread.
205
+ */
206
+ export interface ThreadInspectEvent {
207
+ type: string;
208
+ [key: string]: unknown;
209
+ }
210
+
211
+ /**
212
+ * Response from {@link CopilotKitIntelligence.getThreadEvents}. Mirrors the
213
+ * `ThreadEventsResult` shape returned by the platform's
214
+ * `GET /api/_inspect/threads/:id/events` endpoint.
215
+ */
216
+ export interface ThreadEventsResponse {
217
+ events: ThreadInspectEvent[];
218
+ /** Row IDs the platform failed to decode (raw column corrupted). */
219
+ decodeErrorRowIds: string[];
220
+ /** True when the platform hit its per-thread event cap. */
221
+ truncated: boolean;
222
+ }
223
+
224
+ /**
225
+ * Response from {@link CopilotKitIntelligence.getThreadState}. Mirrors the
226
+ * discriminated `ThreadStateResult` returned by the platform's
227
+ * `GET /api/_inspect/threads/:id/state` endpoint, which folds RFC 6902
228
+ * STATE_DELTA events on top of the latest STATE_SNAPSHOT.
229
+ */
230
+ export type ThreadStateResponse =
231
+ | { kind: "no-snapshot" }
232
+ | { kind: "snapshot-decode-error" }
233
+ | { kind: "snapshot"; state: unknown; skippedDeltas: number };
234
+
201
235
  export interface AcquireThreadLockRequest {
202
236
  threadId: string;
203
237
  runId: string;
@@ -556,6 +590,48 @@ export class CopilotKitIntelligence {
556
590
  );
557
591
  }
558
592
 
593
+ /**
594
+ * Fetch the persisted AG-UI event stream for a thread.
595
+ *
596
+ * Backed by the platform's `GET /api/_inspect/threads/:id/events`
597
+ * introspection endpoint (see Intelligence PR #144). Events are returned
598
+ * in replay order across every run that targeted the thread. The
599
+ * `_inspect/` prefix flags this as debug-only — production code paths
600
+ * must not depend on it.
601
+ *
602
+ * @throws {@link PlatformRequestError} on non-2xx responses.
603
+ */
604
+ async getThreadEvents(params: {
605
+ threadId: string;
606
+ }): Promise<ThreadEventsResponse> {
607
+ return this.#request<ThreadEventsResponse>(
608
+ "GET",
609
+ `/api/_inspect/threads/${encodeURIComponent(params.threadId)}/events`,
610
+ );
611
+ }
612
+
613
+ /**
614
+ * Fetch the current agent state for a thread.
615
+ *
616
+ * Backed by the platform's `GET /api/_inspect/threads/:id/state`
617
+ * introspection endpoint (see Intelligence PR #144). The platform folds
618
+ * RFC 6902 STATE_DELTA events on top of the latest STATE_SNAPSHOT, so
619
+ * the returned state reflects the thread's current state — not just the
620
+ * last snapshot. The discriminated response distinguishes "no snapshot
621
+ * persisted yet" from "snapshot present" so consumers can render the
622
+ * correct empty state.
623
+ *
624
+ * @throws {@link PlatformRequestError} on non-2xx responses.
625
+ */
626
+ async getThreadState(params: {
627
+ threadId: string;
628
+ }): Promise<ThreadStateResponse> {
629
+ return this.#request<ThreadStateResponse>(
630
+ "GET",
631
+ `/api/_inspect/threads/${encodeURIComponent(params.threadId)}/state`,
632
+ );
633
+ }
634
+
559
635
  /**
560
636
  * Mark a thread as archived.
561
637
  *