@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.js
CHANGED
|
@@ -25,14 +25,21 @@ __export(index_exports, {
|
|
|
25
25
|
InMemoryAgentRunner: () => InMemoryAgentRunner,
|
|
26
26
|
VERSION: () => VERSION,
|
|
27
27
|
createCopilotEndpoint: () => createCopilotEndpoint,
|
|
28
|
-
|
|
28
|
+
createFilteringThreadScopeResolver: () => createFilteringThreadScopeResolver,
|
|
29
|
+
createStrictThreadScopeResolver: () => createStrictThreadScopeResolver,
|
|
30
|
+
filterAuthorizedResourceIds: () => filterAuthorizedResourceIds,
|
|
31
|
+
finalizeRunEvents: () => import_shared5.finalizeRunEvents,
|
|
32
|
+
validateResourceIdMatch: () => validateResourceIdMatch
|
|
29
33
|
});
|
|
30
34
|
module.exports = __toCommonJS(index_exports);
|
|
31
35
|
|
|
36
|
+
// src/runtime.ts
|
|
37
|
+
var import_shared2 = require("@copilotkitnext/shared");
|
|
38
|
+
|
|
32
39
|
// package.json
|
|
33
40
|
var package_default = {
|
|
34
41
|
name: "@copilotkitnext/runtime",
|
|
35
|
-
version: "0.0.
|
|
42
|
+
version: "0.0.19-threads-and-attachements.0",
|
|
36
43
|
description: "Server-side runtime package for CopilotKit2",
|
|
37
44
|
main: "dist/index.js",
|
|
38
45
|
types: "dist/index.d.ts",
|
|
@@ -68,9 +75,9 @@ var package_default = {
|
|
|
68
75
|
vitest: "^3.0.5"
|
|
69
76
|
},
|
|
70
77
|
dependencies: {
|
|
71
|
-
"@ag-ui/client": "0.0.40-alpha.
|
|
72
|
-
"@ag-ui/core": "0.0.40-alpha.
|
|
73
|
-
"@ag-ui/encoder": "0.0.40-alpha.
|
|
78
|
+
"@ag-ui/client": "0.0.40-alpha.7",
|
|
79
|
+
"@ag-ui/core": "0.0.40-alpha.7",
|
|
80
|
+
"@ag-ui/encoder": "0.0.40-alpha.7",
|
|
74
81
|
"@copilotkitnext/shared": "workspace:*",
|
|
75
82
|
hono: "^4.6.13",
|
|
76
83
|
rxjs: "7.8.1"
|
|
@@ -90,133 +97,13 @@ var AgentRunner = class {
|
|
|
90
97
|
|
|
91
98
|
// src/runner/in-memory.ts
|
|
92
99
|
var import_rxjs = require("rxjs");
|
|
93
|
-
var import_client2 = require("@ag-ui/client");
|
|
94
|
-
|
|
95
|
-
// src/runner/finalize-events.ts
|
|
96
|
-
var import_node_crypto = require("crypto");
|
|
97
100
|
var import_client = require("@ag-ui/client");
|
|
98
|
-
var
|
|
99
|
-
var defaultAbruptEndMessage = "Run ended without emitting a terminal event";
|
|
100
|
-
function finalizeRunEvents(events, options = {}) {
|
|
101
|
-
const { stopRequested = false, interruptionMessage } = options;
|
|
102
|
-
const resolvedStopMessage = interruptionMessage ?? defaultStopMessage;
|
|
103
|
-
const resolvedAbruptMessage = interruptionMessage && interruptionMessage !== defaultStopMessage ? interruptionMessage : defaultAbruptEndMessage;
|
|
104
|
-
const appended = [];
|
|
105
|
-
const openMessageIds = /* @__PURE__ */ new Set();
|
|
106
|
-
const openToolCalls = /* @__PURE__ */ new Map();
|
|
107
|
-
for (const event of events) {
|
|
108
|
-
switch (event.type) {
|
|
109
|
-
case import_client.EventType.TEXT_MESSAGE_START: {
|
|
110
|
-
const messageId = event.messageId;
|
|
111
|
-
if (typeof messageId === "string") {
|
|
112
|
-
openMessageIds.add(messageId);
|
|
113
|
-
}
|
|
114
|
-
break;
|
|
115
|
-
}
|
|
116
|
-
case import_client.EventType.TEXT_MESSAGE_END: {
|
|
117
|
-
const messageId = event.messageId;
|
|
118
|
-
if (typeof messageId === "string") {
|
|
119
|
-
openMessageIds.delete(messageId);
|
|
120
|
-
}
|
|
121
|
-
break;
|
|
122
|
-
}
|
|
123
|
-
case import_client.EventType.TOOL_CALL_START: {
|
|
124
|
-
const toolCallId = event.toolCallId;
|
|
125
|
-
if (typeof toolCallId === "string") {
|
|
126
|
-
openToolCalls.set(toolCallId, {
|
|
127
|
-
hasEnd: false,
|
|
128
|
-
hasResult: false
|
|
129
|
-
});
|
|
130
|
-
}
|
|
131
|
-
break;
|
|
132
|
-
}
|
|
133
|
-
case import_client.EventType.TOOL_CALL_END: {
|
|
134
|
-
const toolCallId = event.toolCallId;
|
|
135
|
-
const info = toolCallId ? openToolCalls.get(toolCallId) : void 0;
|
|
136
|
-
if (info) {
|
|
137
|
-
info.hasEnd = true;
|
|
138
|
-
}
|
|
139
|
-
break;
|
|
140
|
-
}
|
|
141
|
-
case import_client.EventType.TOOL_CALL_RESULT: {
|
|
142
|
-
const toolCallId = event.toolCallId;
|
|
143
|
-
const info = toolCallId ? openToolCalls.get(toolCallId) : void 0;
|
|
144
|
-
if (info) {
|
|
145
|
-
info.hasResult = true;
|
|
146
|
-
}
|
|
147
|
-
break;
|
|
148
|
-
}
|
|
149
|
-
default:
|
|
150
|
-
break;
|
|
151
|
-
}
|
|
152
|
-
}
|
|
153
|
-
const hasRunFinished = events.some((event) => event.type === import_client.EventType.RUN_FINISHED);
|
|
154
|
-
const hasRunError = events.some((event) => event.type === import_client.EventType.RUN_ERROR);
|
|
155
|
-
const hasTerminalEvent = hasRunFinished || hasRunError;
|
|
156
|
-
const terminalEventMissing = !hasTerminalEvent;
|
|
157
|
-
for (const messageId of openMessageIds) {
|
|
158
|
-
const endEvent = {
|
|
159
|
-
type: import_client.EventType.TEXT_MESSAGE_END,
|
|
160
|
-
messageId
|
|
161
|
-
};
|
|
162
|
-
events.push(endEvent);
|
|
163
|
-
appended.push(endEvent);
|
|
164
|
-
}
|
|
165
|
-
for (const [toolCallId, info] of openToolCalls) {
|
|
166
|
-
if (!info.hasEnd) {
|
|
167
|
-
const endEvent = {
|
|
168
|
-
type: import_client.EventType.TOOL_CALL_END,
|
|
169
|
-
toolCallId
|
|
170
|
-
};
|
|
171
|
-
events.push(endEvent);
|
|
172
|
-
appended.push(endEvent);
|
|
173
|
-
}
|
|
174
|
-
if (terminalEventMissing && !info.hasResult) {
|
|
175
|
-
const resultEvent = {
|
|
176
|
-
type: import_client.EventType.TOOL_CALL_RESULT,
|
|
177
|
-
toolCallId,
|
|
178
|
-
messageId: `${toolCallId ?? (0, import_node_crypto.randomUUID)()}-result`,
|
|
179
|
-
role: "tool",
|
|
180
|
-
content: JSON.stringify(
|
|
181
|
-
stopRequested ? {
|
|
182
|
-
status: "stopped",
|
|
183
|
-
reason: "stop_requested",
|
|
184
|
-
message: resolvedStopMessage
|
|
185
|
-
} : {
|
|
186
|
-
status: "error",
|
|
187
|
-
reason: "missing_terminal_event",
|
|
188
|
-
message: resolvedAbruptMessage
|
|
189
|
-
}
|
|
190
|
-
)
|
|
191
|
-
};
|
|
192
|
-
events.push(resultEvent);
|
|
193
|
-
appended.push(resultEvent);
|
|
194
|
-
}
|
|
195
|
-
}
|
|
196
|
-
if (terminalEventMissing) {
|
|
197
|
-
if (stopRequested) {
|
|
198
|
-
const finishedEvent = {
|
|
199
|
-
type: import_client.EventType.RUN_FINISHED
|
|
200
|
-
};
|
|
201
|
-
events.push(finishedEvent);
|
|
202
|
-
appended.push(finishedEvent);
|
|
203
|
-
} else {
|
|
204
|
-
const errorEvent = {
|
|
205
|
-
type: import_client.EventType.RUN_ERROR,
|
|
206
|
-
message: resolvedAbruptMessage,
|
|
207
|
-
code: "INCOMPLETE_STREAM"
|
|
208
|
-
};
|
|
209
|
-
events.push(errorEvent);
|
|
210
|
-
appended.push(errorEvent);
|
|
211
|
-
}
|
|
212
|
-
}
|
|
213
|
-
return appended;
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
// src/runner/in-memory.ts
|
|
101
|
+
var import_shared = require("@copilotkitnext/shared");
|
|
217
102
|
var InMemoryEventStore = class {
|
|
218
|
-
constructor(threadId) {
|
|
103
|
+
constructor(threadId, resourceIds, properties) {
|
|
219
104
|
this.threadId = threadId;
|
|
105
|
+
this.resourceIds = resourceIds;
|
|
106
|
+
this.properties = properties;
|
|
220
107
|
}
|
|
221
108
|
/** The subject that current consumers subscribe to. */
|
|
222
109
|
subject = null;
|
|
@@ -236,11 +123,41 @@ var InMemoryEventStore = class {
|
|
|
236
123
|
currentEvents = null;
|
|
237
124
|
};
|
|
238
125
|
var GLOBAL_STORE = /* @__PURE__ */ new Map();
|
|
126
|
+
function matchesScope(store, scope) {
|
|
127
|
+
if (scope === void 0 || scope === null) {
|
|
128
|
+
return true;
|
|
129
|
+
}
|
|
130
|
+
const scopeIds = Array.isArray(scope.resourceId) ? scope.resourceId : [scope.resourceId];
|
|
131
|
+
return scopeIds.some((scopeId) => store.resourceIds.includes(scopeId));
|
|
132
|
+
}
|
|
239
133
|
var InMemoryAgentRunner = class extends AgentRunner {
|
|
240
134
|
run(request) {
|
|
241
135
|
let existingStore = GLOBAL_STORE.get(request.threadId);
|
|
242
|
-
if (!existingStore) {
|
|
243
|
-
|
|
136
|
+
if (!existingStore && request.scope === null) {
|
|
137
|
+
throw new Error(
|
|
138
|
+
"Cannot create thread with null scope. Admin users must specify an explicit resourceId for the thread owner."
|
|
139
|
+
);
|
|
140
|
+
}
|
|
141
|
+
let resourceIds;
|
|
142
|
+
if (request.scope === void 0) {
|
|
143
|
+
resourceIds = ["global"];
|
|
144
|
+
} else if (request.scope === null) {
|
|
145
|
+
resourceIds = [];
|
|
146
|
+
} else if (Array.isArray(request.scope.resourceId)) {
|
|
147
|
+
if (request.scope.resourceId.length === 0) {
|
|
148
|
+
throw new Error("Invalid scope: resourceId array cannot be empty");
|
|
149
|
+
}
|
|
150
|
+
resourceIds = request.scope.resourceId;
|
|
151
|
+
} else {
|
|
152
|
+
resourceIds = [request.scope.resourceId];
|
|
153
|
+
}
|
|
154
|
+
if (existingStore) {
|
|
155
|
+
if (request.scope !== null && !matchesScope(existingStore, request.scope)) {
|
|
156
|
+
throw new Error("Unauthorized: Cannot run on thread owned by different resource");
|
|
157
|
+
}
|
|
158
|
+
resourceIds = existingStore.resourceIds;
|
|
159
|
+
} else {
|
|
160
|
+
existingStore = new InMemoryEventStore(request.threadId, resourceIds, request.scope?.properties);
|
|
244
161
|
GLOBAL_STORE.set(request.threadId, existingStore);
|
|
245
162
|
}
|
|
246
163
|
const store = existingStore;
|
|
@@ -260,7 +177,7 @@ var InMemoryAgentRunner = class extends AgentRunner {
|
|
|
260
177
|
if ("messageId" in event && typeof event.messageId === "string") {
|
|
261
178
|
historicMessageIds.add(event.messageId);
|
|
262
179
|
}
|
|
263
|
-
if (event.type ===
|
|
180
|
+
if (event.type === import_client.EventType.RUN_STARTED) {
|
|
264
181
|
const runStarted = event;
|
|
265
182
|
const messages = runStarted.input?.messages ?? [];
|
|
266
183
|
for (const message of messages) {
|
|
@@ -281,12 +198,10 @@ var InMemoryAgentRunner = class extends AgentRunner {
|
|
|
281
198
|
await request.agent.runAgent(request.input, {
|
|
282
199
|
onEvent: ({ event }) => {
|
|
283
200
|
let processedEvent = event;
|
|
284
|
-
if (event.type ===
|
|
201
|
+
if (event.type === import_client.EventType.RUN_STARTED) {
|
|
285
202
|
const runStartedEvent = event;
|
|
286
203
|
if (!runStartedEvent.input) {
|
|
287
|
-
const sanitizedMessages = request.input.messages ? request.input.messages.filter(
|
|
288
|
-
(message) => !historicMessageIds.has(message.id)
|
|
289
|
-
) : void 0;
|
|
204
|
+
const sanitizedMessages = request.input.messages ? request.input.messages.filter((message) => !historicMessageIds.has(message.id)) : void 0;
|
|
290
205
|
const updatedInput = {
|
|
291
206
|
...request.input,
|
|
292
207
|
...sanitizedMessages !== void 0 ? { messages: sanitizedMessages } : {}
|
|
@@ -316,7 +231,7 @@ var InMemoryAgentRunner = class extends AgentRunner {
|
|
|
316
231
|
}
|
|
317
232
|
}
|
|
318
233
|
});
|
|
319
|
-
const appendedEvents = finalizeRunEvents(currentRunEvents, {
|
|
234
|
+
const appendedEvents = (0, import_shared.finalizeRunEvents)(currentRunEvents, {
|
|
320
235
|
stopRequested: store.stopRequested
|
|
321
236
|
});
|
|
322
237
|
for (const event of appendedEvents) {
|
|
@@ -324,7 +239,7 @@ var InMemoryAgentRunner = class extends AgentRunner {
|
|
|
324
239
|
nextSubject.next(event);
|
|
325
240
|
}
|
|
326
241
|
if (store.currentRunId) {
|
|
327
|
-
const compactedEvents = (0,
|
|
242
|
+
const compactedEvents = (0, import_client.compactEvents)(currentRunEvents);
|
|
328
243
|
store.historicRuns.push({
|
|
329
244
|
threadId: request.threadId,
|
|
330
245
|
runId: store.currentRunId,
|
|
@@ -342,7 +257,7 @@ var InMemoryAgentRunner = class extends AgentRunner {
|
|
|
342
257
|
runSubject.complete();
|
|
343
258
|
nextSubject.complete();
|
|
344
259
|
} catch {
|
|
345
|
-
const appendedEvents = finalizeRunEvents(currentRunEvents, {
|
|
260
|
+
const appendedEvents = (0, import_shared.finalizeRunEvents)(currentRunEvents, {
|
|
346
261
|
stopRequested: store.stopRequested
|
|
347
262
|
});
|
|
348
263
|
for (const event of appendedEvents) {
|
|
@@ -350,7 +265,7 @@ var InMemoryAgentRunner = class extends AgentRunner {
|
|
|
350
265
|
nextSubject.next(event);
|
|
351
266
|
}
|
|
352
267
|
if (store.currentRunId && currentRunEvents.length > 0) {
|
|
353
|
-
const compactedEvents = (0,
|
|
268
|
+
const compactedEvents = (0, import_client.compactEvents)(currentRunEvents);
|
|
354
269
|
store.historicRuns.push({
|
|
355
270
|
threadId: request.threadId,
|
|
356
271
|
runId: store.currentRunId,
|
|
@@ -383,7 +298,7 @@ var InMemoryAgentRunner = class extends AgentRunner {
|
|
|
383
298
|
connect(request) {
|
|
384
299
|
const store = GLOBAL_STORE.get(request.threadId);
|
|
385
300
|
const connectionSubject = new import_rxjs.ReplaySubject(Infinity);
|
|
386
|
-
if (!store) {
|
|
301
|
+
if (!store || !matchesScope(store, request.scope)) {
|
|
387
302
|
connectionSubject.complete();
|
|
388
303
|
return connectionSubject.asObservable();
|
|
389
304
|
}
|
|
@@ -391,7 +306,7 @@ var InMemoryAgentRunner = class extends AgentRunner {
|
|
|
391
306
|
for (const run of store.historicRuns) {
|
|
392
307
|
allHistoricEvents.push(...run.events);
|
|
393
308
|
}
|
|
394
|
-
const compactedEvents = (0,
|
|
309
|
+
const compactedEvents = (0, import_client.compactEvents)(allHistoricEvents);
|
|
395
310
|
const emittedMessageIds = /* @__PURE__ */ new Set();
|
|
396
311
|
for (const event of compactedEvents) {
|
|
397
312
|
connectionSubject.next(event);
|
|
@@ -419,54 +334,256 @@ var InMemoryAgentRunner = class extends AgentRunner {
|
|
|
419
334
|
const store = GLOBAL_STORE.get(request.threadId);
|
|
420
335
|
return Promise.resolve(store?.isRunning ?? false);
|
|
421
336
|
}
|
|
422
|
-
stop(request) {
|
|
337
|
+
async stop(request) {
|
|
423
338
|
const store = GLOBAL_STORE.get(request.threadId);
|
|
424
|
-
if (!store
|
|
425
|
-
return
|
|
426
|
-
}
|
|
427
|
-
if (store.stopRequested) {
|
|
428
|
-
return Promise.resolve(false);
|
|
339
|
+
if (!store) {
|
|
340
|
+
return false;
|
|
429
341
|
}
|
|
430
|
-
store.
|
|
431
|
-
|
|
432
|
-
const agent = store.agent;
|
|
433
|
-
if (!agent) {
|
|
434
|
-
store.stopRequested = false;
|
|
342
|
+
if (store.isRunning) {
|
|
343
|
+
store.stopRequested = true;
|
|
435
344
|
store.isRunning = false;
|
|
436
|
-
|
|
345
|
+
const agent = store.agent;
|
|
346
|
+
try {
|
|
347
|
+
if (agent) {
|
|
348
|
+
agent.abortRun();
|
|
349
|
+
return true;
|
|
350
|
+
}
|
|
351
|
+
return false;
|
|
352
|
+
} catch (error) {
|
|
353
|
+
console.warn("Failed to abort in-memory runner:", error);
|
|
354
|
+
store.stopRequested = false;
|
|
355
|
+
store.isRunning = true;
|
|
356
|
+
return false;
|
|
357
|
+
}
|
|
437
358
|
}
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
359
|
+
return false;
|
|
360
|
+
}
|
|
361
|
+
async listThreads(request) {
|
|
362
|
+
const limit = request.limit ?? 50;
|
|
363
|
+
const offset = request.offset ?? 0;
|
|
364
|
+
if (request.scope !== void 0 && request.scope !== null) {
|
|
365
|
+
const scopeIds = Array.isArray(request.scope.resourceId) ? request.scope.resourceId : [request.scope.resourceId];
|
|
366
|
+
if (scopeIds.length === 0) {
|
|
367
|
+
return { threads: [], total: 0 };
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
const threadInfos = [];
|
|
371
|
+
for (const [threadId, store] of GLOBAL_STORE.entries()) {
|
|
372
|
+
if (threadId.includes("-suggestions-")) {
|
|
373
|
+
continue;
|
|
374
|
+
}
|
|
375
|
+
if (!matchesScope(store, request.scope)) {
|
|
376
|
+
continue;
|
|
377
|
+
}
|
|
378
|
+
if (store.historicRuns.length === 0) {
|
|
379
|
+
continue;
|
|
380
|
+
}
|
|
381
|
+
const firstRun = store.historicRuns[0];
|
|
382
|
+
const lastRun = store.historicRuns[store.historicRuns.length - 1];
|
|
383
|
+
if (!firstRun || !lastRun) {
|
|
384
|
+
continue;
|
|
385
|
+
}
|
|
386
|
+
threadInfos.push({
|
|
387
|
+
threadId,
|
|
388
|
+
createdAt: firstRun.createdAt,
|
|
389
|
+
lastActivityAt: lastRun.createdAt,
|
|
390
|
+
store
|
|
391
|
+
});
|
|
392
|
+
}
|
|
393
|
+
threadInfos.sort((a, b) => b.lastActivityAt - a.lastActivityAt);
|
|
394
|
+
const total = threadInfos.length;
|
|
395
|
+
const paginatedInfos = threadInfos.slice(offset, offset + limit);
|
|
396
|
+
const threads = paginatedInfos.map((info) => {
|
|
397
|
+
let firstMessage;
|
|
398
|
+
const firstRun = info.store.historicRuns[0];
|
|
399
|
+
if (firstRun) {
|
|
400
|
+
const textContent = firstRun.events.find((e) => e.type === import_client.EventType.TEXT_MESSAGE_CONTENT);
|
|
401
|
+
if (textContent?.delta) {
|
|
402
|
+
firstMessage = textContent.delta.substring(0, 100);
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
const messageIds = /* @__PURE__ */ new Set();
|
|
406
|
+
for (const run of info.store.historicRuns) {
|
|
407
|
+
for (const event of run.events) {
|
|
408
|
+
if ("messageId" in event && typeof event.messageId === "string") {
|
|
409
|
+
messageIds.add(event.messageId);
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
return {
|
|
414
|
+
threadId: info.threadId,
|
|
415
|
+
createdAt: info.createdAt,
|
|
416
|
+
lastActivityAt: info.lastActivityAt,
|
|
417
|
+
isRunning: info.store.isRunning,
|
|
418
|
+
messageCount: messageIds.size,
|
|
419
|
+
firstMessage,
|
|
420
|
+
resourceId: info.store.resourceIds[0] || "unknown",
|
|
421
|
+
// Return first for backward compatibility
|
|
422
|
+
properties: info.store.properties
|
|
423
|
+
};
|
|
424
|
+
});
|
|
425
|
+
return { threads, total };
|
|
426
|
+
}
|
|
427
|
+
async getThreadMetadata(threadId, scope) {
|
|
428
|
+
const store = GLOBAL_STORE.get(threadId);
|
|
429
|
+
if (!store || !matchesScope(store, scope) || store.historicRuns.length === 0) {
|
|
430
|
+
return null;
|
|
431
|
+
}
|
|
432
|
+
const firstRun = store.historicRuns[0];
|
|
433
|
+
const lastRun = store.historicRuns[store.historicRuns.length - 1];
|
|
434
|
+
if (!firstRun || !lastRun) {
|
|
435
|
+
return null;
|
|
436
|
+
}
|
|
437
|
+
let firstMessage;
|
|
438
|
+
const textContent = firstRun.events.find((e) => e.type === import_client.EventType.TEXT_MESSAGE_CONTENT);
|
|
439
|
+
if (textContent?.delta) {
|
|
440
|
+
firstMessage = textContent.delta.substring(0, 100);
|
|
441
|
+
}
|
|
442
|
+
const messageIds = /* @__PURE__ */ new Set();
|
|
443
|
+
for (const run of store.historicRuns) {
|
|
444
|
+
for (const event of run.events) {
|
|
445
|
+
if ("messageId" in event && typeof event.messageId === "string") {
|
|
446
|
+
messageIds.add(event.messageId);
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
return {
|
|
451
|
+
threadId,
|
|
452
|
+
createdAt: firstRun.createdAt,
|
|
453
|
+
lastActivityAt: lastRun.createdAt,
|
|
454
|
+
isRunning: store.isRunning,
|
|
455
|
+
messageCount: messageIds.size,
|
|
456
|
+
firstMessage,
|
|
457
|
+
resourceId: store.resourceIds[0] || "unknown",
|
|
458
|
+
// Return first for backward compatibility
|
|
459
|
+
properties: store.properties
|
|
460
|
+
};
|
|
461
|
+
}
|
|
462
|
+
async deleteThread(threadId, scope) {
|
|
463
|
+
const store = GLOBAL_STORE.get(threadId);
|
|
464
|
+
if (!store || !matchesScope(store, scope)) {
|
|
465
|
+
return;
|
|
466
|
+
}
|
|
467
|
+
if (store.agent) {
|
|
468
|
+
try {
|
|
469
|
+
store.agent.abortRun();
|
|
470
|
+
} catch (error) {
|
|
471
|
+
console.warn("Failed to abort agent during thread deletion:", error);
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
store.subject?.complete();
|
|
475
|
+
GLOBAL_STORE.delete(threadId);
|
|
476
|
+
}
|
|
477
|
+
/**
|
|
478
|
+
* Clear all threads from the global store (for testing purposes only)
|
|
479
|
+
* @internal
|
|
480
|
+
*/
|
|
481
|
+
clearAllThreads() {
|
|
482
|
+
for (const [threadId, store] of GLOBAL_STORE.entries()) {
|
|
483
|
+
if (store.agent) {
|
|
484
|
+
try {
|
|
485
|
+
store.agent.abortRun();
|
|
486
|
+
} catch (error) {
|
|
487
|
+
console.warn("Failed to abort agent during clearAllThreads:", error);
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
store.subject?.complete();
|
|
491
|
+
GLOBAL_STORE.delete(threadId);
|
|
446
492
|
}
|
|
447
493
|
}
|
|
448
494
|
};
|
|
449
495
|
|
|
450
496
|
// src/runtime.ts
|
|
451
497
|
var VERSION = package_default.version;
|
|
452
|
-
var CopilotRuntime = class {
|
|
498
|
+
var CopilotRuntime = class _CopilotRuntime {
|
|
499
|
+
/**
|
|
500
|
+
* Built-in global scope for single-user apps or demos.
|
|
501
|
+
*
|
|
502
|
+
* All threads are globally accessible when using this scope.
|
|
503
|
+
*
|
|
504
|
+
* @example
|
|
505
|
+
* ```typescript
|
|
506
|
+
* new CopilotRuntime({
|
|
507
|
+
* agents: { myAgent },
|
|
508
|
+
* resolveThreadsScope: CopilotRuntime.GLOBAL_SCOPE,
|
|
509
|
+
* suppressResourceIdWarning: true
|
|
510
|
+
* });
|
|
511
|
+
* ```
|
|
512
|
+
*/
|
|
513
|
+
static GLOBAL_SCOPE = async (context) => ({
|
|
514
|
+
resourceId: "global"
|
|
515
|
+
});
|
|
516
|
+
/**
|
|
517
|
+
* Parses the client-declared resource ID(s) from the request header.
|
|
518
|
+
*
|
|
519
|
+
* This is a utility method used internally by handlers to extract the
|
|
520
|
+
* `X-CopilotKit-Resource-ID` header sent by the client via `CopilotKitProvider`.
|
|
521
|
+
*
|
|
522
|
+
* **You typically don't need to call this directly** - it's automatically called
|
|
523
|
+
* by the runtime handlers and passed to your `resolveThreadsScope` function as
|
|
524
|
+
* the `clientDeclared` parameter.
|
|
525
|
+
*
|
|
526
|
+
* @param request - The incoming HTTP request
|
|
527
|
+
* @returns The parsed resource ID(s), or undefined if header is missing
|
|
528
|
+
* - Returns a string if single ID
|
|
529
|
+
* - Returns an array if multiple comma-separated IDs
|
|
530
|
+
* - Returns undefined if header not present
|
|
531
|
+
*
|
|
532
|
+
* @example
|
|
533
|
+
* ```typescript
|
|
534
|
+
* // Automatically used internally:
|
|
535
|
+
* const clientDeclared = CopilotRuntime.parseClientDeclaredResourceId(request);
|
|
536
|
+
* const scope = await runtime.resolveThreadsScope({ request, clientDeclared });
|
|
537
|
+
* ```
|
|
538
|
+
*/
|
|
539
|
+
static parseClientDeclaredResourceId(request) {
|
|
540
|
+
const header = request.headers.get("X-CopilotKit-Resource-ID");
|
|
541
|
+
if (!header) {
|
|
542
|
+
return void 0;
|
|
543
|
+
}
|
|
544
|
+
const values = header.split(",").map((v) => decodeURIComponent(v.trim()));
|
|
545
|
+
return values.length === 1 ? values[0] : values;
|
|
546
|
+
}
|
|
453
547
|
agents;
|
|
454
548
|
transcriptionService;
|
|
455
549
|
beforeRequestMiddleware;
|
|
456
550
|
afterRequestMiddleware;
|
|
457
551
|
runner;
|
|
552
|
+
resolveThreadsScope;
|
|
553
|
+
suppressResourceIdWarning;
|
|
458
554
|
constructor({
|
|
459
555
|
agents,
|
|
460
556
|
transcriptionService,
|
|
461
557
|
beforeRequestMiddleware,
|
|
462
558
|
afterRequestMiddleware,
|
|
463
|
-
runner
|
|
559
|
+
runner,
|
|
560
|
+
resolveThreadsScope,
|
|
561
|
+
suppressResourceIdWarning = false
|
|
464
562
|
}) {
|
|
465
563
|
this.agents = agents;
|
|
466
564
|
this.transcriptionService = transcriptionService;
|
|
467
565
|
this.beforeRequestMiddleware = beforeRequestMiddleware;
|
|
468
566
|
this.afterRequestMiddleware = afterRequestMiddleware;
|
|
469
567
|
this.runner = runner ?? new InMemoryAgentRunner();
|
|
568
|
+
this.resolveThreadsScope = resolveThreadsScope ?? _CopilotRuntime.GLOBAL_SCOPE;
|
|
569
|
+
this.suppressResourceIdWarning = suppressResourceIdWarning;
|
|
570
|
+
if (!resolveThreadsScope && !suppressResourceIdWarning) {
|
|
571
|
+
this.logGlobalScopeWarning();
|
|
572
|
+
}
|
|
573
|
+
}
|
|
574
|
+
logGlobalScopeWarning() {
|
|
575
|
+
const isProduction = process.env.NODE_ENV === "production";
|
|
576
|
+
if (isProduction) {
|
|
577
|
+
import_shared2.logger.error({
|
|
578
|
+
msg: "CopilotKit Security Warning: GLOBAL_SCOPE in production",
|
|
579
|
+
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"
|
|
580
|
+
});
|
|
581
|
+
} else {
|
|
582
|
+
import_shared2.logger.warn({
|
|
583
|
+
msg: "CopilotKit: Using GLOBAL_SCOPE",
|
|
584
|
+
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"
|
|
585
|
+
});
|
|
586
|
+
}
|
|
470
587
|
}
|
|
471
588
|
};
|
|
472
589
|
|
|
@@ -475,13 +592,9 @@ var import_hono = require("hono");
|
|
|
475
592
|
var import_cors = require("hono/cors");
|
|
476
593
|
|
|
477
594
|
// src/handlers/handle-run.ts
|
|
478
|
-
var
|
|
595
|
+
var import_client2 = require("@ag-ui/client");
|
|
479
596
|
var import_encoder = require("@ag-ui/encoder");
|
|
480
|
-
async function handleRunAgent({
|
|
481
|
-
runtime,
|
|
482
|
-
request,
|
|
483
|
-
agentId
|
|
484
|
-
}) {
|
|
597
|
+
async function handleRunAgent({ runtime, request, agentId }) {
|
|
485
598
|
try {
|
|
486
599
|
const agents = await runtime.agents;
|
|
487
600
|
if (!agents[agentId]) {
|
|
@@ -518,11 +631,28 @@ async function handleRunAgent({
|
|
|
518
631
|
const writer = stream.writable.getWriter();
|
|
519
632
|
const encoder = new import_encoder.EventEncoder();
|
|
520
633
|
let streamClosed = false;
|
|
634
|
+
let subscription;
|
|
635
|
+
let abortListener;
|
|
636
|
+
const cleanupAbortListener = () => {
|
|
637
|
+
if (abortListener) {
|
|
638
|
+
request.signal.removeEventListener("abort", abortListener);
|
|
639
|
+
abortListener = void 0;
|
|
640
|
+
}
|
|
641
|
+
};
|
|
642
|
+
const closeStream = async () => {
|
|
643
|
+
if (!streamClosed) {
|
|
644
|
+
try {
|
|
645
|
+
await writer.close();
|
|
646
|
+
} catch {
|
|
647
|
+
}
|
|
648
|
+
streamClosed = true;
|
|
649
|
+
}
|
|
650
|
+
};
|
|
521
651
|
(async () => {
|
|
522
652
|
let input;
|
|
523
653
|
try {
|
|
524
654
|
const requestBody = await request.json();
|
|
525
|
-
input =
|
|
655
|
+
input = import_client2.RunAgentInputSchema.parse(requestBody);
|
|
526
656
|
} catch {
|
|
527
657
|
return new Response(
|
|
528
658
|
JSON.stringify({
|
|
@@ -531,13 +661,26 @@ async function handleRunAgent({
|
|
|
531
661
|
{ status: 400 }
|
|
532
662
|
);
|
|
533
663
|
}
|
|
664
|
+
const clientDeclared = CopilotRuntime["parseClientDeclaredResourceId"](request);
|
|
665
|
+
const scope = await runtime.resolveThreadsScope({ request, clientDeclared });
|
|
666
|
+
if (scope === void 0) {
|
|
667
|
+
throw new Error("Unauthorized: No resource scope provided");
|
|
668
|
+
}
|
|
534
669
|
agent.setMessages(input.messages);
|
|
535
670
|
agent.setState(input.state);
|
|
536
671
|
agent.threadId = input.threadId;
|
|
537
|
-
|
|
672
|
+
const stopRunner = async () => {
|
|
673
|
+
try {
|
|
674
|
+
await runtime.runner.stop({ threadId: input.threadId });
|
|
675
|
+
} catch (stopError) {
|
|
676
|
+
console.error("Error stopping runner:", stopError);
|
|
677
|
+
}
|
|
678
|
+
};
|
|
679
|
+
subscription = runtime.runner.run({
|
|
538
680
|
threadId: input.threadId,
|
|
539
681
|
agent,
|
|
540
|
-
input
|
|
682
|
+
input,
|
|
683
|
+
scope
|
|
541
684
|
}).subscribe({
|
|
542
685
|
next: async (event) => {
|
|
543
686
|
if (!request.signal.aborted && !streamClosed) {
|
|
@@ -552,42 +695,39 @@ async function handleRunAgent({
|
|
|
552
695
|
},
|
|
553
696
|
error: async (error) => {
|
|
554
697
|
console.error("Error running agent:", error);
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
await writer.close();
|
|
558
|
-
streamClosed = true;
|
|
559
|
-
} catch {
|
|
560
|
-
}
|
|
561
|
-
}
|
|
698
|
+
cleanupAbortListener();
|
|
699
|
+
await closeStream();
|
|
562
700
|
},
|
|
563
701
|
complete: async () => {
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
await writer.close();
|
|
567
|
-
streamClosed = true;
|
|
568
|
-
} catch {
|
|
569
|
-
}
|
|
570
|
-
}
|
|
702
|
+
cleanupAbortListener();
|
|
703
|
+
await closeStream();
|
|
571
704
|
}
|
|
572
705
|
});
|
|
706
|
+
const handleAbort = () => {
|
|
707
|
+
subscription?.unsubscribe();
|
|
708
|
+
subscription = void 0;
|
|
709
|
+
cleanupAbortListener();
|
|
710
|
+
void stopRunner();
|
|
711
|
+
void closeStream();
|
|
712
|
+
};
|
|
713
|
+
if (request.signal.aborted) {
|
|
714
|
+
handleAbort();
|
|
715
|
+
} else {
|
|
716
|
+
abortListener = handleAbort;
|
|
717
|
+
request.signal.addEventListener("abort", abortListener);
|
|
718
|
+
}
|
|
573
719
|
})().catch((error) => {
|
|
574
720
|
console.error("Error running agent:", error);
|
|
575
|
-
console.error(
|
|
576
|
-
"Error stack:",
|
|
577
|
-
error instanceof Error ? error.stack : "No stack trace"
|
|
578
|
-
);
|
|
721
|
+
console.error("Error stack:", error instanceof Error ? error.stack : "No stack trace");
|
|
579
722
|
console.error("Error details:", {
|
|
580
723
|
name: error instanceof Error ? error.name : "Unknown",
|
|
581
724
|
message: error instanceof Error ? error.message : String(error),
|
|
582
725
|
cause: error instanceof Error ? error.cause : void 0
|
|
583
726
|
});
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
} catch {
|
|
589
|
-
}
|
|
590
|
-
}
|
|
727
|
+
subscription?.unsubscribe();
|
|
728
|
+
subscription = void 0;
|
|
729
|
+
cleanupAbortListener();
|
|
730
|
+
void closeStream();
|
|
591
731
|
});
|
|
592
732
|
return new Response(stream.readable, {
|
|
593
733
|
status: 200,
|
|
@@ -599,10 +739,7 @@ async function handleRunAgent({
|
|
|
599
739
|
});
|
|
600
740
|
} catch (error) {
|
|
601
741
|
console.error("Error running agent:", error);
|
|
602
|
-
console.error(
|
|
603
|
-
"Error stack:",
|
|
604
|
-
error instanceof Error ? error.stack : "No stack trace"
|
|
605
|
-
);
|
|
742
|
+
console.error("Error stack:", error instanceof Error ? error.stack : "No stack trace");
|
|
606
743
|
console.error("Error details:", {
|
|
607
744
|
name: error instanceof Error ? error.name : "Unknown",
|
|
608
745
|
message: error instanceof Error ? error.message : String(error),
|
|
@@ -760,10 +897,10 @@ async function handleTranscribe({
|
|
|
760
897
|
}
|
|
761
898
|
|
|
762
899
|
// src/endpoint.ts
|
|
763
|
-
var
|
|
900
|
+
var import_shared4 = require("@copilotkitnext/shared");
|
|
764
901
|
|
|
765
902
|
// src/middleware.ts
|
|
766
|
-
var
|
|
903
|
+
var import_shared3 = require("@copilotkitnext/shared");
|
|
767
904
|
async function callBeforeRequestMiddleware({
|
|
768
905
|
runtime,
|
|
769
906
|
request,
|
|
@@ -774,7 +911,7 @@ async function callBeforeRequestMiddleware({
|
|
|
774
911
|
if (typeof mw === "function") {
|
|
775
912
|
return mw({ runtime, request, path });
|
|
776
913
|
}
|
|
777
|
-
|
|
914
|
+
import_shared3.logger.warn({ mw }, "Unsupported beforeRequestMiddleware value \u2013 skipped");
|
|
778
915
|
return;
|
|
779
916
|
}
|
|
780
917
|
async function callAfterRequestMiddleware({
|
|
@@ -787,17 +924,13 @@ async function callAfterRequestMiddleware({
|
|
|
787
924
|
if (typeof mw === "function") {
|
|
788
925
|
return mw({ runtime, response, path });
|
|
789
926
|
}
|
|
790
|
-
|
|
927
|
+
import_shared3.logger.warn({ mw }, "Unsupported afterRequestMiddleware value \u2013 skipped");
|
|
791
928
|
}
|
|
792
929
|
|
|
793
930
|
// src/handlers/handle-connect.ts
|
|
794
|
-
var
|
|
931
|
+
var import_client3 = require("@ag-ui/client");
|
|
795
932
|
var import_encoder2 = require("@ag-ui/encoder");
|
|
796
|
-
async function handleConnectAgent({
|
|
797
|
-
runtime,
|
|
798
|
-
request,
|
|
799
|
-
agentId
|
|
800
|
-
}) {
|
|
933
|
+
async function handleConnectAgent({ runtime, request, agentId }) {
|
|
801
934
|
try {
|
|
802
935
|
const agents = await runtime.agents;
|
|
803
936
|
if (!agents[agentId]) {
|
|
@@ -816,11 +949,28 @@ async function handleConnectAgent({
|
|
|
816
949
|
const writer = stream.writable.getWriter();
|
|
817
950
|
const encoder = new import_encoder2.EventEncoder();
|
|
818
951
|
let streamClosed = false;
|
|
952
|
+
let subscription;
|
|
953
|
+
let abortListener;
|
|
954
|
+
const cleanupAbortListener = () => {
|
|
955
|
+
if (abortListener) {
|
|
956
|
+
request.signal.removeEventListener("abort", abortListener);
|
|
957
|
+
abortListener = void 0;
|
|
958
|
+
}
|
|
959
|
+
};
|
|
960
|
+
const closeStream = async () => {
|
|
961
|
+
if (!streamClosed) {
|
|
962
|
+
try {
|
|
963
|
+
await writer.close();
|
|
964
|
+
} catch {
|
|
965
|
+
}
|
|
966
|
+
streamClosed = true;
|
|
967
|
+
}
|
|
968
|
+
};
|
|
819
969
|
(async () => {
|
|
820
970
|
let input;
|
|
821
971
|
try {
|
|
822
972
|
const requestBody = await request.json();
|
|
823
|
-
input =
|
|
973
|
+
input = import_client3.RunAgentInputSchema.parse(requestBody);
|
|
824
974
|
} catch {
|
|
825
975
|
return new Response(
|
|
826
976
|
JSON.stringify({
|
|
@@ -829,8 +979,14 @@ async function handleConnectAgent({
|
|
|
829
979
|
{ status: 400 }
|
|
830
980
|
);
|
|
831
981
|
}
|
|
832
|
-
|
|
833
|
-
|
|
982
|
+
const clientDeclared = CopilotRuntime["parseClientDeclaredResourceId"](request);
|
|
983
|
+
const scope = await runtime.resolveThreadsScope({ request, clientDeclared });
|
|
984
|
+
if (scope === void 0) {
|
|
985
|
+
throw new Error("Unauthorized: No resource scope provided");
|
|
986
|
+
}
|
|
987
|
+
subscription = runtime.runner.connect({
|
|
988
|
+
threadId: input.threadId,
|
|
989
|
+
scope
|
|
834
990
|
}).subscribe({
|
|
835
991
|
next: async (event) => {
|
|
836
992
|
if (!request.signal.aborted && !streamClosed) {
|
|
@@ -845,42 +1001,38 @@ async function handleConnectAgent({
|
|
|
845
1001
|
},
|
|
846
1002
|
error: async (error) => {
|
|
847
1003
|
console.error("Error running agent:", error);
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
await writer.close();
|
|
851
|
-
streamClosed = true;
|
|
852
|
-
} catch {
|
|
853
|
-
}
|
|
854
|
-
}
|
|
1004
|
+
cleanupAbortListener();
|
|
1005
|
+
await closeStream();
|
|
855
1006
|
},
|
|
856
1007
|
complete: async () => {
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
await writer.close();
|
|
860
|
-
streamClosed = true;
|
|
861
|
-
} catch {
|
|
862
|
-
}
|
|
863
|
-
}
|
|
1008
|
+
cleanupAbortListener();
|
|
1009
|
+
await closeStream();
|
|
864
1010
|
}
|
|
865
1011
|
});
|
|
1012
|
+
const handleAbort = () => {
|
|
1013
|
+
subscription?.unsubscribe();
|
|
1014
|
+
subscription = void 0;
|
|
1015
|
+
cleanupAbortListener();
|
|
1016
|
+
void closeStream();
|
|
1017
|
+
};
|
|
1018
|
+
if (request.signal.aborted) {
|
|
1019
|
+
handleAbort();
|
|
1020
|
+
} else {
|
|
1021
|
+
abortListener = handleAbort;
|
|
1022
|
+
request.signal.addEventListener("abort", abortListener);
|
|
1023
|
+
}
|
|
866
1024
|
})().catch((error) => {
|
|
867
1025
|
console.error("Error running agent:", error);
|
|
868
|
-
console.error(
|
|
869
|
-
"Error stack:",
|
|
870
|
-
error instanceof Error ? error.stack : "No stack trace"
|
|
871
|
-
);
|
|
1026
|
+
console.error("Error stack:", error instanceof Error ? error.stack : "No stack trace");
|
|
872
1027
|
console.error("Error details:", {
|
|
873
1028
|
name: error instanceof Error ? error.name : "Unknown",
|
|
874
1029
|
message: error instanceof Error ? error.message : String(error),
|
|
875
1030
|
cause: error instanceof Error ? error.cause : void 0
|
|
876
1031
|
});
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
} catch {
|
|
882
|
-
}
|
|
883
|
-
}
|
|
1032
|
+
subscription?.unsubscribe();
|
|
1033
|
+
subscription = void 0;
|
|
1034
|
+
cleanupAbortListener();
|
|
1035
|
+
void closeStream();
|
|
884
1036
|
});
|
|
885
1037
|
return new Response(stream.readable, {
|
|
886
1038
|
status: 200,
|
|
@@ -892,10 +1044,7 @@ async function handleConnectAgent({
|
|
|
892
1044
|
});
|
|
893
1045
|
} catch (error) {
|
|
894
1046
|
console.error("Error running agent:", error);
|
|
895
|
-
console.error(
|
|
896
|
-
"Error stack:",
|
|
897
|
-
error instanceof Error ? error.stack : "No stack trace"
|
|
898
|
-
);
|
|
1047
|
+
console.error("Error stack:", error instanceof Error ? error.stack : "No stack trace");
|
|
899
1048
|
console.error("Error details:", {
|
|
900
1049
|
name: error instanceof Error ? error.name : "Unknown",
|
|
901
1050
|
message: error instanceof Error ? error.message : String(error),
|
|
@@ -915,7 +1064,7 @@ async function handleConnectAgent({
|
|
|
915
1064
|
}
|
|
916
1065
|
|
|
917
1066
|
// src/handlers/handle-stop.ts
|
|
918
|
-
var
|
|
1067
|
+
var import_client4 = require("@ag-ui/client");
|
|
919
1068
|
async function handleStopAgent({
|
|
920
1069
|
runtime,
|
|
921
1070
|
request,
|
|
@@ -936,7 +1085,35 @@ async function handleStopAgent({
|
|
|
936
1085
|
}
|
|
937
1086
|
);
|
|
938
1087
|
}
|
|
939
|
-
const
|
|
1088
|
+
const clientDeclared = CopilotRuntime["parseClientDeclaredResourceId"](request);
|
|
1089
|
+
const scope = await runtime.resolveThreadsScope({ request, clientDeclared });
|
|
1090
|
+
if (scope === void 0) {
|
|
1091
|
+
return new Response(
|
|
1092
|
+
JSON.stringify({
|
|
1093
|
+
error: "Unauthorized",
|
|
1094
|
+
message: "No resource scope provided"
|
|
1095
|
+
}),
|
|
1096
|
+
{
|
|
1097
|
+
status: 401,
|
|
1098
|
+
headers: { "Content-Type": "application/json" }
|
|
1099
|
+
}
|
|
1100
|
+
);
|
|
1101
|
+
}
|
|
1102
|
+
const runner = await runtime.runner;
|
|
1103
|
+
const metadata = await runner.getThreadMetadata(threadId, scope);
|
|
1104
|
+
if (!metadata) {
|
|
1105
|
+
return new Response(
|
|
1106
|
+
JSON.stringify({
|
|
1107
|
+
error: "Thread not found",
|
|
1108
|
+
message: `Thread '${threadId}' does not exist or you don't have access`
|
|
1109
|
+
}),
|
|
1110
|
+
{
|
|
1111
|
+
status: 404,
|
|
1112
|
+
headers: { "Content-Type": "application/json" }
|
|
1113
|
+
}
|
|
1114
|
+
);
|
|
1115
|
+
}
|
|
1116
|
+
const stopped = await runner.stop({ threadId });
|
|
940
1117
|
if (!stopped) {
|
|
941
1118
|
return new Response(
|
|
942
1119
|
JSON.stringify({
|
|
@@ -953,7 +1130,7 @@ async function handleStopAgent({
|
|
|
953
1130
|
JSON.stringify({
|
|
954
1131
|
stopped: true,
|
|
955
1132
|
interrupt: {
|
|
956
|
-
type:
|
|
1133
|
+
type: import_client4.EventType.RUN_ERROR,
|
|
957
1134
|
message: "Run stopped by user",
|
|
958
1135
|
code: "STOPPED"
|
|
959
1136
|
}
|
|
@@ -978,6 +1155,141 @@ async function handleStopAgent({
|
|
|
978
1155
|
}
|
|
979
1156
|
}
|
|
980
1157
|
|
|
1158
|
+
// src/handlers/handle-threads.ts
|
|
1159
|
+
async function handleListThreads({ runtime, request }) {
|
|
1160
|
+
try {
|
|
1161
|
+
const clientDeclared = CopilotRuntime["parseClientDeclaredResourceId"](request);
|
|
1162
|
+
const scope = await runtime.resolveThreadsScope({ request, clientDeclared });
|
|
1163
|
+
if (scope === void 0) {
|
|
1164
|
+
return new Response(
|
|
1165
|
+
JSON.stringify({
|
|
1166
|
+
error: "Unauthorized",
|
|
1167
|
+
message: "No resource scope provided"
|
|
1168
|
+
}),
|
|
1169
|
+
{
|
|
1170
|
+
status: 401,
|
|
1171
|
+
headers: { "Content-Type": "application/json" }
|
|
1172
|
+
}
|
|
1173
|
+
);
|
|
1174
|
+
}
|
|
1175
|
+
const url = new URL(request.url);
|
|
1176
|
+
const limitParam = url.searchParams.get("limit");
|
|
1177
|
+
const offsetParam = url.searchParams.get("offset");
|
|
1178
|
+
const parsedLimit = limitParam ? Number.parseInt(limitParam, 10) : NaN;
|
|
1179
|
+
const parsedOffset = offsetParam ? Number.parseInt(offsetParam, 10) : NaN;
|
|
1180
|
+
const limit = Math.max(1, Math.min(100, Number.isNaN(parsedLimit) ? 20 : parsedLimit));
|
|
1181
|
+
const offset = Math.max(0, Number.isNaN(parsedOffset) ? 0 : parsedOffset);
|
|
1182
|
+
const runner = await runtime.runner;
|
|
1183
|
+
const result = await runner.listThreads({ scope, limit, offset });
|
|
1184
|
+
return new Response(JSON.stringify(result), {
|
|
1185
|
+
status: 200,
|
|
1186
|
+
headers: { "Content-Type": "application/json" }
|
|
1187
|
+
});
|
|
1188
|
+
} catch (error) {
|
|
1189
|
+
const errorMessage = error instanceof Error ? error.message : "Unknown error occurred";
|
|
1190
|
+
return new Response(
|
|
1191
|
+
JSON.stringify({
|
|
1192
|
+
error: "Failed to list threads",
|
|
1193
|
+
message: errorMessage
|
|
1194
|
+
}),
|
|
1195
|
+
{
|
|
1196
|
+
status: 500,
|
|
1197
|
+
headers: { "Content-Type": "application/json" }
|
|
1198
|
+
}
|
|
1199
|
+
);
|
|
1200
|
+
}
|
|
1201
|
+
}
|
|
1202
|
+
async function handleGetThread({ runtime, threadId, request }) {
|
|
1203
|
+
try {
|
|
1204
|
+
const clientDeclared = CopilotRuntime["parseClientDeclaredResourceId"](request);
|
|
1205
|
+
const scope = await runtime.resolveThreadsScope({ request, clientDeclared });
|
|
1206
|
+
if (scope === void 0) {
|
|
1207
|
+
return new Response(
|
|
1208
|
+
JSON.stringify({
|
|
1209
|
+
error: "Unauthorized",
|
|
1210
|
+
message: "No resource scope provided"
|
|
1211
|
+
}),
|
|
1212
|
+
{
|
|
1213
|
+
status: 401,
|
|
1214
|
+
headers: { "Content-Type": "application/json" }
|
|
1215
|
+
}
|
|
1216
|
+
);
|
|
1217
|
+
}
|
|
1218
|
+
const runner = await runtime.runner;
|
|
1219
|
+
const metadata = await runner.getThreadMetadata(threadId, scope);
|
|
1220
|
+
if (!metadata) {
|
|
1221
|
+
return new Response(
|
|
1222
|
+
JSON.stringify({
|
|
1223
|
+
error: "Thread not found",
|
|
1224
|
+
message: `Thread '${threadId}' does not exist`
|
|
1225
|
+
}),
|
|
1226
|
+
{
|
|
1227
|
+
status: 404,
|
|
1228
|
+
headers: { "Content-Type": "application/json" }
|
|
1229
|
+
}
|
|
1230
|
+
);
|
|
1231
|
+
}
|
|
1232
|
+
return new Response(JSON.stringify(metadata), {
|
|
1233
|
+
status: 200,
|
|
1234
|
+
headers: { "Content-Type": "application/json" }
|
|
1235
|
+
});
|
|
1236
|
+
} catch (error) {
|
|
1237
|
+
const errorMessage = error instanceof Error ? error.message : "Unknown error occurred";
|
|
1238
|
+
return new Response(
|
|
1239
|
+
JSON.stringify({
|
|
1240
|
+
error: "Failed to get thread",
|
|
1241
|
+
message: errorMessage
|
|
1242
|
+
}),
|
|
1243
|
+
{
|
|
1244
|
+
status: 500,
|
|
1245
|
+
headers: { "Content-Type": "application/json" }
|
|
1246
|
+
}
|
|
1247
|
+
);
|
|
1248
|
+
}
|
|
1249
|
+
}
|
|
1250
|
+
async function handleDeleteThread({ runtime, threadId, request }) {
|
|
1251
|
+
if (!threadId) {
|
|
1252
|
+
return new Response(JSON.stringify({ error: "Thread ID required" }), {
|
|
1253
|
+
status: 400,
|
|
1254
|
+
headers: { "Content-Type": "application/json" }
|
|
1255
|
+
});
|
|
1256
|
+
}
|
|
1257
|
+
try {
|
|
1258
|
+
const clientDeclared = CopilotRuntime["parseClientDeclaredResourceId"](request);
|
|
1259
|
+
const scope = await runtime.resolveThreadsScope({ request, clientDeclared });
|
|
1260
|
+
if (scope === void 0) {
|
|
1261
|
+
return new Response(
|
|
1262
|
+
JSON.stringify({
|
|
1263
|
+
error: "Unauthorized",
|
|
1264
|
+
message: "No resource scope provided"
|
|
1265
|
+
}),
|
|
1266
|
+
{
|
|
1267
|
+
status: 401,
|
|
1268
|
+
headers: { "Content-Type": "application/json" }
|
|
1269
|
+
}
|
|
1270
|
+
);
|
|
1271
|
+
}
|
|
1272
|
+
const runner = await runtime.runner;
|
|
1273
|
+
await runner.deleteThread(threadId, scope);
|
|
1274
|
+
return new Response(JSON.stringify({ success: true }), {
|
|
1275
|
+
status: 200,
|
|
1276
|
+
headers: { "Content-Type": "application/json" }
|
|
1277
|
+
});
|
|
1278
|
+
} catch (error) {
|
|
1279
|
+
const errorMessage = error instanceof Error ? error.message : "Unknown error occurred";
|
|
1280
|
+
return new Response(
|
|
1281
|
+
JSON.stringify({
|
|
1282
|
+
error: "Failed to delete thread",
|
|
1283
|
+
message: errorMessage
|
|
1284
|
+
}),
|
|
1285
|
+
{
|
|
1286
|
+
status: 500,
|
|
1287
|
+
headers: { "Content-Type": "application/json" }
|
|
1288
|
+
}
|
|
1289
|
+
);
|
|
1290
|
+
}
|
|
1291
|
+
}
|
|
1292
|
+
|
|
981
1293
|
// src/endpoint.ts
|
|
982
1294
|
function createCopilotEndpoint({ runtime, basePath }) {
|
|
983
1295
|
const app = new import_hono.Hono();
|
|
@@ -1001,7 +1313,7 @@ function createCopilotEndpoint({ runtime, basePath }) {
|
|
|
1001
1313
|
c.set("modifiedRequest", maybeModifiedRequest);
|
|
1002
1314
|
}
|
|
1003
1315
|
} catch (error) {
|
|
1004
|
-
|
|
1316
|
+
import_shared4.logger.error({ err: error, url: request.url, path }, "Error running before request middleware");
|
|
1005
1317
|
if (error instanceof Response) {
|
|
1006
1318
|
return error;
|
|
1007
1319
|
}
|
|
@@ -1017,7 +1329,7 @@ function createCopilotEndpoint({ runtime, basePath }) {
|
|
|
1017
1329
|
response,
|
|
1018
1330
|
path
|
|
1019
1331
|
}).catch((error) => {
|
|
1020
|
-
|
|
1332
|
+
import_shared4.logger.error({ err: error, url: c.req.url, path }, "Error running after request middleware");
|
|
1021
1333
|
});
|
|
1022
1334
|
}).post("/agent/:agentId/run", async (c) => {
|
|
1023
1335
|
const agentId = c.req.param("agentId");
|
|
@@ -1029,7 +1341,7 @@ function createCopilotEndpoint({ runtime, basePath }) {
|
|
|
1029
1341
|
agentId
|
|
1030
1342
|
});
|
|
1031
1343
|
} catch (error) {
|
|
1032
|
-
|
|
1344
|
+
import_shared4.logger.error({ err: error, url: request.url, path: c.req.path }, "Error running request handler");
|
|
1033
1345
|
throw error;
|
|
1034
1346
|
}
|
|
1035
1347
|
}).post("/agent/:agentId/connect", async (c) => {
|
|
@@ -1042,7 +1354,7 @@ function createCopilotEndpoint({ runtime, basePath }) {
|
|
|
1042
1354
|
agentId
|
|
1043
1355
|
});
|
|
1044
1356
|
} catch (error) {
|
|
1045
|
-
|
|
1357
|
+
import_shared4.logger.error({ err: error, url: request.url, path: c.req.path }, "Error running request handler");
|
|
1046
1358
|
throw error;
|
|
1047
1359
|
}
|
|
1048
1360
|
}).post("/agent/:agentId/stop/:threadId", async (c) => {
|
|
@@ -1057,7 +1369,7 @@ function createCopilotEndpoint({ runtime, basePath }) {
|
|
|
1057
1369
|
threadId
|
|
1058
1370
|
});
|
|
1059
1371
|
} catch (error) {
|
|
1060
|
-
|
|
1372
|
+
import_shared4.logger.error({ err: error, url: request.url, path: c.req.path }, "Error running request handler");
|
|
1061
1373
|
throw error;
|
|
1062
1374
|
}
|
|
1063
1375
|
}).get("/info", async (c) => {
|
|
@@ -1068,7 +1380,7 @@ function createCopilotEndpoint({ runtime, basePath }) {
|
|
|
1068
1380
|
request
|
|
1069
1381
|
});
|
|
1070
1382
|
} catch (error) {
|
|
1071
|
-
|
|
1383
|
+
import_shared4.logger.error({ err: error, url: request.url, path: c.req.path }, "Error running request handler");
|
|
1072
1384
|
throw error;
|
|
1073
1385
|
}
|
|
1074
1386
|
}).post("/transcribe", async (c) => {
|
|
@@ -1079,13 +1391,92 @@ function createCopilotEndpoint({ runtime, basePath }) {
|
|
|
1079
1391
|
request
|
|
1080
1392
|
});
|
|
1081
1393
|
} catch (error) {
|
|
1082
|
-
|
|
1394
|
+
import_shared4.logger.error({ err: error, url: request.url, path: c.req.path }, "Error running request handler");
|
|
1395
|
+
throw error;
|
|
1396
|
+
}
|
|
1397
|
+
}).get("/threads", async (c) => {
|
|
1398
|
+
const request = c.get("modifiedRequest") || c.req.raw;
|
|
1399
|
+
try {
|
|
1400
|
+
return await handleListThreads({
|
|
1401
|
+
runtime,
|
|
1402
|
+
request
|
|
1403
|
+
});
|
|
1404
|
+
} catch (error) {
|
|
1405
|
+
import_shared4.logger.error({ err: error, url: request.url, path: c.req.path }, "Error running request handler");
|
|
1406
|
+
throw error;
|
|
1407
|
+
}
|
|
1408
|
+
}).get("/threads/:threadId", async (c) => {
|
|
1409
|
+
const threadId = c.req.param("threadId");
|
|
1410
|
+
const request = c.get("modifiedRequest") || c.req.raw;
|
|
1411
|
+
try {
|
|
1412
|
+
return await handleGetThread({
|
|
1413
|
+
runtime,
|
|
1414
|
+
request,
|
|
1415
|
+
threadId
|
|
1416
|
+
});
|
|
1417
|
+
} catch (error) {
|
|
1418
|
+
import_shared4.logger.error({ err: error, url: request.url, path: c.req.path }, "Error running request handler");
|
|
1419
|
+
throw error;
|
|
1420
|
+
}
|
|
1421
|
+
}).delete("/threads/:threadId", async (c) => {
|
|
1422
|
+
const threadId = c.req.param("threadId");
|
|
1423
|
+
const request = c.get("modifiedRequest") || c.req.raw;
|
|
1424
|
+
try {
|
|
1425
|
+
return await handleDeleteThread({
|
|
1426
|
+
runtime,
|
|
1427
|
+
request,
|
|
1428
|
+
threadId
|
|
1429
|
+
});
|
|
1430
|
+
} catch (error) {
|
|
1431
|
+
import_shared4.logger.error({ err: error, url: request.url, path: c.req.path }, "Error running request handler");
|
|
1083
1432
|
throw error;
|
|
1084
1433
|
}
|
|
1085
1434
|
}).notFound((c) => {
|
|
1086
1435
|
return c.json({ error: "Not found" }, 404);
|
|
1087
1436
|
});
|
|
1088
1437
|
}
|
|
1438
|
+
|
|
1439
|
+
// src/runner/index.ts
|
|
1440
|
+
var import_shared5 = require("@copilotkitnext/shared");
|
|
1441
|
+
|
|
1442
|
+
// src/resource-id-helpers.ts
|
|
1443
|
+
function validateResourceIdMatch(clientDeclared, serverAuthorized) {
|
|
1444
|
+
if (!clientDeclared) {
|
|
1445
|
+
return;
|
|
1446
|
+
}
|
|
1447
|
+
const clientIds = Array.isArray(clientDeclared) ? clientDeclared : [clientDeclared];
|
|
1448
|
+
const authorizedIds = Array.isArray(serverAuthorized) ? serverAuthorized : [serverAuthorized];
|
|
1449
|
+
const hasMatch = clientIds.some((clientId) => authorizedIds.includes(clientId));
|
|
1450
|
+
if (!hasMatch) {
|
|
1451
|
+
throw new Error("Unauthorized: Client-declared resourceId does not match authenticated user");
|
|
1452
|
+
}
|
|
1453
|
+
}
|
|
1454
|
+
function filterAuthorizedResourceIds(clientDeclared, serverAuthorized) {
|
|
1455
|
+
const authorizedIds = Array.isArray(serverAuthorized) ? serverAuthorized : [serverAuthorized];
|
|
1456
|
+
if (!clientDeclared) {
|
|
1457
|
+
return serverAuthorized;
|
|
1458
|
+
}
|
|
1459
|
+
const clientIds = Array.isArray(clientDeclared) ? clientDeclared : [clientDeclared];
|
|
1460
|
+
const filtered = clientIds.filter((id) => authorizedIds.includes(id));
|
|
1461
|
+
if (filtered.length === 0) {
|
|
1462
|
+
throw new Error("Unauthorized: None of the client-declared resourceIds are authorized");
|
|
1463
|
+
}
|
|
1464
|
+
return Array.isArray(clientDeclared) ? filtered : filtered[0];
|
|
1465
|
+
}
|
|
1466
|
+
function createStrictThreadScopeResolver(getUserId) {
|
|
1467
|
+
return async ({ request, clientDeclared }) => {
|
|
1468
|
+
const userId = await getUserId(request);
|
|
1469
|
+
validateResourceIdMatch(clientDeclared, userId);
|
|
1470
|
+
return { resourceId: userId };
|
|
1471
|
+
};
|
|
1472
|
+
}
|
|
1473
|
+
function createFilteringThreadScopeResolver(getUserResourceIds) {
|
|
1474
|
+
return async ({ request, clientDeclared }) => {
|
|
1475
|
+
const userResourceIds = await getUserResourceIds(request);
|
|
1476
|
+
const resourceId = filterAuthorizedResourceIds(clientDeclared, userResourceIds);
|
|
1477
|
+
return { resourceId };
|
|
1478
|
+
};
|
|
1479
|
+
}
|
|
1089
1480
|
// Annotate the CommonJS export names for ESM import in node:
|
|
1090
1481
|
0 && (module.exports = {
|
|
1091
1482
|
AgentRunner,
|
|
@@ -1093,6 +1484,10 @@ function createCopilotEndpoint({ runtime, basePath }) {
|
|
|
1093
1484
|
InMemoryAgentRunner,
|
|
1094
1485
|
VERSION,
|
|
1095
1486
|
createCopilotEndpoint,
|
|
1096
|
-
|
|
1487
|
+
createFilteringThreadScopeResolver,
|
|
1488
|
+
createStrictThreadScopeResolver,
|
|
1489
|
+
filterAuthorizedResourceIds,
|
|
1490
|
+
finalizeRunEvents,
|
|
1491
|
+
validateResourceIdMatch
|
|
1097
1492
|
});
|
|
1098
1493
|
//# sourceMappingURL=index.js.map
|