From 5aff8e0c70283ad234ded89570209eb59925de41 Mon Sep 17 00:00:00 2001 From: Anton Evseev <78427278+slayoffer@users.noreply.github.com> Date: Tue, 3 Mar 2026 20:05:04 +1000 Subject: [PATCH] refactor(openclaw): replace console.log with debug() helper gated by plugin config (#456) Replace ~73 console.log calls with a debug() helper that is silent by default. Debug output is now controlled via plugin config param (debug: true) instead of environment variables, making it easier for users to configure. Co-authored-by: Claude Opus 4.6 --- hindsight-integrations/openclaw/src/index.ts | 127 ++++++++++--------- hindsight-integrations/openclaw/src/types.ts | 1 + 2 files changed, 68 insertions(+), 60 deletions(-) diff --git a/hindsight-integrations/openclaw/src/index.ts b/hindsight-integrations/openclaw/src/index.ts index bce77118..0bde0462 100644 --- a/hindsight-integrations/openclaw/src/index.ts +++ b/hindsight-integrations/openclaw/src/index.ts @@ -4,6 +4,12 @@ import { HindsightClient, type HindsightClientOptions } from './client.js'; import { dirname } from 'path'; import { fileURLToPath } from 'url'; +// Debug logging: silent by default, enable with debug: true in plugin config +let debugEnabled = false; +const debug = (...args: unknown[]) => { + if (debugEnabled) console.log(...args); +}; + // Module-level state let embedManager: HindsightEmbedManager | null = null; let client: HindsightClient | null = null; @@ -53,7 +59,7 @@ async function lazyReinit(): Promise { return; // Only external API mode supports lazy reinit } - console.log('[Hindsight] Attempting lazy re-initialization...'); + debug('[Hindsight] Attempting lazy re-initialization...'); try { await checkExternalApiHealth(externalApi.apiUrl, externalApi.apiToken); @@ -76,7 +82,7 @@ async function lazyReinit(): Promise { isInitialized = true; // Replace the rejected initPromise with a resolved one initPromise = Promise.resolve(); - console.log('[Hindsight] ✓ Lazy re-initialization succeeded'); + debug('[Hindsight] ✓ Lazy re-initialization succeeded'); } catch (error) { console.warn(`[Hindsight] Lazy re-initialization failed (will retry in ${REINIT_COOLDOWN_MS / 1000}s):`, error instanceof Error ? error.message : error); } finally { @@ -119,7 +125,7 @@ if (typeof global !== 'undefined') { try { await client.setBankMission(config.bankMission); banksWithMissionSet.add(bankId); - console.log(`[Hindsight] Set mission for new bank: ${bankId}`); + debug(`[Hindsight] Set mission for new bank: ${bankId}`); } catch (error) { // Log but don't fail - bank mission is not critical console.warn(`[Hindsight] Could not set bank mission for ${bankId}: ${error}`); @@ -402,7 +408,7 @@ async function checkExternalApiHealth(apiUrl: string, apiToken?: string | null): for (let attempt = 1; attempt <= maxRetries; attempt++) { try { - console.log(`[Hindsight] Checking external API health at ${healthUrl}... (attempt ${attempt}/${maxRetries})`); + debug(`[Hindsight] Checking external API health at ${healthUrl}... (attempt ${attempt}/${maxRetries})`); const headers: Record = {}; if (apiToken) { headers['Authorization'] = `Bearer ${apiToken}`; @@ -412,11 +418,11 @@ async function checkExternalApiHealth(apiUrl: string, apiToken?: string | null): throw new Error(`HTTP ${response.status}: ${response.statusText}`); } const data = await response.json() as { status?: string }; - console.log(`[Hindsight] External API health: ${JSON.stringify(data)}`); + debug(`[Hindsight] External API health: ${JSON.stringify(data)}`); return; } catch (error) { if (attempt < maxRetries) { - console.log(`[Hindsight] Health check attempt ${attempt} failed, retrying in ${retryDelay}ms...`); + debug(`[Hindsight] Health check attempt ${attempt} failed, retrying in ${retryDelay}ms...`); await new Promise(resolve => setTimeout(resolve, retryDelay)); } else { throw new Error(`Cannot connect to external Hindsight API at ${apiUrl}: ${error}`, { cause: error }); @@ -447,42 +453,43 @@ function getPluginConfig(api: MoltbotPluginAPI): PluginConfig { excludeProviders: Array.isArray(config.excludeProviders) ? config.excludeProviders : [], autoRecall: config.autoRecall !== false, // Default: true (on) — backward compatible retainEveryNTurns: config.retainEveryNTurns, + debug: config.debug ?? false, }; } export default function (api: MoltbotPluginAPI) { try { - console.log('[Hindsight] Plugin loading...'); + debug('[Hindsight] Plugin loading...'); - // Get plugin config first (needed for LLM detection) - console.log('[Hindsight] Getting plugin config...'); + // Get plugin config first (needed for LLM detection and debug flag) const pluginConfig = getPluginConfig(api); + debugEnabled = pluginConfig.debug ?? false; // Store config globally for bank ID derivation in hooks currentPluginConfig = pluginConfig; // Detect LLM configuration (env vars > plugin config > auto-detect) - console.log('[Hindsight] Detecting LLM config...'); + debug('[Hindsight] Detecting LLM config...'); const llmConfig = detectLLMConfig(pluginConfig); const baseUrlInfo = llmConfig.baseUrl ? `, base URL: ${llmConfig.baseUrl}` : ''; const modelInfo = llmConfig.model || 'default'; if (llmConfig.provider === 'ollama') { - console.log(`[Hindsight] ✓ Using provider: ${llmConfig.provider}, model: ${modelInfo} (${llmConfig.source})`); + debug(`[Hindsight] ✓ Using provider: ${llmConfig.provider}, model: ${modelInfo} (${llmConfig.source})`); } else { - console.log(`[Hindsight] ✓ Using provider: ${llmConfig.provider}, model: ${modelInfo} (${llmConfig.source}${baseUrlInfo})`); + debug(`[Hindsight] ✓ Using provider: ${llmConfig.provider}, model: ${modelInfo} (${llmConfig.source}${baseUrlInfo})`); } if (pluginConfig.bankMission) { - console.log(`[Hindsight] Custom bank mission configured: "${pluginConfig.bankMission.substring(0, 50)}..."`); + debug(`[Hindsight] Custom bank mission configured: "${pluginConfig.bankMission.substring(0, 50)}..."`); } // Log dynamic bank ID mode if (pluginConfig.dynamicBankId) { const prefixInfo = pluginConfig.bankIdPrefix ? ` (prefix: ${pluginConfig.bankIdPrefix})` : ''; - console.log(`[Hindsight] ✓ Dynamic bank IDs enabled${prefixInfo} - each channel gets isolated memory`); + debug(`[Hindsight] ✓ Dynamic bank IDs enabled${prefixInfo} - each channel gets isolated memory`); } else { - console.log(`[Hindsight] Dynamic bank IDs disabled - using static bank: ${DEFAULT_BANK_NAME}`); + debug(`[Hindsight] Dynamic bank IDs disabled - using static bank: ${DEFAULT_BANK_NAME}`); } // Detect external API mode @@ -494,49 +501,49 @@ export default function (api: MoltbotPluginAPI) { if (externalApi.apiUrl) { // External API mode - skip local daemon usingExternalApi = true; - console.log(`[Hindsight] ✓ Using external API: ${externalApi.apiUrl}`); + debug(`[Hindsight] ✓ Using external API: ${externalApi.apiUrl}`); // Set env vars so CLI commands (uvx hindsight-embed) use external API process.env.HINDSIGHT_EMBED_API_URL = externalApi.apiUrl; if (externalApi.apiToken) { process.env.HINDSIGHT_EMBED_API_TOKEN = externalApi.apiToken; - console.log('[Hindsight] API token configured'); + debug('[Hindsight] API token configured'); } } else { - console.log(`[Hindsight] Daemon idle timeout: ${pluginConfig.daemonIdleTimeout}s (0 = never timeout)`); - console.log(`[Hindsight] API Port: ${apiPort}`); + debug(`[Hindsight] Daemon idle timeout: ${pluginConfig.daemonIdleTimeout}s (0 = never timeout)`); + debug(`[Hindsight] API Port: ${apiPort}`); } // Initialize in background (non-blocking) - console.log('[Hindsight] Starting initialization in background...'); + debug('[Hindsight] Starting initialization in background...'); initPromise = (async () => { try { if (usingExternalApi && externalApi.apiUrl) { // External API mode - check health, skip daemon startup - console.log('[Hindsight] External API mode - skipping local daemon...'); + debug('[Hindsight] External API mode - skipping local daemon...'); await checkExternalApiHealth(externalApi.apiUrl, externalApi.apiToken); // Initialize client with direct HTTP mode - console.log('[Hindsight] Creating HindsightClient (HTTP mode)...'); + debug('[Hindsight] Creating HindsightClient (HTTP mode)...'); client = new HindsightClient(buildClientOptions(llmConfig, pluginConfig, externalApi)); // Set default bank (will be overridden per-request when dynamic bank IDs are enabled) const defaultBankId = deriveBankId(undefined, pluginConfig); - console.log(`[Hindsight] Default bank: ${defaultBankId}`); + debug(`[Hindsight] Default bank: ${defaultBankId}`); client.setBankId(defaultBankId); // Note: Bank mission will be set per-bank when dynamic bank IDs are enabled // For now, set it on the default bank if (pluginConfig.bankMission && !pluginConfig.dynamicBankId) { - console.log(`[Hindsight] Setting bank mission...`); + debug(`[Hindsight] Setting bank mission...`); await client.setBankMission(pluginConfig.bankMission); } isInitialized = true; - console.log('[Hindsight] ✓ Ready (external API mode)'); + debug('[Hindsight] ✓ Ready (external API mode)'); } else { // Local daemon mode - start hindsight-embed daemon - console.log('[Hindsight] Creating HindsightEmbedManager...'); + debug('[Hindsight] Creating HindsightEmbedManager...'); embedManager = new HindsightEmbedManager( apiPort, llmConfig.provider, @@ -549,27 +556,27 @@ export default function (api: MoltbotPluginAPI) { ); // Start the embedded server - console.log('[Hindsight] Starting embedded server...'); + debug('[Hindsight] Starting embedded server...'); await embedManager.start(); // Initialize client (local daemon mode — no apiUrl) - console.log('[Hindsight] Creating HindsightClient (subprocess mode)...'); + debug('[Hindsight] Creating HindsightClient (subprocess mode)...'); client = new HindsightClient(buildClientOptions(llmConfig, pluginConfig, { apiUrl: null, apiToken: null })); // Set default bank (will be overridden per-request when dynamic bank IDs are enabled) const defaultBankId = deriveBankId(undefined, pluginConfig); - console.log(`[Hindsight] Default bank: ${defaultBankId}`); + debug(`[Hindsight] Default bank: ${defaultBankId}`); client.setBankId(defaultBankId); // Note: Bank mission will be set per-bank when dynamic bank IDs are enabled // For now, set it on the default bank if (pluginConfig.bankMission && !pluginConfig.dynamicBankId) { - console.log(`[Hindsight] Setting bank mission...`); + debug(`[Hindsight] Setting bank mission...`); await client.setBankMission(pluginConfig.bankMission); } isInitialized = true; - console.log('[Hindsight] ✓ Ready'); + debug('[Hindsight] ✓ Ready'); } } catch (error) { console.error('[Hindsight] Initialization error:', error); @@ -581,11 +588,11 @@ export default function (api: MoltbotPluginAPI) { initPromise.catch(() => {}); // Register background service for cleanup - console.log('[Hindsight] Registering service...'); + debug('[Hindsight] Registering service...'); api.registerService({ id: 'hindsight-memory', async start() { - console.log('[Hindsight] Service start called...'); + debug('[Hindsight] Service start called...'); // Wait for background init if still pending if (initPromise) { @@ -603,7 +610,7 @@ export default function (api: MoltbotPluginAPI) { if (externalApi.apiUrl && isInitialized) { try { await checkExternalApiHealth(externalApi.apiUrl, externalApi.apiToken); - console.log('[Hindsight] External API is healthy'); + debug('[Hindsight] External API is healthy'); return; } catch (error) { console.error('[Hindsight] External API health check failed:', error); @@ -617,11 +624,11 @@ export default function (api: MoltbotPluginAPI) { if (embedManager && isInitialized) { const healthy = await embedManager.checkHealth(); if (healthy) { - console.log('[Hindsight] Daemon is healthy'); + debug('[Hindsight] Daemon is healthy'); return; } - console.log('[Hindsight] Daemon is not responding - reinitializing...'); + debug('[Hindsight] Daemon is not responding - reinitializing...'); // Reset state for reinitialization embedManager = null; client = null; @@ -631,7 +638,7 @@ export default function (api: MoltbotPluginAPI) { // Reinitialize if needed (fresh start or recovery) if (!isInitialized) { - console.log('[Hindsight] Reinitializing...'); + debug('[Hindsight] Reinitializing...'); const reinitPluginConfig = getPluginConfig(api); currentPluginConfig = reinitPluginConfig; const llmConfig = detectLLMConfig(reinitPluginConfig); @@ -657,7 +664,7 @@ export default function (api: MoltbotPluginAPI) { } isInitialized = true; - console.log('[Hindsight] Reinitialization complete (external API mode)'); + debug('[Hindsight] Reinitialization complete (external API mode)'); } else { // Local daemon mode embedManager = new HindsightEmbedManager( @@ -682,14 +689,14 @@ export default function (api: MoltbotPluginAPI) { } isInitialized = true; - console.log('[Hindsight] Reinitialization complete'); + debug('[Hindsight] Reinitialization complete'); } } }, async stop() { try { - console.log('[Hindsight] Service stopping...'); + debug('[Hindsight] Service stopping...'); // Only stop daemon if in local mode if (!usingExternalApi && embedManager) { @@ -700,7 +707,7 @@ export default function (api: MoltbotPluginAPI) { client = null; isInitialized = false; - console.log('[Hindsight] Service stopped'); + debug('[Hindsight] Service stopped'); } catch (error) { console.error('[Hindsight] Service stop error:', error); throw error; @@ -708,10 +715,10 @@ export default function (api: MoltbotPluginAPI) { }, }); - console.log('[Hindsight] Plugin loaded successfully'); + debug('[Hindsight] Plugin loaded successfully'); // Register agent hooks for auto-recall and auto-retention - console.log('[Hindsight] Registering agent hooks...'); + debug('[Hindsight] Registering agent hooks...'); // Store session key and context for retention let currentSessionKey: string | undefined; @@ -729,19 +736,19 @@ export default function (api: MoltbotPluginAPI) { // Check if this provider is excluded if (ctx?.messageProvider && pluginConfig.excludeProviders?.includes(ctx.messageProvider)) { - console.log(`[Hindsight] Skipping recall for excluded provider: ${ctx.messageProvider}`); + debug(`[Hindsight] Skipping recall for excluded provider: ${ctx.messageProvider}`); return; } // Skip auto-recall when disabled (agent has its own recall tool) if (!pluginConfig.autoRecall) { - console.log('[Hindsight] Auto-recall disabled via config, skipping'); + debug('[Hindsight] Auto-recall disabled via config, skipping'); return; } // Derive bank ID from context const bankId = deriveBankId(ctx, pluginConfig); - console.log(`[Hindsight] before_agent_start - bank: ${bankId}, channel: ${ctx?.messageProvider}/${ctx?.channelId}`); + debug(`[Hindsight] before_agent_start - bank: ${bankId}, channel: ${ctx?.messageProvider}/${ctx?.channelId}`); // Get the user's latest message for recall — only the raw user text, not the full prompt // rawMessage is clean user text; prompt includes envelope, system events, media notes, etc. @@ -760,7 +767,7 @@ export default function (api: MoltbotPluginAPI) { // Wait for client to be ready const clientGlobal = (global as any).__hindsightClient; if (!clientGlobal) { - console.log('[Hindsight] Client global not available, skipping auto-recall'); + debug('[Hindsight] Client global not available, skipping auto-recall'); return; } @@ -769,18 +776,18 @@ export default function (api: MoltbotPluginAPI) { // Get client configured for this context's bank (async to handle mission setup) const client = await clientGlobal.getClientForContext(ctx); if (!client) { - console.log('[Hindsight] Client not initialized, skipping auto-recall'); + debug('[Hindsight] Client not initialized, skipping auto-recall'); return; } - console.log(`[Hindsight] Auto-recall for bank ${bankId}, prompt: ${prompt.substring(0, 50)}`); + debug(`[Hindsight] Auto-recall for bank ${bankId}, prompt: ${prompt.substring(0, 50)}`); // Recall with deduplication: reuse in-flight request for same bank const recallKey = bankId; const existing = inflightRecalls.get(recallKey); let recallPromise: Promise; if (existing) { - console.log(`[Hindsight] Reusing in-flight recall for bank ${bankId}`); + debug(`[Hindsight] Reusing in-flight recall for bank ${bankId}`); recallPromise = existing; } else { recallPromise = client.recall({ query: prompt, max_tokens: 2048 }, RECALL_TIMEOUT_MS); @@ -791,7 +798,7 @@ export default function (api: MoltbotPluginAPI) { const response = await recallPromise; if (!response.results || response.results.length === 0) { - console.log('[Hindsight] No memories found for auto-recall'); + debug('[Hindsight] No memories found for auto-recall'); return; } @@ -805,7 +812,7 @@ ${memoriesJson} User message: ${prompt} `; - console.log(`[Hindsight] Auto-recall: Injecting ${response.results.length} memories from bank ${bankId}`); + debug(`[Hindsight] Auto-recall: Injecting ${response.results.length} memories from bank ${bankId}`); // Inject context before the user message return { prependContext: contextMessage }; @@ -829,17 +836,17 @@ User message: ${prompt} // Check if this provider is excluded if (effectiveCtx?.messageProvider && pluginConfig.excludeProviders?.includes(effectiveCtx.messageProvider)) { - console.log(`[Hindsight] Skipping retain for excluded provider: ${effectiveCtx.messageProvider}`); + debug(`[Hindsight] Skipping retain for excluded provider: ${effectiveCtx.messageProvider}`); return; } // Derive bank ID from context const bankId = deriveBankId(effectiveCtx, pluginConfig); - console.log(`[Hindsight Hook] agent_end triggered - bank: ${bankId}`); + debug(`[Hindsight Hook] agent_end triggered - bank: ${bankId}`); // Check event success and messages if (!event.success || !Array.isArray(event.messages) || event.messages.length === 0) { - console.log('[Hindsight Hook] Skipping: success:', event.success, 'messages:', event.messages?.length); + debug('[Hindsight Hook] Skipping: success:', event.success, 'messages:', event.messages?.length); return; } @@ -870,14 +877,14 @@ User message: ${prompt} if (turnCount % retainEveryN !== 0) { const nextRetain = Math.ceil(turnCount / retainEveryN) * retainEveryN; - console.log(`[Hindsight Hook] Skipping retain (turn ${turnCount}, next at ${nextRetain})`); + debug(`[Hindsight Hook] Skipping retain (turn ${turnCount}, next at ${nextRetain})`); return; } // Sliding window: N turns of new content + 2-turn overlap for context continuity const windowSize = retainEveryN * 2 + 4; messagesToRetain = event.messages.slice(-windowSize); - console.log(`[Hindsight Hook] Chunked retain at turn ${turnCount} \u2014 last ${messagesToRetain.length} msgs`); + debug(`[Hindsight Hook] Chunked retain at turn ${turnCount} \u2014 last ${messagesToRetain.length} msgs`); } // Format messages into a transcript @@ -904,7 +911,7 @@ User message: ${prompt} .join('\n\n'); if (!transcript.trim() || transcript.length < 10) { - console.log('[Hindsight Hook] Transcript too short, skipping'); + debug('[Hindsight Hook] Transcript too short, skipping'); return; } @@ -925,12 +932,12 @@ User message: ${prompt} }, }); - console.log(`[Hindsight] Retained ${messagesToRetain.length} messages to bank ${bankId} for session ${documentId}`); + debug(`[Hindsight] Retained ${messagesToRetain.length} messages to bank ${bankId} for session ${documentId}`); } catch (error) { console.error('[Hindsight] Error retaining messages:', error); } }); - console.log('[Hindsight] Hooks registered'); + debug('[Hindsight] Hooks registered'); } catch (error) { console.error('[Hindsight] Plugin loading error:', error); if (error instanceof Error) { diff --git a/hindsight-integrations/openclaw/src/types.ts b/hindsight-integrations/openclaw/src/types.ts index aa9a6b17..b0de1fdc 100644 --- a/hindsight-integrations/openclaw/src/types.ts +++ b/hindsight-integrations/openclaw/src/types.ts @@ -45,6 +45,7 @@ export interface PluginConfig { excludeProviders?: string[]; // Message providers to exclude from recall/retain (e.g. ['telegram', 'discord']) autoRecall?: boolean; // Auto-recall memories on every prompt (default: true). Set to false when agent has its own recall tool. retainEveryNTurns?: number; // Retain every Nth turn instead of every turn (default: 10). Reduces O(n²) storage growth for long sessions. + debug?: boolean; // Enable debug logging (default: false) } export interface ServiceConfig {