Search result cards rendered inline, then rescued from the collapsible wrapper
feat-004 makes every `legal_search` and `web_search` result visible as a clickable card in the assistant message regardless of whether Olava synthesizes anything useful. bug-003, 45 minutes later, fixes the UX failure where those cards landed inside the collapsible "Completed in N steps" wrapper and were invisible by default.
The backend change adds a ReferenceResult type and threads it through runToolCalls. Both the legal and web search dispatch cases now push one ReferenceResult per result and emit a reference_added SSE event live as results arrive. Those events get pushed into the assistant message's events[] array, so reloading the chat keeps all cards visible.
On the frontend, a new ReferenceBlock component renders title, source label, date, snippet, and a blue dot connector, with a hover state and new-tab navigation on click. The AssistantMessage.tsx event grouper dispatches reference_added events inline between content blocks.
The bug: the grouper treated reference_added as pre-response chatter and accumulated them into PreResponseWrapper alongside other tool events. That wrapper collapses to "Completed in N steps" by default. When Olava under-synthesizes - which it does on multi-source research queries (the motivating test case had Olava emit \n\n three times alongside 11 web references) - the source cards were invisible until the user expanded the disclosure. The fix extends the EventGroup discriminated union with a "reference" kind that breaks out of the pre-wrapper exactly like a "content" event does.
The comment in the diff is worth noting: feat-005's research orchestrator later reverses this - reference cards go back inside the wrapper in research mode because synthesis is reliably non-empty there. If you're importing both features, do them together.
Spotted something wrong? Or know the PR text has fresher detail than the writeup above?