feat(secretary): buildStepLine принимает готовую суть (essence)
Task 2/5 плана. Если передан essence{user,assistant} — берём его дословно
(+ чистка пробелов); иначе прежний фолбэк firstSentence. «делал: <tools>»
остаётся детерминированным. Свод секретаря 110/110.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -61,7 +61,7 @@ export function buildStepsFromRaw(rawText, session) {
|
||||
// Человекочитаемая строка шага для раздела «Шаги (Слой 1)»: «Ход N — я: … · ты: … · делал: …».
|
||||
// Суть — первая фраза реплики; служебные строки (экономия/coverage/вердикт) отброшены;
|
||||
// «делал» — имена инструментов из действий хода. Название файла полного хода добавляет рендер.
|
||||
export function buildStepLine({ turn, user, assistant, actions = [] } = {}) {
|
||||
export function buildStepLine({ turn, user, assistant, actions = [], essence = null } = {}) {
|
||||
// Содержательная фраза: убираем ведущую нумерацию списка («1.»/«2)»), копим до ≥25 симв.,
|
||||
// чтобы не выдать обрывок «Стоп.»; длинное усекаем.
|
||||
const firstSentence = (s) => {
|
||||
@@ -82,8 +82,11 @@ export function buildStepLine({ turn, user, assistant, actions = [] } = {}) {
|
||||
};
|
||||
const cleanA = String(assistant ?? '').split('\n')
|
||||
.filter((l) => !/^\s*(экономия:|coverage:|вердикт:)/i.test(l)).join(' ');
|
||||
const u = sysLabel(user) || firstSentence(user) || '(без вопроса)';
|
||||
const a = firstSentence(cleanA) || '(без ответа)';
|
||||
const clean1 = (s) => String(s ?? '').replace(/\s+/g, ' ').trim();
|
||||
const eU = essence && clean1(essence.user);
|
||||
const eA = essence && clean1(essence.assistant);
|
||||
const u = eU || sysLabel(user) || firstSentence(user) || '(без вопроса)';
|
||||
const a = eA || firstSentence(cleanA) || '(без ответа)';
|
||||
const did = [...new Set((actions || []).map((t) => String(t).trim()).filter(Boolean))].join(', ') || '—';
|
||||
return `Ход ${turn} — я: ${u} · ты: ${a} · делал: ${did}`;
|
||||
}
|
||||
|
||||
@@ -99,6 +99,17 @@ describe('buildStepLine', () => {
|
||||
expect(buildStepLine({ turn: 1, user: 'Stop hook feedback: coverage missing', assistant: '' })).toContain('я: (гейт проверки)');
|
||||
expect(buildStepLine({ turn: 2, user: 'Base directory for this skill: C:\\x\\skills\\writing-plans\\SKILL.md', assistant: 'x.' })).toContain('я: (навык: writing-plans)');
|
||||
});
|
||||
it('essence: берёт модельную суть дословно + детерминированный «делал»', () => {
|
||||
const s = buildStepLine({ turn: 12, user: 'длинная вода без точек '.repeat(10),
|
||||
assistant: 'вода', actions: ['Read', 'Read', 'Grep'],
|
||||
essence: { user: 'промпт не логируется?', assistant: 'достать можно: поймать или пересобрать' } });
|
||||
expect(s).toBe('Ход 12 — я: промпт не логируется? · ты: достать можно: поймать или пересобрать · делал: Read, Grep');
|
||||
});
|
||||
it('без essence — прежний фолбэк (firstSentence)', () => {
|
||||
const s = buildStepLine({ turn: 2, user: 'сделай флажок.', assistant: 'Готово.', essence: null });
|
||||
expect(s).toContain('я: сделай флажок');
|
||||
expect(s).toContain('ты: Готово');
|
||||
});
|
||||
});
|
||||
|
||||
describe('writeFileAtomic — запись через temp + rename (защита от полузаписи при параллельных сессиях)', () => {
|
||||
|
||||
Reference in New Issue
Block a user