/** * Query Logger for NotebookLM MCP Server * * Provides persistent logging of all Q&A interactions with NotebookLM: * - Full question and answer content * - Session and notebook context * - Quota information at time of query * - Duration and metadata * * Features: * - JSONL format with daily rotation * - 90-day retention (configurable) * - Search and retrieval by session, notebook, date * - Full content storage for research review */ /** * Query log entry structure */ export interface QueryLogEntry { timestamp: string; queryId: string; sessionId: string; notebookId?: string; notebookUrl: string; notebookName?: string; question: string; answer: string; answerLength: number; durationMs: number; quotaInfo: { used: number; limit: number; remaining: number; tier: string; }; metadata?: Record; } /** * Query logger configuration */ export interface QueryLoggerConfig { enabled: boolean; logDir: string; retentionDays: number; } /** * Search options for query retrieval */ export interface QuerySearchOptions { limit?: number; startDate?: string; endDate?: string; caseSensitive?: boolean; } /** * Query Logger Class * * Logs all Q&A interactions to JSONL files for later review. */ export declare class QueryLogger { private static instances; private static processHandlersRegistered; private config; private currentLogFile; private writeQueue; private isWriting; /** * Dedicated scanner for on-disk log redaction. Uses `medium` minimum * severity so we don't redact legitimate base64 payloads (images, PDFs, * JWT payloads) that frequently appear in NotebookLM answers. Real * credentials live at critical/high/medium severity. * * ACCEPTED RISK (L11): the only `severity: "low"` rule in the scanner is the * "High Entropy String" pattern (/\b[A-Za-z0-9+/]{32,}={0,2}\b/, see * secrets-scanner.ts). It is DELIBERATELY NOT redacted at rest because * NotebookLM answers routinely contain long, high-entropy base64 that is NOT * a secret — inline image/PDF data-URIs, JWT payload segments, GCS object * names, CSRF tokens, document hashes. Redacting at "low" would shred this * legitimate research content (high false-positive rate) for marginal gain: * genuine credentials (API keys, bearer tokens, private keys, connection * strings) already match dedicated critical/high/medium rules and are * redacted regardless. The base64 false-positive cost outweighs the residual * risk of an unstructured low-confidence entropy hit slipping through, so the * threshold stays at "medium" by design. */ private scanner; private stats; constructor(config?: Partial); /** * Ensure query log directory exists */ private ensureLogDirectory; /** * Initialize log file for today */ private initializeLogFile; /** * Clean up old log files based on retention policy */ private cleanOldLogs; /** * Write entry to log file */ private writeEntry; /** * Determine the next sequence-suffixed log file for `date` when the base file (or a * prior rotation) has hit the size cap (M9). Scans the directory for the highest * existing query-log-DATE.NNN.jsonl suffix and returns the next one, so rotation is * correct across writers and restarts rather than relying on a per-process counter. */ private nextRotatedFile; /** * Log a query (Q&A pair). * * Question and answer text are passed through the secrets scanner before * persistence so leaked credentials (API keys, tokens, private keys) are * redacted at rest. The original in-memory entry is never mutated — only * the on-disk record is sanitized. */ logQuery(entry: Omit): Promise; /** * Get all queries for a specific session */ getQueriesForSession(sessionId: string): Promise; /** * Get all queries for a specific notebook URL */ getQueriesForNotebook(notebookUrl: string): Promise; /** * Get all queries for a specific notebook ID */ getQueriesForNotebookId(notebookId: string): Promise; /** * Get all queries for a specific date (YYYY-MM-DD) */ getQueriesForDate(date: string): Promise; /** * Get recent queries */ getRecentQueries(limit?: number): Promise; /** * Search queries by pattern in question or answer */ searchQueries(pattern: string, options?: QuerySearchOptions): Promise; /** * Get all available log files */ getLogFiles(): string[]; /** * Get statistics */ getStats(): typeof this.stats; /** * Force flush any pending writes */ flush(): Promise; private registerProcessHandlers; private static flushAllSync; private flushQueueSync; /** * Read and parse a log file */ private readLogFile; /** * Get all queries from all log files */ private getAllQueries; /** * Filter queries across all log files */ private filterQueries; } /** * Get or create the global query logger */ export declare function getQueryLogger(): QueryLogger; /** * Convenience function for quick query logging */ export declare function logQuery(entry: Omit): Promise;