Skip to content
This repository was archived by the owner on Aug 6, 2026. It is now read-only.
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
Original file line numberDiff line numberDiff line change
Expand Up@@ -137,7 +137,10 @@ vi.mock("@features/sidebar/hooks/useTaskViewed", () => ({
},
}));

vi.mock("@utils/analytics", () => ({ track: vi.fn() }));
vi.mock("@utils/analytics", () => ({
track: vi.fn(),
buildPermissionToolMetadata: vi.fn(() => ({})),
}));
vi.mock("@utils/logger", () => ({
logger: {
scope: () => ({
Expand Down
25 changes: 24 additions & 1 deletion apps/code/src/renderer/features/sessions/service/service.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -40,7 +40,7 @@ import {
import { ANALYTICS_EVENTS } from "@shared/types/analytics";
import type { AcpMessage, StoredLogEntry } from "@shared/types/session-events";
import { isJsonRpcRequest } from "@shared/types/session-events";
import { track } from "@utils/analytics";
import { buildPermissionToolMetadata, track } from "@utils/analytics";
import { logger } from "@utils/logger";
import {
notifyPermissionRequest,
Expand DownExpand Up@@ -571,6 +571,8 @@ export class SessionService {
track(ANALYTICS_EVENTS.TASK_RUN_STARTED, {
task_id: taskId,
execution_type: "local",
initial_mode: executionMode,
adapter,
});

const preferredModel = model ?? DEFAULT_GATEWAY_MODEL;
Expand DownExpand Up@@ -1493,6 +1495,12 @@ export class SessionService {
return;
}

const permission = session.pendingPermissions.get(toolCallId);
track(ANALYTICS_EVENTS.PERMISSION_RESPONDED, {
task_id: taskId,
...buildPermissionToolMetadata(permission, optionId, customInput),
});

this.resolvePermission(session, toolCallId);

try {
Expand DownExpand Up@@ -1530,6 +1538,12 @@ export class SessionService {
return;
}

const permission = session.pendingPermissions.get(toolCallId);
track(ANALYTICS_EVENTS.PERMISSION_CANCELLED, {
task_id: taskId,
...buildPermissionToolMetadata(permission),
});

this.resolvePermission(session, toolCallId);

try {
Expand DownExpand Up@@ -1636,6 +1650,15 @@ export class SessionService {
return;
}

if (configOption.currentValue !== value) {
track(ANALYTICS_EVENTS.SESSION_CONFIG_CHANGED, {
task_id: taskId,
category,
from_value: configOption.currentValue,
to_value: value,
});
}

await this.setSessionConfigOption(taskId, configOption.id, value);
}

Expand Down
24 changes: 24 additions & 0 deletions apps/code/src/renderer/utils/analytics.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -3,6 +3,7 @@ import posthog from "posthog-js/dist/module.full.no-external";
// The module.full.no-external bundle includes rrweb but not the initSessionRecording function
// posthog-recorder (vs lazy-recorder) ensures recording is ready immediately
import "posthog-js/dist/posthog-recorder";
import type { PermissionRequest } from "@renderer/features/sessions/utils/parseSessionLogs";
import type {
EventPropertyMap,
UserIdentifyProperties,
Expand DownExpand Up@@ -128,6 +129,29 @@ export function track<K extends keyof EventPropertyMap>(
posthog.capture(eventName, args[0]);
}

/**
* Build tool metadata for analytics on permission requests
*/
export function buildPermissionToolMetadata(
permission?: PermissionRequest,
selectedOptionId?: string,
customInput?: string,
): Record<string, any> {
const selectedOption = permission?.options?.find(
(o) => o.optionId === selectedOptionId,
);
const rawInput = permission?.toolCall?.rawInput as
| Record<string, unknown>
| undefined;

return {
tool_name: rawInput?.toolName,
option_id: selectedOptionId,
option_kind: selectedOption?.kind ?? "unknown",
custom_input: customInput,
};
}

/**
* Capture an exception for error tracking using PostHog's built-in exception tracking.
*/
Expand Down
38 changes: 38 additions & 0 deletions apps/code/src/shared/types/analytics.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -64,6 +64,8 @@ export interface TaskRunStartedProperties {
task_id: string;
execution_type: ExecutionType;
model?: string;
initial_mode?: string;
adapter?: string;
}

export interface TaskRunCompletedProperties {
Expand DownExpand Up@@ -151,6 +153,28 @@ export interface AgentSessionErrorProperties {
error_type: string;
}

// Permission events
export interface PermissionRespondedProperties {
task_id: string;
tool_name?: string;
option_id?: string;
option_kind?: string;
custom_input?: string;
}

export interface PermissionCancelledProperties {
task_id: string;
tool_name?: string;
}

// Session config events
export interface SessionConfigChangedProperties {
task_id: string;
category: string;
from_value: string;
to_value: string;
}

// Feedback events
export interface TaskFeedbackProperties {
task_id: string;
Expand DownExpand Up@@ -203,6 +227,13 @@ export const ANALYTICS_EVENTS = {
COMMAND_MENU_ACTION: "Command menu action",
COMMAND_CENTER_VIEWED: "Command center viewed",

// Permission events
PERMISSION_RESPONDED: "Permission responded",
PERMISSION_CANCELLED: "Permission cancelled",

// Session config events
SESSION_CONFIG_CHANGED: "Session config changed",

// Settings events
SETTING_CHANGED: "Setting changed",

Expand DownExpand Up@@ -249,6 +280,13 @@ export type EventPropertyMap = {
[ANALYTICS_EVENTS.COMMAND_MENU_ACTION]: CommandMenuActionProperties;
[ANALYTICS_EVENTS.COMMAND_CENTER_VIEWED]: never;

// Permission events
[ANALYTICS_EVENTS.PERMISSION_RESPONDED]: PermissionRespondedProperties;
[ANALYTICS_EVENTS.PERMISSION_CANCELLED]: PermissionCancelledProperties;

// Session config events
[ANALYTICS_EVENTS.SESSION_CONFIG_CHANGED]: SessionConfigChangedProperties;

// Settings events
[ANALYTICS_EVENTS.SETTING_CHANGED]: SettingChangedProperties;

Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -356,7 +356,7 @@ async function handleDefaultPermissionFlow(
kind: toolInfo.kind,
content: toolInfo.content,
locations: toolInfo.locations,
rawInput: toolInput as Record<string, unknown>,
rawInput: { ...(toolInput as Record<string, unknown>), toolName },
},
});

Expand Down
Loading