@copilotkitnext/runtime 0.0.17 → 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 +246 -8
- package/dist/index.d.ts +246 -8
- package/dist/index.js +650 -255
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +638 -251
- package/dist/index.mjs.map +1 -1
- package/package.json +7 -7
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"
|
|
@@ -60,137 +63,15 @@ var AgentRunner = class {
|
|
|
60
63
|
// src/runner/in-memory.ts
|
|
61
64
|
import { ReplaySubject } from "rxjs";
|
|
62
65
|
import {
|
|
63
|
-
EventType
|
|
66
|
+
EventType,
|
|
64
67
|
compactEvents
|
|
65
68
|
} 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
|
|
69
|
+
import { finalizeRunEvents } from "@copilotkitnext/shared";
|
|
191
70
|
var InMemoryEventStore = class {
|
|
192
|
-
constructor(threadId) {
|
|
71
|
+
constructor(threadId, resourceIds, properties) {
|
|
193
72
|
this.threadId = threadId;
|
|
73
|
+
this.resourceIds = resourceIds;
|
|
74
|
+
this.properties = properties;
|
|
194
75
|
}
|
|
195
76
|
/** The subject that current consumers subscribe to. */
|
|
196
77
|
subject = null;
|
|
@@ -210,11 +91,41 @@ var InMemoryEventStore = class {
|
|
|
210
91
|
currentEvents = null;
|
|
211
92
|
};
|
|
212
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
|
+
}
|
|
213
101
|
var InMemoryAgentRunner = class extends AgentRunner {
|
|
214
102
|
run(request) {
|
|
215
103
|
let existingStore = GLOBAL_STORE.get(request.threadId);
|
|
216
|
-
if (!existingStore) {
|
|
217
|
-
|
|
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);
|
|
218
129
|
GLOBAL_STORE.set(request.threadId, existingStore);
|
|
219
130
|
}
|
|
220
131
|
const store = existingStore;
|
|
@@ -234,7 +145,7 @@ var InMemoryAgentRunner = class extends AgentRunner {
|
|
|
234
145
|
if ("messageId" in event && typeof event.messageId === "string") {
|
|
235
146
|
historicMessageIds.add(event.messageId);
|
|
236
147
|
}
|
|
237
|
-
if (event.type ===
|
|
148
|
+
if (event.type === EventType.RUN_STARTED) {
|
|
238
149
|
const runStarted = event;
|
|
239
150
|
const messages = runStarted.input?.messages ?? [];
|
|
240
151
|
for (const message of messages) {
|
|
@@ -255,12 +166,10 @@ var InMemoryAgentRunner = class extends AgentRunner {
|
|
|
255
166
|
await request.agent.runAgent(request.input, {
|
|
256
167
|
onEvent: ({ event }) => {
|
|
257
168
|
let processedEvent = event;
|
|
258
|
-
if (event.type ===
|
|
169
|
+
if (event.type === EventType.RUN_STARTED) {
|
|
259
170
|
const runStartedEvent = event;
|
|
260
171
|
if (!runStartedEvent.input) {
|
|
261
|
-
const sanitizedMessages = request.input.messages ? request.input.messages.filter(
|
|
262
|
-
(message) => !historicMessageIds.has(message.id)
|
|
263
|
-
) : void 0;
|
|
172
|
+
const sanitizedMessages = request.input.messages ? request.input.messages.filter((message) => !historicMessageIds.has(message.id)) : void 0;
|
|
264
173
|
const updatedInput = {
|
|
265
174
|
...request.input,
|
|
266
175
|
...sanitizedMessages !== void 0 ? { messages: sanitizedMessages } : {}
|
|
@@ -357,7 +266,7 @@ var InMemoryAgentRunner = class extends AgentRunner {
|
|
|
357
266
|
connect(request) {
|
|
358
267
|
const store = GLOBAL_STORE.get(request.threadId);
|
|
359
268
|
const connectionSubject = new ReplaySubject(Infinity);
|
|
360
|
-
if (!store) {
|
|
269
|
+
if (!store || !matchesScope(store, request.scope)) {
|
|
361
270
|
connectionSubject.complete();
|
|
362
271
|
return connectionSubject.asObservable();
|
|
363
272
|
}
|
|
@@ -393,54 +302,256 @@ var InMemoryAgentRunner = class extends AgentRunner {
|
|
|
393
302
|
const store = GLOBAL_STORE.get(request.threadId);
|
|
394
303
|
return Promise.resolve(store?.isRunning ?? false);
|
|
395
304
|
}
|
|
396
|
-
stop(request) {
|
|
305
|
+
async stop(request) {
|
|
397
306
|
const store = GLOBAL_STORE.get(request.threadId);
|
|
398
|
-
if (!store
|
|
399
|
-
return
|
|
400
|
-
}
|
|
401
|
-
if (store.stopRequested) {
|
|
402
|
-
return Promise.resolve(false);
|
|
307
|
+
if (!store) {
|
|
308
|
+
return false;
|
|
403
309
|
}
|
|
404
|
-
store.
|
|
405
|
-
|
|
406
|
-
const agent = store.agent;
|
|
407
|
-
if (!agent) {
|
|
408
|
-
store.stopRequested = false;
|
|
310
|
+
if (store.isRunning) {
|
|
311
|
+
store.stopRequested = true;
|
|
409
312
|
store.isRunning = false;
|
|
410
|
-
|
|
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
|
+
}
|
|
411
326
|
}
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
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);
|
|
420
460
|
}
|
|
421
461
|
}
|
|
422
462
|
};
|
|
423
463
|
|
|
424
464
|
// src/runtime.ts
|
|
425
465
|
var VERSION = package_default.version;
|
|
426
|
-
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
|
+
}
|
|
427
515
|
agents;
|
|
428
516
|
transcriptionService;
|
|
429
517
|
beforeRequestMiddleware;
|
|
430
518
|
afterRequestMiddleware;
|
|
431
519
|
runner;
|
|
520
|
+
resolveThreadsScope;
|
|
521
|
+
suppressResourceIdWarning;
|
|
432
522
|
constructor({
|
|
433
523
|
agents,
|
|
434
524
|
transcriptionService,
|
|
435
525
|
beforeRequestMiddleware,
|
|
436
526
|
afterRequestMiddleware,
|
|
437
|
-
runner
|
|
527
|
+
runner,
|
|
528
|
+
resolveThreadsScope,
|
|
529
|
+
suppressResourceIdWarning = false
|
|
438
530
|
}) {
|
|
439
531
|
this.agents = agents;
|
|
440
532
|
this.transcriptionService = transcriptionService;
|
|
441
533
|
this.beforeRequestMiddleware = beforeRequestMiddleware;
|
|
442
534
|
this.afterRequestMiddleware = afterRequestMiddleware;
|
|
443
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
|
+
}
|
|
444
555
|
}
|
|
445
556
|
};
|
|
446
557
|
|
|
@@ -449,15 +560,9 @@ import { Hono } from "hono";
|
|
|
449
560
|
import { cors } from "hono/cors";
|
|
450
561
|
|
|
451
562
|
// src/handlers/handle-run.ts
|
|
452
|
-
import {
|
|
453
|
-
RunAgentInputSchema
|
|
454
|
-
} from "@ag-ui/client";
|
|
563
|
+
import { RunAgentInputSchema } from "@ag-ui/client";
|
|
455
564
|
import { EventEncoder } from "@ag-ui/encoder";
|
|
456
|
-
async function handleRunAgent({
|
|
457
|
-
runtime,
|
|
458
|
-
request,
|
|
459
|
-
agentId
|
|
460
|
-
}) {
|
|
565
|
+
async function handleRunAgent({ runtime, request, agentId }) {
|
|
461
566
|
try {
|
|
462
567
|
const agents = await runtime.agents;
|
|
463
568
|
if (!agents[agentId]) {
|
|
@@ -494,6 +599,23 @@ async function handleRunAgent({
|
|
|
494
599
|
const writer = stream.writable.getWriter();
|
|
495
600
|
const encoder = new EventEncoder();
|
|
496
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
|
+
};
|
|
497
619
|
(async () => {
|
|
498
620
|
let input;
|
|
499
621
|
try {
|
|
@@ -507,13 +629,26 @@ async function handleRunAgent({
|
|
|
507
629
|
{ status: 400 }
|
|
508
630
|
);
|
|
509
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
|
+
}
|
|
510
637
|
agent.setMessages(input.messages);
|
|
511
638
|
agent.setState(input.state);
|
|
512
639
|
agent.threadId = input.threadId;
|
|
513
|
-
|
|
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({
|
|
514
648
|
threadId: input.threadId,
|
|
515
649
|
agent,
|
|
516
|
-
input
|
|
650
|
+
input,
|
|
651
|
+
scope
|
|
517
652
|
}).subscribe({
|
|
518
653
|
next: async (event) => {
|
|
519
654
|
if (!request.signal.aborted && !streamClosed) {
|
|
@@ -528,42 +663,39 @@ async function handleRunAgent({
|
|
|
528
663
|
},
|
|
529
664
|
error: async (error) => {
|
|
530
665
|
console.error("Error running agent:", error);
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
await writer.close();
|
|
534
|
-
streamClosed = true;
|
|
535
|
-
} catch {
|
|
536
|
-
}
|
|
537
|
-
}
|
|
666
|
+
cleanupAbortListener();
|
|
667
|
+
await closeStream();
|
|
538
668
|
},
|
|
539
669
|
complete: async () => {
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
await writer.close();
|
|
543
|
-
streamClosed = true;
|
|
544
|
-
} catch {
|
|
545
|
-
}
|
|
546
|
-
}
|
|
670
|
+
cleanupAbortListener();
|
|
671
|
+
await closeStream();
|
|
547
672
|
}
|
|
548
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
|
+
}
|
|
549
687
|
})().catch((error) => {
|
|
550
688
|
console.error("Error running agent:", error);
|
|
551
|
-
console.error(
|
|
552
|
-
"Error stack:",
|
|
553
|
-
error instanceof Error ? error.stack : "No stack trace"
|
|
554
|
-
);
|
|
689
|
+
console.error("Error stack:", error instanceof Error ? error.stack : "No stack trace");
|
|
555
690
|
console.error("Error details:", {
|
|
556
691
|
name: error instanceof Error ? error.name : "Unknown",
|
|
557
692
|
message: error instanceof Error ? error.message : String(error),
|
|
558
693
|
cause: error instanceof Error ? error.cause : void 0
|
|
559
694
|
});
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
} catch {
|
|
565
|
-
}
|
|
566
|
-
}
|
|
695
|
+
subscription?.unsubscribe();
|
|
696
|
+
subscription = void 0;
|
|
697
|
+
cleanupAbortListener();
|
|
698
|
+
void closeStream();
|
|
567
699
|
});
|
|
568
700
|
return new Response(stream.readable, {
|
|
569
701
|
status: 200,
|
|
@@ -575,10 +707,7 @@ async function handleRunAgent({
|
|
|
575
707
|
});
|
|
576
708
|
} catch (error) {
|
|
577
709
|
console.error("Error running agent:", error);
|
|
578
|
-
console.error(
|
|
579
|
-
"Error stack:",
|
|
580
|
-
error instanceof Error ? error.stack : "No stack trace"
|
|
581
|
-
);
|
|
710
|
+
console.error("Error stack:", error instanceof Error ? error.stack : "No stack trace");
|
|
582
711
|
console.error("Error details:", {
|
|
583
712
|
name: error instanceof Error ? error.name : "Unknown",
|
|
584
713
|
message: error instanceof Error ? error.message : String(error),
|
|
@@ -736,10 +865,10 @@ async function handleTranscribe({
|
|
|
736
865
|
}
|
|
737
866
|
|
|
738
867
|
// src/endpoint.ts
|
|
739
|
-
import { logger as
|
|
868
|
+
import { logger as logger3 } from "@copilotkitnext/shared";
|
|
740
869
|
|
|
741
870
|
// src/middleware.ts
|
|
742
|
-
import { logger } from "@copilotkitnext/shared";
|
|
871
|
+
import { logger as logger2 } from "@copilotkitnext/shared";
|
|
743
872
|
async function callBeforeRequestMiddleware({
|
|
744
873
|
runtime,
|
|
745
874
|
request,
|
|
@@ -750,7 +879,7 @@ async function callBeforeRequestMiddleware({
|
|
|
750
879
|
if (typeof mw === "function") {
|
|
751
880
|
return mw({ runtime, request, path });
|
|
752
881
|
}
|
|
753
|
-
|
|
882
|
+
logger2.warn({ mw }, "Unsupported beforeRequestMiddleware value \u2013 skipped");
|
|
754
883
|
return;
|
|
755
884
|
}
|
|
756
885
|
async function callAfterRequestMiddleware({
|
|
@@ -763,17 +892,13 @@ async function callAfterRequestMiddleware({
|
|
|
763
892
|
if (typeof mw === "function") {
|
|
764
893
|
return mw({ runtime, response, path });
|
|
765
894
|
}
|
|
766
|
-
|
|
895
|
+
logger2.warn({ mw }, "Unsupported afterRequestMiddleware value \u2013 skipped");
|
|
767
896
|
}
|
|
768
897
|
|
|
769
898
|
// src/handlers/handle-connect.ts
|
|
770
899
|
import { RunAgentInputSchema as RunAgentInputSchema2 } from "@ag-ui/client";
|
|
771
900
|
import { EventEncoder as EventEncoder2 } from "@ag-ui/encoder";
|
|
772
|
-
async function handleConnectAgent({
|
|
773
|
-
runtime,
|
|
774
|
-
request,
|
|
775
|
-
agentId
|
|
776
|
-
}) {
|
|
901
|
+
async function handleConnectAgent({ runtime, request, agentId }) {
|
|
777
902
|
try {
|
|
778
903
|
const agents = await runtime.agents;
|
|
779
904
|
if (!agents[agentId]) {
|
|
@@ -792,6 +917,23 @@ async function handleConnectAgent({
|
|
|
792
917
|
const writer = stream.writable.getWriter();
|
|
793
918
|
const encoder = new EventEncoder2();
|
|
794
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
|
+
};
|
|
795
937
|
(async () => {
|
|
796
938
|
let input;
|
|
797
939
|
try {
|
|
@@ -805,8 +947,14 @@ async function handleConnectAgent({
|
|
|
805
947
|
{ status: 400 }
|
|
806
948
|
);
|
|
807
949
|
}
|
|
808
|
-
|
|
809
|
-
|
|
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
|
|
810
958
|
}).subscribe({
|
|
811
959
|
next: async (event) => {
|
|
812
960
|
if (!request.signal.aborted && !streamClosed) {
|
|
@@ -821,42 +969,38 @@ async function handleConnectAgent({
|
|
|
821
969
|
},
|
|
822
970
|
error: async (error) => {
|
|
823
971
|
console.error("Error running agent:", error);
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
await writer.close();
|
|
827
|
-
streamClosed = true;
|
|
828
|
-
} catch {
|
|
829
|
-
}
|
|
830
|
-
}
|
|
972
|
+
cleanupAbortListener();
|
|
973
|
+
await closeStream();
|
|
831
974
|
},
|
|
832
975
|
complete: async () => {
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
await writer.close();
|
|
836
|
-
streamClosed = true;
|
|
837
|
-
} catch {
|
|
838
|
-
}
|
|
839
|
-
}
|
|
976
|
+
cleanupAbortListener();
|
|
977
|
+
await closeStream();
|
|
840
978
|
}
|
|
841
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
|
+
}
|
|
842
992
|
})().catch((error) => {
|
|
843
993
|
console.error("Error running agent:", error);
|
|
844
|
-
console.error(
|
|
845
|
-
"Error stack:",
|
|
846
|
-
error instanceof Error ? error.stack : "No stack trace"
|
|
847
|
-
);
|
|
994
|
+
console.error("Error stack:", error instanceof Error ? error.stack : "No stack trace");
|
|
848
995
|
console.error("Error details:", {
|
|
849
996
|
name: error instanceof Error ? error.name : "Unknown",
|
|
850
997
|
message: error instanceof Error ? error.message : String(error),
|
|
851
998
|
cause: error instanceof Error ? error.cause : void 0
|
|
852
999
|
});
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
} catch {
|
|
858
|
-
}
|
|
859
|
-
}
|
|
1000
|
+
subscription?.unsubscribe();
|
|
1001
|
+
subscription = void 0;
|
|
1002
|
+
cleanupAbortListener();
|
|
1003
|
+
void closeStream();
|
|
860
1004
|
});
|
|
861
1005
|
return new Response(stream.readable, {
|
|
862
1006
|
status: 200,
|
|
@@ -868,10 +1012,7 @@ async function handleConnectAgent({
|
|
|
868
1012
|
});
|
|
869
1013
|
} catch (error) {
|
|
870
1014
|
console.error("Error running agent:", error);
|
|
871
|
-
console.error(
|
|
872
|
-
"Error stack:",
|
|
873
|
-
error instanceof Error ? error.stack : "No stack trace"
|
|
874
|
-
);
|
|
1015
|
+
console.error("Error stack:", error instanceof Error ? error.stack : "No stack trace");
|
|
875
1016
|
console.error("Error details:", {
|
|
876
1017
|
name: error instanceof Error ? error.name : "Unknown",
|
|
877
1018
|
message: error instanceof Error ? error.message : String(error),
|
|
@@ -891,7 +1032,7 @@ async function handleConnectAgent({
|
|
|
891
1032
|
}
|
|
892
1033
|
|
|
893
1034
|
// src/handlers/handle-stop.ts
|
|
894
|
-
import { EventType as
|
|
1035
|
+
import { EventType as EventType2 } from "@ag-ui/client";
|
|
895
1036
|
async function handleStopAgent({
|
|
896
1037
|
runtime,
|
|
897
1038
|
request,
|
|
@@ -912,7 +1053,35 @@ async function handleStopAgent({
|
|
|
912
1053
|
}
|
|
913
1054
|
);
|
|
914
1055
|
}
|
|
915
|
-
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 });
|
|
916
1085
|
if (!stopped) {
|
|
917
1086
|
return new Response(
|
|
918
1087
|
JSON.stringify({
|
|
@@ -929,7 +1098,7 @@ async function handleStopAgent({
|
|
|
929
1098
|
JSON.stringify({
|
|
930
1099
|
stopped: true,
|
|
931
1100
|
interrupt: {
|
|
932
|
-
type:
|
|
1101
|
+
type: EventType2.RUN_ERROR,
|
|
933
1102
|
message: "Run stopped by user",
|
|
934
1103
|
code: "STOPPED"
|
|
935
1104
|
}
|
|
@@ -954,6 +1123,141 @@ async function handleStopAgent({
|
|
|
954
1123
|
}
|
|
955
1124
|
}
|
|
956
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
|
+
|
|
957
1261
|
// src/endpoint.ts
|
|
958
1262
|
function createCopilotEndpoint({ runtime, basePath }) {
|
|
959
1263
|
const app = new Hono();
|
|
@@ -977,7 +1281,7 @@ function createCopilotEndpoint({ runtime, basePath }) {
|
|
|
977
1281
|
c.set("modifiedRequest", maybeModifiedRequest);
|
|
978
1282
|
}
|
|
979
1283
|
} catch (error) {
|
|
980
|
-
|
|
1284
|
+
logger3.error({ err: error, url: request.url, path }, "Error running before request middleware");
|
|
981
1285
|
if (error instanceof Response) {
|
|
982
1286
|
return error;
|
|
983
1287
|
}
|
|
@@ -993,7 +1297,7 @@ function createCopilotEndpoint({ runtime, basePath }) {
|
|
|
993
1297
|
response,
|
|
994
1298
|
path
|
|
995
1299
|
}).catch((error) => {
|
|
996
|
-
|
|
1300
|
+
logger3.error({ err: error, url: c.req.url, path }, "Error running after request middleware");
|
|
997
1301
|
});
|
|
998
1302
|
}).post("/agent/:agentId/run", async (c) => {
|
|
999
1303
|
const agentId = c.req.param("agentId");
|
|
@@ -1005,7 +1309,7 @@ function createCopilotEndpoint({ runtime, basePath }) {
|
|
|
1005
1309
|
agentId
|
|
1006
1310
|
});
|
|
1007
1311
|
} catch (error) {
|
|
1008
|
-
|
|
1312
|
+
logger3.error({ err: error, url: request.url, path: c.req.path }, "Error running request handler");
|
|
1009
1313
|
throw error;
|
|
1010
1314
|
}
|
|
1011
1315
|
}).post("/agent/:agentId/connect", async (c) => {
|
|
@@ -1018,7 +1322,7 @@ function createCopilotEndpoint({ runtime, basePath }) {
|
|
|
1018
1322
|
agentId
|
|
1019
1323
|
});
|
|
1020
1324
|
} catch (error) {
|
|
1021
|
-
|
|
1325
|
+
logger3.error({ err: error, url: request.url, path: c.req.path }, "Error running request handler");
|
|
1022
1326
|
throw error;
|
|
1023
1327
|
}
|
|
1024
1328
|
}).post("/agent/:agentId/stop/:threadId", async (c) => {
|
|
@@ -1033,7 +1337,7 @@ function createCopilotEndpoint({ runtime, basePath }) {
|
|
|
1033
1337
|
threadId
|
|
1034
1338
|
});
|
|
1035
1339
|
} catch (error) {
|
|
1036
|
-
|
|
1340
|
+
logger3.error({ err: error, url: request.url, path: c.req.path }, "Error running request handler");
|
|
1037
1341
|
throw error;
|
|
1038
1342
|
}
|
|
1039
1343
|
}).get("/info", async (c) => {
|
|
@@ -1044,7 +1348,7 @@ function createCopilotEndpoint({ runtime, basePath }) {
|
|
|
1044
1348
|
request
|
|
1045
1349
|
});
|
|
1046
1350
|
} catch (error) {
|
|
1047
|
-
|
|
1351
|
+
logger3.error({ err: error, url: request.url, path: c.req.path }, "Error running request handler");
|
|
1048
1352
|
throw error;
|
|
1049
1353
|
}
|
|
1050
1354
|
}).post("/transcribe", async (c) => {
|
|
@@ -1055,19 +1359,102 @@ function createCopilotEndpoint({ runtime, basePath }) {
|
|
|
1055
1359
|
request
|
|
1056
1360
|
});
|
|
1057
1361
|
} catch (error) {
|
|
1058
|
-
|
|
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");
|
|
1059
1400
|
throw error;
|
|
1060
1401
|
}
|
|
1061
1402
|
}).notFound((c) => {
|
|
1062
1403
|
return c.json({ error: "Not found" }, 404);
|
|
1063
1404
|
});
|
|
1064
1405
|
}
|
|
1406
|
+
|
|
1407
|
+
// src/runner/index.ts
|
|
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
|
+
}
|
|
1065
1448
|
export {
|
|
1066
1449
|
AgentRunner,
|
|
1067
1450
|
CopilotRuntime,
|
|
1068
1451
|
InMemoryAgentRunner,
|
|
1069
1452
|
VERSION,
|
|
1070
1453
|
createCopilotEndpoint,
|
|
1071
|
-
|
|
1454
|
+
createFilteringThreadScopeResolver,
|
|
1455
|
+
createStrictThreadScopeResolver,
|
|
1456
|
+
filterAuthorizedResourceIds,
|
|
1457
|
+
finalizeRunEvents2 as finalizeRunEvents,
|
|
1458
|
+
validateResourceIdMatch
|
|
1072
1459
|
};
|
|
1073
1460
|
//# sourceMappingURL=index.mjs.map
|