refactor(logging): Centralize console logging with debugLogger (#11590)

This commit is contained in:
Abhi
2025-10-21 16:35:22 -04:00
committed by GitHub
parent f5e07d94bd
commit b364f37655
72 changed files with 345 additions and 289 deletions
+2 -1
View File
@@ -43,6 +43,7 @@ import { templateString } from './utils.js';
import { parseThought } from '../utils/thoughtUtils.js';
import { type z } from 'zod';
import { zodToJsonSchema } from 'zod-to-json-schema';
import { debugLogger } from '../utils/debugLogger.js';
/** A callback function to report on agent activity. */
export type ActivityCallback = (activity: SubagentActivityEvent) => void;
@@ -506,7 +507,7 @@ export class AgentExecutor<TOutput extends z.ZodTypeAny> {
if (!allowedToolNames.has(functionCall.name as string)) {
const error = `Unauthorized tool call: '${functionCall.name}' is not available to this agent.`;
console.warn(`[AgentExecutor] Blocked call: ${error}`);
debugLogger.warn(`[AgentExecutor] Blocked call: ${error}`);
syncResponseParts.push({
functionResponse: {
+4 -3
View File
@@ -8,6 +8,7 @@ import type { Config } from '../config/config.js';
import type { AgentDefinition } from './types.js';
import { CodebaseInvestigatorAgent } from './codebase-investigator.js';
import { type z } from 'zod';
import { debugLogger } from '../utils/debugLogger.js';
/**
* Manages the discovery, loading, validation, and registration of
@@ -26,7 +27,7 @@ export class AgentRegistry {
this.loadBuiltInAgents();
if (this.config.getDebugMode()) {
console.log(
debugLogger.log(
`[AgentRegistry] Initialized with ${this.agents.size} agents.`,
);
}
@@ -72,14 +73,14 @@ export class AgentRegistry {
): void {
// Basic validation
if (!definition.name || !definition.description) {
console.warn(
debugLogger.warn(
`[AgentRegistry] Skipping invalid agent definition. Missing name or description.`,
);
return;
}
if (this.agents.has(definition.name) && this.config.getDebugMode()) {
console.log(`[AgentRegistry] Overriding agent '${definition.name}'`);
debugLogger.log(`[AgentRegistry] Overriding agent '${definition.name}'`);
}
this.agents.set(definition.name, definition);
+15 -12
View File
@@ -25,6 +25,7 @@ import readline from 'node:readline';
import { Storage } from '../config/storage.js';
import { OAuthCredentialStorage } from './oauth-credential-storage.js';
import { FORCE_ENCRYPTED_FILE_ENV_VAR } from '../mcp/token-storage/index.js';
import { debugLogger } from '../utils/debugLogger.js';
const userAccountManager = new UserAccountManager();
@@ -110,10 +111,10 @@ async function initOauthClient(
await fetchAndCacheUserInfo(client);
} catch (error) {
// Non-fatal, continue with existing auth.
console.warn('Failed to fetch user info:', getErrorMessage(error));
debugLogger.warn('Failed to fetch user info:', getErrorMessage(error));
}
}
console.log('Loaded cached credentials.');
debugLogger.log('Loaded cached credentials.');
return client;
}
@@ -122,13 +123,13 @@ async function initOauthClient(
// the identity of the user logged into Cloud Shell.
if (authType === AuthType.CLOUD_SHELL) {
try {
console.log("Attempting to authenticate via Cloud Shell VM's ADC.");
debugLogger.log("Attempting to authenticate via Cloud Shell VM's ADC.");
const computeClient = new Compute({
// We can leave this empty, since the metadata server will provide
// the service account email.
});
await computeClient.getAccessToken();
console.log('Authentication successful.');
debugLogger.log('Authentication successful.');
// Do not cache creds in this case; note that Compute client will handle its own refresh
return computeClient;
@@ -161,7 +162,7 @@ async function initOauthClient(
} else {
const webLogin = await authWithWeb(client);
console.log(
debugLogger.log(
`\n\nCode Assist login required.\n` +
`Attempting to open authentication page in your browser.\n` +
`Otherwise navigate to:\n\n${webLogin.authUrl}\n\n`,
@@ -193,7 +194,7 @@ async function initOauthClient(
`Failed to open browser: ${getErrorMessage(err)}`,
);
}
console.log('Waiting for authentication...');
debugLogger.log('Waiting for authentication...');
// Add timeout to prevent infinite waiting when browser tab gets stuck
const authTimeout = 5 * 60 * 1000; // 5 minutes timeout
@@ -236,10 +237,12 @@ async function authWithUserCode(client: OAuth2Client): Promise<boolean> {
code_challenge: codeVerifier.codeChallenge,
state,
});
console.log('Please visit the following URL to authorize the application:');
console.log('');
console.log(authUrl);
console.log('');
debugLogger.log(
'Please visit the following URL to authorize the application:',
);
debugLogger.log('');
debugLogger.log(authUrl);
debugLogger.log('');
const code = await new Promise<string>((resolve) => {
const rl = readline.createInterface({
@@ -337,7 +340,7 @@ async function authWithWeb(client: OAuth2Client): Promise<OauthWebLogin> {
try {
await fetchAndCacheUserInfo(client);
} catch (error) {
console.warn(
debugLogger.warn(
'Failed to retrieve Google Account ID during authentication:',
getErrorMessage(error),
);
@@ -462,7 +465,7 @@ async function loadCachedCredentials(client: OAuth2Client): Promise<boolean> {
return true;
} catch (error) {
// Log specific error for debugging, but continue trying other paths
console.debug(
debugLogger.debug(
`Failed to load credentials from ${keyFile}:`,
getErrorMessage(error),
);
+4 -3
View File
@@ -157,6 +157,7 @@ import {
DEFAULT_FILE_FILTERING_OPTIONS,
DEFAULT_MEMORY_FILE_FILTERING_OPTIONS,
} from './constants.js';
import { debugLogger } from '../utils/debugLogger.js';
export type { FileFilteringOptions };
export {
@@ -1123,7 +1124,7 @@ export class Config {
// the tool registry.
const messageBusEnabled = this.getEnableMessageBusIntegration();
if (this.debugMode && messageBusEnabled) {
console.log(
debugLogger.log(
`[DEBUG] enableMessageBusIntegration setting: ${messageBusEnabled}`,
);
}
@@ -1131,7 +1132,7 @@ export class Config {
? [...args, this.getMessageBus()]
: args;
if (this.debugMode && messageBusEnabled) {
console.log(
debugLogger.log(
`[DEBUG] Registering ${className} with messageBus: ${messageBusEnabled ? 'YES' : 'NO'}`,
);
}
@@ -1206,7 +1207,7 @@ export class Config {
);
}
} else if (this.getDebugMode()) {
console.log(
debugLogger.log(
`[Config] Skipping registration of agent '${definition.name}' due to allow/exclude configuration.`,
);
}
-28
View File
@@ -1385,34 +1385,11 @@ ${JSON.stringify(
// Assert
expect(finalResult).toBeInstanceOf(Turn);
// Debug: Check how many times checkNextSpeaker was called
const callCount = mockCheckNextSpeaker.mock.calls.length;
// If infinite loop protection is working, checkNextSpeaker should be called many times
// but stop at MAX_TURNS (100). Since each recursive call should trigger checkNextSpeaker,
// we expect it to be called multiple times before hitting the limit
expect(mockCheckNextSpeaker).toHaveBeenCalled();
// The test should demonstrate that the infinite loop protection works:
// - If checkNextSpeaker is called many times (close to MAX_TURNS), it shows the loop was happening
// - If it's only called once, the recursive behavior might not be triggered
if (callCount === 0) {
throw new Error(
'checkNextSpeaker was never called - the recursive condition was not met',
);
} else if (callCount === 1) {
// This might be expected behavior if the turn has pending tool calls or other conditions prevent recursion
console.log(
'checkNextSpeaker called only once - no infinite loop occurred',
);
} else {
console.log(
`checkNextSpeaker called ${callCount} times - infinite loop protection worked`,
);
// If called multiple times, we expect it to be stopped before MAX_TURNS
expect(callCount).toBeLessThanOrEqual(100); // Should not exceed MAX_TURNS
}
// The stream should produce events and eventually terminate
expect(eventCount).toBeGreaterThanOrEqual(1);
expect(eventCount).toBeLessThan(200); // Should not exceed our safety limit
@@ -1537,11 +1514,6 @@ ${JSON.stringify(
// the loop should stop at MAX_TURNS (100)
expect(callCount).toBeLessThanOrEqual(100); // Should not exceed MAX_TURNS
expect(eventCount).toBeLessThanOrEqual(200); // Should have reasonable number of events
console.log(
`Infinite loop protection working: checkNextSpeaker called ${callCount} times, ` +
`${eventCount} events generated (properly bounded by MAX_TURNS)`,
);
});
it('should yield ContextWindowWillOverflow when the context window is about to overflow', async () => {
+3 -2
View File
@@ -52,6 +52,7 @@ import type { IdeContext, File } from '../ide/types.js';
import { handleFallback } from '../fallback/handler.js';
import type { RoutingContext } from '../routing/routingStrategy.js';
import { uiTelemetryService } from '../telemetry/uiTelemetry.js';
import { debugLogger } from '../utils/debugLogger.js';
export function isThinkingSupported(model: string) {
return model.startsWith('gemini-2.5') || model === DEFAULT_GEMINI_MODEL_AUTO;
@@ -341,7 +342,7 @@ My setup is complete. I will provide my first command in the next turn.
];
if (this.config.getDebugMode()) {
console.log(contextParts.join('\n'));
debugLogger.log(contextParts.join('\n'));
}
return {
contextParts,
@@ -451,7 +452,7 @@ My setup is complete. I will provide my first command in the next turn.
];
if (this.config.getDebugMode()) {
console.log(contextParts.join('\n'));
debugLogger.log(contextParts.join('\n'));
}
return {
contextParts,
+11 -10
View File
@@ -8,6 +8,7 @@ import path from 'node:path';
import { promises as fs } from 'node:fs';
import type { Content } from '@google/genai';
import type { Storage } from '../config/storage.js';
import { debugLogger } from '../utils/debugLogger.js';
const LOG_FILE_NAME = 'logs.json';
@@ -82,7 +83,7 @@ export class Logger {
const fileContent = await fs.readFile(this.logFilePath, 'utf-8');
const parsedLogs = JSON.parse(fileContent);
if (!Array.isArray(parsedLogs)) {
console.debug(
debugLogger.debug(
`Log file at ${this.logFilePath} is not a valid JSON array. Starting with empty logs.`,
);
await this._backupCorruptedLogFile('malformed_array');
@@ -102,14 +103,14 @@ export class Logger {
return [];
}
if (error instanceof SyntaxError) {
console.debug(
debugLogger.debug(
`Invalid JSON in log file ${this.logFilePath}. Backing up and starting fresh.`,
error,
);
await this._backupCorruptedLogFile('invalid_json');
return [];
}
console.debug(
debugLogger.debug(
`Failed to read or parse log file ${this.logFilePath}:`,
error,
);
@@ -122,7 +123,7 @@ export class Logger {
const backupPath = `${this.logFilePath}.${reason}.${Date.now()}.bak`;
try {
await fs.rename(this.logFilePath, backupPath);
console.debug(`Backed up corrupted log file to ${backupPath}`);
debugLogger.debug(`Backed up corrupted log file to ${backupPath}`);
} catch (_backupError) {
// If rename fails (e.g. file doesn't exist), no need to log an error here as the primary error (e.g. invalid JSON) is already handled.
}
@@ -166,7 +167,7 @@ export class Logger {
entryToAppend: LogEntry,
): Promise<LogEntry | null> {
if (!this.logFilePath) {
console.debug('Log file path not set. Cannot persist log entry.');
debugLogger.debug('Log file path not set. Cannot persist log entry.');
throw new Error('Log file path not set during update attempt.');
}
@@ -174,7 +175,7 @@ export class Logger {
try {
currentLogsOnDisk = await this._readLogFile();
} catch (readError) {
console.debug(
debugLogger.debug(
'Critical error reading log file before append:',
readError,
);
@@ -205,7 +206,7 @@ export class Logger {
);
if (entryExists) {
console.debug(
debugLogger.debug(
`Duplicate log entry detected and skipped: session ${entryToAppend.sessionId}, messageId ${entryToAppend.messageId}`,
);
this.logs = currentLogsOnDisk; // Ensure in-memory is synced with disk
@@ -223,7 +224,7 @@ export class Logger {
this.logs = currentLogsOnDisk;
return entryToAppend; // Return the successfully appended entry
} catch (error) {
console.debug('Error writing to log file:', error);
debugLogger.debug('Error writing to log file:', error);
throw error;
}
}
@@ -242,7 +243,7 @@ export class Logger {
async logMessage(type: MessageSenderType, message: string): Promise<void> {
if (!this.initialized || this.sessionId === undefined) {
console.debug(
debugLogger.debug(
'Logger not initialized or session ID missing. Cannot log message.',
);
return;
@@ -341,7 +342,7 @@ export class Logger {
const fileContent = await fs.readFile(path, 'utf-8');
const parsedContent = JSON.parse(fileContent);
if (!Array.isArray(parsedContent)) {
console.warn(
debugLogger.warn(
`Checkpoint file at ${path} is not a valid JSON array. Returning empty checkpoint.`,
);
return [];
+2 -1
View File
@@ -22,6 +22,7 @@ import { isGitRepository } from '../utils/gitUtils.js';
import { CodebaseInvestigatorAgent } from '../agents/codebase-investigator.js';
import type { Config } from '../config/config.js';
import { GEMINI_DIR } from '../utils/paths.js';
import { debugLogger } from '../utils/debugLogger.js';
export function resolvePathFromEnv(envVar?: string): {
isSwitch: boolean;
@@ -56,7 +57,7 @@ export function resolvePathFromEnv(envVar?: string): {
}
} catch (error) {
// If os.homedir() fails, we catch the error instead of crashing.
console.warn(
debugLogger.warn(
`Could not resolve home directory for path: ${trimmedEnvVar}`,
error,
);
+2 -1
View File
@@ -24,10 +24,11 @@ import * as path from 'node:path';
import { EnvHttpProxyAgent } from 'undici';
import { ListToolsResultSchema } from '@modelcontextprotocol/sdk/types.js';
import { IDE_REQUEST_TIMEOUT_MS } from './constants.js';
import { debugLogger } from '../utils/debugLogger.js';
const logger = {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
debug: (...args: any[]) => console.debug('[DEBUG] [IDEClient]', ...args),
debug: (...args: any[]) => debugLogger.debug('[DEBUG] [IDEClient]', ...args),
// eslint-disable-next-line @typescript-eslint/no-explicit-any
error: (...args: any[]) => console.error('[ERROR] [IDEClient]', ...args),
};
+27 -20
View File
@@ -13,6 +13,7 @@ import type { OAuthToken } from './token-storage/types.js';
import { MCPOAuthTokenStorage } from './oauth-token-storage.js';
import { getErrorMessage } from '../utils/errors.js';
import { OAuthUtils } from './oauth-utils.js';
import { debugLogger } from '../utils/debugLogger.js';
export const OAUTH_DISPLAY_MESSAGE_EVENT = 'oauth-display-message' as const;
@@ -258,7 +259,9 @@ export class MCPOAuthProvider {
server.on('error', reject);
server.listen(REDIRECT_PORT, () => {
console.log(`OAuth callback server listening on port ${REDIRECT_PORT}`);
debugLogger.log(
`OAuth callback server listening on port ${REDIRECT_PORT}`,
);
});
// Timeout after 5 minutes
@@ -314,7 +317,7 @@ export class MCPOAuthProvider {
OAuthUtils.buildResourceParameter(mcpServerUrl),
);
} catch (error) {
console.warn(
debugLogger.warn(
`Could not add resource parameter: ${getErrorMessage(error)}`,
);
}
@@ -371,7 +374,7 @@ export class MCPOAuthProvider {
OAuthUtils.buildResourceParameter(resourceUrl),
);
} catch (error) {
console.warn(
debugLogger.warn(
`Could not add resource parameter: ${getErrorMessage(error)}`,
);
}
@@ -413,7 +416,7 @@ export class MCPOAuthProvider {
!contentType.includes('application/json') &&
!contentType.includes('application/x-www-form-urlencoded')
) {
console.warn(
debugLogger.warn(
`Token endpoint returned unexpected content-type: ${contentType}. ` +
`Expected application/json or application/x-www-form-urlencoded. ` +
`Will attempt to parse response.`,
@@ -493,7 +496,7 @@ export class MCPOAuthProvider {
OAuthUtils.buildResourceParameter(mcpServerUrl),
);
} catch (error) {
console.warn(
debugLogger.warn(
`Could not add resource parameter: ${getErrorMessage(error)}`,
);
}
@@ -535,7 +538,7 @@ export class MCPOAuthProvider {
!contentType.includes('application/json') &&
!contentType.includes('application/x-www-form-urlencoded')
) {
console.warn(
debugLogger.warn(
`Token refresh endpoint returned unexpected content-type: ${contentType}. ` +
`Expected application/json or application/x-www-form-urlencoded. ` +
`Will attempt to parse response.`,
@@ -593,13 +596,13 @@ export class MCPOAuthProvider {
if (events) {
events.emit(OAUTH_DISPLAY_MESSAGE_EVENT, message);
} else {
console.log(message);
debugLogger.log(message);
}
};
// If no authorization URL is provided, try to discover OAuth configuration
if (!config.authorizationUrl && mcpServerUrl) {
console.debug(`Starting OAuth for MCP server "${serverName}"…
debugLogger.debug(`Starting OAuth for MCP server "${serverName}"…
No authorization URL; using OAuth discovery`);
// First check if the server requires authentication via WWW-Authenticate header
@@ -636,7 +639,7 @@ export class MCPOAuthProvider {
}
}
} catch (error) {
console.debug(
debugLogger.debug(
`Failed to check endpoint for authentication requirements: ${getErrorMessage(error)}`,
);
}
@@ -681,7 +684,7 @@ export class MCPOAuthProvider {
const authUrl = new URL(config.authorizationUrl);
const serverUrl = `${authUrl.protocol}//${authUrl.host}`;
console.debug('→ Attempting dynamic client registration...');
debugLogger.debug('→ Attempting dynamic client registration...');
// Get the authorization server metadata for registration
const authServerMetadata =
@@ -707,7 +710,7 @@ export class MCPOAuthProvider {
config.clientSecret = clientRegistration.client_secret;
}
console.debug('✓ Dynamic client registration successful');
debugLogger.debug('✓ Dynamic client registration successful');
} else {
throw new Error(
'No client ID provided and dynamic registration not supported',
@@ -747,7 +750,7 @@ ${authUrl}
try {
await openBrowserSecurely(authUrl);
} catch (error) {
console.warn(
debugLogger.warn(
'Failed to open browser automatically:',
getErrorMessage(error),
);
@@ -756,7 +759,9 @@ ${authUrl}
// Wait for callback
const { code } = await callbackPromise;
console.debug('✓ Authorization code received, exchanging for tokens...');
debugLogger.debug(
'✓ Authorization code received, exchanging for tokens...',
);
// Exchange code for tokens
const tokenResponse = await this.exchangeCodeForToken(
@@ -791,7 +796,7 @@ ${authUrl}
config.tokenUrl,
mcpServerUrl,
);
console.debug('✓ Authentication successful! Token saved.');
debugLogger.debug('✓ Authentication successful! Token saved.');
// Verify token was saved
const savedToken = await this.tokenStorage.getCredentials(serverName);
@@ -802,7 +807,7 @@ ${authUrl}
.update(savedToken.token.accessToken)
.digest('hex')
.slice(0, 8);
console.debug(
debugLogger.debug(
`✓ Token verification successful (fingerprint: ${tokenFingerprint})`,
);
} else {
@@ -829,29 +834,31 @@ ${authUrl}
serverName: string,
config: MCPOAuthConfig,
): Promise<string | null> {
console.debug(`Getting valid token for server: ${serverName}`);
debugLogger.debug(`Getting valid token for server: ${serverName}`);
const credentials = await this.tokenStorage.getCredentials(serverName);
if (!credentials) {
console.debug(`No credentials found for server: ${serverName}`);
debugLogger.debug(`No credentials found for server: ${serverName}`);
return null;
}
const { token } = credentials;
console.debug(
debugLogger.debug(
`Found token for server: ${serverName}, expired: ${this.tokenStorage.isTokenExpired(token)}`,
);
// Check if token is expired
if (!this.tokenStorage.isTokenExpired(token)) {
console.debug(`Returning valid token for server: ${serverName}`);
debugLogger.debug(`Returning valid token for server: ${serverName}`);
return token.accessToken;
}
// Try to refresh if we have a refresh token
if (token.refreshToken && config.clientId && credentials.tokenUrl) {
try {
console.log(`Refreshing expired token for MCP server: ${serverName}`);
debugLogger.log(
`Refreshing expired token for MCP server: ${serverName}`,
);
const newTokenResponse = await this.refreshAccessToken(
config,
+8 -7
View File
@@ -6,6 +6,7 @@
import type { MCPOAuthConfig } from './oauth-provider.js';
import { getErrorMessage } from '../utils/errors.js';
import { debugLogger } from '../utils/debugLogger.js';
/**
* OAuth authorization server metadata as per RFC 8414.
@@ -94,7 +95,7 @@ export class OAuthUtils {
}
return (await response.json()) as OAuthProtectedResourceMetadata;
} catch (error) {
console.debug(
debugLogger.debug(
`Failed to fetch protected resource metadata from ${resourceMetadataUrl}: ${getErrorMessage(error)}`,
);
return null;
@@ -117,7 +118,7 @@ export class OAuthUtils {
}
return (await response.json()) as OAuthAuthorizationServerMetadata;
} catch (error) {
console.debug(
debugLogger.debug(
`Failed to fetch authorization server metadata from ${authServerMetadataUrl}: ${getErrorMessage(error)}`,
);
return null;
@@ -205,7 +206,7 @@ export class OAuthUtils {
}
}
console.debug(
debugLogger.debug(
`Metadata discovery failed for authorization server ${authServerUrl}`,
);
return null;
@@ -249,7 +250,7 @@ export class OAuthUtils {
if (authServerMetadata) {
const config = this.metadataToOAuthConfig(authServerMetadata);
if (authServerMetadata.registration_endpoint) {
console.log(
debugLogger.log(
'Dynamic client registration is supported at:',
authServerMetadata.registration_endpoint,
);
@@ -259,14 +260,14 @@ export class OAuthUtils {
}
// Fallback: try well-known endpoints at the base URL
console.debug(`Trying OAuth discovery fallback at ${serverUrl}`);
debugLogger.debug(`Trying OAuth discovery fallback at ${serverUrl}`);
const authServerMetadata =
await this.discoverAuthorizationServerMetadata(serverUrl);
if (authServerMetadata) {
const config = this.metadataToOAuthConfig(authServerMetadata);
if (authServerMetadata.registration_endpoint) {
console.log(
debugLogger.log(
'Dynamic client registration is supported at:',
authServerMetadata.registration_endpoint,
);
@@ -276,7 +277,7 @@ export class OAuthUtils {
return null;
} catch (error) {
console.debug(
debugLogger.debug(
`Failed to discover OAuth configuration: ${getErrorMessage(error)}`,
);
return null;
+2 -1
View File
@@ -5,6 +5,7 @@
*/
import type { DiscoveredMCPPrompt } from '../tools/mcp-client.js';
import { debugLogger } from '../utils/debugLogger.js';
export class PromptRegistry {
private prompts: Map<string, DiscoveredMCPPrompt> = new Map();
@@ -16,7 +17,7 @@ export class PromptRegistry {
registerPrompt(prompt: DiscoveredMCPPrompt): void {
if (this.prompts.has(prompt.name)) {
const newName = `${prompt.serverName}_${prompt.name}`;
console.warn(
debugLogger.warn(
`Prompt with name "${prompt.name}" is already registered. Renaming to "${newName}".`,
);
this.prompts.set(newName, { ...prompt, name: newName });
@@ -27,6 +27,7 @@ import {
isFunctionCall,
isFunctionResponse,
} from '../../utils/messageInspectors.js';
import { debugLogger } from '../../utils/debugLogger.js';
const CLASSIFIER_GENERATION_CONFIG: GenerateContentConfig = {
temperature: 0,
@@ -153,7 +154,7 @@ export class ClassifierStrategy implements RoutingStrategy {
promptId = `classifier-router-fallback-${Date.now()}-${Math.random()
.toString(16)
.slice(2)}`;
console.warn(
debugLogger.warn(
`Could not find promptId in context. This is unexpected. Using a fallback ID: ${promptId}`,
);
}
@@ -206,7 +207,7 @@ export class ClassifierStrategy implements RoutingStrategy {
} catch (error) {
// If the classifier fails for any reason (API error, parsing error, etc.),
// we log it and return null to allow the composite strategy to proceed.
console.warn(`[Routing] ClassifierStrategy failed:`, error);
debugLogger.warn(`[Routing] ClassifierStrategy failed:`, error);
return null;
}
}
@@ -23,6 +23,7 @@ import {
isFunctionCall,
isFunctionResponse,
} from '../utils/messageInspectors.js';
import { debugLogger } from '../utils/debugLogger.js';
const TOOL_CALL_LOOP_THRESHOLD = 5;
const CONTENT_LOOP_THRESHOLD = 10;
@@ -431,14 +432,14 @@ export class LoopDetectionService {
});
} catch (e) {
// Do nothing, treat it as a non-loop.
this.config.getDebugMode() ? console.error(e) : console.debug(e);
this.config.getDebugMode() ? console.error(e) : debugLogger.debug(e);
return false;
}
if (typeof result['confidence'] === 'number') {
if (result['confidence'] > 0.9) {
if (typeof result['reasoning'] === 'string' && result['reasoning']) {
console.warn(result['reasoning']);
debugLogger.warn(result['reasoning']);
}
logLoopDetected(
this.config,
@@ -8,6 +8,7 @@ import type { Config } from '../config/config.js';
import { isPerformanceMonitoringActive } from './metrics.js';
import { getMemoryMonitor } from './memory-monitor.js';
import { ActivityType } from './activity-types.js';
import { debugLogger } from '../utils/debugLogger.js';
/**
* Activity event data structure
@@ -152,7 +153,7 @@ export class ActivityMonitor {
listener(event);
} catch (error) {
// Silently catch listener errors to avoid disrupting the application
console.debug('ActivityMonitor listener error:', error);
debugLogger.debug('ActivityMonitor listener error:', error);
}
});
}
@@ -53,6 +53,7 @@ import {
detectIdeFromEnv,
isCloudShell,
} from '../../ide/detect-ide.js';
import { debugLogger } from '../../utils/debugLogger.js';
export enum EventNames {
START_SESSION = 'start_session',
@@ -238,7 +239,7 @@ export class ClearcutLogger {
]);
if (wasAtCapacity && this.config?.getDebugMode()) {
console.debug(
debugLogger.debug(
`ClearcutLogger: Dropped old event to prevent memory leak (queue size: ${this.events.size})`,
);
}
@@ -282,14 +283,14 @@ export class ClearcutLogger {
}
this.flushToClearcut().catch((error) => {
console.debug('Error flushing to Clearcut:', error);
debugLogger.debug('Error flushing to Clearcut:', error);
});
}
async flushToClearcut(): Promise<LogResponse> {
if (this.flushing) {
if (this.config?.getDebugMode()) {
console.debug(
debugLogger.debug(
'ClearcutLogger: Flush already in progress, marking pending flush.',
);
}
@@ -299,7 +300,7 @@ export class ClearcutLogger {
this.flushing = true;
if (this.config?.getDebugMode()) {
console.log('Flushing log events to Clearcut.');
debugLogger.log('Flushing log events to Clearcut.');
}
const eventsToSend = this.events.toArray() as LogEventEntry[][];
this.events.clear();
@@ -359,7 +360,7 @@ export class ClearcutLogger {
// Fire and forget the pending flush
this.flushToClearcut().catch((error) => {
if (this.config?.getDebugMode()) {
console.debug('Error in pending flush to Clearcut:', error);
debugLogger.debug('Error in pending flush to Clearcut:', error);
}
});
}
@@ -451,7 +452,7 @@ export class ClearcutLogger {
// Flush start event immediately
this.enqueueLogEvent(this.createLogEvent(EventNames.START_SESSION, data));
this.flushToClearcut().catch((error) => {
console.debug('Error flushing to Clearcut:', error);
debugLogger.debug('Error flushing to Clearcut:', error);
});
}
@@ -668,14 +669,14 @@ export class ClearcutLogger {
logFlashFallbackEvent(): void {
this.enqueueLogEvent(this.createLogEvent(EventNames.FLASH_FALLBACK, []));
this.flushToClearcut().catch((error) => {
console.debug('Error flushing to Clearcut:', error);
debugLogger.debug('Error flushing to Clearcut:', error);
});
}
logRipgrepFallbackEvent(): void {
this.enqueueLogEvent(this.createLogEvent(EventNames.RIPGREP_FALLBACK, []));
this.flushToClearcut().catch((error) => {
console.debug('Error flushing to Clearcut:', error);
debugLogger.debug('Error flushing to Clearcut:', error);
});
}
@@ -811,7 +812,7 @@ export class ClearcutLogger {
// Flush immediately on session end.
this.enqueueLogEvent(this.createLogEvent(EventNames.END_SESSION, []));
this.flushToClearcut().catch((error) => {
console.debug('Error flushing to Clearcut:', error);
debugLogger.debug('Error flushing to Clearcut:', error);
});
}
@@ -910,7 +911,7 @@ export class ClearcutLogger {
this.createLogEvent(EventNames.EXTENSION_INSTALL, data),
);
this.flushToClearcut().catch((error) => {
console.debug('Error flushing to Clearcut:', error);
debugLogger.debug('Error flushing to Clearcut:', error);
});
}
@@ -930,7 +931,7 @@ export class ClearcutLogger {
this.createLogEvent(EventNames.EXTENSION_UNINSTALL, data),
);
this.flushToClearcut().catch((error) => {
console.debug('Error flushing to Clearcut:', error);
debugLogger.debug('Error flushing to Clearcut:', error);
});
}
@@ -962,7 +963,7 @@ export class ClearcutLogger {
this.createLogEvent(EventNames.EXTENSION_UPDATE, data),
);
this.flushToClearcut().catch((error) => {
console.debug('Error flushing to Clearcut:', error);
debugLogger.debug('Error flushing to Clearcut:', error);
});
}
@@ -1047,7 +1048,7 @@ export class ClearcutLogger {
this.createLogEvent(EventNames.EXTENSION_ENABLE, data),
);
this.flushToClearcut().catch((error) => {
console.debug('Error flushing to Clearcut:', error);
debugLogger.debug('Error flushing to Clearcut:', error);
});
}
@@ -1082,7 +1083,7 @@ export class ClearcutLogger {
this.createLogEvent(EventNames.EXTENSION_DISABLE, data),
);
this.flushToClearcut().catch((error) => {
console.debug('Error flushing to Clearcut:', error);
debugLogger.debug('Error flushing to Clearcut:', error);
});
}
@@ -1240,7 +1241,7 @@ export class ClearcutLogger {
getConfigJson() {
const configJson = safeJsonStringifyBooleanValuesOnly(this.config);
console.debug(configJson);
debugLogger.debug(configJson);
return safeJsonStringifyBooleanValuesOnly(this.config);
}
@@ -1254,7 +1255,7 @@ export class ClearcutLogger {
// Log a warning if we're dropping events
if (eventsToSend.length > MAX_RETRY_EVENTS && this.config?.getDebugMode()) {
console.warn(
debugLogger.warn(
`ClearcutLogger: Dropping ${
eventsToSend.length - MAX_RETRY_EVENTS
} events due to retry queue limit. Total events: ${
@@ -1269,7 +1270,7 @@ export class ClearcutLogger {
if (numEventsToRequeue === 0) {
if (this.config?.getDebugMode()) {
console.debug(
debugLogger.debug(
`ClearcutLogger: No events re-queued (queue size: ${this.events.size})`,
);
}
@@ -1292,7 +1293,7 @@ export class ClearcutLogger {
}
if (this.config?.getDebugMode()) {
console.debug(
debugLogger.debug(
`ClearcutLogger: Re-queued ${numEventsToRequeue} events for retry (queue size: ${this.events.size})`,
);
}
+3 -2
View File
@@ -43,6 +43,7 @@ import {
GcpLogExporter,
} from './gcp-exporters.js';
import { TelemetryTarget } from './index.js';
import { debugLogger } from '../utils/debugLogger.js';
// For troubleshooting, set the log level to DiagLogLevel.DEBUG
diag.setLogger(new DiagConsoleLogger(), DiagLogLevel.INFO);
@@ -184,7 +185,7 @@ export function initializeTelemetry(config: Config): void {
try {
sdk.start();
if (config.getDebugMode()) {
console.log('OpenTelemetry SDK started successfully.');
debugLogger.log('OpenTelemetry SDK started successfully.');
}
telemetryInitialized = true;
initializeMetrics(config);
@@ -211,7 +212,7 @@ export async function shutdownTelemetry(config: Config): Promise<void> {
ClearcutLogger.getInstance()?.shutdown();
await sdk.shutdown();
if (config.getDebugMode()) {
console.log('OpenTelemetry SDK shut down successfully.');
debugLogger.log('OpenTelemetry SDK shut down successfully.');
}
} catch (error) {
console.error('Error shutting down SDK:', error);
+3 -2
View File
@@ -34,6 +34,7 @@ import type {
import { IdeClient } from '../ide/ide-client.js';
import { safeLiteralReplace } from '../utils/textUtils.js';
import { EDIT_TOOL_NAME, READ_FILE_TOOL_NAME } from './tool-names.js';
import { debugLogger } from '../utils/debugLogger.js';
export function applyReplacement(
currentContent: string | null,
@@ -255,12 +256,12 @@ class EditToolInvocation implements ToolInvocation<EditToolParams, ToolResult> {
throw error;
}
const errorMsg = error instanceof Error ? error.message : String(error);
console.log(`Error preparing edit: ${errorMsg}`);
debugLogger.log(`Error preparing edit: ${errorMsg}`);
return false;
}
if (editData.error) {
console.log(`Error: ${editData.error.display}`);
debugLogger.log(`Error: ${editData.error.display}`);
return false;
}
+5 -5
View File
@@ -388,7 +388,7 @@ class GrepToolInvocation extends BaseToolInvocation<
});
return this.parseGrepOutput(output, absolutePath);
} catch (gitError: unknown) {
console.debug(
debugLogger.debug(
`GrepLogic: git grep failed: ${getErrorMessage(
gitError,
)}. Falling back...`,
@@ -397,7 +397,7 @@ class GrepToolInvocation extends BaseToolInvocation<
}
// --- Strategy 2: System grep ---
console.debug(
debugLogger.debug(
'GrepLogic: System grep is being considered as fallback strategy.',
);
@@ -494,7 +494,7 @@ class GrepToolInvocation extends BaseToolInvocation<
});
return this.parseGrepOutput(output, absolutePath);
} catch (grepError: unknown) {
console.debug(
debugLogger.debug(
`GrepLogic: System grep failed: ${getErrorMessage(
grepError,
)}. Falling back...`,
@@ -503,7 +503,7 @@ class GrepToolInvocation extends BaseToolInvocation<
}
// --- Strategy 3: Pure JavaScript Fallback ---
console.debug(
debugLogger.debug(
'GrepLogic: Falling back to JavaScript grep implementation.',
);
strategyUsed = 'javascript fallback';
@@ -541,7 +541,7 @@ class GrepToolInvocation extends BaseToolInvocation<
} catch (readError: unknown) {
// Ignore errors like permission denied or file gone during read
if (!isNodeError(readError) || readError.code !== 'ENOENT') {
console.debug(
debugLogger.debug(
`GrepLogic: Could not read/process ${fileAbsolutePath}: ${getErrorMessage(
readError,
)}`,
+26 -18
View File
@@ -41,6 +41,7 @@ import type {
WorkspaceContext,
} from '../utils/workspaceContext.js';
import type { ToolRegistry } from './tool-registry.js';
import { debugLogger } from '../utils/debugLogger.js';
export const MCP_DEFAULT_TIMEOUT_MSEC = 10 * 60 * 1000; // default to 10 minutes
@@ -321,7 +322,7 @@ async function handleAutomaticOAuth(
wwwAuthenticate: string,
): Promise<boolean> {
try {
console.log(`🔐 '${mcpServerName}' requires OAuth authentication`);
debugLogger.log(`🔐 '${mcpServerName}' requires OAuth authentication`);
// Always try to parse the resource metadata URI from the www-authenticate header
let oauthConfig;
@@ -358,13 +359,13 @@ async function handleAutomaticOAuth(
// Perform OAuth authentication
// Pass the server URL for proper discovery
const serverUrl = mcpServerConfig.httpUrl || mcpServerConfig.url;
console.log(
debugLogger.log(
`Starting OAuth authentication for server '${mcpServerName}'...`,
);
const authProvider = new MCPOAuthProvider(new MCPOAuthTokenStorage());
await authProvider.authenticate(mcpServerName, oauthAuthConfig, serverUrl);
console.log(
debugLogger.log(
`OAuth authentication successful for server '${mcpServerName}'`,
);
return true;
@@ -840,12 +841,12 @@ export async function connectToMcpServer(
},
);
if (hasStoredTokens) {
console.log(
debugLogger.log(
`Stored OAuth token for SSE server '${mcpServerName}' was rejected. ` +
`Please re-authenticate using: /mcp auth ${mcpServerName}`,
);
} else {
console.log(
debugLogger.log(
`401 error received for SSE server '${mcpServerName}' without OAuth configuration. ` +
`Please authenticate using: /mcp auth ${mcpServerName}`,
);
@@ -862,7 +863,7 @@ export async function connectToMcpServer(
// If we didn't get the header from the error string, try to get it from the server
if (!wwwAuthenticate && hasNetworkTransport(mcpServerConfig)) {
console.log(
debugLogger.log(
`No www-authenticate header in error, trying to fetch it from server...`,
);
try {
@@ -880,13 +881,13 @@ export async function connectToMcpServer(
if (response.status === 401) {
wwwAuthenticate = response.headers.get('www-authenticate');
if (wwwAuthenticate) {
console.log(
debugLogger.log(
`Found www-authenticate header from server: ${wwwAuthenticate}`,
);
}
}
} catch (fetchError) {
console.debug(
debugLogger.debug(
`Failed to fetch www-authenticate header: ${getErrorMessage(
fetchError,
)}`,
@@ -895,7 +896,7 @@ export async function connectToMcpServer(
}
if (wwwAuthenticate) {
console.log(
debugLogger.log(
`Received 401 with www-authenticate header: ${wwwAuthenticate}`,
);
@@ -907,7 +908,7 @@ export async function connectToMcpServer(
);
if (oauthSuccess) {
// Retry connection with OAuth token
console.log(
debugLogger.log(
`Retrying connection to '${mcpServerName}' with OAuth token...`,
);
@@ -1000,12 +1001,12 @@ export async function connectToMcpServer(
},
);
if (hasStoredTokens) {
console.log(
debugLogger.log(
`Stored OAuth token for SSE server '${mcpServerName}' was rejected. ` +
`Please re-authenticate using: /mcp auth ${mcpServerName}`,
);
} else {
console.log(
debugLogger.log(
`401 error received for SSE server '${mcpServerName}' without OAuth configuration. ` +
`Please authenticate using: /mcp auth ${mcpServerName}`,
);
@@ -1018,7 +1019,9 @@ export async function connectToMcpServer(
}
// For SSE/HTTP servers, try to discover OAuth configuration from the base URL
console.log(`🔍 Attempting OAuth discovery for '${mcpServerName}'...`);
debugLogger.log(
`🔍 Attempting OAuth discovery for '${mcpServerName}'...`,
);
if (hasNetworkTransport(mcpServerConfig)) {
const serverUrl = new URL(
@@ -1030,7 +1033,7 @@ export async function connectToMcpServer(
// Try to discover OAuth configuration from the base URL
const oauthConfig = await OAuthUtils.discoverOAuthConfig(baseUrl);
if (oauthConfig) {
console.log(
debugLogger.log(
`Discovered OAuth configuration from base URL for server '${mcpServerName}'`,
);
@@ -1046,7 +1049,7 @@ export async function connectToMcpServer(
// Pass the server URL for proper discovery
const authServerUrl =
mcpServerConfig.httpUrl || mcpServerConfig.url;
console.log(
debugLogger.log(
`Starting OAuth authentication for server '${mcpServerName}'...`,
);
const authProvider = new MCPOAuthProvider(
@@ -1257,7 +1260,9 @@ export async function createTransport(
if (accessToken) {
hasOAuthConfig = true;
console.log(`Found stored OAuth token for server '${mcpServerName}'`);
debugLogger.log(
`Found stored OAuth token for server '${mcpServerName}'`,
);
}
}
}
@@ -1322,7 +1327,10 @@ export async function createTransport(
if (debugMode) {
transport.stderr!.on('data', (data) => {
const stderrStr = data.toString().trim();
console.debug(`[DEBUG] [MCP STDERR (${mcpServerName})]: `, stderrStr);
debugLogger.debug(
`[DEBUG] [MCP STDERR (${mcpServerName})]: `,
stderrStr,
);
});
}
return transport;
@@ -1340,7 +1348,7 @@ export function isEnabled(
mcpServerConfig: MCPServerConfig,
): boolean {
if (!funcDecl.name) {
console.warn(
debugLogger.warn(
`Discovered a function declaration without a name from MCP server '${mcpServerName}'. Skipping.`,
);
return false;
@@ -733,8 +733,6 @@ Content of file[1]
const invocation = tool.build({ paths: files });
await invocation.execute(new AbortController().signal);
console.log('Execution order:', executionOrder);
// Verify concurrent execution pattern
// In parallel execution: all "start:" events should come before all "end:" events
// In sequential execution: "start:file1", "end:file1", "start:file2", "end:file2", etc.
+2 -1
View File
@@ -19,6 +19,7 @@ import type { Config } from '../config/config.js';
import { fileExists } from '../utils/fileUtils.js';
import { Storage } from '../config/storage.js';
import { GREP_TOOL_NAME } from './tool-names.js';
import { debugLogger } from '../utils/debugLogger.js';
const DEFAULT_TOTAL_MAX_MATCHES = 20000;
@@ -179,7 +180,7 @@ class GrepToolInvocation extends BaseToolInvocation<
const totalMaxMatches = DEFAULT_TOTAL_MAX_MATCHES;
if (this.config.getDebugMode()) {
console.log(`[GrepTool] Total result limit: ${totalMaxMatches}`);
debugLogger.log(`[GrepTool] Total result limit: ${totalMaxMatches}`);
}
for (const searchDir of searchDirectories) {
+3 -2
View File
@@ -39,6 +39,7 @@ import { logSmartEditCorrectionEvent } from '../telemetry/loggers.js';
import { correctPath } from '../utils/pathCorrector.js';
import { EDIT_TOOL_NAME, READ_FILE_TOOL_NAME } from './tool-names.js';
import { debugLogger } from '../utils/debugLogger.js';
interface ReplacementContext {
params: EditToolParams;
currentContent: string;
@@ -616,12 +617,12 @@ class EditToolInvocation implements ToolInvocation<EditToolParams, ToolResult> {
throw error;
}
const errorMsg = error instanceof Error ? error.message : String(error);
console.log(`Error preparing edit: ${errorMsg}`);
debugLogger.log(`Error preparing edit: ${errorMsg}`);
return false;
}
if (editData.error) {
console.log(`Error: ${editData.error.display}`);
debugLogger.log(`Error: ${editData.error.display}`);
return false;
}
+3 -2
View File
@@ -21,6 +21,7 @@ import { parse } from 'shell-quote';
import { ToolErrorType } from './tool-error.js';
import { safeJsonStringify } from '../utils/safeJsonStringify.js';
import type { EventEmitter } from 'node:events';
import { debugLogger } from '../utils/debugLogger.js';
type ToolParams = Record<string, unknown>;
@@ -187,7 +188,7 @@ export class ToolRegistry {
tool = tool.asFullyQualifiedTool();
} else {
// Decide on behavior: throw error, log warning, or allow overwrite
console.warn(
debugLogger.warn(
`Tool with name "${tool.name}" is already registered. Overwriting.`,
);
}
@@ -379,7 +380,7 @@ export class ToolRegistry {
// register each function as a tool
for (const func of functions) {
if (!func.name) {
console.warn('Discovered a tool with no name. Skipping.');
debugLogger.warn('Discovered a tool with no name. Skipping.');
continue;
}
const parameters =
+3 -2
View File
@@ -32,6 +32,7 @@ import {
WebFetchFallbackAttemptEvent,
} from '../telemetry/index.js';
import { WEB_FETCH_TOOL_NAME } from './tool-names.js';
import { debugLogger } from '../utils/debugLogger.js';
const URL_FETCH_TIMEOUT_MS = 10000;
const MAX_CONTENT_LENGTH = 100000;
@@ -274,7 +275,7 @@ ${textContent}
DEFAULT_GEMINI_FLASH_MODEL,
);
console.debug(
debugLogger.debug(
`[WebFetchTool] Full response for prompt "${userPrompt.substring(
0,
50,
@@ -367,7 +368,7 @@ ${sourceListFormatted.join('\n')}`;
const llmContent = responseText;
console.debug(
debugLogger.debug(
`[WebFetchTool] Formatted tool response for prompt "${userPrompt}:\n\n":`,
llmContent,
);
+4 -2
View File
@@ -8,11 +8,13 @@ import * as fs from 'node:fs/promises';
import * as path from 'node:path';
import type { FileDiscoveryService } from '../services/fileDiscoveryService.js';
import type { FileFilteringOptions } from '../config/constants.js';
import { debugLogger } from './debugLogger.js';
// Simple console logger for now.
// TODO: Integrate with a more robust server-side logger.
const logger = {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
debug: (...args: any[]) => console.debug('[DEBUG] [BfsFileSearch]', ...args),
debug: (...args: any[]) =>
debugLogger.debug('[DEBUG] [BfsFileSearch]', ...args),
};
interface BfsFileSearchOptions {
@@ -84,7 +86,7 @@ export async function bfsFileSearch(
} catch (error) {
// Warn user that a directory could not be read, as this affects search results.
const message = (error as Error)?.message ?? 'Unknown error';
console.warn(
debugLogger.warn(
`[WARN] Skipping unreadable directory: ${currentDir} (${message})`,
);
if (debug) {
+3 -2
View File
@@ -14,6 +14,7 @@ import type { FileSystemService } from '../services/fileSystemService.js';
import { ToolErrorType } from '../tools/tool-error.js';
import { BINARY_EXTENSIONS } from './ignorePatterns.js';
import { createRequire as createModuleRequire } from 'node:module';
import { debugLogger } from './debugLogger.js';
const requireModule = createModuleRequire(import.meta.url);
@@ -260,7 +261,7 @@ export async function isBinaryFile(filePath: string): Promise<boolean> {
// If >30% non-printable characters, consider it binary
return nonPrintableCount / bytesRead > 0.3;
} catch (error) {
console.warn(
debugLogger.warn(
`Failed to check if file is binary: ${filePath}`,
error instanceof Error ? error.message : String(error),
);
@@ -270,7 +271,7 @@ export async function isBinaryFile(filePath: string): Promise<boolean> {
try {
await fh.close();
} catch (closeError) {
console.warn(
debugLogger.warn(
`Failed to close file handle for: ${filePath}`,
closeError instanceof Error ? closeError.message : String(closeError),
);
@@ -11,6 +11,7 @@ import { getErrorMessage, isNodeError } from './errors.js';
import type { FileDiscoveryService } from '../services/fileDiscoveryService.js';
import type { FileFilteringOptions } from '../config/constants.js';
import { DEFAULT_FILE_FILTERING_OPTIONS } from '../config/constants.js';
import { debugLogger } from './debugLogger.js';
const MAX_ITEMS = 200;
const TRUNCATION_INDICATOR = '...';
@@ -104,7 +105,7 @@ async function readFullStructure(
isNodeError(error) &&
(error.code === 'EACCES' || error.code === 'ENOENT')
) {
console.warn(
debugLogger.warn(
`Warning: Could not read directory ${currentPath}: ${error.message}`,
);
if (currentPath === rootPath && error.code === 'ENOENT') {
+2 -1
View File
@@ -10,6 +10,7 @@ import { type BaseLlmClient } from '../core/baseLlmClient.js';
import { LruCache } from './LruCache.js';
import { DEFAULT_GEMINI_FLASH_MODEL } from '../config/models.js';
import { promptIdContext } from './promptIdContext.js';
import { debugLogger } from './debugLogger.js';
const MAX_CACHE_SIZE = 50;
@@ -112,7 +113,7 @@ export async function FixLLMEditWithInstruction(
let promptId = promptIdContext.getStore();
if (!promptId) {
promptId = `llm-fixer-fallback-${Date.now()}-${Math.random().toString(16).slice(2)}`;
console.warn(
debugLogger.warn(
`Could not find promptId in context. This is unexpected. Using a fallback ID: ${promptId}`,
);
}
+4 -2
View File
@@ -16,15 +16,17 @@ import type { FileFilteringOptions } from '../config/constants.js';
import { DEFAULT_MEMORY_FILE_FILTERING_OPTIONS } from '../config/constants.js';
import { GEMINI_DIR } from './paths.js';
import type { GeminiCLIExtension } from '../config/config.js';
import { debugLogger } from './debugLogger.js';
// Simple console logger, similar to the one previously in CLI's config.ts
// TODO: Integrate with a more robust server-side logger if available/appropriate.
const logger = {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
debug: (...args: any[]) =>
console.debug('[DEBUG] [MemoryDiscovery]', ...args),
debugLogger.debug('[DEBUG] [MemoryDiscovery]', ...args),
// eslint-disable-next-line @typescript-eslint/no-explicit-any
warn: (...args: any[]) => console.warn('[WARN] [MemoryDiscovery]', ...args),
warn: (...args: any[]) =>
debugLogger.warn('[WARN] [MemoryDiscovery]', ...args),
// eslint-disable-next-line @typescript-eslint/no-explicit-any
error: (...args: any[]) =>
console.error('[ERROR] [MemoryDiscovery]', ...args),
@@ -8,14 +8,16 @@ import * as fs from 'node:fs/promises';
import * as path from 'node:path';
import { isSubpath } from './paths.js';
import { marked, type Token } from 'marked';
import { debugLogger } from './debugLogger.js';
// Simple console logger for import processing
const logger = {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
debug: (...args: any[]) =>
console.debug('[DEBUG] [ImportProcessor]', ...args),
debugLogger.debug('[DEBUG] [ImportProcessor]', ...args),
// eslint-disable-next-line @typescript-eslint/no-explicit-any
warn: (...args: any[]) => console.warn('[WARN] [ImportProcessor]', ...args),
warn: (...args: any[]) =>
debugLogger.warn('[WARN] [ImportProcessor]', ...args),
// eslint-disable-next-line @typescript-eslint/no-explicit-any
error: (...args: any[]) =>
console.error('[ERROR] [ImportProcessor]', ...args),
@@ -9,6 +9,7 @@ import { DEFAULT_GEMINI_FLASH_MODEL } from '../config/models.js';
import type { BaseLlmClient } from '../core/baseLlmClient.js';
import type { GeminiChat } from '../core/geminiChat.js';
import { isFunctionResponse } from './messageInspectors.js';
import { debugLogger } from './debugLogger.js';
const CHECK_PROMPT = `Analyze *only* the content and structure of your immediately preceding response (your last turn in the conversation history). Based *strictly* on that response, determine who should logically speak next: the 'user' or the 'model' (you).
**Decision Rules (apply in order):**
@@ -126,7 +127,7 @@ export async function checkNextSpeaker(
}
return null;
} catch (error) {
console.warn(
debugLogger.warn(
'Failed to talk to Gemini endpoint when seeing if conversation should continue.',
error,
);
+2 -1
View File
@@ -10,6 +10,7 @@ import { ApiError } from '@google/genai';
import type { HttpError } from './retry.js';
import { retryWithBackoff } from './retry.js';
import { setSimulate429 } from './testUtils.js';
import { debugLogger } from './debugLogger.js';
// Helper to create a mock function that fails a certain number of times
const createFailingFunction = (
@@ -43,7 +44,7 @@ describe('retryWithBackoff', () => {
// Disable 429 simulation for tests
setSimulate429(false);
// Suppress unhandled promise rejection warnings for tests that expect errors
console.warn = vi.fn();
debugLogger.warn = vi.fn();
});
afterEach(() => {
+9 -8
View File
@@ -12,6 +12,7 @@ import {
isGenericQuotaExceededError,
} from './quotaErrorDetection.js';
import { delay, createAbortError } from './delay.js';
import { debugLogger } from './debugLogger.js';
const FETCH_FAILED_MESSAGE =
'exception TypeError: fetch failed sending request';
@@ -168,7 +169,7 @@ export async function retryWithBackoff<T>(
}
} catch (fallbackError) {
// If fallback fails, continue with original error
console.warn('Fallback to Flash model failed:', fallbackError);
debugLogger.warn('Fallback to Flash model failed:', fallbackError);
}
}
@@ -195,7 +196,7 @@ export async function retryWithBackoff<T>(
}
} catch (fallbackError) {
// If fallback fails, continue with original error
console.warn('Fallback to Flash model failed:', fallbackError);
debugLogger.warn('Fallback to Flash model failed:', fallbackError);
}
}
@@ -227,7 +228,7 @@ export async function retryWithBackoff<T>(
}
} catch (fallbackError) {
// If fallback fails, continue with original error
console.warn('Fallback to Flash model failed:', fallbackError);
debugLogger.warn('Fallback to Flash model failed:', fallbackError);
}
}
@@ -244,7 +245,7 @@ export async function retryWithBackoff<T>(
if (delayDurationMs > 0) {
// Respect Retry-After header if present and parsed
console.warn(
debugLogger.warn(
`Attempt ${attempt} failed with status ${delayErrorStatus ?? 'unknown'}. Retrying after explicit delay of ${delayDurationMs}ms...`,
error,
);
@@ -367,13 +368,13 @@ function logRetryAttempt(
}
if (errorStatus === 429) {
console.warn(message, error);
debugLogger.warn(message, error);
} else if (errorStatus && errorStatus >= 500 && errorStatus < 600) {
console.error(message, error);
} else if (error instanceof Error) {
// Fallback for errors that might not have a status but have a message
if (error.message.includes('429')) {
console.warn(
debugLogger.warn(
`Attempt ${attempt} failed with 429 error (no Retry-After header). Retrying with backoff...`,
error,
);
@@ -383,9 +384,9 @@ function logRetryAttempt(
error,
);
} else {
console.warn(message, error); // Default to warn for other errors
debugLogger.warn(message, error); // Default to warn for other errors
}
} else {
console.warn(message, error); // Default to warn if error type is unknown
debugLogger.warn(message, error); // Default to warn if error type is unknown
}
}
+5 -4
View File
@@ -7,6 +7,7 @@
import { execSync } from 'node:child_process';
import os from 'node:os';
import { detect as chardetDetect } from 'chardet';
import { debugLogger } from './debugLogger.js';
// Cache for system encoding to avoid repeated detection
// Use undefined to indicate "not yet checked" vs null meaning "checked but failed"
@@ -66,7 +67,7 @@ export function getSystemEncoding(): string | null {
`Unable to parse Windows code page from 'chcp' output "${output.trim()}". `,
);
} catch (error) {
console.warn(
debugLogger.warn(
`Failed to get Windows code page using 'chcp' command: ${error instanceof Error ? error.message : String(error)}. ` +
`Will attempt to detect encoding from command output instead.`,
);
@@ -88,7 +89,7 @@ export function getSystemEncoding(): string | null {
.toString()
.trim();
} catch (_e) {
console.warn('Failed to get locale charmap.');
debugLogger.warn('Failed to get locale charmap.');
return null;
}
}
@@ -141,7 +142,7 @@ export function windowsCodePageToEncoding(cp: number): string | null {
return map[cp];
}
console.warn(`Unable to determine encoding for windows code page ${cp}.`);
debugLogger.warn(`Unable to determine encoding for windows code page ${cp}.`);
return null; // Return null if no mapping found
}
@@ -159,7 +160,7 @@ export function detectEncodingFromBuffer(buffer: Buffer): string | null {
return detected.toLowerCase();
}
} catch (error) {
console.warn('Failed to detect encoding with chardet:', error);
debugLogger.warn('Failed to detect encoding with chardet:', error);
}
return null;
@@ -7,6 +7,7 @@
import path from 'node:path';
import { promises as fsp, readFileSync } from 'node:fs';
import { Storage } from '../config/storage.js';
import { debugLogger } from './debugLogger.js';
interface UserAccounts {
active: string | null;
@@ -33,7 +34,7 @@ export class UserAccountManager {
// Inlined validation logic
if (typeof parsed !== 'object' || parsed === null) {
console.log('Invalid accounts file schema, starting fresh.');
debugLogger.log('Invalid accounts file schema, starting fresh.');
return defaultState;
}
const { active, old } = parsed as Partial<UserAccounts>;
@@ -43,7 +44,7 @@ export class UserAccountManager {
(Array.isArray(old) && old.every((i) => typeof i === 'string')));
if (!isValid) {
console.log('Invalid accounts file schema, starting fresh.');
debugLogger.log('Invalid accounts file schema, starting fresh.');
return defaultState;
}
@@ -66,7 +67,10 @@ export class UserAccountManager {
) {
return defaultState;
}
console.log('Error during sync read of accounts, starting fresh.', error);
debugLogger.log(
'Error during sync read of accounts, starting fresh.',
error,
);
return defaultState;
}
}
@@ -84,7 +88,7 @@ export class UserAccountManager {
) {
return defaultState;
}
console.log('Could not parse accounts file, starting fresh.', error);
debugLogger.log('Could not parse accounts file, starting fresh.', error);
return defaultState;
}
}
+2 -1
View File
@@ -8,6 +8,7 @@ import { isNodeError } from '../utils/errors.js';
import * as fs from 'node:fs';
import * as path from 'node:path';
import * as process from 'node:process';
import { debugLogger } from './debugLogger.js';
export type Unsubscribe = () => void;
@@ -72,7 +73,7 @@ export class WorkspaceContext {
this.directories.add(resolved);
this.notifyDirectoriesChanged();
} catch (err) {
console.warn(
debugLogger.warn(
`[WARN] Skipping unreadable directory: ${directory} (${err instanceof Error ? err.message : String(err)})`,
);
}