src — session-pruning
src — session-pruning
The session-pruning module provides a robust and configurable system for automatically managing the lifecycle of "prunable items" such as messages, memories, and other session-related data. Its primary goal is to prevent unbounded growth of session data, ensuring efficient resource usage and adherence to defined retention policies.
This module allows developers to define rules based on various conditions (e.g., age, count, size, token usage, type) and specify actions to take when these conditions are met (e.g., delete, archive, summarize, compact).
Core Concepts
At the heart of the session pruning module are several key interfaces and classes that define how pruning is configured and executed.
PrunableItem
The PrunableItem interface represents any piece of data that can be subject to pruning. It includes essential metadata required for evaluation:
interface PrunableItem {
id: string;
sessionId: string;
type: 39;message39; | 39;memory39; | 39;checkpoint39; | 39;file39;;
createdAt: Date;
accessedAt?: Date;
sizeBytes: number;
tokens?: number;
metadata?: Record<string, unknown>;
content?: string; class="hl-cmt">// For summarization/compaction
}
When integrating with this module, your data structures must conform to PrunableItem to be managed by the PruningManager.
Pruning Rules
Pruning logic is encapsulated in PruningRule objects. Each rule consists of:
id: A unique identifier for the rule.name: A human-readable name.priority: Rules are evaluated in descending order of priority. The first rule an item matches determines the action.enabled: Whether the rule is active.conditions: An array ofPruningConditions. All conditions in a rule must be met for the rule to apply to an item.action: APruningActionto perform if all conditions are met.
Pruning Conditions
PruningConditions define the criteria for an item to be considered for pruning. The module supports several built-in condition types:
age:AgePruningCondition- Prunes items older thanmaxAgeMs.count:CountPruningCondition- Prunes items if the session or global item count exceedsmaxCount.size:SizePruningCondition- Prunes items larger thanmaxBytes.tokens:TokenPruningCondition- Prunes items with more thanmaxTokens.type:TypePruningCondition- Prunes items based on theirtypeproperty (e.g., 'message', 'memory'). Can be configured toincludeorexcludespecified types.custom:CustomPruningCondition- Allows defining custom evaluation logic via a registeredConditionEvaluator.
Pruning Actions
PruningActions specify what happens to an item once a rule's conditions are met:
delete:DeletePruningAction- Permanently removes the item from the manager.archive:ArchivePruningAction- Removes the item from active management and moves it to an internalarchivedItemscollection. This is useful for soft-deletes or moving to secondary storage.summarize:SummarizePruningAction- Reduces thecontentandtokensof an item, simulating summarization. Note: The current implementation provides a basic truncation; a real summarization service would be integrated here.compact:CompactPruningAction- Reduces thecontent,sizeBytes, andtokensof an item by a specifiedratio.
Pruning Configuration
The PruningConfig interface defines the global settings for the PruningManager:
interface PruningConfig {
enabled: boolean;
rules: PruningRule[];
checkIntervalMs: number; class="hl-cmt">// How often to check if pruning is needed
minPruneIntervalMs: number; class="hl-cmt">// Minimum time between actual pruning runs
dryRun: boolean; class="hl-cmt">// If true, items are identified but not modified
sessionConfigs?: Record<string, SessionPruningConfig>; class="hl-cmt">// Session-specific overrides
}
The DEFAULT_PRUNING_CONFIG provides sensible defaults, including rules for age, count, and token limits.
SessionPruningConfig allows overriding global rules or exempting specific sessions from pruning.
The PruningManager
The PruningManager class is the central orchestrator of the session pruning module. It extends EventEmitter to provide real-time feedback on pruning operations.
Manager Lifecycle
- Instantiation: A
PruningManagerinstance is created, optionally with an initialPruningConfig.
const manager = new PruningManager({ dryRun: true });
- Configuration: Rules, session-specific settings, and custom evaluators can be added or modified dynamically using methods like
addRule(),setSessionConfig(), andregisterEvaluator(). - Item Management:
PrunableItems are added to the manager usingaddItem(). The manager maintains an internal collection of these items. - Automatic Pruning: Call
manager.start()to begin periodic checks and pruning based oncheckIntervalMs. - Manual Pruning: Call
manager.prune()to trigger an immediate pruning run. - Stopping: Call
manager.stop()to halt automatic pruning.
Configuration Management
The PruningManager provides methods to manage its configuration:
getConfig(): Retrieves the current configuration.updateConfig(config: Partial: Merges new settings into the existing configuration. If) checkIntervalMschanges, the automatic pruning interval is restarted.addRule(rule: PruningRule): Adds or updates a pruning rule. Rules are automatically sorted bypriority.removeRule(ruleId: string): Removes a rule by its ID.setRuleEnabled(ruleId: string, enabled: boolean): Enables or disables a specific rule.setSessionConfig(sessionId: string, config: Partial: Applies specific pruning settings for a given session, including exemption.) removeSessionConfig(sessionId: string): Clears session-specific settings.registerEvaluator(name: string, evaluator: ConditionEvaluator): Registers a custom function forcustompruning conditions.
Item Management
The manager acts as a simple in-memory store for PrunableItems:
addItem(item: PrunableItem): Adds an item to be managed.removeItem(id: string): Removes an item by its ID.getItem(id: string): Retrieves an item.getAllItems(): Returns all currently managed items.getSessionItems(sessionId: string): Returns items belonging to a specific session.getArchivedItems(): Returns items that have been moved to the archive.clearItems(): Removes all managed items.
Pruning Operations
The core pruning logic resides in checkAndPrune() and prune().
checkAndPrune()
This method is called periodically when the manager is start()ed. It first checks:
- If pruning is
enabledin the configuration. - If
minPruneIntervalMshas passed since the last prune. - If any global thresholds (total items, tokens, or size) defined in the rules are exceeded via
shouldPrune().
If all checks pass, it triggers an actual prune() operation.
prune()
This is the main entry point for executing a pruning run. It performs the following steps:
graph TD
A[PruningManager.prune()] --> B{Get Items & Build Context};
B --> C[buildEvaluationContext()];
B --> D[findCandidates(items, context)];
D --> E{For each candidate};
E -- Session Exempt? --> F[Skip Item];
E -- Not Exempt --> G[executeAction(candidate)];
G --> H[Add to prunedItems];
F --> I[Add to skippedItems];
H --> E;
I --> E;
E -- All candidates processed --> J[calculateStats()];
J --> K[Emit 'complete' event];
K --> L[Return PruningResult];
- Initialization: Sets up tracking for
prunedItems,skippedItems, anderrors. Emits astartevent. - Item Selection: Gathers all
PrunableItems, optionally filtering bysessionIdif specified. - Context Building: Calls
buildEvaluationContext()to prepareSessionStatsandGlobalStatsfor efficient condition evaluation. - Candidate Identification:
findCandidates()iterates through items and rules. For each item, it checks if all conditions of any enabled rule are met usingevaluateConditions(). The first matching rule determines the candidate. Candidates are sorted by rule priority. - Action Execution: For each
PruningCandidate:
- It checks if the item's session is
exempt(unlessforcepruning is enabled). - If not exempt,
executeAction()is called to perform the specifiedPruningAction(delete, archive, summarize, compact). item-prunedoritem-skippedevents are emitted.
- Statistics & Completion: After processing all candidates,
calculateStats()compiles aPruningStatssummary. Acompleteevent is emitted with thePruningResult.
Condition Evaluation
The conditionEvaluators map holds functions that implement the logic for each PruningCondition type. When evaluateConditions() is called, it retrieves the appropriate evaluator based on condition.type and executes it.
Developers can extend this by registering custom evaluators using registerEvaluator(). This allows for highly specific pruning logic without modifying the core module.
manager.registerEvaluator(39;myCustomCondition39;, (item, condition, context) => {
if (condition.type !== 39;custom39; || condition.fn !== 39;myCustomCondition39;) return false;
class="hl-cmt">// Example: Prune if item metadata has a specific flag
return item.metadata?.[39;doNotKeep39;] === true;
});
class="hl-cmt">// Then, define a rule:
manager.addRule({
id: 39;custom-flag-prune39;,
name: 39;Prune items with custom flag39;,
priority: 50,
enabled: true,
conditions: [{ type: 39;custom39;, fn: 39;myCustomCondition39; }],
action: { type: 39;delete39; },
});
Statistics
The getStats() method provides a snapshot of the current configuration, global statistics (GlobalStats), and per-session statistics (SessionStats). This is useful for monitoring the state of managed items and understanding pruning triggers.
Singleton Access
The module provides singleton access to the PruningManager for convenience:
getPruningManager(config?: Partial: Returns the singleton instance, creating it if it doesn't exist. Subsequent calls return the same instance.) resetPruningManager(): Stops the current manager instance and clears the singleton, allowing a new instance to be created on the nextgetPruningManager()call. This is primarily useful for testing or re-initialization.
Events
The PruningManager extends EventEmitter and emits several events during its operation, allowing external components to react to pruning activities:
start: Emitted when a pruning operation begins.- Payload:
PruningConfig progress: Emitted periodically during a pruning run, indicating progress.- Payload:
PruningProgress(current,total,percent,currentItem) item-pruned: Emitted when an item is successfully pruned.- Payload:
PrunedItem(the item, action taken, reason, saved bytes/tokens) item-skipped: Emitted when an item is skipped from pruning (e.g., due to session exemption).- Payload:
SkippedItem(the item, reason) error: Emitted when an error occurs during pruning.- Payload:
PruningError(item, rule, error message, recoverability) complete: Emitted when a pruning operation finishes (successfully or with errors).- Payload:
PruningResult(summary of the operation)
Integration Points
To integrate the session-pruning module:
- Initialize: Get the
PruningManagerinstance usinggetPruningManager(). - Configure: Define your
PruningRules andPruningConfig. Add rules usingmanager.addRule(). - Add Items: Whenever a new
PrunableItem(e.g., a new message, memory entry) is created, add it to the manager usingmanager.addItem(). - Start Automatic Pruning: Call
manager.start()to enable continuous monitoring and pruning. - Listen to Events: Subscribe to relevant events (e.g.,
item-pruned,error,complete) to handle pruned items, log issues, or update UI.
import { getPruningManager, PruningManager, PrunableItem } from 39;./session-pruning/index.js39;;
const manager: PruningManager = getPruningManager({
enabled: true,
checkIntervalMs: 30 * 1000, class="hl-cmt">// Check every 30 seconds
rules: [
{
id: 39;my-app-rule39;,
name: 39;Delete old messages39;,
priority: 100,
enabled: true,
conditions: [{ type: 39;age39;, maxAgeMs: 7 * 24 * 60 * 60 * 1000 }], class="hl-cmt">// 7 days
action: { type: 39;delete39; },
},
],
});
manager.on(39;item-pruned39;, (prunedItem) => {
console.log(`Item ${prunedItem.item.id} was ${prunedItem.action} because: ${prunedItem.reason}`);
class="hl-cmt">// Here you might update your database or UI
});
manager.on(39;error39;, (error) => {
console.error(39;Pruning error:39;, error.error, error.item?.id);
});
manager.on(39;complete39;, (result) => {
console.log(`Pruning run completed. Pruned: ${result.stats.prunedCount}, Skipped: ${result.stats.skippedCount}`);
});
class="hl-cmt">// Start automatic pruning
manager.start();
class="hl-cmt">// Example: Add an item
const message: PrunableItem = {
id: 39;msg-12339;,
sessionId: 39;session-abc39;,
type: 39;message39;,
createdAt: new Date(),
sizeBytes: 100,
tokens: 20,
content: 39;Hello, this is a message.39;,
};
manager.addItem(message);
class="hl-cmt">// You can also trigger a manual prune
class="hl-cmt">// manager.prune({ force: true });