π File detail
services/SessionMemory/sessionMemoryUtils.ts
π― Use case
This file lives under βservices/β, which covers long-lived services (LSP, MCP, OAuth, tool execution, memory, compaction, voice, settings sync, β¦). On the API surface it exposes SessionMemoryConfig, DEFAULT_SESSION_MEMORY_CONFIG, getLastSummarizedMessageId, setLastSummarizedMessageId, and markExtractionStarted (and more) β mainly functions, hooks, or classes. It composes internal code from utils and analytics (relative imports). What the file header says: Session Memory utility functions that can be imported without circular dependencies. These are separate from the main sessionMemory.ts to avoid importing runAgent.
Generated from folder role, exports, dependency roots, and inline comments β not hand-reviewed for every path.
π§ Inline summary
Session Memory utility functions that can be imported without circular dependencies. These are separate from the main sessionMemory.ts to avoid importing runAgent.
π€ Exports (heuristic)
SessionMemoryConfigDEFAULT_SESSION_MEMORY_CONFIGgetLastSummarizedMessageIdsetLastSummarizedMessageIdmarkExtractionStartedmarkExtractionCompletedwaitForSessionMemoryExtractiongetSessionMemoryContentsetSessionMemoryConfiggetSessionMemoryConfigrecordExtractionTokenCountisSessionMemoryInitializedmarkSessionMemoryInitializedhasMetInitializationThresholdhasMetUpdateThresholdgetToolCallsBetweenUpdatesresetSessionMemoryState
π₯οΈ Source preview
/**
* 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
}