Domain: Retention — Function Reference
Lifecycle governance for memory, RAG (Retrieval-Augmented Generation), and audit data. Manages retention policies for two isolated domains: memory and rag. Policies are persisted atomically in data/state/retention-policies.json. Provides rules evaluation (eligible vs. protected) and archive manifest creation. Does NOT directly delete records — callers use the eligible list.
MCP Tools Exposed
Exposed through unified controller. Key tool names: retention_configure, retention_get, retention_apply, retention_archive, retention_policies.
| Tool Name | Description | Key Inputs | Returns |
|---|---|---|---|
retention_configure |
Set retention policy for a domain | domain: memory\|rag, config: Partial<Policy> |
{ success, domain, policy, updated_at } |
retention_get |
Get current policy for a domain | domain: memory\|rag |
RetentionPolicy object |
retention_apply |
Evaluate resources against policy | domain, resources[], options? |
{ success, eligible[], protected[], counts } |
retention_archive |
Archive resources to cold storage | domain, resources[], options? |
{ success, archive_id, manifest_path, resource_count, total_size_bytes } |
retention_policies |
Get all policies (legacy) | none | { version, updated_at, memory, rag } |
Core Functions
configureRetentionPolicy(domain, config)
Purpose: Merge partial config onto existing policy with bounds clamping, then save.
Parameters: domain: "memory"|"rag", config: Partial<RetentionPolicy>
Returns: { success, domain, policy, updated_at }
Side effects: Writes atomically to data/state/retention-policies.json.
Notes for rewrite: Enforces INV-RET-1 (atomic write), INV-RET-4 (bounds), INV-RET-8 (ISO timestamp).
getRetentionPolicy(domain)
Purpose: Read the current retention policy for a domain (no mutation).
Parameters: domain: "memory"|"rag"
Returns: RetentionPolicy object.
applyRetentionRules(domain, resources, options)
Purpose: Classify resources into eligible (can be deleted) vs. protected. Does NOT delete anything.
Parameters: domain: "memory"|"rag", resources: Resource[], options?: object
Returns: { success, domain, cutoff_date, policy_applied, eligible[], protected[], counts: { eligible, protected, total } }
Algorithm: See filtering logic below.
getRetentionPolicies()
Purpose: Return both policies together. DEPRECATED — use getRetentionPolicy(domain).
Returns: { version, updated_at, memory, rag }
setRetentionPolicy(domain, patch)
Purpose: Alias for configureRetentionPolicy. DEPRECATED.
resolveGcParamsWithPolicy(domain, rawArgs, validatedArgs)
Purpose: Merge explicit GC call arguments with policy defaults. Explicit args win over policy defaults.
Parameters: domain, rawArgs: object (raw tool args), validatedArgs: object (validated)
Returns: GcParams — all GC parameters resolved to final values.
Notes for rewrite: Used by GC tools to apply policy-based defaults when tool caller omits parameters.
Database Operations
None. All persistence is via JSON files in data/state/ and data/archive/.
Key Algorithms
Atomic Write (atomicWriteFileSync)
tmpPath = filePath + ".tmp-" + pid + "-" + timestamp
writeFileSync(tmpPath, data)
renameSync(tmpPath, filePath) // atomic
on error: unlinkSync(tmpPath), rethrow
Policy Normalization (normalizePolicy)
older_than_days: clamp(value, 1, 3650, fallback) // INV-RET-4
source_types: filter to { context, action, thought }, dedup, fallback if empty // INV-RET-3
keep_session_ids: dedup string array
keep_context_ids: dedup positive integer array
limit: clamp(value, 1, 20000, fallback) // INV-RET-4
vacuum: boolean, fallback if non-boolean
auto_gc: boolean, fallback if non-boolean
Retention Rules Evaluation (applyRetentionRules)
cutoffDate = now - older_than_days days
keepSessionIds = Set(policy.keep_session_ids)
keepContextIds = Set(policy.keep_context_ids)
for each resource:
if resource.session_id in keepSessionIds: protected (reason: protected_session)
elif resource.context_id in keepContextIds: protected (reason: protected_context)
elif resource.source_type not in ALLOWED_SOURCE_TYPES: protected (reason: invalid_source_type)
elif resource.created_at > cutoffDate: protected (reason: within_retention_period)
else: eligible
limitedEligible = eligible.slice(0, policy.limit)
RetentionPolicy Schema
{
older_than_days: number (1-3650, default 30)
source_types: string[] (subset of ["context","action","thought"])
keep_session_ids: string[]
keep_context_ids: number[]
limit: number (1-20000, default 5000)
vacuum: boolean (default false)
auto_gc: boolean (default false)
}
8 Core Invariants
- INV-RET-1: All policy updates are atomic (write-to-temp-then-rename)
- INV-RET-2:
memoryandragpolicies are isolated — changes to one never affect the other - INV-RET-3: Only
{context, action, thought}are valid source types; invalid types filtered at normalization - INV-RET-4:
older_than_daysclamped to [1, 3650];limitclamped to [1, 20000] - INV-RET-5: Resources in
keep_session_idsorkeep_context_idsare never eligible - INV-RET-6:
DEFAULT_POLICYisObject.freeze()d — modifications always create new objects - INV-RET-7: State includes
versionfield for migration support - INV-RET-8:
updated_atis always ISO 8601, set on every mutation