@copilotkit/web-inspector 1.61.1 → 1.61.2

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.
@@ -1 +1 @@
1
- {"version":3,"file":"context-helpers.cjs","names":[],"sources":["../../src/lib/context-helpers.ts"],"sourcesContent":["import type { Anchor, ContextState, Position, Size } from \"./types\";\n\nexport function updateSizeFromElement(\n state: ContextState,\n element: HTMLElement,\n fallback: Size,\n): void {\n const rect = element.getBoundingClientRect();\n state.size = {\n width: rect.width || fallback.width,\n height: rect.height || fallback.height,\n };\n}\n\nexport function clampSize(\n size: Size,\n viewport: Size,\n edgeMargin: number,\n minWidth: number,\n minHeight: number,\n): Size {\n const maxWidth = Math.max(minWidth, viewport.width - edgeMargin * 2);\n const maxHeight = Math.max(minHeight, viewport.height - edgeMargin * 2);\n\n return {\n width: clamp(size.width, minWidth, maxWidth),\n height: clamp(size.height, minHeight, maxHeight),\n };\n}\n\nexport function constrainToViewport(\n state: ContextState,\n position: Position,\n viewport: Size,\n edgeMargin: number,\n): Position {\n const maxX = Math.max(\n edgeMargin,\n viewport.width - state.size.width - edgeMargin,\n );\n const maxY = Math.max(\n edgeMargin,\n viewport.height - state.size.height - edgeMargin,\n );\n\n return {\n x: clamp(position.x, edgeMargin, maxX),\n y: clamp(position.y, edgeMargin, maxY),\n };\n}\n\nexport function keepPositionWithinViewport(\n state: ContextState,\n viewport: Size,\n edgeMargin: number,\n): void {\n state.position = constrainToViewport(\n state,\n state.position,\n viewport,\n edgeMargin,\n );\n}\n\nexport function centerContext(\n state: ContextState,\n viewport: Size,\n edgeMargin: number,\n): Position {\n const centered: Position = {\n x: Math.round((viewport.width - state.size.width) / 2),\n y: Math.round((viewport.height - state.size.height) / 2),\n };\n\n state.position = constrainToViewport(state, centered, viewport, edgeMargin);\n updateAnchorFromPosition(state, viewport, edgeMargin);\n return state.position;\n}\n\nexport function updateAnchorFromPosition(\n state: ContextState,\n viewport: Size,\n edgeMargin: number,\n): void {\n const centerX = state.position.x + state.size.width / 2;\n const centerY = state.position.y + state.size.height / 2;\n\n const horizontal: Anchor[\"horizontal\"] =\n centerX < viewport.width / 2 ? \"left\" : \"right\";\n const vertical: Anchor[\"vertical\"] =\n centerY < viewport.height / 2 ? \"top\" : \"bottom\";\n\n state.anchor = { horizontal, vertical };\n\n const maxHorizontalOffset = Math.max(\n edgeMargin,\n viewport.width - state.size.width - edgeMargin,\n );\n const maxVerticalOffset = Math.max(\n edgeMargin,\n viewport.height - state.size.height - edgeMargin,\n );\n\n state.anchorOffset = {\n x:\n horizontal === \"left\"\n ? clamp(state.position.x, edgeMargin, maxHorizontalOffset)\n : clamp(\n viewport.width - state.position.x - state.size.width,\n edgeMargin,\n maxHorizontalOffset,\n ),\n y:\n vertical === \"top\"\n ? clamp(state.position.y, edgeMargin, maxVerticalOffset)\n : clamp(\n viewport.height - state.position.y - state.size.height,\n edgeMargin,\n maxVerticalOffset,\n ),\n };\n}\n\nexport function applyAnchorPosition(\n state: ContextState,\n viewport: Size,\n edgeMargin: number,\n): Position {\n const maxHorizontalOffset = Math.max(\n edgeMargin,\n viewport.width - state.size.width - edgeMargin,\n );\n const maxVerticalOffset = Math.max(\n edgeMargin,\n viewport.height - state.size.height - edgeMargin,\n );\n\n const horizontalOffset = clamp(\n state.anchorOffset.x,\n edgeMargin,\n maxHorizontalOffset,\n );\n const verticalOffset = clamp(\n state.anchorOffset.y,\n edgeMargin,\n maxVerticalOffset,\n );\n\n const x =\n state.anchor.horizontal === \"left\"\n ? horizontalOffset\n : viewport.width - state.size.width - horizontalOffset;\n\n const y =\n state.anchor.vertical === \"top\"\n ? verticalOffset\n : viewport.height - state.size.height - verticalOffset;\n\n state.anchorOffset = { x: horizontalOffset, y: verticalOffset };\n state.position = constrainToViewport(state, { x, y }, viewport, edgeMargin);\n return state.position;\n}\n\nfunction clamp(value: number, min: number, max: number): number {\n return Math.min(Math.max(min, value), max);\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"context-helpers.cjs","names":[],"sources":["../../src/lib/context-helpers.ts"],"sourcesContent":["import type { Anchor, ContextState, Position, Size } from \"./types.js\";\n\nexport function updateSizeFromElement(\n state: ContextState,\n element: HTMLElement,\n fallback: Size,\n): void {\n const rect = element.getBoundingClientRect();\n state.size = {\n width: rect.width || fallback.width,\n height: rect.height || fallback.height,\n };\n}\n\nexport function clampSize(\n size: Size,\n viewport: Size,\n edgeMargin: number,\n minWidth: number,\n minHeight: number,\n): Size {\n const maxWidth = Math.max(minWidth, viewport.width - edgeMargin * 2);\n const maxHeight = Math.max(minHeight, viewport.height - edgeMargin * 2);\n\n return {\n width: clamp(size.width, minWidth, maxWidth),\n height: clamp(size.height, minHeight, maxHeight),\n };\n}\n\nexport function constrainToViewport(\n state: ContextState,\n position: Position,\n viewport: Size,\n edgeMargin: number,\n): Position {\n const maxX = Math.max(\n edgeMargin,\n viewport.width - state.size.width - edgeMargin,\n );\n const maxY = Math.max(\n edgeMargin,\n viewport.height - state.size.height - edgeMargin,\n );\n\n return {\n x: clamp(position.x, edgeMargin, maxX),\n y: clamp(position.y, edgeMargin, maxY),\n };\n}\n\nexport function keepPositionWithinViewport(\n state: ContextState,\n viewport: Size,\n edgeMargin: number,\n): void {\n state.position = constrainToViewport(\n state,\n state.position,\n viewport,\n edgeMargin,\n );\n}\n\nexport function centerContext(\n state: ContextState,\n viewport: Size,\n edgeMargin: number,\n): Position {\n const centered: Position = {\n x: Math.round((viewport.width - state.size.width) / 2),\n y: Math.round((viewport.height - state.size.height) / 2),\n };\n\n state.position = constrainToViewport(state, centered, viewport, edgeMargin);\n updateAnchorFromPosition(state, viewport, edgeMargin);\n return state.position;\n}\n\nexport function updateAnchorFromPosition(\n state: ContextState,\n viewport: Size,\n edgeMargin: number,\n): void {\n const centerX = state.position.x + state.size.width / 2;\n const centerY = state.position.y + state.size.height / 2;\n\n const horizontal: Anchor[\"horizontal\"] =\n centerX < viewport.width / 2 ? \"left\" : \"right\";\n const vertical: Anchor[\"vertical\"] =\n centerY < viewport.height / 2 ? \"top\" : \"bottom\";\n\n state.anchor = { horizontal, vertical };\n\n const maxHorizontalOffset = Math.max(\n edgeMargin,\n viewport.width - state.size.width - edgeMargin,\n );\n const maxVerticalOffset = Math.max(\n edgeMargin,\n viewport.height - state.size.height - edgeMargin,\n );\n\n state.anchorOffset = {\n x:\n horizontal === \"left\"\n ? clamp(state.position.x, edgeMargin, maxHorizontalOffset)\n : clamp(\n viewport.width - state.position.x - state.size.width,\n edgeMargin,\n maxHorizontalOffset,\n ),\n y:\n vertical === \"top\"\n ? clamp(state.position.y, edgeMargin, maxVerticalOffset)\n : clamp(\n viewport.height - state.position.y - state.size.height,\n edgeMargin,\n maxVerticalOffset,\n ),\n };\n}\n\nexport function applyAnchorPosition(\n state: ContextState,\n viewport: Size,\n edgeMargin: number,\n): Position {\n const maxHorizontalOffset = Math.max(\n edgeMargin,\n viewport.width - state.size.width - edgeMargin,\n );\n const maxVerticalOffset = Math.max(\n edgeMargin,\n viewport.height - state.size.height - edgeMargin,\n );\n\n const horizontalOffset = clamp(\n state.anchorOffset.x,\n edgeMargin,\n maxHorizontalOffset,\n );\n const verticalOffset = clamp(\n state.anchorOffset.y,\n edgeMargin,\n maxVerticalOffset,\n );\n\n const x =\n state.anchor.horizontal === \"left\"\n ? horizontalOffset\n : viewport.width - state.size.width - horizontalOffset;\n\n const y =\n state.anchor.vertical === \"top\"\n ? verticalOffset\n : viewport.height - state.size.height - verticalOffset;\n\n state.anchorOffset = { x: horizontalOffset, y: verticalOffset };\n state.position = constrainToViewport(state, { x, y }, viewport, edgeMargin);\n return state.position;\n}\n\nfunction clamp(value: number, min: number, max: number): number {\n return Math.min(Math.max(min, value), max);\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"context-helpers.mjs","names":[],"sources":["../../src/lib/context-helpers.ts"],"sourcesContent":["import type { Anchor, ContextState, Position, Size } from \"./types\";\n\nexport function updateSizeFromElement(\n state: ContextState,\n element: HTMLElement,\n fallback: Size,\n): void {\n const rect = element.getBoundingClientRect();\n state.size = {\n width: rect.width || fallback.width,\n height: rect.height || fallback.height,\n };\n}\n\nexport function clampSize(\n size: Size,\n viewport: Size,\n edgeMargin: number,\n minWidth: number,\n minHeight: number,\n): Size {\n const maxWidth = Math.max(minWidth, viewport.width - edgeMargin * 2);\n const maxHeight = Math.max(minHeight, viewport.height - edgeMargin * 2);\n\n return {\n width: clamp(size.width, minWidth, maxWidth),\n height: clamp(size.height, minHeight, maxHeight),\n };\n}\n\nexport function constrainToViewport(\n state: ContextState,\n position: Position,\n viewport: Size,\n edgeMargin: number,\n): Position {\n const maxX = Math.max(\n edgeMargin,\n viewport.width - state.size.width - edgeMargin,\n );\n const maxY = Math.max(\n edgeMargin,\n viewport.height - state.size.height - edgeMargin,\n );\n\n return {\n x: clamp(position.x, edgeMargin, maxX),\n y: clamp(position.y, edgeMargin, maxY),\n };\n}\n\nexport function keepPositionWithinViewport(\n state: ContextState,\n viewport: Size,\n edgeMargin: number,\n): void {\n state.position = constrainToViewport(\n state,\n state.position,\n viewport,\n edgeMargin,\n );\n}\n\nexport function centerContext(\n state: ContextState,\n viewport: Size,\n edgeMargin: number,\n): Position {\n const centered: Position = {\n x: Math.round((viewport.width - state.size.width) / 2),\n y: Math.round((viewport.height - state.size.height) / 2),\n };\n\n state.position = constrainToViewport(state, centered, viewport, edgeMargin);\n updateAnchorFromPosition(state, viewport, edgeMargin);\n return state.position;\n}\n\nexport function updateAnchorFromPosition(\n state: ContextState,\n viewport: Size,\n edgeMargin: number,\n): void {\n const centerX = state.position.x + state.size.width / 2;\n const centerY = state.position.y + state.size.height / 2;\n\n const horizontal: Anchor[\"horizontal\"] =\n centerX < viewport.width / 2 ? \"left\" : \"right\";\n const vertical: Anchor[\"vertical\"] =\n centerY < viewport.height / 2 ? \"top\" : \"bottom\";\n\n state.anchor = { horizontal, vertical };\n\n const maxHorizontalOffset = Math.max(\n edgeMargin,\n viewport.width - state.size.width - edgeMargin,\n );\n const maxVerticalOffset = Math.max(\n edgeMargin,\n viewport.height - state.size.height - edgeMargin,\n );\n\n state.anchorOffset = {\n x:\n horizontal === \"left\"\n ? clamp(state.position.x, edgeMargin, maxHorizontalOffset)\n : clamp(\n viewport.width - state.position.x - state.size.width,\n edgeMargin,\n maxHorizontalOffset,\n ),\n y:\n vertical === \"top\"\n ? clamp(state.position.y, edgeMargin, maxVerticalOffset)\n : clamp(\n viewport.height - state.position.y - state.size.height,\n edgeMargin,\n maxVerticalOffset,\n ),\n };\n}\n\nexport function applyAnchorPosition(\n state: ContextState,\n viewport: Size,\n edgeMargin: number,\n): Position {\n const maxHorizontalOffset = Math.max(\n edgeMargin,\n viewport.width - state.size.width - edgeMargin,\n );\n const maxVerticalOffset = Math.max(\n edgeMargin,\n viewport.height - state.size.height - edgeMargin,\n );\n\n const horizontalOffset = clamp(\n state.anchorOffset.x,\n edgeMargin,\n maxHorizontalOffset,\n );\n const verticalOffset = clamp(\n state.anchorOffset.y,\n edgeMargin,\n maxVerticalOffset,\n );\n\n const x =\n state.anchor.horizontal === \"left\"\n ? horizontalOffset\n : viewport.width - state.size.width - horizontalOffset;\n\n const y =\n state.anchor.vertical === \"top\"\n ? verticalOffset\n : viewport.height - state.size.height - verticalOffset;\n\n state.anchorOffset = { x: horizontalOffset, y: verticalOffset };\n state.position = constrainToViewport(state, { x, y }, viewport, edgeMargin);\n return state.position;\n}\n\nfunction clamp(value: number, min: number, max: number): number {\n return Math.min(Math.max(min, value), max);\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"context-helpers.mjs","names":[],"sources":["../../src/lib/context-helpers.ts"],"sourcesContent":["import type { Anchor, ContextState, Position, Size } from \"./types.js\";\n\nexport function updateSizeFromElement(\n state: ContextState,\n element: HTMLElement,\n fallback: Size,\n): void {\n const rect = element.getBoundingClientRect();\n state.size = {\n width: rect.width || fallback.width,\n height: rect.height || fallback.height,\n };\n}\n\nexport function clampSize(\n size: Size,\n viewport: Size,\n edgeMargin: number,\n minWidth: number,\n minHeight: number,\n): Size {\n const maxWidth = Math.max(minWidth, viewport.width - edgeMargin * 2);\n const maxHeight = Math.max(minHeight, viewport.height - edgeMargin * 2);\n\n return {\n width: clamp(size.width, minWidth, maxWidth),\n height: clamp(size.height, minHeight, maxHeight),\n };\n}\n\nexport function constrainToViewport(\n state: ContextState,\n position: Position,\n viewport: Size,\n edgeMargin: number,\n): Position {\n const maxX = Math.max(\n edgeMargin,\n viewport.width - state.size.width - edgeMargin,\n );\n const maxY = Math.max(\n edgeMargin,\n viewport.height - state.size.height - edgeMargin,\n );\n\n return {\n x: clamp(position.x, edgeMargin, maxX),\n y: clamp(position.y, edgeMargin, maxY),\n };\n}\n\nexport function keepPositionWithinViewport(\n state: ContextState,\n viewport: Size,\n edgeMargin: number,\n): void {\n state.position = constrainToViewport(\n state,\n state.position,\n viewport,\n edgeMargin,\n );\n}\n\nexport function centerContext(\n state: ContextState,\n viewport: Size,\n edgeMargin: number,\n): Position {\n const centered: Position = {\n x: Math.round((viewport.width - state.size.width) / 2),\n y: Math.round((viewport.height - state.size.height) / 2),\n };\n\n state.position = constrainToViewport(state, centered, viewport, edgeMargin);\n updateAnchorFromPosition(state, viewport, edgeMargin);\n return state.position;\n}\n\nexport function updateAnchorFromPosition(\n state: ContextState,\n viewport: Size,\n edgeMargin: number,\n): void {\n const centerX = state.position.x + state.size.width / 2;\n const centerY = state.position.y + state.size.height / 2;\n\n const horizontal: Anchor[\"horizontal\"] =\n centerX < viewport.width / 2 ? \"left\" : \"right\";\n const vertical: Anchor[\"vertical\"] =\n centerY < viewport.height / 2 ? \"top\" : \"bottom\";\n\n state.anchor = { horizontal, vertical };\n\n const maxHorizontalOffset = Math.max(\n edgeMargin,\n viewport.width - state.size.width - edgeMargin,\n );\n const maxVerticalOffset = Math.max(\n edgeMargin,\n viewport.height - state.size.height - edgeMargin,\n );\n\n state.anchorOffset = {\n x:\n horizontal === \"left\"\n ? clamp(state.position.x, edgeMargin, maxHorizontalOffset)\n : clamp(\n viewport.width - state.position.x - state.size.width,\n edgeMargin,\n maxHorizontalOffset,\n ),\n y:\n vertical === \"top\"\n ? clamp(state.position.y, edgeMargin, maxVerticalOffset)\n : clamp(\n viewport.height - state.position.y - state.size.height,\n edgeMargin,\n maxVerticalOffset,\n ),\n };\n}\n\nexport function applyAnchorPosition(\n state: ContextState,\n viewport: Size,\n edgeMargin: number,\n): Position {\n const maxHorizontalOffset = Math.max(\n edgeMargin,\n viewport.width - state.size.width - edgeMargin,\n );\n const maxVerticalOffset = Math.max(\n edgeMargin,\n viewport.height - state.size.height - edgeMargin,\n );\n\n const horizontalOffset = clamp(\n state.anchorOffset.x,\n edgeMargin,\n maxHorizontalOffset,\n );\n const verticalOffset = clamp(\n state.anchorOffset.y,\n edgeMargin,\n maxVerticalOffset,\n );\n\n const x =\n state.anchor.horizontal === \"left\"\n ? horizontalOffset\n : viewport.width - state.size.width - horizontalOffset;\n\n const y =\n state.anchor.vertical === \"top\"\n ? verticalOffset\n : viewport.height - state.size.height - verticalOffset;\n\n state.anchorOffset = { x: horizontalOffset, y: verticalOffset };\n state.position = constrainToViewport(state, { x, y }, viewport, edgeMargin);\n return state.position;\n}\n\nfunction clamp(value: number, min: number, max: number): number {\n return Math.min(Math.max(min, value), max);\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"persistence.cjs","names":[],"sources":["../../src/lib/persistence.ts"],"sourcesContent":["import type { Anchor, DockMode, Position, Size } from \"./types\";\n\nexport type PersistedContextState = {\n anchor?: Anchor;\n anchorOffset?: Position;\n size?: Size;\n hasCustomPosition?: boolean;\n};\n\nexport type PersistedState = {\n button?: Omit<PersistedContextState, \"size\">;\n window?: PersistedContextState;\n isOpen?: boolean;\n dockMode?: DockMode;\n selectedMenu?: string;\n selectedContext?: string;\n};\n\nexport function loadInspectorState(storageKey: string): PersistedState | null {\n if (typeof window === \"undefined\") {\n return null;\n }\n\n const raw = window.localStorage.getItem(storageKey);\n if (raw) {\n try {\n const parsed = JSON.parse(raw);\n if (parsed && typeof parsed === \"object\") {\n return parsed as PersistedState;\n }\n } catch {\n // Fall through to cookie migration path\n }\n }\n\n // Backwards compatibility: try to read the legacy cookie and migrate it\n if (typeof document !== \"undefined\") {\n const prefix = `${storageKey}=`;\n const entry = document.cookie\n .split(\"; \")\n .find((cookie) => cookie.startsWith(prefix));\n if (entry) {\n const legacyRaw = entry.substring(prefix.length);\n try {\n const parsed = JSON.parse(decodeURIComponent(legacyRaw));\n if (parsed && typeof parsed === \"object\") {\n return parsed as PersistedState;\n }\n } catch {\n return null;\n }\n }\n }\n\n return null;\n}\n\nexport function saveInspectorState(\n storageKey: string,\n state: PersistedState,\n): void {\n if (typeof window === \"undefined\") {\n return;\n }\n\n try {\n window.localStorage.setItem(storageKey, JSON.stringify(state));\n } catch (error) {\n console.warn(\"Failed to persist inspector state\", error);\n }\n}\n\nexport function isValidAnchor(value: unknown): value is Anchor {\n if (!value || typeof value !== \"object\") {\n return false;\n }\n\n const candidate = value as Anchor;\n return (\n (candidate.horizontal === \"left\" || candidate.horizontal === \"right\") &&\n (candidate.vertical === \"top\" || candidate.vertical === \"bottom\")\n );\n}\n\nexport function isValidPosition(value: unknown): value is Position {\n if (!value || typeof value !== \"object\") {\n return false;\n }\n\n const candidate = value as Position;\n return isFiniteNumber(candidate.x) && isFiniteNumber(candidate.y);\n}\n\nexport function isValidSize(value: unknown): value is Size {\n if (!value || typeof value !== \"object\") {\n return false;\n }\n\n const candidate = value as Size;\n return isFiniteNumber(candidate.width) && isFiniteNumber(candidate.height);\n}\n\nexport function isFiniteNumber(value: unknown): value is number {\n return typeof value === \"number\" && Number.isFinite(value);\n}\n\nexport function isValidDockMode(value: unknown): value is DockMode {\n return value === \"floating\" || value === \"docked-left\";\n}\n\n// Telemetry persistence — flat per-key localStorage rather than the\n// JSON-blob shape used for window/dock state, because each value is\n// independent and we want to read/write them without round-tripping\n// the whole inspector state object.\nconst TELEMETRY_DISTINCT_ID_KEY = \"cpk:inspector:telemetry:distinct_id\";\nconst TELEMETRY_OPT_OUT_KEY = \"cpk:inspector:telemetry:opt_out\";\nconst TELEMETRY_DISCLOSURE_SHOWN_KEY =\n \"cpk:inspector:telemetry:disclosure_shown\";\n\n// Module-level fallback for when localStorage is unavailable (private mode,\n// quota exceeded, etc.). Cached so that banner_viewed and banner_clicked from\n// the same page-load share one distinct_id even without persistent storage —\n// funnel coherence within a session is preserved even when storage fails.\nlet inMemoryFallbackId: string | null = null;\n\nexport function getOrCreateTelemetryDistinctId(): string {\n if (typeof window === \"undefined\") {\n // SSR / test fallback. A non-persistent ID is preferable to throwing\n // because telemetry must never break the host application.\n return generateUuidV4();\n }\n\n try {\n const existing = window.localStorage.getItem(TELEMETRY_DISTINCT_ID_KEY);\n if (existing && existing.length > 0) return existing;\n const fresh = generateUuidV4();\n window.localStorage.setItem(TELEMETRY_DISTINCT_ID_KEY, fresh);\n return fresh;\n } catch {\n return (inMemoryFallbackId ??= generateUuidV4());\n }\n}\n\n// Test-only reset so the in-memory fallback doesn't leak between test cases.\nexport function _resetTelemetryPersistenceForTesting(): void {\n inMemoryFallbackId = null;\n}\n\nexport function isTelemetryOptedOut(): boolean {\n if (typeof window === \"undefined\") return false;\n try {\n return window.localStorage.getItem(TELEMETRY_OPT_OUT_KEY) === \"true\";\n } catch {\n return false;\n }\n}\n\nexport function setTelemetryOptOut(optedOut: boolean): void {\n if (typeof window === \"undefined\") return;\n try {\n if (optedOut) {\n window.localStorage.setItem(TELEMETRY_OPT_OUT_KEY, \"true\");\n } else {\n window.localStorage.removeItem(TELEMETRY_OPT_OUT_KEY);\n }\n } catch {\n // No-op — see getOrCreateTelemetryDistinctId.\n }\n}\n\nexport function hasTelemetryDisclosureBeenShown(): boolean {\n if (typeof window === \"undefined\") return false;\n try {\n return (\n window.localStorage.getItem(TELEMETRY_DISCLOSURE_SHOWN_KEY) === \"true\"\n );\n } catch {\n return false;\n }\n}\n\nexport function markTelemetryDisclosureShown(): void {\n if (typeof window === \"undefined\") return;\n try {\n window.localStorage.setItem(TELEMETRY_DISCLOSURE_SHOWN_KEY, \"true\");\n } catch {\n // No-op.\n }\n}\n\nfunction generateUuidV4(): string {\n if (\n typeof globalThis.crypto !== \"undefined\" &&\n typeof globalThis.crypto.randomUUID === \"function\"\n ) {\n return globalThis.crypto.randomUUID();\n }\n // Fallback for environments without crypto.randomUUID (older browsers,\n // some test runners). Not cryptographically strong; acceptable because\n // the value is just an anonymous correlation ID.\n return \"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx\".replace(/[xy]/g, (c) => {\n const r = (Math.random() * 16) | 0;\n const v = c === \"x\" ? r : (r & 0x3) | 0x8;\n return v.toString(16);\n });\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"persistence.cjs","names":[],"sources":["../../src/lib/persistence.ts"],"sourcesContent":["import type { Anchor, DockMode, Position, Size } from \"./types.js\";\n\nexport type PersistedContextState = {\n anchor?: Anchor;\n anchorOffset?: Position;\n size?: Size;\n hasCustomPosition?: boolean;\n};\n\nexport type PersistedState = {\n button?: Omit<PersistedContextState, \"size\">;\n window?: PersistedContextState;\n isOpen?: boolean;\n dockMode?: DockMode;\n selectedMenu?: string;\n selectedContext?: string;\n};\n\nexport function loadInspectorState(storageKey: string): PersistedState | null {\n if (typeof window === \"undefined\") {\n return null;\n }\n\n const raw = window.localStorage.getItem(storageKey);\n if (raw) {\n try {\n const parsed = JSON.parse(raw);\n if (parsed && typeof parsed === \"object\") {\n return parsed as PersistedState;\n }\n } catch {\n // Fall through to cookie migration path\n }\n }\n\n // Backwards compatibility: try to read the legacy cookie and migrate it\n if (typeof document !== \"undefined\") {\n const prefix = `${storageKey}=`;\n const entry = document.cookie\n .split(\"; \")\n .find((cookie) => cookie.startsWith(prefix));\n if (entry) {\n const legacyRaw = entry.substring(prefix.length);\n try {\n const parsed = JSON.parse(decodeURIComponent(legacyRaw));\n if (parsed && typeof parsed === \"object\") {\n return parsed as PersistedState;\n }\n } catch {\n return null;\n }\n }\n }\n\n return null;\n}\n\nexport function saveInspectorState(\n storageKey: string,\n state: PersistedState,\n): void {\n if (typeof window === \"undefined\") {\n return;\n }\n\n try {\n window.localStorage.setItem(storageKey, JSON.stringify(state));\n } catch (error) {\n console.warn(\"Failed to persist inspector state\", error);\n }\n}\n\nexport function isValidAnchor(value: unknown): value is Anchor {\n if (!value || typeof value !== \"object\") {\n return false;\n }\n\n const candidate = value as Anchor;\n return (\n (candidate.horizontal === \"left\" || candidate.horizontal === \"right\") &&\n (candidate.vertical === \"top\" || candidate.vertical === \"bottom\")\n );\n}\n\nexport function isValidPosition(value: unknown): value is Position {\n if (!value || typeof value !== \"object\") {\n return false;\n }\n\n const candidate = value as Position;\n return isFiniteNumber(candidate.x) && isFiniteNumber(candidate.y);\n}\n\nexport function isValidSize(value: unknown): value is Size {\n if (!value || typeof value !== \"object\") {\n return false;\n }\n\n const candidate = value as Size;\n return isFiniteNumber(candidate.width) && isFiniteNumber(candidate.height);\n}\n\nexport function isFiniteNumber(value: unknown): value is number {\n return typeof value === \"number\" && Number.isFinite(value);\n}\n\nexport function isValidDockMode(value: unknown): value is DockMode {\n return value === \"floating\" || value === \"docked-left\";\n}\n\n// Telemetry persistence — flat per-key localStorage rather than the\n// JSON-blob shape used for window/dock state, because each value is\n// independent and we want to read/write them without round-tripping\n// the whole inspector state object.\nconst TELEMETRY_DISTINCT_ID_KEY = \"cpk:inspector:telemetry:distinct_id\";\nconst TELEMETRY_OPT_OUT_KEY = \"cpk:inspector:telemetry:opt_out\";\nconst TELEMETRY_DISCLOSURE_SHOWN_KEY =\n \"cpk:inspector:telemetry:disclosure_shown\";\n\n// Module-level fallback for when localStorage is unavailable (private mode,\n// quota exceeded, etc.). Cached so that banner_viewed and banner_clicked from\n// the same page-load share one distinct_id even without persistent storage —\n// funnel coherence within a session is preserved even when storage fails.\nlet inMemoryFallbackId: string | null = null;\n\nexport function getOrCreateTelemetryDistinctId(): string {\n if (typeof window === \"undefined\") {\n // SSR / test fallback. A non-persistent ID is preferable to throwing\n // because telemetry must never break the host application.\n return generateUuidV4();\n }\n\n try {\n const existing = window.localStorage.getItem(TELEMETRY_DISTINCT_ID_KEY);\n if (existing && existing.length > 0) return existing;\n const fresh = generateUuidV4();\n window.localStorage.setItem(TELEMETRY_DISTINCT_ID_KEY, fresh);\n return fresh;\n } catch {\n return (inMemoryFallbackId ??= generateUuidV4());\n }\n}\n\n// Test-only reset so the in-memory fallback doesn't leak between test cases.\nexport function _resetTelemetryPersistenceForTesting(): void {\n inMemoryFallbackId = null;\n}\n\nexport function isTelemetryOptedOut(): boolean {\n if (typeof window === \"undefined\") return false;\n try {\n return window.localStorage.getItem(TELEMETRY_OPT_OUT_KEY) === \"true\";\n } catch {\n return false;\n }\n}\n\nexport function setTelemetryOptOut(optedOut: boolean): void {\n if (typeof window === \"undefined\") return;\n try {\n if (optedOut) {\n window.localStorage.setItem(TELEMETRY_OPT_OUT_KEY, \"true\");\n } else {\n window.localStorage.removeItem(TELEMETRY_OPT_OUT_KEY);\n }\n } catch {\n // No-op — see getOrCreateTelemetryDistinctId.\n }\n}\n\nexport function hasTelemetryDisclosureBeenShown(): boolean {\n if (typeof window === \"undefined\") return false;\n try {\n return (\n window.localStorage.getItem(TELEMETRY_DISCLOSURE_SHOWN_KEY) === \"true\"\n );\n } catch {\n return false;\n }\n}\n\nexport function markTelemetryDisclosureShown(): void {\n if (typeof window === \"undefined\") return;\n try {\n window.localStorage.setItem(TELEMETRY_DISCLOSURE_SHOWN_KEY, \"true\");\n } catch {\n // No-op.\n }\n}\n\nfunction generateUuidV4(): string {\n if (\n typeof globalThis.crypto !== \"undefined\" &&\n typeof globalThis.crypto.randomUUID === \"function\"\n ) {\n return globalThis.crypto.randomUUID();\n }\n // Fallback for environments without crypto.randomUUID (older browsers,\n // some test runners). Not cryptographically strong; acceptable because\n // the value is just an anonymous correlation ID.\n return \"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx\".replace(/[xy]/g, (c) => {\n const r = (Math.random() * 16) | 0;\n const v = c === \"x\" ? r : (r & 0x3) | 0x8;\n return v.toString(16);\n });\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"persistence.mjs","names":[],"sources":["../../src/lib/persistence.ts"],"sourcesContent":["import type { Anchor, DockMode, Position, Size } from \"./types\";\n\nexport type PersistedContextState = {\n anchor?: Anchor;\n anchorOffset?: Position;\n size?: Size;\n hasCustomPosition?: boolean;\n};\n\nexport type PersistedState = {\n button?: Omit<PersistedContextState, \"size\">;\n window?: PersistedContextState;\n isOpen?: boolean;\n dockMode?: DockMode;\n selectedMenu?: string;\n selectedContext?: string;\n};\n\nexport function loadInspectorState(storageKey: string): PersistedState | null {\n if (typeof window === \"undefined\") {\n return null;\n }\n\n const raw = window.localStorage.getItem(storageKey);\n if (raw) {\n try {\n const parsed = JSON.parse(raw);\n if (parsed && typeof parsed === \"object\") {\n return parsed as PersistedState;\n }\n } catch {\n // Fall through to cookie migration path\n }\n }\n\n // Backwards compatibility: try to read the legacy cookie and migrate it\n if (typeof document !== \"undefined\") {\n const prefix = `${storageKey}=`;\n const entry = document.cookie\n .split(\"; \")\n .find((cookie) => cookie.startsWith(prefix));\n if (entry) {\n const legacyRaw = entry.substring(prefix.length);\n try {\n const parsed = JSON.parse(decodeURIComponent(legacyRaw));\n if (parsed && typeof parsed === \"object\") {\n return parsed as PersistedState;\n }\n } catch {\n return null;\n }\n }\n }\n\n return null;\n}\n\nexport function saveInspectorState(\n storageKey: string,\n state: PersistedState,\n): void {\n if (typeof window === \"undefined\") {\n return;\n }\n\n try {\n window.localStorage.setItem(storageKey, JSON.stringify(state));\n } catch (error) {\n console.warn(\"Failed to persist inspector state\", error);\n }\n}\n\nexport function isValidAnchor(value: unknown): value is Anchor {\n if (!value || typeof value !== \"object\") {\n return false;\n }\n\n const candidate = value as Anchor;\n return (\n (candidate.horizontal === \"left\" || candidate.horizontal === \"right\") &&\n (candidate.vertical === \"top\" || candidate.vertical === \"bottom\")\n );\n}\n\nexport function isValidPosition(value: unknown): value is Position {\n if (!value || typeof value !== \"object\") {\n return false;\n }\n\n const candidate = value as Position;\n return isFiniteNumber(candidate.x) && isFiniteNumber(candidate.y);\n}\n\nexport function isValidSize(value: unknown): value is Size {\n if (!value || typeof value !== \"object\") {\n return false;\n }\n\n const candidate = value as Size;\n return isFiniteNumber(candidate.width) && isFiniteNumber(candidate.height);\n}\n\nexport function isFiniteNumber(value: unknown): value is number {\n return typeof value === \"number\" && Number.isFinite(value);\n}\n\nexport function isValidDockMode(value: unknown): value is DockMode {\n return value === \"floating\" || value === \"docked-left\";\n}\n\n// Telemetry persistence — flat per-key localStorage rather than the\n// JSON-blob shape used for window/dock state, because each value is\n// independent and we want to read/write them without round-tripping\n// the whole inspector state object.\nconst TELEMETRY_DISTINCT_ID_KEY = \"cpk:inspector:telemetry:distinct_id\";\nconst TELEMETRY_OPT_OUT_KEY = \"cpk:inspector:telemetry:opt_out\";\nconst TELEMETRY_DISCLOSURE_SHOWN_KEY =\n \"cpk:inspector:telemetry:disclosure_shown\";\n\n// Module-level fallback for when localStorage is unavailable (private mode,\n// quota exceeded, etc.). Cached so that banner_viewed and banner_clicked from\n// the same page-load share one distinct_id even without persistent storage —\n// funnel coherence within a session is preserved even when storage fails.\nlet inMemoryFallbackId: string | null = null;\n\nexport function getOrCreateTelemetryDistinctId(): string {\n if (typeof window === \"undefined\") {\n // SSR / test fallback. A non-persistent ID is preferable to throwing\n // because telemetry must never break the host application.\n return generateUuidV4();\n }\n\n try {\n const existing = window.localStorage.getItem(TELEMETRY_DISTINCT_ID_KEY);\n if (existing && existing.length > 0) return existing;\n const fresh = generateUuidV4();\n window.localStorage.setItem(TELEMETRY_DISTINCT_ID_KEY, fresh);\n return fresh;\n } catch {\n return (inMemoryFallbackId ??= generateUuidV4());\n }\n}\n\n// Test-only reset so the in-memory fallback doesn't leak between test cases.\nexport function _resetTelemetryPersistenceForTesting(): void {\n inMemoryFallbackId = null;\n}\n\nexport function isTelemetryOptedOut(): boolean {\n if (typeof window === \"undefined\") return false;\n try {\n return window.localStorage.getItem(TELEMETRY_OPT_OUT_KEY) === \"true\";\n } catch {\n return false;\n }\n}\n\nexport function setTelemetryOptOut(optedOut: boolean): void {\n if (typeof window === \"undefined\") return;\n try {\n if (optedOut) {\n window.localStorage.setItem(TELEMETRY_OPT_OUT_KEY, \"true\");\n } else {\n window.localStorage.removeItem(TELEMETRY_OPT_OUT_KEY);\n }\n } catch {\n // No-op — see getOrCreateTelemetryDistinctId.\n }\n}\n\nexport function hasTelemetryDisclosureBeenShown(): boolean {\n if (typeof window === \"undefined\") return false;\n try {\n return (\n window.localStorage.getItem(TELEMETRY_DISCLOSURE_SHOWN_KEY) === \"true\"\n );\n } catch {\n return false;\n }\n}\n\nexport function markTelemetryDisclosureShown(): void {\n if (typeof window === \"undefined\") return;\n try {\n window.localStorage.setItem(TELEMETRY_DISCLOSURE_SHOWN_KEY, \"true\");\n } catch {\n // No-op.\n }\n}\n\nfunction generateUuidV4(): string {\n if (\n typeof globalThis.crypto !== \"undefined\" &&\n typeof globalThis.crypto.randomUUID === \"function\"\n ) {\n return globalThis.crypto.randomUUID();\n }\n // Fallback for environments without crypto.randomUUID (older browsers,\n // some test runners). Not cryptographically strong; acceptable because\n // the value is just an anonymous correlation ID.\n return \"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx\".replace(/[xy]/g, (c) => {\n const r = (Math.random() * 16) | 0;\n const v = c === \"x\" ? r : (r & 0x3) | 0x8;\n return v.toString(16);\n });\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"persistence.mjs","names":[],"sources":["../../src/lib/persistence.ts"],"sourcesContent":["import type { Anchor, DockMode, Position, Size } from \"./types.js\";\n\nexport type PersistedContextState = {\n anchor?: Anchor;\n anchorOffset?: Position;\n size?: Size;\n hasCustomPosition?: boolean;\n};\n\nexport type PersistedState = {\n button?: Omit<PersistedContextState, \"size\">;\n window?: PersistedContextState;\n isOpen?: boolean;\n dockMode?: DockMode;\n selectedMenu?: string;\n selectedContext?: string;\n};\n\nexport function loadInspectorState(storageKey: string): PersistedState | null {\n if (typeof window === \"undefined\") {\n return null;\n }\n\n const raw = window.localStorage.getItem(storageKey);\n if (raw) {\n try {\n const parsed = JSON.parse(raw);\n if (parsed && typeof parsed === \"object\") {\n return parsed as PersistedState;\n }\n } catch {\n // Fall through to cookie migration path\n }\n }\n\n // Backwards compatibility: try to read the legacy cookie and migrate it\n if (typeof document !== \"undefined\") {\n const prefix = `${storageKey}=`;\n const entry = document.cookie\n .split(\"; \")\n .find((cookie) => cookie.startsWith(prefix));\n if (entry) {\n const legacyRaw = entry.substring(prefix.length);\n try {\n const parsed = JSON.parse(decodeURIComponent(legacyRaw));\n if (parsed && typeof parsed === \"object\") {\n return parsed as PersistedState;\n }\n } catch {\n return null;\n }\n }\n }\n\n return null;\n}\n\nexport function saveInspectorState(\n storageKey: string,\n state: PersistedState,\n): void {\n if (typeof window === \"undefined\") {\n return;\n }\n\n try {\n window.localStorage.setItem(storageKey, JSON.stringify(state));\n } catch (error) {\n console.warn(\"Failed to persist inspector state\", error);\n }\n}\n\nexport function isValidAnchor(value: unknown): value is Anchor {\n if (!value || typeof value !== \"object\") {\n return false;\n }\n\n const candidate = value as Anchor;\n return (\n (candidate.horizontal === \"left\" || candidate.horizontal === \"right\") &&\n (candidate.vertical === \"top\" || candidate.vertical === \"bottom\")\n );\n}\n\nexport function isValidPosition(value: unknown): value is Position {\n if (!value || typeof value !== \"object\") {\n return false;\n }\n\n const candidate = value as Position;\n return isFiniteNumber(candidate.x) && isFiniteNumber(candidate.y);\n}\n\nexport function isValidSize(value: unknown): value is Size {\n if (!value || typeof value !== \"object\") {\n return false;\n }\n\n const candidate = value as Size;\n return isFiniteNumber(candidate.width) && isFiniteNumber(candidate.height);\n}\n\nexport function isFiniteNumber(value: unknown): value is number {\n return typeof value === \"number\" && Number.isFinite(value);\n}\n\nexport function isValidDockMode(value: unknown): value is DockMode {\n return value === \"floating\" || value === \"docked-left\";\n}\n\n// Telemetry persistence — flat per-key localStorage rather than the\n// JSON-blob shape used for window/dock state, because each value is\n// independent and we want to read/write them without round-tripping\n// the whole inspector state object.\nconst TELEMETRY_DISTINCT_ID_KEY = \"cpk:inspector:telemetry:distinct_id\";\nconst TELEMETRY_OPT_OUT_KEY = \"cpk:inspector:telemetry:opt_out\";\nconst TELEMETRY_DISCLOSURE_SHOWN_KEY =\n \"cpk:inspector:telemetry:disclosure_shown\";\n\n// Module-level fallback for when localStorage is unavailable (private mode,\n// quota exceeded, etc.). Cached so that banner_viewed and banner_clicked from\n// the same page-load share one distinct_id even without persistent storage —\n// funnel coherence within a session is preserved even when storage fails.\nlet inMemoryFallbackId: string | null = null;\n\nexport function getOrCreateTelemetryDistinctId(): string {\n if (typeof window === \"undefined\") {\n // SSR / test fallback. A non-persistent ID is preferable to throwing\n // because telemetry must never break the host application.\n return generateUuidV4();\n }\n\n try {\n const existing = window.localStorage.getItem(TELEMETRY_DISTINCT_ID_KEY);\n if (existing && existing.length > 0) return existing;\n const fresh = generateUuidV4();\n window.localStorage.setItem(TELEMETRY_DISTINCT_ID_KEY, fresh);\n return fresh;\n } catch {\n return (inMemoryFallbackId ??= generateUuidV4());\n }\n}\n\n// Test-only reset so the in-memory fallback doesn't leak between test cases.\nexport function _resetTelemetryPersistenceForTesting(): void {\n inMemoryFallbackId = null;\n}\n\nexport function isTelemetryOptedOut(): boolean {\n if (typeof window === \"undefined\") return false;\n try {\n return window.localStorage.getItem(TELEMETRY_OPT_OUT_KEY) === \"true\";\n } catch {\n return false;\n }\n}\n\nexport function setTelemetryOptOut(optedOut: boolean): void {\n if (typeof window === \"undefined\") return;\n try {\n if (optedOut) {\n window.localStorage.setItem(TELEMETRY_OPT_OUT_KEY, \"true\");\n } else {\n window.localStorage.removeItem(TELEMETRY_OPT_OUT_KEY);\n }\n } catch {\n // No-op — see getOrCreateTelemetryDistinctId.\n }\n}\n\nexport function hasTelemetryDisclosureBeenShown(): boolean {\n if (typeof window === \"undefined\") return false;\n try {\n return (\n window.localStorage.getItem(TELEMETRY_DISCLOSURE_SHOWN_KEY) === \"true\"\n );\n } catch {\n return false;\n }\n}\n\nexport function markTelemetryDisclosureShown(): void {\n if (typeof window === \"undefined\") return;\n try {\n window.localStorage.setItem(TELEMETRY_DISCLOSURE_SHOWN_KEY, \"true\");\n } catch {\n // No-op.\n }\n}\n\nfunction generateUuidV4(): string {\n if (\n typeof globalThis.crypto !== \"undefined\" &&\n typeof globalThis.crypto.randomUUID === \"function\"\n ) {\n return globalThis.crypto.randomUUID();\n }\n // Fallback for environments without crypto.randomUUID (older browsers,\n // some test runners). Not cryptographically strong; acceptable because\n // the value is just an anonymous correlation ID.\n return \"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx\".replace(/[xy]/g, (c) => {\n const r = (Math.random() * 16) | 0;\n const v = c === \"x\" ? r : (r & 0x3) | 0x8;\n return v.toString(16);\n });\n}\n"],"mappings":"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"}
@@ -1,15 +1,40 @@
1
1
  const require_persistence = require('./persistence.cjs');
2
+ const require_package = require('../package.cjs');
2
3
 
3
4
  //#region src/lib/telemetry.ts
4
5
  const TELEMETRY_EVENTS = {
5
6
  bannerViewed: "oss.inspector.banner_viewed",
6
7
  bannerClicked: "oss.inspector.banner_clicked",
7
- threadsTabClicked: "oss.inspector.threads_tab_clicked"
8
+ threadsTabClicked: "oss.inspector.threads_tab_clicked",
9
+ threadsLockedViewed: "oss.inspector.threads_locked_viewed",
10
+ threadsIntelligenceSignupClicked: "oss.inspector.threads_intelligence_signup_clicked",
11
+ threadsTalkToEngineerClicked: "oss.inspector.threads_talk_to_engineer_clicked",
12
+ talkToEngineerClicked: "oss.inspector.talk_to_engineer_clicked",
13
+ threadsEmptyEnabledViewed: "oss.inspector.threads_empty_enabled_viewed",
14
+ threadsEnabledViewed: "oss.inspector.threads_enabled_viewed"
8
15
  };
9
16
  const TELEMETRY_INGEST_URL = "https://telemetry.copilotkit.ai/ingest";
10
17
  const TELEMETRY_DOCS_URL = "https://docs.copilotkit.ai/telemetry";
11
18
  const PACKAGE_NAME = "@copilotkit/web-inspector";
19
+ const PACKAGE_VERSION = require_package.version;
12
20
  const FETCH_TIMEOUT_MS = 3e3;
21
+ function isThreadsTelemetryEvent(event) {
22
+ return event === TELEMETRY_EVENTS.threadsTabClicked || event === TELEMETRY_EVENTS.threadsLockedViewed || event === TELEMETRY_EVENTS.threadsIntelligenceSignupClicked || event === TELEMETRY_EVENTS.threadsTalkToEngineerClicked || event === TELEMETRY_EVENTS.talkToEngineerClicked || event === TELEMETRY_EVENTS.threadsEmptyEnabledViewed || event === TELEMETRY_EVENTS.threadsEnabledViewed;
23
+ }
24
+ function getRuntimeUrlType(runtimeUrl) {
25
+ if (!runtimeUrl) return "missing";
26
+ if (runtimeUrl.startsWith("/") && !runtimeUrl.startsWith("//")) return "relative";
27
+ try {
28
+ const baseHref = typeof window !== "undefined" ? window.location.href : "https://copilotkit.ai";
29
+ const url = new URL(runtimeUrl, baseHref);
30
+ const baseUrl = new URL(baseHref);
31
+ const hostname = url.hostname.toLowerCase();
32
+ if (hostname === "localhost" || hostname === "127.0.0.1" || hostname === "[::1]") return "localhost";
33
+ return url.origin === baseUrl.origin ? "same_origin" : "remote";
34
+ } catch {
35
+ return "invalid";
36
+ }
37
+ }
13
38
  /**
14
39
  * Fire-and-forget telemetry send. Returns synchronously; the network
15
40
  * call is dispatched in the background and any failure is swallowed.
@@ -19,16 +44,32 @@ const FETCH_TIMEOUT_MS = 3e3;
19
44
  * inspector's mount lifecycle instead.
20
45
  */
21
46
  function track(event, properties = {}) {
47
+ if (require_persistence.isTelemetryOptedOut()) return;
22
48
  const distinctId = require_persistence.getOrCreateTelemetryDistinctId();
23
- postBestEffort(TELEMETRY_INGEST_URL, JSON.stringify({
24
- event,
25
- properties: {
26
- ...properties,
27
- distinct_id: distinctId
28
- },
29
- package: { name: PACKAGE_NAME },
30
- ts: Math.floor(Date.now() / 1e3)
31
- }), distinctId);
49
+ const threadsProperties = isThreadsTelemetryEvent(event) ? {
50
+ package_name: PACKAGE_NAME,
51
+ package_version: PACKAGE_VERSION,
52
+ inspector_distinct_id: distinctId
53
+ } : {};
54
+ let body;
55
+ try {
56
+ body = JSON.stringify({
57
+ event,
58
+ properties: {
59
+ ...properties,
60
+ ...threadsProperties,
61
+ distinct_id: distinctId
62
+ },
63
+ package: {
64
+ name: PACKAGE_NAME,
65
+ ...isThreadsTelemetryEvent(event) ? { version: PACKAGE_VERSION } : {}
66
+ },
67
+ ts: Math.floor(Date.now() / 1e3)
68
+ });
69
+ } catch {
70
+ return;
71
+ }
72
+ postBestEffort(TELEMETRY_INGEST_URL, body, distinctId);
32
73
  }
33
74
  function trackBannerViewed(props) {
34
75
  track(TELEMETRY_EVENTS.bannerViewed, props);
@@ -36,8 +77,26 @@ function trackBannerViewed(props) {
36
77
  function trackBannerClicked(props) {
37
78
  track(TELEMETRY_EVENTS.bannerClicked, props);
38
79
  }
39
- function trackThreadsTabClicked() {
40
- track(TELEMETRY_EVENTS.threadsTabClicked);
80
+ function trackThreadsTabClicked(props = {}) {
81
+ track(TELEMETRY_EVENTS.threadsTabClicked, props);
82
+ }
83
+ function trackThreadsLockedViewed(props) {
84
+ track(TELEMETRY_EVENTS.threadsLockedViewed, props);
85
+ }
86
+ function trackThreadsIntelligenceSignupClicked(props) {
87
+ track(TELEMETRY_EVENTS.threadsIntelligenceSignupClicked, props);
88
+ }
89
+ function trackThreadsTalkToEngineerClicked(props) {
90
+ track(TELEMETRY_EVENTS.threadsTalkToEngineerClicked, props);
91
+ }
92
+ function trackTalkToEngineerClicked(props) {
93
+ track(TELEMETRY_EVENTS.talkToEngineerClicked, props);
94
+ }
95
+ function trackThreadsEmptyEnabledViewed(props) {
96
+ track(TELEMETRY_EVENTS.threadsEmptyEnabledViewed, props);
97
+ }
98
+ function trackThreadsEnabledViewed(props) {
99
+ track(TELEMETRY_EVENTS.threadsEnabledViewed, props);
41
100
  }
42
101
  /**
43
102
  * Returns the inspector's anonymous distinct-ID for cross-domain
@@ -104,9 +163,16 @@ async function postBestEffort(url, body, distinctId) {
104
163
  //#endregion
105
164
  exports.TELEMETRY_DOCS_URL = TELEMETRY_DOCS_URL;
106
165
  exports.ensureTelemetryDistinctId = ensureTelemetryDistinctId;
166
+ exports.getRuntimeUrlType = getRuntimeUrlType;
107
167
  exports.getTelemetryDistinctIdForUrl = getTelemetryDistinctIdForUrl;
108
168
  exports.maybeShowDisclosure = maybeShowDisclosure;
109
169
  exports.trackBannerClicked = trackBannerClicked;
110
170
  exports.trackBannerViewed = trackBannerViewed;
171
+ exports.trackTalkToEngineerClicked = trackTalkToEngineerClicked;
172
+ exports.trackThreadsEmptyEnabledViewed = trackThreadsEmptyEnabledViewed;
173
+ exports.trackThreadsEnabledViewed = trackThreadsEnabledViewed;
174
+ exports.trackThreadsIntelligenceSignupClicked = trackThreadsIntelligenceSignupClicked;
175
+ exports.trackThreadsLockedViewed = trackThreadsLockedViewed;
111
176
  exports.trackThreadsTabClicked = trackThreadsTabClicked;
177
+ exports.trackThreadsTalkToEngineerClicked = trackThreadsTalkToEngineerClicked;
112
178
  //# sourceMappingURL=telemetry.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"telemetry.cjs","names":["getOrCreateTelemetryDistinctId","isTelemetryOptedOut","hasTelemetryDisclosureBeenShown"],"sources":["../../src/lib/telemetry.ts"],"sourcesContent":["// Inspector-side anonymous telemetry. Three V1 events fire from index.ts —\n// `oss.inspector.banner_viewed`, `oss.inspector.banner_clicked`, and\n// `oss.inspector.threads_tab_clicked`. POSTs directly from the browser\n// to the CopilotKit telemetry sink at `telemetry.copilotkit.ai/ingest`,\n// where a Lambda fan-out forwards events to PostHog / Reo / Scarf.\n//\n// The endpoint URL is intentionally clearly named so it's obvious in\n// DevTools / Network tab — transparency for opt-in users.\n//\n// Privacy invariants enforced here:\n// - We never send message content, agent state, prompts, completions,\n// or banner markdown. Properties are scoped to event metadata only\n// (banner_id/timestamp, cta location). Reviewers should grep call\n// sites for any unintended payload.\n// - The opt-out short-circuits before any network call. There is no\n// buffer, no retry queue.\n// - All errors are swallowed; telemetry must never break the host app.\n\nimport {\n getOrCreateTelemetryDistinctId,\n hasTelemetryDisclosureBeenShown,\n isTelemetryOptedOut,\n markTelemetryDisclosureShown,\n} from \"./persistence\";\n\n// V1 funnel events. Namespaced `oss.inspector.*` so the lambda's\n// event-type allowlist (oss-path-to-production) can gate them\n// server-side. Adding a new event here requires a corresponding\n// allowlist entry on the lambda or events will be rejected.\nexport const TELEMETRY_EVENTS = {\n bannerViewed: \"oss.inspector.banner_viewed\",\n bannerClicked: \"oss.inspector.banner_clicked\",\n threadsTabClicked: \"oss.inspector.threads_tab_clicked\",\n} as const;\n\nexport type TelemetryEvent =\n (typeof TELEMETRY_EVENTS)[keyof typeof TELEMETRY_EVENTS];\n\n// Per the OSS-96 ticket — the URL is intentionally clearly named for\n// transparency in the network tab.\nexport const TELEMETRY_INGEST_URL = \"https://telemetry.copilotkit.ai/ingest\";\n\n// Surfaced in console disclosure and the in-product opt-out panel.\n// Keep in sync with the canonical telemetry docs page on main\n// (`docs/content/docs/(root)/(other)/telemetry/index.mdx`).\n// Mirror constant: packages/runtime/src/lib/telemetry-disclosure.ts\nexport const TELEMETRY_DOCS_URL = \"https://docs.copilotkit.ai/telemetry\";\n\nconst PACKAGE_NAME = \"@copilotkit/web-inspector\";\n\n// 3-second cap so a slow gateway can't hang the host app. Matches the\n// runtime's existing scarf-client convention.\nconst FETCH_TIMEOUT_MS = 3000;\n\n/**\n * Fire-and-forget telemetry send. Returns synchronously; the network\n * call is dispatched in the background and any failure is swallowed.\n *\n * Short-circuits when the user has opted out. Does NOT itself trigger\n * the first-run disclosure — call `maybeShowDisclosure()` from the\n * inspector's mount lifecycle instead.\n */\nexport function track(\n event: TelemetryEvent,\n properties: Record<string, unknown> = {},\n): void {\n const distinctId = getOrCreateTelemetryDistinctId();\n const body = JSON.stringify({\n event,\n properties: {\n ...properties,\n distinct_id: distinctId,\n },\n package: { name: PACKAGE_NAME },\n ts: Math.floor(Date.now() / 1000),\n });\n\n void postBestEffort(TELEMETRY_INGEST_URL, body, distinctId);\n}\n\n// --- Typed per-event helpers ---\n// These enforce the known property shape for each V1 event at the call\n// site, so callers can't accidentally include PII under a wrong key.\n\nexport function trackBannerViewed(props: {\n banner_id: string;\n cta_label?: string;\n}): void {\n track(TELEMETRY_EVENTS.bannerViewed, props);\n}\n\nexport function trackBannerClicked(props: {\n banner_id: string;\n cta: \"body\" | \"dismiss\";\n cta_label?: string;\n}): void {\n track(TELEMETRY_EVENTS.bannerClicked, props);\n}\n\nexport function trackThreadsTabClicked(): void {\n track(TELEMETRY_EVENTS.threadsTabClicked);\n}\n\n/**\n * Returns the inspector's anonymous distinct-ID for cross-domain\n * propagation onto outbound banner-CTA links, or `null` when the user\n * is opted out.\n *\n * The website / Ops API reads this query param on signup-flow landing\n * pages and calls `posthog.alias(...)` to merge the inspector's anon\n * ID with the website's anon ID, enabling the\n * `banner_viewed → banner_clicked → signup_attributed` funnel.\n * `identify()` itself is out of scope here (it happens on signup, in\n * the website / Ops API).\n *\n * Opt-out short-circuits this too: if the user has opted out, we do\n * NOT leak an anon ID across domains.\n */\nexport function getTelemetryDistinctIdForUrl(): string | null {\n if (isTelemetryOptedOut()) return null;\n return getOrCreateTelemetryDistinctId();\n}\n\n/**\n * Seeds the anonymous distinct-ID into localStorage on inspector mount\n * so it is ready for cross-domain propagation onto banner-CTA links\n * even before the first event fires. No-op when the user has opted out.\n */\nexport function ensureTelemetryDistinctId(): void {\n if (isTelemetryOptedOut()) return;\n getOrCreateTelemetryDistinctId();\n}\n\n/**\n * Fires the one-time console disclosure on inspector mount, when the\n * user is not opted out and hasn't seen it before. Idempotent across\n * calls within a single session because `markTelemetryDisclosureShown`\n * persists to localStorage.\n *\n * If the user is opted out, we deliberately do nothing and do NOT mark\n * the flag — so a future opt-in flips back to \"first run\" behavior.\n */\nexport function maybeShowDisclosure(): void {\n if (isTelemetryOptedOut()) return;\n if (hasTelemetryDisclosureBeenShown()) return;\n // eslint-disable-next-line no-console\n console.info(\n `[CopilotKit Inspector] anonymous interaction telemetry enabled — see ${TELEMETRY_DOCS_URL} to opt out.`,\n );\n markTelemetryDisclosureShown();\n}\n\nexport { isTelemetryOptedOut };\n\nasync function postBestEffort(\n url: string,\n body: string,\n distinctId: string,\n): Promise<void> {\n if (typeof fetch === \"undefined\") return;\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), FETCH_TIMEOUT_MS);\n try {\n await fetch(url, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n \"X-CopilotKit-Telemetry-Id\": distinctId,\n },\n body,\n signal: controller.signal,\n // No credentials / no Authorization header — anonymous endpoint.\n });\n } catch {\n // Silent failure — telemetry must not break the application.\n } finally {\n clearTimeout(timeoutId);\n }\n}\n"],"mappings":";;;AA6BA,MAAa,mBAAmB;CAC9B,cAAc;CACd,eAAe;CACf,mBAAmB;CACpB;AAOD,MAAa,uBAAuB;AAMpC,MAAa,qBAAqB;AAElC,MAAM,eAAe;AAIrB,MAAM,mBAAmB;;;;;;;;;AAUzB,SAAgB,MACd,OACA,aAAsC,EAAE,EAClC;CACN,MAAM,aAAaA,oDAAgC;AAWnD,CAAK,eAAe,sBAVP,KAAK,UAAU;EAC1B;EACA,YAAY;GACV,GAAG;GACH,aAAa;GACd;EACD,SAAS,EAAE,MAAM,cAAc;EAC/B,IAAI,KAAK,MAAM,KAAK,KAAK,GAAG,IAAK;EAClC,CAAC,EAE8C,WAAW;;AAO7D,SAAgB,kBAAkB,OAGzB;AACP,OAAM,iBAAiB,cAAc,MAAM;;AAG7C,SAAgB,mBAAmB,OAI1B;AACP,OAAM,iBAAiB,eAAe,MAAM;;AAG9C,SAAgB,yBAA+B;AAC7C,OAAM,iBAAiB,kBAAkB;;;;;;;;;;;;;;;;;AAkB3C,SAAgB,+BAA8C;AAC5D,KAAIC,yCAAqB,CAAE,QAAO;AAClC,QAAOD,oDAAgC;;;;;;;AAQzC,SAAgB,4BAAkC;AAChD,KAAIC,yCAAqB,CAAE;AAC3B,qDAAgC;;;;;;;;;;;AAYlC,SAAgB,sBAA4B;AAC1C,KAAIA,yCAAqB,CAAE;AAC3B,KAAIC,qDAAiC,CAAE;AAEvC,SAAQ,KACN,wEAAwE,mBAAmB,cAC5F;AACD,mDAA8B;;AAKhC,eAAe,eACb,KACA,MACA,YACe;AACf,KAAI,OAAO,UAAU,YAAa;CAClC,MAAM,aAAa,IAAI,iBAAiB;CACxC,MAAM,YAAY,iBAAiB,WAAW,OAAO,EAAE,iBAAiB;AACxE,KAAI;AACF,QAAM,MAAM,KAAK;GACf,QAAQ;GACR,SAAS;IACP,gBAAgB;IAChB,6BAA6B;IAC9B;GACD;GACA,QAAQ,WAAW;GAEpB,CAAC;SACI,WAEE;AACR,eAAa,UAAU"}
1
+ {"version":3,"file":"telemetry.cjs","names":["isTelemetryOptedOut","getOrCreateTelemetryDistinctId","hasTelemetryDisclosureBeenShown"],"sources":["../../src/lib/telemetry.ts"],"sourcesContent":["// Inspector-side anonymous telemetry. V1 events fire from index.ts for\n// banner and thread-inspection interactions. POSTs directly from the browser\n// to the CopilotKit telemetry sink at `telemetry.copilotkit.ai/ingest`,\n// where a Lambda fan-out forwards events to PostHog / Reo / Scarf.\n//\n// The endpoint URL is intentionally clearly named so it's obvious in\n// DevTools / Network tab — transparency for opt-in users.\n//\n// Privacy invariants enforced here:\n// - We never send message content, agent state, prompts, completions,\n// or banner markdown. Properties are scoped to event metadata only\n// (banner_id/timestamp, cta location). Reviewers should grep call\n// sites for any unintended payload.\n// - The opt-out short-circuits before any network call. There is no\n// buffer, no retry queue.\n// - All errors are swallowed; telemetry must never break the host app.\n\nimport {\n getOrCreateTelemetryDistinctId,\n hasTelemetryDisclosureBeenShown,\n isTelemetryOptedOut,\n markTelemetryDisclosureShown,\n} from \"./persistence.js\";\nimport packageJson from \"../../package.json\" with { type: \"json\" };\n\n// V1 funnel events. Namespaced `oss.inspector.*` so the lambda's\n// owned-prefix gate (oss-path-to-production) can accept them server-side\n// without a per-event sink deploy.\nexport const TELEMETRY_EVENTS = {\n bannerViewed: \"oss.inspector.banner_viewed\",\n bannerClicked: \"oss.inspector.banner_clicked\",\n threadsTabClicked: \"oss.inspector.threads_tab_clicked\",\n threadsLockedViewed: \"oss.inspector.threads_locked_viewed\",\n threadsIntelligenceSignupClicked:\n \"oss.inspector.threads_intelligence_signup_clicked\",\n threadsTalkToEngineerClicked:\n \"oss.inspector.threads_talk_to_engineer_clicked\",\n talkToEngineerClicked: \"oss.inspector.talk_to_engineer_clicked\",\n threadsEmptyEnabledViewed: \"oss.inspector.threads_empty_enabled_viewed\",\n threadsEnabledViewed: \"oss.inspector.threads_enabled_viewed\",\n} as const;\n\nexport type TelemetryEvent =\n (typeof TELEMETRY_EVENTS)[keyof typeof TELEMETRY_EVENTS];\n\n// Per the OSS-96 ticket — the URL is intentionally clearly named for\n// transparency in the network tab.\nexport const TELEMETRY_INGEST_URL = \"https://telemetry.copilotkit.ai/ingest\";\n\n// Surfaced in console disclosure and the in-product opt-out panel.\n// Keep in sync with the canonical telemetry docs page on main\n// (`docs/content/docs/(root)/(other)/telemetry/index.mdx`).\n// Mirror constant: packages/runtime/src/lib/telemetry-disclosure.ts\nexport const TELEMETRY_DOCS_URL = \"https://docs.copilotkit.ai/telemetry\";\n\nconst PACKAGE_NAME = \"@copilotkit/web-inspector\";\nconst PACKAGE_VERSION = packageJson.version;\n\n// 3-second cap so a slow gateway can't hang the host app. Matches the\n// runtime's existing scarf-client convention.\nconst FETCH_TIMEOUT_MS = 3000;\n\nfunction isThreadsTelemetryEvent(event: TelemetryEvent): boolean {\n return (\n event === TELEMETRY_EVENTS.threadsTabClicked ||\n event === TELEMETRY_EVENTS.threadsLockedViewed ||\n event === TELEMETRY_EVENTS.threadsIntelligenceSignupClicked ||\n event === TELEMETRY_EVENTS.threadsTalkToEngineerClicked ||\n event === TELEMETRY_EVENTS.talkToEngineerClicked ||\n event === TELEMETRY_EVENTS.threadsEmptyEnabledViewed ||\n event === TELEMETRY_EVENTS.threadsEnabledViewed\n );\n}\n\nexport type RuntimeUrlType =\n | \"missing\"\n | \"relative\"\n | \"localhost\"\n | \"same_origin\"\n | \"remote\"\n | \"invalid\";\n\nexport function getRuntimeUrlType(\n runtimeUrl: string | undefined,\n): RuntimeUrlType {\n if (!runtimeUrl) return \"missing\";\n if (runtimeUrl.startsWith(\"/\") && !runtimeUrl.startsWith(\"//\")) {\n return \"relative\";\n }\n\n try {\n const baseHref =\n typeof window !== \"undefined\"\n ? window.location.href\n : \"https://copilotkit.ai\";\n const url = new URL(runtimeUrl, baseHref);\n const baseUrl = new URL(baseHref);\n const hostname = url.hostname.toLowerCase();\n\n if (\n hostname === \"localhost\" ||\n hostname === \"127.0.0.1\" ||\n hostname === \"[::1]\"\n ) {\n return \"localhost\";\n }\n\n return url.origin === baseUrl.origin ? \"same_origin\" : \"remote\";\n } catch {\n return \"invalid\";\n }\n}\n\n/**\n * Fire-and-forget telemetry send. Returns synchronously; the network\n * call is dispatched in the background and any failure is swallowed.\n *\n * Short-circuits when the user has opted out. Does NOT itself trigger\n * the first-run disclosure — call `maybeShowDisclosure()` from the\n * inspector's mount lifecycle instead.\n */\nexport function track(\n event: TelemetryEvent,\n properties: Record<string, unknown> = {},\n): void {\n if (isTelemetryOptedOut()) return;\n\n const distinctId = getOrCreateTelemetryDistinctId();\n const threadsProperties = isThreadsTelemetryEvent(event)\n ? {\n package_name: PACKAGE_NAME,\n package_version: PACKAGE_VERSION,\n inspector_distinct_id: distinctId,\n }\n : {};\n let body: string;\n try {\n body = JSON.stringify({\n event,\n properties: {\n ...properties,\n ...threadsProperties,\n distinct_id: distinctId,\n },\n package: {\n name: PACKAGE_NAME,\n ...(isThreadsTelemetryEvent(event) ? { version: PACKAGE_VERSION } : {}),\n },\n ts: Math.floor(Date.now() / 1000),\n });\n } catch {\n return;\n }\n\n void postBestEffort(TELEMETRY_INGEST_URL, body, distinctId);\n}\n\n// --- Typed per-event helpers ---\n// These enforce the known property shape for each V1 event at the call\n// site, so callers can't accidentally include PII under a wrong key.\n\nexport function trackBannerViewed(props: {\n banner_id: string;\n cta_label?: string;\n}): void {\n track(TELEMETRY_EVENTS.bannerViewed, props);\n}\n\nexport function trackBannerClicked(props: {\n banner_id: string;\n cta: \"body\" | \"dismiss\";\n cta_label?: string;\n}): void {\n track(TELEMETRY_EVENTS.bannerClicked, props);\n}\n\nexport type InspectorThreadTelemetryProps = {\n package_name?: typeof PACKAGE_NAME;\n package_version?: string;\n inspector_distinct_id?: string;\n posthog_distinct_id?: string;\n intelligence_status?:\n | \"intelligence_not_enabled\"\n | \"intelligence_enabled\"\n | \"unknown\";\n thread_service_status?: \"unavailable\" | \"available\" | \"unknown\" | \"error\";\n license_status?:\n | \"valid\"\n | \"none\"\n | \"expired\"\n | \"expiring\"\n | \"invalid\"\n | \"unknown\";\n runtime_mode?: \"sse\" | \"intelligence\";\n runtime_url_type?: RuntimeUrlType;\n cta_surface?:\n | \"threads_locked\"\n | \"threads_header\"\n | \"threads_empty\"\n | \"threads_populated\";\n cta?: \"signup\" | \"talk_to_engineer\";\n telemetry_disabled?: boolean;\n thread_count?: number;\n};\n\nexport function trackThreadsTabClicked(\n props: InspectorThreadTelemetryProps = {},\n): void {\n track(TELEMETRY_EVENTS.threadsTabClicked, props);\n}\n\nexport function trackThreadsLockedViewed(\n props: InspectorThreadTelemetryProps,\n): void {\n track(TELEMETRY_EVENTS.threadsLockedViewed, props);\n}\n\nexport function trackThreadsIntelligenceSignupClicked(\n props: InspectorThreadTelemetryProps,\n): void {\n track(TELEMETRY_EVENTS.threadsIntelligenceSignupClicked, props);\n}\n\nexport function trackThreadsTalkToEngineerClicked(\n props: InspectorThreadTelemetryProps,\n): void {\n track(TELEMETRY_EVENTS.threadsTalkToEngineerClicked, props);\n}\n\nexport function trackTalkToEngineerClicked(\n props: InspectorThreadTelemetryProps,\n): void {\n track(TELEMETRY_EVENTS.talkToEngineerClicked, props);\n}\n\nexport function trackThreadsEmptyEnabledViewed(\n props: InspectorThreadTelemetryProps,\n): void {\n track(TELEMETRY_EVENTS.threadsEmptyEnabledViewed, props);\n}\n\nexport function trackThreadsEnabledViewed(\n props: InspectorThreadTelemetryProps,\n): void {\n track(TELEMETRY_EVENTS.threadsEnabledViewed, props);\n}\n\n/**\n * Returns the inspector's anonymous distinct-ID for cross-domain\n * propagation onto outbound banner-CTA links, or `null` when the user\n * is opted out.\n *\n * The website / Ops API reads this query param on signup-flow landing\n * pages and calls `posthog.alias(...)` to merge the inspector's anon\n * ID with the website's anon ID, enabling the\n * `banner_viewed → banner_clicked → signup_attributed` funnel.\n * `identify()` itself is out of scope here (it happens on signup, in\n * the website / Ops API).\n *\n * Opt-out short-circuits this too: if the user has opted out, we do\n * NOT leak an anon ID across domains.\n */\nexport function getTelemetryDistinctIdForUrl(): string | null {\n if (isTelemetryOptedOut()) return null;\n return getOrCreateTelemetryDistinctId();\n}\n\n/**\n * Seeds the anonymous distinct-ID into localStorage on inspector mount\n * so it is ready for cross-domain propagation onto banner-CTA links\n * even before the first event fires. No-op when the user has opted out.\n */\nexport function ensureTelemetryDistinctId(): void {\n if (isTelemetryOptedOut()) return;\n getOrCreateTelemetryDistinctId();\n}\n\n/**\n * Fires the one-time console disclosure on inspector mount, when the\n * user is not opted out and hasn't seen it before. Idempotent across\n * calls within a single session because `markTelemetryDisclosureShown`\n * persists to localStorage.\n *\n * If the user is opted out, we deliberately do nothing and do NOT mark\n * the flag — so a future opt-in flips back to \"first run\" behavior.\n */\nexport function maybeShowDisclosure(): void {\n if (isTelemetryOptedOut()) return;\n if (hasTelemetryDisclosureBeenShown()) return;\n // eslint-disable-next-line no-console\n console.info(\n `[CopilotKit Inspector] anonymous interaction telemetry enabled — see ${TELEMETRY_DOCS_URL} to opt out.`,\n );\n markTelemetryDisclosureShown();\n}\n\nexport { isTelemetryOptedOut };\n\nasync function postBestEffort(\n url: string,\n body: string,\n distinctId: string,\n): Promise<void> {\n if (typeof fetch === \"undefined\") return;\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), FETCH_TIMEOUT_MS);\n try {\n await fetch(url, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n \"X-CopilotKit-Telemetry-Id\": distinctId,\n },\n body,\n signal: controller.signal,\n // No credentials / no Authorization header — anonymous endpoint.\n });\n } catch {\n // Silent failure — telemetry must not break the application.\n } finally {\n clearTimeout(timeoutId);\n }\n}\n"],"mappings":"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"}
@@ -1,15 +1,40 @@
1
1
  import { getOrCreateTelemetryDistinctId, hasTelemetryDisclosureBeenShown, isTelemetryOptedOut, markTelemetryDisclosureShown } from "./persistence.mjs";
2
+ import { version } from "../package.mjs";
2
3
 
3
4
  //#region src/lib/telemetry.ts
4
5
  const TELEMETRY_EVENTS = {
5
6
  bannerViewed: "oss.inspector.banner_viewed",
6
7
  bannerClicked: "oss.inspector.banner_clicked",
7
- threadsTabClicked: "oss.inspector.threads_tab_clicked"
8
+ threadsTabClicked: "oss.inspector.threads_tab_clicked",
9
+ threadsLockedViewed: "oss.inspector.threads_locked_viewed",
10
+ threadsIntelligenceSignupClicked: "oss.inspector.threads_intelligence_signup_clicked",
11
+ threadsTalkToEngineerClicked: "oss.inspector.threads_talk_to_engineer_clicked",
12
+ talkToEngineerClicked: "oss.inspector.talk_to_engineer_clicked",
13
+ threadsEmptyEnabledViewed: "oss.inspector.threads_empty_enabled_viewed",
14
+ threadsEnabledViewed: "oss.inspector.threads_enabled_viewed"
8
15
  };
9
16
  const TELEMETRY_INGEST_URL = "https://telemetry.copilotkit.ai/ingest";
10
17
  const TELEMETRY_DOCS_URL = "https://docs.copilotkit.ai/telemetry";
11
18
  const PACKAGE_NAME = "@copilotkit/web-inspector";
19
+ const PACKAGE_VERSION = version;
12
20
  const FETCH_TIMEOUT_MS = 3e3;
21
+ function isThreadsTelemetryEvent(event) {
22
+ return event === TELEMETRY_EVENTS.threadsTabClicked || event === TELEMETRY_EVENTS.threadsLockedViewed || event === TELEMETRY_EVENTS.threadsIntelligenceSignupClicked || event === TELEMETRY_EVENTS.threadsTalkToEngineerClicked || event === TELEMETRY_EVENTS.talkToEngineerClicked || event === TELEMETRY_EVENTS.threadsEmptyEnabledViewed || event === TELEMETRY_EVENTS.threadsEnabledViewed;
23
+ }
24
+ function getRuntimeUrlType(runtimeUrl) {
25
+ if (!runtimeUrl) return "missing";
26
+ if (runtimeUrl.startsWith("/") && !runtimeUrl.startsWith("//")) return "relative";
27
+ try {
28
+ const baseHref = typeof window !== "undefined" ? window.location.href : "https://copilotkit.ai";
29
+ const url = new URL(runtimeUrl, baseHref);
30
+ const baseUrl = new URL(baseHref);
31
+ const hostname = url.hostname.toLowerCase();
32
+ if (hostname === "localhost" || hostname === "127.0.0.1" || hostname === "[::1]") return "localhost";
33
+ return url.origin === baseUrl.origin ? "same_origin" : "remote";
34
+ } catch {
35
+ return "invalid";
36
+ }
37
+ }
13
38
  /**
14
39
  * Fire-and-forget telemetry send. Returns synchronously; the network
15
40
  * call is dispatched in the background and any failure is swallowed.
@@ -19,16 +44,32 @@ const FETCH_TIMEOUT_MS = 3e3;
19
44
  * inspector's mount lifecycle instead.
20
45
  */
21
46
  function track(event, properties = {}) {
47
+ if (isTelemetryOptedOut()) return;
22
48
  const distinctId = getOrCreateTelemetryDistinctId();
23
- postBestEffort(TELEMETRY_INGEST_URL, JSON.stringify({
24
- event,
25
- properties: {
26
- ...properties,
27
- distinct_id: distinctId
28
- },
29
- package: { name: PACKAGE_NAME },
30
- ts: Math.floor(Date.now() / 1e3)
31
- }), distinctId);
49
+ const threadsProperties = isThreadsTelemetryEvent(event) ? {
50
+ package_name: PACKAGE_NAME,
51
+ package_version: PACKAGE_VERSION,
52
+ inspector_distinct_id: distinctId
53
+ } : {};
54
+ let body;
55
+ try {
56
+ body = JSON.stringify({
57
+ event,
58
+ properties: {
59
+ ...properties,
60
+ ...threadsProperties,
61
+ distinct_id: distinctId
62
+ },
63
+ package: {
64
+ name: PACKAGE_NAME,
65
+ ...isThreadsTelemetryEvent(event) ? { version: PACKAGE_VERSION } : {}
66
+ },
67
+ ts: Math.floor(Date.now() / 1e3)
68
+ });
69
+ } catch {
70
+ return;
71
+ }
72
+ postBestEffort(TELEMETRY_INGEST_URL, body, distinctId);
32
73
  }
33
74
  function trackBannerViewed(props) {
34
75
  track(TELEMETRY_EVENTS.bannerViewed, props);
@@ -36,8 +77,26 @@ function trackBannerViewed(props) {
36
77
  function trackBannerClicked(props) {
37
78
  track(TELEMETRY_EVENTS.bannerClicked, props);
38
79
  }
39
- function trackThreadsTabClicked() {
40
- track(TELEMETRY_EVENTS.threadsTabClicked);
80
+ function trackThreadsTabClicked(props = {}) {
81
+ track(TELEMETRY_EVENTS.threadsTabClicked, props);
82
+ }
83
+ function trackThreadsLockedViewed(props) {
84
+ track(TELEMETRY_EVENTS.threadsLockedViewed, props);
85
+ }
86
+ function trackThreadsIntelligenceSignupClicked(props) {
87
+ track(TELEMETRY_EVENTS.threadsIntelligenceSignupClicked, props);
88
+ }
89
+ function trackThreadsTalkToEngineerClicked(props) {
90
+ track(TELEMETRY_EVENTS.threadsTalkToEngineerClicked, props);
91
+ }
92
+ function trackTalkToEngineerClicked(props) {
93
+ track(TELEMETRY_EVENTS.talkToEngineerClicked, props);
94
+ }
95
+ function trackThreadsEmptyEnabledViewed(props) {
96
+ track(TELEMETRY_EVENTS.threadsEmptyEnabledViewed, props);
97
+ }
98
+ function trackThreadsEnabledViewed(props) {
99
+ track(TELEMETRY_EVENTS.threadsEnabledViewed, props);
41
100
  }
42
101
  /**
43
102
  * Returns the inspector's anonymous distinct-ID for cross-domain
@@ -102,5 +161,5 @@ async function postBestEffort(url, body, distinctId) {
102
161
  }
103
162
 
104
163
  //#endregion
105
- export { TELEMETRY_DOCS_URL, ensureTelemetryDistinctId, getTelemetryDistinctIdForUrl, maybeShowDisclosure, trackBannerClicked, trackBannerViewed, trackThreadsTabClicked };
164
+ export { TELEMETRY_DOCS_URL, ensureTelemetryDistinctId, getRuntimeUrlType, getTelemetryDistinctIdForUrl, maybeShowDisclosure, trackBannerClicked, trackBannerViewed, trackTalkToEngineerClicked, trackThreadsEmptyEnabledViewed, trackThreadsEnabledViewed, trackThreadsIntelligenceSignupClicked, trackThreadsLockedViewed, trackThreadsTabClicked, trackThreadsTalkToEngineerClicked };
106
165
  //# sourceMappingURL=telemetry.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"telemetry.mjs","names":[],"sources":["../../src/lib/telemetry.ts"],"sourcesContent":["// Inspector-side anonymous telemetry. Three V1 events fire from index.ts —\n// `oss.inspector.banner_viewed`, `oss.inspector.banner_clicked`, and\n// `oss.inspector.threads_tab_clicked`. POSTs directly from the browser\n// to the CopilotKit telemetry sink at `telemetry.copilotkit.ai/ingest`,\n// where a Lambda fan-out forwards events to PostHog / Reo / Scarf.\n//\n// The endpoint URL is intentionally clearly named so it's obvious in\n// DevTools / Network tab — transparency for opt-in users.\n//\n// Privacy invariants enforced here:\n// - We never send message content, agent state, prompts, completions,\n// or banner markdown. Properties are scoped to event metadata only\n// (banner_id/timestamp, cta location). Reviewers should grep call\n// sites for any unintended payload.\n// - The opt-out short-circuits before any network call. There is no\n// buffer, no retry queue.\n// - All errors are swallowed; telemetry must never break the host app.\n\nimport {\n getOrCreateTelemetryDistinctId,\n hasTelemetryDisclosureBeenShown,\n isTelemetryOptedOut,\n markTelemetryDisclosureShown,\n} from \"./persistence\";\n\n// V1 funnel events. Namespaced `oss.inspector.*` so the lambda's\n// event-type allowlist (oss-path-to-production) can gate them\n// server-side. Adding a new event here requires a corresponding\n// allowlist entry on the lambda or events will be rejected.\nexport const TELEMETRY_EVENTS = {\n bannerViewed: \"oss.inspector.banner_viewed\",\n bannerClicked: \"oss.inspector.banner_clicked\",\n threadsTabClicked: \"oss.inspector.threads_tab_clicked\",\n} as const;\n\nexport type TelemetryEvent =\n (typeof TELEMETRY_EVENTS)[keyof typeof TELEMETRY_EVENTS];\n\n// Per the OSS-96 ticket — the URL is intentionally clearly named for\n// transparency in the network tab.\nexport const TELEMETRY_INGEST_URL = \"https://telemetry.copilotkit.ai/ingest\";\n\n// Surfaced in console disclosure and the in-product opt-out panel.\n// Keep in sync with the canonical telemetry docs page on main\n// (`docs/content/docs/(root)/(other)/telemetry/index.mdx`).\n// Mirror constant: packages/runtime/src/lib/telemetry-disclosure.ts\nexport const TELEMETRY_DOCS_URL = \"https://docs.copilotkit.ai/telemetry\";\n\nconst PACKAGE_NAME = \"@copilotkit/web-inspector\";\n\n// 3-second cap so a slow gateway can't hang the host app. Matches the\n// runtime's existing scarf-client convention.\nconst FETCH_TIMEOUT_MS = 3000;\n\n/**\n * Fire-and-forget telemetry send. Returns synchronously; the network\n * call is dispatched in the background and any failure is swallowed.\n *\n * Short-circuits when the user has opted out. Does NOT itself trigger\n * the first-run disclosure — call `maybeShowDisclosure()` from the\n * inspector's mount lifecycle instead.\n */\nexport function track(\n event: TelemetryEvent,\n properties: Record<string, unknown> = {},\n): void {\n const distinctId = getOrCreateTelemetryDistinctId();\n const body = JSON.stringify({\n event,\n properties: {\n ...properties,\n distinct_id: distinctId,\n },\n package: { name: PACKAGE_NAME },\n ts: Math.floor(Date.now() / 1000),\n });\n\n void postBestEffort(TELEMETRY_INGEST_URL, body, distinctId);\n}\n\n// --- Typed per-event helpers ---\n// These enforce the known property shape for each V1 event at the call\n// site, so callers can't accidentally include PII under a wrong key.\n\nexport function trackBannerViewed(props: {\n banner_id: string;\n cta_label?: string;\n}): void {\n track(TELEMETRY_EVENTS.bannerViewed, props);\n}\n\nexport function trackBannerClicked(props: {\n banner_id: string;\n cta: \"body\" | \"dismiss\";\n cta_label?: string;\n}): void {\n track(TELEMETRY_EVENTS.bannerClicked, props);\n}\n\nexport function trackThreadsTabClicked(): void {\n track(TELEMETRY_EVENTS.threadsTabClicked);\n}\n\n/**\n * Returns the inspector's anonymous distinct-ID for cross-domain\n * propagation onto outbound banner-CTA links, or `null` when the user\n * is opted out.\n *\n * The website / Ops API reads this query param on signup-flow landing\n * pages and calls `posthog.alias(...)` to merge the inspector's anon\n * ID with the website's anon ID, enabling the\n * `banner_viewed → banner_clicked → signup_attributed` funnel.\n * `identify()` itself is out of scope here (it happens on signup, in\n * the website / Ops API).\n *\n * Opt-out short-circuits this too: if the user has opted out, we do\n * NOT leak an anon ID across domains.\n */\nexport function getTelemetryDistinctIdForUrl(): string | null {\n if (isTelemetryOptedOut()) return null;\n return getOrCreateTelemetryDistinctId();\n}\n\n/**\n * Seeds the anonymous distinct-ID into localStorage on inspector mount\n * so it is ready for cross-domain propagation onto banner-CTA links\n * even before the first event fires. No-op when the user has opted out.\n */\nexport function ensureTelemetryDistinctId(): void {\n if (isTelemetryOptedOut()) return;\n getOrCreateTelemetryDistinctId();\n}\n\n/**\n * Fires the one-time console disclosure on inspector mount, when the\n * user is not opted out and hasn't seen it before. Idempotent across\n * calls within a single session because `markTelemetryDisclosureShown`\n * persists to localStorage.\n *\n * If the user is opted out, we deliberately do nothing and do NOT mark\n * the flag — so a future opt-in flips back to \"first run\" behavior.\n */\nexport function maybeShowDisclosure(): void {\n if (isTelemetryOptedOut()) return;\n if (hasTelemetryDisclosureBeenShown()) return;\n // eslint-disable-next-line no-console\n console.info(\n `[CopilotKit Inspector] anonymous interaction telemetry enabled — see ${TELEMETRY_DOCS_URL} to opt out.`,\n );\n markTelemetryDisclosureShown();\n}\n\nexport { isTelemetryOptedOut };\n\nasync function postBestEffort(\n url: string,\n body: string,\n distinctId: string,\n): Promise<void> {\n if (typeof fetch === \"undefined\") return;\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), FETCH_TIMEOUT_MS);\n try {\n await fetch(url, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n \"X-CopilotKit-Telemetry-Id\": distinctId,\n },\n body,\n signal: controller.signal,\n // No credentials / no Authorization header — anonymous endpoint.\n });\n } catch {\n // Silent failure — telemetry must not break the application.\n } finally {\n clearTimeout(timeoutId);\n }\n}\n"],"mappings":";;;AA6BA,MAAa,mBAAmB;CAC9B,cAAc;CACd,eAAe;CACf,mBAAmB;CACpB;AAOD,MAAa,uBAAuB;AAMpC,MAAa,qBAAqB;AAElC,MAAM,eAAe;AAIrB,MAAM,mBAAmB;;;;;;;;;AAUzB,SAAgB,MACd,OACA,aAAsC,EAAE,EAClC;CACN,MAAM,aAAa,gCAAgC;AAWnD,CAAK,eAAe,sBAVP,KAAK,UAAU;EAC1B;EACA,YAAY;GACV,GAAG;GACH,aAAa;GACd;EACD,SAAS,EAAE,MAAM,cAAc;EAC/B,IAAI,KAAK,MAAM,KAAK,KAAK,GAAG,IAAK;EAClC,CAAC,EAE8C,WAAW;;AAO7D,SAAgB,kBAAkB,OAGzB;AACP,OAAM,iBAAiB,cAAc,MAAM;;AAG7C,SAAgB,mBAAmB,OAI1B;AACP,OAAM,iBAAiB,eAAe,MAAM;;AAG9C,SAAgB,yBAA+B;AAC7C,OAAM,iBAAiB,kBAAkB;;;;;;;;;;;;;;;;;AAkB3C,SAAgB,+BAA8C;AAC5D,KAAI,qBAAqB,CAAE,QAAO;AAClC,QAAO,gCAAgC;;;;;;;AAQzC,SAAgB,4BAAkC;AAChD,KAAI,qBAAqB,CAAE;AAC3B,iCAAgC;;;;;;;;;;;AAYlC,SAAgB,sBAA4B;AAC1C,KAAI,qBAAqB,CAAE;AAC3B,KAAI,iCAAiC,CAAE;AAEvC,SAAQ,KACN,wEAAwE,mBAAmB,cAC5F;AACD,+BAA8B;;AAKhC,eAAe,eACb,KACA,MACA,YACe;AACf,KAAI,OAAO,UAAU,YAAa;CAClC,MAAM,aAAa,IAAI,iBAAiB;CACxC,MAAM,YAAY,iBAAiB,WAAW,OAAO,EAAE,iBAAiB;AACxE,KAAI;AACF,QAAM,MAAM,KAAK;GACf,QAAQ;GACR,SAAS;IACP,gBAAgB;IAChB,6BAA6B;IAC9B;GACD;GACA,QAAQ,WAAW;GAEpB,CAAC;SACI,WAEE;AACR,eAAa,UAAU"}
1
+ {"version":3,"file":"telemetry.mjs","names":["packageJson.version"],"sources":["../../src/lib/telemetry.ts"],"sourcesContent":["// Inspector-side anonymous telemetry. V1 events fire from index.ts for\n// banner and thread-inspection interactions. POSTs directly from the browser\n// to the CopilotKit telemetry sink at `telemetry.copilotkit.ai/ingest`,\n// where a Lambda fan-out forwards events to PostHog / Reo / Scarf.\n//\n// The endpoint URL is intentionally clearly named so it's obvious in\n// DevTools / Network tab — transparency for opt-in users.\n//\n// Privacy invariants enforced here:\n// - We never send message content, agent state, prompts, completions,\n// or banner markdown. Properties are scoped to event metadata only\n// (banner_id/timestamp, cta location). Reviewers should grep call\n// sites for any unintended payload.\n// - The opt-out short-circuits before any network call. There is no\n// buffer, no retry queue.\n// - All errors are swallowed; telemetry must never break the host app.\n\nimport {\n getOrCreateTelemetryDistinctId,\n hasTelemetryDisclosureBeenShown,\n isTelemetryOptedOut,\n markTelemetryDisclosureShown,\n} from \"./persistence.js\";\nimport packageJson from \"../../package.json\" with { type: \"json\" };\n\n// V1 funnel events. Namespaced `oss.inspector.*` so the lambda's\n// owned-prefix gate (oss-path-to-production) can accept them server-side\n// without a per-event sink deploy.\nexport const TELEMETRY_EVENTS = {\n bannerViewed: \"oss.inspector.banner_viewed\",\n bannerClicked: \"oss.inspector.banner_clicked\",\n threadsTabClicked: \"oss.inspector.threads_tab_clicked\",\n threadsLockedViewed: \"oss.inspector.threads_locked_viewed\",\n threadsIntelligenceSignupClicked:\n \"oss.inspector.threads_intelligence_signup_clicked\",\n threadsTalkToEngineerClicked:\n \"oss.inspector.threads_talk_to_engineer_clicked\",\n talkToEngineerClicked: \"oss.inspector.talk_to_engineer_clicked\",\n threadsEmptyEnabledViewed: \"oss.inspector.threads_empty_enabled_viewed\",\n threadsEnabledViewed: \"oss.inspector.threads_enabled_viewed\",\n} as const;\n\nexport type TelemetryEvent =\n (typeof TELEMETRY_EVENTS)[keyof typeof TELEMETRY_EVENTS];\n\n// Per the OSS-96 ticket — the URL is intentionally clearly named for\n// transparency in the network tab.\nexport const TELEMETRY_INGEST_URL = \"https://telemetry.copilotkit.ai/ingest\";\n\n// Surfaced in console disclosure and the in-product opt-out panel.\n// Keep in sync with the canonical telemetry docs page on main\n// (`docs/content/docs/(root)/(other)/telemetry/index.mdx`).\n// Mirror constant: packages/runtime/src/lib/telemetry-disclosure.ts\nexport const TELEMETRY_DOCS_URL = \"https://docs.copilotkit.ai/telemetry\";\n\nconst PACKAGE_NAME = \"@copilotkit/web-inspector\";\nconst PACKAGE_VERSION = packageJson.version;\n\n// 3-second cap so a slow gateway can't hang the host app. Matches the\n// runtime's existing scarf-client convention.\nconst FETCH_TIMEOUT_MS = 3000;\n\nfunction isThreadsTelemetryEvent(event: TelemetryEvent): boolean {\n return (\n event === TELEMETRY_EVENTS.threadsTabClicked ||\n event === TELEMETRY_EVENTS.threadsLockedViewed ||\n event === TELEMETRY_EVENTS.threadsIntelligenceSignupClicked ||\n event === TELEMETRY_EVENTS.threadsTalkToEngineerClicked ||\n event === TELEMETRY_EVENTS.talkToEngineerClicked ||\n event === TELEMETRY_EVENTS.threadsEmptyEnabledViewed ||\n event === TELEMETRY_EVENTS.threadsEnabledViewed\n );\n}\n\nexport type RuntimeUrlType =\n | \"missing\"\n | \"relative\"\n | \"localhost\"\n | \"same_origin\"\n | \"remote\"\n | \"invalid\";\n\nexport function getRuntimeUrlType(\n runtimeUrl: string | undefined,\n): RuntimeUrlType {\n if (!runtimeUrl) return \"missing\";\n if (runtimeUrl.startsWith(\"/\") && !runtimeUrl.startsWith(\"//\")) {\n return \"relative\";\n }\n\n try {\n const baseHref =\n typeof window !== \"undefined\"\n ? window.location.href\n : \"https://copilotkit.ai\";\n const url = new URL(runtimeUrl, baseHref);\n const baseUrl = new URL(baseHref);\n const hostname = url.hostname.toLowerCase();\n\n if (\n hostname === \"localhost\" ||\n hostname === \"127.0.0.1\" ||\n hostname === \"[::1]\"\n ) {\n return \"localhost\";\n }\n\n return url.origin === baseUrl.origin ? \"same_origin\" : \"remote\";\n } catch {\n return \"invalid\";\n }\n}\n\n/**\n * Fire-and-forget telemetry send. Returns synchronously; the network\n * call is dispatched in the background and any failure is swallowed.\n *\n * Short-circuits when the user has opted out. Does NOT itself trigger\n * the first-run disclosure — call `maybeShowDisclosure()` from the\n * inspector's mount lifecycle instead.\n */\nexport function track(\n event: TelemetryEvent,\n properties: Record<string, unknown> = {},\n): void {\n if (isTelemetryOptedOut()) return;\n\n const distinctId = getOrCreateTelemetryDistinctId();\n const threadsProperties = isThreadsTelemetryEvent(event)\n ? {\n package_name: PACKAGE_NAME,\n package_version: PACKAGE_VERSION,\n inspector_distinct_id: distinctId,\n }\n : {};\n let body: string;\n try {\n body = JSON.stringify({\n event,\n properties: {\n ...properties,\n ...threadsProperties,\n distinct_id: distinctId,\n },\n package: {\n name: PACKAGE_NAME,\n ...(isThreadsTelemetryEvent(event) ? { version: PACKAGE_VERSION } : {}),\n },\n ts: Math.floor(Date.now() / 1000),\n });\n } catch {\n return;\n }\n\n void postBestEffort(TELEMETRY_INGEST_URL, body, distinctId);\n}\n\n// --- Typed per-event helpers ---\n// These enforce the known property shape for each V1 event at the call\n// site, so callers can't accidentally include PII under a wrong key.\n\nexport function trackBannerViewed(props: {\n banner_id: string;\n cta_label?: string;\n}): void {\n track(TELEMETRY_EVENTS.bannerViewed, props);\n}\n\nexport function trackBannerClicked(props: {\n banner_id: string;\n cta: \"body\" | \"dismiss\";\n cta_label?: string;\n}): void {\n track(TELEMETRY_EVENTS.bannerClicked, props);\n}\n\nexport type InspectorThreadTelemetryProps = {\n package_name?: typeof PACKAGE_NAME;\n package_version?: string;\n inspector_distinct_id?: string;\n posthog_distinct_id?: string;\n intelligence_status?:\n | \"intelligence_not_enabled\"\n | \"intelligence_enabled\"\n | \"unknown\";\n thread_service_status?: \"unavailable\" | \"available\" | \"unknown\" | \"error\";\n license_status?:\n | \"valid\"\n | \"none\"\n | \"expired\"\n | \"expiring\"\n | \"invalid\"\n | \"unknown\";\n runtime_mode?: \"sse\" | \"intelligence\";\n runtime_url_type?: RuntimeUrlType;\n cta_surface?:\n | \"threads_locked\"\n | \"threads_header\"\n | \"threads_empty\"\n | \"threads_populated\";\n cta?: \"signup\" | \"talk_to_engineer\";\n telemetry_disabled?: boolean;\n thread_count?: number;\n};\n\nexport function trackThreadsTabClicked(\n props: InspectorThreadTelemetryProps = {},\n): void {\n track(TELEMETRY_EVENTS.threadsTabClicked, props);\n}\n\nexport function trackThreadsLockedViewed(\n props: InspectorThreadTelemetryProps,\n): void {\n track(TELEMETRY_EVENTS.threadsLockedViewed, props);\n}\n\nexport function trackThreadsIntelligenceSignupClicked(\n props: InspectorThreadTelemetryProps,\n): void {\n track(TELEMETRY_EVENTS.threadsIntelligenceSignupClicked, props);\n}\n\nexport function trackThreadsTalkToEngineerClicked(\n props: InspectorThreadTelemetryProps,\n): void {\n track(TELEMETRY_EVENTS.threadsTalkToEngineerClicked, props);\n}\n\nexport function trackTalkToEngineerClicked(\n props: InspectorThreadTelemetryProps,\n): void {\n track(TELEMETRY_EVENTS.talkToEngineerClicked, props);\n}\n\nexport function trackThreadsEmptyEnabledViewed(\n props: InspectorThreadTelemetryProps,\n): void {\n track(TELEMETRY_EVENTS.threadsEmptyEnabledViewed, props);\n}\n\nexport function trackThreadsEnabledViewed(\n props: InspectorThreadTelemetryProps,\n): void {\n track(TELEMETRY_EVENTS.threadsEnabledViewed, props);\n}\n\n/**\n * Returns the inspector's anonymous distinct-ID for cross-domain\n * propagation onto outbound banner-CTA links, or `null` when the user\n * is opted out.\n *\n * The website / Ops API reads this query param on signup-flow landing\n * pages and calls `posthog.alias(...)` to merge the inspector's anon\n * ID with the website's anon ID, enabling the\n * `banner_viewed → banner_clicked → signup_attributed` funnel.\n * `identify()` itself is out of scope here (it happens on signup, in\n * the website / Ops API).\n *\n * Opt-out short-circuits this too: if the user has opted out, we do\n * NOT leak an anon ID across domains.\n */\nexport function getTelemetryDistinctIdForUrl(): string | null {\n if (isTelemetryOptedOut()) return null;\n return getOrCreateTelemetryDistinctId();\n}\n\n/**\n * Seeds the anonymous distinct-ID into localStorage on inspector mount\n * so it is ready for cross-domain propagation onto banner-CTA links\n * even before the first event fires. No-op when the user has opted out.\n */\nexport function ensureTelemetryDistinctId(): void {\n if (isTelemetryOptedOut()) return;\n getOrCreateTelemetryDistinctId();\n}\n\n/**\n * Fires the one-time console disclosure on inspector mount, when the\n * user is not opted out and hasn't seen it before. Idempotent across\n * calls within a single session because `markTelemetryDisclosureShown`\n * persists to localStorage.\n *\n * If the user is opted out, we deliberately do nothing and do NOT mark\n * the flag — so a future opt-in flips back to \"first run\" behavior.\n */\nexport function maybeShowDisclosure(): void {\n if (isTelemetryOptedOut()) return;\n if (hasTelemetryDisclosureBeenShown()) return;\n // eslint-disable-next-line no-console\n console.info(\n `[CopilotKit Inspector] anonymous interaction telemetry enabled — see ${TELEMETRY_DOCS_URL} to opt out.`,\n );\n markTelemetryDisclosureShown();\n}\n\nexport { isTelemetryOptedOut };\n\nasync function postBestEffort(\n url: string,\n body: string,\n distinctId: string,\n): Promise<void> {\n if (typeof fetch === \"undefined\") return;\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), FETCH_TIMEOUT_MS);\n try {\n await fetch(url, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n \"X-CopilotKit-Telemetry-Id\": distinctId,\n },\n body,\n signal: controller.signal,\n // No credentials / no Authorization header — anonymous endpoint.\n });\n } catch {\n // Silent failure — telemetry must not break the application.\n } finally {\n clearTimeout(timeoutId);\n }\n}\n"],"mappings":"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"}
@@ -0,0 +1,8 @@
1
+ //#region src/lib/types.d.ts
2
+ type Anchor = {
3
+ horizontal: "left" | "right";
4
+ vertical: "top" | "bottom";
5
+ };
6
+ //#endregion
7
+ export { Anchor };
8
+ //# sourceMappingURL=types.d.cts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.cts","names":[],"sources":["../../src/lib/types.ts"],"mappings":";KAEY,MAAA;EACV,UAAA;EACA,QAAA;AAAA"}
@@ -0,0 +1,8 @@
1
+ //#region src/lib/types.d.ts
2
+ type Anchor = {
3
+ horizontal: "left" | "right";
4
+ vertical: "top" | "bottom";
5
+ };
6
+ //#endregion
7
+ export { Anchor };
8
+ //# sourceMappingURL=types.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.mts","names":[],"sources":["../../src/lib/types.ts"],"mappings":";KAEY,MAAA;EACV,UAAA;EACA,QAAA;AAAA"}
@@ -0,0 +1,12 @@
1
+
2
+ //#region package.json
3
+ var version = "1.61.2";
4
+
5
+ //#endregion
6
+ Object.defineProperty(exports, 'version', {
7
+ enumerable: true,
8
+ get: function () {
9
+ return version;
10
+ }
11
+ });
12
+ //# sourceMappingURL=package.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"package.cjs","names":[],"sources":["../package.json"],"sourcesContent":[""],"mappings":""}
@@ -0,0 +1,6 @@
1
+ //#region package.json
2
+ var version = "1.61.2";
3
+
4
+ //#endregion
5
+ export { version };
6
+ //# sourceMappingURL=package.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"package.mjs","names":[],"sources":["../package.json"],"sourcesContent":[""],"mappings":""}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@copilotkit/web-inspector",
3
- "version": "1.61.1",
3
+ "version": "1.61.2",
4
4
  "description": "Lit-based web component for the CopilotKit web inspector",
5
5
  "repository": {
6
6
  "type": "git",
@@ -27,7 +27,7 @@
27
27
  "lit": "^3.2.0",
28
28
  "lucide": "^0.525.0",
29
29
  "marked": "^12.0.2",
30
- "@copilotkit/core": "1.61.1"
30
+ "@copilotkit/core": "1.61.2"
31
31
  },
32
32
  "devDependencies": {
33
33
  "@tailwindcss/cli": "^4.1.11",