@copilotkit/core 1.66.4 → 1.67.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/README.md ADDED
@@ -0,0 +1,61 @@
1
+ # @copilotkit/core
2
+
3
+ `@copilotkit/core` is the framework-neutral client for CopilotKit runtimes. It
4
+ manages runtime agents, frontend tools, shared context, suggestions, thread
5
+ stores, and subscriptions.
6
+
7
+ ## Trusted Inspector metadata
8
+
9
+ When the connected runtime reports `inspectorMetadata: true` in its runtime-info
10
+ response, Core loads the optional `InspectorMetadataV1` value in the background.
11
+ The runtime connection and agent notifications finish first, so a slow or
12
+ unavailable metadata route cannot delay the app.
13
+
14
+ Core exposes the object returned by Shared normalization unchanged through the
15
+ getter and subscriber event. Older runtimes may omit
16
+ `usage.expiringSoonCount`; that absence remains valid V1 usage. A value of `0`
17
+ means known zero and stays different from absence. Shared omits a malformed
18
+ expiry leaf without removing valid `used`, `limit`, or sibling modules. Core
19
+ does not calculate or rebuild expiry and does not require a V2 schema.
20
+
21
+ Read the latest value with `inspectorMetadata`, refresh it without reconnecting,
22
+ or subscribe to changes:
23
+
24
+ ```ts
25
+ import { CopilotKitCore } from "@copilotkit/core";
26
+
27
+ const copilotkit = new CopilotKitCore({
28
+ runtimeUrl: "/api/copilotkit",
29
+ headers: { Authorization: "Bearer app-session" },
30
+ credentials: "include",
31
+ });
32
+
33
+ const subscription = copilotkit.subscribe({
34
+ onInspectorMetadataChanged: ({ inspectorMetadata }) => {
35
+ console.log(inspectorMetadata);
36
+ },
37
+ });
38
+
39
+ await copilotkit.refreshInspectorMetadata();
40
+ console.log(copilotkit.inspectorMetadata);
41
+
42
+ subscription.unsubscribe();
43
+ ```
44
+
45
+ Core sends the current headers and fetch credentials to the Copilot Runtime. A
46
+ call to `setHeaders()` or `setCredentials()` clears the prior value before it
47
+ starts a new metadata refresh, so trusted context cannot cross an auth-context
48
+ change. Changing the runtime URL or transport, losing the capability, or
49
+ disconnecting also clears the value.
50
+
51
+ Each refresh cancels the prior request and has a five-second deadline. Core also
52
+ checks the runtime URL, requested and resolved transport, headers, credentials,
53
+ connection, and capability before publishing a response. A stale success or
54
+ failure cannot replace metadata from a newer connection. Route, timeout, parse,
55
+ and subscriber failures stay isolated from the runtime connection.
56
+
57
+ See the
58
+ [`CopilotKitCore` reference](https://docs.copilotkit.ai/reference/core/classes/CopilotKitCore)
59
+ and
60
+ [`CopilotKitCoreSubscriber` reference](https://docs.copilotkit.ai/reference/core/types/CopilotKitCoreSubscriber)
61
+ for the full API.
package/dist/index.cjs CHANGED
@@ -895,6 +895,14 @@ var ProxiedCopilotRuntimeAgent = class ProxiedCopilotRuntimeAgent extends _ag_ui
895
895
 
896
896
  //#endregion
897
897
  //#region src/core/agent-registry.ts
898
+ /** Maximum wait for optional Inspector metadata before degrading to absence. */
899
+ const INSPECTOR_METADATA_REQUEST_TIMEOUT_MS = 5e3;
900
+ /** Build case-insensitive JSON headers without mutating the Core snapshot. */
901
+ function withJsonContentType(headers) {
902
+ const requestHeaders = new Headers(headers);
903
+ if (!requestHeaders.has("content-type")) requestHeaders.set("content-type", "application/json");
904
+ return requestHeaders;
905
+ }
898
906
  /**
899
907
  * Manages agent registration, lifecycle, and runtime connectivity for CopilotKitCore.
900
908
  * Handles both local development agents and remote runtime agents.
@@ -914,6 +922,15 @@ var AgentRegistry = class {
914
922
  _intelligence;
915
923
  _threadEndpoints;
916
924
  _suggestions;
925
+ _inspectorMetadata;
926
+ _inspectorMetadataSupported = false;
927
+ inspectorMetadataRefreshReady = false;
928
+ inspectorMetadataConnectionGeneration = 0;
929
+ inspectorMetadataGeneration = 0;
930
+ inspectorMetadataHeadersGeneration = 0;
931
+ inspectorMetadataCredentialsGeneration = 0;
932
+ inspectorMetadataAbortController;
933
+ inspectorMetadataNotificationQueue = Promise.resolve();
917
934
  _a2uiEnabled = false;
918
935
  _a2uiAgents;
919
936
  _openGenerativeUIEnabled = false;
@@ -966,6 +983,9 @@ var AgentRegistry = class {
966
983
  get suggestions() {
967
984
  return this._suggestions;
968
985
  }
986
+ get inspectorMetadata() {
987
+ return this._inspectorMetadata;
988
+ }
969
989
  get a2uiEnabled() {
970
990
  return this._a2uiEnabled;
971
991
  }
@@ -1000,6 +1020,7 @@ var AgentRegistry = class {
1000
1020
  setRuntimeUrl(runtimeUrl, options) {
1001
1021
  const normalizedRuntimeUrl = runtimeUrl ? runtimeUrl.replace(/\/$/, "") : void 0;
1002
1022
  if (this._runtimeUrl === normalizedRuntimeUrl) return;
1023
+ this.invalidateInspectorMetadataConnection();
1003
1024
  this._runtimeUrl = normalizedRuntimeUrl;
1004
1025
  if (options?.deferConnection) return;
1005
1026
  this.updateRuntimeConnection();
@@ -1023,6 +1044,7 @@ var AgentRegistry = class {
1023
1044
  }
1024
1045
  setRuntimeTransport(runtimeTransport) {
1025
1046
  if (this._requestedTransport === runtimeTransport) return;
1047
+ this.invalidateInspectorMetadataConnection();
1026
1048
  this._requestedTransport = runtimeTransport;
1027
1049
  this._runtimeTransport = runtimeTransport;
1028
1050
  this.updateRuntimeConnection();
@@ -1159,6 +1181,141 @@ var AgentRegistry = class {
1159
1181
  this.applyCredentialsToAgent(agent);
1160
1182
  });
1161
1183
  }
1184
+ /** Refresh metadata after the Core header snapshot changes. */
1185
+ handleHeadersChanged() {
1186
+ this.inspectorMetadataHeadersGeneration += 1;
1187
+ this.setInspectorMetadata(void 0);
1188
+ this.refreshInspectorMetadata();
1189
+ }
1190
+ /** Refresh metadata after the Core credentials mode changes. */
1191
+ handleCredentialsChanged() {
1192
+ this.inspectorMetadataCredentialsGeneration += 1;
1193
+ this.setInspectorMetadata(void 0);
1194
+ this.refreshInspectorMetadata();
1195
+ }
1196
+ /**
1197
+ * Fetch trusted inspector metadata independently from runtime discovery.
1198
+ * Optional-route failures degrade to absent metadata and never affect the
1199
+ * runtime connection state.
1200
+ */
1201
+ async refreshInspectorMetadata() {
1202
+ const generation = ++this.inspectorMetadataGeneration;
1203
+ this.inspectorMetadataAbortController?.abort();
1204
+ this.inspectorMetadataAbortController = void 0;
1205
+ if (!this._inspectorMetadataSupported || !this.inspectorMetadataRefreshReady || !this.runtimeUrl || this._runtimeConnectionStatus !== CopilotKitCoreRuntimeConnectionStatus.Connected) {
1206
+ this.setInspectorMetadata(void 0);
1207
+ return;
1208
+ }
1209
+ const runtimeUrl = this.runtimeUrl;
1210
+ const requestedTransport = this._requestedTransport;
1211
+ const resolvedTransport = this._runtimeTransport;
1212
+ const headersGeneration = this.inspectorMetadataHeadersGeneration;
1213
+ const credentialsGeneration = this.inspectorMetadataCredentialsGeneration;
1214
+ const friends = this.core;
1215
+ const headers = { ...friends.headers };
1216
+ const credentials = friends.credentials;
1217
+ const abortController = new AbortController();
1218
+ this.inspectorMetadataAbortController = abortController;
1219
+ let timedOut = false;
1220
+ let timeoutId;
1221
+ let rejectForAbort;
1222
+ const aborted = new Promise((_resolve, reject) => {
1223
+ rejectForAbort = reject;
1224
+ });
1225
+ const handleAbort = () => {
1226
+ rejectForAbort?.(/* @__PURE__ */ new Error("Inspector metadata request aborted"));
1227
+ };
1228
+ abortController.signal.addEventListener("abort", handleAbort, { once: true });
1229
+ const timeout = new Promise((_resolve, reject) => {
1230
+ timeoutId = setTimeout(() => {
1231
+ timedOut = true;
1232
+ reject(/* @__PURE__ */ new Error("Inspector metadata request timed out"));
1233
+ abortController.abort();
1234
+ }, INSPECTOR_METADATA_REQUEST_TIMEOUT_MS);
1235
+ });
1236
+ let nextMetadata;
1237
+ try {
1238
+ const request = (async () => {
1239
+ const response = resolvedTransport === "single" ? await this.fetchInspectorMetadataSingle({
1240
+ runtimeUrl,
1241
+ headers,
1242
+ credentials,
1243
+ signal: abortController.signal
1244
+ }) : await this.fetchInspectorMetadataRest({
1245
+ runtimeUrl,
1246
+ headers,
1247
+ credentials,
1248
+ signal: abortController.signal
1249
+ });
1250
+ if (response.status === 204 || !response.ok) return;
1251
+ return (0, _copilotkit_shared.parseInspectorMetadataV1)(await response.json());
1252
+ })();
1253
+ nextMetadata = await Promise.race([
1254
+ request,
1255
+ aborted,
1256
+ timeout
1257
+ ]);
1258
+ } catch {
1259
+ nextMetadata = void 0;
1260
+ } finally {
1261
+ if (timeoutId !== void 0) clearTimeout(timeoutId);
1262
+ abortController.signal.removeEventListener("abort", handleAbort);
1263
+ }
1264
+ if (!this.isInspectorMetadataRequestCurrent({
1265
+ generation,
1266
+ runtimeUrl,
1267
+ requestedTransport,
1268
+ resolvedTransport,
1269
+ headersGeneration,
1270
+ credentialsGeneration,
1271
+ signal: abortController.signal,
1272
+ allowAbortedSignal: timedOut
1273
+ })) return;
1274
+ this.inspectorMetadataAbortController = void 0;
1275
+ this.setInspectorMetadata(nextMetadata);
1276
+ }
1277
+ async fetchInspectorMetadataRest({ runtimeUrl, headers, credentials, signal }) {
1278
+ return fetch(`${runtimeUrl}/inspector-metadata`, {
1279
+ method: "GET",
1280
+ headers,
1281
+ ...credentials ? { credentials } : {},
1282
+ signal
1283
+ });
1284
+ }
1285
+ async fetchInspectorMetadataSingle({ runtimeUrl, headers, credentials, signal }) {
1286
+ return fetch(runtimeUrl, {
1287
+ method: "POST",
1288
+ headers: withJsonContentType(headers),
1289
+ body: JSON.stringify({ method: "inspector/metadata" }),
1290
+ ...credentials ? { credentials } : {},
1291
+ signal
1292
+ });
1293
+ }
1294
+ isInspectorMetadataRequestCurrent({ generation, runtimeUrl, requestedTransport, resolvedTransport, headersGeneration, credentialsGeneration, signal, allowAbortedSignal = false }) {
1295
+ return (allowAbortedSignal || !signal.aborted) && generation === this.inspectorMetadataGeneration && runtimeUrl === this.runtimeUrl && requestedTransport === this._requestedTransport && resolvedTransport === this._runtimeTransport && headersGeneration === this.inspectorMetadataHeadersGeneration && credentialsGeneration === this.inspectorMetadataCredentialsGeneration && this._inspectorMetadataSupported && this.inspectorMetadataRefreshReady && this._runtimeConnectionStatus === CopilotKitCoreRuntimeConnectionStatus.Connected;
1296
+ }
1297
+ invalidateInspectorMetadataConnection() {
1298
+ this._inspectorMetadataSupported = false;
1299
+ this.inspectorMetadataRefreshReady = false;
1300
+ this.inspectorMetadataConnectionGeneration += 1;
1301
+ this.inspectorMetadataGeneration += 1;
1302
+ this.inspectorMetadataAbortController?.abort();
1303
+ this.inspectorMetadataAbortController = void 0;
1304
+ this.setInspectorMetadata(void 0);
1305
+ }
1306
+ /**
1307
+ * Publish one state transition without making refreshes wait on subscribers.
1308
+ * Each queued callback closes over its readonly snapshot so reentrant changes
1309
+ * cannot replace the value that an older publication is still delivering.
1310
+ */
1311
+ setInspectorMetadata(inspectorMetadata) {
1312
+ if (JSON.stringify(this._inspectorMetadata) === JSON.stringify(inspectorMetadata)) return;
1313
+ this._inspectorMetadata = inspectorMetadata;
1314
+ const snapshot = inspectorMetadata;
1315
+ this.inspectorMetadataNotificationQueue = this.inspectorMetadataNotificationQueue.then(() => this.notifyInspectorMetadataChanged(snapshot)).catch((error) => {
1316
+ console.error("Subscriber onInspectorMetadataChanged queue error:", error);
1317
+ });
1318
+ }
1162
1319
  /**
1163
1320
  * Update runtime connection and fetch remote agents
1164
1321
  */
@@ -1179,6 +1336,7 @@ var AgentRegistry = class {
1179
1336
  }
1180
1337
  async performRuntimeConnection() {
1181
1338
  if (!this.runtimeUrl) {
1339
+ this.invalidateInspectorMetadataConnection();
1182
1340
  this._runtimeConnectionStatus = CopilotKitCoreRuntimeConnectionStatus.Disconnected;
1183
1341
  this._runtimeVersion = void 0;
1184
1342
  this._audioFileTranscriptionEnabled = false;
@@ -1195,10 +1353,14 @@ var AgentRegistry = class {
1195
1353
  await this.notifyAgentsChanged();
1196
1354
  return;
1197
1355
  }
1356
+ const inspectorMetadataConnectionGeneration = this.inspectorMetadataConnectionGeneration;
1198
1357
  this._runtimeConnectionStatus = CopilotKitCoreRuntimeConnectionStatus.Connecting;
1199
1358
  await this.notifyRuntimeStatusChanged(CopilotKitCoreRuntimeConnectionStatus.Connecting);
1200
1359
  try {
1201
- const runtimeInfoResponse = await this.fetchRuntimeInfo();
1360
+ if (inspectorMetadataConnectionGeneration !== this.inspectorMetadataConnectionGeneration) return;
1361
+ const { runtimeInfo: runtimeInfoResponse, resolvedTransport } = await this.fetchRuntimeInfo();
1362
+ if (inspectorMetadataConnectionGeneration !== this.inspectorMetadataConnectionGeneration) return;
1363
+ this._runtimeTransport = resolvedTransport;
1202
1364
  const { version, ...runtimeInfo } = runtimeInfoResponse;
1203
1365
  const credentials = this.core.credentials;
1204
1366
  const rawDebug = this.core.debug;
@@ -1234,6 +1396,9 @@ var AgentRegistry = class {
1234
1396
  this._intelligence = runtimeInfoResponse.intelligence;
1235
1397
  this._threadEndpoints = runtimeInfoResponse.threadEndpoints;
1236
1398
  this._suggestions = runtimeInfoResponse.suggestions;
1399
+ this._inspectorMetadataSupported = runtimeInfoResponse.inspectorMetadata === true;
1400
+ this.inspectorMetadataRefreshReady = false;
1401
+ if (!this._inspectorMetadataSupported) this.setInspectorMetadata(void 0);
1237
1402
  const a2uiInfo = runtimeInfoResponse.a2ui;
1238
1403
  this._a2uiEnabled = a2uiInfo?.enabled ?? runtimeInfoResponse.a2uiEnabled ?? false;
1239
1404
  this._a2uiAgents = a2uiInfo?.enabled ? a2uiInfo.agents : void 0;
@@ -1242,7 +1407,12 @@ var AgentRegistry = class {
1242
1407
  this._telemetryDisabled = runtimeInfoResponse.telemetryDisabled ?? false;
1243
1408
  await this.notifyRuntimeStatusChanged(CopilotKitCoreRuntimeConnectionStatus.Connected);
1244
1409
  await this.notifyAgentsChanged();
1410
+ if (inspectorMetadataConnectionGeneration !== this.inspectorMetadataConnectionGeneration || this._runtimeConnectionStatus !== CopilotKitCoreRuntimeConnectionStatus.Connected) return;
1411
+ this.inspectorMetadataRefreshReady = true;
1412
+ if (this._inspectorMetadataSupported) this.refreshInspectorMetadata();
1245
1413
  } catch (error) {
1414
+ if (inspectorMetadataConnectionGeneration !== this.inspectorMetadataConnectionGeneration) return;
1415
+ this.invalidateInspectorMetadataConnection();
1246
1416
  this._runtimeConnectionStatus = CopilotKitCoreRuntimeConnectionStatus.Error;
1247
1417
  this._runtimeVersion = void 0;
1248
1418
  this._audioFileTranscriptionEnabled = false;
@@ -1268,24 +1438,31 @@ var AgentRegistry = class {
1268
1438
  }
1269
1439
  }
1270
1440
  async fetchRuntimeInfo() {
1271
- if (!this.runtimeUrl) throw new Error("Runtime URL is not set");
1441
+ const runtimeUrl = this.runtimeUrl;
1442
+ if (!runtimeUrl) throw new Error("Runtime URL is not set");
1272
1443
  const baseHeaders = this.core.headers;
1273
1444
  const credentials = this.core.credentials;
1274
1445
  const headers = { ...baseHeaders };
1275
- if (this._runtimeTransport === "single") return this.fetchRuntimeInfoSingle(headers, credentials);
1276
- if (this._runtimeTransport === "auto") return this.fetchRuntimeInfoAutoDetect(headers, credentials);
1277
- const response = await fetch(`${this.runtimeUrl}/info`, {
1446
+ const runtimeTransport = this._runtimeTransport;
1447
+ if (runtimeTransport === "single") return {
1448
+ runtimeInfo: await this.fetchRuntimeInfoSingle(runtimeUrl, headers, credentials),
1449
+ resolvedTransport: "single"
1450
+ };
1451
+ if (runtimeTransport === "auto") return this.fetchRuntimeInfoAutoDetect(runtimeUrl, headers, credentials);
1452
+ const response = await fetch(`${runtimeUrl}/info`, {
1278
1453
  headers,
1279
1454
  ...credentials ? { credentials } : {}
1280
1455
  });
1281
1456
  if (!response.ok) throw new Error(`Runtime info request failed with status ${response.status}`);
1282
- return await response.json();
1457
+ return {
1458
+ runtimeInfo: await response.json(),
1459
+ resolvedTransport: "rest"
1460
+ };
1283
1461
  }
1284
- async fetchRuntimeInfoSingle(headers, credentials) {
1285
- if (!headers["Content-Type"]) headers["Content-Type"] = "application/json";
1286
- const response = await fetch(this.runtimeUrl, {
1462
+ async fetchRuntimeInfoSingle(runtimeUrl, headers, credentials) {
1463
+ const response = await fetch(runtimeUrl, {
1287
1464
  method: "POST",
1288
- headers,
1465
+ headers: withJsonContentType(headers),
1289
1466
  body: JSON.stringify({ method: "info" }),
1290
1467
  ...credentials ? { credentials } : {}
1291
1468
  });
@@ -1294,22 +1471,24 @@ var AgentRegistry = class {
1294
1471
  }
1295
1472
  /**
1296
1473
  * Auto-detect transport by trying REST first, then falling back to single-endpoint.
1297
- * Updates `_runtimeTransport` to the detected value so subsequent requests use it directly.
1474
+ * The caller commits the detected transport only after confirming this
1475
+ * connection attempt is still current.
1298
1476
  */
1299
- async fetchRuntimeInfoAutoDetect(headers, credentials) {
1477
+ async fetchRuntimeInfoAutoDetect(runtimeUrl, headers, credentials) {
1300
1478
  try {
1301
- const response = await fetch(`${this.runtimeUrl}/info`, {
1479
+ const response = await fetch(`${runtimeUrl}/info`, {
1302
1480
  headers: { ...headers },
1303
1481
  ...credentials ? { credentials } : {}
1304
1482
  });
1305
- if (response.status >= 200 && response.status < 300) {
1306
- this._runtimeTransport = "rest";
1307
- return await response.json();
1308
- }
1483
+ if (response.status >= 200 && response.status < 300) return {
1484
+ runtimeInfo: await response.json(),
1485
+ resolvedTransport: "rest"
1486
+ };
1309
1487
  } catch {}
1310
- const result = await this.fetchRuntimeInfoSingle({ ...headers }, credentials);
1311
- this._runtimeTransport = "single";
1312
- return result;
1488
+ return {
1489
+ runtimeInfo: await this.fetchRuntimeInfoSingle(runtimeUrl, { ...headers }, credentials),
1490
+ resolvedTransport: "single"
1491
+ };
1313
1492
  }
1314
1493
  /**
1315
1494
  * Assign agent IDs to a record of agents
@@ -1345,6 +1524,12 @@ var AgentRegistry = class {
1345
1524
  agents: this._agents
1346
1525
  }), "Subscriber onAgentsChanged error:");
1347
1526
  }
1527
+ async notifyInspectorMetadataChanged(inspectorMetadata) {
1528
+ await this.core.notifySubscribers((subscriber) => subscriber.onInspectorMetadataChanged?.({
1529
+ copilotkit: this.core,
1530
+ inspectorMetadata
1531
+ }), "Subscriber onInspectorMetadataChanged error:");
1532
+ }
1348
1533
  };
1349
1534
 
1350
1535
  //#endregion
@@ -4160,6 +4345,14 @@ var CopilotKitCore = class {
4160
4345
  get suggestions() {
4161
4346
  return this.agentRegistry.suggestions;
4162
4347
  }
4348
+ /** Trusted, optional metadata advertised by the connected runtime. */
4349
+ get inspectorMetadata() {
4350
+ return this.agentRegistry.inspectorMetadata;
4351
+ }
4352
+ /** Refresh trusted inspector metadata without reconnecting runtime agents. */
4353
+ async refreshInspectorMetadata() {
4354
+ await this.agentRegistry.refreshInspectorMetadata();
4355
+ }
4163
4356
  get a2uiEnabled() {
4164
4357
  return this.agentRegistry.a2uiEnabled;
4165
4358
  }
@@ -4215,6 +4408,7 @@ var CopilotKitCore = class {
4215
4408
  this._headers = normalizeHeaders(headers);
4216
4409
  if (this._memoryStore) this.syncMemoryContext();
4217
4410
  this.agentRegistry.applyHeadersToAgents(this.agentRegistry.agents);
4411
+ this.agentRegistry.handleHeadersChanged();
4218
4412
  this.notifySubscribers((subscriber) => subscriber.onHeadersChanged?.({
4219
4413
  copilotkit: this,
4220
4414
  headers: this.headers
@@ -4223,6 +4417,7 @@ var CopilotKitCore = class {
4223
4417
  setCredentials(credentials) {
4224
4418
  this._credentials = credentials;
4225
4419
  this.agentRegistry.applyCredentialsToAgents(this.agentRegistry.agents);
4420
+ this.agentRegistry.handleCredentialsChanged();
4226
4421
  }
4227
4422
  setProperties(properties) {
4228
4423
  this._properties = properties;