Summary
Replace the flat chat.Message{Role, Content} with a part-aware model. handleResponse() dispatches by StreamChunk.Type to accumulate structured parts. renderMessage() dispatches per part type to the right component (ToolCallBlock, DiffBlock, CodeBlock, ErrorBlock, markdown).
Scope
In:
MessagePart type with PartText, PartThinking, PartToolCall, PartError
ToolCallPart struct with ID, Name, DisplayType, State, Result, Error
Message.Parts []MessagePart replacing flat Content string
handleResponse() dispatching by chunk type to accumulate parts
renderMessage() dispatching per part to correct component
- Tool call rendering by DisplayType: diff->DiffBlock, code->CodeBlock, bash->CodeBlock, generic->ToolCallBlock
- Thinking parts as dim collapsed text
- Backward compat:
Raw field for gallery mode
Out:
- Streaming markdown optimization (SB-013 — additive later)
- Interactive tool call expansion/collapse (future)
- Iteration status display (skip for MVP)
Key Files
app/cli/internal/chat/model.go # MODIFY — Message, MessagePart types, handleResponse dispatch
app/cli/internal/chat/view.go # MODIFY — renderMessage per-part dispatch
Acceptance Criteria
Depends On
SB-027
Spec
docs/specs/2026-03-22-message-parts.md (Phases 3-4)
Summary
Replace the flat
chat.Message{Role, Content}with a part-aware model.handleResponse()dispatches byStreamChunk.Typeto accumulate structured parts.renderMessage()dispatches per part type to the right component (ToolCallBlock, DiffBlock, CodeBlock, ErrorBlock, markdown).Scope
In:
MessageParttype withPartText,PartThinking,PartToolCall,PartErrorToolCallPartstruct with ID, Name, DisplayType, State, Result, ErrorMessage.Parts []MessagePartreplacing flatContentstringhandleResponse()dispatching by chunk type to accumulate partsrenderMessage()dispatching per part to correct componentRawfield for gallery modeOut:
Key Files
Acceptance Criteria
Depends On
SB-027
Spec
docs/specs/2026-03-22-message-parts.md(Phases 3-4)