{"version":3,"file":"telemetry-client.cjs","names":["Analytics","flattenObject","lambdaClient","parseAndWarnTelemetryId"],"sources":["../../src/telemetry/telemetry-client.ts"],"sourcesContent":["import { Analytics } from \"@segment/analytics-node\";\nimport type { AnalyticsEvents } from \"./events\";\nimport { flattenObject } from \"./utils\";\nimport { v4 as uuidv4 } from \"uuid\";\nimport { lambdaClient, parseAndWarnTelemetryId } from \"./lambda-client\";\n\n/**\n * Checks if telemetry is disabled via environment variables.\n * Users can opt out by setting:\n * - COPILOTKIT_TELEMETRY_DISABLED=true or COPILOTKIT_TELEMETRY_DISABLED=1\n * - DO_NOT_TRACK=true or DO_NOT_TRACK=1\n */\nexport function isTelemetryDisabled(): boolean {\n  return (\n    (process.env as Record<string, string | undefined>)\n      .COPILOTKIT_TELEMETRY_DISABLED === \"true\" ||\n    (process.env as Record<string, string | undefined>)\n      .COPILOTKIT_TELEMETRY_DISABLED === \"1\" ||\n    (process.env as Record<string, string | undefined>).DO_NOT_TRACK ===\n      \"true\" ||\n    (process.env as Record<string, string | undefined>).DO_NOT_TRACK === \"1\"\n  );\n}\n\nexport class TelemetryClient {\n  segment: Analytics | undefined;\n  globalProperties: Record<string, any> = {};\n  cloudConfiguration: { publicApiKey: string; baseUrl: string } | null = null;\n  // EIP / Intelligence license token (Ed25519-signed JWT). The lambda\n  // client decodes its payload to extract telemetry_id. Customer API\n  // keys are NOT used here — they flow only into Segment.\n  private licenseToken: string | null = null;\n  // Parsed telemetry_id from the license-token JWT payload. Cached at\n  // setLicenseToken time so `capture()` can branch on identified vs\n  // anonymous without re-parsing per event. Null when the token is\n  // absent or yielded no telemetry_id.\n  private telemetryId: string | null = null;\n  packageName: string;\n  packageVersion: string;\n  private telemetryDisabled: boolean = false;\n  // Client-side sampling rate for anonymous events. Identified events\n  // (those whose license token yielded a telemetry_id) bypass the gate\n  // entirely. Applied uniformly to both the lambda sink and Segment —\n  // one dice roll per capture, both sinks see the same decision.\n  private sampleRate: number = 0.05;\n  private anonymousId = `anon_${uuidv4()}`;\n\n  constructor({\n    packageName,\n    packageVersion,\n    telemetryDisabled,\n    telemetryBaseUrl,\n    sampleRate,\n  }: {\n    packageName: string;\n    packageVersion: string;\n    telemetryDisabled?: boolean;\n    telemetryBaseUrl?: string;\n    sampleRate?: number;\n  }) {\n    this.packageName = packageName;\n    this.packageVersion = packageVersion;\n    this.telemetryDisabled = telemetryDisabled || isTelemetryDisabled();\n\n    if (this.telemetryDisabled) {\n      return;\n    }\n\n    this.setSampleRate(sampleRate);\n\n    // eslint-disable-next-line\n    const writeKey =\n      process.env.COPILOTKIT_SEGMENT_WRITE_KEY ||\n      \"n7XAZtQCGS2v1vvBy3LgBCv2h3Y8whja\";\n\n    this.segment = new Analytics({\n      writeKey,\n    });\n\n    this.setGlobalProperties({\n      \"copilotkit.package.name\": packageName,\n      \"copilotkit.package.version\": packageVersion,\n    });\n  }\n\n  private shouldSendEvent() {\n    const randomNumber = Math.random();\n    return randomNumber < this.sampleRate;\n  }\n\n  async capture<K extends keyof AnalyticsEvents>(\n    event: K,\n    properties: AnalyticsEvents[K],\n  ) {\n    if (this.telemetryDisabled) {\n      return;\n    }\n\n    // Anonymous callers (no telemetry_id) are gated by sampleRate.\n    // Identified callers (license token with telemetry_id) always send —\n    // the volume is bounded by paying-customer count and full fidelity\n    // per identified customer is worth the marginal cost.\n    if (!this.telemetryId && !this.shouldSendEvent()) {\n      return;\n    }\n\n    // Identified events ship at 100% effective rate, anonymous events at\n    // sampleRate. Compute per-event so downstream weight-based extrapolation\n    // (sampleWeight = 1 / effectiveRate) is correct for both populations;\n    // a single global sampleWeight would overweight identified-customer\n    // counts by 1/sampleRate.\n    const effectiveSampleRate = this.telemetryId ? 1 : this.sampleRate;\n    const samplingMeta = {\n      sampleRate: effectiveSampleRate,\n      sampleRateAdjustmentFactor: 1 - effectiveSampleRate,\n      sampleWeight: 1 / effectiveSampleRate,\n    };\n\n    const flattenedProperties = flattenObject(properties);\n    const propertiesWithGlobal: Record<string, any> = {\n      ...this.globalProperties,\n      ...samplingMeta,\n      ...flattenedProperties,\n    };\n    const orderedPropertiesWithGlobal = Object.keys(propertiesWithGlobal)\n      .sort()\n      .reduce(\n        (obj, key) => {\n          obj[key] = propertiesWithGlobal[key];\n          return obj;\n        },\n        {} as Record<string, any>,\n      );\n\n    await lambdaClient.send({\n      event,\n      properties: flattenedProperties,\n      globalProperties: { ...this.globalProperties, ...samplingMeta },\n      packageName: this.packageName,\n      packageVersion: this.packageVersion,\n      licenseToken: this.licenseToken ?? undefined,\n    });\n\n    if (this.segment) {\n      this.segment.track({\n        anonymousId: this.anonymousId,\n        event,\n        properties: { ...orderedPropertiesWithGlobal },\n      });\n    }\n  }\n\n  setGlobalProperties(properties: Record<string, any>) {\n    const flattenedProperties = flattenObject(properties);\n    this.globalProperties = {\n      ...this.globalProperties,\n      ...flattenedProperties,\n    };\n  }\n\n  setCloudConfiguration(properties: { publicApiKey: string; baseUrl: string }) {\n    this.cloudConfiguration = properties;\n\n    this.setGlobalProperties({\n      cloud: {\n        publicApiKey: properties.publicApiKey,\n        baseUrl: properties.baseUrl,\n      },\n    });\n  }\n\n  // The license token isn't added to globalProperties — we don't want\n  // the JWT itself shipped on every event. Only its decoded telemetry_id\n  // travels, in the X-CopilotKit-Telemetry-Id header set by lambda-client.\n  setLicenseToken(licenseToken: string) {\n    this.licenseToken = licenseToken;\n    this.telemetryId = parseAndWarnTelemetryId(licenseToken);\n  }\n\n  private setSampleRate(sampleRate: number | undefined) {\n    let _sampleRate: number;\n\n    _sampleRate = sampleRate ?? 0.05;\n\n    // eslint-disable-next-line\n    if (process.env.COPILOTKIT_TELEMETRY_SAMPLE_RATE) {\n      // eslint-disable-next-line\n      _sampleRate = parseFloat(process.env.COPILOTKIT_TELEMETRY_SAMPLE_RATE);\n    }\n\n    // Number.isNaN guards against parseFloat(\"nonsense\") slipping past the\n    // range check (all NaN comparisons are false), which would silently\n    // drop every anonymous event with no signal — especially important\n    // since the default is now 0.05, making env-var overrides more common.\n    if (Number.isNaN(_sampleRate) || _sampleRate < 0 || _sampleRate > 1) {\n      throw new Error(\"Sample rate must be between 0 and 1\");\n    }\n\n    this.sampleRate = _sampleRate;\n    // Per-event sampling metadata (sampleRate/sampleRateAdjustmentFactor/\n    // sampleWeight) is computed in capture() so identified events get\n    // their own effectiveSampleRate=1 weight instead of the anonymous\n    // population's 1/sampleRate.\n  }\n}\n"],"mappings":";;;;;;;;;;;;;AAYA,SAAgB,sBAA+B;AAC7C,QACG,QAAQ,IACN,kCAAkC,UACpC,QAAQ,IACN,kCAAkC,OACpC,QAAQ,IAA2C,iBAClD,UACD,QAAQ,IAA2C,iBAAiB;;AAIzE,IAAa,kBAAb,MAA6B;CAuB3B,YAAY,EACV,aACA,gBACA,mBACA,kBACA,cAOC;0BAjCqC,EAAE;4BAC6B;sBAIjC;qBAKD;2BAGA;oBAKR;qBACP,sBAAgB;AAepC,OAAK,cAAc;AACnB,OAAK,iBAAiB;AACtB,OAAK,oBAAoB,qBAAqB,qBAAqB;AAEnE,MAAI,KAAK,kBACP;AAGF,OAAK,cAAc,WAAW;AAO9B,OAAK,UAAU,IAAIA,kCAAU,EAC3B,UAJA,QAAQ,IAAI,gCACZ,oCAID,CAAC;AAEF,OAAK,oBAAoB;GACvB,2BAA2B;GAC3B,8BAA8B;GAC/B,CAAC;;CAGJ,AAAQ,kBAAkB;AAExB,SADqB,KAAK,QAAQ,GACZ,KAAK;;CAG7B,MAAM,QACJ,OACA,YACA;AACA,MAAI,KAAK,kBACP;AAOF,MAAI,CAAC,KAAK,eAAe,CAAC,KAAK,iBAAiB,CAC9C;EAQF,MAAM,sBAAsB,KAAK,cAAc,IAAI,KAAK;EACxD,MAAM,eAAe;GACnB,YAAY;GACZ,4BAA4B,IAAI;GAChC,cAAc,IAAI;GACnB;EAED,MAAM,sBAAsBC,4BAAc,WAAW;EACrD,MAAM,uBAA4C;GAChD,GAAG,KAAK;GACR,GAAG;GACH,GAAG;GACJ;EACD,MAAM,8BAA8B,OAAO,KAAK,qBAAqB,CAClE,MAAM,CACN,QACE,KAAK,QAAQ;AACZ,OAAI,OAAO,qBAAqB;AAChC,UAAO;KAET,EAAE,CACH;AAEH,QAAMC,mCAAa,KAAK;GACtB;GACA,YAAY;GACZ,kBAAkB;IAAE,GAAG,KAAK;IAAkB,GAAG;IAAc;GAC/D,aAAa,KAAK;GAClB,gBAAgB,KAAK;GACrB,cAAc,KAAK,gBAAgB;GACpC,CAAC;AAEF,MAAI,KAAK,QACP,MAAK,QAAQ,MAAM;GACjB,aAAa,KAAK;GAClB;GACA,YAAY,EAAE,GAAG,6BAA6B;GAC/C,CAAC;;CAIN,oBAAoB,YAAiC;EACnD,MAAM,sBAAsBD,4BAAc,WAAW;AACrD,OAAK,mBAAmB;GACtB,GAAG,KAAK;GACR,GAAG;GACJ;;CAGH,sBAAsB,YAAuD;AAC3E,OAAK,qBAAqB;AAE1B,OAAK,oBAAoB,EACvB,OAAO;GACL,cAAc,WAAW;GACzB,SAAS,WAAW;GACrB,EACF,CAAC;;CAMJ,gBAAgB,cAAsB;AACpC,OAAK,eAAe;AACpB,OAAK,cAAcE,8CAAwB,aAAa;;CAG1D,AAAQ,cAAc,YAAgC;EACpD,IAAI;AAEJ,gBAAc,cAAc;AAG5B,MAAI,QAAQ,IAAI,iCAEd,eAAc,WAAW,QAAQ,IAAI,iCAAiC;AAOxE,MAAI,OAAO,MAAM,YAAY,IAAI,cAAc,KAAK,cAAc,EAChE,OAAM,IAAI,MAAM,sCAAsC;AAGxD,OAAK,aAAa"}