Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions src/core/Cline.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3798,6 +3798,8 @@ export class Cline {
return
}

telemetryService.captureCheckpointDiffed(this.taskId)

if (!previousCommitHash && mode === "checkpoint") {
const previousCheckpoint = this.clineMessages
.filter(({ say }) => say === "checkpoint_saved")
Expand Down Expand Up @@ -3849,6 +3851,8 @@ export class Cline {
return
}

telemetryService.captureCheckpointCreated(this.taskId)

// Start the checkpoint process in the background.
service.saveCheckpoint(`Task: ${this.taskId}, Time: ${Date.now()}`).catch((err) => {
console.error("[Cline#checkpointSave] caught unexpected error, disabling checkpoints", err)
Expand Down Expand Up @@ -3880,6 +3884,8 @@ export class Cline {
try {
await service.restoreCheckpoint(commitHash)

telemetryService.captureCheckpointRestored(this.taskId)

await this.providerRef.deref()?.postMessageToWebview({ type: "currentCheckpointUpdated", text: commitHash })

if (mode === "restore") {
Expand Down
9 changes: 9 additions & 0 deletions src/core/webview/ClineProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2567,6 +2567,15 @@ export class ClineProvider implements vscode.WebviewViewProvider {
properties.apiProvider = apiConfiguration.apiProvider
}

// Add model ID if available
const currentCline = this.getCurrentCline()
if (currentCline?.api) {
const { id: modelId } = currentCline.api.getModel()
if (modelId) {
properties.modelId = modelId
}
}

return properties
}
}
59 changes: 59 additions & 0 deletions src/core/webview/__tests__/ClineProvider.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1652,3 +1652,62 @@ describe("ContextProxy integration", () => {
expect(mockContextProxy.setValues).toBeDefined()
})
})

describe("getTelemetryProperties", () => {
let provider: ClineProvider
let mockContext: vscode.ExtensionContext
let mockOutputChannel: vscode.OutputChannel
let mockCline: any

beforeEach(() => {
// Reset mocks
jest.clearAllMocks()

// Setup basic mocks
mockContext = {
globalState: {
get: jest.fn().mockImplementation((key: string) => {
if (key === "mode") return "code"
if (key === "apiProvider") return "anthropic"
return undefined
}),
update: jest.fn(),
keys: jest.fn().mockReturnValue([]),
},
secrets: { get: jest.fn(), store: jest.fn(), delete: jest.fn() },
extensionUri: {} as vscode.Uri,
globalStorageUri: { fsPath: "/test/path" },
extension: { packageJSON: { version: "1.0.0" } },
} as unknown as vscode.ExtensionContext

mockOutputChannel = { appendLine: jest.fn() } as unknown as vscode.OutputChannel
provider = new ClineProvider(mockContext, mockOutputChannel)

// Setup Cline instance with mocked getModel method
const { Cline } = require("../../Cline")
mockCline = new Cline()
mockCline.api = {
getModel: jest.fn().mockReturnValue({
id: "claude-3-7-sonnet-20250219",
info: { contextWindow: 200000 },
}),
}
})

test("includes basic properties in telemetry", async () => {
const properties = await provider.getTelemetryProperties()

expect(properties).toHaveProperty("vscodeVersion")
expect(properties).toHaveProperty("platform")
expect(properties).toHaveProperty("appVersion", "1.0.0")
})

test("includes model ID from current Cline instance if available", async () => {
// Add mock Cline to stack
await provider.addClineToStack(mockCline)

const properties = await provider.getTelemetryProperties()

expect(properties).toHaveProperty("modelId", "claude-3-7-sonnet-20250219")
})
})
15 changes: 15 additions & 0 deletions src/services/telemetry/TelemetryService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@ class PostHogClient {
CONVERSATION_MESSAGE: "Conversation Message",
MODE_SWITCH: "Mode Switched",
TOOL_USED: "Tool Used",
CHECKPOINT_CREATED: "Checkpoint Created",
CHECKPOINT_RESTORED: "Checkpoint Restored",
CHECKPOINT_DIFFED: "Checkpoint Diffed",
},
}

Expand Down Expand Up @@ -246,6 +249,18 @@ class TelemetryService {
})
}

public captureCheckpointCreated(taskId: string): void {
this.captureEvent(PostHogClient.EVENTS.TASK.CHECKPOINT_CREATED, { taskId })
}

public captureCheckpointDiffed(taskId: string): void {
this.captureEvent(PostHogClient.EVENTS.TASK.CHECKPOINT_DIFFED, { taskId })
}

public captureCheckpointRestored(taskId: string): void {
this.captureEvent(PostHogClient.EVENTS.TASK.CHECKPOINT_RESTORED, { taskId })
}

/**
* Checks if telemetry is currently enabled
* @returns Whether telemetry is enabled
Expand Down