How Context Trigger Persistence Works with CONTEXT_LAST_RUN_KV_KEY in the Durable KV Store
Context trigger persistence uses the durable KV store to record last-run timestamps for compact and clear operations, ensuring consistent cadence across restarts and config changes.
The munder-difflin repository implements an intelligent context management system that automatically compacts or clears agent context windows based on configurable thresholds. To maintain reliable scheduling across process lifecycles, the system persists execution timestamps using CONTEXT_LAST_RUN_KV_KEY in a durable key-value store.
Understanding the Context Trigger Mechanism
The context trigger (also called "auto-compact / auto-clear") operates on a dual-cadence schedule. It fires at regular intervals only when an agent's live context window exceeds a configurable pressure threshold. Because Node.js processes restart during config edits, system sleep/wake cycles, or deployments, the trigger cannot rely solely on in-memory timers.
According to the source code in src/main/index.ts, the solution stores the last execution time for each trigger half (compact and clear) in a durable KV entry. This allows syncContextTriggers() to calculate remaining time accurately across restarts rather than resetting the interval on every boot.
How CONTEXT_LAST_RUN_KV_KEY Stores Execution State
The persistence mechanism centers on a single constant that identifies the storage location for context trigger metadata.
The KV Key Definition
In src/main/index.ts, the system defines the storage key as a constant string:
const CONTEXT_LAST_RUN_KV_KEY = 'triggers.context.lastRun';
This key holds a JSON map with the structure { compact: number, clear: number }, where each value represents a Unix timestamp in milliseconds.
Lazy Loading from the Durable Store
The contextRunMap() function implements a caching layer over the durable store. It checks an in-memory cache first, then falls back to the KV store via persist.getKv only when necessary. Error handling ensures that missing or corrupted entries default to an empty object rather than crashing the process.
function contextRunMap(): Record<string, number> {
if (!contextLastRun) {
try { contextLastRun = persist.getKv<Record<string, number>>(CONTEXT_LAST_RUN_KV_KEY) ?? {}; }
catch { contextLastRun = {}; }
}
return contextLastRun;
}
This pattern minimizes I/O operations while guaranteeing that the system can always recover from storage failures.
Reading and Writing Last-Run Timestamps
The system provides specialized functions for updating and querying the persisted state, both wrapped in try-catch blocks because persistence is treated as "best-effort" rather than critical path.
Stamping a New Execution Time
When a trigger fires successfully, stampContextRun(action) writes the current timestamp to the map and immediately persists it back to the KV store using persist.setKv:
function stampContextRun(action: 'compact' | 'clear'): number {
const map = contextRunMap();
const at = Date.now();
map[action] = at;
try { persist.setKv(CONTEXT_LAST_RUN_KV_KEY, map); } catch { /* DB best-effort */ }
return at;
}
This function returns the timestamp for immediate use in timer calculations while asynchronously updating the durable store.
Retrieving Persisted Timestamps
The contextLastRunAt(action) function retrieves the stored timestamp for a specific action. If the entry does not exist—common during first launch or after data corruption—it automatically stamps a fresh timestamp to prevent the trigger from firing immediately on boot:
function contextLastRunAt(action: 'compact' | 'clear'): number {
const map = contextRunMap();
const v = map[action];
if (typeof v === 'number' && Number.isFinite(v)) return v;
return stampContextRun(action);
}
This defensive programming ensures that new installations or repaired databases do not experience unexpected immediate compaction cycles.
Timer Calculation and Cross-Process Consistency
The syncContextTriggers() function in src/main/index.ts uses the persisted timestamps to compute accurate remaining intervals. By subtracting the stored last-run time from the configured cadence (rule.everyMs), the system honors partial intervals across restarts:
const remaining = Math.max(0, rule.everyMs - (Date.now() - contextLastRunAt(action)));
This calculation guarantees that an operator who edits the context trigger configuration twice a day will still see the trigger fire at the intended interval, rather than resetting to a fresh interval on every edit. The durable KV store bridges the gap between process lifecycles, providing stateful cadence management that respects user expectations.
Practical Code Examples
You can manually query the last run timestamp to build monitoring dashboards or debug trigger behavior:
// Example: manually query the last run timestamp for the 'compact' half
import { persist } from './persist'; // assume persistence helper is exported
const key = 'triggers.context.lastRun';
const map = await persist.getKv<Record<string, number>>(key);
const lastCompact = map?.compact ?? 0;
console.log('Compact last ran at:', new Date(lastCompact));
For maintenance operations such as migrations or resetting stuck triggers, you can overwrite the timestamps directly:
// Example: force-reset the timestamps (e.g., after a migration)
async function resetContextTimestamps() {
const emptyMap = { compact: 0, clear: 0 };
await persist.setKv('triggers.context.lastRun', emptyMap);
}
resetContextTimestamps();
Summary
- CONTEXT_LAST_RUN_KV_KEY (
'triggers.context.lastRun') stores a JSON map of last-run timestamps for compact and clear operations in the durable KV store. - Lazy loading via
contextRunMap()minimizes I/O while providing resilient fallback to empty state on corruption. - Best-effort persistence means updates use try-catch blocks to prevent storage failures from crashing the trigger system.
- Accurate timer calculation uses persisted timestamps to compute
remainingtime, ensuring consistent cadence across process restarts and configuration changes. - Key files include
src/main/index.ts(core logic),src/shared/triggers.ts(default configuration),src/main/config.ts(user config merging), andsrc/main/persist.ts(KV store implementations).
Frequently Asked Questions
What happens if the KV store entry is corrupted or missing?
If persist.getKv throws an error or returns null, the contextRunMap() function catches the exception and initializes an empty object {}. This allows the system to continue operating, and the next trigger execution will stamp a fresh timestamp via contextLastRunAt(), effectively resetting the schedule.
Why does the system use Date.now() rather than a sequence number or counter?
The system uses Unix timestamps (Date.now()) because they support deterministic interval calculation using simple arithmetic subtraction. This approach enables syncContextTriggers() to compute remaining time accurately even if the process was offline for hours or days, whereas a sequence-based system would lose temporal context during downtime.
Can I safely delete the CONTEXT_LAST_RUN_KV_KEY entry to force immediate trigger execution?
Yes, deleting the key or calling resetContextTimestamps() as shown in the examples will cause contextLastRunAt() to return a fresh timestamp on the next check. However, because syncContextTriggers() calculates remaining time based on rule.everyMs, a fresh timestamp means the trigger will wait the full interval before firing unless the context pressure threshold is also exceeded.
How does this persistence interact with the DEFAULT_CONTEXT_TRIGGER configuration?
The DEFAULT_CONTEXT_TRIGGER defined in src/shared/triggers.ts provides the base cadence (everyMs) and pressure thresholds. When src/main/config.ts loads user configuration, it merges custom settings with defaults. The persisted timestamps in CONTEXT_LAST_RUN_KV_KEY are then compared against the merged configuration's everyMs value, ensuring that operator changes to the cadence take effect while still respecting when the trigger last ran under previous settings.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →