@copilotkit/web-inspector 1.73.3 → 1.74.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. package/dist/index.cjs +250 -233
  2. package/dist/index.cjs.map +1 -1
  3. package/dist/index.d.cts +24 -6
  4. package/dist/index.d.cts.map +1 -1
  5. package/dist/index.d.mts +24 -6
  6. package/dist/index.d.mts.map +1 -1
  7. package/dist/index.mjs +251 -234
  8. package/dist/index.mjs.map +1 -1
  9. package/dist/index.umd.js +1165 -591
  10. package/dist/index.umd.js.map +1 -1
  11. package/dist/lib/notification-loader.cjs +38 -0
  12. package/dist/lib/notification-loader.cjs.map +1 -0
  13. package/dist/lib/notification-loader.mjs +38 -0
  14. package/dist/lib/notification-loader.mjs.map +1 -0
  15. package/dist/lib/notifications.cjs +173 -0
  16. package/dist/lib/notifications.cjs.map +1 -0
  17. package/dist/lib/notifications.d.cts +17 -0
  18. package/dist/lib/notifications.d.cts.map +1 -0
  19. package/dist/lib/notifications.d.mts +17 -0
  20. package/dist/lib/notifications.d.mts.map +1 -0
  21. package/dist/lib/notifications.mjs +168 -0
  22. package/dist/lib/notifications.mjs.map +1 -0
  23. package/dist/lib/persistence.cjs +124 -48
  24. package/dist/lib/persistence.cjs.map +1 -1
  25. package/dist/lib/persistence.mjs +121 -45
  26. package/dist/lib/persistence.mjs.map +1 -1
  27. package/dist/lib/telemetry.cjs +42 -0
  28. package/dist/lib/telemetry.cjs.map +1 -1
  29. package/dist/lib/telemetry.d.cts.map +1 -1
  30. package/dist/lib/telemetry.d.mts.map +1 -1
  31. package/dist/lib/telemetry.mjs +35 -1
  32. package/dist/lib/telemetry.mjs.map +1 -1
  33. package/dist/package.cjs +1 -1
  34. package/dist/package.mjs +1 -1
  35. package/dist/styles/generated.cjs +1 -1
  36. package/dist/styles/generated.cjs.map +1 -1
  37. package/dist/styles/generated.mjs +1 -1
  38. package/dist/styles/generated.mjs.map +1 -1
  39. package/dist/styles/notification-article.cjs +244 -0
  40. package/dist/styles/notification-article.cjs.map +1 -0
  41. package/dist/styles/notification-article.mjs +244 -0
  42. package/dist/styles/notification-article.mjs.map +1 -0
  43. package/package.json +4 -3
@@ -8,6 +8,14 @@ const TELEMETRY_EVENTS = {
8
8
  whatsNewSignalViewed: "oss.inspector.whats_new_signal_viewed",
9
9
  errorSignalViewed: "oss.inspector.error_signal_viewed",
10
10
  whatsNewClicked: "oss.inspector.whats_new_clicked",
11
+ hudViewed: "oss.inspector.hud_viewed",
12
+ hudNotificationViewed: "oss.inspector.hud_notification_viewed",
13
+ hudNotificationClicked: "oss.inspector.hud_notification_clicked",
14
+ hudFeatureToggleViewed: "oss.inspector.hud_feature_toggle_viewed",
15
+ hudFeatureToggleClicked: "oss.inspector.hud_feature_toggle_clicked",
16
+ hudFeatureClicked: "oss.inspector.hud_feature_clicked",
17
+ hudHideViewed: "oss.inspector.hud_hide_viewed",
18
+ hudHideClicked: "oss.inspector.hud_hide_clicked",
11
19
  threadsTabClicked: "oss.inspector.threads_tab_clicked",
12
20
  threadsTryFromHereClicked: "oss.inspector.threads_try_from_here_clicked",
13
21
  threadsLockedViewed: "oss.inspector.threads_locked_viewed",
@@ -99,6 +107,32 @@ function trackWhatsNewViewed(props) {
99
107
  function trackWhatsNewSignalViewed(props) {
100
108
  track(TELEMETRY_EVENTS.whatsNewSignalViewed, props);
101
109
  }
110
+ /** One impression per rendered HUD presentation in a visible tab. */
111
+ function trackHudViewed(props) {
112
+ track(TELEMETRY_EVENTS.hudViewed, props);
113
+ }
114
+ /** The UUID identifies the notification consistently across HUD and article events. */
115
+ function trackHudNotificationViewed(props) {
116
+ track(TELEMETRY_EVENTS.hudNotificationViewed, props);
117
+ }
118
+ function trackHudNotificationClicked(props) {
119
+ track(TELEMETRY_EVENTS.hudNotificationClicked, props);
120
+ }
121
+ function trackHudFeatureToggleViewed(props) {
122
+ track(TELEMETRY_EVENTS.hudFeatureToggleViewed, props);
123
+ }
124
+ function trackHudFeatureToggleClicked(props) {
125
+ track(TELEMETRY_EVENTS.hudFeatureToggleClicked, props);
126
+ }
127
+ function trackHudFeatureClicked(props) {
128
+ track(TELEMETRY_EVENTS.hudFeatureClicked, props);
129
+ }
130
+ function trackHudHideViewed(props) {
131
+ track(TELEMETRY_EVENTS.hudHideViewed, props);
132
+ }
133
+ function trackHudHideClicked(props) {
134
+ track(TELEMETRY_EVENTS.hudHideClicked, props);
135
+ }
102
136
  /**
103
137
  * Fires when the launcher's error signal becomes *visible* — not when it arms.
104
138
  * Arming can happen with the panel open or the tab hidden, where there is no
@@ -442,6 +476,14 @@ exports.trackHomeFeaturePromptClicked = trackHomeFeaturePromptClicked;
442
476
  exports.trackHomePromptCopied = trackHomePromptCopied;
443
477
  exports.trackHomeStoryBeatSelected = trackHomeStoryBeatSelected;
444
478
  exports.trackHomeViewed = trackHomeViewed;
479
+ exports.trackHudFeatureClicked = trackHudFeatureClicked;
480
+ exports.trackHudFeatureToggleClicked = trackHudFeatureToggleClicked;
481
+ exports.trackHudFeatureToggleViewed = trackHudFeatureToggleViewed;
482
+ exports.trackHudHideClicked = trackHudHideClicked;
483
+ exports.trackHudHideViewed = trackHudHideViewed;
484
+ exports.trackHudNotificationClicked = trackHudNotificationClicked;
485
+ exports.trackHudNotificationViewed = trackHudNotificationViewed;
486
+ exports.trackHudViewed = trackHudViewed;
445
487
  exports.trackInspectorOpened = trackInspectorOpened;
446
488
  exports.trackLearningEvidenceOpened = trackLearningEvidenceOpened;
447
489
  exports.trackLearningPageChanged = trackLearningPageChanged;
@@ -1 +1 @@
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// What's new and thread-inspection interactions. POSTs directly from the\n// browser to the CopilotKit telemetry sink at\n// `telemetry.copilotkit.ai/ingest`, where a Lambda fan-out forwards events to\n// 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 announcement markdown. Feature-specific properties are scoped to\n// event metadata only (banner_id/timestamp, cta location). Reviewers\n// should grep call 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 opened: \"oss.inspector.opened\",\n whatsNewViewed: \"oss.inspector.whats_new_viewed\",\n whatsNewSignalViewed: \"oss.inspector.whats_new_signal_viewed\",\n errorSignalViewed: \"oss.inspector.error_signal_viewed\",\n whatsNewClicked: \"oss.inspector.whats_new_clicked\",\n threadsTabClicked: \"oss.inspector.threads_tab_clicked\",\n threadsTryFromHereClicked: \"oss.inspector.threads_try_from_here_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 threadsExampleViewed: \"oss.inspector.threads_example_viewed\",\n threadsExampleSelected: \"oss.inspector.threads_example_selected\",\n threadsExampleTourStarted: \"oss.inspector.threads_example_tour_started\",\n threadsExampleTourStepViewed:\n \"oss.inspector.threads_example_tour_step_viewed\",\n threadsExampleTourDismissed: \"oss.inspector.threads_example_tour_dismissed\",\n threadsExampleTourCompleted: \"oss.inspector.threads_example_tour_completed\",\n threadsExampleTourReopened: \"oss.inspector.threads_example_tour_reopened\",\n memoriesTabClicked: \"oss.inspector.memories_tab_clicked\",\n learningPaneViewed: \"oss.inspector.learning_pane_viewed\",\n learningSetupPromptClicked: \"oss.inspector.learning_setup_prompt_clicked\",\n learningSnapshotLoaded: \"oss.inspector.learning_snapshot_loaded\",\n learningSkillToggled: \"oss.inspector.learning_skill_toggled\",\n learningEvidenceOpened: \"oss.inspector.learning_evidence_opened\",\n learningPageChanged: \"oss.inspector.learning_page_changed\",\n learningWebAppOpened: \"oss.inspector.learning_web_app_opened\",\n homeViewed: \"oss.inspector.home_viewed\",\n homeCtaClicked: \"oss.inspector.home_cta_clicked\",\n homeFeaturePromptClicked: \"oss.inspector.home_feature_prompt_clicked\",\n // Carries the CLI's own `onboarding_run_id`, which is the whole point: it is\n // the first event that can be joined to `cli.onboarding.completed` on the\n // Intelligence side. `home_cta_clicked` only ever proved someone clicked a\n // link, never that an install followed.\n homePromptCopied: \"oss.inspector.home_prompt_copied\",\n // Which step of the Intelligence story a developer opened by hand. Carries\n // the beat as a property rather than splitting into one event per label,\n // because the labels are expected to change as the story is iterated and a\n // per-label event would retire with them.\n homeStoryBeatSelected: \"oss.inspector.home_story_beat_selected\",\n metadataModuleViewed: \"oss.inspector.metadata_module_viewed\",\n metadataActionClicked: \"oss.inspector.metadata_action_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 live shell-docs telemetry page\n// (`showcase/shell-docs/src/content/docs/integrations/built-in-agent/telemetry.mdx`).\n// Mirror constant: packages/runtime/src/v1-deprecated/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\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 try {\n const distinctId = getOrCreateTelemetryDistinctId();\n // Every event carries package identity and the inspector's anonymous\n // distinct-ID, so any of them can be segmented by inspector version.\n const body = JSON.stringify({\n event,\n properties: {\n ...properties,\n package_name: PACKAGE_NAME,\n package_version: PACKAGE_VERSION,\n inspector_distinct_id: distinctId,\n distinct_id: distinctId,\n },\n package: {\n name: PACKAGE_NAME,\n version: PACKAGE_VERSION,\n },\n ts: Math.floor(Date.now() / 1000),\n });\n void postBestEffort(TELEMETRY_INGEST_URL, body, distinctId);\n } catch {\n // Identity and serialization failures are best-effort too.\n }\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\n/**\n * Where an announcement was rendered when the event fired. What's new is the\n * only surface that carries one, so the value is currently a constant — it\n * stays a stamped property rather than an inferred one so a second surface\n * can be added without changing the event's shape.\n */\nexport type WhatsNewSurface = \"whats_new\";\n\n/**\n * Whether a launcher signal was animated, or held static because the reader\n * asked for reduced motion. Shared by every launcher signal so an\n * accessibility setting is never mistaken for disinterest.\n */\nexport type LauncherSignalPresentation = \"animated\" | \"reduced_motion\";\n\nexport type WhatsNewSignalPresentation = LauncherSignalPresentation;\n\n/**\n * Which failure class raised the launcher's error signal. A closed enum: the\n * failure *message* is never transmitted, so prompts, URLs and identifiers\n * embedded in an error cannot leave the browser.\n *\n * `connection` and `threads` are wiring *state*. `run`, `tool` and `memory`\n * are unread *events* that clear when their landing view is read.\n */\nexport type InspectorWiringErrorSource = \"connection\" | \"threads\";\nexport type InspectorEventErrorSource = \"run\" | \"tool\" | \"memory\";\nexport type InspectorErrorSignalSource =\n | InspectorWiringErrorSource\n | InspectorEventErrorSource;\n\n/**\n * Fires when What's new has rendered *with content*. A loading state is not\n * an impression, so the metric cannot inflate itself by counting readers who\n * arrived before the feed resolved.\n */\nexport function trackWhatsNewViewed(props: {\n banner_id: string;\n surface: WhatsNewSurface;\n cta_label?: string;\n}): void {\n track(TELEMETRY_EVENTS.whatsNewViewed, props);\n}\n\n/** Fires when the unread launcher signal is presented in a visible tab. */\nexport function trackWhatsNewSignalViewed(props: {\n banner_id: string;\n surface: \"launcher\";\n presentation: WhatsNewSignalPresentation;\n cta_label?: string;\n}): void {\n track(TELEMETRY_EVENTS.whatsNewSignalViewed, props);\n}\n\n/**\n * Whether the launcher opened its pill for this outage, or suppressed it.\n *\n * `suppressed` means no pill was shown. In practice that is the no-room\n * fallback — neither side of the launcher had space for the label — because\n * every other path to a visible error signal opens one.\n */\nexport type InspectorErrorSignalLabel = \"shown\" | \"suppressed\";\n\n/**\n * Fires when the launcher's error signal becomes *visible* — not when it arms.\n * Arming can happen with the panel open or the tab hidden, where there is no\n * launcher to look at, so counting arms would inflate the denominator this\n * event exists to provide.\n *\n * Deliberately carries three fixed enum values and nothing else. This is the\n * one place a later change could casually attach a free-text field, and the\n * failure message must never be transmitted.\n *\n * `label` is deliberately not a new event: the catalogue's size is asserted\n * and spelled out in a test title, and a property answers the one open\n * question — how often the no-room fallback fires — without touching either.\n *\n * The pill's own marginal effect cannot be measured here and must not be\n * reverse-engineered from this data: it ships together with the dot and the\n * beat, so there is no period with one and not the other.\n */\nexport function trackErrorSignalViewed(props: {\n source: InspectorErrorSignalSource;\n presentation: LauncherSignalPresentation;\n label: InspectorErrorSignalLabel;\n}): void {\n track(TELEMETRY_EVENTS.errorSignalViewed, {\n source: props.source,\n presentation: props.presentation,\n label: props.label,\n });\n}\n\n/**\n * Fires for links followed inside the announcement body. `body` is the only\n * call-to-action left: dismissal no longer exists, so there is nothing else a\n * click on this surface can mean.\n */\nexport function trackWhatsNewClicked(props: {\n banner_id: string;\n cta: \"body\";\n cta_label?: string;\n}): void {\n track(TELEMETRY_EVENTS.whatsNewClicked, props);\n}\n\n/**\n * How the panel was opened. Restoring a persisted-open panel on mount is\n * deliberately NOT a source: it is not an open *intent*, and counting it\n * would turn every dev-server hot reload into an \"open\".\n */\nexport type InspectorOpenSource = \"floating_button\" | \"message_toolbar\";\n\nexport type InspectorOpenedTelemetryProps = {\n open_source: InspectorOpenSource;\n package_name?: typeof PACKAGE_NAME;\n package_version?: string;\n inspector_distinct_id?: string;\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 /** True when an unseen announcement was on screen at open time. */\n has_unseen_announcement?: boolean;\n /** True when an error signal was on the launcher at open time. */\n has_error_signal?: boolean;\n /** Which failure class was signalling at open time, when one was. */\n error_signal_source?: InspectorErrorSignalSource;\n /** True when this is the first Inspector open after install or upgrade. */\n first_open?: boolean;\n};\n\n/**\n * Panel-open signal (OSS-566). Before this event, opens could only be\n * inferred from in-panel activity (a floor) or from the announcement's own\n * click event (which misses the floating-button path entirely).\n */\nexport function trackInspectorOpened(\n props: InspectorOpenedTelemetryProps,\n): void {\n track(TELEMETRY_EVENTS.opened, props);\n}\n\nexport type ExampleKind = \"realtime_sync\" | \"manage_history\" | \"inspect_runs\";\nexport type ThreadsUsageBucket =\n | \"absent\"\n | \"empty\"\n | \"within_limit\"\n | \"at_or_over_limit\"\n | \"unlimited\"\n | \"unknown_limit\";\nexport type ThreadsExpiryBucket = \"unavailable\" | \"zero\" | \"positive\";\nexport type InspectorGroupKey = \"home\" | \"workbench\" | \"inspect\";\nexport type InspectorLeafKey =\n | \"home\"\n | \"whats-new\"\n | \"playground\"\n | \"threads\"\n | \"ag-ui-events\"\n | \"agents\"\n | \"frontend-tools\"\n | \"capabilities\"\n | \"agent-context\"\n | \"memories\";\nexport type MetadataActionPlacement = \"threads_footer\" | \"threads_locked\";\nexport type ExampleTourStep = 1 | 2 | 3;\nexport type ExampleTourTab = \"timeline\" | \"raw-events\" | \"state\";\n\nexport type InspectorThreadTelemetryProps = Readonly<{\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 | \"sidebar_footer\";\n cta?: \"signup\" | \"talk_to_engineer\";\n telemetry_disabled?: boolean;\n has_threads?: boolean;\n usage_bucket?: ThreadsUsageBucket;\n expiry_bucket?: ThreadsExpiryBucket;\n group_key?: InspectorGroupKey;\n leaf_key?: InspectorLeafKey;\n example_kind?: ExampleKind;\n tour_step?: ExampleTourStep;\n tour_tab?: ExampleTourTab;\n dismiss_method?: \"skip\" | \"done\";\n outcome?: \"success\" | \"failure\";\n}>;\n\n/** Rebuild the common Thread payload from its closed coarse allowlist. */\nfunction threadCommonProperties(props: InspectorThreadTelemetryProps) {\n return {\n ...(props.intelligence_status === undefined\n ? {}\n : { intelligence_status: props.intelligence_status }),\n ...(props.thread_service_status === undefined\n ? {}\n : { thread_service_status: props.thread_service_status }),\n ...(props.license_status === undefined\n ? {}\n : { license_status: props.license_status }),\n ...(props.runtime_mode === undefined\n ? {}\n : { runtime_mode: props.runtime_mode }),\n ...(props.runtime_url_type === undefined\n ? {}\n : { runtime_url_type: props.runtime_url_type }),\n ...(props.telemetry_disabled === undefined\n ? {}\n : { telemetry_disabled: props.telemetry_disabled }),\n ...(props.has_threads === undefined\n ? {}\n : { has_threads: props.has_threads }),\n ...(props.usage_bucket === undefined\n ? {}\n : { usage_bucket: props.usage_bucket }),\n ...(props.expiry_bucket === undefined\n ? {}\n : { expiry_bucket: props.expiry_bucket }),\n ...(props.group_key === undefined ? {} : { group_key: props.group_key }),\n ...(props.leaf_key === undefined ? {} : { leaf_key: props.leaf_key }),\n };\n}\n\n/** Add only the fields used by the three Thread CTA funnels. */\nfunction threadCtaProperties(props: InspectorThreadTelemetryProps) {\n return {\n ...threadCommonProperties(props),\n ...(props.cta === undefined ? {} : { cta: props.cta }),\n ...(props.cta_surface === undefined\n ? {}\n : { cta_surface: props.cta_surface }),\n ...(props.posthog_distinct_id === undefined\n ? {}\n : { posthog_distinct_id: props.posthog_distinct_id }),\n };\n}\n\n/** Add only the closed example kind used by example impressions/selections. */\nfunction threadExampleProperties(props: InspectorThreadTelemetryProps) {\n return {\n ...threadCommonProperties(props),\n ...(props.example_kind === undefined\n ? {}\n : { example_kind: props.example_kind }),\n };\n}\n\n/** Add only a bounded example kind and tour step/tab pair. */\nfunction threadTourProperties(props: InspectorThreadTelemetryProps) {\n return {\n ...threadExampleProperties(props),\n ...(props.tour_step === undefined ? {} : { tour_step: props.tour_step }),\n ...(props.tour_tab === undefined ? {} : { tour_tab: props.tour_tab }),\n };\n}\n\n/** Add the bounded dismissal leaf used only by terminal tour events. */\nfunction threadTourTerminalProperties(props: InspectorThreadTelemetryProps) {\n return {\n ...threadTourProperties(props),\n ...(props.dismiss_method === undefined\n ? {}\n : { dismiss_method: props.dismiss_method }),\n };\n}\n\nexport function trackThreadsTabClicked(\n props: InspectorThreadTelemetryProps = {},\n): void {\n track(TELEMETRY_EVENTS.threadsTabClicked, threadCommonProperties(props));\n}\n\nexport function trackThreadsTryFromHereClicked(\n props: InspectorThreadTelemetryProps,\n): void {\n track(TELEMETRY_EVENTS.threadsTryFromHereClicked, {\n ...threadCommonProperties(props),\n ...(props.outcome === undefined ? {} : { outcome: props.outcome }),\n });\n}\n\nexport function trackThreadsLockedViewed(\n props: InspectorThreadTelemetryProps,\n): void {\n track(TELEMETRY_EVENTS.threadsLockedViewed, threadCommonProperties(props));\n}\n\nexport function trackThreadsIntelligenceSignupClicked(\n props: InspectorThreadTelemetryProps,\n): void {\n track(\n TELEMETRY_EVENTS.threadsIntelligenceSignupClicked,\n threadCtaProperties(props),\n );\n}\n\nexport function trackThreadsTalkToEngineerClicked(\n props: InspectorThreadTelemetryProps,\n): void {\n track(\n TELEMETRY_EVENTS.threadsTalkToEngineerClicked,\n threadCtaProperties(props),\n );\n}\n\nexport function trackTalkToEngineerClicked(\n props: InspectorThreadTelemetryProps,\n): void {\n track(TELEMETRY_EVENTS.talkToEngineerClicked, threadCtaProperties(props));\n}\n\nexport function trackThreadsEmptyEnabledViewed(\n props: InspectorThreadTelemetryProps,\n): void {\n track(\n TELEMETRY_EVENTS.threadsEmptyEnabledViewed,\n threadCommonProperties(props),\n );\n}\n\nexport function trackThreadsEnabledViewed(\n props: InspectorThreadTelemetryProps,\n): void {\n track(TELEMETRY_EVENTS.threadsEnabledViewed, threadCommonProperties(props));\n}\n\nexport function trackThreadsExampleViewed(\n props: InspectorThreadTelemetryProps,\n): void {\n track(TELEMETRY_EVENTS.threadsExampleViewed, threadExampleProperties(props));\n}\n\nexport function trackThreadsExampleSelected(\n props: InspectorThreadTelemetryProps,\n): void {\n track(\n TELEMETRY_EVENTS.threadsExampleSelected,\n threadExampleProperties(props),\n );\n}\n\nexport function trackThreadsExampleTourStarted(\n props: InspectorThreadTelemetryProps,\n): void {\n track(\n TELEMETRY_EVENTS.threadsExampleTourStarted,\n threadTourProperties(props),\n );\n}\n\nexport function trackThreadsExampleTourStepViewed(\n props: InspectorThreadTelemetryProps,\n): void {\n track(\n TELEMETRY_EVENTS.threadsExampleTourStepViewed,\n threadTourProperties(props),\n );\n}\n\nexport function trackThreadsExampleTourDismissed(\n props: InspectorThreadTelemetryProps,\n): void {\n track(\n TELEMETRY_EVENTS.threadsExampleTourDismissed,\n threadTourTerminalProperties(props),\n );\n}\n\nexport function trackThreadsExampleTourCompleted(\n props: InspectorThreadTelemetryProps,\n): void {\n track(\n TELEMETRY_EVENTS.threadsExampleTourCompleted,\n threadTourTerminalProperties(props),\n );\n}\n\nexport function trackThreadsExampleTourReopened(\n props: InspectorThreadTelemetryProps,\n): void {\n track(\n TELEMETRY_EVENTS.threadsExampleTourReopened,\n threadTourProperties(props),\n );\n}\n\nexport type InspectorMemoryTelemetryProps = {\n package_name?: typeof PACKAGE_NAME;\n package_version?: string;\n inspector_distinct_id?: string;\n posthog_distinct_id?: string;\n memory_count?: number;\n available?: boolean;\n};\n\nexport function trackMemoriesTabClicked(\n props: InspectorMemoryTelemetryProps = {},\n): void {\n track(TELEMETRY_EVENTS.memoriesTabClicked, props);\n}\n\nexport type InspectorLearningViewState =\n | \"loading\"\n | \"error\"\n | \"selection_required\"\n | \"invalid\"\n | \"results\"\n | \"first_run\"\n | \"ready\"\n | \"empty\"\n | \"setup\"\n | \"landing\";\nexport type InspectorLearningCountBucket =\n | \"zero\"\n | \"one\"\n | \"two_to_five\"\n | \"six_to_twenty\"\n | \"twenty_one_plus\";\nexport type InspectorLearningDurationBucket =\n | \"under_250ms\"\n | \"250ms_to_1s\"\n | \"1s_to_3s\"\n | \"3s_plus\";\n\nexport function learningCountBucket(\n value: number,\n): InspectorLearningCountBucket {\n if (value <= 0) return \"zero\";\n if (value === 1) return \"one\";\n if (value <= 5) return \"two_to_five\";\n if (value <= 20) return \"six_to_twenty\";\n return \"twenty_one_plus\";\n}\n\nexport function learningDurationBucket(\n durationMs: number,\n): InspectorLearningDurationBucket {\n if (durationMs < 250) return \"under_250ms\";\n if (durationMs < 1_000) return \"250ms_to_1s\";\n if (durationMs < 3_000) return \"1s_to_3s\";\n return \"3s_plus\";\n}\n\nexport function trackLearningPaneViewed(props: {\n state: InspectorLearningViewState;\n}): void {\n track(TELEMETRY_EVENTS.learningPaneViewed, props);\n}\n\nexport function trackLearningSetupPromptClicked(props: {\n outcome: \"success\" | \"failure\";\n}): void {\n track(TELEMETRY_EVENTS.learningSetupPromptClicked, props);\n}\n\nexport function trackLearningSnapshotLoaded(props: {\n outcome: \"success\" | \"unsupported\" | \"failure\";\n duration_bucket: InspectorLearningDurationBucket;\n skills_bucket: InspectorLearningCountBucket;\n insights_bucket: InspectorLearningCountBucket;\n pending_threads_bucket: InspectorLearningCountBucket;\n}): void {\n track(TELEMETRY_EVENTS.learningSnapshotLoaded, props);\n}\n\nexport function trackLearningSkillToggled(props: {\n action: \"expanded\" | \"collapsed\";\n}): void {\n track(TELEMETRY_EVENTS.learningSkillToggled, props);\n}\n\nexport function trackLearningEvidenceOpened(): void {\n track(TELEMETRY_EVENTS.learningEvidenceOpened);\n}\n\nexport function trackLearningPageChanged(props: {\n section: \"skills\" | \"insights\";\n direction: \"previous\" | \"next\";\n}): void {\n track(TELEMETRY_EVENTS.learningPageChanged, props);\n}\n\nexport function trackLearningWebAppOpened(props: {\n category: \"learning\" | \"runs\" | \"candidates\";\n}): void {\n track(TELEMETRY_EVENTS.learningWebAppOpened, props);\n}\n\nexport type InspectorMetadataTelemetryModule = \"identity\" | \"plan\" | \"action\";\nexport type InspectorMetadataLicenseBucket =\n | \"valid\"\n | \"none\"\n | \"expired\"\n | \"unknown\";\nexport type InspectorMetadataActionKind =\n | \"manage_plan\"\n | \"renew\"\n | \"enable_intelligence\";\n\nexport type InspectorHomeTelemetryProps = Readonly<{\n action_kind?: InspectorMetadataActionKind;\n group_key?: InspectorGroupKey;\n leaf_key?: InspectorLeafKey;\n}>;\n\nexport function trackHomeViewed(props: InspectorHomeTelemetryProps = {}): void {\n track(TELEMETRY_EVENTS.homeViewed, {\n group_key: props.group_key ?? \"home\",\n leaf_key: props.leaf_key ?? \"home\",\n });\n}\n\nexport function trackHomeCtaClicked(props: InspectorHomeTelemetryProps): void {\n track(TELEMETRY_EVENTS.homeCtaClicked, {\n action_kind: props.action_kind,\n group_key: props.group_key ?? \"home\",\n leaf_key: props.leaf_key ?? \"home\",\n });\n}\n\n/**\n * Correlates a copied feature setup prompt with its onboarding run. The run ID\n * is generated locally for this click, not derived from account or runtime\n * data, so it can be safely joined with the onboarding flow downstream.\n */\nexport function trackHomeFeaturePromptClicked(props: {\n feature_id: string;\n onboarding_run_id: string;\n}): void {\n track(TELEMETRY_EVENTS.homeFeaturePromptClicked, {\n feature_id: props.feature_id,\n onboarding_run_id: props.onboarding_run_id,\n group_key: \"home\",\n leaf_key: \"home\",\n });\n}\n\nexport type InspectorHomePromptCopiedTelemetryProps = Readonly<{\n /** The id minted for this session and substituted into the copied prompt. */\n onboarding_run_id: string;\n /** Whether the clipboard write actually landed. */\n outcome: \"copied\" | \"failed\";\n}>;\n\n/**\n * Report a copy of the Intelligence install prompt.\n *\n * `outcome` is reported rather than only emitting on success, because a\n * clipboard that refuses is indistinguishable from a developer who never\n * pressed the button — and the two call for opposite fixes.\n */\nexport function trackHomePromptCopied(\n props: InspectorHomePromptCopiedTelemetryProps,\n): void {\n track(TELEMETRY_EVENTS.homePromptCopied, {\n onboarding_run_id: props.onboarding_run_id,\n outcome: props.outcome,\n group_key: \"home\",\n leaf_key: \"home\",\n });\n}\n\nexport type InspectorHomeStoryBeatTelemetryProps = Readonly<{\n /** Stable id of the step, e.g. \"threads\". Survives a label rename. */\n beat: string;\n /** Its position in the rail, so a reorder can be evaluated against clicks. */\n beat_index: number;\n}>;\n\n/**\n * Report a step of the Intelligence story that a developer opened themselves.\n *\n * Only a press reports. The story also advances on its own every few seconds,\n * and reporting that would bury the handful of real interactions under a\n * metronome — one event per idle developer per six seconds, none of it intent.\n */\nexport function trackHomeStoryBeatSelected(\n props: InspectorHomeStoryBeatTelemetryProps,\n): void {\n track(TELEMETRY_EVENTS.homeStoryBeatSelected, {\n beat: props.beat,\n beat_index: props.beat_index,\n group_key: \"home\",\n leaf_key: \"home\",\n });\n}\n\nexport type InspectorMetadataModuleViewedTelemetryProps = Readonly<{\n module: InspectorMetadataTelemetryModule;\n license_bucket: InspectorMetadataLicenseBucket;\n action_kind?: InspectorMetadataActionKind;\n usage_bucket: ThreadsUsageBucket;\n expiry_bucket: ThreadsExpiryBucket;\n group_key?: InspectorGroupKey;\n leaf_key?: InspectorLeafKey;\n action_placement?: MetadataActionPlacement;\n}>;\n\nexport type InspectorMetadataActionClickedTelemetryProps = Readonly<{\n action_kind: Exclude<InspectorMetadataActionKind, \"enable_intelligence\">;\n license_bucket: InspectorMetadataLicenseBucket;\n usage_bucket: ThreadsUsageBucket;\n expiry_bucket: ThreadsExpiryBucket;\n group_key?: InspectorGroupKey;\n leaf_key?: InspectorLeafKey;\n action_placement?: MetadataActionPlacement;\n}>;\n\nfunction metadataCoarseProperties(\n props:\n | InspectorMetadataModuleViewedTelemetryProps\n | InspectorMetadataActionClickedTelemetryProps,\n) {\n const navigation =\n props.group_key === undefined || props.leaf_key === undefined\n ? {}\n : { group_key: props.group_key, leaf_key: props.leaf_key };\n return {\n license_bucket: props.license_bucket,\n usage_bucket: props.usage_bucket,\n expiry_bucket: props.expiry_bucket,\n ...navigation,\n ...(props.action_placement === undefined\n ? {}\n : { action_placement: props.action_placement }),\n };\n}\n\n/**\n * Tracks one visible Inspector metadata module using coarse fields only.\n * Rebuilding the payload here prevents local labels, IDs, URLs, and usage\n * values from crossing the telemetry boundary through extra object fields.\n */\nexport function trackMetadataModuleViewed(\n props: InspectorMetadataModuleViewedTelemetryProps,\n): void {\n track(TELEMETRY_EVENTS.metadataModuleViewed, {\n module: props.module,\n ...(props.action_kind === undefined\n ? {}\n : { action_kind: props.action_kind }),\n ...metadataCoarseProperties(props),\n });\n}\n\n/**\n * Tracks clicks for managed plan and renewal actions. The existing\n * Intelligence-enable funnel keeps its original event and never calls this\n * helper, which prevents a single click from producing two wire events.\n */\nexport function trackMetadataActionClicked(\n props: InspectorMetadataActionClickedTelemetryProps,\n): void {\n track(TELEMETRY_EVENTS.metadataActionClicked, {\n module: \"action\",\n action_kind: props.action_kind,\n ...metadataCoarseProperties(props),\n });\n}\n\n/**\n * Returns the inspector's anonymous distinct-ID for cross-domain\n * propagation onto outbound announcement-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 * `whats_new_viewed → whats_new_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 announcement-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 let timeoutId: ReturnType<typeof setTimeout> | undefined;\n try {\n const controller = new AbortController();\n timeoutId = setTimeout(() => controller.abort(), FETCH_TIMEOUT_MS);\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 if (timeoutId !== undefined) clearTimeout(timeoutId);\n }\n}\n"],"mappings":"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"}
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// the launcher HUD, What's new, and thread-inspection interactions. POSTs directly from the\n// browser to the CopilotKit telemetry sink at\n// `telemetry.copilotkit.ai/ingest`, where a Lambda fan-out forwards events to\n// 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 announcement markdown. Feature-specific properties are scoped to\n// event metadata only (notification UUID, cta location). Reviewers\n// should grep call 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 opened: \"oss.inspector.opened\",\n whatsNewViewed: \"oss.inspector.whats_new_viewed\",\n whatsNewSignalViewed: \"oss.inspector.whats_new_signal_viewed\",\n errorSignalViewed: \"oss.inspector.error_signal_viewed\",\n whatsNewClicked: \"oss.inspector.whats_new_clicked\",\n hudViewed: \"oss.inspector.hud_viewed\",\n hudNotificationViewed: \"oss.inspector.hud_notification_viewed\",\n hudNotificationClicked: \"oss.inspector.hud_notification_clicked\",\n hudFeatureToggleViewed: \"oss.inspector.hud_feature_toggle_viewed\",\n hudFeatureToggleClicked: \"oss.inspector.hud_feature_toggle_clicked\",\n hudFeatureClicked: \"oss.inspector.hud_feature_clicked\",\n hudHideViewed: \"oss.inspector.hud_hide_viewed\",\n hudHideClicked: \"oss.inspector.hud_hide_clicked\",\n threadsTabClicked: \"oss.inspector.threads_tab_clicked\",\n threadsTryFromHereClicked: \"oss.inspector.threads_try_from_here_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 threadsExampleViewed: \"oss.inspector.threads_example_viewed\",\n threadsExampleSelected: \"oss.inspector.threads_example_selected\",\n threadsExampleTourStarted: \"oss.inspector.threads_example_tour_started\",\n threadsExampleTourStepViewed:\n \"oss.inspector.threads_example_tour_step_viewed\",\n threadsExampleTourDismissed: \"oss.inspector.threads_example_tour_dismissed\",\n threadsExampleTourCompleted: \"oss.inspector.threads_example_tour_completed\",\n threadsExampleTourReopened: \"oss.inspector.threads_example_tour_reopened\",\n memoriesTabClicked: \"oss.inspector.memories_tab_clicked\",\n learningPaneViewed: \"oss.inspector.learning_pane_viewed\",\n learningSetupPromptClicked: \"oss.inspector.learning_setup_prompt_clicked\",\n learningSnapshotLoaded: \"oss.inspector.learning_snapshot_loaded\",\n learningSkillToggled: \"oss.inspector.learning_skill_toggled\",\n learningEvidenceOpened: \"oss.inspector.learning_evidence_opened\",\n learningPageChanged: \"oss.inspector.learning_page_changed\",\n learningWebAppOpened: \"oss.inspector.learning_web_app_opened\",\n homeViewed: \"oss.inspector.home_viewed\",\n homeCtaClicked: \"oss.inspector.home_cta_clicked\",\n homeFeaturePromptClicked: \"oss.inspector.home_feature_prompt_clicked\",\n // Carries the CLI's own `onboarding_run_id`, which is the whole point: it is\n // the first event that can be joined to `cli.onboarding.completed` on the\n // Intelligence side. `home_cta_clicked` only ever proved someone clicked a\n // link, never that an install followed.\n homePromptCopied: \"oss.inspector.home_prompt_copied\",\n // Which step of the Intelligence story a developer opened by hand. Carries\n // the beat as a property rather than splitting into one event per label,\n // because the labels are expected to change as the story is iterated and a\n // per-label event would retire with them.\n homeStoryBeatSelected: \"oss.inspector.home_story_beat_selected\",\n metadataModuleViewed: \"oss.inspector.metadata_module_viewed\",\n metadataActionClicked: \"oss.inspector.metadata_action_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 live shell-docs telemetry page\n// (`showcase/shell-docs/src/content/docs/integrations/built-in-agent/telemetry.mdx`).\n// Mirror constant: packages/runtime/src/v1-deprecated/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\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 try {\n const distinctId = getOrCreateTelemetryDistinctId();\n // Every event carries package identity and the inspector's anonymous\n // distinct-ID, so any of them can be segmented by inspector version.\n const body = JSON.stringify({\n event,\n properties: {\n ...properties,\n package_name: PACKAGE_NAME,\n package_version: PACKAGE_VERSION,\n inspector_distinct_id: distinctId,\n distinct_id: distinctId,\n },\n package: {\n name: PACKAGE_NAME,\n version: PACKAGE_VERSION,\n },\n ts: Math.floor(Date.now() / 1000),\n });\n void postBestEffort(TELEMETRY_INGEST_URL, body, distinctId);\n } catch {\n // Identity and serialization failures are best-effort too.\n }\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\n/**\n * Where an announcement was rendered when the event fired. What's new is the\n * only surface of `whats_new_viewed`. The HUD has its own notification event,\n * so its impressions do not change the meaning of this event.\n */\nexport type WhatsNewSurface = \"whats_new\";\n\n/**\n * Whether a launcher signal was animated, or held static because the reader\n * asked for reduced motion. Shared by every launcher signal so an\n * accessibility setting is never mistaken for disinterest.\n */\nexport type LauncherSignalPresentation = \"animated\" | \"reduced_motion\";\n\nexport type WhatsNewSignalPresentation = LauncherSignalPresentation;\n\n/**\n * Which failure class raised the launcher's error signal. A closed enum: the\n * failure *message* is never transmitted, so prompts, URLs and identifiers\n * embedded in an error cannot leave the browser.\n *\n * `connection` and `threads` are wiring *state*. `run`, `tool` and `memory`\n * are unread *events* that clear when their landing view is read.\n */\nexport type InspectorWiringErrorSource = \"connection\" | \"threads\";\nexport type InspectorEventErrorSource = \"run\" | \"tool\" | \"memory\";\nexport type InspectorErrorSignalSource =\n | InspectorWiringErrorSource\n | InspectorEventErrorSource;\n\n/**\n * Fires when What's new has rendered *with content*. A loading state is not\n * an impression, so the metric cannot inflate itself by counting readers who\n * arrived before the feed resolved.\n */\nexport function trackWhatsNewViewed(props: {\n banner_id: string;\n notification_id?: string;\n surface: WhatsNewSurface;\n cta_label?: string;\n}): void {\n track(TELEMETRY_EVENTS.whatsNewViewed, props);\n}\n\n/** Fires when the unread launcher signal is presented in a visible tab. */\nexport function trackWhatsNewSignalViewed(props: {\n banner_id: string;\n notification_id?: string;\n surface: \"launcher\";\n presentation: WhatsNewSignalPresentation;\n cta_label?: string;\n}): void {\n track(TELEMETRY_EVENTS.whatsNewSignalViewed, props);\n}\n\nexport type HudFeature = \"threads\" | \"learning\";\n\n/**\n * What opened the HUD presentation. `intro` is the automatic preview that\n * plays after every mount, `user` is a pointer or keyboard open. A user who\n * takes over a playing intro keeps `intro`, because the presentation itself\n * was not requested.\n */\nexport type HudTrigger = \"intro\" | \"user\";\n\n/** One impression per rendered HUD presentation in a visible tab. */\nexport function trackHudViewed(props: { trigger: HudTrigger }): void {\n track(TELEMETRY_EVENTS.hudViewed, props);\n}\n\n/** The UUID identifies the notification consistently across HUD and article events. */\nexport function trackHudNotificationViewed(props: {\n banner_id: string;\n notification_id: string;\n trigger: HudTrigger;\n}): void {\n track(TELEMETRY_EVENTS.hudNotificationViewed, props);\n}\n\nexport function trackHudNotificationClicked(props: {\n banner_id: string;\n notification_id: string;\n action: \"open\" | \"dismiss\";\n trigger: HudTrigger;\n}): void {\n track(TELEMETRY_EVENTS.hudNotificationClicked, props);\n}\n\nexport function trackHudFeatureToggleViewed(props: {\n feature: HudFeature;\n trigger: HudTrigger;\n}): void {\n track(TELEMETRY_EVENTS.hudFeatureToggleViewed, props);\n}\n\nexport function trackHudFeatureToggleClicked(props: {\n feature: HudFeature;\n trigger: HudTrigger;\n}): void {\n track(TELEMETRY_EVENTS.hudFeatureToggleClicked, props);\n}\n\nexport function trackHudFeatureClicked(props: {\n feature: HudFeature;\n control: \"row\" | \"action\" | \"learn_more\";\n trigger: HudTrigger;\n}): void {\n track(TELEMETRY_EVENTS.hudFeatureClicked, props);\n}\n\nexport function trackHudHideViewed(props: { trigger: HudTrigger }): void {\n track(TELEMETRY_EVENTS.hudHideViewed, props);\n}\n\nexport function trackHudHideClicked(props: { trigger: HudTrigger }): void {\n track(TELEMETRY_EVENTS.hudHideClicked, props);\n}\n\n/**\n * Whether the launcher opened its pill for this outage, or suppressed it.\n *\n * `suppressed` means no pill was shown. In practice that is the no-room\n * fallback — neither side of the launcher had space for the label — because\n * every other path to a visible error signal opens one.\n */\nexport type InspectorErrorSignalLabel = \"shown\" | \"suppressed\";\n\n/**\n * Fires when the launcher's error signal becomes *visible* — not when it arms.\n * Arming can happen with the panel open or the tab hidden, where there is no\n * launcher to look at, so counting arms would inflate the denominator this\n * event exists to provide.\n *\n * Deliberately carries three fixed enum values and nothing else. This is the\n * one place a later change could casually attach a free-text field, and the\n * failure message must never be transmitted.\n *\n * `label` is deliberately not a new event: the catalogue's size is asserted\n * and spelled out in a test title, and a property answers the one open\n * question — how often the no-room fallback fires — without touching either.\n *\n * The pill's own marginal effect cannot be measured here and must not be\n * reverse-engineered from this data: it ships together with the dot and the\n * beat, so there is no period with one and not the other.\n */\nexport function trackErrorSignalViewed(props: {\n source: InspectorErrorSignalSource;\n presentation: LauncherSignalPresentation;\n label: InspectorErrorSignalLabel;\n}): void {\n track(TELEMETRY_EVENTS.errorSignalViewed, {\n source: props.source,\n presentation: props.presentation,\n label: props.label,\n });\n}\n\n/**\n * Fires for links followed inside the announcement body. `body` is the only\n * call-to-action left: dismissal no longer exists, so there is nothing else a\n * click on this surface can mean.\n */\nexport function trackWhatsNewClicked(props: {\n banner_id: string;\n notification_id?: string;\n cta: \"body\";\n cta_label?: string;\n}): void {\n track(TELEMETRY_EVENTS.whatsNewClicked, props);\n}\n\n/**\n * How the panel was opened. Restoring a persisted-open panel on mount is\n * deliberately NOT a source: it is not an open *intent*, and counting it\n * would turn every dev-server hot reload into an \"open\".\n */\nexport type InspectorOpenSource = \"floating_button\" | \"message_toolbar\";\n\nexport type InspectorOpenedTelemetryProps = {\n open_source: InspectorOpenSource;\n package_name?: typeof PACKAGE_NAME;\n package_version?: string;\n inspector_distinct_id?: string;\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 /** True when an unseen announcement was on screen at open time. */\n has_unseen_announcement?: boolean;\n /** True when an error signal was on the launcher at open time. */\n has_error_signal?: boolean;\n /** Which failure class was signalling at open time, when one was. */\n error_signal_source?: InspectorErrorSignalSource;\n /** True when this is the first Inspector open after install or upgrade. */\n first_open?: boolean;\n};\n\n/**\n * Panel-open signal (OSS-566). Before this event, opens could only be\n * inferred from in-panel activity (a floor) or from the announcement's own\n * click event (which misses the floating-button path entirely).\n */\nexport function trackInspectorOpened(\n props: InspectorOpenedTelemetryProps,\n): void {\n track(TELEMETRY_EVENTS.opened, props);\n}\n\nexport type ExampleKind = \"realtime_sync\" | \"manage_history\" | \"inspect_runs\";\nexport type ThreadsUsageBucket =\n | \"absent\"\n | \"empty\"\n | \"within_limit\"\n | \"at_or_over_limit\"\n | \"unlimited\"\n | \"unknown_limit\";\nexport type ThreadsExpiryBucket = \"unavailable\" | \"zero\" | \"positive\";\nexport type InspectorGroupKey = \"home\" | \"workbench\" | \"inspect\";\nexport type InspectorLeafKey =\n | \"home\"\n | \"whats-new\"\n | \"playground\"\n | \"threads\"\n | \"ag-ui-events\"\n | \"agents\"\n | \"frontend-tools\"\n | \"capabilities\"\n | \"agent-context\"\n | \"memories\";\nexport type MetadataActionPlacement = \"threads_footer\" | \"threads_locked\";\nexport type ExampleTourStep = 1 | 2 | 3;\nexport type ExampleTourTab = \"timeline\" | \"raw-events\" | \"state\";\n\nexport type InspectorThreadTelemetryProps = Readonly<{\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 | \"sidebar_footer\";\n cta?: \"signup\" | \"talk_to_engineer\";\n telemetry_disabled?: boolean;\n has_threads?: boolean;\n usage_bucket?: ThreadsUsageBucket;\n expiry_bucket?: ThreadsExpiryBucket;\n group_key?: InspectorGroupKey;\n leaf_key?: InspectorLeafKey;\n example_kind?: ExampleKind;\n tour_step?: ExampleTourStep;\n tour_tab?: ExampleTourTab;\n dismiss_method?: \"skip\" | \"done\";\n outcome?: \"success\" | \"failure\";\n}>;\n\n/** Rebuild the common Thread payload from its closed coarse allowlist. */\nfunction threadCommonProperties(props: InspectorThreadTelemetryProps) {\n return {\n ...(props.intelligence_status === undefined\n ? {}\n : { intelligence_status: props.intelligence_status }),\n ...(props.thread_service_status === undefined\n ? {}\n : { thread_service_status: props.thread_service_status }),\n ...(props.license_status === undefined\n ? {}\n : { license_status: props.license_status }),\n ...(props.runtime_mode === undefined\n ? {}\n : { runtime_mode: props.runtime_mode }),\n ...(props.runtime_url_type === undefined\n ? {}\n : { runtime_url_type: props.runtime_url_type }),\n ...(props.telemetry_disabled === undefined\n ? {}\n : { telemetry_disabled: props.telemetry_disabled }),\n ...(props.has_threads === undefined\n ? {}\n : { has_threads: props.has_threads }),\n ...(props.usage_bucket === undefined\n ? {}\n : { usage_bucket: props.usage_bucket }),\n ...(props.expiry_bucket === undefined\n ? {}\n : { expiry_bucket: props.expiry_bucket }),\n ...(props.group_key === undefined ? {} : { group_key: props.group_key }),\n ...(props.leaf_key === undefined ? {} : { leaf_key: props.leaf_key }),\n };\n}\n\n/** Add only the fields used by the three Thread CTA funnels. */\nfunction threadCtaProperties(props: InspectorThreadTelemetryProps) {\n return {\n ...threadCommonProperties(props),\n ...(props.cta === undefined ? {} : { cta: props.cta }),\n ...(props.cta_surface === undefined\n ? {}\n : { cta_surface: props.cta_surface }),\n ...(props.posthog_distinct_id === undefined\n ? {}\n : { posthog_distinct_id: props.posthog_distinct_id }),\n };\n}\n\n/** Add only the closed example kind used by example impressions/selections. */\nfunction threadExampleProperties(props: InspectorThreadTelemetryProps) {\n return {\n ...threadCommonProperties(props),\n ...(props.example_kind === undefined\n ? {}\n : { example_kind: props.example_kind }),\n };\n}\n\n/** Add only a bounded example kind and tour step/tab pair. */\nfunction threadTourProperties(props: InspectorThreadTelemetryProps) {\n return {\n ...threadExampleProperties(props),\n ...(props.tour_step === undefined ? {} : { tour_step: props.tour_step }),\n ...(props.tour_tab === undefined ? {} : { tour_tab: props.tour_tab }),\n };\n}\n\n/** Add the bounded dismissal leaf used only by terminal tour events. */\nfunction threadTourTerminalProperties(props: InspectorThreadTelemetryProps) {\n return {\n ...threadTourProperties(props),\n ...(props.dismiss_method === undefined\n ? {}\n : { dismiss_method: props.dismiss_method }),\n };\n}\n\nexport function trackThreadsTabClicked(\n props: InspectorThreadTelemetryProps = {},\n): void {\n track(TELEMETRY_EVENTS.threadsTabClicked, threadCommonProperties(props));\n}\n\nexport function trackThreadsTryFromHereClicked(\n props: InspectorThreadTelemetryProps,\n): void {\n track(TELEMETRY_EVENTS.threadsTryFromHereClicked, {\n ...threadCommonProperties(props),\n ...(props.outcome === undefined ? {} : { outcome: props.outcome }),\n });\n}\n\nexport function trackThreadsLockedViewed(\n props: InspectorThreadTelemetryProps,\n): void {\n track(TELEMETRY_EVENTS.threadsLockedViewed, threadCommonProperties(props));\n}\n\nexport function trackThreadsIntelligenceSignupClicked(\n props: InspectorThreadTelemetryProps,\n): void {\n track(\n TELEMETRY_EVENTS.threadsIntelligenceSignupClicked,\n threadCtaProperties(props),\n );\n}\n\nexport function trackThreadsTalkToEngineerClicked(\n props: InspectorThreadTelemetryProps,\n): void {\n track(\n TELEMETRY_EVENTS.threadsTalkToEngineerClicked,\n threadCtaProperties(props),\n );\n}\n\nexport function trackTalkToEngineerClicked(\n props: InspectorThreadTelemetryProps,\n): void {\n track(TELEMETRY_EVENTS.talkToEngineerClicked, threadCtaProperties(props));\n}\n\nexport function trackThreadsEmptyEnabledViewed(\n props: InspectorThreadTelemetryProps,\n): void {\n track(\n TELEMETRY_EVENTS.threadsEmptyEnabledViewed,\n threadCommonProperties(props),\n );\n}\n\nexport function trackThreadsEnabledViewed(\n props: InspectorThreadTelemetryProps,\n): void {\n track(TELEMETRY_EVENTS.threadsEnabledViewed, threadCommonProperties(props));\n}\n\nexport function trackThreadsExampleViewed(\n props: InspectorThreadTelemetryProps,\n): void {\n track(TELEMETRY_EVENTS.threadsExampleViewed, threadExampleProperties(props));\n}\n\nexport function trackThreadsExampleSelected(\n props: InspectorThreadTelemetryProps,\n): void {\n track(\n TELEMETRY_EVENTS.threadsExampleSelected,\n threadExampleProperties(props),\n );\n}\n\nexport function trackThreadsExampleTourStarted(\n props: InspectorThreadTelemetryProps,\n): void {\n track(\n TELEMETRY_EVENTS.threadsExampleTourStarted,\n threadTourProperties(props),\n );\n}\n\nexport function trackThreadsExampleTourStepViewed(\n props: InspectorThreadTelemetryProps,\n): void {\n track(\n TELEMETRY_EVENTS.threadsExampleTourStepViewed,\n threadTourProperties(props),\n );\n}\n\nexport function trackThreadsExampleTourDismissed(\n props: InspectorThreadTelemetryProps,\n): void {\n track(\n TELEMETRY_EVENTS.threadsExampleTourDismissed,\n threadTourTerminalProperties(props),\n );\n}\n\nexport function trackThreadsExampleTourCompleted(\n props: InspectorThreadTelemetryProps,\n): void {\n track(\n TELEMETRY_EVENTS.threadsExampleTourCompleted,\n threadTourTerminalProperties(props),\n );\n}\n\nexport function trackThreadsExampleTourReopened(\n props: InspectorThreadTelemetryProps,\n): void {\n track(\n TELEMETRY_EVENTS.threadsExampleTourReopened,\n threadTourProperties(props),\n );\n}\n\nexport type InspectorMemoryTelemetryProps = {\n package_name?: typeof PACKAGE_NAME;\n package_version?: string;\n inspector_distinct_id?: string;\n posthog_distinct_id?: string;\n memory_count?: number;\n available?: boolean;\n};\n\nexport function trackMemoriesTabClicked(\n props: InspectorMemoryTelemetryProps = {},\n): void {\n track(TELEMETRY_EVENTS.memoriesTabClicked, props);\n}\n\nexport type InspectorLearningViewState =\n | \"loading\"\n | \"error\"\n | \"selection_required\"\n | \"invalid\"\n | \"results\"\n | \"first_run\"\n | \"ready\"\n | \"empty\"\n | \"setup\"\n | \"landing\";\nexport type InspectorLearningCountBucket =\n | \"zero\"\n | \"one\"\n | \"two_to_five\"\n | \"six_to_twenty\"\n | \"twenty_one_plus\";\nexport type InspectorLearningDurationBucket =\n | \"under_250ms\"\n | \"250ms_to_1s\"\n | \"1s_to_3s\"\n | \"3s_plus\";\n\nexport function learningCountBucket(\n value: number,\n): InspectorLearningCountBucket {\n if (value <= 0) return \"zero\";\n if (value === 1) return \"one\";\n if (value <= 5) return \"two_to_five\";\n if (value <= 20) return \"six_to_twenty\";\n return \"twenty_one_plus\";\n}\n\nexport function learningDurationBucket(\n durationMs: number,\n): InspectorLearningDurationBucket {\n if (durationMs < 250) return \"under_250ms\";\n if (durationMs < 1_000) return \"250ms_to_1s\";\n if (durationMs < 3_000) return \"1s_to_3s\";\n return \"3s_plus\";\n}\n\nexport function trackLearningPaneViewed(props: {\n state: InspectorLearningViewState;\n}): void {\n track(TELEMETRY_EVENTS.learningPaneViewed, props);\n}\n\nexport function trackLearningSetupPromptClicked(props: {\n outcome: \"success\" | \"failure\";\n}): void {\n track(TELEMETRY_EVENTS.learningSetupPromptClicked, props);\n}\n\nexport function trackLearningSnapshotLoaded(props: {\n outcome: \"success\" | \"unsupported\" | \"failure\";\n duration_bucket: InspectorLearningDurationBucket;\n skills_bucket: InspectorLearningCountBucket;\n insights_bucket: InspectorLearningCountBucket;\n pending_threads_bucket: InspectorLearningCountBucket;\n}): void {\n track(TELEMETRY_EVENTS.learningSnapshotLoaded, props);\n}\n\nexport function trackLearningSkillToggled(props: {\n action: \"expanded\" | \"collapsed\";\n}): void {\n track(TELEMETRY_EVENTS.learningSkillToggled, props);\n}\n\nexport function trackLearningEvidenceOpened(): void {\n track(TELEMETRY_EVENTS.learningEvidenceOpened);\n}\n\nexport function trackLearningPageChanged(props: {\n section: \"skills\" | \"insights\";\n direction: \"previous\" | \"next\";\n}): void {\n track(TELEMETRY_EVENTS.learningPageChanged, props);\n}\n\nexport function trackLearningWebAppOpened(props: {\n category: \"learning\" | \"runs\" | \"candidates\";\n}): void {\n track(TELEMETRY_EVENTS.learningWebAppOpened, props);\n}\n\nexport type InspectorMetadataTelemetryModule = \"identity\" | \"plan\" | \"action\";\nexport type InspectorMetadataLicenseBucket =\n | \"valid\"\n | \"none\"\n | \"expired\"\n | \"unknown\";\nexport type InspectorMetadataActionKind =\n | \"manage_plan\"\n | \"renew\"\n | \"enable_intelligence\";\n\nexport type InspectorHomeTelemetryProps = Readonly<{\n action_kind?: InspectorMetadataActionKind;\n group_key?: InspectorGroupKey;\n leaf_key?: InspectorLeafKey;\n}>;\n\nexport function trackHomeViewed(props: InspectorHomeTelemetryProps = {}): void {\n track(TELEMETRY_EVENTS.homeViewed, {\n group_key: props.group_key ?? \"home\",\n leaf_key: props.leaf_key ?? \"home\",\n });\n}\n\nexport function trackHomeCtaClicked(props: InspectorHomeTelemetryProps): void {\n track(TELEMETRY_EVENTS.homeCtaClicked, {\n action_kind: props.action_kind,\n group_key: props.group_key ?? \"home\",\n leaf_key: props.leaf_key ?? \"home\",\n });\n}\n\n/**\n * Correlates a copied feature setup prompt with its onboarding run. The run ID\n * is generated locally for this click, not derived from account or runtime\n * data, so it can be safely joined with the onboarding flow downstream.\n */\nexport function trackHomeFeaturePromptClicked(props: {\n feature_id: string;\n onboarding_run_id: string;\n}): void {\n track(TELEMETRY_EVENTS.homeFeaturePromptClicked, {\n feature_id: props.feature_id,\n onboarding_run_id: props.onboarding_run_id,\n group_key: \"home\",\n leaf_key: \"home\",\n });\n}\n\nexport type InspectorHomePromptCopiedTelemetryProps = Readonly<{\n /** The id minted for this session and substituted into the copied prompt. */\n onboarding_run_id: string;\n /** Whether the clipboard write actually landed. */\n outcome: \"copied\" | \"failed\";\n}>;\n\n/**\n * Report a copy of the Intelligence install prompt.\n *\n * `outcome` is reported rather than only emitting on success, because a\n * clipboard that refuses is indistinguishable from a developer who never\n * pressed the button — and the two call for opposite fixes.\n */\nexport function trackHomePromptCopied(\n props: InspectorHomePromptCopiedTelemetryProps,\n): void {\n track(TELEMETRY_EVENTS.homePromptCopied, {\n onboarding_run_id: props.onboarding_run_id,\n outcome: props.outcome,\n group_key: \"home\",\n leaf_key: \"home\",\n });\n}\n\nexport type InspectorHomeStoryBeatTelemetryProps = Readonly<{\n /** Stable id of the step, e.g. \"threads\". Survives a label rename. */\n beat: string;\n /** Its position in the rail, so a reorder can be evaluated against clicks. */\n beat_index: number;\n}>;\n\n/**\n * Report a step of the Intelligence story that a developer opened themselves.\n *\n * Only a press reports. The story also advances on its own every few seconds,\n * and reporting that would bury the handful of real interactions under a\n * metronome — one event per idle developer per six seconds, none of it intent.\n */\nexport function trackHomeStoryBeatSelected(\n props: InspectorHomeStoryBeatTelemetryProps,\n): void {\n track(TELEMETRY_EVENTS.homeStoryBeatSelected, {\n beat: props.beat,\n beat_index: props.beat_index,\n group_key: \"home\",\n leaf_key: \"home\",\n });\n}\n\nexport type InspectorMetadataModuleViewedTelemetryProps = Readonly<{\n module: InspectorMetadataTelemetryModule;\n license_bucket: InspectorMetadataLicenseBucket;\n action_kind?: InspectorMetadataActionKind;\n usage_bucket: ThreadsUsageBucket;\n expiry_bucket: ThreadsExpiryBucket;\n group_key?: InspectorGroupKey;\n leaf_key?: InspectorLeafKey;\n action_placement?: MetadataActionPlacement;\n}>;\n\nexport type InspectorMetadataActionClickedTelemetryProps = Readonly<{\n action_kind: Exclude<InspectorMetadataActionKind, \"enable_intelligence\">;\n license_bucket: InspectorMetadataLicenseBucket;\n usage_bucket: ThreadsUsageBucket;\n expiry_bucket: ThreadsExpiryBucket;\n group_key?: InspectorGroupKey;\n leaf_key?: InspectorLeafKey;\n action_placement?: MetadataActionPlacement;\n}>;\n\nfunction metadataCoarseProperties(\n props:\n | InspectorMetadataModuleViewedTelemetryProps\n | InspectorMetadataActionClickedTelemetryProps,\n) {\n const navigation =\n props.group_key === undefined || props.leaf_key === undefined\n ? {}\n : { group_key: props.group_key, leaf_key: props.leaf_key };\n return {\n license_bucket: props.license_bucket,\n usage_bucket: props.usage_bucket,\n expiry_bucket: props.expiry_bucket,\n ...navigation,\n ...(props.action_placement === undefined\n ? {}\n : { action_placement: props.action_placement }),\n };\n}\n\n/**\n * Tracks one visible Inspector metadata module using coarse fields only.\n * Rebuilding the payload here prevents local labels, IDs, URLs, and usage\n * values from crossing the telemetry boundary through extra object fields.\n */\nexport function trackMetadataModuleViewed(\n props: InspectorMetadataModuleViewedTelemetryProps,\n): void {\n track(TELEMETRY_EVENTS.metadataModuleViewed, {\n module: props.module,\n ...(props.action_kind === undefined\n ? {}\n : { action_kind: props.action_kind }),\n ...metadataCoarseProperties(props),\n });\n}\n\n/**\n * Tracks clicks for managed plan and renewal actions. The existing\n * Intelligence-enable funnel keeps its original event and never calls this\n * helper, which prevents a single click from producing two wire events.\n */\nexport function trackMetadataActionClicked(\n props: InspectorMetadataActionClickedTelemetryProps,\n): void {\n track(TELEMETRY_EVENTS.metadataActionClicked, {\n module: \"action\",\n action_kind: props.action_kind,\n ...metadataCoarseProperties(props),\n });\n}\n\n/**\n * Returns the inspector's anonymous distinct-ID for cross-domain\n * propagation onto outbound announcement-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 * `whats_new_viewed → whats_new_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 announcement-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 let timeoutId: ReturnType<typeof setTimeout> | undefined;\n try {\n const controller = new AbortController();\n timeoutId = setTimeout(() => controller.abort(), FETCH_TIMEOUT_MS);\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 if (timeoutId !== undefined) clearTimeout(timeoutId);\n }\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"telemetry.d.cts","names":[],"sources":["../../src/lib/telemetry.ts"],"mappings":";;;;;;KAmSY,mBAAA"}
1
+ {"version":3,"file":"telemetry.d.cts","names":[],"sources":["../../src/lib/telemetry.ts"],"mappings":";;;;;;KA4WY,mBAAA"}
@@ -1 +1 @@
1
- {"version":3,"file":"telemetry.d.mts","names":[],"sources":["../../src/lib/telemetry.ts"],"mappings":";;;;;;KAmSY,mBAAA"}
1
+ {"version":3,"file":"telemetry.d.mts","names":[],"sources":["../../src/lib/telemetry.ts"],"mappings":";;;;;;KA4WY,mBAAA"}
@@ -8,6 +8,14 @@ const TELEMETRY_EVENTS = {
8
8
  whatsNewSignalViewed: "oss.inspector.whats_new_signal_viewed",
9
9
  errorSignalViewed: "oss.inspector.error_signal_viewed",
10
10
  whatsNewClicked: "oss.inspector.whats_new_clicked",
11
+ hudViewed: "oss.inspector.hud_viewed",
12
+ hudNotificationViewed: "oss.inspector.hud_notification_viewed",
13
+ hudNotificationClicked: "oss.inspector.hud_notification_clicked",
14
+ hudFeatureToggleViewed: "oss.inspector.hud_feature_toggle_viewed",
15
+ hudFeatureToggleClicked: "oss.inspector.hud_feature_toggle_clicked",
16
+ hudFeatureClicked: "oss.inspector.hud_feature_clicked",
17
+ hudHideViewed: "oss.inspector.hud_hide_viewed",
18
+ hudHideClicked: "oss.inspector.hud_hide_clicked",
11
19
  threadsTabClicked: "oss.inspector.threads_tab_clicked",
12
20
  threadsTryFromHereClicked: "oss.inspector.threads_try_from_here_clicked",
13
21
  threadsLockedViewed: "oss.inspector.threads_locked_viewed",
@@ -99,6 +107,32 @@ function trackWhatsNewViewed(props) {
99
107
  function trackWhatsNewSignalViewed(props) {
100
108
  track(TELEMETRY_EVENTS.whatsNewSignalViewed, props);
101
109
  }
110
+ /** One impression per rendered HUD presentation in a visible tab. */
111
+ function trackHudViewed(props) {
112
+ track(TELEMETRY_EVENTS.hudViewed, props);
113
+ }
114
+ /** The UUID identifies the notification consistently across HUD and article events. */
115
+ function trackHudNotificationViewed(props) {
116
+ track(TELEMETRY_EVENTS.hudNotificationViewed, props);
117
+ }
118
+ function trackHudNotificationClicked(props) {
119
+ track(TELEMETRY_EVENTS.hudNotificationClicked, props);
120
+ }
121
+ function trackHudFeatureToggleViewed(props) {
122
+ track(TELEMETRY_EVENTS.hudFeatureToggleViewed, props);
123
+ }
124
+ function trackHudFeatureToggleClicked(props) {
125
+ track(TELEMETRY_EVENTS.hudFeatureToggleClicked, props);
126
+ }
127
+ function trackHudFeatureClicked(props) {
128
+ track(TELEMETRY_EVENTS.hudFeatureClicked, props);
129
+ }
130
+ function trackHudHideViewed(props) {
131
+ track(TELEMETRY_EVENTS.hudHideViewed, props);
132
+ }
133
+ function trackHudHideClicked(props) {
134
+ track(TELEMETRY_EVENTS.hudHideClicked, props);
135
+ }
102
136
  /**
103
137
  * Fires when the launcher's error signal becomes *visible* — not when it arms.
104
138
  * Arming can happen with the panel open or the tab hidden, where there is no
@@ -429,5 +463,5 @@ async function postBestEffort(url, body, distinctId) {
429
463
  }
430
464
 
431
465
  //#endregion
432
- export { TELEMETRY_DOCS_URL, ensureTelemetryDistinctId, getRuntimeUrlType, getTelemetryDistinctIdForUrl, learningCountBucket, learningDurationBucket, maybeShowDisclosure, trackErrorSignalViewed, trackHomeCtaClicked, trackHomeFeaturePromptClicked, trackHomePromptCopied, trackHomeStoryBeatSelected, trackHomeViewed, trackInspectorOpened, trackLearningEvidenceOpened, trackLearningPageChanged, trackLearningPaneViewed, trackLearningSetupPromptClicked, trackLearningSkillToggled, trackLearningSnapshotLoaded, trackLearningWebAppOpened, trackMemoriesTabClicked, trackMetadataActionClicked, trackMetadataModuleViewed, trackTalkToEngineerClicked, trackThreadsEmptyEnabledViewed, trackThreadsEnabledViewed, trackThreadsExampleSelected, trackThreadsExampleTourCompleted, trackThreadsExampleTourDismissed, trackThreadsExampleTourReopened, trackThreadsExampleTourStarted, trackThreadsExampleTourStepViewed, trackThreadsExampleViewed, trackThreadsIntelligenceSignupClicked, trackThreadsLockedViewed, trackThreadsTabClicked, trackThreadsTalkToEngineerClicked, trackThreadsTryFromHereClicked, trackWhatsNewClicked, trackWhatsNewSignalViewed, trackWhatsNewViewed };
466
+ export { TELEMETRY_DOCS_URL, ensureTelemetryDistinctId, getRuntimeUrlType, getTelemetryDistinctIdForUrl, learningCountBucket, learningDurationBucket, maybeShowDisclosure, trackErrorSignalViewed, trackHomeCtaClicked, trackHomeFeaturePromptClicked, trackHomePromptCopied, trackHomeStoryBeatSelected, trackHomeViewed, trackHudFeatureClicked, trackHudFeatureToggleClicked, trackHudFeatureToggleViewed, trackHudHideClicked, trackHudHideViewed, trackHudNotificationClicked, trackHudNotificationViewed, trackHudViewed, trackInspectorOpened, trackLearningEvidenceOpened, trackLearningPageChanged, trackLearningPaneViewed, trackLearningSetupPromptClicked, trackLearningSkillToggled, trackLearningSnapshotLoaded, trackLearningWebAppOpened, trackMemoriesTabClicked, trackMetadataActionClicked, trackMetadataModuleViewed, trackTalkToEngineerClicked, trackThreadsEmptyEnabledViewed, trackThreadsEnabledViewed, trackThreadsExampleSelected, trackThreadsExampleTourCompleted, trackThreadsExampleTourDismissed, trackThreadsExampleTourReopened, trackThreadsExampleTourStarted, trackThreadsExampleTourStepViewed, trackThreadsExampleViewed, trackThreadsIntelligenceSignupClicked, trackThreadsLockedViewed, trackThreadsTabClicked, trackThreadsTalkToEngineerClicked, trackThreadsTryFromHereClicked, trackWhatsNewClicked, trackWhatsNewSignalViewed, trackWhatsNewViewed };
433
467
  //# sourceMappingURL=telemetry.mjs.map
@@ -1 +1 @@
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// What's new and thread-inspection interactions. POSTs directly from the\n// browser to the CopilotKit telemetry sink at\n// `telemetry.copilotkit.ai/ingest`, where a Lambda fan-out forwards events to\n// 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 announcement markdown. Feature-specific properties are scoped to\n// event metadata only (banner_id/timestamp, cta location). Reviewers\n// should grep call 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 opened: \"oss.inspector.opened\",\n whatsNewViewed: \"oss.inspector.whats_new_viewed\",\n whatsNewSignalViewed: \"oss.inspector.whats_new_signal_viewed\",\n errorSignalViewed: \"oss.inspector.error_signal_viewed\",\n whatsNewClicked: \"oss.inspector.whats_new_clicked\",\n threadsTabClicked: \"oss.inspector.threads_tab_clicked\",\n threadsTryFromHereClicked: \"oss.inspector.threads_try_from_here_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 threadsExampleViewed: \"oss.inspector.threads_example_viewed\",\n threadsExampleSelected: \"oss.inspector.threads_example_selected\",\n threadsExampleTourStarted: \"oss.inspector.threads_example_tour_started\",\n threadsExampleTourStepViewed:\n \"oss.inspector.threads_example_tour_step_viewed\",\n threadsExampleTourDismissed: \"oss.inspector.threads_example_tour_dismissed\",\n threadsExampleTourCompleted: \"oss.inspector.threads_example_tour_completed\",\n threadsExampleTourReopened: \"oss.inspector.threads_example_tour_reopened\",\n memoriesTabClicked: \"oss.inspector.memories_tab_clicked\",\n learningPaneViewed: \"oss.inspector.learning_pane_viewed\",\n learningSetupPromptClicked: \"oss.inspector.learning_setup_prompt_clicked\",\n learningSnapshotLoaded: \"oss.inspector.learning_snapshot_loaded\",\n learningSkillToggled: \"oss.inspector.learning_skill_toggled\",\n learningEvidenceOpened: \"oss.inspector.learning_evidence_opened\",\n learningPageChanged: \"oss.inspector.learning_page_changed\",\n learningWebAppOpened: \"oss.inspector.learning_web_app_opened\",\n homeViewed: \"oss.inspector.home_viewed\",\n homeCtaClicked: \"oss.inspector.home_cta_clicked\",\n homeFeaturePromptClicked: \"oss.inspector.home_feature_prompt_clicked\",\n // Carries the CLI's own `onboarding_run_id`, which is the whole point: it is\n // the first event that can be joined to `cli.onboarding.completed` on the\n // Intelligence side. `home_cta_clicked` only ever proved someone clicked a\n // link, never that an install followed.\n homePromptCopied: \"oss.inspector.home_prompt_copied\",\n // Which step of the Intelligence story a developer opened by hand. Carries\n // the beat as a property rather than splitting into one event per label,\n // because the labels are expected to change as the story is iterated and a\n // per-label event would retire with them.\n homeStoryBeatSelected: \"oss.inspector.home_story_beat_selected\",\n metadataModuleViewed: \"oss.inspector.metadata_module_viewed\",\n metadataActionClicked: \"oss.inspector.metadata_action_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 live shell-docs telemetry page\n// (`showcase/shell-docs/src/content/docs/integrations/built-in-agent/telemetry.mdx`).\n// Mirror constant: packages/runtime/src/v1-deprecated/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\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 try {\n const distinctId = getOrCreateTelemetryDistinctId();\n // Every event carries package identity and the inspector's anonymous\n // distinct-ID, so any of them can be segmented by inspector version.\n const body = JSON.stringify({\n event,\n properties: {\n ...properties,\n package_name: PACKAGE_NAME,\n package_version: PACKAGE_VERSION,\n inspector_distinct_id: distinctId,\n distinct_id: distinctId,\n },\n package: {\n name: PACKAGE_NAME,\n version: PACKAGE_VERSION,\n },\n ts: Math.floor(Date.now() / 1000),\n });\n void postBestEffort(TELEMETRY_INGEST_URL, body, distinctId);\n } catch {\n // Identity and serialization failures are best-effort too.\n }\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\n/**\n * Where an announcement was rendered when the event fired. What's new is the\n * only surface that carries one, so the value is currently a constant — it\n * stays a stamped property rather than an inferred one so a second surface\n * can be added without changing the event's shape.\n */\nexport type WhatsNewSurface = \"whats_new\";\n\n/**\n * Whether a launcher signal was animated, or held static because the reader\n * asked for reduced motion. Shared by every launcher signal so an\n * accessibility setting is never mistaken for disinterest.\n */\nexport type LauncherSignalPresentation = \"animated\" | \"reduced_motion\";\n\nexport type WhatsNewSignalPresentation = LauncherSignalPresentation;\n\n/**\n * Which failure class raised the launcher's error signal. A closed enum: the\n * failure *message* is never transmitted, so prompts, URLs and identifiers\n * embedded in an error cannot leave the browser.\n *\n * `connection` and `threads` are wiring *state*. `run`, `tool` and `memory`\n * are unread *events* that clear when their landing view is read.\n */\nexport type InspectorWiringErrorSource = \"connection\" | \"threads\";\nexport type InspectorEventErrorSource = \"run\" | \"tool\" | \"memory\";\nexport type InspectorErrorSignalSource =\n | InspectorWiringErrorSource\n | InspectorEventErrorSource;\n\n/**\n * Fires when What's new has rendered *with content*. A loading state is not\n * an impression, so the metric cannot inflate itself by counting readers who\n * arrived before the feed resolved.\n */\nexport function trackWhatsNewViewed(props: {\n banner_id: string;\n surface: WhatsNewSurface;\n cta_label?: string;\n}): void {\n track(TELEMETRY_EVENTS.whatsNewViewed, props);\n}\n\n/** Fires when the unread launcher signal is presented in a visible tab. */\nexport function trackWhatsNewSignalViewed(props: {\n banner_id: string;\n surface: \"launcher\";\n presentation: WhatsNewSignalPresentation;\n cta_label?: string;\n}): void {\n track(TELEMETRY_EVENTS.whatsNewSignalViewed, props);\n}\n\n/**\n * Whether the launcher opened its pill for this outage, or suppressed it.\n *\n * `suppressed` means no pill was shown. In practice that is the no-room\n * fallback — neither side of the launcher had space for the label — because\n * every other path to a visible error signal opens one.\n */\nexport type InspectorErrorSignalLabel = \"shown\" | \"suppressed\";\n\n/**\n * Fires when the launcher's error signal becomes *visible* — not when it arms.\n * Arming can happen with the panel open or the tab hidden, where there is no\n * launcher to look at, so counting arms would inflate the denominator this\n * event exists to provide.\n *\n * Deliberately carries three fixed enum values and nothing else. This is the\n * one place a later change could casually attach a free-text field, and the\n * failure message must never be transmitted.\n *\n * `label` is deliberately not a new event: the catalogue's size is asserted\n * and spelled out in a test title, and a property answers the one open\n * question — how often the no-room fallback fires — without touching either.\n *\n * The pill's own marginal effect cannot be measured here and must not be\n * reverse-engineered from this data: it ships together with the dot and the\n * beat, so there is no period with one and not the other.\n */\nexport function trackErrorSignalViewed(props: {\n source: InspectorErrorSignalSource;\n presentation: LauncherSignalPresentation;\n label: InspectorErrorSignalLabel;\n}): void {\n track(TELEMETRY_EVENTS.errorSignalViewed, {\n source: props.source,\n presentation: props.presentation,\n label: props.label,\n });\n}\n\n/**\n * Fires for links followed inside the announcement body. `body` is the only\n * call-to-action left: dismissal no longer exists, so there is nothing else a\n * click on this surface can mean.\n */\nexport function trackWhatsNewClicked(props: {\n banner_id: string;\n cta: \"body\";\n cta_label?: string;\n}): void {\n track(TELEMETRY_EVENTS.whatsNewClicked, props);\n}\n\n/**\n * How the panel was opened. Restoring a persisted-open panel on mount is\n * deliberately NOT a source: it is not an open *intent*, and counting it\n * would turn every dev-server hot reload into an \"open\".\n */\nexport type InspectorOpenSource = \"floating_button\" | \"message_toolbar\";\n\nexport type InspectorOpenedTelemetryProps = {\n open_source: InspectorOpenSource;\n package_name?: typeof PACKAGE_NAME;\n package_version?: string;\n inspector_distinct_id?: string;\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 /** True when an unseen announcement was on screen at open time. */\n has_unseen_announcement?: boolean;\n /** True when an error signal was on the launcher at open time. */\n has_error_signal?: boolean;\n /** Which failure class was signalling at open time, when one was. */\n error_signal_source?: InspectorErrorSignalSource;\n /** True when this is the first Inspector open after install or upgrade. */\n first_open?: boolean;\n};\n\n/**\n * Panel-open signal (OSS-566). Before this event, opens could only be\n * inferred from in-panel activity (a floor) or from the announcement's own\n * click event (which misses the floating-button path entirely).\n */\nexport function trackInspectorOpened(\n props: InspectorOpenedTelemetryProps,\n): void {\n track(TELEMETRY_EVENTS.opened, props);\n}\n\nexport type ExampleKind = \"realtime_sync\" | \"manage_history\" | \"inspect_runs\";\nexport type ThreadsUsageBucket =\n | \"absent\"\n | \"empty\"\n | \"within_limit\"\n | \"at_or_over_limit\"\n | \"unlimited\"\n | \"unknown_limit\";\nexport type ThreadsExpiryBucket = \"unavailable\" | \"zero\" | \"positive\";\nexport type InspectorGroupKey = \"home\" | \"workbench\" | \"inspect\";\nexport type InspectorLeafKey =\n | \"home\"\n | \"whats-new\"\n | \"playground\"\n | \"threads\"\n | \"ag-ui-events\"\n | \"agents\"\n | \"frontend-tools\"\n | \"capabilities\"\n | \"agent-context\"\n | \"memories\";\nexport type MetadataActionPlacement = \"threads_footer\" | \"threads_locked\";\nexport type ExampleTourStep = 1 | 2 | 3;\nexport type ExampleTourTab = \"timeline\" | \"raw-events\" | \"state\";\n\nexport type InspectorThreadTelemetryProps = Readonly<{\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 | \"sidebar_footer\";\n cta?: \"signup\" | \"talk_to_engineer\";\n telemetry_disabled?: boolean;\n has_threads?: boolean;\n usage_bucket?: ThreadsUsageBucket;\n expiry_bucket?: ThreadsExpiryBucket;\n group_key?: InspectorGroupKey;\n leaf_key?: InspectorLeafKey;\n example_kind?: ExampleKind;\n tour_step?: ExampleTourStep;\n tour_tab?: ExampleTourTab;\n dismiss_method?: \"skip\" | \"done\";\n outcome?: \"success\" | \"failure\";\n}>;\n\n/** Rebuild the common Thread payload from its closed coarse allowlist. */\nfunction threadCommonProperties(props: InspectorThreadTelemetryProps) {\n return {\n ...(props.intelligence_status === undefined\n ? {}\n : { intelligence_status: props.intelligence_status }),\n ...(props.thread_service_status === undefined\n ? {}\n : { thread_service_status: props.thread_service_status }),\n ...(props.license_status === undefined\n ? {}\n : { license_status: props.license_status }),\n ...(props.runtime_mode === undefined\n ? {}\n : { runtime_mode: props.runtime_mode }),\n ...(props.runtime_url_type === undefined\n ? {}\n : { runtime_url_type: props.runtime_url_type }),\n ...(props.telemetry_disabled === undefined\n ? {}\n : { telemetry_disabled: props.telemetry_disabled }),\n ...(props.has_threads === undefined\n ? {}\n : { has_threads: props.has_threads }),\n ...(props.usage_bucket === undefined\n ? {}\n : { usage_bucket: props.usage_bucket }),\n ...(props.expiry_bucket === undefined\n ? {}\n : { expiry_bucket: props.expiry_bucket }),\n ...(props.group_key === undefined ? {} : { group_key: props.group_key }),\n ...(props.leaf_key === undefined ? {} : { leaf_key: props.leaf_key }),\n };\n}\n\n/** Add only the fields used by the three Thread CTA funnels. */\nfunction threadCtaProperties(props: InspectorThreadTelemetryProps) {\n return {\n ...threadCommonProperties(props),\n ...(props.cta === undefined ? {} : { cta: props.cta }),\n ...(props.cta_surface === undefined\n ? {}\n : { cta_surface: props.cta_surface }),\n ...(props.posthog_distinct_id === undefined\n ? {}\n : { posthog_distinct_id: props.posthog_distinct_id }),\n };\n}\n\n/** Add only the closed example kind used by example impressions/selections. */\nfunction threadExampleProperties(props: InspectorThreadTelemetryProps) {\n return {\n ...threadCommonProperties(props),\n ...(props.example_kind === undefined\n ? {}\n : { example_kind: props.example_kind }),\n };\n}\n\n/** Add only a bounded example kind and tour step/tab pair. */\nfunction threadTourProperties(props: InspectorThreadTelemetryProps) {\n return {\n ...threadExampleProperties(props),\n ...(props.tour_step === undefined ? {} : { tour_step: props.tour_step }),\n ...(props.tour_tab === undefined ? {} : { tour_tab: props.tour_tab }),\n };\n}\n\n/** Add the bounded dismissal leaf used only by terminal tour events. */\nfunction threadTourTerminalProperties(props: InspectorThreadTelemetryProps) {\n return {\n ...threadTourProperties(props),\n ...(props.dismiss_method === undefined\n ? {}\n : { dismiss_method: props.dismiss_method }),\n };\n}\n\nexport function trackThreadsTabClicked(\n props: InspectorThreadTelemetryProps = {},\n): void {\n track(TELEMETRY_EVENTS.threadsTabClicked, threadCommonProperties(props));\n}\n\nexport function trackThreadsTryFromHereClicked(\n props: InspectorThreadTelemetryProps,\n): void {\n track(TELEMETRY_EVENTS.threadsTryFromHereClicked, {\n ...threadCommonProperties(props),\n ...(props.outcome === undefined ? {} : { outcome: props.outcome }),\n });\n}\n\nexport function trackThreadsLockedViewed(\n props: InspectorThreadTelemetryProps,\n): void {\n track(TELEMETRY_EVENTS.threadsLockedViewed, threadCommonProperties(props));\n}\n\nexport function trackThreadsIntelligenceSignupClicked(\n props: InspectorThreadTelemetryProps,\n): void {\n track(\n TELEMETRY_EVENTS.threadsIntelligenceSignupClicked,\n threadCtaProperties(props),\n );\n}\n\nexport function trackThreadsTalkToEngineerClicked(\n props: InspectorThreadTelemetryProps,\n): void {\n track(\n TELEMETRY_EVENTS.threadsTalkToEngineerClicked,\n threadCtaProperties(props),\n );\n}\n\nexport function trackTalkToEngineerClicked(\n props: InspectorThreadTelemetryProps,\n): void {\n track(TELEMETRY_EVENTS.talkToEngineerClicked, threadCtaProperties(props));\n}\n\nexport function trackThreadsEmptyEnabledViewed(\n props: InspectorThreadTelemetryProps,\n): void {\n track(\n TELEMETRY_EVENTS.threadsEmptyEnabledViewed,\n threadCommonProperties(props),\n );\n}\n\nexport function trackThreadsEnabledViewed(\n props: InspectorThreadTelemetryProps,\n): void {\n track(TELEMETRY_EVENTS.threadsEnabledViewed, threadCommonProperties(props));\n}\n\nexport function trackThreadsExampleViewed(\n props: InspectorThreadTelemetryProps,\n): void {\n track(TELEMETRY_EVENTS.threadsExampleViewed, threadExampleProperties(props));\n}\n\nexport function trackThreadsExampleSelected(\n props: InspectorThreadTelemetryProps,\n): void {\n track(\n TELEMETRY_EVENTS.threadsExampleSelected,\n threadExampleProperties(props),\n );\n}\n\nexport function trackThreadsExampleTourStarted(\n props: InspectorThreadTelemetryProps,\n): void {\n track(\n TELEMETRY_EVENTS.threadsExampleTourStarted,\n threadTourProperties(props),\n );\n}\n\nexport function trackThreadsExampleTourStepViewed(\n props: InspectorThreadTelemetryProps,\n): void {\n track(\n TELEMETRY_EVENTS.threadsExampleTourStepViewed,\n threadTourProperties(props),\n );\n}\n\nexport function trackThreadsExampleTourDismissed(\n props: InspectorThreadTelemetryProps,\n): void {\n track(\n TELEMETRY_EVENTS.threadsExampleTourDismissed,\n threadTourTerminalProperties(props),\n );\n}\n\nexport function trackThreadsExampleTourCompleted(\n props: InspectorThreadTelemetryProps,\n): void {\n track(\n TELEMETRY_EVENTS.threadsExampleTourCompleted,\n threadTourTerminalProperties(props),\n );\n}\n\nexport function trackThreadsExampleTourReopened(\n props: InspectorThreadTelemetryProps,\n): void {\n track(\n TELEMETRY_EVENTS.threadsExampleTourReopened,\n threadTourProperties(props),\n );\n}\n\nexport type InspectorMemoryTelemetryProps = {\n package_name?: typeof PACKAGE_NAME;\n package_version?: string;\n inspector_distinct_id?: string;\n posthog_distinct_id?: string;\n memory_count?: number;\n available?: boolean;\n};\n\nexport function trackMemoriesTabClicked(\n props: InspectorMemoryTelemetryProps = {},\n): void {\n track(TELEMETRY_EVENTS.memoriesTabClicked, props);\n}\n\nexport type InspectorLearningViewState =\n | \"loading\"\n | \"error\"\n | \"selection_required\"\n | \"invalid\"\n | \"results\"\n | \"first_run\"\n | \"ready\"\n | \"empty\"\n | \"setup\"\n | \"landing\";\nexport type InspectorLearningCountBucket =\n | \"zero\"\n | \"one\"\n | \"two_to_five\"\n | \"six_to_twenty\"\n | \"twenty_one_plus\";\nexport type InspectorLearningDurationBucket =\n | \"under_250ms\"\n | \"250ms_to_1s\"\n | \"1s_to_3s\"\n | \"3s_plus\";\n\nexport function learningCountBucket(\n value: number,\n): InspectorLearningCountBucket {\n if (value <= 0) return \"zero\";\n if (value === 1) return \"one\";\n if (value <= 5) return \"two_to_five\";\n if (value <= 20) return \"six_to_twenty\";\n return \"twenty_one_plus\";\n}\n\nexport function learningDurationBucket(\n durationMs: number,\n): InspectorLearningDurationBucket {\n if (durationMs < 250) return \"under_250ms\";\n if (durationMs < 1_000) return \"250ms_to_1s\";\n if (durationMs < 3_000) return \"1s_to_3s\";\n return \"3s_plus\";\n}\n\nexport function trackLearningPaneViewed(props: {\n state: InspectorLearningViewState;\n}): void {\n track(TELEMETRY_EVENTS.learningPaneViewed, props);\n}\n\nexport function trackLearningSetupPromptClicked(props: {\n outcome: \"success\" | \"failure\";\n}): void {\n track(TELEMETRY_EVENTS.learningSetupPromptClicked, props);\n}\n\nexport function trackLearningSnapshotLoaded(props: {\n outcome: \"success\" | \"unsupported\" | \"failure\";\n duration_bucket: InspectorLearningDurationBucket;\n skills_bucket: InspectorLearningCountBucket;\n insights_bucket: InspectorLearningCountBucket;\n pending_threads_bucket: InspectorLearningCountBucket;\n}): void {\n track(TELEMETRY_EVENTS.learningSnapshotLoaded, props);\n}\n\nexport function trackLearningSkillToggled(props: {\n action: \"expanded\" | \"collapsed\";\n}): void {\n track(TELEMETRY_EVENTS.learningSkillToggled, props);\n}\n\nexport function trackLearningEvidenceOpened(): void {\n track(TELEMETRY_EVENTS.learningEvidenceOpened);\n}\n\nexport function trackLearningPageChanged(props: {\n section: \"skills\" | \"insights\";\n direction: \"previous\" | \"next\";\n}): void {\n track(TELEMETRY_EVENTS.learningPageChanged, props);\n}\n\nexport function trackLearningWebAppOpened(props: {\n category: \"learning\" | \"runs\" | \"candidates\";\n}): void {\n track(TELEMETRY_EVENTS.learningWebAppOpened, props);\n}\n\nexport type InspectorMetadataTelemetryModule = \"identity\" | \"plan\" | \"action\";\nexport type InspectorMetadataLicenseBucket =\n | \"valid\"\n | \"none\"\n | \"expired\"\n | \"unknown\";\nexport type InspectorMetadataActionKind =\n | \"manage_plan\"\n | \"renew\"\n | \"enable_intelligence\";\n\nexport type InspectorHomeTelemetryProps = Readonly<{\n action_kind?: InspectorMetadataActionKind;\n group_key?: InspectorGroupKey;\n leaf_key?: InspectorLeafKey;\n}>;\n\nexport function trackHomeViewed(props: InspectorHomeTelemetryProps = {}): void {\n track(TELEMETRY_EVENTS.homeViewed, {\n group_key: props.group_key ?? \"home\",\n leaf_key: props.leaf_key ?? \"home\",\n });\n}\n\nexport function trackHomeCtaClicked(props: InspectorHomeTelemetryProps): void {\n track(TELEMETRY_EVENTS.homeCtaClicked, {\n action_kind: props.action_kind,\n group_key: props.group_key ?? \"home\",\n leaf_key: props.leaf_key ?? \"home\",\n });\n}\n\n/**\n * Correlates a copied feature setup prompt with its onboarding run. The run ID\n * is generated locally for this click, not derived from account or runtime\n * data, so it can be safely joined with the onboarding flow downstream.\n */\nexport function trackHomeFeaturePromptClicked(props: {\n feature_id: string;\n onboarding_run_id: string;\n}): void {\n track(TELEMETRY_EVENTS.homeFeaturePromptClicked, {\n feature_id: props.feature_id,\n onboarding_run_id: props.onboarding_run_id,\n group_key: \"home\",\n leaf_key: \"home\",\n });\n}\n\nexport type InspectorHomePromptCopiedTelemetryProps = Readonly<{\n /** The id minted for this session and substituted into the copied prompt. */\n onboarding_run_id: string;\n /** Whether the clipboard write actually landed. */\n outcome: \"copied\" | \"failed\";\n}>;\n\n/**\n * Report a copy of the Intelligence install prompt.\n *\n * `outcome` is reported rather than only emitting on success, because a\n * clipboard that refuses is indistinguishable from a developer who never\n * pressed the button — and the two call for opposite fixes.\n */\nexport function trackHomePromptCopied(\n props: InspectorHomePromptCopiedTelemetryProps,\n): void {\n track(TELEMETRY_EVENTS.homePromptCopied, {\n onboarding_run_id: props.onboarding_run_id,\n outcome: props.outcome,\n group_key: \"home\",\n leaf_key: \"home\",\n });\n}\n\nexport type InspectorHomeStoryBeatTelemetryProps = Readonly<{\n /** Stable id of the step, e.g. \"threads\". Survives a label rename. */\n beat: string;\n /** Its position in the rail, so a reorder can be evaluated against clicks. */\n beat_index: number;\n}>;\n\n/**\n * Report a step of the Intelligence story that a developer opened themselves.\n *\n * Only a press reports. The story also advances on its own every few seconds,\n * and reporting that would bury the handful of real interactions under a\n * metronome — one event per idle developer per six seconds, none of it intent.\n */\nexport function trackHomeStoryBeatSelected(\n props: InspectorHomeStoryBeatTelemetryProps,\n): void {\n track(TELEMETRY_EVENTS.homeStoryBeatSelected, {\n beat: props.beat,\n beat_index: props.beat_index,\n group_key: \"home\",\n leaf_key: \"home\",\n });\n}\n\nexport type InspectorMetadataModuleViewedTelemetryProps = Readonly<{\n module: InspectorMetadataTelemetryModule;\n license_bucket: InspectorMetadataLicenseBucket;\n action_kind?: InspectorMetadataActionKind;\n usage_bucket: ThreadsUsageBucket;\n expiry_bucket: ThreadsExpiryBucket;\n group_key?: InspectorGroupKey;\n leaf_key?: InspectorLeafKey;\n action_placement?: MetadataActionPlacement;\n}>;\n\nexport type InspectorMetadataActionClickedTelemetryProps = Readonly<{\n action_kind: Exclude<InspectorMetadataActionKind, \"enable_intelligence\">;\n license_bucket: InspectorMetadataLicenseBucket;\n usage_bucket: ThreadsUsageBucket;\n expiry_bucket: ThreadsExpiryBucket;\n group_key?: InspectorGroupKey;\n leaf_key?: InspectorLeafKey;\n action_placement?: MetadataActionPlacement;\n}>;\n\nfunction metadataCoarseProperties(\n props:\n | InspectorMetadataModuleViewedTelemetryProps\n | InspectorMetadataActionClickedTelemetryProps,\n) {\n const navigation =\n props.group_key === undefined || props.leaf_key === undefined\n ? {}\n : { group_key: props.group_key, leaf_key: props.leaf_key };\n return {\n license_bucket: props.license_bucket,\n usage_bucket: props.usage_bucket,\n expiry_bucket: props.expiry_bucket,\n ...navigation,\n ...(props.action_placement === undefined\n ? {}\n : { action_placement: props.action_placement }),\n };\n}\n\n/**\n * Tracks one visible Inspector metadata module using coarse fields only.\n * Rebuilding the payload here prevents local labels, IDs, URLs, and usage\n * values from crossing the telemetry boundary through extra object fields.\n */\nexport function trackMetadataModuleViewed(\n props: InspectorMetadataModuleViewedTelemetryProps,\n): void {\n track(TELEMETRY_EVENTS.metadataModuleViewed, {\n module: props.module,\n ...(props.action_kind === undefined\n ? {}\n : { action_kind: props.action_kind }),\n ...metadataCoarseProperties(props),\n });\n}\n\n/**\n * Tracks clicks for managed plan and renewal actions. The existing\n * Intelligence-enable funnel keeps its original event and never calls this\n * helper, which prevents a single click from producing two wire events.\n */\nexport function trackMetadataActionClicked(\n props: InspectorMetadataActionClickedTelemetryProps,\n): void {\n track(TELEMETRY_EVENTS.metadataActionClicked, {\n module: \"action\",\n action_kind: props.action_kind,\n ...metadataCoarseProperties(props),\n });\n}\n\n/**\n * Returns the inspector's anonymous distinct-ID for cross-domain\n * propagation onto outbound announcement-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 * `whats_new_viewed → whats_new_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 announcement-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 let timeoutId: ReturnType<typeof setTimeout> | undefined;\n try {\n const controller = new AbortController();\n timeoutId = setTimeout(() => controller.abort(), FETCH_TIMEOUT_MS);\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 if (timeoutId !== undefined) clearTimeout(timeoutId);\n }\n}\n"],"mappings":"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"}
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// the launcher HUD, What's new, and thread-inspection interactions. POSTs directly from the\n// browser to the CopilotKit telemetry sink at\n// `telemetry.copilotkit.ai/ingest`, where a Lambda fan-out forwards events to\n// 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 announcement markdown. Feature-specific properties are scoped to\n// event metadata only (notification UUID, cta location). Reviewers\n// should grep call 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 opened: \"oss.inspector.opened\",\n whatsNewViewed: \"oss.inspector.whats_new_viewed\",\n whatsNewSignalViewed: \"oss.inspector.whats_new_signal_viewed\",\n errorSignalViewed: \"oss.inspector.error_signal_viewed\",\n whatsNewClicked: \"oss.inspector.whats_new_clicked\",\n hudViewed: \"oss.inspector.hud_viewed\",\n hudNotificationViewed: \"oss.inspector.hud_notification_viewed\",\n hudNotificationClicked: \"oss.inspector.hud_notification_clicked\",\n hudFeatureToggleViewed: \"oss.inspector.hud_feature_toggle_viewed\",\n hudFeatureToggleClicked: \"oss.inspector.hud_feature_toggle_clicked\",\n hudFeatureClicked: \"oss.inspector.hud_feature_clicked\",\n hudHideViewed: \"oss.inspector.hud_hide_viewed\",\n hudHideClicked: \"oss.inspector.hud_hide_clicked\",\n threadsTabClicked: \"oss.inspector.threads_tab_clicked\",\n threadsTryFromHereClicked: \"oss.inspector.threads_try_from_here_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 threadsExampleViewed: \"oss.inspector.threads_example_viewed\",\n threadsExampleSelected: \"oss.inspector.threads_example_selected\",\n threadsExampleTourStarted: \"oss.inspector.threads_example_tour_started\",\n threadsExampleTourStepViewed:\n \"oss.inspector.threads_example_tour_step_viewed\",\n threadsExampleTourDismissed: \"oss.inspector.threads_example_tour_dismissed\",\n threadsExampleTourCompleted: \"oss.inspector.threads_example_tour_completed\",\n threadsExampleTourReopened: \"oss.inspector.threads_example_tour_reopened\",\n memoriesTabClicked: \"oss.inspector.memories_tab_clicked\",\n learningPaneViewed: \"oss.inspector.learning_pane_viewed\",\n learningSetupPromptClicked: \"oss.inspector.learning_setup_prompt_clicked\",\n learningSnapshotLoaded: \"oss.inspector.learning_snapshot_loaded\",\n learningSkillToggled: \"oss.inspector.learning_skill_toggled\",\n learningEvidenceOpened: \"oss.inspector.learning_evidence_opened\",\n learningPageChanged: \"oss.inspector.learning_page_changed\",\n learningWebAppOpened: \"oss.inspector.learning_web_app_opened\",\n homeViewed: \"oss.inspector.home_viewed\",\n homeCtaClicked: \"oss.inspector.home_cta_clicked\",\n homeFeaturePromptClicked: \"oss.inspector.home_feature_prompt_clicked\",\n // Carries the CLI's own `onboarding_run_id`, which is the whole point: it is\n // the first event that can be joined to `cli.onboarding.completed` on the\n // Intelligence side. `home_cta_clicked` only ever proved someone clicked a\n // link, never that an install followed.\n homePromptCopied: \"oss.inspector.home_prompt_copied\",\n // Which step of the Intelligence story a developer opened by hand. Carries\n // the beat as a property rather than splitting into one event per label,\n // because the labels are expected to change as the story is iterated and a\n // per-label event would retire with them.\n homeStoryBeatSelected: \"oss.inspector.home_story_beat_selected\",\n metadataModuleViewed: \"oss.inspector.metadata_module_viewed\",\n metadataActionClicked: \"oss.inspector.metadata_action_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 live shell-docs telemetry page\n// (`showcase/shell-docs/src/content/docs/integrations/built-in-agent/telemetry.mdx`).\n// Mirror constant: packages/runtime/src/v1-deprecated/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\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 try {\n const distinctId = getOrCreateTelemetryDistinctId();\n // Every event carries package identity and the inspector's anonymous\n // distinct-ID, so any of them can be segmented by inspector version.\n const body = JSON.stringify({\n event,\n properties: {\n ...properties,\n package_name: PACKAGE_NAME,\n package_version: PACKAGE_VERSION,\n inspector_distinct_id: distinctId,\n distinct_id: distinctId,\n },\n package: {\n name: PACKAGE_NAME,\n version: PACKAGE_VERSION,\n },\n ts: Math.floor(Date.now() / 1000),\n });\n void postBestEffort(TELEMETRY_INGEST_URL, body, distinctId);\n } catch {\n // Identity and serialization failures are best-effort too.\n }\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\n/**\n * Where an announcement was rendered when the event fired. What's new is the\n * only surface of `whats_new_viewed`. The HUD has its own notification event,\n * so its impressions do not change the meaning of this event.\n */\nexport type WhatsNewSurface = \"whats_new\";\n\n/**\n * Whether a launcher signal was animated, or held static because the reader\n * asked for reduced motion. Shared by every launcher signal so an\n * accessibility setting is never mistaken for disinterest.\n */\nexport type LauncherSignalPresentation = \"animated\" | \"reduced_motion\";\n\nexport type WhatsNewSignalPresentation = LauncherSignalPresentation;\n\n/**\n * Which failure class raised the launcher's error signal. A closed enum: the\n * failure *message* is never transmitted, so prompts, URLs and identifiers\n * embedded in an error cannot leave the browser.\n *\n * `connection` and `threads` are wiring *state*. `run`, `tool` and `memory`\n * are unread *events* that clear when their landing view is read.\n */\nexport type InspectorWiringErrorSource = \"connection\" | \"threads\";\nexport type InspectorEventErrorSource = \"run\" | \"tool\" | \"memory\";\nexport type InspectorErrorSignalSource =\n | InspectorWiringErrorSource\n | InspectorEventErrorSource;\n\n/**\n * Fires when What's new has rendered *with content*. A loading state is not\n * an impression, so the metric cannot inflate itself by counting readers who\n * arrived before the feed resolved.\n */\nexport function trackWhatsNewViewed(props: {\n banner_id: string;\n notification_id?: string;\n surface: WhatsNewSurface;\n cta_label?: string;\n}): void {\n track(TELEMETRY_EVENTS.whatsNewViewed, props);\n}\n\n/** Fires when the unread launcher signal is presented in a visible tab. */\nexport function trackWhatsNewSignalViewed(props: {\n banner_id: string;\n notification_id?: string;\n surface: \"launcher\";\n presentation: WhatsNewSignalPresentation;\n cta_label?: string;\n}): void {\n track(TELEMETRY_EVENTS.whatsNewSignalViewed, props);\n}\n\nexport type HudFeature = \"threads\" | \"learning\";\n\n/**\n * What opened the HUD presentation. `intro` is the automatic preview that\n * plays after every mount, `user` is a pointer or keyboard open. A user who\n * takes over a playing intro keeps `intro`, because the presentation itself\n * was not requested.\n */\nexport type HudTrigger = \"intro\" | \"user\";\n\n/** One impression per rendered HUD presentation in a visible tab. */\nexport function trackHudViewed(props: { trigger: HudTrigger }): void {\n track(TELEMETRY_EVENTS.hudViewed, props);\n}\n\n/** The UUID identifies the notification consistently across HUD and article events. */\nexport function trackHudNotificationViewed(props: {\n banner_id: string;\n notification_id: string;\n trigger: HudTrigger;\n}): void {\n track(TELEMETRY_EVENTS.hudNotificationViewed, props);\n}\n\nexport function trackHudNotificationClicked(props: {\n banner_id: string;\n notification_id: string;\n action: \"open\" | \"dismiss\";\n trigger: HudTrigger;\n}): void {\n track(TELEMETRY_EVENTS.hudNotificationClicked, props);\n}\n\nexport function trackHudFeatureToggleViewed(props: {\n feature: HudFeature;\n trigger: HudTrigger;\n}): void {\n track(TELEMETRY_EVENTS.hudFeatureToggleViewed, props);\n}\n\nexport function trackHudFeatureToggleClicked(props: {\n feature: HudFeature;\n trigger: HudTrigger;\n}): void {\n track(TELEMETRY_EVENTS.hudFeatureToggleClicked, props);\n}\n\nexport function trackHudFeatureClicked(props: {\n feature: HudFeature;\n control: \"row\" | \"action\" | \"learn_more\";\n trigger: HudTrigger;\n}): void {\n track(TELEMETRY_EVENTS.hudFeatureClicked, props);\n}\n\nexport function trackHudHideViewed(props: { trigger: HudTrigger }): void {\n track(TELEMETRY_EVENTS.hudHideViewed, props);\n}\n\nexport function trackHudHideClicked(props: { trigger: HudTrigger }): void {\n track(TELEMETRY_EVENTS.hudHideClicked, props);\n}\n\n/**\n * Whether the launcher opened its pill for this outage, or suppressed it.\n *\n * `suppressed` means no pill was shown. In practice that is the no-room\n * fallback — neither side of the launcher had space for the label — because\n * every other path to a visible error signal opens one.\n */\nexport type InspectorErrorSignalLabel = \"shown\" | \"suppressed\";\n\n/**\n * Fires when the launcher's error signal becomes *visible* — not when it arms.\n * Arming can happen with the panel open or the tab hidden, where there is no\n * launcher to look at, so counting arms would inflate the denominator this\n * event exists to provide.\n *\n * Deliberately carries three fixed enum values and nothing else. This is the\n * one place a later change could casually attach a free-text field, and the\n * failure message must never be transmitted.\n *\n * `label` is deliberately not a new event: the catalogue's size is asserted\n * and spelled out in a test title, and a property answers the one open\n * question — how often the no-room fallback fires — without touching either.\n *\n * The pill's own marginal effect cannot be measured here and must not be\n * reverse-engineered from this data: it ships together with the dot and the\n * beat, so there is no period with one and not the other.\n */\nexport function trackErrorSignalViewed(props: {\n source: InspectorErrorSignalSource;\n presentation: LauncherSignalPresentation;\n label: InspectorErrorSignalLabel;\n}): void {\n track(TELEMETRY_EVENTS.errorSignalViewed, {\n source: props.source,\n presentation: props.presentation,\n label: props.label,\n });\n}\n\n/**\n * Fires for links followed inside the announcement body. `body` is the only\n * call-to-action left: dismissal no longer exists, so there is nothing else a\n * click on this surface can mean.\n */\nexport function trackWhatsNewClicked(props: {\n banner_id: string;\n notification_id?: string;\n cta: \"body\";\n cta_label?: string;\n}): void {\n track(TELEMETRY_EVENTS.whatsNewClicked, props);\n}\n\n/**\n * How the panel was opened. Restoring a persisted-open panel on mount is\n * deliberately NOT a source: it is not an open *intent*, and counting it\n * would turn every dev-server hot reload into an \"open\".\n */\nexport type InspectorOpenSource = \"floating_button\" | \"message_toolbar\";\n\nexport type InspectorOpenedTelemetryProps = {\n open_source: InspectorOpenSource;\n package_name?: typeof PACKAGE_NAME;\n package_version?: string;\n inspector_distinct_id?: string;\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 /** True when an unseen announcement was on screen at open time. */\n has_unseen_announcement?: boolean;\n /** True when an error signal was on the launcher at open time. */\n has_error_signal?: boolean;\n /** Which failure class was signalling at open time, when one was. */\n error_signal_source?: InspectorErrorSignalSource;\n /** True when this is the first Inspector open after install or upgrade. */\n first_open?: boolean;\n};\n\n/**\n * Panel-open signal (OSS-566). Before this event, opens could only be\n * inferred from in-panel activity (a floor) or from the announcement's own\n * click event (which misses the floating-button path entirely).\n */\nexport function trackInspectorOpened(\n props: InspectorOpenedTelemetryProps,\n): void {\n track(TELEMETRY_EVENTS.opened, props);\n}\n\nexport type ExampleKind = \"realtime_sync\" | \"manage_history\" | \"inspect_runs\";\nexport type ThreadsUsageBucket =\n | \"absent\"\n | \"empty\"\n | \"within_limit\"\n | \"at_or_over_limit\"\n | \"unlimited\"\n | \"unknown_limit\";\nexport type ThreadsExpiryBucket = \"unavailable\" | \"zero\" | \"positive\";\nexport type InspectorGroupKey = \"home\" | \"workbench\" | \"inspect\";\nexport type InspectorLeafKey =\n | \"home\"\n | \"whats-new\"\n | \"playground\"\n | \"threads\"\n | \"ag-ui-events\"\n | \"agents\"\n | \"frontend-tools\"\n | \"capabilities\"\n | \"agent-context\"\n | \"memories\";\nexport type MetadataActionPlacement = \"threads_footer\" | \"threads_locked\";\nexport type ExampleTourStep = 1 | 2 | 3;\nexport type ExampleTourTab = \"timeline\" | \"raw-events\" | \"state\";\n\nexport type InspectorThreadTelemetryProps = Readonly<{\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 | \"sidebar_footer\";\n cta?: \"signup\" | \"talk_to_engineer\";\n telemetry_disabled?: boolean;\n has_threads?: boolean;\n usage_bucket?: ThreadsUsageBucket;\n expiry_bucket?: ThreadsExpiryBucket;\n group_key?: InspectorGroupKey;\n leaf_key?: InspectorLeafKey;\n example_kind?: ExampleKind;\n tour_step?: ExampleTourStep;\n tour_tab?: ExampleTourTab;\n dismiss_method?: \"skip\" | \"done\";\n outcome?: \"success\" | \"failure\";\n}>;\n\n/** Rebuild the common Thread payload from its closed coarse allowlist. */\nfunction threadCommonProperties(props: InspectorThreadTelemetryProps) {\n return {\n ...(props.intelligence_status === undefined\n ? {}\n : { intelligence_status: props.intelligence_status }),\n ...(props.thread_service_status === undefined\n ? {}\n : { thread_service_status: props.thread_service_status }),\n ...(props.license_status === undefined\n ? {}\n : { license_status: props.license_status }),\n ...(props.runtime_mode === undefined\n ? {}\n : { runtime_mode: props.runtime_mode }),\n ...(props.runtime_url_type === undefined\n ? {}\n : { runtime_url_type: props.runtime_url_type }),\n ...(props.telemetry_disabled === undefined\n ? {}\n : { telemetry_disabled: props.telemetry_disabled }),\n ...(props.has_threads === undefined\n ? {}\n : { has_threads: props.has_threads }),\n ...(props.usage_bucket === undefined\n ? {}\n : { usage_bucket: props.usage_bucket }),\n ...(props.expiry_bucket === undefined\n ? {}\n : { expiry_bucket: props.expiry_bucket }),\n ...(props.group_key === undefined ? {} : { group_key: props.group_key }),\n ...(props.leaf_key === undefined ? {} : { leaf_key: props.leaf_key }),\n };\n}\n\n/** Add only the fields used by the three Thread CTA funnels. */\nfunction threadCtaProperties(props: InspectorThreadTelemetryProps) {\n return {\n ...threadCommonProperties(props),\n ...(props.cta === undefined ? {} : { cta: props.cta }),\n ...(props.cta_surface === undefined\n ? {}\n : { cta_surface: props.cta_surface }),\n ...(props.posthog_distinct_id === undefined\n ? {}\n : { posthog_distinct_id: props.posthog_distinct_id }),\n };\n}\n\n/** Add only the closed example kind used by example impressions/selections. */\nfunction threadExampleProperties(props: InspectorThreadTelemetryProps) {\n return {\n ...threadCommonProperties(props),\n ...(props.example_kind === undefined\n ? {}\n : { example_kind: props.example_kind }),\n };\n}\n\n/** Add only a bounded example kind and tour step/tab pair. */\nfunction threadTourProperties(props: InspectorThreadTelemetryProps) {\n return {\n ...threadExampleProperties(props),\n ...(props.tour_step === undefined ? {} : { tour_step: props.tour_step }),\n ...(props.tour_tab === undefined ? {} : { tour_tab: props.tour_tab }),\n };\n}\n\n/** Add the bounded dismissal leaf used only by terminal tour events. */\nfunction threadTourTerminalProperties(props: InspectorThreadTelemetryProps) {\n return {\n ...threadTourProperties(props),\n ...(props.dismiss_method === undefined\n ? {}\n : { dismiss_method: props.dismiss_method }),\n };\n}\n\nexport function trackThreadsTabClicked(\n props: InspectorThreadTelemetryProps = {},\n): void {\n track(TELEMETRY_EVENTS.threadsTabClicked, threadCommonProperties(props));\n}\n\nexport function trackThreadsTryFromHereClicked(\n props: InspectorThreadTelemetryProps,\n): void {\n track(TELEMETRY_EVENTS.threadsTryFromHereClicked, {\n ...threadCommonProperties(props),\n ...(props.outcome === undefined ? {} : { outcome: props.outcome }),\n });\n}\n\nexport function trackThreadsLockedViewed(\n props: InspectorThreadTelemetryProps,\n): void {\n track(TELEMETRY_EVENTS.threadsLockedViewed, threadCommonProperties(props));\n}\n\nexport function trackThreadsIntelligenceSignupClicked(\n props: InspectorThreadTelemetryProps,\n): void {\n track(\n TELEMETRY_EVENTS.threadsIntelligenceSignupClicked,\n threadCtaProperties(props),\n );\n}\n\nexport function trackThreadsTalkToEngineerClicked(\n props: InspectorThreadTelemetryProps,\n): void {\n track(\n TELEMETRY_EVENTS.threadsTalkToEngineerClicked,\n threadCtaProperties(props),\n );\n}\n\nexport function trackTalkToEngineerClicked(\n props: InspectorThreadTelemetryProps,\n): void {\n track(TELEMETRY_EVENTS.talkToEngineerClicked, threadCtaProperties(props));\n}\n\nexport function trackThreadsEmptyEnabledViewed(\n props: InspectorThreadTelemetryProps,\n): void {\n track(\n TELEMETRY_EVENTS.threadsEmptyEnabledViewed,\n threadCommonProperties(props),\n );\n}\n\nexport function trackThreadsEnabledViewed(\n props: InspectorThreadTelemetryProps,\n): void {\n track(TELEMETRY_EVENTS.threadsEnabledViewed, threadCommonProperties(props));\n}\n\nexport function trackThreadsExampleViewed(\n props: InspectorThreadTelemetryProps,\n): void {\n track(TELEMETRY_EVENTS.threadsExampleViewed, threadExampleProperties(props));\n}\n\nexport function trackThreadsExampleSelected(\n props: InspectorThreadTelemetryProps,\n): void {\n track(\n TELEMETRY_EVENTS.threadsExampleSelected,\n threadExampleProperties(props),\n );\n}\n\nexport function trackThreadsExampleTourStarted(\n props: InspectorThreadTelemetryProps,\n): void {\n track(\n TELEMETRY_EVENTS.threadsExampleTourStarted,\n threadTourProperties(props),\n );\n}\n\nexport function trackThreadsExampleTourStepViewed(\n props: InspectorThreadTelemetryProps,\n): void {\n track(\n TELEMETRY_EVENTS.threadsExampleTourStepViewed,\n threadTourProperties(props),\n );\n}\n\nexport function trackThreadsExampleTourDismissed(\n props: InspectorThreadTelemetryProps,\n): void {\n track(\n TELEMETRY_EVENTS.threadsExampleTourDismissed,\n threadTourTerminalProperties(props),\n );\n}\n\nexport function trackThreadsExampleTourCompleted(\n props: InspectorThreadTelemetryProps,\n): void {\n track(\n TELEMETRY_EVENTS.threadsExampleTourCompleted,\n threadTourTerminalProperties(props),\n );\n}\n\nexport function trackThreadsExampleTourReopened(\n props: InspectorThreadTelemetryProps,\n): void {\n track(\n TELEMETRY_EVENTS.threadsExampleTourReopened,\n threadTourProperties(props),\n );\n}\n\nexport type InspectorMemoryTelemetryProps = {\n package_name?: typeof PACKAGE_NAME;\n package_version?: string;\n inspector_distinct_id?: string;\n posthog_distinct_id?: string;\n memory_count?: number;\n available?: boolean;\n};\n\nexport function trackMemoriesTabClicked(\n props: InspectorMemoryTelemetryProps = {},\n): void {\n track(TELEMETRY_EVENTS.memoriesTabClicked, props);\n}\n\nexport type InspectorLearningViewState =\n | \"loading\"\n | \"error\"\n | \"selection_required\"\n | \"invalid\"\n | \"results\"\n | \"first_run\"\n | \"ready\"\n | \"empty\"\n | \"setup\"\n | \"landing\";\nexport type InspectorLearningCountBucket =\n | \"zero\"\n | \"one\"\n | \"two_to_five\"\n | \"six_to_twenty\"\n | \"twenty_one_plus\";\nexport type InspectorLearningDurationBucket =\n | \"under_250ms\"\n | \"250ms_to_1s\"\n | \"1s_to_3s\"\n | \"3s_plus\";\n\nexport function learningCountBucket(\n value: number,\n): InspectorLearningCountBucket {\n if (value <= 0) return \"zero\";\n if (value === 1) return \"one\";\n if (value <= 5) return \"two_to_five\";\n if (value <= 20) return \"six_to_twenty\";\n return \"twenty_one_plus\";\n}\n\nexport function learningDurationBucket(\n durationMs: number,\n): InspectorLearningDurationBucket {\n if (durationMs < 250) return \"under_250ms\";\n if (durationMs < 1_000) return \"250ms_to_1s\";\n if (durationMs < 3_000) return \"1s_to_3s\";\n return \"3s_plus\";\n}\n\nexport function trackLearningPaneViewed(props: {\n state: InspectorLearningViewState;\n}): void {\n track(TELEMETRY_EVENTS.learningPaneViewed, props);\n}\n\nexport function trackLearningSetupPromptClicked(props: {\n outcome: \"success\" | \"failure\";\n}): void {\n track(TELEMETRY_EVENTS.learningSetupPromptClicked, props);\n}\n\nexport function trackLearningSnapshotLoaded(props: {\n outcome: \"success\" | \"unsupported\" | \"failure\";\n duration_bucket: InspectorLearningDurationBucket;\n skills_bucket: InspectorLearningCountBucket;\n insights_bucket: InspectorLearningCountBucket;\n pending_threads_bucket: InspectorLearningCountBucket;\n}): void {\n track(TELEMETRY_EVENTS.learningSnapshotLoaded, props);\n}\n\nexport function trackLearningSkillToggled(props: {\n action: \"expanded\" | \"collapsed\";\n}): void {\n track(TELEMETRY_EVENTS.learningSkillToggled, props);\n}\n\nexport function trackLearningEvidenceOpened(): void {\n track(TELEMETRY_EVENTS.learningEvidenceOpened);\n}\n\nexport function trackLearningPageChanged(props: {\n section: \"skills\" | \"insights\";\n direction: \"previous\" | \"next\";\n}): void {\n track(TELEMETRY_EVENTS.learningPageChanged, props);\n}\n\nexport function trackLearningWebAppOpened(props: {\n category: \"learning\" | \"runs\" | \"candidates\";\n}): void {\n track(TELEMETRY_EVENTS.learningWebAppOpened, props);\n}\n\nexport type InspectorMetadataTelemetryModule = \"identity\" | \"plan\" | \"action\";\nexport type InspectorMetadataLicenseBucket =\n | \"valid\"\n | \"none\"\n | \"expired\"\n | \"unknown\";\nexport type InspectorMetadataActionKind =\n | \"manage_plan\"\n | \"renew\"\n | \"enable_intelligence\";\n\nexport type InspectorHomeTelemetryProps = Readonly<{\n action_kind?: InspectorMetadataActionKind;\n group_key?: InspectorGroupKey;\n leaf_key?: InspectorLeafKey;\n}>;\n\nexport function trackHomeViewed(props: InspectorHomeTelemetryProps = {}): void {\n track(TELEMETRY_EVENTS.homeViewed, {\n group_key: props.group_key ?? \"home\",\n leaf_key: props.leaf_key ?? \"home\",\n });\n}\n\nexport function trackHomeCtaClicked(props: InspectorHomeTelemetryProps): void {\n track(TELEMETRY_EVENTS.homeCtaClicked, {\n action_kind: props.action_kind,\n group_key: props.group_key ?? \"home\",\n leaf_key: props.leaf_key ?? \"home\",\n });\n}\n\n/**\n * Correlates a copied feature setup prompt with its onboarding run. The run ID\n * is generated locally for this click, not derived from account or runtime\n * data, so it can be safely joined with the onboarding flow downstream.\n */\nexport function trackHomeFeaturePromptClicked(props: {\n feature_id: string;\n onboarding_run_id: string;\n}): void {\n track(TELEMETRY_EVENTS.homeFeaturePromptClicked, {\n feature_id: props.feature_id,\n onboarding_run_id: props.onboarding_run_id,\n group_key: \"home\",\n leaf_key: \"home\",\n });\n}\n\nexport type InspectorHomePromptCopiedTelemetryProps = Readonly<{\n /** The id minted for this session and substituted into the copied prompt. */\n onboarding_run_id: string;\n /** Whether the clipboard write actually landed. */\n outcome: \"copied\" | \"failed\";\n}>;\n\n/**\n * Report a copy of the Intelligence install prompt.\n *\n * `outcome` is reported rather than only emitting on success, because a\n * clipboard that refuses is indistinguishable from a developer who never\n * pressed the button — and the two call for opposite fixes.\n */\nexport function trackHomePromptCopied(\n props: InspectorHomePromptCopiedTelemetryProps,\n): void {\n track(TELEMETRY_EVENTS.homePromptCopied, {\n onboarding_run_id: props.onboarding_run_id,\n outcome: props.outcome,\n group_key: \"home\",\n leaf_key: \"home\",\n });\n}\n\nexport type InspectorHomeStoryBeatTelemetryProps = Readonly<{\n /** Stable id of the step, e.g. \"threads\". Survives a label rename. */\n beat: string;\n /** Its position in the rail, so a reorder can be evaluated against clicks. */\n beat_index: number;\n}>;\n\n/**\n * Report a step of the Intelligence story that a developer opened themselves.\n *\n * Only a press reports. The story also advances on its own every few seconds,\n * and reporting that would bury the handful of real interactions under a\n * metronome — one event per idle developer per six seconds, none of it intent.\n */\nexport function trackHomeStoryBeatSelected(\n props: InspectorHomeStoryBeatTelemetryProps,\n): void {\n track(TELEMETRY_EVENTS.homeStoryBeatSelected, {\n beat: props.beat,\n beat_index: props.beat_index,\n group_key: \"home\",\n leaf_key: \"home\",\n });\n}\n\nexport type InspectorMetadataModuleViewedTelemetryProps = Readonly<{\n module: InspectorMetadataTelemetryModule;\n license_bucket: InspectorMetadataLicenseBucket;\n action_kind?: InspectorMetadataActionKind;\n usage_bucket: ThreadsUsageBucket;\n expiry_bucket: ThreadsExpiryBucket;\n group_key?: InspectorGroupKey;\n leaf_key?: InspectorLeafKey;\n action_placement?: MetadataActionPlacement;\n}>;\n\nexport type InspectorMetadataActionClickedTelemetryProps = Readonly<{\n action_kind: Exclude<InspectorMetadataActionKind, \"enable_intelligence\">;\n license_bucket: InspectorMetadataLicenseBucket;\n usage_bucket: ThreadsUsageBucket;\n expiry_bucket: ThreadsExpiryBucket;\n group_key?: InspectorGroupKey;\n leaf_key?: InspectorLeafKey;\n action_placement?: MetadataActionPlacement;\n}>;\n\nfunction metadataCoarseProperties(\n props:\n | InspectorMetadataModuleViewedTelemetryProps\n | InspectorMetadataActionClickedTelemetryProps,\n) {\n const navigation =\n props.group_key === undefined || props.leaf_key === undefined\n ? {}\n : { group_key: props.group_key, leaf_key: props.leaf_key };\n return {\n license_bucket: props.license_bucket,\n usage_bucket: props.usage_bucket,\n expiry_bucket: props.expiry_bucket,\n ...navigation,\n ...(props.action_placement === undefined\n ? {}\n : { action_placement: props.action_placement }),\n };\n}\n\n/**\n * Tracks one visible Inspector metadata module using coarse fields only.\n * Rebuilding the payload here prevents local labels, IDs, URLs, and usage\n * values from crossing the telemetry boundary through extra object fields.\n */\nexport function trackMetadataModuleViewed(\n props: InspectorMetadataModuleViewedTelemetryProps,\n): void {\n track(TELEMETRY_EVENTS.metadataModuleViewed, {\n module: props.module,\n ...(props.action_kind === undefined\n ? {}\n : { action_kind: props.action_kind }),\n ...metadataCoarseProperties(props),\n });\n}\n\n/**\n * Tracks clicks for managed plan and renewal actions. The existing\n * Intelligence-enable funnel keeps its original event and never calls this\n * helper, which prevents a single click from producing two wire events.\n */\nexport function trackMetadataActionClicked(\n props: InspectorMetadataActionClickedTelemetryProps,\n): void {\n track(TELEMETRY_EVENTS.metadataActionClicked, {\n module: \"action\",\n action_kind: props.action_kind,\n ...metadataCoarseProperties(props),\n });\n}\n\n/**\n * Returns the inspector's anonymous distinct-ID for cross-domain\n * propagation onto outbound announcement-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 * `whats_new_viewed → whats_new_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 announcement-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 let timeoutId: ReturnType<typeof setTimeout> | undefined;\n try {\n const controller = new AbortController();\n timeoutId = setTimeout(() => controller.abort(), FETCH_TIMEOUT_MS);\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 if (timeoutId !== undefined) clearTimeout(timeoutId);\n }\n}\n"],"mappings":"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"}
package/dist/package.cjs CHANGED
@@ -1,6 +1,6 @@
1
1
 
2
2
  //#region package.json
3
- var version = "1.73.3";
3
+ var version = "1.74.0";
4
4
 
5
5
  //#endregion
6
6
  Object.defineProperty(exports, 'version', {
package/dist/package.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  //#region package.json
2
- var version = "1.73.3";
2
+ var version = "1.74.0";
3
3
 
4
4
  //#endregion
5
5
  export { version };