diff --git a/tools/secretary-reconcile.mjs b/tools/secretary-reconcile.mjs index 5df00bf..44881dd 100644 --- a/tools/secretary-reconcile.mjs +++ b/tools/secretary-reconcile.mjs @@ -275,13 +275,15 @@ export async function reconcileTurn({ proto, ex, turn, session, callModel, diag catch (e) { report({ reason: 'model-threw', error: String((e && e.message) || e) }); return null; } const parsed = parseReconcileResponse(typeof text === 'string' ? text : ''); if (!parsed) { report({ reason: 'bad-json' }); return null; } // кривой JSON — прежний протокол цел + const step = parsed.step || null; + const finish = (p) => { const out = collapseProtocol(p); return step ? { ...out, step } : out; }; const returned = collapseProtocol(parsed); const guard = reconcileGuard(clean, returned); - if (guard.ok) return collapseProtocol(stampProvenance(clean, returned, turn, session)); + if (guard.ok) return finish(stampProvenance(clean, returned, turn, session)); // Потеряны строки → НЕ выкидываем ход: возвращаем пропавшие старые строки на место (модель-агностично). // Что модель уронила — хук вернул; что обновила (закрыла вопрос, добавила решение) — сохранено. report({ reason: 'guard-restored', lost: guard.lost }); - return collapseProtocol(stampProvenance(clean, restoreLostLines(clean, returned), turn, session)); + return finish(stampProvenance(clean, restoreLostLines(clean, returned), turn, session)); } /** Протокол к записи независимо от исхода reconcile: при успехе база — updated, при срыве — diff --git a/tools/secretary-reconcile.test.mjs b/tools/secretary-reconcile.test.mjs index e681c27..005a6b0 100644 --- a/tools/secretary-reconcile.test.mjs +++ b/tools/secretary-reconcile.test.mjs @@ -164,6 +164,14 @@ describe('reconcileTurn', () => { expect(out).toBeNull(); expect(n).toBe(1); }); + it('проброс step из ответа модели в результат; без step — поля нет', async () => { + const withStep = async () => '{ "subject":"дело", "decisions":[{"text":"A","struck":false}], "open":[{"text":"Q?","struck":true}], "will":[], "doneNext":[], "step":{"user":"u","assistant":"a"} }'; + const r1 = await reconcileTurn({ proto, ex, turn: 5, session: 's1', callModel: withStep }); + expect(r1.step).toEqual({ user: 'u', assistant: 'a' }); + const noStep = async () => '{ "subject":"дело", "decisions":[{"text":"A","struck":false}], "open":[{"text":"Q?","struck":true}], "will":[], "doneNext":[] }'; + const r2 = await reconcileTurn({ proto, ex, turn: 5, session: 's1', callModel: noStep }); + expect(r2.step).toBeUndefined(); + }); }); import { mergeTurnIntoProtocol, formatReconcileLogLine, restoreLostLines, collapseProtocol } from './secretary-reconcile.mjs';