import { describe, it, expect } from 'vitest'; import { parseLastExchange } from './secretary-transcript.mjs'; describe('parseLastExchange', () => { it('тащит последний user + assistant + действия', () => { const t = [ JSON.stringify({ message: { role: 'user', content: 'старое' } }), JSON.stringify({ message: { role: 'user', content: 'привет' } }), JSON.stringify({ message: { role: 'assistant', content: [ { type: 'text', text: 'ответ' }, { type: 'tool_use', name: 'Read', input: { f: 'x' } }, ] } }), ].join('\n'); const ex = parseLastExchange(t); expect(ex.user).toBe('привет'); expect(ex.assistant).toBe('ответ'); expect(ex.actions).toEqual([{ tool: 'Read', input: '{"f":"x"}' }]); }); it('строковый content тоже понимает; битые строки пропускает', () => { const t = ['не-json', JSON.stringify({ message: { role: 'user', content: 'у' } }), JSON.stringify({ message: { role: 'assistant', content: 'а' } })].join('\n'); const ex = parseLastExchange(t); expect(ex.user).toBe('у'); expect(ex.assistant).toBe('а'); expect(ex.actions).toEqual([]); }); it('пропускает tool_result (role:user) — берёт настоящий промпт + все действия', () => { const t = [ JSON.stringify({ message: { role: 'user', content: 'настоящий вопрос' } }), JSON.stringify({ message: { role: 'assistant', content: [ { type: 'text', text: 'думаю' }, { type: 'tool_use', name: 'Read', input: { f: 'a' } }] } }), JSON.stringify({ message: { role: 'user', content: [{ type: 'tool_result', content: 'результат' }] } }), JSON.stringify({ message: { role: 'assistant', content: [{ type: 'text', text: 'готово' }] } }), ].join('\n'); const ex = parseLastExchange(t); expect(ex.user).toBe('настоящий вопрос'); expect(ex.assistant).toContain('думаю'); expect(ex.assistant).toContain('готово'); expect(ex.actions).toEqual([{ tool: 'Read', input: '{"f":"a"}' }]); }); }); describe('parseLastExchange — захват выдачи инструмента (tool_result по tool_use_id)', () => { it('привязывает результат к действию по совпадающему id', () => { const t = [ JSON.stringify({ message: { role: 'user', content: 'вопрос' } }), JSON.stringify({ message: { role: 'assistant', content: [ { type: 'tool_use', id: 'tu_1', name: 'Read', input: { f: 'a' } }] } }), JSON.stringify({ message: { role: 'user', content: [ { type: 'tool_result', tool_use_id: 'tu_1', content: 'СОДЕРЖИМОЕ ФАЙЛА' }] } }), ].join('\n'); const ex = parseLastExchange(t); expect(ex.actions).toEqual([{ tool: 'Read', input: '{"f":"a"}', result: 'СОДЕРЖИМОЕ ФАЙЛА' }]); }); it('результат из массива text-блоков склеивается', () => { const t = [ JSON.stringify({ message: { role: 'user', content: 'в' } }), JSON.stringify({ message: { role: 'assistant', content: [ { type: 'tool_use', id: 'tu_9', name: 'Bash', input: {} }] } }), JSON.stringify({ message: { role: 'user', content: [ { type: 'tool_result', tool_use_id: 'tu_9', content: [{ type: 'text', text: 'строка вывода' }] }] } }), ].join('\n'); const ex = parseLastExchange(t); expect(ex.actions[0].result).toBe('строка вывода'); }); it('длинный результат усечён и оканчивается маркером …', () => { const big = 'x'.repeat(5000); const t = [ JSON.stringify({ message: { role: 'user', content: 'в' } }), JSON.stringify({ message: { role: 'assistant', content: [ { type: 'tool_use', id: 'tu_2', name: 'Read', input: {} }] } }), JSON.stringify({ message: { role: 'user', content: [ { type: 'tool_result', tool_use_id: 'tu_2', content: big }] } }), ].join('\n'); const ex = parseLastExchange(t); expect(ex.actions[0].result.length).toBeLessThan(big.length); expect(ex.actions[0].result.endsWith('…')).toBe(true); }); it('без совпадающего id результат не привязывается — старая форма {tool,input} цела', () => { const t = [ JSON.stringify({ message: { role: 'user', content: 'в' } }), JSON.stringify({ message: { role: 'assistant', content: [ { type: 'tool_use', id: 'tu_3', name: 'Read', input: { f: 'z' } }] } }), ].join('\n'); const ex = parseLastExchange(t); expect(ex.actions).toEqual([{ tool: 'Read', input: '{"f":"z"}' }]); }); });