77 lines
1.9 KiB
TypeScript
77 lines
1.9 KiB
TypeScript
import { join } from "node:path";
|
|
|
|
import { config } from "../config.js";
|
|
import {
|
|
atomicWriteJson,
|
|
ensureDirectory,
|
|
readJsonFile,
|
|
} from "../utils/fileStore.js";
|
|
|
|
export type LearningSessionState = {
|
|
lastGatedTurn: number;
|
|
lastReviewedTurn: number;
|
|
pendingReview: boolean;
|
|
sessionId: string;
|
|
updatedAt: string;
|
|
};
|
|
|
|
export class LearningStateStore {
|
|
constructor(private readonly baseDir = config.LEARNING_STATE_STORAGE_DIR) {}
|
|
|
|
async initialize() {
|
|
await ensureDirectory(this.baseDir);
|
|
}
|
|
|
|
async read(sessionId: string): Promise<LearningSessionState> {
|
|
const existing = await readJsonFile<LearningSessionState>(this.filePath(sessionId));
|
|
if (existing) {
|
|
return existing;
|
|
}
|
|
return {
|
|
lastGatedTurn: 0,
|
|
lastReviewedTurn: 0,
|
|
pendingReview: false,
|
|
sessionId,
|
|
updatedAt: new Date(0).toISOString(),
|
|
};
|
|
}
|
|
|
|
async write(state: LearningSessionState) {
|
|
await atomicWriteJson(this.filePath(state.sessionId), {
|
|
...state,
|
|
updatedAt: new Date().toISOString(),
|
|
});
|
|
}
|
|
|
|
async markPending(sessionId: string, pendingReview: boolean) {
|
|
const current = await this.read(sessionId);
|
|
await this.write({
|
|
...current,
|
|
pendingReview,
|
|
});
|
|
}
|
|
|
|
async completeReview(sessionId: string, reviewedTurnCount: number) {
|
|
const current = await this.read(sessionId);
|
|
await this.write({
|
|
...current,
|
|
lastGatedTurn: Math.max(current.lastGatedTurn, reviewedTurnCount),
|
|
lastReviewedTurn: reviewedTurnCount,
|
|
pendingReview: false,
|
|
});
|
|
}
|
|
|
|
async completeGate(sessionId: string, gatedTurnCount: number) {
|
|
const current = await this.read(sessionId);
|
|
await this.write({
|
|
...current,
|
|
lastGatedTurn: gatedTurnCount,
|
|
pendingReview: false,
|
|
});
|
|
}
|
|
|
|
private filePath(sessionId: string) {
|
|
return join(this.baseDir, `${sessionId}.json`);
|
|
}
|
|
}
|