source dump of claude code
at main 207 lines 6.1 kB view raw
1/** 2 * Session Memory utility functions that can be imported without circular dependencies. 3 * These are separate from the main sessionMemory.ts to avoid importing runAgent. 4 */ 5 6import { isFsInaccessible } from '../../utils/errors.js' 7import { getFsImplementation } from '../../utils/fsOperations.js' 8import { getSessionMemoryPath } from '../../utils/permissions/filesystem.js' 9import { sleep } from '../../utils/sleep.js' 10import { logEvent } from '../analytics/index.js' 11 12const EXTRACTION_WAIT_TIMEOUT_MS = 15000 13const EXTRACTION_STALE_THRESHOLD_MS = 60000 // 1 minute 14 15/** 16 * Configuration for session memory extraction thresholds 17 */ 18export type SessionMemoryConfig = { 19 /** Minimum context window tokens before initializing session memory. 20 * Uses the same token counting as autocompact (input + output + cache tokens) 21 * to ensure consistent behavior between the two features. */ 22 minimumMessageTokensToInit: number 23 /** Minimum context window growth (in tokens) between session memory updates. 24 * Uses the same token counting as autocompact (tokenCountWithEstimation) 25 * to measure actual context growth, not cumulative API usage. */ 26 minimumTokensBetweenUpdate: number 27 /** Number of tool calls between session memory updates */ 28 toolCallsBetweenUpdates: number 29} 30 31// Default configuration values 32export const DEFAULT_SESSION_MEMORY_CONFIG: SessionMemoryConfig = { 33 minimumMessageTokensToInit: 10000, 34 minimumTokensBetweenUpdate: 5000, 35 toolCallsBetweenUpdates: 3, 36} 37 38// Current session memory configuration 39let sessionMemoryConfig: SessionMemoryConfig = { 40 ...DEFAULT_SESSION_MEMORY_CONFIG, 41} 42 43// Track the last summarized message ID (shared state) 44let lastSummarizedMessageId: string | undefined 45 46// Track extraction state with timestamp (set by sessionMemory.ts) 47let extractionStartedAt: number | undefined 48 49// Track context size at last memory extraction (for minimumTokensBetweenUpdate) 50let tokensAtLastExtraction = 0 51 52// Track whether session memory has been initialized (met minimumMessageTokensToInit) 53let sessionMemoryInitialized = false 54 55/** 56 * Get the message ID up to which the session memory is current 57 */ 58export function getLastSummarizedMessageId(): string | undefined { 59 return lastSummarizedMessageId 60} 61 62/** 63 * Set the last summarized message ID (called from sessionMemory.ts) 64 */ 65export function setLastSummarizedMessageId( 66 messageId: string | undefined, 67): void { 68 lastSummarizedMessageId = messageId 69} 70 71/** 72 * Mark extraction as started (called from sessionMemory.ts) 73 */ 74export function markExtractionStarted(): void { 75 extractionStartedAt = Date.now() 76} 77 78/** 79 * Mark extraction as completed (called from sessionMemory.ts) 80 */ 81export function markExtractionCompleted(): void { 82 extractionStartedAt = undefined 83} 84 85/** 86 * Wait for any in-progress session memory extraction to complete (with 15s timeout) 87 * Returns immediately if no extraction is in progress or if extraction is stale (>1min old). 88 */ 89export async function waitForSessionMemoryExtraction(): Promise<void> { 90 const startTime = Date.now() 91 while (extractionStartedAt) { 92 const extractionAge = Date.now() - extractionStartedAt 93 if (extractionAge > EXTRACTION_STALE_THRESHOLD_MS) { 94 // Extraction is stale, don't wait 95 return 96 } 97 98 if (Date.now() - startTime > EXTRACTION_WAIT_TIMEOUT_MS) { 99 // Timeout - continue anyway 100 return 101 } 102 103 await sleep(1000) 104 } 105} 106 107/** 108 * Get the current session memory content 109 */ 110export async function getSessionMemoryContent(): Promise<string | null> { 111 const fs = getFsImplementation() 112 const memoryPath = getSessionMemoryPath() 113 114 try { 115 const content = await fs.readFile(memoryPath, { encoding: 'utf-8' }) 116 117 logEvent('tengu_session_memory_loaded', { 118 content_length: content.length, 119 }) 120 121 return content 122 } catch (e: unknown) { 123 if (isFsInaccessible(e)) return null 124 throw e 125 } 126} 127 128/** 129 * Set the session memory configuration 130 */ 131export function setSessionMemoryConfig( 132 config: Partial<SessionMemoryConfig>, 133): void { 134 sessionMemoryConfig = { 135 ...sessionMemoryConfig, 136 ...config, 137 } 138} 139 140/** 141 * Get the current session memory configuration 142 */ 143export function getSessionMemoryConfig(): SessionMemoryConfig { 144 return { ...sessionMemoryConfig } 145} 146 147/** 148 * Record the context size at the time of extraction. 149 * Used to measure context growth for minimumTokensBetweenUpdate threshold. 150 */ 151export function recordExtractionTokenCount(currentTokenCount: number): void { 152 tokensAtLastExtraction = currentTokenCount 153} 154 155/** 156 * Check if session memory has been initialized (met minimumTokensToInit threshold) 157 */ 158export function isSessionMemoryInitialized(): boolean { 159 return sessionMemoryInitialized 160} 161 162/** 163 * Mark session memory as initialized 164 */ 165export function markSessionMemoryInitialized(): void { 166 sessionMemoryInitialized = true 167} 168 169/** 170 * Check if we've met the threshold to initialize session memory. 171 * Uses total context window tokens (same as autocompact) for consistent behavior. 172 */ 173export function hasMetInitializationThreshold( 174 currentTokenCount: number, 175): boolean { 176 return currentTokenCount >= sessionMemoryConfig.minimumMessageTokensToInit 177} 178 179/** 180 * Check if we've met the threshold for the next update. 181 * Measures actual context window growth since last extraction 182 * (same metric as autocompact and initialization threshold). 183 */ 184export function hasMetUpdateThreshold(currentTokenCount: number): boolean { 185 const tokensSinceLastExtraction = currentTokenCount - tokensAtLastExtraction 186 return ( 187 tokensSinceLastExtraction >= sessionMemoryConfig.minimumTokensBetweenUpdate 188 ) 189} 190 191/** 192 * Get the configured number of tool calls between updates 193 */ 194export function getToolCallsBetweenUpdates(): number { 195 return sessionMemoryConfig.toolCallsBetweenUpdates 196} 197 198/** 199 * Reset session memory state (useful for testing) 200 */ 201export function resetSessionMemoryState(): void { 202 sessionMemoryConfig = { ...DEFAULT_SESSION_MEMORY_CONFIG } 203 tokensAtLastExtraction = 0 204 sessionMemoryInitialized = false 205 lastSummarizedMessageId = undefined 206 extractionStartedAt = undefined 207}