@claudetools/tools 0.8.2 → 0.8.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +41 -0
- package/dist/context/deduplication.d.ts +72 -0
- package/dist/context/deduplication.js +77 -0
- package/dist/context/deduplication.test.d.ts +6 -0
- package/dist/context/deduplication.test.js +84 -0
- package/dist/context/emergency-eviction.d.ts +73 -0
- package/dist/context/emergency-eviction.example.d.ts +13 -0
- package/dist/context/emergency-eviction.example.js +94 -0
- package/dist/context/emergency-eviction.js +226 -0
- package/dist/context/eviction-engine.d.ts +76 -0
- package/dist/context/eviction-engine.example.d.ts +7 -0
- package/dist/context/eviction-engine.example.js +144 -0
- package/dist/context/eviction-engine.js +176 -0
- package/dist/context/example-usage.d.ts +1 -0
- package/dist/context/example-usage.js +128 -0
- package/dist/context/exchange-summariser.d.ts +80 -0
- package/dist/context/exchange-summariser.js +261 -0
- package/dist/context/health-monitor.d.ts +97 -0
- package/dist/context/health-monitor.example.d.ts +1 -0
- package/dist/context/health-monitor.example.js +164 -0
- package/dist/context/health-monitor.js +210 -0
- package/dist/context/importance-scorer.d.ts +94 -0
- package/dist/context/importance-scorer.example.d.ts +1 -0
- package/dist/context/importance-scorer.example.js +140 -0
- package/dist/context/importance-scorer.js +187 -0
- package/dist/context/index.d.ts +9 -0
- package/dist/context/index.js +16 -0
- package/dist/context/session-helper.d.ts +10 -0
- package/dist/context/session-helper.js +51 -0
- package/dist/context/session-store.d.ts +94 -0
- package/dist/context/session-store.js +286 -0
- package/dist/context/usage-estimator.d.ts +131 -0
- package/dist/context/usage-estimator.js +260 -0
- package/dist/context/usage-estimator.test.d.ts +1 -0
- package/dist/context/usage-estimator.test.js +208 -0
- package/dist/context-cli.d.ts +16 -0
- package/dist/context-cli.js +309 -0
- package/dist/evaluation/build-dataset.d.ts +1 -0
- package/dist/evaluation/build-dataset.js +135 -0
- package/dist/evaluation/threshold-eval.d.ts +63 -0
- package/dist/evaluation/threshold-eval.js +250 -0
- package/dist/handlers/codedna-handlers.d.ts +2 -2
- package/dist/handlers/tool-handlers.js +126 -165
- package/dist/helpers/api-client.d.ts +5 -1
- package/dist/helpers/api-client.js +3 -1
- package/dist/helpers/compact-formatter.d.ts +51 -0
- package/dist/helpers/compact-formatter.js +130 -0
- package/dist/helpers/engagement-tracker.d.ts +10 -0
- package/dist/helpers/engagement-tracker.js +61 -0
- package/dist/helpers/error-tracking.js +1 -1
- package/dist/helpers/session-validation.d.ts +76 -0
- package/dist/helpers/session-validation.js +221 -0
- package/dist/helpers/usage-analytics.js +1 -1
- package/dist/hooks/index.d.ts +4 -0
- package/dist/hooks/index.js +6 -0
- package/dist/hooks/post-tool-use-hook-cli.d.ts +2 -0
- package/dist/hooks/post-tool-use-hook-cli.js +34 -0
- package/dist/hooks/post-tool-use.d.ts +67 -0
- package/dist/hooks/post-tool-use.js +234 -0
- package/dist/hooks/stop-hook-cli.d.ts +2 -0
- package/dist/hooks/stop-hook-cli.js +34 -0
- package/dist/hooks/stop.d.ts +64 -0
- package/dist/hooks/stop.js +192 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +2 -0
- package/dist/logger.d.ts +1 -1
- package/dist/logger.js +4 -0
- package/dist/resources.js +3 -0
- package/dist/setup.js +206 -2
- package/dist/templates/claude-md.d.ts +1 -1
- package/dist/templates/claude-md.js +23 -35
- package/dist/templates/worker-prompt.js +35 -202
- package/dist/tools.js +26 -20
- package/package.json +6 -2
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
// =============================================================================
|
|
2
|
+
// Health Monitor Usage Example
|
|
3
|
+
// =============================================================================
|
|
4
|
+
// Demonstrates how to use the health monitor to track and calibrate
|
|
5
|
+
// context estimation accuracy.
|
|
6
|
+
// =============================================================================
|
|
7
|
+
import { getHealthMonitor } from './index.js';
|
|
8
|
+
// =============================================================================
|
|
9
|
+
// Example 1: Basic Health Check
|
|
10
|
+
// =============================================================================
|
|
11
|
+
function exampleBasicHealthCheck() {
|
|
12
|
+
console.log('\n=== Example 1: Basic Health Check ===\n');
|
|
13
|
+
const monitor = getHealthMonitor();
|
|
14
|
+
const session = {
|
|
15
|
+
session_id: 'example-session-1',
|
|
16
|
+
started_at: new Date(),
|
|
17
|
+
injected_facts: [],
|
|
18
|
+
};
|
|
19
|
+
const health = monitor.checkHealth(session);
|
|
20
|
+
console.log('Health Status:');
|
|
21
|
+
console.log(` Estimated Fill: ${(health.estimatedFill * 100).toFixed(1)}%`);
|
|
22
|
+
console.log(` Calibration Factor: ${health.calibrationFactor.toFixed(3)}`);
|
|
23
|
+
console.log(` Drift Warning: ${health.driftWarning}`);
|
|
24
|
+
console.log(` Recommendation: ${health.recommendation}`);
|
|
25
|
+
console.log('\nMetrics:');
|
|
26
|
+
console.log(` Sessions Monitored: ${health.metrics.sessionsMonitored}`);
|
|
27
|
+
console.log(` Average Drift: ${(health.metrics.avgDrift * 100).toFixed(1)}%`);
|
|
28
|
+
console.log(` Last Calibration: ${health.metrics.lastCalibration || 'Never'}`);
|
|
29
|
+
}
|
|
30
|
+
// =============================================================================
|
|
31
|
+
// Example 2: Calibration with Actual Usage
|
|
32
|
+
// =============================================================================
|
|
33
|
+
function exampleCalibration() {
|
|
34
|
+
console.log('\n=== Example 2: Calibration with Actual Usage ===\n');
|
|
35
|
+
const monitor = getHealthMonitor();
|
|
36
|
+
const session = {
|
|
37
|
+
session_id: 'example-session-2',
|
|
38
|
+
started_at: new Date(),
|
|
39
|
+
injected_facts: [],
|
|
40
|
+
};
|
|
41
|
+
// Simulate receiving actual usage data from /context command
|
|
42
|
+
console.log('Calibrating with actual usage data...\n');
|
|
43
|
+
// Session 1: Actual usage 60%, estimated 50%
|
|
44
|
+
monitor.calibrate(session, 0.6);
|
|
45
|
+
console.log('Session 1: Actual 60%, Estimated 50%');
|
|
46
|
+
// Session 2: Actual usage 65%, estimated 50%
|
|
47
|
+
monitor.calibrate(session, 0.65);
|
|
48
|
+
console.log('Session 2: Actual 65%, Estimated 50%');
|
|
49
|
+
// Session 3: Actual usage 70%, estimated 50%
|
|
50
|
+
monitor.calibrate(session, 0.7);
|
|
51
|
+
console.log('Session 3: Actual 70%, Estimated 50%');
|
|
52
|
+
// Check health after calibration
|
|
53
|
+
const health = monitor.checkHealth(session);
|
|
54
|
+
console.log('\nHealth after calibration:');
|
|
55
|
+
console.log(` Calibration Factor: ${health.calibrationFactor.toFixed(3)}`);
|
|
56
|
+
console.log(` Drift Warning: ${health.driftWarning}`);
|
|
57
|
+
console.log(` Average Drift: ${(health.metrics.avgDrift * 100).toFixed(1)}%`);
|
|
58
|
+
console.log(` Recommendation: ${health.recommendation}`);
|
|
59
|
+
}
|
|
60
|
+
// =============================================================================
|
|
61
|
+
// Example 3: Drift History Analysis
|
|
62
|
+
// =============================================================================
|
|
63
|
+
function exampleDriftHistory() {
|
|
64
|
+
console.log('\n=== Example 3: Drift History Analysis ===\n');
|
|
65
|
+
const monitor = getHealthMonitor();
|
|
66
|
+
const session = {
|
|
67
|
+
session_id: 'example-session-3',
|
|
68
|
+
started_at: new Date(),
|
|
69
|
+
injected_facts: [],
|
|
70
|
+
};
|
|
71
|
+
// Add some calibration samples
|
|
72
|
+
const samples = [0.55, 0.58, 0.62, 0.65, 0.70];
|
|
73
|
+
samples.forEach((actual, index) => {
|
|
74
|
+
monitor.calibrate(session, actual);
|
|
75
|
+
console.log(`Sample ${index + 1}: Actual ${(actual * 100).toFixed(0)}%`);
|
|
76
|
+
});
|
|
77
|
+
// Get drift history
|
|
78
|
+
console.log('\nDrift History (newest first):');
|
|
79
|
+
const history = monitor.getDriftHistory();
|
|
80
|
+
history.slice(0, 5).forEach((record, index) => {
|
|
81
|
+
console.log(` ${index + 1}. ${record.timestamp.toISOString()}: ` +
|
|
82
|
+
`Est: ${(record.estimated * 100).toFixed(1)}%, ` +
|
|
83
|
+
`Actual: ${(record.actual * 100).toFixed(1)}%, ` +
|
|
84
|
+
`Drift: ${record.drift > 0 ? '+' : ''}${(record.drift * 100).toFixed(1)}%`);
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
// =============================================================================
|
|
88
|
+
// Example 4: Integration with Context Management
|
|
89
|
+
// =============================================================================
|
|
90
|
+
function exampleIntegration() {
|
|
91
|
+
console.log('\n=== Example 4: Integration Pattern ===\n');
|
|
92
|
+
const monitor = getHealthMonitor();
|
|
93
|
+
// Simulated context management flow
|
|
94
|
+
const session = {
|
|
95
|
+
session_id: 'example-session-4',
|
|
96
|
+
started_at: new Date(),
|
|
97
|
+
injected_facts: [],
|
|
98
|
+
};
|
|
99
|
+
console.log('Context Management Flow:\n');
|
|
100
|
+
// 1. Check health before injection
|
|
101
|
+
console.log('1. Check health status');
|
|
102
|
+
const healthBefore = monitor.checkHealth(session);
|
|
103
|
+
console.log(` Current calibration factor: ${healthBefore.calibrationFactor.toFixed(3)}`);
|
|
104
|
+
// 2. Use calibrated estimates for injection decisions
|
|
105
|
+
console.log('\n2. Use calibrated estimates');
|
|
106
|
+
const baseEstimate = 0.5;
|
|
107
|
+
const calibratedEstimate = baseEstimate * healthBefore.calibrationFactor;
|
|
108
|
+
console.log(` Base estimate: ${(baseEstimate * 100).toFixed(1)}%`);
|
|
109
|
+
console.log(` Calibrated estimate: ${(calibratedEstimate * 100).toFixed(1)}%`);
|
|
110
|
+
// 3. After session, calibrate with actual usage
|
|
111
|
+
console.log('\n3. Calibrate with actual usage');
|
|
112
|
+
const actualUsage = 0.65;
|
|
113
|
+
monitor.calibrate(session, actualUsage);
|
|
114
|
+
console.log(` Actual usage: ${(actualUsage * 100).toFixed(1)}%`);
|
|
115
|
+
// 4. Check updated health
|
|
116
|
+
console.log('\n4. Check updated health');
|
|
117
|
+
const healthAfter = monitor.checkHealth(session);
|
|
118
|
+
console.log(` New calibration factor: ${healthAfter.calibrationFactor.toFixed(3)}`);
|
|
119
|
+
console.log(` Drift warning: ${healthAfter.driftWarning}`);
|
|
120
|
+
if (healthAfter.driftWarning) {
|
|
121
|
+
console.log(` ⚠️ ${healthAfter.recommendation}`);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
// =============================================================================
|
|
125
|
+
// Example 5: Handling Drift Warnings
|
|
126
|
+
// =============================================================================
|
|
127
|
+
function exampleDriftWarnings() {
|
|
128
|
+
console.log('\n=== Example 5: Handling Drift Warnings ===\n');
|
|
129
|
+
const monitor = getHealthMonitor();
|
|
130
|
+
monitor.reset(); // Start fresh
|
|
131
|
+
const session = {
|
|
132
|
+
session_id: 'example-session-5',
|
|
133
|
+
started_at: new Date(),
|
|
134
|
+
injected_facts: [],
|
|
135
|
+
};
|
|
136
|
+
console.log('Simulating consistent underestimation:\n');
|
|
137
|
+
// Simulate consistent underestimation (actual >> estimated)
|
|
138
|
+
for (let i = 0; i < 5; i++) {
|
|
139
|
+
const actual = 0.75 + i * 0.02; // Gradually increasing
|
|
140
|
+
monitor.calibrate(session, actual);
|
|
141
|
+
console.log(`Calibration ${i + 1}: Actual ${(actual * 100).toFixed(1)}%`);
|
|
142
|
+
}
|
|
143
|
+
const health = monitor.checkHealth(session);
|
|
144
|
+
console.log('\nHealth Check Result:');
|
|
145
|
+
console.log(` ⚠️ Drift Warning: ${health.driftWarning}`);
|
|
146
|
+
console.log(` 📊 Average Drift: ${(health.metrics.avgDrift * 100).toFixed(1)}%`);
|
|
147
|
+
console.log(` 🔧 Calibration Factor: ${health.calibrationFactor.toFixed(3)}`);
|
|
148
|
+
console.log(`\n💡 ${health.recommendation}`);
|
|
149
|
+
console.log('\nNext steps:');
|
|
150
|
+
console.log(' - Future estimates will be multiplied by calibration factor');
|
|
151
|
+
console.log(' - Monitor drift over time to ensure accuracy improves');
|
|
152
|
+
console.log(' - Consider more aggressive eviction if needed');
|
|
153
|
+
}
|
|
154
|
+
// =============================================================================
|
|
155
|
+
// Run All Examples
|
|
156
|
+
// =============================================================================
|
|
157
|
+
if (import.meta.url === `file://${process.argv[1]}`) {
|
|
158
|
+
exampleBasicHealthCheck();
|
|
159
|
+
exampleCalibration();
|
|
160
|
+
exampleDriftHistory();
|
|
161
|
+
exampleIntegration();
|
|
162
|
+
exampleDriftWarnings();
|
|
163
|
+
console.log('\n=== Examples Complete ===\n');
|
|
164
|
+
}
|
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
// =============================================================================
|
|
2
|
+
// Health Monitor - Context estimation accuracy tracking
|
|
3
|
+
// =============================================================================
|
|
4
|
+
// Monitors drift between estimated and actual token usage to calibrate
|
|
5
|
+
// estimates and warn when accuracy degrades.
|
|
6
|
+
//
|
|
7
|
+
// Calibration Logic:
|
|
8
|
+
// - Records drift when actual fill data is available
|
|
9
|
+
// - Adjusts future estimates by calibration factor
|
|
10
|
+
// - Warns if drift exceeds 10%
|
|
11
|
+
// =============================================================================
|
|
12
|
+
// -----------------------------------------------------------------------------
|
|
13
|
+
// Configuration
|
|
14
|
+
// -----------------------------------------------------------------------------
|
|
15
|
+
const HEALTH_CONFIG = {
|
|
16
|
+
/** Maximum number of drift records to store in memory */
|
|
17
|
+
MAX_DRIFT_HISTORY: 100,
|
|
18
|
+
/** Drift threshold for warnings (10%) */
|
|
19
|
+
DRIFT_WARNING_THRESHOLD: 0.1,
|
|
20
|
+
/** Number of samples needed before adjusting calibration */
|
|
21
|
+
MIN_SAMPLES_FOR_CALIBRATION: 3,
|
|
22
|
+
/** Weight of new samples in moving average (0-1) */
|
|
23
|
+
CALIBRATION_SMOOTHING: 0.3,
|
|
24
|
+
};
|
|
25
|
+
// -----------------------------------------------------------------------------
|
|
26
|
+
// Health Monitor Implementation
|
|
27
|
+
// -----------------------------------------------------------------------------
|
|
28
|
+
export class HealthMonitor {
|
|
29
|
+
driftHistory = [];
|
|
30
|
+
calibrationFactor = 1.0;
|
|
31
|
+
lastCalibrationDate = null;
|
|
32
|
+
/**
|
|
33
|
+
* Check health status of session estimation
|
|
34
|
+
*
|
|
35
|
+
* @param session - Current session state
|
|
36
|
+
* @returns Health status with metrics and recommendations
|
|
37
|
+
*/
|
|
38
|
+
checkHealth(session) {
|
|
39
|
+
const estimatedFill = this.getEstimatedFill(session);
|
|
40
|
+
const avgDrift = this.calculateAverageDrift();
|
|
41
|
+
const driftWarning = Math.abs(avgDrift) > HEALTH_CONFIG.DRIFT_WARNING_THRESHOLD;
|
|
42
|
+
return {
|
|
43
|
+
estimatedFill,
|
|
44
|
+
calibrationFactor: this.calibrationFactor,
|
|
45
|
+
driftWarning,
|
|
46
|
+
recommendation: this.generateRecommendation(avgDrift, driftWarning),
|
|
47
|
+
metrics: {
|
|
48
|
+
sessionsMonitored: this.driftHistory.length,
|
|
49
|
+
avgDrift,
|
|
50
|
+
lastCalibration: this.lastCalibrationDate,
|
|
51
|
+
},
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Calibrate estimates using actual fill measurement
|
|
56
|
+
* Records drift and adjusts calibration factor if needed
|
|
57
|
+
*
|
|
58
|
+
* @param session - Current session state
|
|
59
|
+
* @param actualFill - Actual token usage (0.0 to 1.0)
|
|
60
|
+
*/
|
|
61
|
+
calibrate(session, actualFill) {
|
|
62
|
+
const estimatedFill = this.getEstimatedFill(session);
|
|
63
|
+
const drift = actualFill - estimatedFill;
|
|
64
|
+
const driftPercent = drift / actualFill;
|
|
65
|
+
// Record drift
|
|
66
|
+
this.recordDrift(estimatedFill, actualFill, drift);
|
|
67
|
+
// Update calibration if we have enough samples
|
|
68
|
+
if (this.driftHistory.length >= HEALTH_CONFIG.MIN_SAMPLES_FOR_CALIBRATION) {
|
|
69
|
+
this.updateCalibrationFactor();
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Get current calibration factor
|
|
74
|
+
* Multiply estimated tokens by this factor to get calibrated estimate
|
|
75
|
+
*
|
|
76
|
+
* @returns Current calibration factor
|
|
77
|
+
*/
|
|
78
|
+
getCalibrationFactor() {
|
|
79
|
+
return this.calibrationFactor;
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Get drift history for analysis
|
|
83
|
+
*
|
|
84
|
+
* @returns Array of drift records (newest first)
|
|
85
|
+
*/
|
|
86
|
+
getDriftHistory() {
|
|
87
|
+
return [...this.driftHistory].reverse();
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Clear drift history and reset calibration
|
|
91
|
+
* Useful when estimation algorithm changes
|
|
92
|
+
*/
|
|
93
|
+
reset() {
|
|
94
|
+
this.driftHistory = [];
|
|
95
|
+
this.calibrationFactor = 1.0;
|
|
96
|
+
this.lastCalibrationDate = null;
|
|
97
|
+
}
|
|
98
|
+
// ---------------------------------------------------------------------------
|
|
99
|
+
// Private Helpers
|
|
100
|
+
// ---------------------------------------------------------------------------
|
|
101
|
+
/**
|
|
102
|
+
* Get estimated fill from session state
|
|
103
|
+
* This would normally come from the usage estimator
|
|
104
|
+
*/
|
|
105
|
+
getEstimatedFill(session) {
|
|
106
|
+
// In practice, this would call usageEstimator.getContextFill(session)
|
|
107
|
+
// For now, return a placeholder that accounts for calibration
|
|
108
|
+
return 0.5 * this.calibrationFactor; // Placeholder
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Record a drift measurement
|
|
112
|
+
*/
|
|
113
|
+
recordDrift(estimated, actual, drift) {
|
|
114
|
+
const record = {
|
|
115
|
+
timestamp: new Date(),
|
|
116
|
+
estimated,
|
|
117
|
+
actual,
|
|
118
|
+
drift,
|
|
119
|
+
};
|
|
120
|
+
this.driftHistory.push(record);
|
|
121
|
+
// Trim history if it exceeds max size
|
|
122
|
+
if (this.driftHistory.length > HEALTH_CONFIG.MAX_DRIFT_HISTORY) {
|
|
123
|
+
this.driftHistory.shift(); // Remove oldest
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Calculate average drift from recent history
|
|
128
|
+
* Uses exponential weighting (recent samples weighted more)
|
|
129
|
+
*/
|
|
130
|
+
calculateAverageDrift() {
|
|
131
|
+
if (this.driftHistory.length === 0) {
|
|
132
|
+
return 0;
|
|
133
|
+
}
|
|
134
|
+
// Use last 10 samples for average
|
|
135
|
+
const recentSamples = this.driftHistory.slice(-10);
|
|
136
|
+
let weightedSum = 0;
|
|
137
|
+
let totalWeight = 0;
|
|
138
|
+
recentSamples.forEach((record, index) => {
|
|
139
|
+
// Exponential weight: newer samples weighted more heavily
|
|
140
|
+
const weight = Math.exp(index / recentSamples.length);
|
|
141
|
+
const driftPercent = record.drift / record.actual;
|
|
142
|
+
weightedSum += driftPercent * weight;
|
|
143
|
+
totalWeight += weight;
|
|
144
|
+
});
|
|
145
|
+
return weightedSum / totalWeight;
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* Update calibration factor based on drift history
|
|
149
|
+
* Uses exponential moving average to smooth adjustments
|
|
150
|
+
*/
|
|
151
|
+
updateCalibrationFactor() {
|
|
152
|
+
const avgDrift = this.calculateAverageDrift();
|
|
153
|
+
// Calculate new factor: if we're consistently underestimating (positive drift),
|
|
154
|
+
// increase the factor; if overestimating, decrease it
|
|
155
|
+
const targetFactor = 1 / (1 - avgDrift);
|
|
156
|
+
// Smooth the adjustment using exponential moving average
|
|
157
|
+
this.calibrationFactor =
|
|
158
|
+
this.calibrationFactor * (1 - HEALTH_CONFIG.CALIBRATION_SMOOTHING) +
|
|
159
|
+
targetFactor * HEALTH_CONFIG.CALIBRATION_SMOOTHING;
|
|
160
|
+
this.lastCalibrationDate = new Date();
|
|
161
|
+
}
|
|
162
|
+
/**
|
|
163
|
+
* Generate health recommendation based on drift
|
|
164
|
+
*/
|
|
165
|
+
generateRecommendation(avgDrift, driftWarning) {
|
|
166
|
+
if (!driftWarning) {
|
|
167
|
+
return 'Estimation accuracy is good. No action needed.';
|
|
168
|
+
}
|
|
169
|
+
const driftPercent = Math.abs(avgDrift * 100).toFixed(1);
|
|
170
|
+
if (avgDrift > 0) {
|
|
171
|
+
// Underestimating (actual usage higher than estimated)
|
|
172
|
+
return `Warning: Estimates are ${driftPercent}% too low. Context window may fill faster than expected. Consider more aggressive eviction.`;
|
|
173
|
+
}
|
|
174
|
+
else {
|
|
175
|
+
// Overestimating (actual usage lower than estimated)
|
|
176
|
+
return `Warning: Estimates are ${driftPercent}% too high. Context window has more capacity than estimated. Can inject more facts safely.`;
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
// -----------------------------------------------------------------------------
|
|
181
|
+
// Factory
|
|
182
|
+
// -----------------------------------------------------------------------------
|
|
183
|
+
/**
|
|
184
|
+
* Create a new health monitor instance
|
|
185
|
+
*/
|
|
186
|
+
export function createHealthMonitor() {
|
|
187
|
+
return new HealthMonitor();
|
|
188
|
+
}
|
|
189
|
+
// -----------------------------------------------------------------------------
|
|
190
|
+
// Singleton Instance
|
|
191
|
+
// -----------------------------------------------------------------------------
|
|
192
|
+
let monitorInstance = null;
|
|
193
|
+
/**
|
|
194
|
+
* Get or create the singleton health monitor instance
|
|
195
|
+
*/
|
|
196
|
+
export function getHealthMonitor() {
|
|
197
|
+
if (!monitorInstance) {
|
|
198
|
+
monitorInstance = new HealthMonitor();
|
|
199
|
+
}
|
|
200
|
+
return monitorInstance;
|
|
201
|
+
}
|
|
202
|
+
/**
|
|
203
|
+
* Reset the singleton instance (for testing)
|
|
204
|
+
*/
|
|
205
|
+
export function resetHealthMonitor() {
|
|
206
|
+
if (monitorInstance) {
|
|
207
|
+
monitorInstance.reset();
|
|
208
|
+
}
|
|
209
|
+
monitorInstance = null;
|
|
210
|
+
}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import type { ScoredFact } from '@claudetools/shared';
|
|
2
|
+
/**
|
|
3
|
+
* Fact that has been injected into the session context
|
|
4
|
+
* Tracks injection time and reference patterns for decay calculation
|
|
5
|
+
*/
|
|
6
|
+
export interface InjectedFact extends ScoredFact {
|
|
7
|
+
injected_at: Date;
|
|
8
|
+
last_referenced?: Date;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Session state tracking for importance scoring
|
|
12
|
+
* Contains session metadata needed for recency calculations
|
|
13
|
+
*/
|
|
14
|
+
export interface SessionState {
|
|
15
|
+
session_id: string;
|
|
16
|
+
started_at: Date;
|
|
17
|
+
injected_facts: InjectedFact[];
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Importance level from fact metadata
|
|
21
|
+
*/
|
|
22
|
+
export type ImportanceLevel = 'critical' | 'high' | 'normal';
|
|
23
|
+
/**
|
|
24
|
+
* Scored fact with computed importance
|
|
25
|
+
*/
|
|
26
|
+
export interface ScoredImportance {
|
|
27
|
+
fact: InjectedFact;
|
|
28
|
+
score: number;
|
|
29
|
+
breakdown: {
|
|
30
|
+
base: number;
|
|
31
|
+
timeDecay: number;
|
|
32
|
+
referenceBoost: number;
|
|
33
|
+
recencyBoost: number;
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
export declare class ImportanceScorer {
|
|
37
|
+
/**
|
|
38
|
+
* Calculate dynamic importance score for a single fact
|
|
39
|
+
*
|
|
40
|
+
* @param fact - The injected fact to score
|
|
41
|
+
* @param session - Current session state
|
|
42
|
+
* @returns Importance score between 0.0 and 1.0
|
|
43
|
+
*/
|
|
44
|
+
calculateImportance(fact: InjectedFact, session: SessionState): number;
|
|
45
|
+
/**
|
|
46
|
+
* Calculate importance with component breakdown for debugging
|
|
47
|
+
*
|
|
48
|
+
* @param fact - The injected fact to score
|
|
49
|
+
* @param session - Current session state
|
|
50
|
+
* @returns Scored fact with breakdown
|
|
51
|
+
*/
|
|
52
|
+
calculateWithBreakdown(fact: InjectedFact, session: SessionState): ScoredImportance;
|
|
53
|
+
/**
|
|
54
|
+
* Score all facts in a session
|
|
55
|
+
*
|
|
56
|
+
* @param session - Session state with injected facts
|
|
57
|
+
* @returns Array of scored facts sorted by score (descending)
|
|
58
|
+
*/
|
|
59
|
+
scoreAllFacts(session: SessionState): ScoredImportance[];
|
|
60
|
+
/**
|
|
61
|
+
* Get facts that should be evicted (lowest importance scores)
|
|
62
|
+
*
|
|
63
|
+
* @param session - Session state
|
|
64
|
+
* @param count - Number of facts to evict
|
|
65
|
+
* @returns Facts to evict (lowest scores first)
|
|
66
|
+
*/
|
|
67
|
+
getEvictionCandidates(session: SessionState, count: number): InjectedFact[];
|
|
68
|
+
/**
|
|
69
|
+
* Get facts above a minimum importance threshold
|
|
70
|
+
*
|
|
71
|
+
* @param session - Session state
|
|
72
|
+
* @param minScore - Minimum score threshold (0.0 to 1.0)
|
|
73
|
+
* @returns Facts above threshold
|
|
74
|
+
*/
|
|
75
|
+
getFactsAboveThreshold(session: SessionState, minScore: number): InjectedFact[];
|
|
76
|
+
/**
|
|
77
|
+
* Extract importance level from fact metadata
|
|
78
|
+
*/
|
|
79
|
+
private extractImportanceLevel;
|
|
80
|
+
/**
|
|
81
|
+
* Calculate reference boost
|
|
82
|
+
* Recently-referenced facts get a boost to stay in context longer
|
|
83
|
+
*/
|
|
84
|
+
private calculateReferenceBoost;
|
|
85
|
+
/**
|
|
86
|
+
* Calculate recency boost
|
|
87
|
+
* Newer injections in the session get a boost
|
|
88
|
+
*/
|
|
89
|
+
private calculateRecencyBoost;
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Create a new importance scorer instance
|
|
93
|
+
*/
|
|
94
|
+
export declare function createImportanceScorer(): ImportanceScorer;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
// =============================================================================
|
|
2
|
+
// Importance Scorer - Example Usage
|
|
3
|
+
// =============================================================================
|
|
4
|
+
// Demonstrates the importance decay algorithm for automatic context management
|
|
5
|
+
// =============================================================================
|
|
6
|
+
import { createImportanceScorer } from './importance-scorer.js';
|
|
7
|
+
// -----------------------------------------------------------------------------
|
|
8
|
+
// Example Session Setup
|
|
9
|
+
// -----------------------------------------------------------------------------
|
|
10
|
+
// Create a session that started 1 hour ago
|
|
11
|
+
const sessionStartTime = new Date(Date.now() - 60 * 60 * 1000);
|
|
12
|
+
// Create facts with different importance levels and injection times
|
|
13
|
+
const facts = [
|
|
14
|
+
// Critical fact injected 5 minutes ago
|
|
15
|
+
{
|
|
16
|
+
edge_id: 'fact1',
|
|
17
|
+
source_entity_id: 'component_a',
|
|
18
|
+
target_entity_id: 'pattern_x',
|
|
19
|
+
relation_type: 'USES',
|
|
20
|
+
fact: 'ComponentA uses PatternX for state management',
|
|
21
|
+
created_at: new Date().toISOString(),
|
|
22
|
+
metadata: {
|
|
23
|
+
critical: true,
|
|
24
|
+
category: 'architecture',
|
|
25
|
+
},
|
|
26
|
+
injected_at: new Date(Date.now() - 5 * 60 * 1000), // 5 minutes ago
|
|
27
|
+
},
|
|
28
|
+
// High importance fact injected 15 minutes ago (still within half-life)
|
|
29
|
+
{
|
|
30
|
+
edge_id: 'fact2',
|
|
31
|
+
source_entity_id: 'bug_123',
|
|
32
|
+
target_entity_id: 'fix_abc',
|
|
33
|
+
relation_type: 'HAD_BUG',
|
|
34
|
+
fact: 'Bug 123 was fixed by implementing retry logic',
|
|
35
|
+
created_at: new Date().toISOString(),
|
|
36
|
+
metadata: {
|
|
37
|
+
important: true,
|
|
38
|
+
category: 'decision',
|
|
39
|
+
},
|
|
40
|
+
injected_at: new Date(Date.now() - 15 * 60 * 1000), // 15 minutes ago
|
|
41
|
+
},
|
|
42
|
+
// Normal fact injected 45 minutes ago (past half-life, should decay)
|
|
43
|
+
{
|
|
44
|
+
edge_id: 'fact3',
|
|
45
|
+
source_entity_id: 'user',
|
|
46
|
+
target_entity_id: 'preference',
|
|
47
|
+
relation_type: 'PREFERS',
|
|
48
|
+
fact: 'User prefers TypeScript over JavaScript',
|
|
49
|
+
created_at: new Date().toISOString(),
|
|
50
|
+
metadata: {
|
|
51
|
+
category: 'preference',
|
|
52
|
+
},
|
|
53
|
+
injected_at: new Date(Date.now() - 45 * 60 * 1000), // 45 minutes ago
|
|
54
|
+
},
|
|
55
|
+
// High importance fact injected 20 minutes ago, referenced 2 minutes ago
|
|
56
|
+
{
|
|
57
|
+
edge_id: 'fact4',
|
|
58
|
+
source_entity_id: 'module_y',
|
|
59
|
+
target_entity_id: 'dependency_z',
|
|
60
|
+
relation_type: 'DEPENDS_ON',
|
|
61
|
+
fact: 'ModuleY depends on DependencyZ for authentication',
|
|
62
|
+
created_at: new Date().toISOString(),
|
|
63
|
+
metadata: {
|
|
64
|
+
important: true,
|
|
65
|
+
category: 'pattern',
|
|
66
|
+
},
|
|
67
|
+
injected_at: new Date(Date.now() - 20 * 60 * 1000), // 20 minutes ago
|
|
68
|
+
last_referenced: new Date(Date.now() - 2 * 60 * 1000), // Referenced 2 minutes ago
|
|
69
|
+
},
|
|
70
|
+
];
|
|
71
|
+
const session = {
|
|
72
|
+
session_id: 'example_session',
|
|
73
|
+
started_at: sessionStartTime,
|
|
74
|
+
injected_facts: facts,
|
|
75
|
+
};
|
|
76
|
+
// -----------------------------------------------------------------------------
|
|
77
|
+
// Score and Display Results
|
|
78
|
+
// -----------------------------------------------------------------------------
|
|
79
|
+
const scorer = createImportanceScorer();
|
|
80
|
+
console.log('='.repeat(80));
|
|
81
|
+
console.log('Importance Scorer - Example Execution');
|
|
82
|
+
console.log('='.repeat(80));
|
|
83
|
+
console.log();
|
|
84
|
+
// Score all facts
|
|
85
|
+
const scored = scorer.scoreAllFacts(session);
|
|
86
|
+
console.log('Scored Facts (highest to lowest importance):');
|
|
87
|
+
console.log('-'.repeat(80));
|
|
88
|
+
scored.forEach((result, index) => {
|
|
89
|
+
console.log(`${index + 1}. ${result.fact.fact}`);
|
|
90
|
+
console.log(` Score: ${result.score.toFixed(3)}`);
|
|
91
|
+
console.log(` Breakdown:`);
|
|
92
|
+
console.log(` - Base Importance: ${result.breakdown.base.toFixed(3)}`);
|
|
93
|
+
console.log(` - Time Decay: ${result.breakdown.timeDecay.toFixed(3)}`);
|
|
94
|
+
console.log(` - Reference Boost: ${result.breakdown.referenceBoost.toFixed(3)}`);
|
|
95
|
+
console.log(` - Recency Boost: ${result.breakdown.recencyBoost.toFixed(3)}`);
|
|
96
|
+
console.log(` Injected: ${Math.round((Date.now() - result.fact.injected_at.getTime()) / 60000)} minutes ago`);
|
|
97
|
+
if (result.fact.last_referenced) {
|
|
98
|
+
console.log(` Last Referenced: ${Math.round((Date.now() - result.fact.last_referenced.getTime()) / 60000)} minutes ago`);
|
|
99
|
+
}
|
|
100
|
+
console.log();
|
|
101
|
+
});
|
|
102
|
+
// -----------------------------------------------------------------------------
|
|
103
|
+
// Eviction Example
|
|
104
|
+
// -----------------------------------------------------------------------------
|
|
105
|
+
console.log('='.repeat(80));
|
|
106
|
+
console.log('Eviction Candidates (lowest 2 scores):');
|
|
107
|
+
console.log('-'.repeat(80));
|
|
108
|
+
const evictionCandidates = scorer.getEvictionCandidates(session, 2);
|
|
109
|
+
evictionCandidates.forEach((fact, index) => {
|
|
110
|
+
console.log(`${index + 1}. ${fact.fact}`);
|
|
111
|
+
});
|
|
112
|
+
console.log();
|
|
113
|
+
// -----------------------------------------------------------------------------
|
|
114
|
+
// Threshold Example
|
|
115
|
+
// -----------------------------------------------------------------------------
|
|
116
|
+
console.log('='.repeat(80));
|
|
117
|
+
console.log('Facts Above Threshold (min score: 0.5):');
|
|
118
|
+
console.log('-'.repeat(80));
|
|
119
|
+
const aboveThreshold = scorer.getFactsAboveThreshold(session, 0.5);
|
|
120
|
+
aboveThreshold.forEach((fact, index) => {
|
|
121
|
+
console.log(`${index + 1}. ${fact.fact}`);
|
|
122
|
+
});
|
|
123
|
+
console.log();
|
|
124
|
+
// -----------------------------------------------------------------------------
|
|
125
|
+
// Algorithm Verification
|
|
126
|
+
// -----------------------------------------------------------------------------
|
|
127
|
+
console.log('='.repeat(80));
|
|
128
|
+
console.log('Algorithm Verification:');
|
|
129
|
+
console.log('-'.repeat(80));
|
|
130
|
+
console.log('✓ Base importance levels: critical (1.0), high (0.7), normal (0.4)');
|
|
131
|
+
console.log('✓ Time decay: Exponential with 30-minute half-life');
|
|
132
|
+
console.log('✓ Reference boost: Recent references keep facts longer');
|
|
133
|
+
console.log('✓ Recency boost: Newer injections weighted higher');
|
|
134
|
+
console.log('✓ Score range: All scores between 0.0 and 1.0');
|
|
135
|
+
console.log();
|
|
136
|
+
console.log('Expected behavior:');
|
|
137
|
+
console.log(' - Critical architecture fact should score highest (recently injected)');
|
|
138
|
+
console.log(' - Recently referenced fact should get boost despite age');
|
|
139
|
+
console.log(' - Old normal fact should score lowest (past half-life, no boost)');
|
|
140
|
+
console.log('='.repeat(80));
|