397777089e
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
58 lines
2.8 KiB
JavaScript
58 lines
2.8 KiB
JavaScript
#!/usr/bin/env node
|
|
/**
|
|
* mentor-journal-store — персист журнала переговоров наставника (F-C2-4) + вердикта.
|
|
* Формат журнала = action-journal (jsonl-цепь + head-подпись [action-journal.mjs:83-119]);
|
|
* файлы mentor-journal-<sess>.jsonl/.head + mentor-verdict-<sess>.json. Atomic temp→rename
|
|
* (зеркало saveTaskId F-C1 / writeAtomic plan-lock). Node fs — НЕ Write-tool.
|
|
*/
|
|
import fsDefault from 'node:fs';
|
|
import { assertSafeSessionId } from './action-journal.mjs';
|
|
|
|
function paths(runtimeDir, sessionId) {
|
|
assertSafeSessionId(sessionId);
|
|
const sep = runtimeDir.endsWith('/') ? '' : '/';
|
|
return {
|
|
jsonl: `${runtimeDir}${sep}mentor-journal-${sessionId}.jsonl`,
|
|
head: `${runtimeDir}${sep}mentor-journal-${sessionId}.head`,
|
|
verdict: `${runtimeDir}${sep}mentor-verdict-${sessionId}.json`,
|
|
};
|
|
}
|
|
|
|
function writeAtomic(path, data, fsImpl) {
|
|
const tmp = `${path}.tmp`;
|
|
fsImpl.writeFileSync(tmp, data);
|
|
fsImpl.renameSync(tmp, path);
|
|
}
|
|
|
|
/** Загрузить цепь переговоров (F-C2-4: ИМЕННО это идёт в onPlanWrite.journalEntries). */
|
|
export function loadMentorJournal({ sessionId, runtimeDir, fsImpl = fsDefault }) {
|
|
const { jsonl, head } = paths(runtimeDir, sessionId);
|
|
let entries = [];
|
|
try {
|
|
entries = String(fsImpl.readFileSync(jsonl, 'utf8')).split('\n').filter(Boolean).map((l) => JSON.parse(l));
|
|
} catch (e) {
|
|
if (e && e.code === 'ENOENT') return { entries: [], headSig: null };
|
|
throw e;
|
|
}
|
|
let headSig = null;
|
|
try { headSig = String(fsImpl.readFileSync(head, 'utf8')).trim() || null; } catch { headSig = null; }
|
|
return { entries, headSig };
|
|
}
|
|
|
|
/** Персист полной цепи (выход onPlanWrite.journal = {entries, headSig}) — atomic. */
|
|
export function persistMentorJournal({ journal, sessionId, runtimeDir, fsImpl = fsDefault }) {
|
|
const { jsonl, head } = paths(runtimeDir, sessionId);
|
|
const lines = (journal && Array.isArray(journal.entries) ? journal.entries : []).map((e) => JSON.stringify(e)).join('\n');
|
|
writeAtomic(jsonl, lines ? lines + '\n' : '', fsImpl);
|
|
writeAtomic(head, String((journal && journal.headSig) ?? ''), fsImpl);
|
|
}
|
|
|
|
/** Персист/чтение последнего вердикта наставника (потребитель — freeze-gate в пути печати). */
|
|
export function persistMentorVerdict({ record, sessionId, runtimeDir, fsImpl = fsDefault }) {
|
|
writeAtomic(paths(runtimeDir, sessionId).verdict, JSON.stringify(record ?? null), fsImpl);
|
|
}
|
|
export function loadMentorVerdict({ sessionId, runtimeDir, fsImpl = fsDefault }) {
|
|
try { return JSON.parse(fsImpl.readFileSync(paths(runtimeDir, sessionId).verdict, 'utf8')); }
|
|
catch (e) { if (e && e.code === 'ENOENT') return null; throw e; }
|
|
}
|