release 1.0.1: fix resume showing a blank transcript

A resumed session loaded its wire messages but never rebuilt the on-screen
history, so -r/-c and /resume rendered a blank transcript. Convert the stored
messages back into transcript lines and seed the panel with them at mount and
after /resume.
This commit is contained in:
Muhammad Zakir Ramadhan
2026-09-17 19:26:42 +07:00
parent 052423556a
commit 8eebc0f4d6
6 changed files with 212 additions and 9 deletions
+93
View File
@@ -0,0 +1,93 @@
import { expect, test } from 'bun:test';
import { historyFromMessages } from '../src/ui/transcript';
import type { Line } from '../src/ui/transcript';
const kind = (lines: Line[]) => lines.map((l) => l.kind);
test('historyFromMessages maps a saved conversation into transcript lines', () => {
const lines = historyFromMessages([
{ role: 'user', content: 'fix the pagination test' },
{ role: 'assistant', content: 'Looking at the suite' },
{
role: 'assistant',
content: [
{ type: 'text', text: 'Found it.' },
{
type: 'tool-call',
toolCallId: 't1',
toolName: 'edit_file',
input: { path: 'test/PageTest.php', oldString: 'a', newString: 'b' },
},
],
},
{
role: 'tool',
content: [{ type: 'tool-result', toolCallId: 't1', toolName: 'edit_file', output: { type: 'text', value: 'Replaced 1 occurrence' } }],
},
]);
// user + assistant text + assistant text part + tool call + existing call line
expect(kind(lines)).toEqual(['user', 'assistant', 'assistant', 'tool']);
const tool = lines.at(-1)!;
expect(tool.kind).toBe('tool');
if (tool.kind === 'tool') {
expect(tool.result).toBe('Replaced 1 occurrence');
expect(tool.ok).toBe(true);
}
});
test('historyFromMessages unwraps SDK-wrapped and json-wrapped tool outputs', () => {
const lines = historyFromMessages([
{
role: 'assistant',
content: [{ type: 'tool-call', toolCallId: 'g1', toolName: 'grep', input: { pattern: 'TODO' } }],
},
{
role: 'tool',
content: [{ type: 'tool-result', toolCallId: 'g1', toolName: 'grep', output: { type: 'text', value: 'src/a.ts:1: TODO' } }],
},
]);
const line = lines[0]!;
expect(line.kind).toBe('tool');
if (line.kind === 'tool') expect(line.result).toBe('1 hit');
});
test('historyFromMessages marks a tool-error call as failed', () => {
const lines = historyFromMessages([
{
role: 'assistant',
content: [{ type: 'tool-call', toolCallId: 'b1', toolName: 'bash', input: { command: 'false' } }],
},
{
role: 'tool',
content: [{ type: 'tool-error', toolCallId: 'b1', toolName: 'bash', output: { type: 'text', value: 'exit 1' } }],
},
]);
const line = lines[0]!;
expect(line.kind).toBe('tool');
if (line.kind === 'tool') {
expect(line.ok).toBe(false);
expect(line.result).toBe('exit 1');
}
});
test('historyFromMessages drops a tool result with no matching call (pruned lead-in)', () => {
const lines = historyFromMessages([
{ role: 'user', content: 'continue' },
{
role: 'tool',
content: [{ type: 'tool-result', toolCallId: 'gone', toolName: 'grep', output: { type: 'text', value: 'x' } }],
},
]);
// Only the user line remains; the orphaned result is not floated.
expect(kind(lines)).toEqual(['user']);
});
test('historyFromMessages skips system and empty messages', () => {
const lines = historyFromMessages([
{ role: 'system', content: 'You are an agent.' },
{ role: 'user', content: '' },
{ role: 'assistant', content: 'ok' },
]);
expect(kind(lines)).toEqual(['assistant']);
});