| export interface SessionCompactionPolicy { |
| enabled: boolean; |
| maxSessionRuns: number; |
| maxRawInputTokens: number; |
| maxSessionAgeHours: number; |
| } |
|
|
| export type NativeContextManagement = "confirmed" | "likely" | "unknown" | "none"; |
|
|
| export interface AdapterSessionManagement { |
| supportsSessionResume: boolean; |
| nativeContextManagement: NativeContextManagement; |
| defaultSessionCompaction: SessionCompactionPolicy; |
| } |
|
|
| export interface ResolvedSessionCompactionPolicy { |
| policy: SessionCompactionPolicy; |
| adapterSessionManagement: AdapterSessionManagement | null; |
| explicitOverride: Partial<SessionCompactionPolicy>; |
| source: "adapter_default" | "agent_override" | "legacy_fallback"; |
| } |
|
|
| const DEFAULT_SESSION_COMPACTION_POLICY: SessionCompactionPolicy = { |
| enabled: true, |
| maxSessionRuns: 200, |
| maxRawInputTokens: 2_000_000, |
| maxSessionAgeHours: 72, |
| }; |
|
|
| |
| |
| const ADAPTER_MANAGED_SESSION_POLICY: SessionCompactionPolicy = { |
| enabled: true, |
| maxSessionRuns: 0, |
| maxRawInputTokens: 0, |
| maxSessionAgeHours: 0, |
| }; |
|
|
| export const LEGACY_SESSIONED_ADAPTER_TYPES = new Set([ |
| "claude_local", |
| "codex_local", |
| "cursor", |
| "gemini_local", |
| "opencode_local", |
| "pi_local", |
| ]); |
|
|
| export const ADAPTER_SESSION_MANAGEMENT: Record<string, AdapterSessionManagement> = { |
| claude_local: { |
| supportsSessionResume: true, |
| nativeContextManagement: "confirmed", |
| defaultSessionCompaction: ADAPTER_MANAGED_SESSION_POLICY, |
| }, |
| codex_local: { |
| supportsSessionResume: true, |
| nativeContextManagement: "confirmed", |
| defaultSessionCompaction: ADAPTER_MANAGED_SESSION_POLICY, |
| }, |
| cursor: { |
| supportsSessionResume: true, |
| nativeContextManagement: "unknown", |
| defaultSessionCompaction: DEFAULT_SESSION_COMPACTION_POLICY, |
| }, |
| gemini_local: { |
| supportsSessionResume: true, |
| nativeContextManagement: "unknown", |
| defaultSessionCompaction: DEFAULT_SESSION_COMPACTION_POLICY, |
| }, |
| opencode_local: { |
| supportsSessionResume: true, |
| nativeContextManagement: "unknown", |
| defaultSessionCompaction: DEFAULT_SESSION_COMPACTION_POLICY, |
| }, |
| pi_local: { |
| supportsSessionResume: true, |
| nativeContextManagement: "unknown", |
| defaultSessionCompaction: DEFAULT_SESSION_COMPACTION_POLICY, |
| }, |
| }; |
|
|
| function isRecord(value: unknown): value is Record<string, unknown> { |
| return typeof value === "object" && value !== null && !Array.isArray(value); |
| } |
|
|
| function readBoolean(value: unknown): boolean | undefined { |
| if (typeof value === "boolean") return value; |
| if (typeof value === "number") { |
| if (value === 1) return true; |
| if (value === 0) return false; |
| return undefined; |
| } |
| if (typeof value !== "string") return undefined; |
| const normalized = value.trim().toLowerCase(); |
| if (normalized === "true" || normalized === "1" || normalized === "yes" || normalized === "on") { |
| return true; |
| } |
| if (normalized === "false" || normalized === "0" || normalized === "no" || normalized === "off") { |
| return false; |
| } |
| return undefined; |
| } |
|
|
| function readNumber(value: unknown): number | undefined { |
| if (typeof value === "number" && Number.isFinite(value)) { |
| return Math.max(0, Math.floor(value)); |
| } |
| if (typeof value !== "string") return undefined; |
| const parsed = Number(value.trim()); |
| return Number.isFinite(parsed) ? Math.max(0, Math.floor(parsed)) : undefined; |
| } |
|
|
| export function getAdapterSessionManagement(adapterType: string | null | undefined): AdapterSessionManagement | null { |
| if (!adapterType) return null; |
| return ADAPTER_SESSION_MANAGEMENT[adapterType] ?? null; |
| } |
|
|
| export function readSessionCompactionOverride(runtimeConfig: unknown): Partial<SessionCompactionPolicy> { |
| const runtime = isRecord(runtimeConfig) ? runtimeConfig : {}; |
| const heartbeat = isRecord(runtime.heartbeat) ? runtime.heartbeat : {}; |
| const compaction = isRecord( |
| heartbeat.sessionCompaction ?? heartbeat.sessionRotation ?? runtime.sessionCompaction, |
| ) |
| ? (heartbeat.sessionCompaction ?? heartbeat.sessionRotation ?? runtime.sessionCompaction) as Record<string, unknown> |
| : {}; |
|
|
| const explicit: Partial<SessionCompactionPolicy> = {}; |
| const enabled = readBoolean(compaction.enabled); |
| const maxSessionRuns = readNumber(compaction.maxSessionRuns); |
| const maxRawInputTokens = readNumber(compaction.maxRawInputTokens); |
| const maxSessionAgeHours = readNumber(compaction.maxSessionAgeHours); |
|
|
| if (enabled !== undefined) explicit.enabled = enabled; |
| if (maxSessionRuns !== undefined) explicit.maxSessionRuns = maxSessionRuns; |
| if (maxRawInputTokens !== undefined) explicit.maxRawInputTokens = maxRawInputTokens; |
| if (maxSessionAgeHours !== undefined) explicit.maxSessionAgeHours = maxSessionAgeHours; |
|
|
| return explicit; |
| } |
|
|
| export function resolveSessionCompactionPolicy( |
| adapterType: string | null | undefined, |
| runtimeConfig: unknown, |
| ): ResolvedSessionCompactionPolicy { |
| const adapterSessionManagement = getAdapterSessionManagement(adapterType); |
| const explicitOverride = readSessionCompactionOverride(runtimeConfig); |
| const hasExplicitOverride = Object.keys(explicitOverride).length > 0; |
| const fallbackEnabled = Boolean(adapterType && LEGACY_SESSIONED_ADAPTER_TYPES.has(adapterType)); |
| const basePolicy = adapterSessionManagement?.defaultSessionCompaction ?? { |
| ...DEFAULT_SESSION_COMPACTION_POLICY, |
| enabled: fallbackEnabled, |
| }; |
|
|
| return { |
| policy: { |
| enabled: explicitOverride.enabled ?? basePolicy.enabled, |
| maxSessionRuns: explicitOverride.maxSessionRuns ?? basePolicy.maxSessionRuns, |
| maxRawInputTokens: explicitOverride.maxRawInputTokens ?? basePolicy.maxRawInputTokens, |
| maxSessionAgeHours: explicitOverride.maxSessionAgeHours ?? basePolicy.maxSessionAgeHours, |
| }, |
| adapterSessionManagement, |
| explicitOverride, |
| source: hasExplicitOverride |
| ? "agent_override" |
| : adapterSessionManagement |
| ? "adapter_default" |
| : "legacy_fallback", |
| }; |
| } |
|
|
| export function hasSessionCompactionThresholds(policy: Pick< |
| SessionCompactionPolicy, |
| "maxSessionRuns" | "maxRawInputTokens" | "maxSessionAgeHours" |
| >) { |
| return policy.maxSessionRuns > 0 || policy.maxRawInputTokens > 0 || policy.maxSessionAgeHours > 0; |
| } |
|
|