telemetry: fire ui.panel_opened from initializeWebview on every host (#14101)

* telemetry: fire ui.panel_opened from initializeWebview on every host

ui.panel_opened was only captured from VscodeWebviewProvider, so the
JetBrains plugin never emitted it: the activation funnel there jumps from
user.extension_activated (core spawned on project open) straight to task
events, with no signal that the Cline UI was ever rendered.

The webview calls the initializeWebview RPC once on mount on every host,
so capture the same event there with source "webview_initialized".
Existing dashboards keyed on ui.panel_opened pick up JetBrains for free;
VS Code gains one extra source value on an event it already emits.

* telemetry: type the ui.panel_opened source

A PanelOpenedSource union on capturePanelOpened keeps the documented
source list and the call sites from drifting apart. Also note that on
JetBrains webview_initialized fires on every webview reload, so a
crash-restart loop emits one per restart.
This commit is contained in:
Mikołaj Kondratek
2026-09-16 09:25:15 +02:00
committed by GitHub
parent fc68af9660
commit 186ac22e29
2 changed files with 16 additions and 2 deletions
@@ -20,6 +20,11 @@ import { refreshOpenRouterModels } from "../models/refreshOpenRouterModels"
*/
export async function initializeWebview(controller: Controller, _request: EmptyRequest): Promise<Empty> {
try {
// The webview calls this once on mount, on every host. The VS Code-only
// "sidebar_resolved"/"sidebar_visible" sources never fire for JetBrains, so
// this is the only host-neutral signal that the Cline UI actually rendered.
telemetryService.capturePanelOpened("webview_initialized")
// Sync workflow toggles with the files on disk so the chat input's slash
// command menu knows about workflows without requiring the user to open
// the Workflows modal first (which is the only other place that refreshes
@@ -92,6 +92,13 @@ export enum TerminalHangStage {
BUFFER_STUCK = "buffer_stuck",
}
/**
* Which hook fired `ui.panel_opened`. "sidebar_resolved" / "sidebar_visible" come from
* VscodeWebviewProvider and are VS Code-only; "webview_initialized" comes from the webview's
* mount-time initializeWebview RPC and fires on every host.
*/
export type PanelOpenedSource = "sidebar_resolved" | "sidebar_visible" | "webview_initialized"
export type TelemetryMetadata = {
/**
* The extension or cline-core version. JetBrains and CLI have different
@@ -328,7 +335,9 @@ export class TelemetryService {
MODEL_FAVORITE_TOGGLED: "ui.model_favorite_toggled",
// Tracks when a button is clicked
BUTTON_CLICKED: "ui.button_clicked",
// Tracks when the Cline panel becomes visible
// Tracks when the Cline panel becomes visible; `source` (PanelOpenedSource) says
// which hook fired. On JetBrains "webview_initialized" also fires on every webview
// reload, e.g. after a core restart, so a crash-restart loop emits one per restart.
PANEL_OPENED: "ui.panel_opened",
// Tracks when the user explicitly starts a new task flow
NEW_TASK_CLICKED: "ui.new_task_clicked",
@@ -944,7 +953,7 @@ export class TelemetryService {
})
}
public capturePanelOpened(source?: string) {
public capturePanelOpened(source: PanelOpenedSource) {
this.capture({
event: TelemetryService.EVENTS.UI.PANEL_OPENED,
properties: { source },