Show tool detail and outcomes in the transcript

Each tool line carries the arguments that identify the call - the paths a batch read is about to pull in, the files a patch touches - and its result line carries a one-line outcome. Worker approvals are labelled as subagent asks, and a subagent result attaches to its step in the panel.

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
This commit is contained in:
Muhammad Zakir Ramadhan
2026-09-03 16:43:13 +07:00
co-authored by Sisyphus
parent 6df41b56d2
commit 55ebb40524
3 changed files with 269 additions and 30 deletions
+25 -1
View File
@@ -151,7 +151,7 @@ test('the info panel renders a markdown body', () => {
});
test('the active tool line names the tool and the file it is touching', () => {
const app = render(<ActiveTool name="read_file" summary="src/session.ts" />);
const app = render(<ActiveTool name="read_file" detail={['src/session.ts']} />);
const frame = app.lastFrame() ?? '';
expect(frame).toContain('read_file');
expect(frame).toContain('src/session.ts');
@@ -164,6 +164,18 @@ test('the active tool line renders before the arguments have arrived', () => {
app.unmount();
});
test('the active tool line shows several detail lines and caps the rest', () => {
const detail = Array.from({ length: 9 }, (_, i) => `src/file${i}.ts`);
const app = render(<ActiveTool name="read_many_files" detail={detail} />);
const frame = app.lastFrame() ?? '';
// One line is never enough for a batch read: which paths are about to enter the
// context is the whole point of showing it.
expect(frame).toContain('src/file0.ts');
expect(frame).toContain('src/file5.ts');
expect(frame).toContain('3 more');
app.unmount();
});
test('thinking collapses to a token count, and expands on request', () => {
const text = 'x'.repeat(1648);
const collapsed = render(<ThinkingPanel text={text} />);
@@ -214,6 +226,18 @@ test('subagent events fold into the panel view', () => {
expect(view[1]).toMatchObject({ id: 'b', status: 'failed', error: 'exploded' });
});
test('a subagent result attaches to its step without clearing the panel state', () => {
const started = applySubagentEvent([], { type: 'start', id: 'a', kind: 'explore', description: 'find auth' });
const stepped = applySubagentEvent(started, { type: 'step', id: 'a', tool: 'grep', summary: 'login' });
const view = applySubagentEvent(stepped, { type: 'result', id: 'a', tool: 'grep', summary: '2 hits', ok: true });
expect(view).toHaveLength(1);
expect(view[0]).toMatchObject({
id: 'a',
steps: [{ tool: 'grep', summary: 'login', outcome: '2 hits', ok: true }],
});
});
test('an event for an unknown id is ignored rather than throwing', () => {
const view = applySubagentEvent([], { type: 'step', id: 'ghost', tool: 'grep', summary: 'x' });
expect(view).toEqual([]);