3fb98fa51c
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
102 lines
4.5 KiB
JavaScript
102 lines
4.5 KiB
JavaScript
#!/usr/bin/env node
|
|
/** round-memory-store — транзиентная память кругов переговоров по задаче (SP2a).
|
|
* Хранит версии артефакта, дословные замечания (по стороне mentor|judge), доводы контроллера
|
|
* (по дорожке M|J). Ключ (taskId, stage∈{spec,plan}). Fail-quiet: любая ошибка → no-op/пусто.
|
|
* Структура файла — зеркало verdict-surface-store (один JSON на задачу в runtime). */
|
|
import { homedir } from 'node:os';
|
|
import { join } from 'node:path';
|
|
import { existsSync, readFileSync, writeFileSync, mkdirSync } from 'node:fs';
|
|
import { diffLines } from './version-diff.mjs';
|
|
|
|
function baseOf(baseDir) { return baseDir || join(homedir(), '.claude', 'runtime'); }
|
|
function memPath(taskId, baseDir) { return join(baseOf(baseDir), `round-memory-${taskId || 'unknown'}.json`); }
|
|
|
|
function readMem(taskId, baseDir) {
|
|
try {
|
|
const p = memPath(taskId, baseDir);
|
|
if (!existsSync(p)) return {};
|
|
const v = JSON.parse(readFileSync(p, 'utf8'));
|
|
return (v && typeof v === 'object') ? v : {};
|
|
} catch { return {}; }
|
|
}
|
|
|
|
function writeMem(taskId, baseDir, obj) {
|
|
try {
|
|
mkdirSync(baseOf(baseDir), { recursive: true });
|
|
writeFileSync(memPath(taskId, baseDir), JSON.stringify(obj));
|
|
return true;
|
|
} catch { return false; }
|
|
}
|
|
|
|
function pushInto(taskId, baseDir, section, key, value) {
|
|
const m = readMem(taskId, baseDir);
|
|
m[section] = m[section] || {};
|
|
if (!Array.isArray(m[section][key])) m[section][key] = [];
|
|
m[section][key].push(value);
|
|
return writeMem(taskId, baseDir, m);
|
|
}
|
|
|
|
function readArr(taskId, baseDir, section, key) {
|
|
const m = readMem(taskId, baseDir);
|
|
const arr = m[section] && m[section][key];
|
|
return Array.isArray(arr) ? arr : [];
|
|
}
|
|
|
|
const str = (t) => String(t == null ? '' : t);
|
|
|
|
export function recordVersion(taskId, stage, text, baseDir) {
|
|
return pushInto(taskId, baseDir, 'versions', stage, str(text));
|
|
}
|
|
export function getVersions(taskId, stage, baseDir) {
|
|
return readArr(taskId, baseDir, 'versions', stage);
|
|
}
|
|
export function diffVersions(taskId, stage, baseDir) {
|
|
const v = getVersions(taskId, stage, baseDir);
|
|
if (v.length < 2) return '';
|
|
return diffLines(v[v.length - 2], v[v.length - 1]);
|
|
}
|
|
|
|
export function recordObjection(taskId, stage, side, text, baseDir) {
|
|
return pushInto(taskId, baseDir, 'objections', `${stage}:${side}`, str(text));
|
|
}
|
|
export function getObjections(taskId, stage, side, baseDir) {
|
|
return readArr(taskId, baseDir, 'objections', `${stage}:${side}`);
|
|
}
|
|
|
|
export function recordArg(taskId, stage, track, text, baseDir) {
|
|
return pushInto(taskId, baseDir, 'args', `${stage}:${track}`, str(text));
|
|
}
|
|
export function getArgs(taskId, stage, track, baseDir) {
|
|
return readArr(taskId, baseDir, 'args', `${stage}:${track}`);
|
|
}
|
|
|
|
export function clearRoundMemory(taskId, baseDir) {
|
|
try {
|
|
const p = memPath(taskId, baseDir);
|
|
if (existsSync(p)) writeFileSync(p, '{}');
|
|
return true;
|
|
} catch { return false; }
|
|
}
|
|
|
|
/** SP2c-2: собрать память кругов для построителя промпта (судья J-side / наставник M-side).
|
|
* side='judge' → дорожка J, свои judge-замечания, БЕЗ замечания-при-возврате (судья холодный);
|
|
* side='mentor' → дорожка M, свои mentor-замечания + ПОСЛЕДНЕЕ замечание судьи (при возврате).
|
|
* versionDiff = diff(последняя сохранённая версия, текущая); пусто пока прошлой версии нет
|
|
* (круг 1 слеп). Fail-quiet → пустая память. */
|
|
export function buildRoundMemory({ taskId, stage, side, currentContent = '', baseDir } = {}) {
|
|
try {
|
|
const track = side === 'judge' ? 'J' : 'M';
|
|
const versions = getVersions(taskId, stage, baseDir);
|
|
const prev = versions.length ? versions[versions.length - 1] : null;
|
|
const versionDiff = prev != null ? diffLines(prev, String(currentContent ?? '')) : '';
|
|
const objections = getObjections(taskId, stage, side, baseDir);
|
|
const args = getArgs(taskId, stage, track, baseDir);
|
|
let judgeObjectionOnReturn = '';
|
|
if (side === 'mentor') {
|
|
const jo = getObjections(taskId, stage, 'judge', baseDir);
|
|
if (jo.length) judgeObjectionOnReturn = jo[jo.length - 1];
|
|
}
|
|
return { objections, args, versionDiff, judgeObjectionOnReturn };
|
|
} catch { return { objections: [], args: [], versionDiff: '', judgeObjectionOnReturn: '' }; }
|
|
}
|