@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
package/dist/cli.js
CHANGED
|
@@ -11,6 +11,7 @@ import { runSetup, runUninstall, runInit, runCleanup } from './setup.js';
|
|
|
11
11
|
import { startServer } from './index.js';
|
|
12
12
|
import { startWatcher, stopWatcher, watcherStatus } from './watcher.js';
|
|
13
13
|
import { generateCodebaseMap, generateCodebaseMapLocal } from './helpers/codebase-mapper.js';
|
|
14
|
+
import { contextStatus, contextEvict, contextSummarise, contextReset, } from './context-cli.js';
|
|
14
15
|
// Get version from package.json
|
|
15
16
|
const __filename = fileURLToPath(import.meta.url);
|
|
16
17
|
const __dirname = dirname(__filename);
|
|
@@ -56,6 +57,10 @@ Commands:
|
|
|
56
57
|
watch --status Check watcher status
|
|
57
58
|
map Generate codebase map for current project
|
|
58
59
|
map --local Generate map locally without uploading
|
|
60
|
+
context status Show current session context usage
|
|
61
|
+
context evict Manually trigger eviction cycle
|
|
62
|
+
context summarise Summarise and compress exchanges
|
|
63
|
+
context reset Clear session state
|
|
59
64
|
|
|
60
65
|
Running without options starts the MCP server.
|
|
61
66
|
|
|
@@ -165,6 +170,42 @@ else if (positionals[0] === 'map') {
|
|
|
165
170
|
process.exit(1);
|
|
166
171
|
});
|
|
167
172
|
}
|
|
173
|
+
else if (positionals[0] === 'context') {
|
|
174
|
+
// Handle context command
|
|
175
|
+
const subcommand = positionals[1];
|
|
176
|
+
const contextArgs = process.argv.slice(4); // Get args after subcommand
|
|
177
|
+
switch (subcommand) {
|
|
178
|
+
case 'status':
|
|
179
|
+
contextStatus(contextArgs).catch((error) => {
|
|
180
|
+
console.error('Context status failed:', error);
|
|
181
|
+
process.exit(1);
|
|
182
|
+
});
|
|
183
|
+
break;
|
|
184
|
+
case 'evict':
|
|
185
|
+
contextEvict(contextArgs).catch((error) => {
|
|
186
|
+
console.error('Context evict failed:', error);
|
|
187
|
+
process.exit(1);
|
|
188
|
+
});
|
|
189
|
+
break;
|
|
190
|
+
case 'summarise':
|
|
191
|
+
case 'summarize': // Support both spellings
|
|
192
|
+
contextSummarise(contextArgs).catch((error) => {
|
|
193
|
+
console.error('Context summarise failed:', error);
|
|
194
|
+
process.exit(1);
|
|
195
|
+
});
|
|
196
|
+
break;
|
|
197
|
+
case 'reset':
|
|
198
|
+
contextReset(contextArgs).catch((error) => {
|
|
199
|
+
console.error('Context reset failed:', error);
|
|
200
|
+
process.exit(1);
|
|
201
|
+
});
|
|
202
|
+
break;
|
|
203
|
+
default:
|
|
204
|
+
console.error(`Unknown context subcommand: ${subcommand}`);
|
|
205
|
+
console.error('Available subcommands: status, evict, summarise, reset');
|
|
206
|
+
process.exit(1);
|
|
207
|
+
}
|
|
208
|
+
}
|
|
168
209
|
else {
|
|
169
210
|
// Start MCP server
|
|
170
211
|
startServer();
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Deduplication Tracker for Context Injection
|
|
3
|
+
*
|
|
4
|
+
* Tracks which facts have been injected into context and when,
|
|
5
|
+
* preventing redundant re-injection unless facts are stale.
|
|
6
|
+
*
|
|
7
|
+
* @module context/deduplication
|
|
8
|
+
*/
|
|
9
|
+
export interface DeduplicationTracker {
|
|
10
|
+
/**
|
|
11
|
+
* Record that a fact was injected at a specific turn
|
|
12
|
+
*/
|
|
13
|
+
recordInjection(factId: string, turn: number): void;
|
|
14
|
+
/**
|
|
15
|
+
* Determine if a fact should be injected
|
|
16
|
+
* @param factId - The fact ID to check
|
|
17
|
+
* @param currentTurn - Current conversation turn number
|
|
18
|
+
* @param freshnessThreshold - Number of turns before fact is considered stale (default: 10)
|
|
19
|
+
* @returns true if fact should be injected, false if it's already fresh in context
|
|
20
|
+
*/
|
|
21
|
+
shouldInject(factId: string, currentTurn: number, freshnessThreshold?: number): boolean;
|
|
22
|
+
/**
|
|
23
|
+
* Get all currently tracked facts and their injection turns
|
|
24
|
+
*/
|
|
25
|
+
getInjectedFacts(): Map<string, number>;
|
|
26
|
+
/**
|
|
27
|
+
* Clear all tracked facts
|
|
28
|
+
*/
|
|
29
|
+
clear(): void;
|
|
30
|
+
/**
|
|
31
|
+
* Get statistics about tracked facts
|
|
32
|
+
*/
|
|
33
|
+
getStats(currentTurn: number): {
|
|
34
|
+
totalTracked: number;
|
|
35
|
+
fresh: number;
|
|
36
|
+
stale: number;
|
|
37
|
+
averageAge: number;
|
|
38
|
+
};
|
|
39
|
+
/**
|
|
40
|
+
* Remove facts older than a certain age to prevent unbounded growth
|
|
41
|
+
*/
|
|
42
|
+
pruneOldFacts(currentTurn: number, maxAge?: number): number;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* In-memory deduplication tracker implementation
|
|
46
|
+
*/
|
|
47
|
+
export declare class InMemoryDeduplicationTracker implements DeduplicationTracker {
|
|
48
|
+
private injectedFacts;
|
|
49
|
+
private defaultFreshnessThreshold;
|
|
50
|
+
constructor(defaultFreshnessThreshold?: number);
|
|
51
|
+
recordInjection(factId: string, turn: number): void;
|
|
52
|
+
shouldInject(factId: string, currentTurn: number, freshnessThreshold?: number): boolean;
|
|
53
|
+
getInjectedFacts(): Map<string, number>;
|
|
54
|
+
clear(): void;
|
|
55
|
+
/**
|
|
56
|
+
* Get statistics about tracked facts
|
|
57
|
+
*/
|
|
58
|
+
getStats(currentTurn: number): {
|
|
59
|
+
totalTracked: number;
|
|
60
|
+
fresh: number;
|
|
61
|
+
stale: number;
|
|
62
|
+
averageAge: number;
|
|
63
|
+
};
|
|
64
|
+
/**
|
|
65
|
+
* Remove facts older than a certain age to prevent unbounded growth
|
|
66
|
+
*/
|
|
67
|
+
pruneOldFacts(currentTurn: number, maxAge?: number): number;
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Create a new deduplication tracker
|
|
71
|
+
*/
|
|
72
|
+
export declare function createDeduplicationTracker(freshnessThreshold?: number): DeduplicationTracker;
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Deduplication Tracker for Context Injection
|
|
3
|
+
*
|
|
4
|
+
* Tracks which facts have been injected into context and when,
|
|
5
|
+
* preventing redundant re-injection unless facts are stale.
|
|
6
|
+
*
|
|
7
|
+
* @module context/deduplication
|
|
8
|
+
*/
|
|
9
|
+
/**
|
|
10
|
+
* In-memory deduplication tracker implementation
|
|
11
|
+
*/
|
|
12
|
+
export class InMemoryDeduplicationTracker {
|
|
13
|
+
injectedFacts;
|
|
14
|
+
defaultFreshnessThreshold;
|
|
15
|
+
constructor(defaultFreshnessThreshold = 10) {
|
|
16
|
+
this.injectedFacts = new Map();
|
|
17
|
+
this.defaultFreshnessThreshold = defaultFreshnessThreshold;
|
|
18
|
+
}
|
|
19
|
+
recordInjection(factId, turn) {
|
|
20
|
+
this.injectedFacts.set(factId, turn);
|
|
21
|
+
}
|
|
22
|
+
shouldInject(factId, currentTurn, freshnessThreshold = this.defaultFreshnessThreshold) {
|
|
23
|
+
const lastInjectedTurn = this.injectedFacts.get(factId);
|
|
24
|
+
// Never injected before - should inject
|
|
25
|
+
if (lastInjectedTurn === undefined) {
|
|
26
|
+
return true;
|
|
27
|
+
}
|
|
28
|
+
// Calculate how many turns ago it was injected
|
|
29
|
+
const turnsSinceInjection = currentTurn - lastInjectedTurn;
|
|
30
|
+
// Should inject if it's been more than threshold turns
|
|
31
|
+
return turnsSinceInjection > freshnessThreshold;
|
|
32
|
+
}
|
|
33
|
+
getInjectedFacts() {
|
|
34
|
+
return new Map(this.injectedFacts);
|
|
35
|
+
}
|
|
36
|
+
clear() {
|
|
37
|
+
this.injectedFacts.clear();
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Get statistics about tracked facts
|
|
41
|
+
*/
|
|
42
|
+
getStats(currentTurn) {
|
|
43
|
+
const facts = Array.from(this.injectedFacts.values());
|
|
44
|
+
const fresh = facts.filter(turn => currentTurn - turn <= this.defaultFreshnessThreshold);
|
|
45
|
+
const stale = facts.filter(turn => currentTurn - turn > this.defaultFreshnessThreshold);
|
|
46
|
+
const totalAge = facts.reduce((sum, turn) => sum + (currentTurn - turn), 0);
|
|
47
|
+
return {
|
|
48
|
+
totalTracked: facts.length,
|
|
49
|
+
fresh: fresh.length,
|
|
50
|
+
stale: stale.length,
|
|
51
|
+
averageAge: facts.length > 0 ? totalAge / facts.length : 0,
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Remove facts older than a certain age to prevent unbounded growth
|
|
56
|
+
*/
|
|
57
|
+
pruneOldFacts(currentTurn, maxAge = 100) {
|
|
58
|
+
let pruned = 0;
|
|
59
|
+
const toDelete = [];
|
|
60
|
+
this.injectedFacts.forEach((turn, factId) => {
|
|
61
|
+
if (currentTurn - turn > maxAge) {
|
|
62
|
+
toDelete.push(factId);
|
|
63
|
+
}
|
|
64
|
+
});
|
|
65
|
+
toDelete.forEach(factId => {
|
|
66
|
+
this.injectedFacts.delete(factId);
|
|
67
|
+
pruned++;
|
|
68
|
+
});
|
|
69
|
+
return pruned;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Create a new deduplication tracker
|
|
74
|
+
*/
|
|
75
|
+
export function createDeduplicationTracker(freshnessThreshold = 10) {
|
|
76
|
+
return new InMemoryDeduplicationTracker(freshnessThreshold);
|
|
77
|
+
}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for DeduplicationTracker
|
|
3
|
+
*
|
|
4
|
+
* These tests demonstrate the usage and verify the logic.
|
|
5
|
+
*/
|
|
6
|
+
import { createDeduplicationTracker } from './deduplication.js';
|
|
7
|
+
// Example usage demonstration
|
|
8
|
+
function demonstrateUsage() {
|
|
9
|
+
const tracker = createDeduplicationTracker(10); // 10-turn freshness threshold
|
|
10
|
+
console.log('\n=== Deduplication Tracker Demo ===\n');
|
|
11
|
+
// Scenario 1: First injection
|
|
12
|
+
console.log('Turn 1: Check if fact_123 should be injected');
|
|
13
|
+
console.log('Result:', tracker.shouldInject('fact_123', 1)); // true - never injected
|
|
14
|
+
tracker.recordInjection('fact_123', 1);
|
|
15
|
+
// Scenario 2: Recent injection (within threshold)
|
|
16
|
+
console.log('\nTurn 5: Check if fact_123 should be injected again');
|
|
17
|
+
console.log('Result:', tracker.shouldInject('fact_123', 5)); // false - only 4 turns ago
|
|
18
|
+
console.log('Turns since injection:', 5 - 1);
|
|
19
|
+
// Scenario 3: Stale injection (beyond threshold)
|
|
20
|
+
console.log('\nTurn 15: Check if fact_123 should be injected again');
|
|
21
|
+
console.log('Result:', tracker.shouldInject('fact_123', 15)); // true - 14 turns ago (> 10)
|
|
22
|
+
console.log('Turns since injection:', 15 - 1);
|
|
23
|
+
// Scenario 4: Multiple facts
|
|
24
|
+
tracker.recordInjection('fact_456', 10);
|
|
25
|
+
tracker.recordInjection('fact_789', 12);
|
|
26
|
+
console.log('\nTurn 20: Check multiple facts');
|
|
27
|
+
console.log('fact_123 (injected turn 1):', tracker.shouldInject('fact_123', 20)); // true - 19 turns
|
|
28
|
+
console.log('fact_456 (injected turn 10):', tracker.shouldInject('fact_456', 20)); // true - 10 turns
|
|
29
|
+
console.log('fact_789 (injected turn 12):', tracker.shouldInject('fact_789', 20)); // false - 8 turns
|
|
30
|
+
// Scenario 5: Statistics
|
|
31
|
+
console.log('\nStatistics at turn 20:');
|
|
32
|
+
console.log(JSON.stringify(tracker.getStats(20), null, 2));
|
|
33
|
+
// Scenario 6: Pruning old facts
|
|
34
|
+
console.log('\nPruning facts older than 50 turns at turn 100:');
|
|
35
|
+
tracker.recordInjection('old_fact', 30);
|
|
36
|
+
const pruned = tracker.pruneOldFacts(100, 50);
|
|
37
|
+
console.log('Pruned:', pruned, 'facts');
|
|
38
|
+
console.log('Remaining facts:', tracker.getInjectedFacts().size);
|
|
39
|
+
}
|
|
40
|
+
// Run tests if this file is executed directly
|
|
41
|
+
if (import.meta.url === `file://${process.argv[1]}`) {
|
|
42
|
+
demonstrateUsage();
|
|
43
|
+
}
|
|
44
|
+
// Simple assertion-based tests
|
|
45
|
+
export function runTests() {
|
|
46
|
+
const tracker = createDeduplicationTracker(10);
|
|
47
|
+
// Test 1: Never injected should return true
|
|
48
|
+
if (!tracker.shouldInject('test_fact', 1)) {
|
|
49
|
+
throw new Error('Test 1 failed: Never injected fact should inject');
|
|
50
|
+
}
|
|
51
|
+
// Test 2: Recent injection should return false
|
|
52
|
+
tracker.recordInjection('test_fact', 1);
|
|
53
|
+
if (tracker.shouldInject('test_fact', 5)) {
|
|
54
|
+
throw new Error('Test 2 failed: Recent fact should not inject');
|
|
55
|
+
}
|
|
56
|
+
// Test 3: Stale injection should return true
|
|
57
|
+
if (!tracker.shouldInject('test_fact', 15)) {
|
|
58
|
+
throw new Error('Test 3 failed: Stale fact should inject');
|
|
59
|
+
}
|
|
60
|
+
// Test 4: Custom threshold
|
|
61
|
+
if (!tracker.shouldInject('test_fact', 6, 5)) {
|
|
62
|
+
throw new Error('Test 4 failed: Custom threshold should allow injection');
|
|
63
|
+
}
|
|
64
|
+
// Test 5: Clear should reset state
|
|
65
|
+
tracker.clear();
|
|
66
|
+
if (tracker.getInjectedFacts().size !== 0) {
|
|
67
|
+
throw new Error('Test 5 failed: Clear should remove all facts');
|
|
68
|
+
}
|
|
69
|
+
// Test 6: Statistics
|
|
70
|
+
tracker.recordInjection('fact1', 1);
|
|
71
|
+
tracker.recordInjection('fact2', 8);
|
|
72
|
+
tracker.recordInjection('fact3', 15);
|
|
73
|
+
const stats = tracker.getStats(20);
|
|
74
|
+
if (stats.totalTracked !== 3) {
|
|
75
|
+
throw new Error('Test 6 failed: Should track 3 facts');
|
|
76
|
+
}
|
|
77
|
+
if (stats.fresh !== 1) {
|
|
78
|
+
throw new Error('Test 6 failed: Should have 1 fresh fact');
|
|
79
|
+
}
|
|
80
|
+
if (stats.stale !== 2) {
|
|
81
|
+
throw new Error('Test 6 failed: Should have 2 stale facts');
|
|
82
|
+
}
|
|
83
|
+
console.log('ā
All tests passed!');
|
|
84
|
+
}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import type { PersistedSessionState } from './session-store.js';
|
|
2
|
+
/**
|
|
3
|
+
* Result of emergency eviction operation
|
|
4
|
+
*/
|
|
5
|
+
export interface EmergencyResult {
|
|
6
|
+
wasEmergency: boolean;
|
|
7
|
+
actionsToken: string[];
|
|
8
|
+
evictedCount: number;
|
|
9
|
+
summarisedExchanges: number;
|
|
10
|
+
compressedCritical: number;
|
|
11
|
+
newEstimatedFill: number;
|
|
12
|
+
userWarning: string;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Emergency eviction configuration
|
|
16
|
+
*/
|
|
17
|
+
export interface EmergencyConfig {
|
|
18
|
+
emergencyThreshold: number;
|
|
19
|
+
targetFill: number;
|
|
20
|
+
criticalCompressionThreshold: number;
|
|
21
|
+
}
|
|
22
|
+
export declare class EmergencyEviction {
|
|
23
|
+
private config;
|
|
24
|
+
private scorer;
|
|
25
|
+
constructor(config?: Partial<EmergencyConfig>);
|
|
26
|
+
/**
|
|
27
|
+
* Check if session is in emergency state
|
|
28
|
+
*/
|
|
29
|
+
isEmergency(session: PersistedSessionState): boolean;
|
|
30
|
+
/**
|
|
31
|
+
* Run emergency eviction protocol
|
|
32
|
+
*
|
|
33
|
+
* Steps:
|
|
34
|
+
* 1. Summarise recent exchanges
|
|
35
|
+
* 2. Evict normal importance facts
|
|
36
|
+
* 3. If still critical, compress high importance facts
|
|
37
|
+
* 4. If still critical, compress critical facts (last resort)
|
|
38
|
+
*
|
|
39
|
+
* @param session - Current session state
|
|
40
|
+
* @returns Emergency result with actions taken
|
|
41
|
+
*/
|
|
42
|
+
runEmergencyEviction(session: PersistedSessionState): Promise<EmergencyResult>;
|
|
43
|
+
/**
|
|
44
|
+
* Get facts by importance level
|
|
45
|
+
*/
|
|
46
|
+
private getFactsByImportance;
|
|
47
|
+
/**
|
|
48
|
+
* Get count of unsummarised exchanges
|
|
49
|
+
*/
|
|
50
|
+
private getUnsummarisedExchangeCount;
|
|
51
|
+
/**
|
|
52
|
+
* Estimate tokens for a set of exchanges
|
|
53
|
+
*/
|
|
54
|
+
private estimateExchangeTokens;
|
|
55
|
+
/**
|
|
56
|
+
* Estimate tokens for a set of facts
|
|
57
|
+
* More realistic estimate: ~500 tokens per fact average
|
|
58
|
+
* (includes context, entities, relationships, metadata)
|
|
59
|
+
*/
|
|
60
|
+
private estimateFactTokens;
|
|
61
|
+
/**
|
|
62
|
+
* Recalculate fill percentage after token change
|
|
63
|
+
*/
|
|
64
|
+
private recalculateFill;
|
|
65
|
+
/**
|
|
66
|
+
* Generate user-facing warning message
|
|
67
|
+
*/
|
|
68
|
+
private generateUserWarning;
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Create emergency eviction instance with optional config
|
|
72
|
+
*/
|
|
73
|
+
export declare function createEmergencyEviction(config?: Partial<EmergencyConfig>): EmergencyEviction;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Example: Monitor session context and trigger emergency eviction
|
|
3
|
+
*/
|
|
4
|
+
declare function monitorSessionContext(sessionId: string): Promise<void>;
|
|
5
|
+
/**
|
|
6
|
+
* Example: Custom emergency configuration for aggressive cleanup
|
|
7
|
+
*/
|
|
8
|
+
declare function aggressiveEmergencyCleanup(sessionId: string): Promise<void>;
|
|
9
|
+
/**
|
|
10
|
+
* Example: Integration with post-tool-use hook
|
|
11
|
+
*/
|
|
12
|
+
declare function postToolUseEmergencyCheck(sessionId: string): Promise<void>;
|
|
13
|
+
export { monitorSessionContext, aggressiveEmergencyCleanup, postToolUseEmergencyCheck };
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
// =============================================================================
|
|
2
|
+
// Emergency Eviction - Usage Example
|
|
3
|
+
// =============================================================================
|
|
4
|
+
import { createEmergencyEviction, getSessionStore, } from './index.js';
|
|
5
|
+
/**
|
|
6
|
+
* Example: Monitor session context and trigger emergency eviction
|
|
7
|
+
*/
|
|
8
|
+
async function monitorSessionContext(sessionId) {
|
|
9
|
+
const sessionStore = getSessionStore();
|
|
10
|
+
const eviction = createEmergencyEviction({
|
|
11
|
+
emergencyThreshold: 0.70, // Trigger at 70% fill
|
|
12
|
+
targetFill: 0.50, // Aim for 50% after eviction
|
|
13
|
+
criticalCompressionThreshold: 0.65, // Compress critical if still > 65%
|
|
14
|
+
});
|
|
15
|
+
// Get current session state
|
|
16
|
+
const session = await sessionStore.getSession(sessionId);
|
|
17
|
+
if (!session) {
|
|
18
|
+
console.log('Session not found');
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
console.log(`Session fill: ${(session.estimated_fill * 100).toFixed(1)}%`);
|
|
22
|
+
// Check if emergency eviction is needed
|
|
23
|
+
if (eviction.isEmergency(session)) {
|
|
24
|
+
console.log('šØ EMERGENCY: Context at critical level, running eviction...');
|
|
25
|
+
const result = await eviction.runEmergencyEviction(session);
|
|
26
|
+
console.log('\nEmergency Eviction Results:');
|
|
27
|
+
console.log('āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā');
|
|
28
|
+
console.log(`Actions taken: ${result.actionsToken.length}`);
|
|
29
|
+
result.actionsToken.forEach((action) => console.log(` ⢠${action}`));
|
|
30
|
+
console.log(`\nEvicted facts: ${result.evictedCount}`);
|
|
31
|
+
console.log(`Summarised exchanges: ${result.summarisedExchanges}`);
|
|
32
|
+
console.log(`Compressed critical facts: ${result.compressedCritical}`);
|
|
33
|
+
console.log(`\nFill: ${(session.estimated_fill * 100).toFixed(1)}% ā ${(result.newEstimatedFill * 100).toFixed(1)}%`);
|
|
34
|
+
console.log(`\nā ļø ${result.userWarning}`);
|
|
35
|
+
// Update session with new fill estimate
|
|
36
|
+
await sessionStore.updateSession(sessionId, {
|
|
37
|
+
estimated_fill: result.newEstimatedFill,
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
else {
|
|
41
|
+
console.log('ā Context within safe limits');
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Example: Custom emergency configuration for aggressive cleanup
|
|
46
|
+
*/
|
|
47
|
+
async function aggressiveEmergencyCleanup(sessionId) {
|
|
48
|
+
const sessionStore = getSessionStore();
|
|
49
|
+
const eviction = createEmergencyEviction({
|
|
50
|
+
emergencyThreshold: 0.60, // Trigger earlier at 60%
|
|
51
|
+
targetFill: 0.30, // Aim for very low fill (30%)
|
|
52
|
+
criticalCompressionThreshold: 0.50, // More aggressive critical compression
|
|
53
|
+
});
|
|
54
|
+
const session = await sessionStore.getSession(sessionId);
|
|
55
|
+
if (!session)
|
|
56
|
+
return;
|
|
57
|
+
if (eviction.isEmergency(session)) {
|
|
58
|
+
const result = await eviction.runEmergencyEviction(session);
|
|
59
|
+
console.log('š„ Aggressive cleanup completed:');
|
|
60
|
+
console.log(` Evicted: ${result.evictedCount} facts`);
|
|
61
|
+
console.log(` Compressed: ${result.compressedCritical} critical facts`);
|
|
62
|
+
console.log(` New fill: ${(result.newEstimatedFill * 100).toFixed(1)}%`);
|
|
63
|
+
await sessionStore.updateSession(sessionId, {
|
|
64
|
+
estimated_fill: result.newEstimatedFill,
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Example: Integration with post-tool-use hook
|
|
70
|
+
*/
|
|
71
|
+
async function postToolUseEmergencyCheck(sessionId) {
|
|
72
|
+
const sessionStore = getSessionStore();
|
|
73
|
+
const eviction = createEmergencyEviction();
|
|
74
|
+
const session = await sessionStore.getSession(sessionId);
|
|
75
|
+
if (!session)
|
|
76
|
+
return;
|
|
77
|
+
// Check after every tool use
|
|
78
|
+
if (eviction.isEmergency(session)) {
|
|
79
|
+
console.log('Emergency eviction triggered by tool use');
|
|
80
|
+
const result = await eviction.runEmergencyEviction(session);
|
|
81
|
+
// Log to user
|
|
82
|
+
if (result.compressedCritical > 0) {
|
|
83
|
+
console.error('ā ļø CRITICAL: Context pressure forced compression of important facts');
|
|
84
|
+
}
|
|
85
|
+
else if (result.evictedCount > 0) {
|
|
86
|
+
console.warn(`Context cleanup: Evicted ${result.evictedCount} facts`);
|
|
87
|
+
}
|
|
88
|
+
await sessionStore.updateSession(sessionId, {
|
|
89
|
+
estimated_fill: result.newEstimatedFill,
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
// Export examples
|
|
94
|
+
export { monitorSessionContext, aggressiveEmergencyCleanup, postToolUseEmergencyCheck };
|