@copilotkitnext/runtime 0.0.1

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/.cursor/rules/runtime.always.mdc +9 -0
  2. package/.turbo/turbo-build.log +22 -0
  3. package/.turbo/turbo-check-types.log +4 -0
  4. package/.turbo/turbo-lint.log +56 -0
  5. package/.turbo/turbo-test$colon$coverage.log +149 -0
  6. package/.turbo/turbo-test.log +107 -0
  7. package/LICENSE +11 -0
  8. package/README-RUNNERS.md +78 -0
  9. package/dist/index.d.mts +245 -0
  10. package/dist/index.d.ts +245 -0
  11. package/dist/index.js +1873 -0
  12. package/dist/index.js.map +1 -0
  13. package/dist/index.mjs +1841 -0
  14. package/dist/index.mjs.map +1 -0
  15. package/eslint.config.mjs +3 -0
  16. package/package.json +62 -0
  17. package/src/__tests__/get-runtime-info.test.ts +117 -0
  18. package/src/__tests__/handle-run.test.ts +69 -0
  19. package/src/__tests__/handle-transcribe.test.ts +289 -0
  20. package/src/__tests__/in-process-agent-runner-messages.test.ts +599 -0
  21. package/src/__tests__/in-process-agent-runner.test.ts +726 -0
  22. package/src/__tests__/middleware.test.ts +432 -0
  23. package/src/__tests__/routing.test.ts +257 -0
  24. package/src/endpoint.ts +150 -0
  25. package/src/handler.ts +3 -0
  26. package/src/handlers/get-runtime-info.ts +50 -0
  27. package/src/handlers/handle-connect.ts +144 -0
  28. package/src/handlers/handle-run.ts +156 -0
  29. package/src/handlers/handle-transcribe.ts +126 -0
  30. package/src/index.ts +8 -0
  31. package/src/middleware.ts +232 -0
  32. package/src/runner/__tests__/enterprise-runner.test.ts +992 -0
  33. package/src/runner/__tests__/event-compaction.test.ts +253 -0
  34. package/src/runner/__tests__/in-memory-runner.test.ts +483 -0
  35. package/src/runner/__tests__/sqlite-runner.test.ts +975 -0
  36. package/src/runner/agent-runner.ts +27 -0
  37. package/src/runner/enterprise.ts +653 -0
  38. package/src/runner/event-compaction.ts +250 -0
  39. package/src/runner/in-memory.ts +322 -0
  40. package/src/runner/index.ts +0 -0
  41. package/src/runner/sqlite.ts +481 -0
  42. package/src/runtime.ts +53 -0
  43. package/src/transcription-service/transcription-service-openai.ts +29 -0
  44. package/src/transcription-service/transcription-service.ts +11 -0
  45. package/tsconfig.json +13 -0
  46. package/tsup.config.ts +11 -0
  47. package/vitest.config.mjs +15 -0
@@ -0,0 +1,432 @@
1
+ import { vi, type MockedFunction } from "vitest";
2
+ import { createCopilotEndpoint } from "../endpoint";
3
+ import { CopilotRuntime } from "../runtime";
4
+ import { logger } from "@copilotkitnext/shared";
5
+ import type { AbstractAgent } from "@ag-ui/client";
6
+ import { WebhookStage } from "../middleware";
7
+ import { afterEach, describe, expect, it } from "vitest";
8
+
9
+ const dummyRuntime = (opts: Partial<CopilotRuntime> = {}) => {
10
+ const runtime = new CopilotRuntime({
11
+ agents: { agent: {} as unknown as AbstractAgent },
12
+ ...opts,
13
+ });
14
+ return runtime;
15
+ };
16
+
17
+ describe("CopilotEndpoint middleware", () => {
18
+ afterEach(() => {
19
+ vi.restoreAllMocks();
20
+ // restore global fetch if it was mocked
21
+ if (fetchMock) {
22
+ global.fetch = originalFetch;
23
+ }
24
+ });
25
+
26
+ let originalFetch: typeof fetch;
27
+ let fetchMock: MockedFunction<typeof fetch> | null = null;
28
+
29
+ const setupFetchMock = (beforeUrl: string, afterUrl: string) => {
30
+ originalFetch = global.fetch;
31
+ fetchMock = vi.fn().mockImplementation(async (url: string) => {
32
+ if (url === beforeUrl) {
33
+ const body = {
34
+ headers: { "x-modified": "yes" },
35
+ body: { foo: "bar" },
36
+ };
37
+ return new Response(JSON.stringify(body), {
38
+ status: 200,
39
+ headers: { "content-type": "application/json" },
40
+ });
41
+ }
42
+ if (url === afterUrl) {
43
+ return new Response(null, { status: 204 });
44
+ }
45
+ throw new Error(`Unexpected fetch URL: ${url}`);
46
+ });
47
+ // Override global fetch for the duration of this test
48
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
49
+ // @ts-ignore
50
+ global.fetch = fetchMock as unknown as typeof fetch;
51
+ };
52
+
53
+ it("processes request through middleware and handler", async () => {
54
+ const originalRequest = new Request("https://example.com/info");
55
+ const modifiedRequest = new Request("https://example.com/info", {
56
+ headers: { "x-modified": "yes" },
57
+ });
58
+
59
+ const before = vi.fn().mockResolvedValue(modifiedRequest);
60
+ const after = vi.fn().mockResolvedValue(undefined);
61
+
62
+ const runtime = dummyRuntime({
63
+ beforeRequestMiddleware: before,
64
+ afterRequestMiddleware: after,
65
+ });
66
+
67
+ const endpoint = createCopilotEndpoint({ runtime, basePath: "/" });
68
+ const response = await endpoint.fetch(originalRequest);
69
+
70
+ expect(before).toHaveBeenCalledWith({
71
+ runtime,
72
+ request: originalRequest,
73
+ path: expect.any(String),
74
+ });
75
+ expect(after).toHaveBeenCalledWith({
76
+ runtime,
77
+ response,
78
+ path: expect.any(String),
79
+ });
80
+ // The response should contain version info from the /info endpoint
81
+ const body = await response.json();
82
+ expect(body).toHaveProperty("version");
83
+ });
84
+
85
+ it("logs and returns Response error from beforeRequestMiddleware", async () => {
86
+ const errorResponse = new Response("Error", { status: 400 });
87
+ const before = vi.fn().mockRejectedValue(errorResponse);
88
+ const after = vi.fn();
89
+ const runtime = dummyRuntime({
90
+ beforeRequestMiddleware: before,
91
+ afterRequestMiddleware: after,
92
+ });
93
+ const logSpy = vi
94
+ .spyOn(logger, "error")
95
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
96
+ .mockImplementation(() => undefined as any);
97
+
98
+ const endpoint = createCopilotEndpoint({ runtime, basePath: "/" });
99
+ const response = await endpoint.fetch(
100
+ new Request("https://example.com/info")
101
+ );
102
+
103
+ expect(response.status).toBe(400);
104
+ expect(logSpy).toHaveBeenCalledWith(
105
+ expect.objectContaining({
106
+ err: errorResponse,
107
+ url: "https://example.com/info",
108
+ path: expect.any(String),
109
+ }),
110
+ "Error running before request middleware"
111
+ );
112
+ expect(after).not.toHaveBeenCalled();
113
+ });
114
+
115
+ it("logs and returns 500 error from beforeRequestMiddleware", async () => {
116
+ const error = new Error("before");
117
+ const before = vi.fn().mockRejectedValue(error);
118
+ const after = vi.fn();
119
+ const runtime = dummyRuntime({
120
+ beforeRequestMiddleware: before,
121
+ afterRequestMiddleware: after,
122
+ });
123
+ const logSpy = vi
124
+ .spyOn(logger, "error")
125
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
126
+ .mockImplementation(() => undefined as any);
127
+
128
+ const endpoint = createCopilotEndpoint({ runtime, basePath: "/" });
129
+
130
+ const response = await endpoint.fetch(
131
+ new Request("https://example.com/info")
132
+ );
133
+
134
+ // Hono catches errors and returns them as 500 responses
135
+ expect(response.status).toBe(500);
136
+
137
+ expect(logSpy).toHaveBeenCalledWith(
138
+ expect.objectContaining({
139
+ err: error,
140
+ url: "https://example.com/info",
141
+ path: expect.any(String),
142
+ }),
143
+ "Error running before request middleware"
144
+ );
145
+ expect(after).not.toHaveBeenCalled();
146
+ });
147
+
148
+ it("logs error from handler", async () => {
149
+ // Create a mock agent that throws an error
150
+ const before = vi.fn();
151
+ const after = vi.fn();
152
+ const errorAgent = {
153
+ clone: () => {
154
+ throw new Error("Agent error");
155
+ },
156
+ } as unknown as AbstractAgent;
157
+
158
+ const runtime = dummyRuntime({
159
+ beforeRequestMiddleware: before,
160
+ afterRequestMiddleware: after,
161
+ agents: { errorAgent },
162
+ });
163
+ const logSpy = vi
164
+ .spyOn(logger, "error")
165
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
166
+ .mockImplementation(() => undefined as any);
167
+
168
+ const endpoint = createCopilotEndpoint({ runtime, basePath: "/" });
169
+
170
+ const response = await endpoint.fetch(
171
+ new Request("https://example.com/agent/errorAgent/run", {
172
+ method: "POST",
173
+ })
174
+ );
175
+
176
+ // Hono catches errors and returns them as 500 responses
177
+ expect(response.status).toBe(500);
178
+
179
+ // The actual handler logs the error, not the middleware
180
+ expect(logSpy).toHaveBeenCalled();
181
+ // After middleware is called even on error
182
+ await new Promise((r) => setTimeout(r, 50));
183
+ expect(after).toHaveBeenCalled();
184
+ });
185
+
186
+ it("logs but does not rethrow error from afterRequestMiddleware", async () => {
187
+ const error = new Error("after");
188
+ const before = vi.fn();
189
+ const after = vi.fn().mockRejectedValue(error);
190
+ const runtime = dummyRuntime({
191
+ beforeRequestMiddleware: before,
192
+ afterRequestMiddleware: after,
193
+ });
194
+ const logSpy = vi
195
+ .spyOn(logger, "error")
196
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
197
+ .mockImplementation(() => undefined as any);
198
+
199
+ const endpoint = createCopilotEndpoint({ runtime, basePath: "/" });
200
+ const response = await endpoint.fetch(
201
+ new Request("https://example.com/info")
202
+ );
203
+
204
+ await new Promise((r) => setImmediate(r));
205
+
206
+ expect(response).toBeInstanceOf(Response);
207
+ expect(after).toHaveBeenCalledWith({
208
+ runtime,
209
+ response,
210
+ path: expect.any(String),
211
+ });
212
+
213
+ await new Promise((r) => setImmediate(r));
214
+
215
+ expect(logSpy).toHaveBeenCalledWith(
216
+ expect.objectContaining({
217
+ err: error,
218
+ url: "https://example.com/info",
219
+ }),
220
+ "Error running after request middleware"
221
+ );
222
+ });
223
+
224
+ it("processes request through webhook middleware URLs", async () => {
225
+ const beforeURL = "https://hooks.example.com/before";
226
+ const afterURL = "https://hooks.example.com/after";
227
+ setupFetchMock(beforeURL, afterURL);
228
+
229
+ const runtime = dummyRuntime({
230
+ beforeRequestMiddleware: beforeURL,
231
+ afterRequestMiddleware: afterURL,
232
+ });
233
+
234
+ const endpoint = createCopilotEndpoint({ runtime, basePath: "/" });
235
+ const response = await endpoint.fetch(
236
+ new Request("https://example.com/info", {
237
+ headers: { foo: "bar" },
238
+ method: "GET",
239
+ })
240
+ );
241
+
242
+ // Wait a bit more for async afterRequestMiddleware
243
+ await new Promise((r) => setTimeout(r, 50));
244
+
245
+ expect(fetchMock).toHaveBeenCalledTimes(2);
246
+
247
+ // Assert payload for before-hook
248
+ const beforeCall = fetchMock!.mock.calls[0];
249
+ expect(beforeCall[0]).toBe(beforeURL);
250
+ expect(beforeCall[1]).toBeDefined();
251
+ expect(beforeCall[1]!.body).toBeDefined();
252
+ const beforePayload = JSON.parse(beforeCall[1]!.body as string);
253
+ expect(beforePayload).toMatchObject({
254
+ method: "GET",
255
+ path: "/info",
256
+ query: "",
257
+ headers: expect.objectContaining({ foo: "bar" }),
258
+ });
259
+ const headers = beforeCall[1]!.headers as Record<string, string>;
260
+ expect(headers["X-CopilotKit-Webhook-Stage"]).toBe(
261
+ WebhookStage.BeforeRequest
262
+ );
263
+
264
+ // Assert payload for after-hook
265
+ const afterCall = fetchMock!.mock.calls[1];
266
+ expect(afterCall[0]).toBe(afterURL);
267
+ expect(afterCall[1]).toBeDefined();
268
+ expect(afterCall[1]!.body).toBeDefined();
269
+ const afterPayload = JSON.parse(afterCall[1]!.body as string);
270
+ expect(afterPayload).toMatchObject({
271
+ status: 200,
272
+ headers: expect.objectContaining({
273
+ "content-type": "application/json",
274
+ }),
275
+ body: expect.any(String),
276
+ });
277
+ const afterHeaders = afterCall[1]!.headers as Record<string, string>;
278
+ expect(afterHeaders["X-CopilotKit-Webhook-Stage"]).toBe(
279
+ WebhookStage.AfterRequest
280
+ );
281
+
282
+ // Response should still be successful
283
+ expect(response.status).toBe(200);
284
+ const body = await response.json();
285
+ expect(body).toHaveProperty("version");
286
+ });
287
+
288
+ it("applies webhook middleware request modifications", async () => {
289
+ const beforeURL = "https://hooks.example.com/before";
290
+ const afterURL = "https://hooks.example.com/after";
291
+ setupFetchMock(beforeURL, afterURL);
292
+
293
+ const runtime = dummyRuntime({
294
+ beforeRequestMiddleware: beforeURL,
295
+ afterRequestMiddleware: afterURL,
296
+ });
297
+
298
+ const endpoint = createCopilotEndpoint({ runtime, basePath: "/" });
299
+
300
+ // Make a POST request to info endpoint since it's simpler
301
+ const response = await endpoint.fetch(
302
+ new Request("https://example.com/info", {
303
+ headers: { foo: "bar" },
304
+ method: "GET",
305
+ })
306
+ );
307
+
308
+ // Should get a successful response
309
+ expect(response.status).toBe(200);
310
+
311
+ // Wait for async afterRequestMiddleware
312
+ await new Promise((r) => setTimeout(r, 100));
313
+
314
+ // The webhook middleware should have been called
315
+ expect(fetchMock).toHaveBeenCalledTimes(2);
316
+ });
317
+
318
+ it("handles webhook middleware timeout", async () => {
319
+ const beforeURL = "https://hooks.example.com/before";
320
+ originalFetch = global.fetch;
321
+
322
+ // Create an AbortController to simulate timeout
323
+ let abortSignal: AbortSignal | undefined;
324
+ fetchMock = vi
325
+ .fn()
326
+ .mockImplementation(async (_url: string, init?: RequestInit) => {
327
+ abortSignal = init?.signal;
328
+ // Wait for abort signal
329
+ return new Promise<Response>((_resolve, reject) => {
330
+ if (abortSignal) {
331
+ abortSignal.addEventListener("abort", () => {
332
+ reject(new Error("Aborted"));
333
+ });
334
+ }
335
+ });
336
+ });
337
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
338
+ // @ts-ignore
339
+ global.fetch = fetchMock as unknown as typeof fetch;
340
+
341
+ const runtime = dummyRuntime({
342
+ beforeRequestMiddleware: beforeURL,
343
+ });
344
+
345
+ const endpoint = createCopilotEndpoint({ runtime, basePath: "/" });
346
+
347
+ // Should return 502 on timeout
348
+ const response = await endpoint.fetch(
349
+ new Request("https://example.com/info")
350
+ );
351
+
352
+ expect(response.status).toBe(502);
353
+
354
+ // Verify that the fetch was aborted due to timeout
355
+ expect(abortSignal?.aborted).toBe(true);
356
+ });
357
+
358
+ it("handles webhook middleware error responses", async () => {
359
+ const beforeURL = "https://hooks.example.com/before";
360
+ originalFetch = global.fetch;
361
+ fetchMock = vi.fn().mockImplementation(async () => {
362
+ return new Response("Bad request", { status: 400 });
363
+ });
364
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
365
+ // @ts-ignore
366
+ global.fetch = fetchMock as unknown as typeof fetch;
367
+
368
+ const runtime = dummyRuntime({
369
+ beforeRequestMiddleware: beforeURL,
370
+ });
371
+
372
+ const endpoint = createCopilotEndpoint({ runtime, basePath: "/" });
373
+
374
+ // Should pass through error response
375
+ const response = await endpoint.fetch(
376
+ new Request("https://example.com/info")
377
+ );
378
+
379
+ expect(response.status).toBe(400);
380
+ expect(await response.text()).toBe("Bad request");
381
+ });
382
+
383
+ it("handles webhook middleware server error", async () => {
384
+ const beforeURL = "https://hooks.example.com/before";
385
+ originalFetch = global.fetch;
386
+ fetchMock = vi.fn().mockImplementation(async () => {
387
+ return new Response("Server error", { status: 500 });
388
+ });
389
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
390
+ // @ts-ignore
391
+ global.fetch = fetchMock as unknown as typeof fetch;
392
+
393
+ const runtime = dummyRuntime({
394
+ beforeRequestMiddleware: beforeURL,
395
+ });
396
+
397
+ const endpoint = createCopilotEndpoint({ runtime, basePath: "/" });
398
+
399
+ // Should return 502 on server error
400
+ const response = await endpoint.fetch(
401
+ new Request("https://example.com/info")
402
+ );
403
+
404
+ expect(response.status).toBe(502);
405
+ });
406
+
407
+ it("handles webhook middleware 204 response", async () => {
408
+ const beforeURL = "https://hooks.example.com/before";
409
+ originalFetch = global.fetch;
410
+ fetchMock = vi.fn().mockImplementation(async () => {
411
+ return new Response(null, { status: 204 });
412
+ });
413
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
414
+ // @ts-ignore
415
+ global.fetch = fetchMock as unknown as typeof fetch;
416
+
417
+ const runtime = dummyRuntime({
418
+ beforeRequestMiddleware: beforeURL,
419
+ });
420
+
421
+ const endpoint = createCopilotEndpoint({ runtime, basePath: "/" });
422
+
423
+ // Should continue with original request on 204
424
+ const response = await endpoint.fetch(
425
+ new Request("https://example.com/info")
426
+ );
427
+
428
+ expect(response.status).toBe(200);
429
+ const body = await response.json();
430
+ expect(body).toHaveProperty("version");
431
+ });
432
+ });
@@ -0,0 +1,257 @@
1
+ import { createCopilotEndpoint } from "../endpoint";
2
+ import { CopilotRuntime } from "../runtime";
3
+ import { describe, it, expect } from "vitest";
4
+ import type { AbstractAgent } from "@ag-ui/client";
5
+
6
+ describe("CopilotEndpoint routing", () => {
7
+ // Helper function to create a Request object with a given URL
8
+ const createRequest = (url: string, method: string = "GET"): Request => {
9
+ return new Request(url, { method });
10
+ };
11
+
12
+ // Create a mock runtime with a basic agent
13
+ const createMockRuntime = () => {
14
+ const createMockAgent = () => {
15
+ const agent: unknown = {
16
+ execute: async () => ({ events: [] }),
17
+ };
18
+ (agent as { clone: () => unknown }).clone = () => createMockAgent();
19
+ return agent as AbstractAgent;
20
+ };
21
+
22
+ return new CopilotRuntime({
23
+ agents: {
24
+ default: createMockAgent(),
25
+ myAgent: createMockAgent(),
26
+ agent123: createMockAgent(),
27
+ "my-agent": createMockAgent(),
28
+ my_agent: createMockAgent(),
29
+ testAgent: createMockAgent(),
30
+ test: createMockAgent(),
31
+ "test%20agent": createMockAgent(),
32
+ "test agent": createMockAgent(),
33
+ },
34
+ });
35
+ };
36
+
37
+ // Helper to test routing
38
+ const testRoute = async (
39
+ url: string,
40
+ method: string = "GET",
41
+ body?: unknown
42
+ ) => {
43
+ const runtime = createMockRuntime();
44
+ const endpoint = createCopilotEndpoint({ runtime, basePath: "/" });
45
+ const requestInit: RequestInit = { method };
46
+ if (body) {
47
+ requestInit.body = JSON.stringify(body);
48
+ requestInit.headers = { "Content-Type": "application/json" };
49
+ }
50
+ const request = createRequest(url, method);
51
+ const response = await endpoint.fetch(request);
52
+ return response;
53
+ };
54
+
55
+ describe("RunAgent route pattern", () => {
56
+ it("should match agent run URL with simple agent name", async () => {
57
+ const response = await testRoute(
58
+ "https://example.com/agent/myAgent/run",
59
+ "POST",
60
+ {
61
+ agentId: "myAgent",
62
+ }
63
+ );
64
+
65
+ // Should not be 404
66
+ expect(response.status).not.toBe(404);
67
+ });
68
+
69
+ it("should match agent run URL with alphanumeric agent name", async () => {
70
+ const response = await testRoute(
71
+ "https://example.com/agent/agent123/run",
72
+ "POST",
73
+ {
74
+ agentId: "agent123",
75
+ }
76
+ );
77
+
78
+ expect(response.status).not.toBe(404);
79
+ });
80
+
81
+ it("should match agent run URL with hyphenated agent name", async () => {
82
+ const response = await testRoute(
83
+ "https://example.com/agent/my-agent/run",
84
+ "POST",
85
+ {
86
+ agentId: "my-agent",
87
+ }
88
+ );
89
+
90
+ expect(response.status).not.toBe(404);
91
+ });
92
+
93
+ it("should match agent run URL with underscored agent name", async () => {
94
+ const response = await testRoute(
95
+ "https://example.com/agent/my_agent/run",
96
+ "POST",
97
+ {
98
+ agentId: "my_agent",
99
+ }
100
+ );
101
+
102
+ expect(response.status).not.toBe(404);
103
+ });
104
+
105
+ it("should not match agent run URL with empty agent name", async () => {
106
+ const response = await testRoute(
107
+ "https://example.com/agent//run",
108
+ "POST"
109
+ );
110
+
111
+ expect(response.status).toBe(404);
112
+ const body = await response.json();
113
+ expect(body).toEqual({ error: "Not found" });
114
+ });
115
+
116
+ it("should not match partial agent run URL", async () => {
117
+ const response = await testRoute(
118
+ "https://example.com/agent/myAgent",
119
+ "POST"
120
+ );
121
+
122
+ expect(response.status).toBe(404);
123
+ });
124
+
125
+ it("should not match agent run URL with extra path segments", async () => {
126
+ const response = await testRoute(
127
+ "https://example.com/agent/myAgent/run/extra",
128
+ "POST"
129
+ );
130
+
131
+ expect(response.status).toBe(404);
132
+ });
133
+ });
134
+
135
+ describe("GetRuntimeInfo route pattern (/info endpoint)", () => {
136
+ it("should match simple info URL", async () => {
137
+ const response = await testRoute("https://example.com/info");
138
+
139
+ expect(response.status).toBe(200);
140
+ const body = await response.json();
141
+ expect(body).toHaveProperty("version");
142
+ });
143
+
144
+ it("should match info URL with query parameters", async () => {
145
+ const response = await testRoute("https://example.com/info?param=value");
146
+
147
+ expect(response.status).toBe(200);
148
+ });
149
+
150
+ it("should not match non-info URLs", async () => {
151
+ const response = await testRoute("https://example.com/agents");
152
+
153
+ expect(response.status).toBe(404);
154
+ });
155
+ });
156
+
157
+ describe("Transcribe route pattern (/transcribe endpoint)", () => {
158
+ it("should match simple transcribe URL", async () => {
159
+ // Transcribe expects POST method and audio data
160
+ const response = await testRoute(
161
+ "https://example.com/transcribe",
162
+ "POST",
163
+ {}
164
+ );
165
+
166
+ // It might return an error since we're not providing audio, but it shouldn't be 404
167
+ expect(response.status).not.toBe(404);
168
+ });
169
+
170
+ it("should match transcribe URL with query parameters", async () => {
171
+ const response = await testRoute(
172
+ "https://example.com/transcribe?format=json",
173
+ "POST",
174
+ {}
175
+ );
176
+
177
+ expect(response.status).not.toBe(404);
178
+ });
179
+
180
+ it("should not match transcribe URLs with extra path segments", async () => {
181
+ const response = await testRoute(
182
+ "https://example.com/transcribe/extra",
183
+ "POST"
184
+ );
185
+
186
+ expect(response.status).toBe(404);
187
+ });
188
+ });
189
+
190
+ describe("Unmatched routes (404 behavior)", () => {
191
+ it("should return 404 for root path", async () => {
192
+ const response = await testRoute("https://example.com/");
193
+
194
+ expect(response.status).toBe(404);
195
+ const body = await response.json();
196
+ expect(body).toEqual({ error: "Not found" });
197
+ });
198
+
199
+ it("should return 404 for unknown paths", async () => {
200
+ const response = await testRoute("https://example.com/unknown/path");
201
+
202
+ expect(response.status).toBe(404);
203
+ });
204
+
205
+ it("should return 404 for malformed agent paths", async () => {
206
+ const response = await testRoute("https://example.com/agent/run", "POST");
207
+
208
+ expect(response.status).toBe(404);
209
+ });
210
+
211
+ it("should return 404 for agents path", async () => {
212
+ const response = await testRoute("https://example.com/agents");
213
+
214
+ expect(response.status).toBe(404);
215
+ });
216
+ });
217
+
218
+ describe("Edge cases", () => {
219
+ it("should handle URLs with different domains", async () => {
220
+ const response = await testRoute(
221
+ "http://localhost:3000/agent/test/run",
222
+ "POST",
223
+ {
224
+ agentId: "test",
225
+ }
226
+ );
227
+
228
+ expect(response.status).not.toBe(404);
229
+ });
230
+
231
+ it("should handle URLs with ports for info endpoint", async () => {
232
+ const response = await testRoute("https://api.example.com:8080/info");
233
+
234
+ expect(response.status).toBe(200);
235
+ });
236
+
237
+ it("should handle URLs with ports for transcribe endpoint", async () => {
238
+ const response = await testRoute(
239
+ "https://api.example.com:8080/transcribe",
240
+ "POST",
241
+ {}
242
+ );
243
+
244
+ expect(response.status).not.toBe(404);
245
+ });
246
+
247
+ it("should handle URLs with special characters in agent names", async () => {
248
+ const response = await testRoute(
249
+ "https://example.com/agent/test%20agent/run",
250
+ "POST",
251
+ { agentId: "test%20agent" }
252
+ );
253
+
254
+ expect(response.status).not.toBe(404);
255
+ });
256
+ });
257
+ });