feat(telemetry): track if session is running in a Git worktree (#23265)

This commit is contained in:
Jerop Kipruto
2026-03-20 15:01:12 -04:00
committed by GitHub
parent 62cb14fa52
commit b459e1a108
6 changed files with 80 additions and 41 deletions
+1
View File
@@ -306,6 +306,7 @@ Emitted at startup with the CLI configuration.
- `extension_ids` (string) - `extension_ids` (string)
- `extensions_count` (int) - `extensions_count` (int)
- `auth_type` (string) - `auth_type` (string)
- `worktree_active` (boolean)
- `github_workflow_name` (string, optional) - `github_workflow_name` (string, optional)
- `github_repository_hash` (string, optional) - `github_repository_hash` (string, optional)
- `github_event_name` (string, optional) - `github_event_name` (string, optional)
@@ -44,6 +44,7 @@ export const createMockConfig = (overrides: Partial<Config> = {}): Config =>
getDeleteSession: vi.fn(() => undefined), getDeleteSession: vi.fn(() => undefined),
setSessionId: vi.fn(), setSessionId: vi.fn(),
getSessionId: vi.fn().mockReturnValue('mock-session-id'), getSessionId: vi.fn().mockReturnValue('mock-session-id'),
getWorktreeSettings: vi.fn(() => undefined),
getContentGeneratorConfig: vi.fn(() => ({ authType: 'google' })), getContentGeneratorConfig: vi.fn(() => ({ authType: 'google' })),
getAcpMode: vi.fn(() => false), getAcpMode: vi.fn(() => false),
isBrowserLaunchSuppressed: vi.fn(() => false), isBrowserLaunchSuppressed: vi.fn(() => false),
@@ -687,6 +687,11 @@ export class ClearcutLogger {
gemini_cli_key: EventMetadataKey.GEMINI_CLI_START_SESSION_EXTENSION_IDS, gemini_cli_key: EventMetadataKey.GEMINI_CLI_START_SESSION_EXTENSION_IDS,
value: event.extension_ids.toString(), value: event.extension_ids.toString(),
}, },
{
gemini_cli_key:
EventMetadataKey.GEMINI_CLI_START_SESSION_WORKTREE_ACTIVE,
value: event.worktree_active.toString(),
},
]; ];
// Add hardware information only to the start session event // Add hardware information only to the start session event
@@ -452,6 +452,9 @@ export enum EventMetadataKey {
// Logs the name of extensions as a comma-separated string // Logs the name of extensions as a comma-separated string
GEMINI_CLI_START_SESSION_EXTENSION_IDS = 120, GEMINI_CLI_START_SESSION_EXTENSION_IDS = 120,
// Logs whether the session is running in a Git worktree.
GEMINI_CLI_START_SESSION_WORKTREE_ACTIVE = 191,
// Logs the setting scope for an extension enablement. // Logs the setting scope for an extension enablement.
GEMINI_CLI_EXTENSION_ENABLE_SETTING_SCOPE = 102, GEMINI_CLI_EXTENSION_ENABLE_SETTING_SCOPE = 102,
+67 -41
View File
@@ -195,48 +195,51 @@ describe('loggers', () => {
}); });
describe('logCliConfiguration', () => { describe('logCliConfiguration', () => {
const baseMockConfig = {
getSessionId: () => 'test-session-id',
getModel: () => 'test-model',
getEmbeddingModel: () => 'test-embedding-model',
getSandbox: () => true,
getCoreTools: () => ['ls', 'read-file'],
getApprovalMode: () => 'default',
getContentGeneratorConfig: () => ({
model: 'test-model',
apiKey: 'test-api-key',
authType: AuthType.USE_VERTEX_AI,
}),
getTelemetryEnabled: () => true,
getUsageStatisticsEnabled: () => true,
getTelemetryLogPromptsEnabled: () => true,
getFileFilteringRespectGitIgnore: () => true,
getFileFilteringAllowBuildArtifacts: () => false,
getDebugMode: () => true,
getMcpServers: () => {
throw new Error('Should not call');
},
getQuestion: () => 'test-question',
getTargetDir: () => 'target-dir',
getProxy: () => 'http://test.proxy.com:8080',
getOutputFormat: () => OutputFormat.JSON,
getExtensions: () =>
[
{ name: 'ext-one', id: 'id-one' },
{ name: 'ext-two', id: 'id-two' },
] as GeminiCLIExtension[],
getMcpClientManager: () => ({
getMcpServers: () => ({
'test-server': {
command: 'test-command',
},
}),
}),
isInteractive: () => false,
getExperiments: () => undefined,
getExperimentsAsync: async () => undefined,
getWorktreeSettings: () => undefined,
} as unknown as Config;
it('should log the cli configuration', async () => { it('should log the cli configuration', async () => {
const mockConfig = { const mockConfig = baseMockConfig;
getSessionId: () => 'test-session-id',
getModel: () => 'test-model',
getEmbeddingModel: () => 'test-embedding-model',
getSandbox: () => true,
getCoreTools: () => ['ls', 'read-file'],
getApprovalMode: () => 'default',
getContentGeneratorConfig: () => ({
model: 'test-model',
apiKey: 'test-api-key',
authType: AuthType.USE_VERTEX_AI,
}),
getTelemetryEnabled: () => true,
getUsageStatisticsEnabled: () => true,
getTelemetryLogPromptsEnabled: () => true,
getFileFilteringRespectGitIgnore: () => true,
getFileFilteringAllowBuildArtifacts: () => false,
getDebugMode: () => true,
getMcpServers: () => {
throw new Error('Should not call');
},
getQuestion: () => 'test-question',
getTargetDir: () => 'target-dir',
getProxy: () => 'http://test.proxy.com:8080',
getOutputFormat: () => OutputFormat.JSON,
getExtensions: () =>
[
{ name: 'ext-one', id: 'id-one' },
{ name: 'ext-two', id: 'id-two' },
] as GeminiCLIExtension[],
getMcpClientManager: () => ({
getMcpServers: () => ({
'test-server': {
command: 'test-command',
},
}),
}),
isInteractive: () => false,
getExperiments: () => undefined,
getExperimentsAsync: async () => undefined,
} as unknown as Config;
const startSessionEvent = new StartSessionEvent(mockConfig); const startSessionEvent = new StartSessionEvent(mockConfig);
logCliConfiguration(mockConfig, startSessionEvent); logCliConfiguration(mockConfig, startSessionEvent);
@@ -270,9 +273,32 @@ describe('loggers', () => {
extensions_count: 2, extensions_count: 2,
extensions: 'ext-one,ext-two', extensions: 'ext-one,ext-two',
auth_type: 'vertex-ai', auth_type: 'vertex-ai',
worktree_active: false,
}, },
}); });
}); });
it('should set worktree_active to true when worktree settings are present', async () => {
const mockConfig = {
...baseMockConfig,
getWorktreeSettings: () => ({
name: 'test-worktree',
path: '/path/to/worktree',
baseSha: 'test-sha',
}),
} as unknown as Config;
const startSessionEvent = new StartSessionEvent(mockConfig);
logCliConfiguration(mockConfig, startSessionEvent);
await new Promise(process.nextTick);
expect(mockLogger.emit).toHaveBeenCalledWith({
body: 'CLI configuration loaded.',
attributes: expect.objectContaining({
worktree_active: true,
}),
});
});
}); });
describe('logUserPrompt', () => { describe('logUserPrompt', () => {
+3
View File
@@ -77,6 +77,7 @@ export class StartSessionEvent implements BaseTelemetryEvent {
extensions: string; extensions: string;
extension_ids: string; extension_ids: string;
auth_type?: string; auth_type?: string;
worktree_active: boolean;
constructor(config: Config, toolRegistry?: ToolRegistry) { constructor(config: Config, toolRegistry?: ToolRegistry) {
const generatorConfig = config.getContentGeneratorConfig(); const generatorConfig = config.getContentGeneratorConfig();
@@ -114,6 +115,7 @@ export class StartSessionEvent implements BaseTelemetryEvent {
this.extensions = extensions.map((e) => e.name).join(','); this.extensions = extensions.map((e) => e.name).join(',');
this.extension_ids = extensions.map((e) => e.id).join(','); this.extension_ids = extensions.map((e) => e.id).join(',');
this.auth_type = generatorConfig?.authType; this.auth_type = generatorConfig?.authType;
this.worktree_active = !!config.getWorktreeSettings();
if (toolRegistry) { if (toolRegistry) {
const mcpTools = toolRegistry const mcpTools = toolRegistry
.getAllTools() .getAllTools()
@@ -147,6 +149,7 @@ export class StartSessionEvent implements BaseTelemetryEvent {
extensions_count: this.extensions_count, extensions_count: this.extensions_count,
extension_ids: this.extension_ids, extension_ids: this.extension_ids,
auth_type: this.auth_type, auth_type: this.auth_type,
worktree_active: this.worktree_active,
}; };
} }