import { TelemetryEvent, TelemetryEventName, TelemetryClient, TelemetryPropertiesProvider, TelemetryEventSubscription, } from "@roo-code/types" export abstract class BaseTelemetryClient implements TelemetryClient { protected providerRef: WeakRef | null = null protected telemetryEnabled: boolean = false constructor( public readonly subscription?: TelemetryEventSubscription, protected readonly debug = false, ) {} protected isEventCapturable(eventName: TelemetryEventName): boolean { if (!this.subscription) { return true } return this.subscription.type === "include" ? this.subscription.events.includes(eventName) : !this.subscription.events.includes(eventName) } /** * Determines if a specific property should be included in telemetry events * Override in subclasses to filter specific properties */ protected isPropertyCapturable(_propertyName: string): boolean { return true } protected async getEventProperties(event: TelemetryEvent): Promise { let providerProperties: TelemetryEvent["properties"] = {} const provider = this.providerRef?.deref() if (provider) { try { // Get properties from the provider providerProperties = await provider.getTelemetryProperties() } catch (error) { // Log error but continue with capturing the event. console.error( `Error getting telemetry properties: ${error instanceof Error ? error.message : String(error)}`, ) } } // Merge provider properties with event-specific properties. // Event properties take precedence in case of conflicts. const mergedProperties = { ...providerProperties, ...(event.properties || {}) } // Filter out properties that shouldn't be captured by this client return Object.fromEntries(Object.entries(mergedProperties).filter(([key]) => this.isPropertyCapturable(key))) } public abstract capture(event: TelemetryEvent): Promise public setProvider(provider: TelemetryPropertiesProvider): void { this.providerRef = new WeakRef(provider) } public abstract updateTelemetryState(didUserOptIn: boolean): void public isTelemetryEnabled(): boolean { return this.telemetryEnabled } public abstract shutdown(): Promise }