@copilotkit/runtime 1.50.1 → 1.50.2-next.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.
package/package.json CHANGED
@@ -9,7 +9,7 @@
9
9
  "publishConfig": {
10
10
  "access": "public"
11
11
  },
12
- "version": "1.50.1",
12
+ "version": "1.50.2-next.0",
13
13
  "sideEffects": false,
14
14
  "main": "./dist/index.js",
15
15
  "module": "./dist/index.mjs",
@@ -72,7 +72,7 @@
72
72
  "rxjs": "7.8.1",
73
73
  "type-graphql": "2.0.0-rc.1",
74
74
  "zod": "^3.23.3",
75
- "@copilotkit/shared": "1.50.1"
75
+ "@copilotkit/shared": "1.50.2-next.0"
76
76
  },
77
77
  "peerDependencies": {
78
78
  "@anthropic-ai/sdk": "^0.57.0",
@@ -1,98 +1,17 @@
1
1
  import { CreateCopilotRuntimeServerOptions, getCommonConfig } from "../shared";
2
2
  import telemetry, { getRuntimeInstanceTelemetryInfo } from "../../telemetry-client";
3
3
  import { createCopilotEndpointSingleRoute } from "@copilotkitnext/runtime";
4
- import { IncomingMessage, ServerResponse } from "http";
5
- import { Readable } from "node:stream";
6
-
7
- type IncomingWithBody = IncomingMessage & { body?: unknown; complete?: boolean };
8
-
9
- export function readableStreamToNodeStream(webStream: ReadableStream): Readable {
10
- const reader = webStream.getReader();
11
-
12
- return new Readable({
13
- async read() {
14
- try {
15
- const { done, value } = await reader.read();
16
- if (done) {
17
- this.push(null);
18
- } else {
19
- this.push(Buffer.from(value));
20
- }
21
- } catch (err) {
22
- this.destroy(err as Error);
23
- }
24
- },
25
- });
26
- }
27
-
28
- function getFullUrl(req: IncomingMessage): string {
29
- const expressPath =
30
- (req as any).originalUrl ??
31
- ((req as any).baseUrl ? `${(req as any).baseUrl}${req.url ?? ""}` : undefined);
32
- const path = expressPath || req.url || "/";
33
- const host =
34
- (req.headers["x-forwarded-host"] as string) || (req.headers.host as string) || "localhost";
35
- const proto =
36
- (req.headers["x-forwarded-proto"] as string) ||
37
- ((req.socket as any).encrypted ? "https" : "http");
38
-
39
- return `${proto}://${host}${path}`;
40
- }
41
-
42
- function toHeaders(rawHeaders: IncomingMessage["headers"]): Headers {
43
- const headers = new Headers();
44
-
45
- for (const [key, value] of Object.entries(rawHeaders)) {
46
- if (value === undefined) continue;
47
-
48
- if (Array.isArray(value)) {
49
- value.forEach((entry) => headers.append(key, entry));
50
- continue;
51
- }
52
-
53
- headers.append(key, value);
54
- }
55
-
56
- return headers;
57
- }
58
-
59
- function isStreamConsumed(req: IncomingWithBody): boolean {
60
- const readableState = (req as any)._readableState;
61
-
62
- return Boolean(
63
- req.readableEnded || req.complete || readableState?.ended || readableState?.endEmitted,
64
- );
65
- }
66
-
67
- function synthesizeBodyFromParsedBody(
68
- parsedBody: unknown,
69
- headers: Headers,
70
- ): { body: BodyInit | null; contentType?: string } {
71
- if (parsedBody === null || parsedBody === undefined) {
72
- return { body: null };
73
- }
74
-
75
- if (parsedBody instanceof Buffer || parsedBody instanceof Uint8Array) {
76
- return { body: parsedBody };
77
- }
78
-
79
- if (typeof parsedBody === "string") {
80
- return { body: parsedBody, contentType: headers.get("content-type") ?? "text/plain" };
81
- }
82
-
83
- return {
84
- body: JSON.stringify(parsedBody),
85
- contentType: "application/json",
86
- };
87
- }
88
-
89
- function isDisturbedOrLockedError(error: unknown): boolean {
90
- return (
91
- error instanceof TypeError &&
92
- typeof error.message === "string" &&
93
- (error.message.includes("disturbed") || error.message.includes("locked"))
94
- );
95
- }
4
+ import type { IncomingMessage, ServerResponse } from "node:http";
5
+ import {
6
+ getFullUrl,
7
+ IncomingWithBody,
8
+ isDisturbedOrLockedError,
9
+ isStreamConsumed,
10
+ nodeStreamToReadableStream,
11
+ readableStreamToNodeStream,
12
+ synthesizeBodyFromParsedBody,
13
+ toHeaders,
14
+ } from "./request-handler";
96
15
 
97
16
  export function copilotRuntimeNodeHttpEndpoint(options: CreateCopilotRuntimeServerOptions) {
98
17
  const commonConfig = getCommonConfig(options);
@@ -124,7 +43,7 @@ export function copilotRuntimeNodeHttpEndpoint(options: CreateCopilotRuntimeServ
124
43
  basePath: options.baseUrl ?? options.endpoint,
125
44
  });
126
45
 
127
- return async function handler(req: IncomingWithBody, res: ServerResponse) {
46
+ const handle = async function handler(req: IncomingWithBody, res: ServerResponse) {
128
47
  const url = getFullUrl(req);
129
48
  const hasBody = req.method !== "GET" && req.method !== "HEAD";
130
49
 
@@ -138,7 +57,7 @@ export function copilotRuntimeNodeHttpEndpoint(options: CreateCopilotRuntimeServ
138
57
  let useDuplex = false;
139
58
 
140
59
  if (hasBody && canStream) {
141
- requestBody = req as unknown as BodyInit;
60
+ requestBody = nodeStreamToReadableStream(req);
142
61
  useDuplex = true;
143
62
  }
144
63
 
@@ -208,4 +127,17 @@ export function copilotRuntimeNodeHttpEndpoint(options: CreateCopilotRuntimeServ
208
127
  res.end();
209
128
  }
210
129
  };
130
+
131
+ return function (
132
+ reqOrRequest: IncomingMessage | Request,
133
+ res?: ServerResponse,
134
+ ): Promise<void> | Promise<Response> | Response {
135
+ if (reqOrRequest instanceof Request) {
136
+ return honoApp.fetch(reqOrRequest as Request);
137
+ }
138
+ if (!res) {
139
+ throw new TypeError("ServerResponse is required for Node HTTP requests");
140
+ }
141
+ return handle(reqOrRequest as IncomingMessage, res);
142
+ };
211
143
  }
@@ -0,0 +1,111 @@
1
+ import type { IncomingMessage } from "http";
2
+ import { Readable } from "node:stream";
3
+
4
+ export type IncomingWithBody = IncomingMessage & { body?: unknown; complete?: boolean };
5
+
6
+ export function readableStreamToNodeStream(webStream: ReadableStream): Readable {
7
+ const reader = webStream.getReader();
8
+
9
+ return new Readable({
10
+ async read() {
11
+ try {
12
+ const { done, value } = await reader.read();
13
+ if (done) {
14
+ this.push(null);
15
+ } else {
16
+ this.push(Buffer.from(value));
17
+ }
18
+ } catch (err) {
19
+ this.destroy(err as Error);
20
+ }
21
+ },
22
+ });
23
+ }
24
+
25
+ export function nodeStreamToReadableStream(nodeStream: Readable): ReadableStream<Uint8Array> {
26
+ return new ReadableStream({
27
+ start(controller) {
28
+ nodeStream.on("data", (chunk) => {
29
+ controller.enqueue(chunk instanceof Buffer ? new Uint8Array(chunk) : chunk);
30
+ });
31
+ nodeStream.on("end", () => {
32
+ controller.close();
33
+ });
34
+ nodeStream.on("error", (err) => {
35
+ controller.error(err);
36
+ });
37
+ },
38
+ cancel() {
39
+ nodeStream.destroy();
40
+ },
41
+ });
42
+ }
43
+
44
+ export function getFullUrl(req: IncomingMessage): string {
45
+ // Use req.url (path relative to mount point) for Hono routing to work correctly.
46
+ // Express sets req.url to the path after the mount point (e.g., "/" when mounted at "/copilotkit").
47
+ // Pure Node HTTP sets req.url to the full path.
48
+ const path = req.url || "/";
49
+ const host =
50
+ (req.headers["x-forwarded-host"] as string) || (req.headers.host as string) || "localhost";
51
+ const proto =
52
+ (req.headers["x-forwarded-proto"] as string) ||
53
+ ((req.socket as any).encrypted ? "https" : "http");
54
+
55
+ return `${proto}://${host}${path}`;
56
+ }
57
+
58
+ export function toHeaders(rawHeaders: IncomingMessage["headers"]): Headers {
59
+ const headers = new Headers();
60
+
61
+ for (const [key, value] of Object.entries(rawHeaders)) {
62
+ if (value === undefined) continue;
63
+
64
+ if (Array.isArray(value)) {
65
+ value.forEach((entry) => headers.append(key, entry));
66
+ continue;
67
+ }
68
+
69
+ headers.append(key, value);
70
+ }
71
+
72
+ return headers;
73
+ }
74
+
75
+ export function isStreamConsumed(req: IncomingWithBody): boolean {
76
+ const readableState = (req as any)._readableState;
77
+
78
+ return Boolean(
79
+ req.readableEnded || req.complete || readableState?.ended || readableState?.endEmitted,
80
+ );
81
+ }
82
+
83
+ export function synthesizeBodyFromParsedBody(
84
+ parsedBody: unknown,
85
+ headers: Headers,
86
+ ): { body: BodyInit | null; contentType?: string } {
87
+ if (parsedBody === null || parsedBody === undefined) {
88
+ return { body: null };
89
+ }
90
+
91
+ if (parsedBody instanceof Buffer || parsedBody instanceof Uint8Array) {
92
+ return { body: parsedBody };
93
+ }
94
+
95
+ if (typeof parsedBody === "string") {
96
+ return { body: parsedBody, contentType: headers.get("content-type") ?? "text/plain" };
97
+ }
98
+
99
+ return {
100
+ body: JSON.stringify(parsedBody),
101
+ contentType: "application/json",
102
+ };
103
+ }
104
+
105
+ export function isDisturbedOrLockedError(error: unknown): boolean {
106
+ return (
107
+ error instanceof TypeError &&
108
+ typeof error.message === "string" &&
109
+ (error.message.includes("disturbed") || error.message.includes("locked"))
110
+ );
111
+ }