feat(chat): add inline activity rows for tool results#40
Conversation
…ic summaries instead of raw raw Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
|
Caution Review failedThe pull request is closed. ℹ️ Recent review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (6)
📝 WalkthroughWalkthroughAssistant chat rendering now filters tool UI from assistant rows, groups codemode executions with child tool activities, and renders tool activity summaries in chat parts. The workspace prompt now requires plain-English tool-call titles, and the thinking-mark component accepts a configurable class name. ChangesAI chat tool activity flow
Sequence Diagram(s)sequenceDiagram
participant AiChatMessageRow
participant getAssistantRowDisplay
participant AiChatMessagePartView
participant AiChatToolActivityRow
AiChatMessageRow->>getAssistantRowDisplay: get assistant row display
getAssistantRowDisplay-->>AiChatMessageRow: hidden or content parts
alt content parts
AiChatMessageRow->>AiChatMessagePartView: render each AiChatRenderablePart
AiChatMessagePartView->>AiChatToolActivityRow: render tool UI / data-tool-group parts
AiChatToolActivityRow-->>AiChatMessagePartView: summary, shimmer, nested child summaries
end
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
Poem
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
Greptile SummaryThis PR replaces raw JSON tool cards with lightweight inline activity rows, adding deterministic human-readable summaries for all workspace, web, research, and codemode tools and hiding invisible tool calls (
Confidence Score: 4/5Safe to merge — the changes are purely presentational and all tool data access is guarded through defensive helpers that fall back to empty arrays/strings on unexpected output shapes. The new activity row component has a non-unique React key pattern that will produce console warnings (and potential list-reconciliation issues) whenever codemode calls the same tool more than once with an identical result string. The codemodeIndex tautology in getDisplayableParts is dead code rather than a runtime defect. The summarizeWorkspaceEdit success path is inconsistent with its failure counterpart. None of these would break the feature in normal use, but the key collision is worth addressing before the component sees heavy streaming traffic. AiChatToolActivityRow.tsx — non-unique child keys and missing chevron state feedback; ai-chat-display-state.ts — redundant codemodeIndex condition and missing input-path fallback in summarizeWorkspaceEdit. Important Files Changed
Reviews (1): Last reviewed commit: "style(ai-chat): increase running shimmer..." | Re-trigger Greptile |
| <div | ||
| key={`${child.toolName}:${child.summary}`} |
There was a problem hiding this comment.
Non-unique React keys for repeated tool calls
The key ${child.toolName}:${child.summary} will collide when codemode invokes the same tool more than once with the same outcome — for example, two web_search calls that each return five results both produce the key "web_search:Found 5 sources". React will warn and may fail to reconcile the list correctly if items are ever reordered or removed during a streaming update. Using the iteration index as a tiebreaker (e.g., ${index}-${child.toolName}:${child.summary}) makes keys stable and unique.
Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!
| return ( | ||
| <Collapsible className="w-fit max-w-full"> | ||
| <CollapsibleTrigger className="w-fit max-w-full text-left"> | ||
| <ActivitySummary activity={activity} canExpand /> | ||
| </CollapsibleTrigger> | ||
| <CollapsibleContent className="mt-2 space-y-2 pl-7"> | ||
| {nestedChildren.length > 0 ? ( | ||
| <div className="space-y-1"> | ||
| {nestedChildren.map((child) => ( | ||
| <div | ||
| key={`${child.toolName}:${child.summary}`} | ||
| className="text-muted-foreground/80 text-sm" | ||
| > | ||
| {child.summary} | ||
| </div> | ||
| ))} | ||
| </div> | ||
| ) : null} | ||
| </CollapsibleContent> | ||
| </Collapsible> | ||
| ); |
There was a problem hiding this comment.
The inner
nestedChildren.length > 0 guard is dead code — hasDetails (already checked above) guarantees this block is only entered with a non-empty array. Collapsing it also fixes the non-unique key issue noted on line 40.
| return ( | |
| <Collapsible className="w-fit max-w-full"> | |
| <CollapsibleTrigger className="w-fit max-w-full text-left"> | |
| <ActivitySummary activity={activity} canExpand /> | |
| </CollapsibleTrigger> | |
| <CollapsibleContent className="mt-2 space-y-2 pl-7"> | |
| {nestedChildren.length > 0 ? ( | |
| <div className="space-y-1"> | |
| {nestedChildren.map((child) => ( | |
| <div | |
| key={`${child.toolName}:${child.summary}`} | |
| className="text-muted-foreground/80 text-sm" | |
| > | |
| {child.summary} | |
| </div> | |
| ))} | |
| </div> | |
| ) : null} | |
| </CollapsibleContent> | |
| </Collapsible> | |
| ); | |
| return ( | |
| <Collapsible className="w-fit max-w-full"> | |
| <CollapsibleTrigger className="w-fit max-w-full text-left"> | |
| <ActivitySummary activity={activity} canExpand /> | |
| </CollapsibleTrigger> | |
| <CollapsibleContent className="mt-2 space-y-2 pl-7"> | |
| <div className="space-y-1"> | |
| {nestedChildren.map((child, index) => ( | |
| <div | |
| key={`${index}-${child.toolName}:${child.summary}`} | |
| className="text-muted-foreground/80 text-sm" | |
| > | |
| {child.summary} | |
| </div> | |
| ))} | |
| </div> | |
| </CollapsibleContent> | |
| </Collapsible> | |
| ); |
| <Shimmer as="span" className="text-sm text-muted-foreground" duration={1.4}> | ||
| {activity.summary} | ||
| </Shimmer> | ||
| {canExpand ? <ChevronDown className="size-3 shrink-0" aria-hidden="true" /> : null} |
There was a problem hiding this comment.
Chevron does not reflect open/closed state
The ChevronDown icon always points downward regardless of whether the Collapsible is open or closed. Radix's CollapsibleTrigger exposes data-state="open" / data-state="closed" on the trigger element, but there's no CSS or class-based rotation wired up. Users get no visual feedback that the section can be collapsed after opening it — the affordance is asymmetric and relies on muscle memory after the first click.
Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!
| result.push({ | ||
| type: "data-tool-group", | ||
| part, | ||
| children: index === codemodeIndex ? codemodeChildren : [], | ||
| }); |
There was a problem hiding this comment.
index === codemodeIndex is always true in this branch
codemodeIndex is parts.indexOf(codemodePart), and the loop enters the part === codemodePart branch exactly once — at that same index. The ternary therefore always evaluates to codemodeChildren and can never produce []. The condition can be removed without changing behaviour, and leaving it suggests to future readers that there is a scenario where children should be empty here, which there isn't.
Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!
| function summarizeWorkspaceEdit(output: unknown) { | ||
| const record = asRecord(output); | ||
| return `Updated ${quoteName(getBaseName(getString(record.path)))}`; | ||
| } |
There was a problem hiding this comment.
summarizeWorkspaceEdit success case has no input-path fallback
summarizeFailure for workspace_edit_item falls back to getPathFromToolInput(part.input) when the output doesn't contain a path field. The success path in summarizeWorkspaceEdit lacks this fallback — if the output's path is absent the display silently degrades to Updated item even though the input path is available. Applying the same fallback keeps the two paths consistent.
This PR replaces the current JSON-heavy tool card UI with subtle inline activity rows above assistant replies, making the chat cleaner for non-technical users.
Changes:
ai-chat-display-state.ts: Add
AiChatToolActivitymodel andgetToolActivity()function that derives one visible top-level tool row per message, with optional children for nested tools. Tool parts are no longer treated as displayable content in normal chat. Add deterministic summarizers for all workspace (create/delete/move/rename/edit/list/read), web (search/markdown/links), research (discover/deepen), and codemode tools with counts and item names. Hidesandbox_bashandtime_*tools by default.AiChatToolActivityRow.tsx: New component rendering a single inline activity row with spinner or icon, title, and deterministic summary (e.g.,
Updating workspace — Created 3 items). Supports disclosure of inner tool calls (children) and input/output details without exposing raw JSON by default. Uses gray/muted styling for failures to avoid alarming users.AiChatAssistantPending.tsx: Export
ThinkExThinkingMarkwith optionalclassNameprop for reuse in activity rows during running state.AiChatMessageRow.tsx: Render
AiChatToolActivityRowabove assistant message prose when a visibleactivityis present, keeping tool UI separate from text content.Behavior:
Need help on this PR? Tag
/codesmithwith what you need. Autofix is enabled.Summary by cubic
Replace JSON tool cards with inline activity rows at each tool call, showing deterministic summaries with higher-contrast shimmered progress; tool text matches assistant body size and the prompt enforces short present‑progressive tool titles. Expansion is restricted to codemode child summaries, meeting ENG-119 by keeping tool activity readable and unobtrusive.
New Features
AiChatToolActivityRowwith higher-contrast shimmered progress, concise title, and deterministic summary; expands only for codemode to show nested tool summaries.AiChatToolActivitywithgetToolActivityForPart()and summarizers for workspace (create/delete/move/rename/edit/list/read), web (search/markdown/links), research (discover/deepen), and codemode; hidessandbox_bashandtime_*. Prompt now requires short, present‑progressive titles (e.g., “Reading workspace”).AiChatMessagePartView, groupingcodemode_executeas a single row with aggregated children; exportedThinkExThinkingMarkwith optionalclassName.Bug Fixes
Written for commit 8d16db2. Summary will update on new commits.
Summary by CodeRabbit
New Features
Bug Fixes