diff --git a/tools/secretary-layer1.mjs b/tools/secretary-layer1.mjs index 6461a77..a548e4d 100644 --- a/tools/secretary-layer1.mjs +++ b/tools/secretary-layer1.mjs @@ -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}`; } diff --git a/tools/secretary-layer1.test.mjs b/tools/secretary-layer1.test.mjs index f5e6385..72194ac 100644 --- a/tools/secretary-layer1.test.mjs +++ b/tools/secretary-layer1.test.mjs @@ -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 (защита от полузаписи при параллельных сессиях)', () => {