feat(openclaw): retain last n+2 turns every n turns (default n=10) (#452)

This commit is contained in:
Fabio Scarsi 2026-03-02 09:44:52 +01:00 committed by GitHub
parent 55af468187
commit ad1660b313
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 26 additions and 3 deletions

View file

@ -20,6 +20,7 @@ const banksWithMissionSet = new Set<string>();
// In-flight recall deduplication: concurrent recalls for the same bank reuse one promise
import type { RecallResponse } from './types.js';
const inflightRecalls = new Map<string, Promise<RecallResponse>>();
const turnCountBySession = new Map<string, number>();
const RECALL_TIMEOUT_MS = 10_000;
// Cooldown + guard to prevent concurrent reinit attempts
@ -857,8 +858,29 @@ User message: ${prompt}
return;
}
// --- Chunked retention: only retain every Nth turn ---
const retainEveryN = pluginConfig.retainEveryNTurns ?? 10;
let messagesToRetain = event.messages;
if (retainEveryN > 1) {
const sessionTrackingKey = `${bankId}:${effectiveCtx?.sessionKey || currentSessionKey || 'session'}`;
const turnCount = (turnCountBySession.get(sessionTrackingKey) || 0) + 1;
turnCountBySession.set(sessionTrackingKey, turnCount);
if (turnCount % retainEveryN !== 0) {
const nextRetain = Math.ceil(turnCount / retainEveryN) * retainEveryN;
console.log(`[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`);
}
// Format messages into a transcript
const transcript = event.messages
const transcript = messagesToRetain
.map((msg: any) => {
const role = msg.role || 'unknown';
let content = '';
@ -895,14 +917,14 @@ User message: ${prompt}
document_id: documentId,
metadata: {
retained_at: new Date().toISOString(),
message_count: String(event.messages.length),
message_count: String(messagesToRetain.length),
channel_type: effectiveCtx?.messageProvider,
channel_id: effectiveCtx?.channelId,
sender_id: effectiveCtx?.senderId,
},
});
console.log(`[Hindsight] Retained ${event.messages.length} messages to bank ${bankId} for session ${documentId}`);
console.log(`[Hindsight] Retained ${messagesToRetain.length} messages to bank ${bankId} for session ${documentId}`);
} catch (error) {
console.error('[Hindsight] Error retaining messages:', error);
}

View file

@ -44,6 +44,7 @@ export interface PluginConfig {
bankIdPrefix?: string; // Prefix for bank IDs (e.g. 'prod' -> 'prod-slack-C123')
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.
}
export interface ServiceConfig {