@copilotkit/core 1.70.1 → 1.70.3

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.mjs CHANGED
@@ -1,12 +1,88 @@
1
1
  import { Throttler } from "@tanstack/pacer";
2
- import { AbstractAgent, EventType, HttpAgent, buildResumeArray, isInterruptExpired, randomUUID, runHttpRequest, structuredClone_, transformChunks, transformHttpEventStream } from "@ag-ui/client";
2
+ import { AGUIConnectNotImplementedError, AbstractAgent, EventType, HttpAgent, buildResumeArray, isInterruptExpired, randomUUID, runHttpRequest, structuredClone_, transformChunks, transformHttpEventStream } from "@ag-ui/client";
3
3
  import { RUNTIME_MODE_INTELLIGENCE, RUNTIME_MODE_SSE, logger, parseInspectorMetadataV1, partialJSONParse, phoenixExponentialBackoff, randomUUID as randomUUID$1, resolveDebugConfig, schemaToJsonSchema } from "@copilotkit/shared";
4
4
  import { BehaviorSubject, EMPTY, NEVER, Notification, Observable, Subject, Subscription, asapScheduler, combineLatest, concat, defer, dematerialize, firstValueFrom, from, lastValueFrom, merge, observeOn, of, switchMap, throwError } from "rxjs";
5
5
  import { catchError, concatWith, delay, distinctUntilChanged, endWith, filter, finalize, ignoreElements, map, mergeMap, observeOn as observeOn$1, scan, share, shareReplay, switchMap as switchMap$1, take, takeUntil, tap, timeout, withLatestFrom } from "rxjs/operators";
6
6
  import { Socket } from "phoenix";
7
7
  import { zodToJsonSchema } from "zod-to-json-schema";
8
- import { fromFetch } from "rxjs/fetch";
9
8
 
9
+ //#region src/utils/connect-replay.ts
10
+ /**
11
+ * Runs an agent's `connect()` stream through the AbstractAgent apply pipeline
12
+ * with the `verifyEvents` step omitted.
13
+ *
14
+ * `verifyEvents` enforces AG-UI's *single run* lifecycle rules: exactly one
15
+ * RUN_STARTED opening the stream, no events after a terminal RUN_FINISHED /
16
+ * RUN_ERROR. Those rules are correct for `/run`, but a `/connect` response is a
17
+ * replay of a thread's history and can legitimately carry several past runs
18
+ * back to back — including a run that ended in RUN_ERROR followed by a later
19
+ * RUN_STARTED. Verifying a replay against single-run rules makes hydration of
20
+ * an existing thread fail outright:
21
+ *
22
+ * Cannot send event type 'RUN_STARTED': The run has already errored with
23
+ * 'RUN_ERROR'. No further events can be sent.
24
+ *
25
+ * `transformChunks` is still applied — message reassembly is needed either way.
26
+ *
27
+ * This mirrors the base `AbstractAgent.connectAgent` implementation exactly
28
+ * apart from that omission, so callers keep the same subscriber notifications,
29
+ * detach semantics, and `{ result, newMessages }` return shape.
30
+ *
31
+ * TODO: Remove this in favour of the base implementation once AG-UI's
32
+ * AbstractAgent supports opting out of `verifyEvents` for transports whose
33
+ * connection life-cycle isn't a single run. As of `@ag-ui/client@0.0.57`
34
+ * `connectAgent(parameters?, subscriber?)` takes no such option.
35
+ *
36
+ * @param agent - The agent whose `connect()` stream should be consumed.
37
+ * @param parameters - Run parameters, forwarded to `prepareRunAgentInput`.
38
+ * @param subscriber - Optional one-shot subscriber for this connect call.
39
+ */
40
+ async function ɵconnectWithoutEventVerification(agent, parameters, subscriber) {
41
+ const self = agent;
42
+ try {
43
+ agent.isRunning = true;
44
+ agent.agentId = agent.agentId ?? randomUUID();
45
+ const input = self.prepareRunAgentInput(parameters);
46
+ let result;
47
+ const previousMessageIds = new Set(agent.messages.map((m) => m.id));
48
+ const subscribers = [
49
+ { onRunFinishedEvent: (event) => {
50
+ if (event.outcome === "success") result = event.result;
51
+ } },
52
+ ...agent.subscribers,
53
+ subscriber ?? {}
54
+ ];
55
+ await self.onInitialize(input, subscribers);
56
+ self.activeRunDetach$ = new Subject();
57
+ let resolveCompletion;
58
+ self.activeRunCompletionPromise = new Promise((resolve) => {
59
+ resolveCompletion = resolve;
60
+ });
61
+ const source$ = defer(() => self.connect(input)).pipe(transformChunks(self.debugLogger), takeUntil(self.activeRunDetach$));
62
+ const applied$ = self.apply(input, source$, subscribers);
63
+ await lastValueFrom(self.processApplyEvents(input, applied$, subscribers).pipe(catchError((error) => {
64
+ agent.isRunning = false;
65
+ if (error instanceof AGUIConnectNotImplementedError) return EMPTY;
66
+ return self.onError(input, error, subscribers);
67
+ }), finalize(() => {
68
+ agent.isRunning = false;
69
+ self.onFinalize(input, subscribers);
70
+ resolveCompletion?.();
71
+ resolveCompletion = void 0;
72
+ self.activeRunCompletionPromise = void 0;
73
+ self.activeRunDetach$ = void 0;
74
+ })), { defaultValue: void 0 });
75
+ const newMessages = structuredClone_(agent.messages).filter((m) => !previousMessageIds.has(m.id));
76
+ return {
77
+ result,
78
+ newMessages
79
+ };
80
+ } finally {
81
+ agent.isRunning = false;
82
+ }
83
+ }
84
+
85
+ //#endregion
10
86
  //#region src/utils/phoenix-observable.ts
11
87
  /**
12
88
  * Adapt Phoenix socket open/error callbacks into an observable signal stream.
@@ -203,72 +279,22 @@ var IntelligenceAgent = class IntelligenceAgent extends AbstractAgent {
203
279
  /**
204
280
  * Override of AbstractAgent.connectAgent that removes the `verifyEvents` step.
205
281
  *
206
- * Background: AbstractAgent's connectAgent pipeline runs events through
207
- * `verifyEvents`, which validates that the stream follows the AG-UI protocol
208
- * lifecycle — specifically, it expects a RUN_STARTED event before any content
209
- * events and a RUN_FINISHED/RUN_ERROR event to complete the stream.
210
- *
211
282
  * IntelligenceAgent uses long-lived WebSocket connections rather than
212
283
  * request-scoped SSE streams. When connecting to replay historical messages
213
284
  * for an existing thread, the connection semantics don't map to a single
214
- * agent run start/stop cycle. The replayed events may not include
215
- * RUN_STARTED/RUN_FINISHED bookends (or may contain events from multiple
216
- * past runs), which causes verifyEvents to either never complete or to
217
- * error out.
285
+ * agent run start/stop cycle: the replayed events may omit the
286
+ * RUN_STARTED/RUN_FINISHED bookends, or carry events from several past runs,
287
+ * either of which makes `verifyEvents` stall or error out.
218
288
  *
219
- * This override replicates the base connectAgent implementation exactly,
220
- * substituting only `transformChunks` (which is still needed for message
221
- * reassembly) and omitting `verifyEvents`.
222
- *
223
- * TODO: Remove this override once AG-UI's AbstractAgent supports opting out
224
- * of verifyEvents for transports with different connection life-cycles.
289
+ * See {@link ɵconnectWithoutEventVerification} for the pipeline itself, which
290
+ * the self-hosted `/connect` path shares for the same reason.
225
291
  */
226
292
  async connectAgent(parameters, subscriber) {
227
- const self = this;
228
- try {
229
- this.isRunning = true;
230
- this.agentId = this.agentId ?? randomUUID();
231
- const effectiveParameters = parameters?.runId || !this.canonicalRunId ? parameters : {
232
- ...parameters,
233
- runId: this.canonicalRunId
234
- };
235
- const input = this.prepareRunAgentInput(effectiveParameters);
236
- let result;
237
- const previousMessageIds = new Set(this.messages.map((m) => m.id));
238
- const subscribers = [
239
- { onRunFinishedEvent: (event) => {
240
- if (event.outcome === "success") result = event.result;
241
- } },
242
- ...this.subscribers,
243
- subscriber ?? {}
244
- ];
245
- await this.onInitialize(input, subscribers);
246
- self.activeRunDetach$ = new Subject();
247
- let resolveCompletion;
248
- self.activeRunCompletionPromise = new Promise((resolve) => {
249
- resolveCompletion = resolve;
250
- });
251
- const source$ = defer(() => this.connect(input)).pipe(transformChunks(this.debugLogger), takeUntil(self.activeRunDetach$));
252
- const applied$ = this.apply(input, source$, subscribers);
253
- await lastValueFrom(this.processApplyEvents(input, applied$, subscribers).pipe(catchError((error) => {
254
- this.isRunning = false;
255
- return this.onError(input, error, subscribers);
256
- }), finalize(() => {
257
- this.isRunning = false;
258
- this.onFinalize(input, subscribers);
259
- resolveCompletion?.();
260
- resolveCompletion = void 0;
261
- self.activeRunCompletionPromise = void 0;
262
- self.activeRunDetach$ = void 0;
263
- })), { defaultValue: void 0 });
264
- const newMessages = structuredClone_(this.messages).filter((m) => !previousMessageIds.has(m.id));
265
- return {
266
- result,
267
- newMessages
268
- };
269
- } finally {
270
- this.isRunning = false;
271
- }
293
+ const effectiveParameters = parameters?.runId || !this.canonicalRunId ? parameters : {
294
+ ...parameters,
295
+ runId: this.canonicalRunId
296
+ };
297
+ return ɵconnectWithoutEventVerification(this, effectiveParameters, subscriber);
272
298
  }
273
299
  abortRun() {
274
300
  if (this.activeChannel && this.canonicalRunId) {
@@ -360,13 +386,7 @@ var IntelligenceAgent = class IntelligenceAgent extends AbstractAgent {
360
386
  ...this.headers
361
387
  },
362
388
  body: JSON.stringify({
363
- threadId: input.threadId,
364
- runId: input.runId,
365
- messages: input.messages,
366
- tools: input.tools,
367
- context: input.context,
368
- state: input.state,
369
- forwardedProps: input.forwardedProps,
389
+ ...input,
370
390
  ...mode === "connect" ? { lastSeenEventId: replayCursor === void 0 ? this.getReconnectCursor(input) : replayCursor } : {}
371
391
  }),
372
392
  ...this.credentials ? { credentials: this.credentials } : {}
@@ -733,7 +753,7 @@ var ProxiedCopilotRuntimeAgent = class ProxiedCopilotRuntimeAgent extends HttpAg
733
753
  });
734
754
  }
735
755
  async connectAgent(parameters, subscriber) {
736
- if (this.runtimeMode !== RUNTIME_MODE_INTELLIGENCE) return super.connectAgent(parameters, subscriber);
756
+ if (this.runtimeMode !== RUNTIME_MODE_INTELLIGENCE) return ɵconnectWithoutEventVerification(this, parameters, subscriber);
737
757
  if (this.delegate) await this.delegate.detachActiveRun();
738
758
  await this.resolveDelegate();
739
759
  const delegate = this.delegate;
@@ -985,6 +1005,74 @@ function runtimeRequestMeta(init) {
985
1005
  return init?.ɵruntimeRequest;
986
1006
  }
987
1007
 
1008
+ //#endregion
1009
+ //#region src/utils/single-route-resource-request.ts
1010
+ /** Returns true when a path belongs to an Intelligence resource API. */
1011
+ function isIntelligenceResourcePath(path) {
1012
+ return path === "/threads" || path.startsWith("/threads/") || path === "/memories" || path.startsWith("/memories/") || path === "/annotate";
1013
+ }
1014
+ /** Reads a JSON resource body without changing invalid JSON error behavior. */
1015
+ async function readResourceBody(input, init, headers, method) {
1016
+ if (method === "GET" || method === "HEAD") return void 0;
1017
+ let text;
1018
+ if (init?.body != null) text = await new Response(init.body).text();
1019
+ else if (input instanceof Request) text = await input.clone().text();
1020
+ if (!text) return void 0;
1021
+ if (headers.get("content-type")?.toLowerCase().includes("application/json")) try {
1022
+ return JSON.parse(text);
1023
+ } catch {
1024
+ return text;
1025
+ }
1026
+ return text;
1027
+ }
1028
+ /**
1029
+ * Converts one Runtime resource fetch into a single-route JSON envelope.
1030
+ * Requests outside the mounted Runtime or outside its resource APIs pass
1031
+ * through unchanged.
1032
+ */
1033
+ async function createSingleRouteResourceRequest(input, init, runtimeUrl) {
1034
+ const runtime = new URL(runtimeUrl);
1035
+ const inputUrl = input instanceof Request ? input.url : input instanceof URL ? input.href : input;
1036
+ const target = new URL(inputUrl, runtime);
1037
+ const runtimePath = runtime.pathname.replace(/\/$/, "");
1038
+ if (target.origin !== runtime.origin) return null;
1039
+ if (!target.pathname.startsWith(`${runtimePath}/`)) return null;
1040
+ const resourcePath = target.pathname.slice(runtimePath.length);
1041
+ if (!isIntelligenceResourcePath(resourcePath)) return null;
1042
+ const method = (init?.method ?? (input instanceof Request ? input.method : "GET")).toUpperCase();
1043
+ const headers = new Headers(init?.headers ?? (input instanceof Request ? input.headers : void 0));
1044
+ const body = await readResourceBody(input, init, headers, method);
1045
+ headers.set("content-type", "application/json");
1046
+ headers.delete("content-length");
1047
+ return {
1048
+ input: runtimeUrl,
1049
+ init: {
1050
+ ...input instanceof Request ? {
1051
+ cache: input.cache,
1052
+ credentials: input.credentials,
1053
+ integrity: input.integrity,
1054
+ keepalive: input.keepalive,
1055
+ mode: input.mode,
1056
+ redirect: input.redirect,
1057
+ referrer: input.referrer,
1058
+ referrerPolicy: input.referrerPolicy,
1059
+ signal: input.signal
1060
+ } : {},
1061
+ ...init,
1062
+ method: "POST",
1063
+ headers,
1064
+ body: JSON.stringify({
1065
+ method: "resource/request",
1066
+ params: {
1067
+ path: `${resourcePath}${target.search}`,
1068
+ httpMethod: method
1069
+ },
1070
+ ...body === void 0 ? {} : { body }
1071
+ })
1072
+ }
1073
+ };
1074
+ }
1075
+
988
1076
  //#endregion
989
1077
  //#region src/core/agent-registry.ts
990
1078
  /** Maximum wait for optional Inspector metadata before degrading to absence. */
@@ -1026,7 +1114,9 @@ var AgentRegistry = class {
1026
1114
  _runtimeMode = RUNTIME_MODE_SSE;
1027
1115
  _intelligence;
1028
1116
  _threadEndpoints;
1117
+ _singleRouteResourceOperations = false;
1029
1118
  _suggestions;
1119
+ _inspectorLearning = false;
1030
1120
  _inspectorMetadata;
1031
1121
  _inspectorMetadataSupported = false;
1032
1122
  inspectorMetadataRefreshReady = false;
@@ -1097,6 +1187,9 @@ var AgentRegistry = class {
1097
1187
  get suggestions() {
1098
1188
  return this._suggestions;
1099
1189
  }
1190
+ get inspectorLearning() {
1191
+ return this._inspectorLearning;
1192
+ }
1100
1193
  get inspectorMetadata() {
1101
1194
  return this._inspectorMetadata;
1102
1195
  }
@@ -1147,6 +1240,7 @@ var AgentRegistry = class {
1147
1240
  this.resetRuntimeEntitlementRetry();
1148
1241
  this._licenseStatus = void 0;
1149
1242
  this._runtimeEntitlements = void 0;
1243
+ this._singleRouteResourceOperations = false;
1150
1244
  this._runtimeUrl = normalizedRuntimeUrl;
1151
1245
  if (options?.deferConnection) return;
1152
1246
  this.updateRuntimeConnection({ preserveOnFailure: this.hasLiveRuntimeKnowledgeToProtect() });
@@ -1175,6 +1269,7 @@ var AgentRegistry = class {
1175
1269
  this.resetRuntimeEntitlementRetry();
1176
1270
  this._requestedTransport = runtimeTransport;
1177
1271
  this._runtimeTransport = runtimeTransport;
1272
+ this._singleRouteResourceOperations = false;
1178
1273
  this.updateRuntimeConnection({ preserveOnFailure: this.hasLiveRuntimeKnowledgeToProtect() });
1179
1274
  }
1180
1275
  hasLiveRuntimeKnowledgeToProtect() {
@@ -1346,7 +1441,8 @@ var AgentRegistry = class {
1346
1441
  const meta = () => runtimeRequestMeta(init);
1347
1442
  const watchdog = this.armRuntimeRequestWatchdog(meta());
1348
1443
  try {
1349
- const response = await fetch(input, init);
1444
+ const singleRouteRequest = this._runtimeTransport === "single" && this._singleRouteResourceOperations && this._runtimeUrl ? await createSingleRouteResourceRequest(input, init, this._runtimeUrl) : null;
1445
+ const response = singleRouteRequest ? await fetch(singleRouteRequest.input, singleRouteRequest.init) : await fetch(input, init);
1350
1446
  watchdog.clear();
1351
1447
  this.handleRuntimeRequestOutcome(response.ok ? "ok" : meta()?.nonCritical ? "ignored" : "failed", watchdog.checked);
1352
1448
  return response;
@@ -1495,6 +1591,7 @@ var AgentRegistry = class {
1495
1591
  }
1496
1592
  invalidateInspectorMetadataConnection() {
1497
1593
  this._inspectorMetadataSupported = false;
1594
+ this._inspectorLearning = false;
1498
1595
  this.inspectorMetadataRefreshReady = false;
1499
1596
  this.inspectorMetadataConnectionGeneration += 1;
1500
1597
  this.inspectorMetadataGeneration += 1;
@@ -1693,7 +1790,9 @@ var AgentRegistry = class {
1693
1790
  this._runtimeMode = RUNTIME_MODE_SSE;
1694
1791
  this._intelligence = void 0;
1695
1792
  this._threadEndpoints = void 0;
1793
+ this._singleRouteResourceOperations = false;
1696
1794
  this._suggestions = void 0;
1795
+ this._inspectorLearning = false;
1697
1796
  this._a2uiEnabled = false;
1698
1797
  this._a2uiAgents = void 0;
1699
1798
  this._openGenerativeUIEnabled = false;
@@ -1762,8 +1861,10 @@ var AgentRegistry = class {
1762
1861
  this._audioFileTranscriptionEnabled = runtimeInfoResponse.audioFileTranscriptionEnabled ?? false;
1763
1862
  this._runtimeMode = runtimeInfoResponse.mode ?? RUNTIME_MODE_SSE;
1764
1863
  this._intelligence = runtimeInfoResponse.intelligence;
1765
- this._threadEndpoints = runtimeInfoResponse.threadEndpoints;
1864
+ this._singleRouteResourceOperations = resolvedTransport === "single" && runtimeInfoResponse.singleRoute?.resourceOperations === true;
1865
+ this._threadEndpoints = this._singleRouteResourceOperations ? runtimeInfoResponse.singleRoute?.threadEndpoints : runtimeInfoResponse.threadEndpoints;
1766
1866
  this._suggestions = runtimeInfoResponse.suggestions;
1867
+ this._inspectorLearning = runtimeInfoResponse.inspectorLearning === true;
1767
1868
  this._inspectorMetadataSupported = runtimeInfoResponse.inspectorMetadata === true;
1768
1869
  this.inspectorMetadataRefreshReady = false;
1769
1870
  if (!this._inspectorMetadataSupported) this.setInspectorMetadata(void 0);
@@ -1797,7 +1898,9 @@ var AgentRegistry = class {
1797
1898
  this._runtimeMode = RUNTIME_MODE_SSE;
1798
1899
  this._intelligence = void 0;
1799
1900
  this._threadEndpoints = void 0;
1901
+ this._singleRouteResourceOperations = false;
1800
1902
  this._suggestions = void 0;
1903
+ this._inspectorLearning = false;
1801
1904
  this._a2uiEnabled = false;
1802
1905
  this._a2uiAgents = void 0;
1803
1906
  this._openGenerativeUIEnabled = false;
@@ -4299,8 +4402,24 @@ const selectMemoriesAvailable = createSelector((state) => state.available);
4299
4402
  */
4300
4403
  const selectMemoriesRealtimeStatus = createSelector((state) => state.realtimeStatus);
4301
4404
  const MEMORY_METADATA_EVENT = "memory_metadata";
4405
+ /** Runs a memory request through the injected fetch and aborts on unsubscribe. */
4302
4406
  function memoryFromFetch(input, init) {
4303
- return fromFetch(input, init);
4407
+ const { selector, fetch: fetchImplementation, signal: outerSignal, ...requestInit } = init;
4408
+ return new Observable((subscriber) => {
4409
+ const controller = new AbortController();
4410
+ const abort = () => controller.abort();
4411
+ if (outerSignal?.aborted) controller.abort();
4412
+ else outerSignal?.addEventListener("abort", abort);
4413
+ const subscription = from(fetchImplementation(input, {
4414
+ ...requestInit,
4415
+ signal: controller.signal
4416
+ })).pipe(mergeMap((response) => from(selector(response)))).subscribe(subscriber);
4417
+ return () => {
4418
+ outerSignal?.removeEventListener("abort", abort);
4419
+ controller.abort();
4420
+ subscription.unsubscribe();
4421
+ };
4422
+ });
4304
4423
  }
4305
4424
  /**
4306
4425
  * Fetches the memory snapshot and maps it to a success/failure action. Both
@@ -5058,6 +5177,10 @@ var CopilotKitCore = class {
5058
5177
  get suggestions() {
5059
5178
  return this.agentRegistry.suggestions;
5060
5179
  }
5180
+ /** Whether the connected Runtime exposes debug-authorized Learning data. */
5181
+ get inspectorLearning() {
5182
+ return this.agentRegistry.inspectorLearning;
5183
+ }
5061
5184
  /** Trusted, optional metadata advertised by the connected runtime. */
5062
5185
  get inspectorMetadata() {
5063
5186
  return this.agentRegistry.inspectorMetadata;
@@ -5247,12 +5370,12 @@ var CopilotKitCore = class {
5247
5370
  /**
5248
5371
  * Lazily creates, starts, and context-syncs the core-owned memory store on
5249
5372
  * first access, then returns it. Subsequent calls return the existing store.
5250
- * The store is constructed with a bound `globalThis.fetch` and immediately
5251
- * has its runtime context synced from the current connection state.
5373
+ * The store uses the Core Runtime fetch so REST and single-route transports
5374
+ * share the same resource behavior. Its Runtime context is synced at once.
5252
5375
  */
5253
5376
  ensureMemoryStore() {
5254
5377
  if (!this._memoryStore) {
5255
- this._memoryStore = createMemoryStore({ fetch: globalThis.fetch.bind(globalThis) });
5378
+ this._memoryStore = createMemoryStore({ fetch: this.ɵruntimeFetch });
5256
5379
  this._memoryStore.start();
5257
5380
  this.syncMemoryContext();
5258
5381
  }