Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 14 additions & 10 deletions packages/web-shell/client/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13098,10 +13098,12 @@ export function App({
>
<DrawerTitle className="sr-only">Right panel</DrawerTitle>
<WebShellCustomizationProvider value={customization}>
<ArtifactPanel
{...artifactPanelSharedProps}
variant="drawer"
/>
<CompactModeContext.Provider value={compactMode}>
<ArtifactPanel
{...artifactPanelSharedProps}
variant="drawer"
/>
</CompactModeContext.Provider>
</WebShellCustomizationProvider>
</DrawerContent>
</Drawer>
Expand Down Expand Up @@ -13166,12 +13168,14 @@ export function App({
/>
)}
<WebShellCustomizationProvider value={customization}>
<div className={styles.artifactPanelClip}>
<ArtifactPanel
{...artifactPanelSharedProps}
panelWidth={artifactPanelWidth}
/>
</div>
<CompactModeContext.Provider value={compactMode}>
<div className={styles.artifactPanelClip}>
<ArtifactPanel
{...artifactPanelSharedProps}
panelWidth={artifactPanelWidth}
/>
</div>
</CompactModeContext.Provider>
</WebShellCustomizationProvider>
</div>,
artifactPanelSlotEl,
Expand Down
26 changes: 11 additions & 15 deletions packages/web-shell/client/adapters/transcriptToMessages.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -829,7 +829,7 @@ describe('transcriptBlocksToDaemonMessages', () => {
]);
});

it('keeps each TodoWrite call as a distinct tool entry with its own todos', () => {
it('merges adjacent TodoWrite calls into one tool group like any tool', () => {
const messages = transcriptBlocksToDaemonMessages([
toolBlock('todo-1', 'todo-call-1', 'completed', 1, {
title: 'Update Todos',
Expand Down Expand Up @@ -859,9 +859,7 @@ describe('transcriptBlocksToDaemonMessages', () => {
}),
]);

// Each TodoWrite update stands alone in its own group (it renders as a
// self-contained collapsible checklist), rather than merging with adjacent
// tool calls.
// TodoWrite is an ordinary tool: adjacent calls share one group box.
expect(messages).toEqual([
{
id: 'tg-todo-1',
Expand All @@ -872,13 +870,6 @@ describe('transcriptBlocksToDaemonMessages', () => {
callId: 'todo-call-1',
toolName: 'TodoWrite',
}),
],
},
{
id: 'tg-todo-2',
role: 'tool_group',
timestamp: 2,
tools: [
expect.objectContaining({
callId: 'todo-call-2',
toolName: 'TodoWrite',
Expand Down Expand Up @@ -1028,7 +1019,7 @@ describe('transcriptBlocksToDaemonMessages', () => {
]);
});

it('never merges todo_write updates into or after a regular tool_group', () => {
it('merges todo_write updates with adjacent tool calls like any tool', () => {
const messages = transcriptBlocksToDaemonMessages([
toolBlock('t1', 'tc1', 'completed', 1, { toolName: 'Read' }),
toolBlock('todo-1', 'todo-call-1', 'completed', 2, {
Expand All @@ -1040,9 +1031,14 @@ describe('transcriptBlocksToDaemonMessages', () => {
]);

expect(messages).toMatchObject([
{ role: 'tool_group', tools: [{ callId: 'tc1' }] },
{ role: 'tool_group', tools: [{ callId: 'todo-call-1' }] },
{ role: 'tool_group', tools: [{ callId: 'tc2' }] },
{
role: 'tool_group',
tools: [
{ callId: 'tc1' },
{ callId: 'todo-call-1' },
{ callId: 'tc2' },
],
},
]);
});

Expand Down
8 changes: 2 additions & 6 deletions packages/web-shell/client/adapters/transcriptToMessages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@ import type {
DaemonMessageTodoItem,
DaemonUserMessage,
} from './messageTypes.js';
import { isTodoWriteToolName } from '../utils/todos.js';

interface PermissionToolInfo {
title?: string;
Expand Down Expand Up @@ -999,11 +998,8 @@ function appendToolCallMessage(
//
// Synthetic raw-shell groups (pushed by the `shell` block fallback) use the
// bare block id without the `tg-` prefix and never absorb real tool calls.
// Sub-agent calls and todo_write updates each stand alone in their own group
// box instead of being crammed in with the tools around them: an agent renders
// an expandable panel, and a todo update is its own collapsible checklist.
const isStandalone = (t: DaemonMessageToolCall) =>
isSubAgentToolCall(t) || isTodoWriteToolName(t.toolName);
// TodoWrite merges like any other tool.
const isStandalone = (t: DaemonMessageToolCall) => isSubAgentToolCall(t);
const last = messages[messages.length - 1];
if (
last &&
Expand Down
39 changes: 38 additions & 1 deletion packages/web-shell/client/components/MessageItem.dom.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,16 @@ vi.mock('./messages/AssistantMessage', async () => {
};
});
vi.mock('./messages/SystemMessage', () => ({ SystemMessage: () => null }));
vi.mock('./messages/ToolGroup', () => ({ ToolGroup: () => null }));
vi.mock('./messages/ToolGroup', async () => {
const React = await import('react');
return {
ToolGroup: ({ compactSummary }: { compactSummary?: boolean }) =>
React.createElement('div', {
'data-testid': 'tool-group',
'data-compact-summary': String(compactSummary === true),
}),
};
});
vi.mock('./messages/PlanMessage', () => ({ PlanMessage: () => null }));
vi.mock('./messages/BtwMessage', () => ({ BtwMessage: () => null }));
vi.mock('./messages/UserShellMessage', () => ({
Expand Down Expand Up @@ -230,6 +239,34 @@ describe('MessageItem selectable wrapper', () => {
});

describe('MessageItem tool group spacing', () => {
it('marks only synthetic groups as compact summaries', () => {
const synthetic = render(
<I18nProvider language="en">
<CompactModeContext.Provider value={true}>
{item(toolMsg('summary-agent-1'))}
</CompactModeContext.Provider>
</I18nProvider>,
);
const regular = render(
<I18nProvider language="en">
<CompactModeContext.Provider value={true}>
{item(toolMsg('agent-1'))}
</CompactModeContext.Provider>
</I18nProvider>,
);

expect(
synthetic
.querySelector('[data-testid="tool-group"]')
?.getAttribute('data-compact-summary'),
).toBe('true');
expect(
regular
.querySelector('[data-testid="tool-group"]')
?.getAttribute('data-compact-summary'),
).toBe('false');
});

it('uses larger row spacing only in compact mode', () => {
const compact = render(
<I18nProvider language="en">
Expand Down
1 change: 1 addition & 0 deletions packages/web-shell/client/components/MessageItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,7 @@ export const MessageItem = memo(function MessageItem({
<ToolGroup
tools={message.tools}
thoughts={message.thoughts}
compactSummary={compactMode && message.id.startsWith('summary-')}
pendingApproval={pendingApproval}
Comment thread
ytahdn marked this conversation as resolved.
workspaceCwd={workspaceCwd}
isLocateFlashing={isLocateFlashing}
Expand Down
Loading
Loading