feat(chat): render tool-call chips and live peek status in mobile bubbles

The mobile app was receiving SSE status events but showing the peek text
only while the assistant bubble was empty — as soon as the first token
arrived, the status line was replaced by streaming content and any tool
calls fired mid-turn left no trace. Tool call SSE events were also being
dropped by the parser, and Message.fromJson never read the persisted
tool_calls array, so chips never rendered after reload either.

Parse tool_call SSE frames into a new ChatToolCall event, carry tool
calls on Message, and update the chat and briefing streaming loops to
append chips to the in-flight assistant message as they arrive. Rework
ChatMessageBubble to show a chip row + rolling peek status line above
any streamed text, matching the web ToolCallCard/status indicator
behaviour across chat, briefing, and briefing history surfaces.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-04-13 17:48:16 -04:00
parent 6771ec5e81
commit a3fe0b4b61
7 changed files with 297 additions and 57 deletions
+12
View File
@@ -143,6 +143,18 @@ class MessagesNotifier extends AsyncNotifier<List<Message>> {
} else if (event is ChatStatusUpdate) {
ref.read(streamingStatusProvider(convId).notifier).state =
event.status;
} else if (event is ChatToolCall) {
// Append the tool call to the in-flight assistant message so the
// chip appears live. The reload pass at the end of this function
// will overwrite with the persisted version, which carries the
// same shape — no de-dup needed.
final msgs = state.requireValue;
if (msgs.isEmpty) continue;
final last = msgs.last;
if (last.role != MessageRole.assistant) continue;
final nextCalls = [...?last.toolCalls, event.toolCall];
final updated = last.copyWith(toolCalls: nextCalls);
state = AsyncData([...msgs.sublist(0, msgs.length - 1), updated]);
}
}
} catch (_) {