diff --git a/packages/cli/src/ui/components/ModelDialog.test.tsx b/packages/cli/src/ui/components/ModelDialog.test.tsx index 5c8338c604..447f84302e 100644 --- a/packages/cli/src/ui/components/ModelDialog.test.tsx +++ b/packages/cli/src/ui/components/ModelDialog.test.tsx @@ -52,6 +52,7 @@ const renderComponent = ( getUseSmartEdit: vi.fn(() => false), getUseModelRouter: vi.fn(() => false), getProxy: vi.fn(() => undefined), + isInteractive: vi.fn(() => false), // --- Spread test-specific overrides --- ...contextValue, diff --git a/packages/cli/src/ui/hooks/useGeminiStream.test.tsx b/packages/cli/src/ui/hooks/useGeminiStream.test.tsx index dd4d4393da..679427eea1 100644 --- a/packages/cli/src/ui/hooks/useGeminiStream.test.tsx +++ b/packages/cli/src/ui/hooks/useGeminiStream.test.tsx @@ -219,6 +219,7 @@ describe('useGeminiStream', () => { .mockReturnValue(contentGeneratorConfig), getUseSmartEdit: () => false, getUseModelRouter: () => false, + isInteractive: () => false, } as unknown as Config; mockOnDebugMessage = vi.fn(); mockHandleSlashCommand = vi.fn().mockResolvedValue(false); diff --git a/packages/cli/src/ui/hooks/useToolScheduler.test.ts b/packages/cli/src/ui/hooks/useToolScheduler.test.ts index 30eeab06e9..469e275046 100644 --- a/packages/cli/src/ui/hooks/useToolScheduler.test.ts +++ b/packages/cli/src/ui/hooks/useToolScheduler.test.ts @@ -80,6 +80,7 @@ const mockConfig = { getEnableMessageBusIntegration: () => false, getMessageBus: () => null, getPolicyEngine: () => null, + isInteractive: () => false, } as unknown as Config; const mockTool = new MockTool({ diff --git a/packages/core/src/core/baseLlmClient.test.ts b/packages/core/src/core/baseLlmClient.test.ts index 265d000348..d924fabacb 100644 --- a/packages/core/src/core/baseLlmClient.test.ts +++ b/packages/core/src/core/baseLlmClient.test.ts @@ -69,23 +69,6 @@ const mockContentGenerator = { embedContent: mockEmbedContent, } as unknown as Mocked; -const mockConfig = { - getSessionId: vi.fn().mockReturnValue('test-session-id'), - getContentGeneratorConfig: vi - .fn() - .mockReturnValue({ authType: AuthType.USE_GEMINI }), - getEmbeddingModel: vi.fn().mockReturnValue('test-embedding-model'), - modelConfigService: { - getResolvedConfig: vi.fn().mockImplementation(({ model }) => ({ - model, - generateContentConfig: { - temperature: 0, - topP: 1, - }, - })), - }, -} as unknown as Mocked; - // Helper to create a mock GenerateContentResponse const createMockResponse = (text: string): GenerateContentResponse => ({ @@ -96,6 +79,7 @@ describe('BaseLlmClient', () => { let client: BaseLlmClient; let abortController: AbortController; let defaultOptions: GenerateJsonOptions; + let mockConfig: Mocked; beforeEach(() => { vi.clearAllMocks(); @@ -103,6 +87,25 @@ describe('BaseLlmClient', () => { vi.mocked(getErrorMessage).mockImplementation((e) => e instanceof Error ? e.message : String(e), ); + + mockConfig = { + getSessionId: vi.fn().mockReturnValue('test-session-id'), + getContentGeneratorConfig: vi + .fn() + .mockReturnValue({ authType: AuthType.USE_GEMINI }), + getEmbeddingModel: vi.fn().mockReturnValue('test-embedding-model'), + isInteractive: vi.fn().mockReturnValue(false), + modelConfigService: { + getResolvedConfig: vi.fn().mockImplementation(({ model }) => ({ + model, + generateContentConfig: { + temperature: 0, + topP: 1, + }, + })), + }, + } as unknown as Mocked; + client = new BaseLlmClient(mockContentGenerator, mockConfig); abortController = new AbortController(); defaultOptions = { diff --git a/packages/core/src/core/client.test.ts b/packages/core/src/core/client.test.ts index e716157ff6..cdee60a3b5 100644 --- a/packages/core/src/core/client.test.ts +++ b/packages/core/src/core/client.test.ts @@ -42,6 +42,7 @@ import { ideContextStore } from '../ide/ideContext.js'; import type { ModelRouterService } from '../routing/modelRouterService.js'; import { uiTelemetryService } from '../telemetry/uiTelemetry.js'; import { ChatCompressionService } from '../services/chatCompressionService.js'; +import { ClearcutLogger } from '../telemetry/clearcut-logger/clearcut-logger.js'; vi.mock('../services/chatCompressionService.js'); @@ -174,6 +175,7 @@ describe('Gemini Client (client.ts)', () => { let mockGenerateContentFn: Mock; beforeEach(async () => { vi.resetAllMocks(); + ClearcutLogger.clearInstance(); vi.mocked(uiTelemetryService.setLastPromptTokenCount).mockClear(); vi.mocked(ChatCompressionService.prototype.compress).mockResolvedValue({ @@ -260,6 +262,7 @@ describe('Gemini Client (client.ts)', () => { reasoning: 'test', }), }), + isInteractive: vi.fn().mockReturnValue(false), } as unknown as Config; client = new GeminiClient(mockConfig); diff --git a/packages/core/src/core/coreToolScheduler.test.ts b/packages/core/src/core/coreToolScheduler.test.ts index 073ff712d5..bf75e44335 100644 --- a/packages/core/src/core/coreToolScheduler.test.ts +++ b/packages/core/src/core/coreToolScheduler.test.ts @@ -284,6 +284,7 @@ describe('CoreToolScheduler', () => { const mockConfig = createMockConfig({ getToolRegistry: () => mockToolRegistry, + isInteractive: () => false, }); const scheduler = new CoreToolScheduler({ @@ -349,6 +350,7 @@ describe('CoreToolScheduler', () => { const mockConfig = createMockConfig({ getToolRegistry: () => mockToolRegistry, + isInteractive: () => false, }); const scheduler = new CoreToolScheduler({ @@ -450,6 +452,7 @@ describe('CoreToolScheduler', () => { const mockConfig = createMockConfig({ getToolRegistry: () => mockToolRegistry, + isInteractive: () => false, }); const scheduler = new CoreToolScheduler({ @@ -546,6 +549,7 @@ describe('CoreToolScheduler', () => { const mockConfig = createMockConfig({ getToolRegistry: () => mockToolRegistry, + isInteractive: () => false, }); const scheduler = new CoreToolScheduler({ @@ -584,6 +588,7 @@ describe('CoreToolScheduler', () => { } as unknown as ToolRegistry; const mockConfig = createMockConfig({ getToolRegistry: () => mockToolRegistry, + isInteractive: () => false, }); // Create scheduler @@ -637,6 +642,7 @@ describe('CoreToolScheduler with payload', () => { const mockConfig = createMockConfig({ getToolRegistry: () => mockToolRegistry, + isInteractive: () => false, }); const scheduler = new CoreToolScheduler({ @@ -938,6 +944,7 @@ describe('CoreToolScheduler edit cancellation', () => { const mockConfig = createMockConfig({ getToolRegistry: () => mockToolRegistry, + isInteractive: () => false, }); const scheduler = new CoreToolScheduler({ @@ -1023,6 +1030,7 @@ describe('CoreToolScheduler YOLO mode', () => { const mockConfig = createMockConfig({ getToolRegistry: () => mockToolRegistry, getApprovalMode: () => ApprovalMode.YOLO, + isInteractive: () => false, }); const scheduler = new CoreToolScheduler({ @@ -1109,6 +1117,7 @@ describe('CoreToolScheduler request queueing', () => { const mockConfig = createMockConfig({ getToolRegistry: () => mockToolRegistry, getApprovalMode: () => ApprovalMode.YOLO, // Use YOLO to avoid confirmation prompts + isInteractive: () => false, }); const scheduler = new CoreToolScheduler({ @@ -1222,6 +1231,7 @@ describe('CoreToolScheduler request queueing', () => { terminalWidth: 80, terminalHeight: 24, }), + isInteractive: () => false, }); const scheduler = new CoreToolScheduler({ @@ -1329,6 +1339,7 @@ describe('CoreToolScheduler request queueing', () => { terminalHeight: 24, }), getToolRegistry: () => toolRegistry, + isInteractive: () => false, }); const scheduler = new CoreToolScheduler({ @@ -1385,6 +1396,7 @@ describe('CoreToolScheduler request queueing', () => { const mockConfig = createMockConfig({ getToolRegistry: () => mockToolRegistry, getApprovalMode: () => ApprovalMode.YOLO, + isInteractive: () => false, }); const scheduler = new CoreToolScheduler({ @@ -1440,6 +1452,7 @@ describe('CoreToolScheduler request queueing', () => { setApprovalMode: (mode: ApprovalMode) => { approvalMode = mode; }, + isInteractive: () => false, }); const testTool = new TestApprovalTool(mockConfig); @@ -1611,6 +1624,7 @@ describe('CoreToolScheduler Sequential Execution', () => { const mockConfig = createMockConfig({ getToolRegistry: () => mockToolRegistry, getApprovalMode: () => ApprovalMode.YOLO, // Use YOLO to avoid confirmation prompts + isInteractive: () => false, }); const scheduler = new CoreToolScheduler({ @@ -1710,6 +1724,7 @@ describe('CoreToolScheduler Sequential Execution', () => { const mockConfig = createMockConfig({ getToolRegistry: () => mockToolRegistry, getApprovalMode: () => ApprovalMode.YOLO, + isInteractive: () => false, }); const scheduler = new CoreToolScheduler({ diff --git a/packages/core/src/core/geminiChat.test.ts b/packages/core/src/core/geminiChat.test.ts index f5facae5dc..8f5d313533 100644 --- a/packages/core/src/core/geminiChat.test.ts +++ b/packages/core/src/core/geminiChat.test.ts @@ -128,6 +128,7 @@ describe('GeminiChat', () => { }), getContentGenerator: vi.fn().mockReturnValue(mockContentGenerator), getRetryFetchErrors: vi.fn().mockReturnValue(false), + isInteractive: vi.fn().mockReturnValue(false), } as unknown as Config; // Disable 429 simulation for tests diff --git a/packages/core/src/core/nonInteractiveToolExecutor.test.ts b/packages/core/src/core/nonInteractiveToolExecutor.test.ts index 0af6648485..5ee2b83f49 100644 --- a/packages/core/src/core/nonInteractiveToolExecutor.test.ts +++ b/packages/core/src/core/nonInteractiveToolExecutor.test.ts @@ -65,6 +65,7 @@ describe('executeToolCall', () => { getEnableMessageBusIntegration: () => false, getMessageBus: () => null, getPolicyEngine: () => null, + isInteractive: () => false, } as unknown as Config; abortController = new AbortController(); diff --git a/packages/core/src/fallback/handler.test.ts b/packages/core/src/fallback/handler.test.ts index eb97a7f5e0..133ac7f3f2 100644 --- a/packages/core/src/fallback/handler.test.ts +++ b/packages/core/src/fallback/handler.test.ts @@ -40,6 +40,7 @@ const createMockConfig = (overrides: Partial = {}): Config => isInFallbackMode: vi.fn(() => false), setFallbackMode: vi.fn(), fallbackHandler: undefined, + isInteractive: vi.fn(() => false), ...overrides, }) as unknown as Config; diff --git a/packages/core/src/services/chatCompressionService.test.ts b/packages/core/src/services/chatCompressionService.test.ts index a0766f9ffc..f4472f2301 100644 --- a/packages/core/src/services/chatCompressionService.test.ts +++ b/packages/core/src/services/chatCompressionService.test.ts @@ -117,6 +117,7 @@ describe('ChatCompressionService', () => { mockConfig = { getCompressionThreshold: vi.fn(), getContentGenerator: vi.fn(), + isInteractive: vi.fn().mockReturnValue(false), } as unknown as Config; vi.mocked(tokenLimit).mockReturnValue(1000); diff --git a/packages/core/src/services/loopDetectionService.test.ts b/packages/core/src/services/loopDetectionService.test.ts index 8fd5b0dde3..8195913ff7 100644 --- a/packages/core/src/services/loopDetectionService.test.ts +++ b/packages/core/src/services/loopDetectionService.test.ts @@ -35,6 +35,7 @@ describe('LoopDetectionService', () => { beforeEach(() => { mockConfig = { getTelemetryEnabled: () => true, + isInteractive: () => false, } as unknown as Config; service = new LoopDetectionService(mockConfig); vi.clearAllMocks(); @@ -741,6 +742,7 @@ describe('LoopDetectionService LLM Checks', () => { generateContentConfig: {}, }), }, + isInteractive: () => false, } as unknown as Config; service = new LoopDetectionService(mockConfig); diff --git a/packages/core/src/telemetry/clearcut-logger/clearcut-logger.test.ts b/packages/core/src/telemetry/clearcut-logger/clearcut-logger.test.ts index 65a1dc15da..e754717b30 100644 --- a/packages/core/src/telemetry/clearcut-logger/clearcut-logger.test.ts +++ b/packages/core/src/telemetry/clearcut-logger/clearcut-logger.test.ts @@ -887,8 +887,7 @@ describe('ClearcutLogger', () => { status: 'success', } as SuccessfulToolCall; - // eslint-disable-next-line @typescript-eslint/no-explicit-any - logger?.logToolCallEvent(new ToolCallEvent(completedToolCall as any)); + logger?.logToolCallEvent(new ToolCallEvent(completedToolCall)); const events = getEvents(logger!); expect(events.length).toBe(1); @@ -933,8 +932,7 @@ describe('ClearcutLogger', () => { status: 'success', } as SuccessfulToolCall; - // eslint-disable-next-line @typescript-eslint/no-explicit-any - logger?.logToolCallEvent(new ToolCallEvent(completedToolCall as any)); + logger?.logToolCallEvent(new ToolCallEvent(completedToolCall)); const events = getEvents(logger!); expect(events.length).toBe(1); diff --git a/packages/core/src/telemetry/clearcut-logger/clearcut-logger.ts b/packages/core/src/telemetry/clearcut-logger/clearcut-logger.ts index e31bd40127..bc868b657f 100644 --- a/packages/core/src/telemetry/clearcut-logger/clearcut-logger.ts +++ b/packages/core/src/telemetry/clearcut-logger/clearcut-logger.ts @@ -1301,6 +1301,10 @@ export class ClearcutLogger { gemini_cli_key: EventMetadataKey.GEMINI_CLI_USER_SETTINGS, value: this.getConfigJson(), }, + { + gemini_cli_key: EventMetadataKey.GEMINI_CLI_INTERACTIVE, + value: this.config?.isInteractive().toString() ?? 'false', + }, ]; return [...data, ...defaultLogMetadata]; } diff --git a/packages/core/src/telemetry/clearcut-logger/event-metadata-key.ts b/packages/core/src/telemetry/clearcut-logger/event-metadata-key.ts index e24292c51c..b429df5a49 100644 --- a/packages/core/src/telemetry/clearcut-logger/event-metadata-key.ts +++ b/packages/core/src/telemetry/clearcut-logger/event-metadata-key.ts @@ -473,4 +473,7 @@ export enum EventMetadataKey { // Logs whether the agent recovery attempt was successful. GEMINI_CLI_AGENT_RECOVERY_SUCCESS = 124, + + // Logs whether the session is interactive. + GEMINI_CLI_INTERACTIVE = 125, } diff --git a/packages/core/src/telemetry/loggers.test.circular.ts b/packages/core/src/telemetry/loggers.test.circular.ts index 23a6b6aeed..2f82743de3 100644 --- a/packages/core/src/telemetry/loggers.test.circular.ts +++ b/packages/core/src/telemetry/loggers.test.circular.ts @@ -69,7 +69,6 @@ describe('Circular Reference Handling', () => { durationMs: 100, }; - // Create a tool call event with circular references in function_args const event = new ToolCallEvent(mockCompletedToolCall); // This should not throw an error diff --git a/packages/core/src/telemetry/loggers.test.ts b/packages/core/src/telemetry/loggers.test.ts index 5a8cb12da7..46357d699f 100644 --- a/packages/core/src/telemetry/loggers.test.ts +++ b/packages/core/src/telemetry/loggers.test.ts @@ -211,6 +211,7 @@ describe('loggers', () => { }, }), }), + isInteractive: () => false, } as unknown as Config; const startSessionEvent = new StartSessionEvent(mockConfig); @@ -224,6 +225,7 @@ describe('loggers', () => { 'installation.id': 'test-installation-id', 'event.name': EVENT_CLI_CONFIG, 'event.timestamp': '2025-01-01T00:00:00.000Z', + interactive: false, model: 'test-model', embedding_model: 'test-embedding-model', sandbox_enabled: true, @@ -254,6 +256,7 @@ describe('loggers', () => { getTelemetryEnabled: () => true, getTelemetryLogPromptsEnabled: () => true, getUsageStatisticsEnabled: () => true, + isInteractive: () => false, } as unknown as Config; it('should log a user prompt', () => { @@ -274,6 +277,7 @@ describe('loggers', () => { 'installation.id': 'test-installation-id', 'event.name': EVENT_USER_PROMPT, 'event.timestamp': '2025-01-01T00:00:00.000Z', + interactive: false, prompt_length: 11, prompt: 'test-prompt', prompt_id: 'prompt-id-8', @@ -289,6 +293,7 @@ describe('loggers', () => { getTelemetryLogPromptsEnabled: () => false, getTargetDir: () => 'target-dir', getUsageStatisticsEnabled: () => true, + isInteractive: () => false, } as unknown as Config; const event = new UserPromptEvent( 11, @@ -307,6 +312,7 @@ describe('loggers', () => { 'installation.id': 'test-installation-id', 'event.name': EVENT_USER_PROMPT, 'event.timestamp': '2025-01-01T00:00:00.000Z', + interactive: false, prompt_length: 11, prompt_id: 'prompt-id-9', auth_type: 'cloud-shell', @@ -322,6 +328,7 @@ describe('loggers', () => { getUsageStatisticsEnabled: () => true, getTelemetryEnabled: () => true, getTelemetryLogPromptsEnabled: () => true, + isInteractive: () => false, } as Config; const mockMetrics = { @@ -496,6 +503,7 @@ describe('loggers', () => { getUsageStatisticsEnabled: () => true, getTelemetryEnabled: () => true, getTelemetryLogPromptsEnabled: () => true, + isInteractive: () => false, } as Config; const mockMetrics = { @@ -627,6 +635,7 @@ describe('loggers', () => { getUsageStatisticsEnabled: () => true, getTelemetryEnabled: () => true, getTelemetryLogPromptsEnabled: () => true, + isInteractive: () => false, } as Config; it('should log an API request with request_text', () => { @@ -646,6 +655,7 @@ describe('loggers', () => { 'installation.id': 'test-installation-id', 'event.name': EVENT_API_REQUEST, 'event.timestamp': '2025-01-01T00:00:00.000Z', + interactive: false, model: 'test-model', request_text: 'This is a test request', prompt_id: 'prompt-id-7', @@ -666,6 +676,7 @@ describe('loggers', () => { 'installation.id': 'test-installation-id', 'event.name': EVENT_API_REQUEST, 'event.timestamp': '2025-01-01T00:00:00.000Z', + interactive: false, model: 'test-model', prompt_id: 'prompt-id-6', }, @@ -677,6 +688,7 @@ describe('loggers', () => { const mockConfig = { getSessionId: () => 'test-session-id', getUsageStatisticsEnabled: () => true, + isInteractive: () => false, } as unknown as Config; it('should log flash fallback event', () => { @@ -692,6 +704,7 @@ describe('loggers', () => { 'installation.id': 'test-installation-id', 'event.name': EVENT_FLASH_FALLBACK, 'event.timestamp': '2025-01-01T00:00:00.000Z', + interactive: false, auth_type: 'vertex-ai', }, }); @@ -702,6 +715,7 @@ describe('loggers', () => { const mockConfig = { getSessionId: () => 'test-session-id', getUsageStatisticsEnabled: () => true, + isInteractive: () => false, } as unknown as Config; beforeEach(() => { @@ -794,6 +808,7 @@ describe('loggers', () => { getUsageStatisticsEnabled: () => true, getTelemetryEnabled: () => true, getTelemetryLogPromptsEnabled: () => true, + isInteractive: () => false, } as Config; const mockMetrics = { @@ -865,6 +880,7 @@ describe('loggers', () => { 'installation.id': 'test-installation-id', 'event.name': EVENT_TOOL_CALL, 'event.timestamp': '2025-01-01T00:00:00.000Z', + interactive: false, function_name: 'test-function', function_args: JSON.stringify( { @@ -881,7 +897,8 @@ describe('loggers', () => { tool_type: 'native', error: undefined, error_type: undefined, - + mcp_server_name: undefined, + extension_id: undefined, metadata: { model_added_lines: 1, model_removed_lines: 2, @@ -962,6 +979,7 @@ describe('loggers', () => { 'installation.id': 'test-installation-id', 'event.name': EVENT_TOOL_CALL, 'event.timestamp': '2025-01-01T00:00:00.000Z', + interactive: false, function_name: 'test-function', function_args: JSON.stringify( { @@ -978,6 +996,8 @@ describe('loggers', () => { tool_type: 'native', error: undefined, error_type: undefined, + mcp_server_name: undefined, + extension_id: undefined, metadata: undefined, content_length: undefined, }, @@ -1039,6 +1059,7 @@ describe('loggers', () => { 'installation.id': 'test-installation-id', 'event.name': EVENT_TOOL_CALL, 'event.timestamp': '2025-01-01T00:00:00.000Z', + interactive: false, function_name: 'test-function', function_args: JSON.stringify( { @@ -1055,6 +1076,8 @@ describe('loggers', () => { tool_type: 'native', error: undefined, error_type: undefined, + mcp_server_name: undefined, + extension_id: undefined, metadata: undefined, content_length: 13, }, @@ -1115,6 +1138,7 @@ describe('loggers', () => { 'installation.id': 'test-installation-id', 'event.name': EVENT_TOOL_CALL, 'event.timestamp': '2025-01-01T00:00:00.000Z', + interactive: false, function_name: 'test-function', function_args: JSON.stringify( { @@ -1131,6 +1155,8 @@ describe('loggers', () => { decision: undefined, error: undefined, error_type: undefined, + mcp_server_name: undefined, + extension_id: undefined, metadata: undefined, content_length: 13, }, @@ -1190,6 +1216,7 @@ describe('loggers', () => { 'installation.id': 'test-installation-id', 'event.name': EVENT_TOOL_CALL, 'event.timestamp': '2025-01-01T00:00:00.000Z', + interactive: false, function_name: 'test-function', function_args: JSON.stringify( { @@ -1208,6 +1235,8 @@ describe('loggers', () => { prompt_id: 'prompt-id-5', tool_type: 'native', decision: undefined, + mcp_server_name: undefined, + extension_id: undefined, metadata: undefined, content_length: errorMessage.length, }, @@ -1273,7 +1302,6 @@ describe('loggers', () => { durationMs: 100, }; const event = new ToolCallEvent(call); - logToolCall(mockConfig, event); expect(mockLogger.emit).toHaveBeenCalledWith({ @@ -1286,6 +1314,7 @@ describe('loggers', () => { 'event.timestamp': '2025-01-01T00:00:00.000Z', extension_name: 'test-extension', extension_id: 'test-extension-id', + interactive: false, function_name: 'mock_mcp_tool', function_args: JSON.stringify( { @@ -1333,6 +1362,7 @@ describe('loggers', () => { 'installation.id': 'test-installation-id', 'event.name': EVENT_MALFORMED_JSON_RESPONSE, 'event.timestamp': '2025-01-01T00:00:00.000Z', + interactive: false, model: 'test-model', }, }); @@ -1346,6 +1376,7 @@ describe('loggers', () => { getUsageStatisticsEnabled: () => true, getTelemetryEnabled: () => true, getTelemetryLogPromptsEnabled: () => true, + isInteractive: () => false, } as Config; const mockMetrics = { @@ -1378,6 +1409,7 @@ describe('loggers', () => { 'installation.id': 'test-installation-id', 'event.name': EVENT_FILE_OPERATION, 'event.timestamp': '2025-01-01T00:00:00.000Z', + interactive: false, tool_name: 'test-tool', operation: 'read', lines: 10, @@ -1404,6 +1436,7 @@ describe('loggers', () => { const mockConfig = { getSessionId: () => 'test-session-id', getUsageStatisticsEnabled: () => true, + isInteractive: () => false, } as unknown as Config; it('should log a tool output truncated event', () => { @@ -1426,6 +1459,7 @@ describe('loggers', () => { 'event.name': EVENT_TOOL_OUTPUT_TRUNCATED, 'event.timestamp': '2025-01-01T00:00:00.000Z', eventName: 'tool_output_truncated', + interactive: false, prompt_id: 'prompt-id-1', tool_name: 'test-tool', original_content_length: 1000, @@ -1441,6 +1475,7 @@ describe('loggers', () => { const mockConfig = { getSessionId: () => 'test-session-id', getUsageStatisticsEnabled: () => true, + isInteractive: () => false, } as unknown as Config; beforeEach(() => { @@ -1472,6 +1507,7 @@ describe('loggers', () => { 'installation.id': 'test-installation-id', ...event, 'event.name': EVENT_MODEL_ROUTING, + interactive: false, }, }); @@ -1509,6 +1545,7 @@ describe('loggers', () => { getContentGeneratorConfig: () => null, getUseSmartEdit: () => null, getUseModelRouter: () => null, + isInteractive: () => false, } as unknown as Config; beforeEach(() => { @@ -1542,6 +1579,7 @@ describe('loggers', () => { 'installation.id': 'test-installation-id', 'event.name': EVENT_EXTENSION_INSTALL, 'event.timestamp': '2025-01-01T00:00:00.000Z', + interactive: false, extension_name: 'testing', extension_version: '0.1.0', extension_source: 'git', @@ -1558,6 +1596,7 @@ describe('loggers', () => { getContentGeneratorConfig: () => null, getUseSmartEdit: () => null, getUseModelRouter: () => null, + isInteractive: () => false, } as unknown as Config; beforeEach(() => { @@ -1592,6 +1631,7 @@ describe('loggers', () => { 'installation.id': 'test-installation-id', 'event.name': EVENT_EXTENSION_UPDATE, 'event.timestamp': '2025-01-01T00:00:00.000Z', + interactive: false, extension_name: 'testing', extension_version: '0.1.0', extension_previous_version: '0.1.1', @@ -1609,6 +1649,7 @@ describe('loggers', () => { getContentGeneratorConfig: () => null, getUseSmartEdit: () => null, getUseModelRouter: () => null, + isInteractive: () => false, } as unknown as Config; beforeEach(() => { @@ -1639,6 +1680,7 @@ describe('loggers', () => { 'installation.id': 'test-installation-id', 'event.name': EVENT_EXTENSION_UNINSTALL, 'event.timestamp': '2025-01-01T00:00:00.000Z', + interactive: false, extension_name: 'testing', status: 'success', }, @@ -1650,6 +1692,7 @@ describe('loggers', () => { const mockConfig = { getSessionId: () => 'test-session-id', getUsageStatisticsEnabled: () => true, + isInteractive: () => false, } as unknown as Config; beforeEach(() => { @@ -1677,6 +1720,7 @@ describe('loggers', () => { 'installation.id': 'test-installation-id', 'event.name': EVENT_EXTENSION_ENABLE, 'event.timestamp': '2025-01-01T00:00:00.000Z', + interactive: false, extension_name: 'testing', setting_scope: 'user', }, @@ -1688,6 +1732,7 @@ describe('loggers', () => { const mockConfig = { getSessionId: () => 'test-session-id', getUsageStatisticsEnabled: () => true, + isInteractive: () => false, } as unknown as Config; beforeEach(() => { @@ -1715,6 +1760,7 @@ describe('loggers', () => { 'installation.id': 'test-installation-id', 'event.name': EVENT_EXTENSION_DISABLE, 'event.timestamp': '2025-01-01T00:00:00.000Z', + interactive: false, extension_name: 'testing', setting_scope: 'user', }, @@ -1726,6 +1772,7 @@ describe('loggers', () => { const mockConfig = { getSessionId: () => 'test-session-id', getUsageStatisticsEnabled: () => true, + isInteractive: () => false, } as unknown as Config; beforeEach(() => { @@ -1749,6 +1796,7 @@ describe('loggers', () => { 'installation.id': 'test-installation-id', 'event.name': EVENT_AGENT_START, 'event.timestamp': '2025-01-01T00:00:00.000Z', + interactive: false, agent_id: 'agent-123', agent_name: 'TestAgent', }, @@ -1760,6 +1808,7 @@ describe('loggers', () => { const mockConfig = { getSessionId: () => 'test-session-id', getUsageStatisticsEnabled: () => true, + isInteractive: () => false, } as unknown as Config; beforeEach(() => { @@ -1790,6 +1839,7 @@ describe('loggers', () => { 'installation.id': 'test-installation-id', 'event.name': EVENT_AGENT_FINISH, 'event.timestamp': '2025-01-01T00:00:00.000Z', + interactive: false, agent_id: 'agent-123', agent_name: 'TestAgent', duration_ms: 1000, @@ -1809,6 +1859,7 @@ describe('loggers', () => { const mockConfig = { getSessionId: () => 'test-session-id', getUsageStatisticsEnabled: () => true, + isInteractive: () => false, } as unknown as Config; beforeEach(() => { @@ -1832,6 +1883,7 @@ describe('loggers', () => { 'installation.id': 'test-installation-id', 'event.name': EVENT_WEB_FETCH_FALLBACK_ATTEMPT, 'event.timestamp': '2025-01-01T00:00:00.000Z', + interactive: false, reason: 'private_ip', }, }); diff --git a/packages/core/src/telemetry/telemetryAttributes.ts b/packages/core/src/telemetry/telemetryAttributes.ts index 6c205161f0..83593021f9 100644 --- a/packages/core/src/telemetry/telemetryAttributes.ts +++ b/packages/core/src/telemetry/telemetryAttributes.ts @@ -17,6 +17,7 @@ export function getCommonAttributes(config: Config): Attributes { return { 'session.id': config.getSessionId(), 'installation.id': installationManager.getInstallationId(), + interactive: config.isInteractive(), ...(email && { 'user.email': email }), }; } diff --git a/packages/core/src/telemetry/types.ts b/packages/core/src/telemetry/types.ts index e5b309fea8..8d95363356 100644 --- a/packages/core/src/telemetry/types.ts +++ b/packages/core/src/telemetry/types.ts @@ -161,6 +161,19 @@ export class EndSessionEvent implements BaseTelemetryEvent { this['event.timestamp'] = new Date().toISOString(); this.session_id = config?.getSessionId(); } + + toOpenTelemetryAttributes(config: Config): LogAttributes { + return { + ...getCommonAttributes(config), + 'event.name': this['event.name'], + 'event.timestamp': this['event.timestamp'], + session_id: this.session_id, + }; + } + + toLogBody(): string { + return 'Session ended.'; + } } export const EVENT_USER_PROMPT = 'gemini_cli.user_prompt'; @@ -299,7 +312,7 @@ export class ToolCallEvent implements BaseTelemetryEvent { } } } else { - this.function_name = function_name!; + this.function_name = function_name as string; this.function_args = function_args!; this.duration_ms = duration_ms!; this.success = success!; diff --git a/packages/core/src/tools/edit.test.ts b/packages/core/src/tools/edit.test.ts index 1d35e21d1d..d8f650f463 100644 --- a/packages/core/src/tools/edit.test.ts +++ b/packages/core/src/tools/edit.test.ts @@ -110,6 +110,7 @@ describe('EditTool', () => { getGeminiMdFileCount: () => 0, setGeminiMdFileCount: vi.fn(), getToolRegistry: () => ({}) as any, // Minimal mock for ToolRegistry + isInteractive: () => false, } as unknown as Config; // Reset mocks before each test diff --git a/packages/core/src/tools/read-file.test.ts b/packages/core/src/tools/read-file.test.ts index 6bb253a452..ccc07fd283 100644 --- a/packages/core/src/tools/read-file.test.ts +++ b/packages/core/src/tools/read-file.test.ts @@ -45,6 +45,7 @@ describe('ReadFileTool', () => { storage: { getProjectTempDir: () => path.join(tempRootDir, '.temp'), }, + isInteractive: () => false, } as unknown as Config; tool = new ReadFileTool(mockConfigInstance); }); diff --git a/packages/core/src/tools/read-many-files.test.ts b/packages/core/src/tools/read-many-files.test.ts index 13dec8cc61..47aa6b73d1 100644 --- a/packages/core/src/tools/read-many-files.test.ts +++ b/packages/core/src/tools/read-many-files.test.ts @@ -88,6 +88,7 @@ describe('ReadManyFilesTool', () => { buildExcludePatterns: () => DEFAULT_FILE_EXCLUDES, getReadManyFilesExcludes: () => DEFAULT_FILE_EXCLUDES, }), + isInteractive: () => false, } as Partial as Config; tool = new ReadManyFilesTool(mockConfig); @@ -502,6 +503,7 @@ describe('ReadManyFilesTool', () => { buildExcludePatterns: () => [], getReadManyFilesExcludes: () => [], }), + isInteractive: () => false, } as Partial as Config; tool = new ReadManyFilesTool(mockConfig); diff --git a/packages/core/src/tools/smart-edit.test.ts b/packages/core/src/tools/smart-edit.test.ts index d4d36c516f..417773fc90 100644 --- a/packages/core/src/tools/smart-edit.test.ts +++ b/packages/core/src/tools/smart-edit.test.ts @@ -117,6 +117,7 @@ describe('SmartEditTool', () => { getGeminiMdFileCount: () => 0, setGeminiMdFileCount: vi.fn(), getToolRegistry: () => ({}) as any, + isInteractive: () => false, } as unknown as Config; (mockConfig.getApprovalMode as Mock).mockClear(); diff --git a/packages/core/src/tools/web-fetch.test.ts b/packages/core/src/tools/web-fetch.test.ts index adfc659ba0..7105a8b52e 100644 --- a/packages/core/src/tools/web-fetch.test.ts +++ b/packages/core/src/tools/web-fetch.test.ts @@ -142,6 +142,7 @@ describe('WebFetchTool', () => { setApprovalMode: vi.fn(), getProxy: vi.fn(), getGeminiClient: mockGetGeminiClient, + isInteractive: () => false, } as unknown as Config; }); diff --git a/packages/core/src/tools/write-file.test.ts b/packages/core/src/tools/write-file.test.ts index 14fae7b215..a3b092d828 100644 --- a/packages/core/src/tools/write-file.test.ts +++ b/packages/core/src/tools/write-file.test.ts @@ -96,6 +96,7 @@ const mockConfigInternal = { registerTool: vi.fn(), discoverTools: vi.fn(), }) as unknown as ToolRegistry, + isInteractive: () => false, }; const mockConfig = mockConfigInternal as unknown as Config;