init
This commit is contained in:
@@ -0,0 +1,207 @@
|
||||
/**
|
||||
* 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
|
||||
}
|
||||
Reference in New Issue
Block a user