@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.js CHANGED
@@ -25,14 +25,21 @@ __export(index_exports, {
25
25
  InMemoryAgentRunner: () => InMemoryAgentRunner,
26
26
  VERSION: () => VERSION,
27
27
  createCopilotEndpoint: () => createCopilotEndpoint,
28
- finalizeRunEvents: () => finalizeRunEvents
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.17",
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.6",
72
- "@ag-ui/core": "0.0.40-alpha.6",
73
- "@ag-ui/encoder": "0.0.40-alpha.6",
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 defaultStopMessage = "Run stopped by user";
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
- existingStore = new InMemoryEventStore(request.threadId);
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 === import_client2.EventType.RUN_STARTED) {
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 === import_client2.EventType.RUN_STARTED) {
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, import_client2.compactEvents)(currentRunEvents);
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, import_client2.compactEvents)(currentRunEvents);
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, import_client2.compactEvents)(allHistoricEvents);
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 || !store.isRunning) {
425
- return Promise.resolve(false);
426
- }
427
- if (store.stopRequested) {
428
- return Promise.resolve(false);
339
+ if (!store) {
340
+ return false;
429
341
  }
430
- store.stopRequested = true;
431
- store.isRunning = false;
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
- return Promise.resolve(false);
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
- try {
439
- agent.abortRun();
440
- return Promise.resolve(true);
441
- } catch (error) {
442
- console.error("Failed to abort agent run", error);
443
- store.stopRequested = false;
444
- store.isRunning = true;
445
- return Promise.resolve(false);
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 import_client3 = require("@ag-ui/client");
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 = import_client3.RunAgentInputSchema.parse(requestBody);
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
- runtime.runner.run({
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
- if (!streamClosed) {
556
- try {
557
- await writer.close();
558
- streamClosed = true;
559
- } catch {
560
- }
561
- }
698
+ cleanupAbortListener();
699
+ await closeStream();
562
700
  },
563
701
  complete: async () => {
564
- if (!streamClosed) {
565
- try {
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
- if (!streamClosed) {
585
- try {
586
- writer.close();
587
- streamClosed = true;
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 import_shared2 = require("@copilotkitnext/shared");
900
+ var import_shared4 = require("@copilotkitnext/shared");
764
901
 
765
902
  // src/middleware.ts
766
- var import_shared = require("@copilotkitnext/shared");
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
- import_shared.logger.warn({ mw }, "Unsupported beforeRequestMiddleware value \u2013 skipped");
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
- import_shared.logger.warn({ mw }, "Unsupported afterRequestMiddleware value \u2013 skipped");
927
+ import_shared3.logger.warn({ mw }, "Unsupported afterRequestMiddleware value \u2013 skipped");
791
928
  }
792
929
 
793
930
  // src/handlers/handle-connect.ts
794
- var import_client4 = require("@ag-ui/client");
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 = import_client4.RunAgentInputSchema.parse(requestBody);
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
- runtime.runner.connect({
833
- threadId: input.threadId
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
- if (!streamClosed) {
849
- try {
850
- await writer.close();
851
- streamClosed = true;
852
- } catch {
853
- }
854
- }
1004
+ cleanupAbortListener();
1005
+ await closeStream();
855
1006
  },
856
1007
  complete: async () => {
857
- if (!streamClosed) {
858
- try {
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
- if (!streamClosed) {
878
- try {
879
- writer.close();
880
- streamClosed = true;
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 import_client5 = require("@ag-ui/client");
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 stopped = await runtime.runner.stop({ threadId });
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: import_client5.EventType.RUN_ERROR,
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
- import_shared2.logger.error({ err: error, url: request.url, path }, "Error running before request middleware");
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
- import_shared2.logger.error({ err: error, url: c.req.url, path }, "Error running after request middleware");
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
- import_shared2.logger.error({ err: error, url: request.url, path: c.req.path }, "Error running request handler");
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
- import_shared2.logger.error({ err: error, url: request.url, path: c.req.path }, "Error running request handler");
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
- import_shared2.logger.error({ err: error, url: request.url, path: c.req.path }, "Error running request handler");
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
- import_shared2.logger.error({ err: error, url: request.url, path: c.req.path }, "Error running request handler");
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
- import_shared2.logger.error({ err: error, url: request.url, path: c.req.path }, "Error running request handler");
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
- finalizeRunEvents
1487
+ createFilteringThreadScopeResolver,
1488
+ createStrictThreadScopeResolver,
1489
+ filterAuthorizedResourceIds,
1490
+ finalizeRunEvents,
1491
+ validateResourceIdMatch
1097
1492
  });
1098
1493
  //# sourceMappingURL=index.js.map