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 <noreply@anthropic.com>
This commit is contained in:
Anton Evseev 2026-03-03 20:05:04 +10:00 committed by GitHub
parent e407f4bc55
commit 5aff8e0c70
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 68 additions and 60 deletions

View file

@ -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<void> {
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<void> {
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<string, string> = {};
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<RecallResponse>;
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}
</hindsight_memories>`;
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) {

View file

@ -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 {