@copilotkitnext/runtime 0.0.16 → 0.0.17

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.
package/dist/index.mjs CHANGED
@@ -1,7 +1,7 @@
1
1
  // package.json
2
2
  var package_default = {
3
3
  name: "@copilotkitnext/runtime",
4
- version: "0.0.16",
4
+ version: "0.0.17",
5
5
  description: "Server-side runtime package for CopilotKit2",
6
6
  main: "dist/index.js",
7
7
  types: "dist/index.d.ts",
@@ -60,9 +60,134 @@ var AgentRunner = class {
60
60
  // src/runner/in-memory.ts
61
61
  import { ReplaySubject } from "rxjs";
62
62
  import {
63
- EventType,
63
+ EventType as EventType2,
64
64
  compactEvents
65
65
  } from "@ag-ui/client";
66
+
67
+ // src/runner/finalize-events.ts
68
+ import { randomUUID } from "crypto";
69
+ import {
70
+ EventType
71
+ } from "@ag-ui/client";
72
+ var defaultStopMessage = "Run stopped by user";
73
+ var defaultAbruptEndMessage = "Run ended without emitting a terminal event";
74
+ function finalizeRunEvents(events, options = {}) {
75
+ const { stopRequested = false, interruptionMessage } = options;
76
+ const resolvedStopMessage = interruptionMessage ?? defaultStopMessage;
77
+ const resolvedAbruptMessage = interruptionMessage && interruptionMessage !== defaultStopMessage ? interruptionMessage : defaultAbruptEndMessage;
78
+ const appended = [];
79
+ const openMessageIds = /* @__PURE__ */ new Set();
80
+ const openToolCalls = /* @__PURE__ */ new Map();
81
+ for (const event of events) {
82
+ switch (event.type) {
83
+ case EventType.TEXT_MESSAGE_START: {
84
+ const messageId = event.messageId;
85
+ if (typeof messageId === "string") {
86
+ openMessageIds.add(messageId);
87
+ }
88
+ break;
89
+ }
90
+ case EventType.TEXT_MESSAGE_END: {
91
+ const messageId = event.messageId;
92
+ if (typeof messageId === "string") {
93
+ openMessageIds.delete(messageId);
94
+ }
95
+ break;
96
+ }
97
+ case EventType.TOOL_CALL_START: {
98
+ const toolCallId = event.toolCallId;
99
+ if (typeof toolCallId === "string") {
100
+ openToolCalls.set(toolCallId, {
101
+ hasEnd: false,
102
+ hasResult: false
103
+ });
104
+ }
105
+ break;
106
+ }
107
+ case EventType.TOOL_CALL_END: {
108
+ const toolCallId = event.toolCallId;
109
+ const info = toolCallId ? openToolCalls.get(toolCallId) : void 0;
110
+ if (info) {
111
+ info.hasEnd = true;
112
+ }
113
+ break;
114
+ }
115
+ case EventType.TOOL_CALL_RESULT: {
116
+ const toolCallId = event.toolCallId;
117
+ const info = toolCallId ? openToolCalls.get(toolCallId) : void 0;
118
+ if (info) {
119
+ info.hasResult = true;
120
+ }
121
+ break;
122
+ }
123
+ default:
124
+ break;
125
+ }
126
+ }
127
+ const hasRunFinished = events.some((event) => event.type === EventType.RUN_FINISHED);
128
+ const hasRunError = events.some((event) => event.type === EventType.RUN_ERROR);
129
+ const hasTerminalEvent = hasRunFinished || hasRunError;
130
+ const terminalEventMissing = !hasTerminalEvent;
131
+ for (const messageId of openMessageIds) {
132
+ const endEvent = {
133
+ type: EventType.TEXT_MESSAGE_END,
134
+ messageId
135
+ };
136
+ events.push(endEvent);
137
+ appended.push(endEvent);
138
+ }
139
+ for (const [toolCallId, info] of openToolCalls) {
140
+ if (!info.hasEnd) {
141
+ const endEvent = {
142
+ type: EventType.TOOL_CALL_END,
143
+ toolCallId
144
+ };
145
+ events.push(endEvent);
146
+ appended.push(endEvent);
147
+ }
148
+ if (terminalEventMissing && !info.hasResult) {
149
+ const resultEvent = {
150
+ type: EventType.TOOL_CALL_RESULT,
151
+ toolCallId,
152
+ messageId: `${toolCallId ?? randomUUID()}-result`,
153
+ role: "tool",
154
+ content: JSON.stringify(
155
+ stopRequested ? {
156
+ status: "stopped",
157
+ reason: "stop_requested",
158
+ message: resolvedStopMessage
159
+ } : {
160
+ status: "error",
161
+ reason: "missing_terminal_event",
162
+ message: resolvedAbruptMessage
163
+ }
164
+ )
165
+ };
166
+ events.push(resultEvent);
167
+ appended.push(resultEvent);
168
+ }
169
+ }
170
+ if (terminalEventMissing) {
171
+ if (stopRequested) {
172
+ const finishedEvent = {
173
+ type: EventType.RUN_FINISHED
174
+ };
175
+ events.push(finishedEvent);
176
+ appended.push(finishedEvent);
177
+ } else {
178
+ const errorEvent = {
179
+ type: EventType.RUN_ERROR,
180
+ message: resolvedAbruptMessage,
181
+ code: "INCOMPLETE_STREAM"
182
+ };
183
+ events.push(errorEvent);
184
+ appended.push(errorEvent);
185
+ }
186
+ }
187
+ return appended;
188
+ }
189
+
190
+ // src/runner/in-memory.ts
66
191
  var InMemoryEventStore = class {
67
192
  constructor(threadId) {
68
193
  this.threadId = threadId;
@@ -71,12 +196,18 @@ var InMemoryEventStore = class {
71
196
  subject = null;
72
197
  /** True while a run is actively producing events. */
73
198
  isRunning = false;
74
- /** Lets stop() cancel the current producer. */
75
- abortController = new AbortController();
76
199
  /** Current run ID */
77
200
  currentRunId = null;
78
201
  /** Historic completed runs */
79
202
  historicRuns = [];
203
+ /** Currently running agent instance (if any). */
204
+ agent = null;
205
+ /** Subject returned from run() while the run is active. */
206
+ runSubject = null;
207
+ /** True once stop() has been requested but the run has not yet finalized. */
208
+ stopRequested = false;
209
+ /** Reference to the events emitted in the current run. */
210
+ currentEvents = null;
80
211
  };
81
212
  var GLOBAL_STORE = /* @__PURE__ */ new Map();
82
213
  var InMemoryAgentRunner = class extends AgentRunner {
@@ -92,15 +223,18 @@ var InMemoryAgentRunner = class extends AgentRunner {
92
223
  }
93
224
  store.isRunning = true;
94
225
  store.currentRunId = request.input.runId;
226
+ store.agent = request.agent;
227
+ store.stopRequested = false;
95
228
  const seenMessageIds = /* @__PURE__ */ new Set();
96
229
  const currentRunEvents = [];
230
+ store.currentEvents = currentRunEvents;
97
231
  const historicMessageIds = /* @__PURE__ */ new Set();
98
232
  for (const run of store.historicRuns) {
99
233
  for (const event of run.events) {
100
234
  if ("messageId" in event && typeof event.messageId === "string") {
101
235
  historicMessageIds.add(event.messageId);
102
236
  }
103
- if (event.type === EventType.RUN_STARTED) {
237
+ if (event.type === EventType2.RUN_STARTED) {
104
238
  const runStarted = event;
105
239
  const messages = runStarted.input?.messages ?? [];
106
240
  for (const message of messages) {
@@ -112,8 +246,8 @@ var InMemoryAgentRunner = class extends AgentRunner {
112
246
  const nextSubject = new ReplaySubject(Infinity);
113
247
  const prevSubject = store.subject;
114
248
  store.subject = nextSubject;
115
- store.abortController = new AbortController();
116
249
  const runSubject = new ReplaySubject(Infinity);
250
+ store.runSubject = runSubject;
117
251
  const runAgent = async () => {
118
252
  const lastRun = store.historicRuns[store.historicRuns.length - 1];
119
253
  const parentRunId = lastRun?.runId ?? null;
@@ -121,7 +255,7 @@ var InMemoryAgentRunner = class extends AgentRunner {
121
255
  await request.agent.runAgent(request.input, {
122
256
  onEvent: ({ event }) => {
123
257
  let processedEvent = event;
124
- if (event.type === EventType.RUN_STARTED) {
258
+ if (event.type === EventType2.RUN_STARTED) {
125
259
  const runStartedEvent = event;
126
260
  if (!runStartedEvent.input) {
127
261
  const sanitizedMessages = request.input.messages ? request.input.messages.filter(
@@ -156,6 +290,13 @@ var InMemoryAgentRunner = class extends AgentRunner {
156
290
  }
157
291
  }
158
292
  });
293
+ const appendedEvents = finalizeRunEvents(currentRunEvents, {
294
+ stopRequested: store.stopRequested
295
+ });
296
+ for (const event of appendedEvents) {
297
+ runSubject.next(event);
298
+ nextSubject.next(event);
299
+ }
159
300
  if (store.currentRunId) {
160
301
  const compactedEvents = compactEvents(currentRunEvents);
161
302
  store.historicRuns.push({
@@ -166,11 +307,22 @@ var InMemoryAgentRunner = class extends AgentRunner {
166
307
  createdAt: Date.now()
167
308
  });
168
309
  }
169
- store.isRunning = false;
310
+ store.currentEvents = null;
170
311
  store.currentRunId = null;
312
+ store.agent = null;
313
+ store.runSubject = null;
314
+ store.stopRequested = false;
315
+ store.isRunning = false;
171
316
  runSubject.complete();
172
317
  nextSubject.complete();
173
318
  } catch {
319
+ const appendedEvents = finalizeRunEvents(currentRunEvents, {
320
+ stopRequested: store.stopRequested
321
+ });
322
+ for (const event of appendedEvents) {
323
+ runSubject.next(event);
324
+ nextSubject.next(event);
325
+ }
174
326
  if (store.currentRunId && currentRunEvents.length > 0) {
175
327
  const compactedEvents = compactEvents(currentRunEvents);
176
328
  store.historicRuns.push({
@@ -181,8 +333,12 @@ var InMemoryAgentRunner = class extends AgentRunner {
181
333
  createdAt: Date.now()
182
334
  });
183
335
  }
184
- store.isRunning = false;
336
+ store.currentEvents = null;
185
337
  store.currentRunId = null;
338
+ store.agent = null;
339
+ store.runSubject = null;
340
+ store.stopRequested = false;
341
+ store.isRunning = false;
186
342
  runSubject.complete();
187
343
  nextSubject.complete();
188
344
  }
@@ -217,7 +373,7 @@ var InMemoryAgentRunner = class extends AgentRunner {
217
373
  emittedMessageIds.add(event.messageId);
218
374
  }
219
375
  }
220
- if (store.subject && store.isRunning) {
376
+ if (store.subject && (store.isRunning || store.stopRequested)) {
221
377
  store.subject.subscribe({
222
378
  next: (event) => {
223
379
  if ("messageId" in event && typeof event.messageId === "string" && emittedMessageIds.has(event.messageId)) {
@@ -237,9 +393,31 @@ var InMemoryAgentRunner = class extends AgentRunner {
237
393
  const store = GLOBAL_STORE.get(request.threadId);
238
394
  return Promise.resolve(store?.isRunning ?? false);
239
395
  }
240
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
241
- stop(_request) {
242
- throw new Error("Method not implemented.");
396
+ stop(request) {
397
+ const store = GLOBAL_STORE.get(request.threadId);
398
+ if (!store || !store.isRunning) {
399
+ return Promise.resolve(false);
400
+ }
401
+ if (store.stopRequested) {
402
+ return Promise.resolve(false);
403
+ }
404
+ store.stopRequested = true;
405
+ store.isRunning = false;
406
+ const agent = store.agent;
407
+ if (!agent) {
408
+ store.stopRequested = false;
409
+ store.isRunning = false;
410
+ return Promise.resolve(false);
411
+ }
412
+ try {
413
+ agent.abortRun();
414
+ return Promise.resolve(true);
415
+ } catch (error) {
416
+ console.error("Failed to abort agent run", error);
417
+ store.stopRequested = false;
418
+ store.isRunning = true;
419
+ return Promise.resolve(false);
420
+ }
243
421
  }
244
422
  };
245
423
 
@@ -562,9 +740,6 @@ import { logger as logger2 } from "@copilotkitnext/shared";
562
740
 
563
741
  // src/middleware.ts
564
742
  import { logger } from "@copilotkitnext/shared";
565
- function isMiddlewareURL(value) {
566
- return typeof value === "string" && /^https?:\/\//.test(value);
567
- }
568
743
  async function callBeforeRequestMiddleware({
569
744
  runtime,
570
745
  request,
@@ -575,77 +750,6 @@ async function callBeforeRequestMiddleware({
575
750
  if (typeof mw === "function") {
576
751
  return mw({ runtime, request, path });
577
752
  }
578
- if (isMiddlewareURL(mw)) {
579
- const clone = request.clone();
580
- const url = new URL(request.url);
581
- const headersObj = {};
582
- clone.headers.forEach((v, k) => {
583
- headersObj[k] = v;
584
- });
585
- let bodyJson = void 0;
586
- try {
587
- bodyJson = await clone.json();
588
- } catch {
589
- }
590
- const payload = {
591
- method: request.method,
592
- path: url.pathname,
593
- query: url.search.startsWith("?") ? url.search.slice(1) : url.search,
594
- headers: headersObj,
595
- body: bodyJson
596
- };
597
- const ac = new AbortController();
598
- const to = setTimeout(() => ac.abort(), 2e3);
599
- let res;
600
- try {
601
- res = await fetch(mw, {
602
- method: "POST",
603
- headers: {
604
- "content-type": "application/json",
605
- "X-CopilotKit-Webhook-Stage": "before_request" /* BeforeRequest */
606
- },
607
- body: JSON.stringify(payload),
608
- signal: ac.signal
609
- });
610
- } catch {
611
- clearTimeout(to);
612
- throw new Response(void 0, { status: 502 });
613
- }
614
- clearTimeout(to);
615
- if (res.status >= 500) {
616
- throw new Response(void 0, { status: 502 });
617
- }
618
- if (res.status >= 400) {
619
- const errBody = await res.text();
620
- throw new Response(errBody || null, {
621
- status: res.status,
622
- headers: {
623
- "content-type": res.headers.get("content-type") || "application/json"
624
- }
625
- });
626
- }
627
- if (res.status === 204) return;
628
- let json;
629
- try {
630
- json = await res.json();
631
- } catch {
632
- return;
633
- }
634
- if (json && typeof json === "object") {
635
- const { headers, body } = json;
636
- const init = {
637
- method: request.method
638
- };
639
- if (headers) {
640
- init.headers = headers;
641
- }
642
- if (body !== void 0 && request.method !== "GET" && request.method !== "HEAD") {
643
- init.body = JSON.stringify(body);
644
- }
645
- return new Request(request.url, init);
646
- }
647
- return;
648
- }
649
753
  logger.warn({ mw }, "Unsupported beforeRequestMiddleware value \u2013 skipped");
650
754
  return;
651
755
  }
@@ -659,45 +763,6 @@ async function callAfterRequestMiddleware({
659
763
  if (typeof mw === "function") {
660
764
  return mw({ runtime, response, path });
661
765
  }
662
- if (isMiddlewareURL(mw)) {
663
- const clone = response.clone();
664
- const headersObj = {};
665
- clone.headers.forEach((v, k) => {
666
- headersObj[k] = v;
667
- });
668
- let body = "";
669
- try {
670
- body = await clone.text();
671
- } catch {
672
- }
673
- const payload = {
674
- status: clone.status,
675
- headers: headersObj,
676
- body
677
- };
678
- const ac = new AbortController();
679
- const to = setTimeout(() => ac.abort(), 2e3);
680
- let res;
681
- try {
682
- res = await fetch(mw, {
683
- method: "POST",
684
- headers: {
685
- "content-type": "application/json",
686
- "X-CopilotKit-Webhook-Stage": "after_request" /* AfterRequest */
687
- },
688
- body: JSON.stringify(payload),
689
- signal: ac.signal
690
- });
691
- } finally {
692
- clearTimeout(to);
693
- }
694
- if (!res.ok) {
695
- throw new Error(
696
- `after_request webhook ${mw} responded with ${res.status}`
697
- );
698
- }
699
- return;
700
- }
701
766
  logger.warn({ mw }, "Unsupported afterRequestMiddleware value \u2013 skipped");
702
767
  }
703
768
 
@@ -825,11 +890,72 @@ async function handleConnectAgent({
825
890
  }
826
891
  }
827
892
 
828
- // src/endpoint.ts
829
- function createCopilotEndpoint({
893
+ // src/handlers/handle-stop.ts
894
+ import { EventType as EventType3 } from "@ag-ui/client";
895
+ async function handleStopAgent({
830
896
  runtime,
831
- basePath
897
+ request,
898
+ agentId,
899
+ threadId
832
900
  }) {
901
+ try {
902
+ const agents = await runtime.agents;
903
+ if (!agents[agentId]) {
904
+ return new Response(
905
+ JSON.stringify({
906
+ error: "Agent not found",
907
+ message: `Agent '${agentId}' does not exist`
908
+ }),
909
+ {
910
+ status: 404,
911
+ headers: { "Content-Type": "application/json" }
912
+ }
913
+ );
914
+ }
915
+ const stopped = await runtime.runner.stop({ threadId });
916
+ if (!stopped) {
917
+ return new Response(
918
+ JSON.stringify({
919
+ stopped: false,
920
+ message: `No active run for thread '${threadId}'.`
921
+ }),
922
+ {
923
+ status: 200,
924
+ headers: { "Content-Type": "application/json" }
925
+ }
926
+ );
927
+ }
928
+ return new Response(
929
+ JSON.stringify({
930
+ stopped: true,
931
+ interrupt: {
932
+ type: EventType3.RUN_ERROR,
933
+ message: "Run stopped by user",
934
+ code: "STOPPED"
935
+ }
936
+ }),
937
+ {
938
+ status: 200,
939
+ headers: { "Content-Type": "application/json" }
940
+ }
941
+ );
942
+ } catch (error) {
943
+ console.error("Error stopping agent run:", error);
944
+ return new Response(
945
+ JSON.stringify({
946
+ error: "Failed to stop agent",
947
+ message: error instanceof Error ? error.message : "Unknown error"
948
+ }),
949
+ {
950
+ status: 500,
951
+ headers: { "Content-Type": "application/json" }
952
+ }
953
+ );
954
+ }
955
+ }
956
+
957
+ // src/endpoint.ts
958
+ function createCopilotEndpoint({ runtime, basePath }) {
833
959
  const app = new Hono();
834
960
  return app.basePath(basePath).use(
835
961
  "*",
@@ -851,10 +977,7 @@ function createCopilotEndpoint({
851
977
  c.set("modifiedRequest", maybeModifiedRequest);
852
978
  }
853
979
  } catch (error) {
854
- logger2.error(
855
- { err: error, url: request.url, path },
856
- "Error running before request middleware"
857
- );
980
+ logger2.error({ err: error, url: request.url, path }, "Error running before request middleware");
858
981
  if (error instanceof Response) {
859
982
  return error;
860
983
  }
@@ -870,10 +993,7 @@ function createCopilotEndpoint({
870
993
  response,
871
994
  path
872
995
  }).catch((error) => {
873
- logger2.error(
874
- { err: error, url: c.req.url, path },
875
- "Error running after request middleware"
876
- );
996
+ logger2.error({ err: error, url: c.req.url, path }, "Error running after request middleware");
877
997
  });
878
998
  }).post("/agent/:agentId/run", async (c) => {
879
999
  const agentId = c.req.param("agentId");
@@ -885,10 +1005,7 @@ function createCopilotEndpoint({
885
1005
  agentId
886
1006
  });
887
1007
  } catch (error) {
888
- logger2.error(
889
- { err: error, url: request.url, path: c.req.path },
890
- "Error running request handler"
891
- );
1008
+ logger2.error({ err: error, url: request.url, path: c.req.path }, "Error running request handler");
892
1009
  throw error;
893
1010
  }
894
1011
  }).post("/agent/:agentId/connect", async (c) => {
@@ -901,10 +1018,22 @@ function createCopilotEndpoint({
901
1018
  agentId
902
1019
  });
903
1020
  } catch (error) {
904
- logger2.error(
905
- { err: error, url: request.url, path: c.req.path },
906
- "Error running request handler"
907
- );
1021
+ logger2.error({ err: error, url: request.url, path: c.req.path }, "Error running request handler");
1022
+ throw error;
1023
+ }
1024
+ }).post("/agent/:agentId/stop/:threadId", async (c) => {
1025
+ const agentId = c.req.param("agentId");
1026
+ const threadId = c.req.param("threadId");
1027
+ const request = c.get("modifiedRequest") || c.req.raw;
1028
+ try {
1029
+ return await handleStopAgent({
1030
+ runtime,
1031
+ request,
1032
+ agentId,
1033
+ threadId
1034
+ });
1035
+ } catch (error) {
1036
+ logger2.error({ err: error, url: request.url, path: c.req.path }, "Error running request handler");
908
1037
  throw error;
909
1038
  }
910
1039
  }).get("/info", async (c) => {
@@ -915,10 +1044,7 @@ function createCopilotEndpoint({
915
1044
  request
916
1045
  });
917
1046
  } catch (error) {
918
- logger2.error(
919
- { err: error, url: request.url, path: c.req.path },
920
- "Error running request handler"
921
- );
1047
+ logger2.error({ err: error, url: request.url, path: c.req.path }, "Error running request handler");
922
1048
  throw error;
923
1049
  }
924
1050
  }).post("/transcribe", async (c) => {
@@ -929,10 +1055,7 @@ function createCopilotEndpoint({
929
1055
  request
930
1056
  });
931
1057
  } catch (error) {
932
- logger2.error(
933
- { err: error, url: request.url, path: c.req.path },
934
- "Error running request handler"
935
- );
1058
+ logger2.error({ err: error, url: request.url, path: c.req.path }, "Error running request handler");
936
1059
  throw error;
937
1060
  }
938
1061
  }).notFound((c) => {
@@ -944,6 +1067,7 @@ export {
944
1067
  CopilotRuntime,
945
1068
  InMemoryAgentRunner,
946
1069
  VERSION,
947
- createCopilotEndpoint
1070
+ createCopilotEndpoint,
1071
+ finalizeRunEvents
948
1072
  };
949
1073
  //# sourceMappingURL=index.mjs.map