@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.
Files changed (74) hide show
  1. package/dist/cli.js +41 -0
  2. package/dist/context/deduplication.d.ts +72 -0
  3. package/dist/context/deduplication.js +77 -0
  4. package/dist/context/deduplication.test.d.ts +6 -0
  5. package/dist/context/deduplication.test.js +84 -0
  6. package/dist/context/emergency-eviction.d.ts +73 -0
  7. package/dist/context/emergency-eviction.example.d.ts +13 -0
  8. package/dist/context/emergency-eviction.example.js +94 -0
  9. package/dist/context/emergency-eviction.js +226 -0
  10. package/dist/context/eviction-engine.d.ts +76 -0
  11. package/dist/context/eviction-engine.example.d.ts +7 -0
  12. package/dist/context/eviction-engine.example.js +144 -0
  13. package/dist/context/eviction-engine.js +176 -0
  14. package/dist/context/example-usage.d.ts +1 -0
  15. package/dist/context/example-usage.js +128 -0
  16. package/dist/context/exchange-summariser.d.ts +80 -0
  17. package/dist/context/exchange-summariser.js +261 -0
  18. package/dist/context/health-monitor.d.ts +97 -0
  19. package/dist/context/health-monitor.example.d.ts +1 -0
  20. package/dist/context/health-monitor.example.js +164 -0
  21. package/dist/context/health-monitor.js +210 -0
  22. package/dist/context/importance-scorer.d.ts +94 -0
  23. package/dist/context/importance-scorer.example.d.ts +1 -0
  24. package/dist/context/importance-scorer.example.js +140 -0
  25. package/dist/context/importance-scorer.js +187 -0
  26. package/dist/context/index.d.ts +9 -0
  27. package/dist/context/index.js +16 -0
  28. package/dist/context/session-helper.d.ts +10 -0
  29. package/dist/context/session-helper.js +51 -0
  30. package/dist/context/session-store.d.ts +94 -0
  31. package/dist/context/session-store.js +286 -0
  32. package/dist/context/usage-estimator.d.ts +131 -0
  33. package/dist/context/usage-estimator.js +260 -0
  34. package/dist/context/usage-estimator.test.d.ts +1 -0
  35. package/dist/context/usage-estimator.test.js +208 -0
  36. package/dist/context-cli.d.ts +16 -0
  37. package/dist/context-cli.js +309 -0
  38. package/dist/evaluation/build-dataset.d.ts +1 -0
  39. package/dist/evaluation/build-dataset.js +135 -0
  40. package/dist/evaluation/threshold-eval.d.ts +63 -0
  41. package/dist/evaluation/threshold-eval.js +250 -0
  42. package/dist/handlers/codedna-handlers.d.ts +2 -2
  43. package/dist/handlers/tool-handlers.js +126 -165
  44. package/dist/helpers/api-client.d.ts +5 -1
  45. package/dist/helpers/api-client.js +3 -1
  46. package/dist/helpers/compact-formatter.d.ts +51 -0
  47. package/dist/helpers/compact-formatter.js +130 -0
  48. package/dist/helpers/engagement-tracker.d.ts +10 -0
  49. package/dist/helpers/engagement-tracker.js +61 -0
  50. package/dist/helpers/error-tracking.js +1 -1
  51. package/dist/helpers/session-validation.d.ts +76 -0
  52. package/dist/helpers/session-validation.js +221 -0
  53. package/dist/helpers/usage-analytics.js +1 -1
  54. package/dist/hooks/index.d.ts +4 -0
  55. package/dist/hooks/index.js +6 -0
  56. package/dist/hooks/post-tool-use-hook-cli.d.ts +2 -0
  57. package/dist/hooks/post-tool-use-hook-cli.js +34 -0
  58. package/dist/hooks/post-tool-use.d.ts +67 -0
  59. package/dist/hooks/post-tool-use.js +234 -0
  60. package/dist/hooks/stop-hook-cli.d.ts +2 -0
  61. package/dist/hooks/stop-hook-cli.js +34 -0
  62. package/dist/hooks/stop.d.ts +64 -0
  63. package/dist/hooks/stop.js +192 -0
  64. package/dist/index.d.ts +3 -0
  65. package/dist/index.js +2 -0
  66. package/dist/logger.d.ts +1 -1
  67. package/dist/logger.js +4 -0
  68. package/dist/resources.js +3 -0
  69. package/dist/setup.js +206 -2
  70. package/dist/templates/claude-md.d.ts +1 -1
  71. package/dist/templates/claude-md.js +23 -35
  72. package/dist/templates/worker-prompt.js +35 -202
  73. package/dist/tools.js +26 -20
  74. package/package.json +6 -2
@@ -0,0 +1,128 @@
1
+ // =============================================================================
2
+ // Usage Estimator Example
3
+ // =============================================================================
4
+ //
5
+ // Example demonstrating how to use the context usage estimator in practice.
6
+ //
7
+ // =============================================================================
8
+ import { usageEstimator, estimateTokens, isContextNearLimit, getContextFill, MODEL_CONTEXT_LIMITS, } from './index.js';
9
+ /**
10
+ * Example: Track a complete conversation flow
11
+ */
12
+ function exampleConversation() {
13
+ console.log('=== Context Usage Estimator Example ===\n');
14
+ // 1. Initial session context injection (SessionStart)
15
+ const sessionContext = `
16
+ You are Claude Code, an AI assistant. You have access to memory tools...
17
+ [large system prompt and instructions]
18
+ `.repeat(50);
19
+ const sessionTokens = estimateTokens(sessionContext);
20
+ usageEstimator.trackInjection(sessionTokens);
21
+ console.log(`Session context injected: ${sessionTokens} tokens`);
22
+ // 2. User message
23
+ const userMessage = 'Help me implement a new feature for user authentication';
24
+ const userTokens = estimateTokens(userMessage);
25
+ usageEstimator.trackUserMessage(userTokens);
26
+ console.log(`User message: ${userTokens} tokens`);
27
+ // 3. Context injection based on user query
28
+ const relevantContext = `
29
+ Memory facts about authentication:
30
+ - User authentication uses JWT tokens
31
+ - Tokens expire after 24 hours
32
+ - Refresh tokens are stored in httpOnly cookies
33
+ `.repeat(20);
34
+ const contextTokens = estimateTokens(relevantContext);
35
+ usageEstimator.trackInjection(contextTokens);
36
+ console.log(`Query-based context injected: ${contextTokens} tokens`);
37
+ // 4. Tool outputs (e.g., codebase search results)
38
+ const toolOutput = `
39
+ Found 5 authentication-related files:
40
+ - src/auth/jwt.ts
41
+ - src/auth/refresh.ts
42
+ - src/middleware/auth.ts
43
+ `.repeat(10);
44
+ const toolTokens = estimateTokens(toolOutput);
45
+ usageEstimator.trackToolOutput(toolTokens);
46
+ console.log(`Tool output: ${toolTokens} tokens`);
47
+ // 5. Assistant response
48
+ const assistantResponse = `
49
+ I'll help you implement user authentication. Based on the existing codebase,
50
+ here's my recommendation:
51
+
52
+ [detailed implementation plan]
53
+ `.repeat(100);
54
+ const assistantTokens = estimateTokens(assistantResponse);
55
+ usageEstimator.trackAssistantMessage(assistantTokens);
56
+ console.log(`Assistant response: ${assistantTokens} tokens`);
57
+ // 6. Check status
58
+ console.log('\n--- Current Status ---');
59
+ const usage = usageEstimator.getCurrentUsage();
60
+ console.log(`Total tokens: ${usage.totalEstimated.toLocaleString()}`);
61
+ console.log(`Context fill: ${getContextFill().toFixed(1)}%`);
62
+ console.log(`Approaching limit: ${isContextNearLimit() ? 'YES ⚠️' : 'NO ✅'}`);
63
+ // 7. Print full report
64
+ console.log('\n');
65
+ console.log(usageEstimator.getReport(MODEL_CONTEXT_LIMITS.SONNET));
66
+ }
67
+ /**
68
+ * Example: Simulate approaching context limit
69
+ */
70
+ function exampleApproachingLimit() {
71
+ console.log('\n=== Example: Approaching Limit ===\n');
72
+ // Reset estimator
73
+ usageEstimator.reset();
74
+ // Simulate a long conversation with lots of context
75
+ const limit = MODEL_CONTEXT_LIMITS.SONNET;
76
+ // Large initial injection
77
+ usageEstimator.trackInjection(100_000);
78
+ console.log(`After session start: ${getContextFill().toFixed(1)}% full`);
79
+ // Multiple exchanges
80
+ for (let i = 0; i < 10; i++) {
81
+ usageEstimator.trackExchange(2_000, 8_000);
82
+ }
83
+ console.log(`After 10 exchanges: ${getContextFill().toFixed(1)}% full`);
84
+ // Check if we should start pruning context
85
+ if (isContextNearLimit(80)) {
86
+ console.log('\n⚠️ Context is near limit! Time to:');
87
+ console.log(' - Prune old context');
88
+ console.log(' - Reduce injection size');
89
+ console.log(' - Summarise conversation history');
90
+ const remaining = usageEstimator.getRemainingBudget(limit);
91
+ console.log(`\nRemaining budget: ${remaining.toLocaleString()} tokens`);
92
+ }
93
+ // Show history trend
94
+ const history = usageEstimator.getHistory();
95
+ console.log(`\nHistory snapshots: ${history.length}`);
96
+ console.log('Fill percentage trend:');
97
+ history.slice(-5).forEach((snapshot, i) => {
98
+ console.log(` ${i + 1}. ${snapshot.fillPercentage.toFixed(1)}%`);
99
+ });
100
+ }
101
+ /**
102
+ * Example: Token estimation accuracy
103
+ */
104
+ function exampleTokenEstimation() {
105
+ console.log('\n=== Example: Token Estimation ===\n');
106
+ const samples = [
107
+ 'Hello',
108
+ 'Hello, how are you today?',
109
+ 'The quick brown fox jumps over the lazy dog',
110
+ `function calculateTotal(items) {
111
+ return items.reduce((sum, item) => sum + item.price, 0);
112
+ }`,
113
+ 'a'.repeat(1000),
114
+ ];
115
+ console.log('Character count → Estimated tokens:\n');
116
+ samples.forEach(text => {
117
+ const tokens = estimateTokens(text);
118
+ const preview = text.length > 50 ? text.slice(0, 47) + '...' : text;
119
+ console.log(` ${text.length} chars → ~${tokens} tokens`);
120
+ console.log(` "${preview}"\n`);
121
+ });
122
+ }
123
+ // Run examples if executed directly
124
+ if (import.meta.url === `file://${process.argv[1]}`) {
125
+ exampleConversation();
126
+ exampleApproachingLimit();
127
+ exampleTokenEstimation();
128
+ }
@@ -0,0 +1,80 @@
1
+ import type { SessionState } from './importance-scorer.js';
2
+ import type { Exchange } from './session-store.js';
3
+ /**
4
+ * Session state with exchanges (extends base SessionState)
5
+ */
6
+ export interface SessionStateWithExchanges extends SessionState {
7
+ exchanges: Exchange[];
8
+ }
9
+ /**
10
+ * Result of summarising a batch of exchanges
11
+ */
12
+ export interface SummarisationResult {
13
+ summarisedCount: number;
14
+ summary: string;
15
+ tokensSaved: number;
16
+ }
17
+ /**
18
+ * AI environment interface for summarisation (optional)
19
+ */
20
+ export interface AIEnvironment {
21
+ AI?: {
22
+ run: (model: string, options: {
23
+ messages: Array<{
24
+ role: string;
25
+ content: string;
26
+ }>;
27
+ }) => Promise<unknown>;
28
+ };
29
+ EXTRACTION_MODEL?: string;
30
+ }
31
+ export declare class ExchangeSummariser {
32
+ private env?;
33
+ constructor(env?: AIEnvironment);
34
+ /**
35
+ * Check if session has exchanges that should be summarised
36
+ *
37
+ * @param session - Current session state
38
+ * @param threshold - Number of unsummarised turns to trigger (default: 10)
39
+ * @returns True if summarisation should be triggered
40
+ */
41
+ shouldSummarise(session: SessionStateWithExchanges, threshold?: number): boolean;
42
+ /**
43
+ * Summarise old exchanges in the session
44
+ *
45
+ * @param session - Current session state (with exchanges)
46
+ * @param threshold - Number of unsummarised turns to leave active (default: 10)
47
+ * @returns Summarisation result with token savings
48
+ */
49
+ summariseOldExchanges(session: SessionStateWithExchanges, threshold?: number): Promise<SummarisationResult>;
50
+ /**
51
+ * Get the current summary for a session (if exists)
52
+ *
53
+ * @param session - Session state
54
+ * @returns Summary text or null if no summarised exchanges
55
+ */
56
+ getSummary(session: SessionStateWithExchanges): string | null;
57
+ /**
58
+ * Generate summary for a batch of exchanges
59
+ *
60
+ * Uses AI if available, falls back to extractive summarisation
61
+ */
62
+ private generateSummary;
63
+ /**
64
+ * AI-powered summarisation using Workers AI
65
+ */
66
+ private aiSummarise;
67
+ /**
68
+ * Extractive summarisation (local, no AI required)
69
+ *
70
+ * Strategy: Create a factual summary based on exchange metadata
71
+ */
72
+ private extractiveSummarise;
73
+ }
74
+ /**
75
+ * Create an ExchangeSummariser instance
76
+ *
77
+ * @param env - Optional AI environment for AI-powered summarisation
78
+ * @returns ExchangeSummariser instance
79
+ */
80
+ export declare function createExchangeSummariser(env?: AIEnvironment): ExchangeSummariser;
@@ -0,0 +1,261 @@
1
+ // =============================================================================
2
+ // Exchange Summariser - Automatic Context Management
3
+ // =============================================================================
4
+ // Summarises old exchanges (>10 turns) to reduce token count while preserving
5
+ // decisions, outcomes, and key information.
6
+ //
7
+ // Strategy:
8
+ // - For local/offline: extractive summarisation (key sentences)
9
+ // - With AI available: Workers AI for better quality summaries
10
+ // - Stores summaries in session state
11
+ // - Marks exchanges as summarised to avoid re-processing
12
+ // =============================================================================
13
+ // -----------------------------------------------------------------------------
14
+ // Configuration
15
+ // -----------------------------------------------------------------------------
16
+ const SUMMARISATION_CONFIG = {
17
+ DEFAULT_THRESHOLD: 10, // Default number of turns before summarisation
18
+ EXTRACTIVE_SENTENCES: 3, // Number of sentences to extract in local mode
19
+ MAX_SUMMARY_LENGTH: 500, // Maximum summary length (tokens ~125)
20
+ TURN_BATCH_SIZE: 10, // Summarise in batches of N turns
21
+ };
22
+ // -----------------------------------------------------------------------------
23
+ // Token Estimation
24
+ // -----------------------------------------------------------------------------
25
+ /**
26
+ * Estimate token count for text (rough approximation: ~4 chars per token)
27
+ */
28
+ function estimateTokens(text) {
29
+ return Math.ceil(text.length / 4);
30
+ }
31
+ // -----------------------------------------------------------------------------
32
+ // Exchange Summariser
33
+ // -----------------------------------------------------------------------------
34
+ export class ExchangeSummariser {
35
+ env;
36
+ constructor(env) {
37
+ this.env = env;
38
+ }
39
+ /**
40
+ * Check if session has exchanges that should be summarised
41
+ *
42
+ * @param session - Current session state
43
+ * @param threshold - Number of unsummarised turns to trigger (default: 10)
44
+ * @returns True if summarisation should be triggered
45
+ */
46
+ shouldSummarise(session, threshold = SUMMARISATION_CONFIG.DEFAULT_THRESHOLD) {
47
+ if (!session.exchanges || session.exchanges.length === 0) {
48
+ return false;
49
+ }
50
+ // Count unsummarised exchanges
51
+ const unsummarised = session.exchanges.filter(ex => !ex.summarised_at);
52
+ return unsummarised.length >= threshold;
53
+ }
54
+ /**
55
+ * Summarise old exchanges in the session
56
+ *
57
+ * @param session - Current session state (with exchanges)
58
+ * @param threshold - Number of unsummarised turns to leave active (default: 10)
59
+ * @returns Summarisation result with token savings
60
+ */
61
+ async summariseOldExchanges(session, threshold = SUMMARISATION_CONFIG.DEFAULT_THRESHOLD) {
62
+ if (!session.exchanges || session.exchanges.length === 0) {
63
+ return {
64
+ summarisedCount: 0,
65
+ summary: '',
66
+ tokensSaved: 0,
67
+ };
68
+ }
69
+ // Get unsummarised exchanges, oldest first
70
+ const unsummarised = session.exchanges
71
+ .filter((ex) => !ex.summarised_at)
72
+ .sort((a, b) => a.turn - b.turn);
73
+ // Only summarise if we have enough exchanges beyond threshold
74
+ if (unsummarised.length <= threshold) {
75
+ return {
76
+ summarisedCount: 0,
77
+ summary: '',
78
+ tokensSaved: 0,
79
+ };
80
+ }
81
+ // Identify exchanges to summarise (leave threshold most recent ones active)
82
+ const toSummarise = unsummarised.slice(0, -threshold);
83
+ if (toSummarise.length === 0) {
84
+ return {
85
+ summarisedCount: 0,
86
+ summary: '',
87
+ tokensSaved: 0,
88
+ };
89
+ }
90
+ // Calculate token savings (approximate)
91
+ const originalTokens = toSummarise.reduce((sum, ex) => sum + ex.user_tokens + ex.assistant_tokens + ex.tool_tokens, 0);
92
+ // Generate summary
93
+ const summary = await this.generateSummary(toSummarise, session.session_id);
94
+ const summaryTokens = estimateTokens(summary);
95
+ const tokensSaved = originalTokens - summaryTokens;
96
+ // Mark exchanges as summarised
97
+ const now = new Date();
98
+ toSummarise.forEach((ex) => {
99
+ ex.summarised_at = now;
100
+ });
101
+ console.log(`📝 SUMMARISATION: Turns ${toSummarise[0].turn}-${toSummarise[toSummarise.length - 1].turn} ` +
102
+ `(${toSummarise.length} exchanges): ${originalTokens} → ${summaryTokens} tokens ` +
103
+ `(${((tokensSaved / originalTokens) * 100).toFixed(1)}% reduction)`);
104
+ return {
105
+ summarisedCount: toSummarise.length,
106
+ summary,
107
+ tokensSaved,
108
+ };
109
+ }
110
+ /**
111
+ * Get the current summary for a session (if exists)
112
+ *
113
+ * @param session - Session state
114
+ * @returns Summary text or null if no summarised exchanges
115
+ */
116
+ getSummary(session) {
117
+ if (!session.exchanges || session.exchanges.length === 0) {
118
+ return null;
119
+ }
120
+ const summarised = session.exchanges.filter((ex) => ex.summarised_at);
121
+ if (summarised.length === 0) {
122
+ return null;
123
+ }
124
+ // Return a compact summary header
125
+ const turnRange = `${Math.min(...summarised.map((e) => e.turn))}-${Math.max(...summarised.map((e) => e.turn))}`;
126
+ return `Turns ${turnRange} summary: ${summarised.length} exchanges summarised`;
127
+ }
128
+ // ---------------------------------------------------------------------------
129
+ // Summary Generation
130
+ // ---------------------------------------------------------------------------
131
+ /**
132
+ * Generate summary for a batch of exchanges
133
+ *
134
+ * Uses AI if available, falls back to extractive summarisation
135
+ */
136
+ async generateSummary(exchanges, sessionId) {
137
+ // Try AI-powered summarisation first
138
+ if (this.env?.AI && this.env?.EXTRACTION_MODEL) {
139
+ try {
140
+ return await this.aiSummarise(exchanges);
141
+ }
142
+ catch (error) {
143
+ console.error('AI summarisation failed, falling back to extractive:', error);
144
+ }
145
+ }
146
+ // Fallback: extractive summarisation
147
+ return this.extractiveSummarise(exchanges);
148
+ }
149
+ /**
150
+ * AI-powered summarisation using Workers AI
151
+ */
152
+ async aiSummarise(exchanges) {
153
+ if (!this.env?.AI || !this.env?.EXTRACTION_MODEL) {
154
+ throw new Error('AI not available');
155
+ }
156
+ // Build simple exchange representation
157
+ // Note: We don't have the actual message content in Exchange type,
158
+ // so we focus on metadata
159
+ const turnSummary = exchanges
160
+ .map(ex => {
161
+ const tokens = ex.user_tokens + ex.assistant_tokens + ex.tool_tokens;
162
+ return `Turn ${ex.turn}: ${tokens} tokens (user: ${ex.user_tokens}, assistant: ${ex.assistant_tokens}, tools: ${ex.tool_tokens})`;
163
+ })
164
+ .join('\n');
165
+ const prompt = `You are a conversation summariser. Summarise these conversation turns concisely.
166
+
167
+ TURNS:
168
+ """
169
+ ${turnSummary}
170
+ """
171
+
172
+ Create a 1-2 sentence summary focusing on:
173
+ - Number of turns and turn range
174
+ - Key activity indicators (high token usage = detailed discussion)
175
+ - Progress indicators (tool usage patterns)
176
+
177
+ RESPOND WITH TEXT ONLY. NO JSON. NO MARKDOWN. Just the summary.
178
+
179
+ Example: "Turns 1-10: Investigated authentication bug in src/auth.ts with extensive code review (high token usage). Used grep and read tools to analyse JWT implementation, identified expiry issue."`;
180
+ const response = await this.env.AI.run(this.env.EXTRACTION_MODEL, { messages: [{ role: 'user', content: prompt }] });
181
+ // Parse response
182
+ let summaryText;
183
+ if (typeof response === 'string') {
184
+ summaryText = response;
185
+ }
186
+ else if (response && typeof response === 'object') {
187
+ const responseData = response.response;
188
+ if (responseData) {
189
+ if (typeof responseData === 'string') {
190
+ summaryText = responseData;
191
+ }
192
+ else if (typeof responseData === 'object') {
193
+ // Try to extract a summary field if it's JSON
194
+ summaryText = responseData.summary || JSON.stringify(responseData);
195
+ }
196
+ else {
197
+ summaryText = 'Summary generation failed';
198
+ }
199
+ }
200
+ else {
201
+ const possibleText = response.text
202
+ || response.content
203
+ || 'Summary generation failed';
204
+ summaryText = typeof possibleText === 'string' ? possibleText : 'Summary generation failed';
205
+ }
206
+ }
207
+ else {
208
+ summaryText = 'Summary generation failed';
209
+ }
210
+ // Truncate if too long
211
+ if (summaryText.length > SUMMARISATION_CONFIG.MAX_SUMMARY_LENGTH) {
212
+ summaryText = summaryText.slice(0, SUMMARISATION_CONFIG.MAX_SUMMARY_LENGTH) + '...';
213
+ }
214
+ return summaryText.trim();
215
+ }
216
+ /**
217
+ * Extractive summarisation (local, no AI required)
218
+ *
219
+ * Strategy: Create a factual summary based on exchange metadata
220
+ */
221
+ extractiveSummarise(exchanges) {
222
+ if (exchanges.length === 0) {
223
+ return '';
224
+ }
225
+ const turnRange = `${exchanges[0].turn}-${exchanges[exchanges.length - 1].turn}`;
226
+ const totalTokens = exchanges.reduce((sum, ex) => sum + ex.user_tokens + ex.assistant_tokens + ex.tool_tokens, 0);
227
+ const avgTokens = Math.round(totalTokens / exchanges.length);
228
+ // Classify activity level
229
+ let activity = 'discussion';
230
+ if (avgTokens > 2000) {
231
+ activity = 'extensive code analysis';
232
+ }
233
+ else if (avgTokens > 1000) {
234
+ activity = 'detailed investigation';
235
+ }
236
+ else if (avgTokens > 500) {
237
+ activity = 'moderate discussion';
238
+ }
239
+ else {
240
+ activity = 'brief exchanges';
241
+ }
242
+ // Detect tool usage patterns
243
+ const toolHeavy = exchanges.filter(ex => ex.tool_tokens > ex.assistant_tokens).length;
244
+ const toolPattern = toolHeavy > exchanges.length / 2
245
+ ? ' Heavy tool usage indicates codebase exploration or file operations.'
246
+ : '';
247
+ return `Turns ${turnRange} summary: ${exchanges.length} exchanges with ${activity} (avg ${avgTokens} tokens/turn).${toolPattern}`;
248
+ }
249
+ }
250
+ // -----------------------------------------------------------------------------
251
+ // Factory Functions
252
+ // -----------------------------------------------------------------------------
253
+ /**
254
+ * Create an ExchangeSummariser instance
255
+ *
256
+ * @param env - Optional AI environment for AI-powered summarisation
257
+ * @returns ExchangeSummariser instance
258
+ */
259
+ export function createExchangeSummariser(env) {
260
+ return new ExchangeSummariser(env);
261
+ }
@@ -0,0 +1,97 @@
1
+ import type { SessionState } from './importance-scorer.js';
2
+ /**
3
+ * Health status of context estimation
4
+ */
5
+ export interface HealthStatus {
6
+ estimatedFill: number;
7
+ calibrationFactor: number;
8
+ driftWarning: boolean;
9
+ recommendation: string;
10
+ metrics: {
11
+ sessionsMonitored: number;
12
+ avgDrift: number;
13
+ lastCalibration: Date | null;
14
+ };
15
+ }
16
+ /**
17
+ * Single drift measurement
18
+ */
19
+ export interface DriftRecord {
20
+ timestamp: Date;
21
+ estimated: number;
22
+ actual: number;
23
+ drift: number;
24
+ }
25
+ export declare class HealthMonitor {
26
+ private driftHistory;
27
+ private calibrationFactor;
28
+ private lastCalibrationDate;
29
+ /**
30
+ * Check health status of session estimation
31
+ *
32
+ * @param session - Current session state
33
+ * @returns Health status with metrics and recommendations
34
+ */
35
+ checkHealth(session: SessionState): HealthStatus;
36
+ /**
37
+ * Calibrate estimates using actual fill measurement
38
+ * Records drift and adjusts calibration factor if needed
39
+ *
40
+ * @param session - Current session state
41
+ * @param actualFill - Actual token usage (0.0 to 1.0)
42
+ */
43
+ calibrate(session: SessionState, actualFill: number): void;
44
+ /**
45
+ * Get current calibration factor
46
+ * Multiply estimated tokens by this factor to get calibrated estimate
47
+ *
48
+ * @returns Current calibration factor
49
+ */
50
+ getCalibrationFactor(): number;
51
+ /**
52
+ * Get drift history for analysis
53
+ *
54
+ * @returns Array of drift records (newest first)
55
+ */
56
+ getDriftHistory(): DriftRecord[];
57
+ /**
58
+ * Clear drift history and reset calibration
59
+ * Useful when estimation algorithm changes
60
+ */
61
+ reset(): void;
62
+ /**
63
+ * Get estimated fill from session state
64
+ * This would normally come from the usage estimator
65
+ */
66
+ private getEstimatedFill;
67
+ /**
68
+ * Record a drift measurement
69
+ */
70
+ private recordDrift;
71
+ /**
72
+ * Calculate average drift from recent history
73
+ * Uses exponential weighting (recent samples weighted more)
74
+ */
75
+ private calculateAverageDrift;
76
+ /**
77
+ * Update calibration factor based on drift history
78
+ * Uses exponential moving average to smooth adjustments
79
+ */
80
+ private updateCalibrationFactor;
81
+ /**
82
+ * Generate health recommendation based on drift
83
+ */
84
+ private generateRecommendation;
85
+ }
86
+ /**
87
+ * Create a new health monitor instance
88
+ */
89
+ export declare function createHealthMonitor(): HealthMonitor;
90
+ /**
91
+ * Get or create the singleton health monitor instance
92
+ */
93
+ export declare function getHealthMonitor(): HealthMonitor;
94
+ /**
95
+ * Reset the singleton instance (for testing)
96
+ */
97
+ export declare function resetHealthMonitor(): void;
@@ -0,0 +1 @@
1
+ export {};