@copilotkit/shared 1.69.3 → 1.70.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.
Files changed (58) hide show
  1. package/dist/index.cjs +31 -13
  2. package/dist/index.cjs.map +1 -1
  3. package/dist/index.d.cts +19 -17
  4. package/dist/index.d.cts.map +1 -1
  5. package/dist/index.d.mts +19 -17
  6. package/dist/index.d.mts.map +1 -1
  7. package/dist/index.mjs +29 -15
  8. package/dist/index.mjs.map +1 -1
  9. package/dist/index.umd.js +159 -31
  10. package/dist/index.umd.js.map +1 -1
  11. package/dist/package.cjs +1 -1
  12. package/dist/package.mjs +1 -1
  13. package/dist/telemetry/index.d.mts +3 -2
  14. package/dist/telemetry/lambda-client.cjs +25 -4
  15. package/dist/telemetry/lambda-client.cjs.map +1 -1
  16. package/dist/telemetry/lambda-client.d.cts +14 -1
  17. package/dist/telemetry/lambda-client.d.cts.map +1 -1
  18. package/dist/telemetry/lambda-client.d.mts +14 -1
  19. package/dist/telemetry/lambda-client.d.mts.map +1 -1
  20. package/dist/telemetry/lambda-client.mjs +25 -5
  21. package/dist/telemetry/lambda-client.mjs.map +1 -1
  22. package/dist/telemetry/sampling.cjs +28 -0
  23. package/dist/telemetry/sampling.cjs.map +1 -0
  24. package/dist/telemetry/sampling.d.cts +37 -0
  25. package/dist/telemetry/sampling.d.cts.map +1 -0
  26. package/dist/telemetry/sampling.d.mts +37 -0
  27. package/dist/telemetry/sampling.d.mts.map +1 -0
  28. package/dist/telemetry/sampling.mjs +25 -0
  29. package/dist/telemetry/sampling.mjs.map +1 -0
  30. package/dist/telemetry/telemetry-client.cjs +72 -11
  31. package/dist/telemetry/telemetry-client.cjs.map +1 -1
  32. package/dist/telemetry/telemetry-client.d.cts +43 -1
  33. package/dist/telemetry/telemetry-client.d.cts.map +1 -1
  34. package/dist/telemetry/telemetry-client.d.mts +43 -1
  35. package/dist/telemetry/telemetry-client.d.mts.map +1 -1
  36. package/dist/telemetry/telemetry-client.mjs +73 -12
  37. package/dist/telemetry/telemetry-client.mjs.map +1 -1
  38. package/dist/utils/index.d.cts +1 -1
  39. package/dist/utils/index.d.mts +1 -1
  40. package/dist/utils/types.cjs.map +1 -1
  41. package/dist/utils/types.d.cts +46 -1
  42. package/dist/utils/types.d.cts.map +1 -1
  43. package/dist/utils/types.d.mts +46 -1
  44. package/dist/utils/types.d.mts.map +1 -1
  45. package/dist/utils/types.mjs.map +1 -1
  46. package/package.json +2 -2
  47. package/src/__tests__/license-context.test.ts +224 -25
  48. package/src/index.ts +72 -16
  49. package/src/telemetry/index.ts +2 -0
  50. package/src/telemetry/lambda-client.test.ts +336 -1
  51. package/src/telemetry/lambda-client.ts +56 -15
  52. package/src/telemetry/sampling.test.ts +65 -0
  53. package/src/telemetry/sampling.ts +70 -0
  54. package/src/telemetry/telemetry-blank-license-identity.test.ts +121 -0
  55. package/src/telemetry/telemetry-client.test.ts +438 -15
  56. package/src/telemetry/telemetry-client.ts +145 -30
  57. package/src/utils/__tests__/conditions.test.ts +161 -0
  58. package/src/utils/types.ts +52 -0
package/dist/index.umd.js CHANGED
@@ -1149,6 +1149,24 @@ ${getSeeMoreMarkdown(troubleshootingLink)}`;
1149
1149
  var _process$env;
1150
1150
  const TELEMETRY_SINK_URL = typeof process !== "undefined" && ((_process$env = process.env) === null || _process$env === void 0 ? void 0 : _process$env.COPILOTKIT_TELEMETRY_URL) || "https://telemetry.copilotkit.ai/ingest";
1151
1151
  const FETCH_TIMEOUT_MS = 3e3;
1152
+ const TELEMETRY_ID_PATTERN = /^[A-Za-z0-9_-]{1,128}$/;
1153
+ /**
1154
+ * Return the first telemetry identity accepted by the ingest service.
1155
+ *
1156
+ * Empty and whitespace-only values are unconfigured placeholders and must not
1157
+ * suppress a later identity source. Leading and trailing HTTP spaces and tabs
1158
+ * are removed before validation. The ingest service accepts 1 to 128 ASCII
1159
+ * letters, digits, underscores, and hyphens.
1160
+ *
1161
+ * @internal
1162
+ */
1163
+ function firstNonBlankTelemetryId(...candidates) {
1164
+ for (const candidate of candidates) {
1165
+ if (candidate === void 0) continue;
1166
+ const normalized = candidate.replace(/^[\t ]+|[\t ]+$/g, "");
1167
+ if (TELEMETRY_ID_PATTERN.test(normalized)) return normalized;
1168
+ }
1169
+ }
1152
1170
  const STRIPPED_KEYS = new Set(["cloud.public_api_key", "cloud.publicApiKey"]);
1153
1171
  function stripCloudKeys(obj) {
1154
1172
  if (!obj) return {};
@@ -1161,12 +1179,15 @@ ${getSeeMoreMarkdown(troubleshootingLink)}`;
1161
1179
  const parts = token.split(".");
1162
1180
  if (parts.length !== 3) return null;
1163
1181
  try {
1164
- let b64 = parts[1].replace(/-/g, "+").replace(/_/g, "/");
1182
+ var _firstNonBlankTelemet;
1183
+ const payload = parts[1];
1184
+ if (!/^[A-Za-z0-9_-]+$/.test(payload) || payload.length % 4 === 1) return null;
1185
+ let b64 = payload.replace(/-/g, "+").replace(/_/g, "/");
1165
1186
  const padding = (4 - b64.length % 4) % 4;
1166
1187
  b64 += "=".repeat(padding);
1167
- const json = typeof atob === "function" ? atob(b64) : Buffer.from(b64, "base64").toString("utf8");
1188
+ const json = typeof Buffer !== "undefined" ? Buffer.from(b64, "base64").toString("utf8") : new TextDecoder().decode(Uint8Array.from(atob(b64), (character) => character.charCodeAt(0)));
1168
1189
  const decoded = JSON.parse(json);
1169
- return typeof decoded.telemetry_id === "string" ? decoded.telemetry_id : null;
1190
+ return (_firstNonBlankTelemet = firstNonBlankTelemetryId(typeof decoded.telemetry_id === "string" ? decoded.telemetry_id : void 0)) !== null && _firstNonBlankTelemet !== void 0 ? _firstNonBlankTelemet : null;
1170
1191
  } catch (_unused) {
1171
1192
  return null;
1172
1193
  }
@@ -1178,7 +1199,7 @@ ${getSeeMoreMarkdown(troubleshootingLink)}`;
1178
1199
  }
1179
1200
  async function send(opts) {
1180
1201
  try {
1181
- var _opts$packageVersion;
1202
+ var _firstNonBlankTelemet2, _opts$packageVersion;
1182
1203
  const body = JSON.stringify({
1183
1204
  event: opts.event,
1184
1205
  properties: stripCloudKeys(opts.properties),
@@ -1189,7 +1210,7 @@ ${getSeeMoreMarkdown(troubleshootingLink)}`;
1189
1210
  },
1190
1211
  ts: Math.floor(Date.now() / 1e3)
1191
1212
  });
1192
- const telemetryId = parseTelemetryIdFromLicense(opts.licenseToken);
1213
+ const telemetryId = (_firstNonBlankTelemet2 = firstNonBlankTelemetryId(opts.telemetryId)) !== null && _firstNonBlankTelemet2 !== void 0 ? _firstNonBlankTelemet2 : parseTelemetryIdFromLicense(opts.licenseToken);
1193
1214
  const headers = {
1194
1215
  "Content-Type": "application/json",
1195
1216
  "User-Agent": opts.packageName ? `CopilotKit-Runtime/${(_opts$packageVersion = opts.packageVersion) !== null && _opts$packageVersion !== void 0 ? _opts$packageVersion : "unknown"} (${opts.packageName})` : "CopilotKit-Runtime"
@@ -1211,6 +1232,29 @@ ${getSeeMoreMarkdown(troubleshootingLink)}`;
1211
1232
  }
1212
1233
  const lambdaClient = { send };
1213
1234
 
1235
+ //#endregion
1236
+ //#region src/telemetry/sampling.ts
1237
+ /** Identifies which client emitted an event. */
1238
+ const TELEMETRY_EMITTER_V1 = "v1-shared";
1239
+ const TELEMETRY_EMITTER_V2 = "v2-runtime";
1240
+ /**
1241
+ * Compute the sampling block for one captured event.
1242
+ *
1243
+ * `telemetryId` is the caller's parsed license telemetry_id, or null when
1244
+ * anonymous — it decides the branch, and is deliberately not returned:
1245
+ * only the non-PII shape below travels on the event.
1246
+ */
1247
+ function computeSamplingMeta({ telemetryId, sampleRate }) {
1248
+ const identified = Boolean(telemetryId);
1249
+ const effectiveSampleRate = identified ? 1 : sampleRate;
1250
+ return {
1251
+ sampleRate: effectiveSampleRate,
1252
+ sampleRateAdjustmentFactor: 1 - effectiveSampleRate,
1253
+ sampleWeight: 1 / effectiveSampleRate,
1254
+ telemetry_identified: identified
1255
+ };
1256
+ }
1257
+
1214
1258
  //#endregion
1215
1259
  //#region src/telemetry/telemetry-client.ts
1216
1260
  /**
@@ -1228,6 +1272,7 @@ ${getSeeMoreMarkdown(troubleshootingLink)}`;
1228
1272
  this.cloudConfiguration = null;
1229
1273
  this.licenseToken = null;
1230
1274
  this.telemetryId = null;
1275
+ this.licenseTelemetryId = null;
1231
1276
  this.telemetryDisabled = false;
1232
1277
  this.sampleRate = .05;
1233
1278
  this.anonymousId = `anon_${(0, uuid.v4)()}`;
@@ -1246,19 +1291,29 @@ ${getSeeMoreMarkdown(troubleshootingLink)}`;
1246
1291
  return Math.random() < this.sampleRate;
1247
1292
  }
1248
1293
  async capture(event, properties) {
1249
- var _this$licenseToken;
1294
+ return this.captureWithIdentity(event, properties, {
1295
+ telemetryId: this.telemetryId,
1296
+ licenseToken: this.licenseToken,
1297
+ licenseTelemetryId: this.licenseTelemetryId
1298
+ });
1299
+ }
1300
+ async captureWithIdentity(event, properties, identity) {
1301
+ var _identity$telemetryId, _identity$licenseToke;
1250
1302
  if (this.telemetryDisabled) return;
1251
- if (!this.telemetryId && !this.shouldSendEvent()) return;
1252
- const effectiveSampleRate = this.telemetryId ? 1 : this.sampleRate;
1253
- const samplingMeta = {
1254
- sampleRate: effectiveSampleRate,
1255
- sampleRateAdjustmentFactor: 1 - effectiveSampleRate,
1256
- sampleWeight: 1 / effectiveSampleRate
1303
+ if (!identity.licenseTelemetryId && !this.shouldSendEvent()) return;
1304
+ const eventMeta = {
1305
+ ...computeSamplingMeta({
1306
+ telemetryId: identity.licenseTelemetryId,
1307
+ sampleRate: this.sampleRate
1308
+ }),
1309
+ telemetry_emitter: TELEMETRY_EMITTER_V1,
1310
+ telemetry_event_id: (0, uuid.v4)()
1257
1311
  };
1258
1312
  const flattenedProperties = flattenObject(properties);
1259
1313
  const propertiesWithGlobal = {
1260
1314
  ...this.globalProperties,
1261
- ...samplingMeta,
1315
+ ...eventMeta,
1316
+ telemetry_transport: "segment",
1262
1317
  ...flattenedProperties
1263
1318
  };
1264
1319
  const orderedPropertiesWithGlobal = Object.keys(propertiesWithGlobal).sort().reduce((obj, key) => {
@@ -1270,11 +1325,13 @@ ${getSeeMoreMarkdown(troubleshootingLink)}`;
1270
1325
  properties: flattenedProperties,
1271
1326
  globalProperties: {
1272
1327
  ...this.globalProperties,
1273
- ...samplingMeta
1328
+ ...eventMeta,
1329
+ telemetry_transport: "lambda"
1274
1330
  },
1275
1331
  packageName: this.packageName,
1276
1332
  packageVersion: this.packageVersion,
1277
- licenseToken: (_this$licenseToken = this.licenseToken) !== null && _this$licenseToken !== void 0 ? _this$licenseToken : void 0
1333
+ telemetryId: (_identity$telemetryId = identity.telemetryId) !== null && _identity$telemetryId !== void 0 ? _identity$telemetryId : void 0,
1334
+ licenseToken: (_identity$licenseToke = identity.licenseToken) !== null && _identity$licenseToke !== void 0 ? _identity$licenseToke : void 0
1278
1335
  });
1279
1336
  if (this.segment) this.segment.track({
1280
1337
  anonymousId: this.anonymousId,
@@ -1296,9 +1353,57 @@ ${getSeeMoreMarkdown(troubleshootingLink)}`;
1296
1353
  baseUrl: properties.baseUrl
1297
1354
  } });
1298
1355
  }
1356
+ /**
1357
+ * Atomically configure standalone, legacy, or anonymous telemetry identity.
1358
+ *
1359
+ * A standalone id takes transport precedence over a supplied legacy license
1360
+ * token, but only a license-derived id grants sampling authority. Neither
1361
+ * value is added to event properties.
1362
+ *
1363
+ * @param identity - One standalone id, one legacy license token, or neither.
1364
+ */
1365
+ setTelemetryIdentity(identity) {
1366
+ const resolvedIdentity = this.resolveTelemetryIdentity(identity);
1367
+ this.telemetryId = resolvedIdentity.telemetryId;
1368
+ this.licenseToken = resolvedIdentity.licenseToken;
1369
+ this.licenseTelemetryId = resolvedIdentity.licenseTelemetryId;
1370
+ }
1371
+ /**
1372
+ * Configure legacy license-derived telemetry identity.
1373
+ *
1374
+ * @param licenseToken - License token whose telemetry claim identifies sends.
1375
+ */
1299
1376
  setLicenseToken(licenseToken) {
1300
- this.licenseToken = licenseToken;
1301
- this.telemetryId = parseAndWarnTelemetryId(licenseToken);
1377
+ this.setTelemetryIdentity({ licenseToken });
1378
+ }
1379
+ /**
1380
+ * Create an immutable capture scope for one runtime.
1381
+ *
1382
+ * The scope shares this client's sinks, process-wide opt-out, global
1383
+ * properties, and sampling settings, but snapshots transport identity and
1384
+ * license-derived sampling authority. Constructing another runtime cannot
1385
+ * rewrite an existing scope.
1386
+ *
1387
+ * @param identity - The runtime's construction-time telemetry identity.
1388
+ * @returns A capture-only client bound to that identity.
1389
+ */
1390
+ createScope(identity) {
1391
+ const resolvedIdentity = this.resolveTelemetryIdentity(identity);
1392
+ return { capture: (event, properties) => this.captureWithIdentity(event, properties, resolvedIdentity) };
1393
+ }
1394
+ resolveTelemetryIdentity(identity) {
1395
+ var _identity$licenseToke2;
1396
+ const telemetryId = firstNonBlankTelemetryId(identity.telemetryId);
1397
+ if (telemetryId !== void 0) return {
1398
+ telemetryId,
1399
+ licenseToken: null,
1400
+ licenseTelemetryId: null
1401
+ };
1402
+ return {
1403
+ telemetryId: null,
1404
+ licenseToken: (_identity$licenseToke2 = identity.licenseToken) !== null && _identity$licenseToke2 !== void 0 ? _identity$licenseToke2 : null,
1405
+ licenseTelemetryId: identity.licenseToken ? parseAndWarnTelemetryId(identity.licenseToken) : null
1406
+ };
1302
1407
  }
1303
1408
  setSampleRate(sampleRate) {
1304
1409
  let _sampleRate;
@@ -1686,7 +1791,7 @@ ${getSeeMoreMarkdown(troubleshootingLink)}`;
1686
1791
 
1687
1792
  //#endregion
1688
1793
  //#region package.json
1689
- var version = "1.69.3";
1794
+ var version = "1.70.0";
1690
1795
 
1691
1796
  //#endregion
1692
1797
  //#region src/a2ui-prompts.ts
@@ -1807,27 +1912,46 @@ Use the SAME surfaceId as the main surface. Match action names to button action
1807
1912
  //#endregion
1808
1913
  //#region src/index.ts
1809
1914
  const COPILOTKIT_VERSION = version;
1915
+ /** Read a record value without traversing its prototype chain. */
1916
+ function getOwnRecordValue(record, key) {
1917
+ return Object.prototype.hasOwnProperty.call(record, key) ? record[key] : void 0;
1918
+ }
1919
+ /** Legacy UI surfaces that remain available for every active entitlement. */
1920
+ function isLegacyUiFeature(feature) {
1921
+ return feature === "chat" || feature === "popup" || feature === "sidebar";
1922
+ }
1810
1923
  /**
1811
- * Client-safe license context factory, driven by the license status the
1924
+ * Client-safe license context factory, driven by the license authority the
1812
1925
  * runtime reports via /info.
1813
1926
  *
1814
- * Features are enabled unless the runtime definitively reports the license
1815
- * as "expired" or "invalid". A null/"none"/"unknown" status fails open
1816
- * (unlicensed = unrestricted, with branding), and "expiring" keeps features
1817
- * on while the provider surfaces a warning banner. Per-feature data is not
1818
- * in /info yet, so checkFeature is uniform across features and getLimit has
1819
- * no limits to report. This is inlined here to avoid importing the full
1820
- * license-verifier bundle (which depends on Node's `crypto`) into browser
1821
- * bundles.
1927
+ * A ready managed entitlement is authoritative in both directions. A ready
1928
+ * active self-hosted entitlement is also authoritative, while an inactive
1929
+ * self-hosted response preserves the legacy signed-license fallback. Active
1930
+ * entitlements supply feature grants and limits; authoritative inactive
1931
+ * entitlements deny every feature and limit. Older runtimes that report only a
1932
+ * status retain the legacy behavior: features are enabled unless the status is
1933
+ * "expired" or "invalid", and no limits are reported. This is inlined here to
1934
+ * avoid importing the full license-verifier bundle (which depends on Node's
1935
+ * `crypto`) into browser bundles.
1822
1936
  */
1823
- function createLicenseContextValue(status) {
1824
- const resolvedStatus = status !== null && status !== void 0 ? status : null;
1937
+ function createLicenseContextValue(status, runtimeEntitlements) {
1938
+ const readyEntitlement = (runtimeEntitlements === null || runtimeEntitlements === void 0 ? void 0 : runtimeEntitlements.status) === "ready" ? runtimeEntitlements.entitlement : null;
1939
+ const hasUsableSelfHostedLegacyFallback = readyEntitlement && !readyEntitlement.active && readyEntitlement.source === "selfHostedDeploymentLicense" && (status === "valid" || status === "expiring");
1940
+ const featureAuthority = readyEntitlement && !hasUsableSelfHostedLegacyFallback ? readyEntitlement : null;
1941
+ const activeEntitlement = (featureAuthority === null || featureAuthority === void 0 ? void 0 : featureAuthority.active) ? featureAuthority : null;
1942
+ const resolvedStatus = activeEntitlement ? "valid" : (readyEntitlement === null || readyEntitlement === void 0 ? void 0 : readyEntitlement.source) === "managedOrgSubscription" ? "none" : featureAuthority ? status !== null && status !== void 0 ? status : "none" : status !== null && status !== void 0 ? status : null;
1825
1943
  const featuresEnabled = resolvedStatus !== "expired" && resolvedStatus !== "invalid";
1826
1944
  return {
1827
1945
  status: resolvedStatus,
1828
1946
  license: null,
1829
- checkFeature: () => featuresEnabled,
1830
- getLimit: () => null
1947
+ checkFeature: (feature) => {
1948
+ var _getOwnRecordValue;
1949
+ return featureAuthority ? activeEntitlement ? feature === "threads" && Object.prototype.hasOwnProperty.call(activeEntitlement.limits, "threads.max_count") ? true : (_getOwnRecordValue = getOwnRecordValue(activeEntitlement.features, feature)) !== null && _getOwnRecordValue !== void 0 ? _getOwnRecordValue : isLegacyUiFeature(feature) : false : featuresEnabled;
1950
+ },
1951
+ getLimit: (feature) => {
1952
+ var _getOwnRecordValue2;
1953
+ return activeEntitlement ? (_getOwnRecordValue2 = getOwnRecordValue(activeEntitlement.limits, feature)) !== null && _getOwnRecordValue2 !== void 0 ? _getOwnRecordValue2 : null : null;
1954
+ }
1831
1955
  };
1832
1956
  }
1833
1957
 
@@ -1862,11 +1986,14 @@ exports.RUNTIME_MODE_INTELLIGENCE = RUNTIME_MODE_INTELLIGENCE;
1862
1986
  exports.RUNTIME_MODE_SSE = RUNTIME_MODE_SSE;
1863
1987
  exports.ResolvedCopilotKitError = ResolvedCopilotKitError;
1864
1988
  exports.Severity = Severity;
1989
+ exports.TELEMETRY_EMITTER_V1 = TELEMETRY_EMITTER_V1;
1990
+ exports.TELEMETRY_EMITTER_V2 = TELEMETRY_EMITTER_V2;
1865
1991
  exports.TelemetryClient = TelemetryClient;
1866
1992
  exports.TranscriptionErrorCode = TranscriptionErrorCode;
1867
1993
  exports.TranscriptionErrors = TranscriptionErrors;
1868
1994
  exports.UpgradeRequiredError = UpgradeRequiredError;
1869
1995
  exports.actionParametersToJsonSchema = actionParametersToJsonSchema;
1996
+ exports.computeSamplingMeta = computeSamplingMeta;
1870
1997
  exports.convertJsonSchemaToZodSchema = convertJsonSchemaToZodSchema;
1871
1998
  exports.copyToClipboard = copyToClipboard;
1872
1999
  exports.createLicenseContextValue = createLicenseContextValue;
@@ -1875,6 +2002,7 @@ exports.ensureStructuredError = ensureStructuredError;
1875
2002
  exports.exceedsMaxSize = exceedsMaxSize;
1876
2003
  exports.executeConditions = executeConditions;
1877
2004
  exports.finalizeRunEvents = finalizeRunEvents;
2005
+ exports.firstNonBlankTelemetryId = firstNonBlankTelemetryId;
1878
2006
  exports.formatFileSize = formatFileSize;
1879
2007
  exports.generateVideoThumbnail = generateVideoThumbnail;
1880
2008
  exports.getDocumentIcon = getDocumentIcon;