@copilotkitnext/runtime 0.0.18 → 0.0.19-threads-and-attachements.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/dist/index.d.mts +247 -4
- package/dist/index.d.ts +247 -4
- package/dist/index.js +634 -120
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +629 -121
- package/dist/index.mjs.map +1 -1
- package/package.json +4 -4
package/dist/index.mjs
CHANGED
|
@@ -1,7 +1,10 @@
|
|
|
1
|
+
// src/runtime.ts
|
|
2
|
+
import { logger } from "@copilotkitnext/shared";
|
|
3
|
+
|
|
1
4
|
// package.json
|
|
2
5
|
var package_default = {
|
|
3
6
|
name: "@copilotkitnext/runtime",
|
|
4
|
-
version: "0.0.
|
|
7
|
+
version: "0.0.19-threads-and-attachements.0",
|
|
5
8
|
description: "Server-side runtime package for CopilotKit2",
|
|
6
9
|
main: "dist/index.js",
|
|
7
10
|
types: "dist/index.d.ts",
|
|
@@ -37,9 +40,9 @@ var package_default = {
|
|
|
37
40
|
vitest: "^3.0.5"
|
|
38
41
|
},
|
|
39
42
|
dependencies: {
|
|
40
|
-
"@ag-ui/client": "0.0.40-alpha.
|
|
41
|
-
"@ag-ui/core": "0.0.40-alpha.
|
|
42
|
-
"@ag-ui/encoder": "0.0.40-alpha.
|
|
43
|
+
"@ag-ui/client": "0.0.40-alpha.7",
|
|
44
|
+
"@ag-ui/core": "0.0.40-alpha.7",
|
|
45
|
+
"@ag-ui/encoder": "0.0.40-alpha.7",
|
|
43
46
|
"@copilotkitnext/shared": "workspace:*",
|
|
44
47
|
hono: "^4.6.13",
|
|
45
48
|
rxjs: "7.8.1"
|
|
@@ -65,8 +68,10 @@ import {
|
|
|
65
68
|
} from "@ag-ui/client";
|
|
66
69
|
import { finalizeRunEvents } from "@copilotkitnext/shared";
|
|
67
70
|
var InMemoryEventStore = class {
|
|
68
|
-
constructor(threadId) {
|
|
71
|
+
constructor(threadId, resourceIds, properties) {
|
|
69
72
|
this.threadId = threadId;
|
|
73
|
+
this.resourceIds = resourceIds;
|
|
74
|
+
this.properties = properties;
|
|
70
75
|
}
|
|
71
76
|
/** The subject that current consumers subscribe to. */
|
|
72
77
|
subject = null;
|
|
@@ -86,11 +91,41 @@ var InMemoryEventStore = class {
|
|
|
86
91
|
currentEvents = null;
|
|
87
92
|
};
|
|
88
93
|
var GLOBAL_STORE = /* @__PURE__ */ new Map();
|
|
94
|
+
function matchesScope(store, scope) {
|
|
95
|
+
if (scope === void 0 || scope === null) {
|
|
96
|
+
return true;
|
|
97
|
+
}
|
|
98
|
+
const scopeIds = Array.isArray(scope.resourceId) ? scope.resourceId : [scope.resourceId];
|
|
99
|
+
return scopeIds.some((scopeId) => store.resourceIds.includes(scopeId));
|
|
100
|
+
}
|
|
89
101
|
var InMemoryAgentRunner = class extends AgentRunner {
|
|
90
102
|
run(request) {
|
|
91
103
|
let existingStore = GLOBAL_STORE.get(request.threadId);
|
|
92
|
-
if (!existingStore) {
|
|
93
|
-
|
|
104
|
+
if (!existingStore && request.scope === null) {
|
|
105
|
+
throw new Error(
|
|
106
|
+
"Cannot create thread with null scope. Admin users must specify an explicit resourceId for the thread owner."
|
|
107
|
+
);
|
|
108
|
+
}
|
|
109
|
+
let resourceIds;
|
|
110
|
+
if (request.scope === void 0) {
|
|
111
|
+
resourceIds = ["global"];
|
|
112
|
+
} else if (request.scope === null) {
|
|
113
|
+
resourceIds = [];
|
|
114
|
+
} else if (Array.isArray(request.scope.resourceId)) {
|
|
115
|
+
if (request.scope.resourceId.length === 0) {
|
|
116
|
+
throw new Error("Invalid scope: resourceId array cannot be empty");
|
|
117
|
+
}
|
|
118
|
+
resourceIds = request.scope.resourceId;
|
|
119
|
+
} else {
|
|
120
|
+
resourceIds = [request.scope.resourceId];
|
|
121
|
+
}
|
|
122
|
+
if (existingStore) {
|
|
123
|
+
if (request.scope !== null && !matchesScope(existingStore, request.scope)) {
|
|
124
|
+
throw new Error("Unauthorized: Cannot run on thread owned by different resource");
|
|
125
|
+
}
|
|
126
|
+
resourceIds = existingStore.resourceIds;
|
|
127
|
+
} else {
|
|
128
|
+
existingStore = new InMemoryEventStore(request.threadId, resourceIds, request.scope?.properties);
|
|
94
129
|
GLOBAL_STORE.set(request.threadId, existingStore);
|
|
95
130
|
}
|
|
96
131
|
const store = existingStore;
|
|
@@ -134,9 +169,7 @@ var InMemoryAgentRunner = class extends AgentRunner {
|
|
|
134
169
|
if (event.type === EventType.RUN_STARTED) {
|
|
135
170
|
const runStartedEvent = event;
|
|
136
171
|
if (!runStartedEvent.input) {
|
|
137
|
-
const sanitizedMessages = request.input.messages ? request.input.messages.filter(
|
|
138
|
-
(message) => !historicMessageIds.has(message.id)
|
|
139
|
-
) : void 0;
|
|
172
|
+
const sanitizedMessages = request.input.messages ? request.input.messages.filter((message) => !historicMessageIds.has(message.id)) : void 0;
|
|
140
173
|
const updatedInput = {
|
|
141
174
|
...request.input,
|
|
142
175
|
...sanitizedMessages !== void 0 ? { messages: sanitizedMessages } : {}
|
|
@@ -233,7 +266,7 @@ var InMemoryAgentRunner = class extends AgentRunner {
|
|
|
233
266
|
connect(request) {
|
|
234
267
|
const store = GLOBAL_STORE.get(request.threadId);
|
|
235
268
|
const connectionSubject = new ReplaySubject(Infinity);
|
|
236
|
-
if (!store) {
|
|
269
|
+
if (!store || !matchesScope(store, request.scope)) {
|
|
237
270
|
connectionSubject.complete();
|
|
238
271
|
return connectionSubject.asObservable();
|
|
239
272
|
}
|
|
@@ -269,54 +302,256 @@ var InMemoryAgentRunner = class extends AgentRunner {
|
|
|
269
302
|
const store = GLOBAL_STORE.get(request.threadId);
|
|
270
303
|
return Promise.resolve(store?.isRunning ?? false);
|
|
271
304
|
}
|
|
272
|
-
stop(request) {
|
|
305
|
+
async stop(request) {
|
|
273
306
|
const store = GLOBAL_STORE.get(request.threadId);
|
|
274
|
-
if (!store
|
|
275
|
-
return
|
|
276
|
-
}
|
|
277
|
-
if (store.stopRequested) {
|
|
278
|
-
return Promise.resolve(false);
|
|
307
|
+
if (!store) {
|
|
308
|
+
return false;
|
|
279
309
|
}
|
|
280
|
-
store.
|
|
281
|
-
|
|
282
|
-
const agent = store.agent;
|
|
283
|
-
if (!agent) {
|
|
284
|
-
store.stopRequested = false;
|
|
310
|
+
if (store.isRunning) {
|
|
311
|
+
store.stopRequested = true;
|
|
285
312
|
store.isRunning = false;
|
|
286
|
-
|
|
313
|
+
const agent = store.agent;
|
|
314
|
+
try {
|
|
315
|
+
if (agent) {
|
|
316
|
+
agent.abortRun();
|
|
317
|
+
return true;
|
|
318
|
+
}
|
|
319
|
+
return false;
|
|
320
|
+
} catch (error) {
|
|
321
|
+
console.warn("Failed to abort in-memory runner:", error);
|
|
322
|
+
store.stopRequested = false;
|
|
323
|
+
store.isRunning = true;
|
|
324
|
+
return false;
|
|
325
|
+
}
|
|
287
326
|
}
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
327
|
+
return false;
|
|
328
|
+
}
|
|
329
|
+
async listThreads(request) {
|
|
330
|
+
const limit = request.limit ?? 50;
|
|
331
|
+
const offset = request.offset ?? 0;
|
|
332
|
+
if (request.scope !== void 0 && request.scope !== null) {
|
|
333
|
+
const scopeIds = Array.isArray(request.scope.resourceId) ? request.scope.resourceId : [request.scope.resourceId];
|
|
334
|
+
if (scopeIds.length === 0) {
|
|
335
|
+
return { threads: [], total: 0 };
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
const threadInfos = [];
|
|
339
|
+
for (const [threadId, store] of GLOBAL_STORE.entries()) {
|
|
340
|
+
if (threadId.includes("-suggestions-")) {
|
|
341
|
+
continue;
|
|
342
|
+
}
|
|
343
|
+
if (!matchesScope(store, request.scope)) {
|
|
344
|
+
continue;
|
|
345
|
+
}
|
|
346
|
+
if (store.historicRuns.length === 0) {
|
|
347
|
+
continue;
|
|
348
|
+
}
|
|
349
|
+
const firstRun = store.historicRuns[0];
|
|
350
|
+
const lastRun = store.historicRuns[store.historicRuns.length - 1];
|
|
351
|
+
if (!firstRun || !lastRun) {
|
|
352
|
+
continue;
|
|
353
|
+
}
|
|
354
|
+
threadInfos.push({
|
|
355
|
+
threadId,
|
|
356
|
+
createdAt: firstRun.createdAt,
|
|
357
|
+
lastActivityAt: lastRun.createdAt,
|
|
358
|
+
store
|
|
359
|
+
});
|
|
360
|
+
}
|
|
361
|
+
threadInfos.sort((a, b) => b.lastActivityAt - a.lastActivityAt);
|
|
362
|
+
const total = threadInfos.length;
|
|
363
|
+
const paginatedInfos = threadInfos.slice(offset, offset + limit);
|
|
364
|
+
const threads = paginatedInfos.map((info) => {
|
|
365
|
+
let firstMessage;
|
|
366
|
+
const firstRun = info.store.historicRuns[0];
|
|
367
|
+
if (firstRun) {
|
|
368
|
+
const textContent = firstRun.events.find((e) => e.type === EventType.TEXT_MESSAGE_CONTENT);
|
|
369
|
+
if (textContent?.delta) {
|
|
370
|
+
firstMessage = textContent.delta.substring(0, 100);
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
const messageIds = /* @__PURE__ */ new Set();
|
|
374
|
+
for (const run of info.store.historicRuns) {
|
|
375
|
+
for (const event of run.events) {
|
|
376
|
+
if ("messageId" in event && typeof event.messageId === "string") {
|
|
377
|
+
messageIds.add(event.messageId);
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
return {
|
|
382
|
+
threadId: info.threadId,
|
|
383
|
+
createdAt: info.createdAt,
|
|
384
|
+
lastActivityAt: info.lastActivityAt,
|
|
385
|
+
isRunning: info.store.isRunning,
|
|
386
|
+
messageCount: messageIds.size,
|
|
387
|
+
firstMessage,
|
|
388
|
+
resourceId: info.store.resourceIds[0] || "unknown",
|
|
389
|
+
// Return first for backward compatibility
|
|
390
|
+
properties: info.store.properties
|
|
391
|
+
};
|
|
392
|
+
});
|
|
393
|
+
return { threads, total };
|
|
394
|
+
}
|
|
395
|
+
async getThreadMetadata(threadId, scope) {
|
|
396
|
+
const store = GLOBAL_STORE.get(threadId);
|
|
397
|
+
if (!store || !matchesScope(store, scope) || store.historicRuns.length === 0) {
|
|
398
|
+
return null;
|
|
399
|
+
}
|
|
400
|
+
const firstRun = store.historicRuns[0];
|
|
401
|
+
const lastRun = store.historicRuns[store.historicRuns.length - 1];
|
|
402
|
+
if (!firstRun || !lastRun) {
|
|
403
|
+
return null;
|
|
404
|
+
}
|
|
405
|
+
let firstMessage;
|
|
406
|
+
const textContent = firstRun.events.find((e) => e.type === EventType.TEXT_MESSAGE_CONTENT);
|
|
407
|
+
if (textContent?.delta) {
|
|
408
|
+
firstMessage = textContent.delta.substring(0, 100);
|
|
409
|
+
}
|
|
410
|
+
const messageIds = /* @__PURE__ */ new Set();
|
|
411
|
+
for (const run of store.historicRuns) {
|
|
412
|
+
for (const event of run.events) {
|
|
413
|
+
if ("messageId" in event && typeof event.messageId === "string") {
|
|
414
|
+
messageIds.add(event.messageId);
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
return {
|
|
419
|
+
threadId,
|
|
420
|
+
createdAt: firstRun.createdAt,
|
|
421
|
+
lastActivityAt: lastRun.createdAt,
|
|
422
|
+
isRunning: store.isRunning,
|
|
423
|
+
messageCount: messageIds.size,
|
|
424
|
+
firstMessage,
|
|
425
|
+
resourceId: store.resourceIds[0] || "unknown",
|
|
426
|
+
// Return first for backward compatibility
|
|
427
|
+
properties: store.properties
|
|
428
|
+
};
|
|
429
|
+
}
|
|
430
|
+
async deleteThread(threadId, scope) {
|
|
431
|
+
const store = GLOBAL_STORE.get(threadId);
|
|
432
|
+
if (!store || !matchesScope(store, scope)) {
|
|
433
|
+
return;
|
|
434
|
+
}
|
|
435
|
+
if (store.agent) {
|
|
436
|
+
try {
|
|
437
|
+
store.agent.abortRun();
|
|
438
|
+
} catch (error) {
|
|
439
|
+
console.warn("Failed to abort agent during thread deletion:", error);
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
store.subject?.complete();
|
|
443
|
+
GLOBAL_STORE.delete(threadId);
|
|
444
|
+
}
|
|
445
|
+
/**
|
|
446
|
+
* Clear all threads from the global store (for testing purposes only)
|
|
447
|
+
* @internal
|
|
448
|
+
*/
|
|
449
|
+
clearAllThreads() {
|
|
450
|
+
for (const [threadId, store] of GLOBAL_STORE.entries()) {
|
|
451
|
+
if (store.agent) {
|
|
452
|
+
try {
|
|
453
|
+
store.agent.abortRun();
|
|
454
|
+
} catch (error) {
|
|
455
|
+
console.warn("Failed to abort agent during clearAllThreads:", error);
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
store.subject?.complete();
|
|
459
|
+
GLOBAL_STORE.delete(threadId);
|
|
296
460
|
}
|
|
297
461
|
}
|
|
298
462
|
};
|
|
299
463
|
|
|
300
464
|
// src/runtime.ts
|
|
301
465
|
var VERSION = package_default.version;
|
|
302
|
-
var CopilotRuntime = class {
|
|
466
|
+
var CopilotRuntime = class _CopilotRuntime {
|
|
467
|
+
/**
|
|
468
|
+
* Built-in global scope for single-user apps or demos.
|
|
469
|
+
*
|
|
470
|
+
* All threads are globally accessible when using this scope.
|
|
471
|
+
*
|
|
472
|
+
* @example
|
|
473
|
+
* ```typescript
|
|
474
|
+
* new CopilotRuntime({
|
|
475
|
+
* agents: { myAgent },
|
|
476
|
+
* resolveThreadsScope: CopilotRuntime.GLOBAL_SCOPE,
|
|
477
|
+
* suppressResourceIdWarning: true
|
|
478
|
+
* });
|
|
479
|
+
* ```
|
|
480
|
+
*/
|
|
481
|
+
static GLOBAL_SCOPE = async (context) => ({
|
|
482
|
+
resourceId: "global"
|
|
483
|
+
});
|
|
484
|
+
/**
|
|
485
|
+
* Parses the client-declared resource ID(s) from the request header.
|
|
486
|
+
*
|
|
487
|
+
* This is a utility method used internally by handlers to extract the
|
|
488
|
+
* `X-CopilotKit-Resource-ID` header sent by the client via `CopilotKitProvider`.
|
|
489
|
+
*
|
|
490
|
+
* **You typically don't need to call this directly** - it's automatically called
|
|
491
|
+
* by the runtime handlers and passed to your `resolveThreadsScope` function as
|
|
492
|
+
* the `clientDeclared` parameter.
|
|
493
|
+
*
|
|
494
|
+
* @param request - The incoming HTTP request
|
|
495
|
+
* @returns The parsed resource ID(s), or undefined if header is missing
|
|
496
|
+
* - Returns a string if single ID
|
|
497
|
+
* - Returns an array if multiple comma-separated IDs
|
|
498
|
+
* - Returns undefined if header not present
|
|
499
|
+
*
|
|
500
|
+
* @example
|
|
501
|
+
* ```typescript
|
|
502
|
+
* // Automatically used internally:
|
|
503
|
+
* const clientDeclared = CopilotRuntime.parseClientDeclaredResourceId(request);
|
|
504
|
+
* const scope = await runtime.resolveThreadsScope({ request, clientDeclared });
|
|
505
|
+
* ```
|
|
506
|
+
*/
|
|
507
|
+
static parseClientDeclaredResourceId(request) {
|
|
508
|
+
const header = request.headers.get("X-CopilotKit-Resource-ID");
|
|
509
|
+
if (!header) {
|
|
510
|
+
return void 0;
|
|
511
|
+
}
|
|
512
|
+
const values = header.split(",").map((v) => decodeURIComponent(v.trim()));
|
|
513
|
+
return values.length === 1 ? values[0] : values;
|
|
514
|
+
}
|
|
303
515
|
agents;
|
|
304
516
|
transcriptionService;
|
|
305
517
|
beforeRequestMiddleware;
|
|
306
518
|
afterRequestMiddleware;
|
|
307
519
|
runner;
|
|
520
|
+
resolveThreadsScope;
|
|
521
|
+
suppressResourceIdWarning;
|
|
308
522
|
constructor({
|
|
309
523
|
agents,
|
|
310
524
|
transcriptionService,
|
|
311
525
|
beforeRequestMiddleware,
|
|
312
526
|
afterRequestMiddleware,
|
|
313
|
-
runner
|
|
527
|
+
runner,
|
|
528
|
+
resolveThreadsScope,
|
|
529
|
+
suppressResourceIdWarning = false
|
|
314
530
|
}) {
|
|
315
531
|
this.agents = agents;
|
|
316
532
|
this.transcriptionService = transcriptionService;
|
|
317
533
|
this.beforeRequestMiddleware = beforeRequestMiddleware;
|
|
318
534
|
this.afterRequestMiddleware = afterRequestMiddleware;
|
|
319
535
|
this.runner = runner ?? new InMemoryAgentRunner();
|
|
536
|
+
this.resolveThreadsScope = resolveThreadsScope ?? _CopilotRuntime.GLOBAL_SCOPE;
|
|
537
|
+
this.suppressResourceIdWarning = suppressResourceIdWarning;
|
|
538
|
+
if (!resolveThreadsScope && !suppressResourceIdWarning) {
|
|
539
|
+
this.logGlobalScopeWarning();
|
|
540
|
+
}
|
|
541
|
+
}
|
|
542
|
+
logGlobalScopeWarning() {
|
|
543
|
+
const isProduction = process.env.NODE_ENV === "production";
|
|
544
|
+
if (isProduction) {
|
|
545
|
+
logger.error({
|
|
546
|
+
msg: "CopilotKit Security Warning: GLOBAL_SCOPE in production",
|
|
547
|
+
details: "No resolveThreadsScope configured. All threads are globally accessible to all users. Configure authentication for production: https://docs.copilotkit.ai/security/thread-scoping To suppress this warning (if intentional), set suppressResourceIdWarning: true"
|
|
548
|
+
});
|
|
549
|
+
} else {
|
|
550
|
+
logger.warn({
|
|
551
|
+
msg: "CopilotKit: Using GLOBAL_SCOPE",
|
|
552
|
+
details: "No resolveThreadsScope configured. All threads are globally accessible. This is fine for development, but add authentication for production: https://docs.copilotkit.ai/security/thread-scoping"
|
|
553
|
+
});
|
|
554
|
+
}
|
|
320
555
|
}
|
|
321
556
|
};
|
|
322
557
|
|
|
@@ -325,15 +560,9 @@ import { Hono } from "hono";
|
|
|
325
560
|
import { cors } from "hono/cors";
|
|
326
561
|
|
|
327
562
|
// src/handlers/handle-run.ts
|
|
328
|
-
import {
|
|
329
|
-
RunAgentInputSchema
|
|
330
|
-
} from "@ag-ui/client";
|
|
563
|
+
import { RunAgentInputSchema } from "@ag-ui/client";
|
|
331
564
|
import { EventEncoder } from "@ag-ui/encoder";
|
|
332
|
-
async function handleRunAgent({
|
|
333
|
-
runtime,
|
|
334
|
-
request,
|
|
335
|
-
agentId
|
|
336
|
-
}) {
|
|
565
|
+
async function handleRunAgent({ runtime, request, agentId }) {
|
|
337
566
|
try {
|
|
338
567
|
const agents = await runtime.agents;
|
|
339
568
|
if (!agents[agentId]) {
|
|
@@ -370,6 +599,23 @@ async function handleRunAgent({
|
|
|
370
599
|
const writer = stream.writable.getWriter();
|
|
371
600
|
const encoder = new EventEncoder();
|
|
372
601
|
let streamClosed = false;
|
|
602
|
+
let subscription;
|
|
603
|
+
let abortListener;
|
|
604
|
+
const cleanupAbortListener = () => {
|
|
605
|
+
if (abortListener) {
|
|
606
|
+
request.signal.removeEventListener("abort", abortListener);
|
|
607
|
+
abortListener = void 0;
|
|
608
|
+
}
|
|
609
|
+
};
|
|
610
|
+
const closeStream = async () => {
|
|
611
|
+
if (!streamClosed) {
|
|
612
|
+
try {
|
|
613
|
+
await writer.close();
|
|
614
|
+
} catch {
|
|
615
|
+
}
|
|
616
|
+
streamClosed = true;
|
|
617
|
+
}
|
|
618
|
+
};
|
|
373
619
|
(async () => {
|
|
374
620
|
let input;
|
|
375
621
|
try {
|
|
@@ -383,13 +629,26 @@ async function handleRunAgent({
|
|
|
383
629
|
{ status: 400 }
|
|
384
630
|
);
|
|
385
631
|
}
|
|
632
|
+
const clientDeclared = CopilotRuntime["parseClientDeclaredResourceId"](request);
|
|
633
|
+
const scope = await runtime.resolveThreadsScope({ request, clientDeclared });
|
|
634
|
+
if (scope === void 0) {
|
|
635
|
+
throw new Error("Unauthorized: No resource scope provided");
|
|
636
|
+
}
|
|
386
637
|
agent.setMessages(input.messages);
|
|
387
638
|
agent.setState(input.state);
|
|
388
639
|
agent.threadId = input.threadId;
|
|
389
|
-
|
|
640
|
+
const stopRunner = async () => {
|
|
641
|
+
try {
|
|
642
|
+
await runtime.runner.stop({ threadId: input.threadId });
|
|
643
|
+
} catch (stopError) {
|
|
644
|
+
console.error("Error stopping runner:", stopError);
|
|
645
|
+
}
|
|
646
|
+
};
|
|
647
|
+
subscription = runtime.runner.run({
|
|
390
648
|
threadId: input.threadId,
|
|
391
649
|
agent,
|
|
392
|
-
input
|
|
650
|
+
input,
|
|
651
|
+
scope
|
|
393
652
|
}).subscribe({
|
|
394
653
|
next: async (event) => {
|
|
395
654
|
if (!request.signal.aborted && !streamClosed) {
|
|
@@ -404,42 +663,39 @@ async function handleRunAgent({
|
|
|
404
663
|
},
|
|
405
664
|
error: async (error) => {
|
|
406
665
|
console.error("Error running agent:", error);
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
await writer.close();
|
|
410
|
-
streamClosed = true;
|
|
411
|
-
} catch {
|
|
412
|
-
}
|
|
413
|
-
}
|
|
666
|
+
cleanupAbortListener();
|
|
667
|
+
await closeStream();
|
|
414
668
|
},
|
|
415
669
|
complete: async () => {
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
await writer.close();
|
|
419
|
-
streamClosed = true;
|
|
420
|
-
} catch {
|
|
421
|
-
}
|
|
422
|
-
}
|
|
670
|
+
cleanupAbortListener();
|
|
671
|
+
await closeStream();
|
|
423
672
|
}
|
|
424
673
|
});
|
|
674
|
+
const handleAbort = () => {
|
|
675
|
+
subscription?.unsubscribe();
|
|
676
|
+
subscription = void 0;
|
|
677
|
+
cleanupAbortListener();
|
|
678
|
+
void stopRunner();
|
|
679
|
+
void closeStream();
|
|
680
|
+
};
|
|
681
|
+
if (request.signal.aborted) {
|
|
682
|
+
handleAbort();
|
|
683
|
+
} else {
|
|
684
|
+
abortListener = handleAbort;
|
|
685
|
+
request.signal.addEventListener("abort", abortListener);
|
|
686
|
+
}
|
|
425
687
|
})().catch((error) => {
|
|
426
688
|
console.error("Error running agent:", error);
|
|
427
|
-
console.error(
|
|
428
|
-
"Error stack:",
|
|
429
|
-
error instanceof Error ? error.stack : "No stack trace"
|
|
430
|
-
);
|
|
689
|
+
console.error("Error stack:", error instanceof Error ? error.stack : "No stack trace");
|
|
431
690
|
console.error("Error details:", {
|
|
432
691
|
name: error instanceof Error ? error.name : "Unknown",
|
|
433
692
|
message: error instanceof Error ? error.message : String(error),
|
|
434
693
|
cause: error instanceof Error ? error.cause : void 0
|
|
435
694
|
});
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
} catch {
|
|
441
|
-
}
|
|
442
|
-
}
|
|
695
|
+
subscription?.unsubscribe();
|
|
696
|
+
subscription = void 0;
|
|
697
|
+
cleanupAbortListener();
|
|
698
|
+
void closeStream();
|
|
443
699
|
});
|
|
444
700
|
return new Response(stream.readable, {
|
|
445
701
|
status: 200,
|
|
@@ -451,10 +707,7 @@ async function handleRunAgent({
|
|
|
451
707
|
});
|
|
452
708
|
} catch (error) {
|
|
453
709
|
console.error("Error running agent:", error);
|
|
454
|
-
console.error(
|
|
455
|
-
"Error stack:",
|
|
456
|
-
error instanceof Error ? error.stack : "No stack trace"
|
|
457
|
-
);
|
|
710
|
+
console.error("Error stack:", error instanceof Error ? error.stack : "No stack trace");
|
|
458
711
|
console.error("Error details:", {
|
|
459
712
|
name: error instanceof Error ? error.name : "Unknown",
|
|
460
713
|
message: error instanceof Error ? error.message : String(error),
|
|
@@ -612,10 +865,10 @@ async function handleTranscribe({
|
|
|
612
865
|
}
|
|
613
866
|
|
|
614
867
|
// src/endpoint.ts
|
|
615
|
-
import { logger as
|
|
868
|
+
import { logger as logger3 } from "@copilotkitnext/shared";
|
|
616
869
|
|
|
617
870
|
// src/middleware.ts
|
|
618
|
-
import { logger } from "@copilotkitnext/shared";
|
|
871
|
+
import { logger as logger2 } from "@copilotkitnext/shared";
|
|
619
872
|
async function callBeforeRequestMiddleware({
|
|
620
873
|
runtime,
|
|
621
874
|
request,
|
|
@@ -626,7 +879,7 @@ async function callBeforeRequestMiddleware({
|
|
|
626
879
|
if (typeof mw === "function") {
|
|
627
880
|
return mw({ runtime, request, path });
|
|
628
881
|
}
|
|
629
|
-
|
|
882
|
+
logger2.warn({ mw }, "Unsupported beforeRequestMiddleware value \u2013 skipped");
|
|
630
883
|
return;
|
|
631
884
|
}
|
|
632
885
|
async function callAfterRequestMiddleware({
|
|
@@ -639,17 +892,13 @@ async function callAfterRequestMiddleware({
|
|
|
639
892
|
if (typeof mw === "function") {
|
|
640
893
|
return mw({ runtime, response, path });
|
|
641
894
|
}
|
|
642
|
-
|
|
895
|
+
logger2.warn({ mw }, "Unsupported afterRequestMiddleware value \u2013 skipped");
|
|
643
896
|
}
|
|
644
897
|
|
|
645
898
|
// src/handlers/handle-connect.ts
|
|
646
899
|
import { RunAgentInputSchema as RunAgentInputSchema2 } from "@ag-ui/client";
|
|
647
900
|
import { EventEncoder as EventEncoder2 } from "@ag-ui/encoder";
|
|
648
|
-
async function handleConnectAgent({
|
|
649
|
-
runtime,
|
|
650
|
-
request,
|
|
651
|
-
agentId
|
|
652
|
-
}) {
|
|
901
|
+
async function handleConnectAgent({ runtime, request, agentId }) {
|
|
653
902
|
try {
|
|
654
903
|
const agents = await runtime.agents;
|
|
655
904
|
if (!agents[agentId]) {
|
|
@@ -668,6 +917,23 @@ async function handleConnectAgent({
|
|
|
668
917
|
const writer = stream.writable.getWriter();
|
|
669
918
|
const encoder = new EventEncoder2();
|
|
670
919
|
let streamClosed = false;
|
|
920
|
+
let subscription;
|
|
921
|
+
let abortListener;
|
|
922
|
+
const cleanupAbortListener = () => {
|
|
923
|
+
if (abortListener) {
|
|
924
|
+
request.signal.removeEventListener("abort", abortListener);
|
|
925
|
+
abortListener = void 0;
|
|
926
|
+
}
|
|
927
|
+
};
|
|
928
|
+
const closeStream = async () => {
|
|
929
|
+
if (!streamClosed) {
|
|
930
|
+
try {
|
|
931
|
+
await writer.close();
|
|
932
|
+
} catch {
|
|
933
|
+
}
|
|
934
|
+
streamClosed = true;
|
|
935
|
+
}
|
|
936
|
+
};
|
|
671
937
|
(async () => {
|
|
672
938
|
let input;
|
|
673
939
|
try {
|
|
@@ -681,8 +947,14 @@ async function handleConnectAgent({
|
|
|
681
947
|
{ status: 400 }
|
|
682
948
|
);
|
|
683
949
|
}
|
|
684
|
-
|
|
685
|
-
|
|
950
|
+
const clientDeclared = CopilotRuntime["parseClientDeclaredResourceId"](request);
|
|
951
|
+
const scope = await runtime.resolveThreadsScope({ request, clientDeclared });
|
|
952
|
+
if (scope === void 0) {
|
|
953
|
+
throw new Error("Unauthorized: No resource scope provided");
|
|
954
|
+
}
|
|
955
|
+
subscription = runtime.runner.connect({
|
|
956
|
+
threadId: input.threadId,
|
|
957
|
+
scope
|
|
686
958
|
}).subscribe({
|
|
687
959
|
next: async (event) => {
|
|
688
960
|
if (!request.signal.aborted && !streamClosed) {
|
|
@@ -697,42 +969,38 @@ async function handleConnectAgent({
|
|
|
697
969
|
},
|
|
698
970
|
error: async (error) => {
|
|
699
971
|
console.error("Error running agent:", error);
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
await writer.close();
|
|
703
|
-
streamClosed = true;
|
|
704
|
-
} catch {
|
|
705
|
-
}
|
|
706
|
-
}
|
|
972
|
+
cleanupAbortListener();
|
|
973
|
+
await closeStream();
|
|
707
974
|
},
|
|
708
975
|
complete: async () => {
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
await writer.close();
|
|
712
|
-
streamClosed = true;
|
|
713
|
-
} catch {
|
|
714
|
-
}
|
|
715
|
-
}
|
|
976
|
+
cleanupAbortListener();
|
|
977
|
+
await closeStream();
|
|
716
978
|
}
|
|
717
979
|
});
|
|
980
|
+
const handleAbort = () => {
|
|
981
|
+
subscription?.unsubscribe();
|
|
982
|
+
subscription = void 0;
|
|
983
|
+
cleanupAbortListener();
|
|
984
|
+
void closeStream();
|
|
985
|
+
};
|
|
986
|
+
if (request.signal.aborted) {
|
|
987
|
+
handleAbort();
|
|
988
|
+
} else {
|
|
989
|
+
abortListener = handleAbort;
|
|
990
|
+
request.signal.addEventListener("abort", abortListener);
|
|
991
|
+
}
|
|
718
992
|
})().catch((error) => {
|
|
719
993
|
console.error("Error running agent:", error);
|
|
720
|
-
console.error(
|
|
721
|
-
"Error stack:",
|
|
722
|
-
error instanceof Error ? error.stack : "No stack trace"
|
|
723
|
-
);
|
|
994
|
+
console.error("Error stack:", error instanceof Error ? error.stack : "No stack trace");
|
|
724
995
|
console.error("Error details:", {
|
|
725
996
|
name: error instanceof Error ? error.name : "Unknown",
|
|
726
997
|
message: error instanceof Error ? error.message : String(error),
|
|
727
998
|
cause: error instanceof Error ? error.cause : void 0
|
|
728
999
|
});
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
} catch {
|
|
734
|
-
}
|
|
735
|
-
}
|
|
1000
|
+
subscription?.unsubscribe();
|
|
1001
|
+
subscription = void 0;
|
|
1002
|
+
cleanupAbortListener();
|
|
1003
|
+
void closeStream();
|
|
736
1004
|
});
|
|
737
1005
|
return new Response(stream.readable, {
|
|
738
1006
|
status: 200,
|
|
@@ -744,10 +1012,7 @@ async function handleConnectAgent({
|
|
|
744
1012
|
});
|
|
745
1013
|
} catch (error) {
|
|
746
1014
|
console.error("Error running agent:", error);
|
|
747
|
-
console.error(
|
|
748
|
-
"Error stack:",
|
|
749
|
-
error instanceof Error ? error.stack : "No stack trace"
|
|
750
|
-
);
|
|
1015
|
+
console.error("Error stack:", error instanceof Error ? error.stack : "No stack trace");
|
|
751
1016
|
console.error("Error details:", {
|
|
752
1017
|
name: error instanceof Error ? error.name : "Unknown",
|
|
753
1018
|
message: error instanceof Error ? error.message : String(error),
|
|
@@ -788,7 +1053,35 @@ async function handleStopAgent({
|
|
|
788
1053
|
}
|
|
789
1054
|
);
|
|
790
1055
|
}
|
|
791
|
-
const
|
|
1056
|
+
const clientDeclared = CopilotRuntime["parseClientDeclaredResourceId"](request);
|
|
1057
|
+
const scope = await runtime.resolveThreadsScope({ request, clientDeclared });
|
|
1058
|
+
if (scope === void 0) {
|
|
1059
|
+
return new Response(
|
|
1060
|
+
JSON.stringify({
|
|
1061
|
+
error: "Unauthorized",
|
|
1062
|
+
message: "No resource scope provided"
|
|
1063
|
+
}),
|
|
1064
|
+
{
|
|
1065
|
+
status: 401,
|
|
1066
|
+
headers: { "Content-Type": "application/json" }
|
|
1067
|
+
}
|
|
1068
|
+
);
|
|
1069
|
+
}
|
|
1070
|
+
const runner = await runtime.runner;
|
|
1071
|
+
const metadata = await runner.getThreadMetadata(threadId, scope);
|
|
1072
|
+
if (!metadata) {
|
|
1073
|
+
return new Response(
|
|
1074
|
+
JSON.stringify({
|
|
1075
|
+
error: "Thread not found",
|
|
1076
|
+
message: `Thread '${threadId}' does not exist or you don't have access`
|
|
1077
|
+
}),
|
|
1078
|
+
{
|
|
1079
|
+
status: 404,
|
|
1080
|
+
headers: { "Content-Type": "application/json" }
|
|
1081
|
+
}
|
|
1082
|
+
);
|
|
1083
|
+
}
|
|
1084
|
+
const stopped = await runner.stop({ threadId });
|
|
792
1085
|
if (!stopped) {
|
|
793
1086
|
return new Response(
|
|
794
1087
|
JSON.stringify({
|
|
@@ -830,6 +1123,141 @@ async function handleStopAgent({
|
|
|
830
1123
|
}
|
|
831
1124
|
}
|
|
832
1125
|
|
|
1126
|
+
// src/handlers/handle-threads.ts
|
|
1127
|
+
async function handleListThreads({ runtime, request }) {
|
|
1128
|
+
try {
|
|
1129
|
+
const clientDeclared = CopilotRuntime["parseClientDeclaredResourceId"](request);
|
|
1130
|
+
const scope = await runtime.resolveThreadsScope({ request, clientDeclared });
|
|
1131
|
+
if (scope === void 0) {
|
|
1132
|
+
return new Response(
|
|
1133
|
+
JSON.stringify({
|
|
1134
|
+
error: "Unauthorized",
|
|
1135
|
+
message: "No resource scope provided"
|
|
1136
|
+
}),
|
|
1137
|
+
{
|
|
1138
|
+
status: 401,
|
|
1139
|
+
headers: { "Content-Type": "application/json" }
|
|
1140
|
+
}
|
|
1141
|
+
);
|
|
1142
|
+
}
|
|
1143
|
+
const url = new URL(request.url);
|
|
1144
|
+
const limitParam = url.searchParams.get("limit");
|
|
1145
|
+
const offsetParam = url.searchParams.get("offset");
|
|
1146
|
+
const parsedLimit = limitParam ? Number.parseInt(limitParam, 10) : NaN;
|
|
1147
|
+
const parsedOffset = offsetParam ? Number.parseInt(offsetParam, 10) : NaN;
|
|
1148
|
+
const limit = Math.max(1, Math.min(100, Number.isNaN(parsedLimit) ? 20 : parsedLimit));
|
|
1149
|
+
const offset = Math.max(0, Number.isNaN(parsedOffset) ? 0 : parsedOffset);
|
|
1150
|
+
const runner = await runtime.runner;
|
|
1151
|
+
const result = await runner.listThreads({ scope, limit, offset });
|
|
1152
|
+
return new Response(JSON.stringify(result), {
|
|
1153
|
+
status: 200,
|
|
1154
|
+
headers: { "Content-Type": "application/json" }
|
|
1155
|
+
});
|
|
1156
|
+
} catch (error) {
|
|
1157
|
+
const errorMessage = error instanceof Error ? error.message : "Unknown error occurred";
|
|
1158
|
+
return new Response(
|
|
1159
|
+
JSON.stringify({
|
|
1160
|
+
error: "Failed to list threads",
|
|
1161
|
+
message: errorMessage
|
|
1162
|
+
}),
|
|
1163
|
+
{
|
|
1164
|
+
status: 500,
|
|
1165
|
+
headers: { "Content-Type": "application/json" }
|
|
1166
|
+
}
|
|
1167
|
+
);
|
|
1168
|
+
}
|
|
1169
|
+
}
|
|
1170
|
+
async function handleGetThread({ runtime, threadId, request }) {
|
|
1171
|
+
try {
|
|
1172
|
+
const clientDeclared = CopilotRuntime["parseClientDeclaredResourceId"](request);
|
|
1173
|
+
const scope = await runtime.resolveThreadsScope({ request, clientDeclared });
|
|
1174
|
+
if (scope === void 0) {
|
|
1175
|
+
return new Response(
|
|
1176
|
+
JSON.stringify({
|
|
1177
|
+
error: "Unauthorized",
|
|
1178
|
+
message: "No resource scope provided"
|
|
1179
|
+
}),
|
|
1180
|
+
{
|
|
1181
|
+
status: 401,
|
|
1182
|
+
headers: { "Content-Type": "application/json" }
|
|
1183
|
+
}
|
|
1184
|
+
);
|
|
1185
|
+
}
|
|
1186
|
+
const runner = await runtime.runner;
|
|
1187
|
+
const metadata = await runner.getThreadMetadata(threadId, scope);
|
|
1188
|
+
if (!metadata) {
|
|
1189
|
+
return new Response(
|
|
1190
|
+
JSON.stringify({
|
|
1191
|
+
error: "Thread not found",
|
|
1192
|
+
message: `Thread '${threadId}' does not exist`
|
|
1193
|
+
}),
|
|
1194
|
+
{
|
|
1195
|
+
status: 404,
|
|
1196
|
+
headers: { "Content-Type": "application/json" }
|
|
1197
|
+
}
|
|
1198
|
+
);
|
|
1199
|
+
}
|
|
1200
|
+
return new Response(JSON.stringify(metadata), {
|
|
1201
|
+
status: 200,
|
|
1202
|
+
headers: { "Content-Type": "application/json" }
|
|
1203
|
+
});
|
|
1204
|
+
} catch (error) {
|
|
1205
|
+
const errorMessage = error instanceof Error ? error.message : "Unknown error occurred";
|
|
1206
|
+
return new Response(
|
|
1207
|
+
JSON.stringify({
|
|
1208
|
+
error: "Failed to get thread",
|
|
1209
|
+
message: errorMessage
|
|
1210
|
+
}),
|
|
1211
|
+
{
|
|
1212
|
+
status: 500,
|
|
1213
|
+
headers: { "Content-Type": "application/json" }
|
|
1214
|
+
}
|
|
1215
|
+
);
|
|
1216
|
+
}
|
|
1217
|
+
}
|
|
1218
|
+
async function handleDeleteThread({ runtime, threadId, request }) {
|
|
1219
|
+
if (!threadId) {
|
|
1220
|
+
return new Response(JSON.stringify({ error: "Thread ID required" }), {
|
|
1221
|
+
status: 400,
|
|
1222
|
+
headers: { "Content-Type": "application/json" }
|
|
1223
|
+
});
|
|
1224
|
+
}
|
|
1225
|
+
try {
|
|
1226
|
+
const clientDeclared = CopilotRuntime["parseClientDeclaredResourceId"](request);
|
|
1227
|
+
const scope = await runtime.resolveThreadsScope({ request, clientDeclared });
|
|
1228
|
+
if (scope === void 0) {
|
|
1229
|
+
return new Response(
|
|
1230
|
+
JSON.stringify({
|
|
1231
|
+
error: "Unauthorized",
|
|
1232
|
+
message: "No resource scope provided"
|
|
1233
|
+
}),
|
|
1234
|
+
{
|
|
1235
|
+
status: 401,
|
|
1236
|
+
headers: { "Content-Type": "application/json" }
|
|
1237
|
+
}
|
|
1238
|
+
);
|
|
1239
|
+
}
|
|
1240
|
+
const runner = await runtime.runner;
|
|
1241
|
+
await runner.deleteThread(threadId, scope);
|
|
1242
|
+
return new Response(JSON.stringify({ success: true }), {
|
|
1243
|
+
status: 200,
|
|
1244
|
+
headers: { "Content-Type": "application/json" }
|
|
1245
|
+
});
|
|
1246
|
+
} catch (error) {
|
|
1247
|
+
const errorMessage = error instanceof Error ? error.message : "Unknown error occurred";
|
|
1248
|
+
return new Response(
|
|
1249
|
+
JSON.stringify({
|
|
1250
|
+
error: "Failed to delete thread",
|
|
1251
|
+
message: errorMessage
|
|
1252
|
+
}),
|
|
1253
|
+
{
|
|
1254
|
+
status: 500,
|
|
1255
|
+
headers: { "Content-Type": "application/json" }
|
|
1256
|
+
}
|
|
1257
|
+
);
|
|
1258
|
+
}
|
|
1259
|
+
}
|
|
1260
|
+
|
|
833
1261
|
// src/endpoint.ts
|
|
834
1262
|
function createCopilotEndpoint({ runtime, basePath }) {
|
|
835
1263
|
const app = new Hono();
|
|
@@ -853,7 +1281,7 @@ function createCopilotEndpoint({ runtime, basePath }) {
|
|
|
853
1281
|
c.set("modifiedRequest", maybeModifiedRequest);
|
|
854
1282
|
}
|
|
855
1283
|
} catch (error) {
|
|
856
|
-
|
|
1284
|
+
logger3.error({ err: error, url: request.url, path }, "Error running before request middleware");
|
|
857
1285
|
if (error instanceof Response) {
|
|
858
1286
|
return error;
|
|
859
1287
|
}
|
|
@@ -869,7 +1297,7 @@ function createCopilotEndpoint({ runtime, basePath }) {
|
|
|
869
1297
|
response,
|
|
870
1298
|
path
|
|
871
1299
|
}).catch((error) => {
|
|
872
|
-
|
|
1300
|
+
logger3.error({ err: error, url: c.req.url, path }, "Error running after request middleware");
|
|
873
1301
|
});
|
|
874
1302
|
}).post("/agent/:agentId/run", async (c) => {
|
|
875
1303
|
const agentId = c.req.param("agentId");
|
|
@@ -881,7 +1309,7 @@ function createCopilotEndpoint({ runtime, basePath }) {
|
|
|
881
1309
|
agentId
|
|
882
1310
|
});
|
|
883
1311
|
} catch (error) {
|
|
884
|
-
|
|
1312
|
+
logger3.error({ err: error, url: request.url, path: c.req.path }, "Error running request handler");
|
|
885
1313
|
throw error;
|
|
886
1314
|
}
|
|
887
1315
|
}).post("/agent/:agentId/connect", async (c) => {
|
|
@@ -894,7 +1322,7 @@ function createCopilotEndpoint({ runtime, basePath }) {
|
|
|
894
1322
|
agentId
|
|
895
1323
|
});
|
|
896
1324
|
} catch (error) {
|
|
897
|
-
|
|
1325
|
+
logger3.error({ err: error, url: request.url, path: c.req.path }, "Error running request handler");
|
|
898
1326
|
throw error;
|
|
899
1327
|
}
|
|
900
1328
|
}).post("/agent/:agentId/stop/:threadId", async (c) => {
|
|
@@ -909,7 +1337,7 @@ function createCopilotEndpoint({ runtime, basePath }) {
|
|
|
909
1337
|
threadId
|
|
910
1338
|
});
|
|
911
1339
|
} catch (error) {
|
|
912
|
-
|
|
1340
|
+
logger3.error({ err: error, url: request.url, path: c.req.path }, "Error running request handler");
|
|
913
1341
|
throw error;
|
|
914
1342
|
}
|
|
915
1343
|
}).get("/info", async (c) => {
|
|
@@ -920,7 +1348,7 @@ function createCopilotEndpoint({ runtime, basePath }) {
|
|
|
920
1348
|
request
|
|
921
1349
|
});
|
|
922
1350
|
} catch (error) {
|
|
923
|
-
|
|
1351
|
+
logger3.error({ err: error, url: request.url, path: c.req.path }, "Error running request handler");
|
|
924
1352
|
throw error;
|
|
925
1353
|
}
|
|
926
1354
|
}).post("/transcribe", async (c) => {
|
|
@@ -931,7 +1359,44 @@ function createCopilotEndpoint({ runtime, basePath }) {
|
|
|
931
1359
|
request
|
|
932
1360
|
});
|
|
933
1361
|
} catch (error) {
|
|
934
|
-
|
|
1362
|
+
logger3.error({ err: error, url: request.url, path: c.req.path }, "Error running request handler");
|
|
1363
|
+
throw error;
|
|
1364
|
+
}
|
|
1365
|
+
}).get("/threads", async (c) => {
|
|
1366
|
+
const request = c.get("modifiedRequest") || c.req.raw;
|
|
1367
|
+
try {
|
|
1368
|
+
return await handleListThreads({
|
|
1369
|
+
runtime,
|
|
1370
|
+
request
|
|
1371
|
+
});
|
|
1372
|
+
} catch (error) {
|
|
1373
|
+
logger3.error({ err: error, url: request.url, path: c.req.path }, "Error running request handler");
|
|
1374
|
+
throw error;
|
|
1375
|
+
}
|
|
1376
|
+
}).get("/threads/:threadId", async (c) => {
|
|
1377
|
+
const threadId = c.req.param("threadId");
|
|
1378
|
+
const request = c.get("modifiedRequest") || c.req.raw;
|
|
1379
|
+
try {
|
|
1380
|
+
return await handleGetThread({
|
|
1381
|
+
runtime,
|
|
1382
|
+
request,
|
|
1383
|
+
threadId
|
|
1384
|
+
});
|
|
1385
|
+
} catch (error) {
|
|
1386
|
+
logger3.error({ err: error, url: request.url, path: c.req.path }, "Error running request handler");
|
|
1387
|
+
throw error;
|
|
1388
|
+
}
|
|
1389
|
+
}).delete("/threads/:threadId", async (c) => {
|
|
1390
|
+
const threadId = c.req.param("threadId");
|
|
1391
|
+
const request = c.get("modifiedRequest") || c.req.raw;
|
|
1392
|
+
try {
|
|
1393
|
+
return await handleDeleteThread({
|
|
1394
|
+
runtime,
|
|
1395
|
+
request,
|
|
1396
|
+
threadId
|
|
1397
|
+
});
|
|
1398
|
+
} catch (error) {
|
|
1399
|
+
logger3.error({ err: error, url: request.url, path: c.req.path }, "Error running request handler");
|
|
935
1400
|
throw error;
|
|
936
1401
|
}
|
|
937
1402
|
}).notFound((c) => {
|
|
@@ -941,12 +1406,55 @@ function createCopilotEndpoint({ runtime, basePath }) {
|
|
|
941
1406
|
|
|
942
1407
|
// src/runner/index.ts
|
|
943
1408
|
import { finalizeRunEvents as finalizeRunEvents2 } from "@copilotkitnext/shared";
|
|
1409
|
+
|
|
1410
|
+
// src/resource-id-helpers.ts
|
|
1411
|
+
function validateResourceIdMatch(clientDeclared, serverAuthorized) {
|
|
1412
|
+
if (!clientDeclared) {
|
|
1413
|
+
return;
|
|
1414
|
+
}
|
|
1415
|
+
const clientIds = Array.isArray(clientDeclared) ? clientDeclared : [clientDeclared];
|
|
1416
|
+
const authorizedIds = Array.isArray(serverAuthorized) ? serverAuthorized : [serverAuthorized];
|
|
1417
|
+
const hasMatch = clientIds.some((clientId) => authorizedIds.includes(clientId));
|
|
1418
|
+
if (!hasMatch) {
|
|
1419
|
+
throw new Error("Unauthorized: Client-declared resourceId does not match authenticated user");
|
|
1420
|
+
}
|
|
1421
|
+
}
|
|
1422
|
+
function filterAuthorizedResourceIds(clientDeclared, serverAuthorized) {
|
|
1423
|
+
const authorizedIds = Array.isArray(serverAuthorized) ? serverAuthorized : [serverAuthorized];
|
|
1424
|
+
if (!clientDeclared) {
|
|
1425
|
+
return serverAuthorized;
|
|
1426
|
+
}
|
|
1427
|
+
const clientIds = Array.isArray(clientDeclared) ? clientDeclared : [clientDeclared];
|
|
1428
|
+
const filtered = clientIds.filter((id) => authorizedIds.includes(id));
|
|
1429
|
+
if (filtered.length === 0) {
|
|
1430
|
+
throw new Error("Unauthorized: None of the client-declared resourceIds are authorized");
|
|
1431
|
+
}
|
|
1432
|
+
return Array.isArray(clientDeclared) ? filtered : filtered[0];
|
|
1433
|
+
}
|
|
1434
|
+
function createStrictThreadScopeResolver(getUserId) {
|
|
1435
|
+
return async ({ request, clientDeclared }) => {
|
|
1436
|
+
const userId = await getUserId(request);
|
|
1437
|
+
validateResourceIdMatch(clientDeclared, userId);
|
|
1438
|
+
return { resourceId: userId };
|
|
1439
|
+
};
|
|
1440
|
+
}
|
|
1441
|
+
function createFilteringThreadScopeResolver(getUserResourceIds) {
|
|
1442
|
+
return async ({ request, clientDeclared }) => {
|
|
1443
|
+
const userResourceIds = await getUserResourceIds(request);
|
|
1444
|
+
const resourceId = filterAuthorizedResourceIds(clientDeclared, userResourceIds);
|
|
1445
|
+
return { resourceId };
|
|
1446
|
+
};
|
|
1447
|
+
}
|
|
944
1448
|
export {
|
|
945
1449
|
AgentRunner,
|
|
946
1450
|
CopilotRuntime,
|
|
947
1451
|
InMemoryAgentRunner,
|
|
948
1452
|
VERSION,
|
|
949
1453
|
createCopilotEndpoint,
|
|
950
|
-
|
|
1454
|
+
createFilteringThreadScopeResolver,
|
|
1455
|
+
createStrictThreadScopeResolver,
|
|
1456
|
+
filterAuthorizedResourceIds,
|
|
1457
|
+
finalizeRunEvents2 as finalizeRunEvents,
|
|
1458
|
+
validateResourceIdMatch
|
|
951
1459
|
};
|
|
952
1460
|
//# sourceMappingURL=index.mjs.map
|