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