Files
Codeman/src/session-task-cache.ts
T
arkonandClaude Opus 4.6 e7a9cbe442 refactor: split god files into focused modules (phase 4, steps 2-4)
Split 3 large files into 11 focused sub-modules via composition:

ralph-tracker.ts (3,868 → ~2,400 LOC):
- ralph-plan-tracker.ts: plan task tracking, checkpoints, history
- ralph-fix-plan-watcher.ts: @fix_plan.md file watching
- ralph-stall-detector.ts: iteration stall detection
- ralph-status-parser.ts: RALPH_STATUS block parsing, circuit breaker

respawn-controller.ts (3,611 → ~3,200 LOC):
- respawn-patterns.ts: pure pattern detection functions
- respawn-adaptive-timing.ts: adaptive timing with percentile calc
- respawn-metrics.ts: cycle metrics tracking + aggregation
- respawn-health.ts: pure health scoring functions

session.ts (2,418 → ~1,800 LOC):
- session-cli-builder.ts: CLI argument construction
- session-auto-ops.ts: auto-compact/clear automation
- session-task-cache.ts: task description LRU cache

All external APIs preserved via delegation. Events forwarded
through parent classes. Zero behavioral changes — all 436 tests pass.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-01 04:04:35 +01:00

102 lines
3.0 KiB
TypeScript

/**
* @fileoverview LRU cache for task descriptions parsed from terminal output.
*
* Stores descriptions extracted from Claude Code's Task tool invocations
* (e.g., "Explore(Check files)") keyed by timestamp. Used to correlate
* with SubagentWatcher discoveries for better window titles.
*
* @module session-task-cache
*/
import { LRUMap } from './utils/lru-map.js';
/** Default maximum number of task descriptions to keep */
const DEFAULT_MAX_SIZE = 100;
/** Default maximum age for task descriptions (30 seconds) */
const DEFAULT_MAX_AGE_MS = 30_000;
/**
* LRU cache for task descriptions parsed from terminal output.
*
* Descriptions are keyed by the timestamp when they were parsed.
* Old entries are automatically cleaned up based on maxAgeMs.
* Size is bounded by the underlying LRUMap.
*/
export class SessionTaskCache {
private readonly cache: LRUMap<number, string>;
private readonly maxAgeMs: number;
constructor(maxSize: number = DEFAULT_MAX_SIZE, maxAgeMs: number = DEFAULT_MAX_AGE_MS) {
this.cache = new LRUMap<number, string>({ maxSize });
this.maxAgeMs = maxAgeMs;
}
/**
* Add a task description at the given timestamp.
*/
add(timestamp: number, description: string): void {
this.cache.set(timestamp, description);
}
/**
* Remove task descriptions older than maxAgeMs.
* LRUMap maintains insertion order, so we can break early
* once we find a non-expired entry.
*/
private cleanupOld(): void {
const cutoff = Date.now() - this.maxAgeMs;
for (const timestamp of this.cache.keysInOrder()) {
if (timestamp < cutoff) {
this.cache.delete(timestamp);
} else {
break;
}
}
}
/**
* Get all recent task descriptions sorted by timestamp (most recent first).
*/
getAll(): Array<{ timestamp: number; description: string }> {
this.cleanupOld();
const results: Array<{ timestamp: number; description: string }> = [];
for (const [timestamp, description] of this.cache) {
results.push({ timestamp, description });
}
return results.sort((a, b) => b.timestamp - a.timestamp);
}
/**
* Find a task description that was parsed close to a given timestamp.
* Used to correlate with SubagentWatcher discoveries.
*
* @param subagentStartTime - The timestamp when the subagent was discovered
* @param maxAgeMs - Maximum age difference to consider (default 10 seconds)
* @returns The matching description or undefined
*/
findNear(subagentStartTime: number, maxAgeMs: number = 10000): string | undefined {
this.cleanupOld();
let bestMatch: { timestamp: number; description: string } | undefined;
let bestDiff = Infinity;
for (const [timestamp, description] of this.cache) {
const diff = Math.abs(subagentStartTime - timestamp);
if (diff < maxAgeMs && diff < bestDiff) {
bestMatch = { timestamp, description };
bestDiff = diff;
}
}
return bestMatch?.description;
}
/**
* Clear all cached descriptions.
*/
clear(): void {
this.cache.clear();
}
}