156 lines
4.3 KiB
JavaScript
156 lines
4.3 KiB
JavaScript
/**
|
|
* Context Monitor - Estimates context usage and triggers backups
|
|
*/
|
|
|
|
const fs = require('fs-extra');
|
|
const path = require('path');
|
|
|
|
class ContextMonitor {
|
|
constructor() {
|
|
this.estimatedTokens = 0;
|
|
this.promptCount = 0;
|
|
this.toolExecutions = 0;
|
|
this.sessionStartTime = Date.now();
|
|
|
|
// Configuration
|
|
this.maxContextTokens = 200000; // Conservative estimate
|
|
this.backupThreshold = 0.85; // 85% of max context
|
|
this.timeThresholdMinutes = 30; // 30 minutes
|
|
this.toolThreshold = 25; // 25 tool executions
|
|
|
|
this.lastBackupTime = Date.now();
|
|
this.lastBackupToolCount = 0;
|
|
}
|
|
|
|
/**
|
|
* Update context estimates from user prompt
|
|
*/
|
|
updateFromPrompt(promptData) {
|
|
this.promptCount++;
|
|
|
|
// Estimate tokens from prompt
|
|
const prompt = promptData.prompt || '';
|
|
const estimatedPromptTokens = this._estimateTokens(prompt);
|
|
|
|
this.estimatedTokens += estimatedPromptTokens;
|
|
|
|
// Add context size if provided
|
|
if (promptData.context_size) {
|
|
this.estimatedTokens += promptData.context_size;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Update context estimates from tool usage
|
|
*/
|
|
updateFromToolUse(toolData) {
|
|
this.toolExecutions++;
|
|
|
|
// Estimate tokens from tool parameters and output
|
|
const parameters = JSON.stringify(toolData.parameters || {});
|
|
const output = toolData.output || '';
|
|
const error = toolData.error || '';
|
|
|
|
const toolTokens = this._estimateTokens(parameters + output + error);
|
|
this.estimatedTokens += toolTokens;
|
|
|
|
// File operations add more context
|
|
if (toolData.tool === 'Read' || toolData.tool === 'Edit') {
|
|
this.estimatedTokens += 2000; // Typical file size estimate
|
|
} else if (toolData.tool === 'Bash') {
|
|
this.estimatedTokens += 500; // Command output estimate
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Check if backup should be triggered
|
|
*/
|
|
checkBackupTriggers(hookType, data) {
|
|
const decisions = [];
|
|
|
|
// Context threshold trigger
|
|
const contextRatio = this.getContextUsageRatio();
|
|
if (contextRatio > this.backupThreshold) {
|
|
decisions.push({
|
|
shouldBackup: true,
|
|
reason: `Context usage ${(contextRatio * 100).toFixed(1)}%`,
|
|
urgency: 'high'
|
|
});
|
|
}
|
|
|
|
// Time-based trigger
|
|
const sessionMinutes = (Date.now() - this.sessionStartTime) / (1000 * 60);
|
|
const timeSinceBackup = (Date.now() - this.lastBackupTime) / (1000 * 60);
|
|
|
|
if (timeSinceBackup > this.timeThresholdMinutes) {
|
|
decisions.push({
|
|
shouldBackup: true,
|
|
reason: `${this.timeThresholdMinutes} minutes since last backup`,
|
|
urgency: 'medium'
|
|
});
|
|
}
|
|
|
|
// Tool-based trigger
|
|
const toolsSinceBackup = this.toolExecutions - this.lastBackupToolCount;
|
|
if (toolsSinceBackup >= this.toolThreshold) {
|
|
decisions.push({
|
|
shouldBackup: true,
|
|
reason: `${this.toolThreshold} tools since last backup`,
|
|
urgency: 'medium'
|
|
});
|
|
}
|
|
|
|
// Return highest priority decision
|
|
if (decisions.length > 0) {
|
|
const urgencyOrder = { high: 3, medium: 2, low: 1 };
|
|
decisions.sort((a, b) => urgencyOrder[b.urgency] - urgencyOrder[a.urgency]);
|
|
return decisions[0];
|
|
}
|
|
|
|
return { shouldBackup: false };
|
|
}
|
|
|
|
/**
|
|
* Get current context usage ratio (0.0 to 1.0)
|
|
*/
|
|
getContextUsageRatio() {
|
|
return Math.min(1.0, this.estimatedTokens / this.maxContextTokens);
|
|
}
|
|
|
|
/**
|
|
* Mark that a backup was performed
|
|
*/
|
|
markBackupPerformed() {
|
|
this.lastBackupTime = Date.now();
|
|
this.lastBackupToolCount = this.toolExecutions;
|
|
}
|
|
|
|
/**
|
|
* Estimate tokens from text (rough approximation)
|
|
*/
|
|
_estimateTokens(text) {
|
|
if (!text) return 0;
|
|
|
|
// Rough estimate: ~4 characters per token for English text
|
|
// Add some buffer for formatting and special tokens
|
|
return Math.ceil(text.length / 3.5);
|
|
}
|
|
|
|
/**
|
|
* Get context usage statistics
|
|
*/
|
|
getStats() {
|
|
const sessionMinutes = (Date.now() - this.sessionStartTime) / (1000 * 60);
|
|
|
|
return {
|
|
estimatedTokens: this.estimatedTokens,
|
|
contextUsageRatio: this.getContextUsageRatio(),
|
|
promptCount: this.promptCount,
|
|
toolExecutions: this.toolExecutions,
|
|
sessionMinutes: Math.round(sessionMinutes),
|
|
lastBackupMinutesAgo: Math.round((Date.now() - this.lastBackupTime) / (1000 * 60))
|
|
};
|
|
}
|
|
}
|
|
|
|
module.exports = { ContextMonitor }; |