FE-541: Rich chat UI with tool calls and reasoning rendering#15
Conversation
FE-541 Rich chat UI: tool calls + reasoning rendering
Ask about this escape hatch. Separate useChat instance scoped to current question. Responses don't pollute main interview transcript or entity extraction. |
🤖 Augment PR SummarySummary: This PR adds first-class tool-call streaming support across the server domain events, SSE adapter, and React client so the UI can render tool-call lifecycle state alongside reasoning/text. Changes:
Technical Notes: Tool-call events now carry real IDs to support multiple tool calls and to avoid losing tool names/args through the adapter layers. 🤖 Was this summary useful? React with 👍 or 👎 |
| yield { type: 'text-delta', delta: delta.text }; | ||
| } else if (delta.type === 'input_json_delta' && delta.partial_json) { | ||
| const toolBlock = toolUseBlocks.get(event.index!); | ||
| yield { type: 'tool-call-delta', toolCallId: toolBlock?.toolCallId ?? '', delta: delta.partial_json }; |
There was a problem hiding this comment.
src/server/core.ts:116 — If toolUseBlocks.get(event.index!) returns undefined, this still yields a tool-call-delta with toolCallId: '', which can collide across calls and makes downstream correlation ambiguous. Consider not emitting a tool-call event unless there’s a registered tool block for that index.
Severity: medium
🤖 Was this useful? React with 👍 or 👎, or 🚀 if it prevented an incident/outage.
| } | ||
|
|
||
| case 'tool-call-delta': { | ||
| currentToolArgsJson += event.delta; |
There was a problem hiding this comment.
src/server/sse-adapter.ts:198 — createDomainAdapter() appends every tool-call-delta into a single currentToolArgsJson buffer without checking/associating by event.toolCallId, so interleaved tool calls could produce a final tool-call event with mixed or incorrect args. Consider tracking args per toolCallId (or asserting only one active tool call) to keep concurrency-safe behavior consistent with the new IDs.
Severity: medium
🤖 Was this useful? React with 👍 or 👎, or 🚀 if it prevented an incident/outage.
5cd25b7 to
edd3dc7
Compare
Extend DomainEvent with tool-call-start/delta/end variants, translate SDK tool_use content blocks through SSE adapter as tool-call-streaming-start/ tool-call-delta/tool-call events, and render dynamic-tool parts in the React client. Adds 10 new tests (3 core, 6 SSE adapter, 1 app integration). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
tool-call-delta now carries toolCallId (was missing — blocked concurrent tool calls). tool-call-end now carries toolName (was lost — adapter emitted empty string). Domain adapter uses real ids instead of synthetic ones. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
edd3dc7 to
4cba4a9
Compare

feat: add tool-call lifecycle events to core, SSE adapter, and client
Extend DomainEvent with tool-call-start/delta/end variants, translate
SDK tool_use content blocks through SSE adapter as tool-call-streaming-start/
tool-call-delta/tool-call events, and render dynamic-tool parts in the
React client. Adds 10 new tests (3 core, 6 SSE adapter, 1 app integration).
Co-Authored-By: Claude Opus 4.6 (1M context) noreply@anthropic.com
chore: gitignore .claude/worktrees/
Co-Authored-By: Claude Opus 4.6 (1M context) noreply@anthropic.com
refactor: enrich tool-call DomainEvents with toolCallId and toolName
tool-call-delta now carries toolCallId (was missing — blocked concurrent
tool calls). tool-call-end now carries toolName (was lost — adapter emitted
empty string). Domain adapter uses real ids instead of synthetic ones.
Co-Authored-By: Claude Opus 4.6 (1M context) noreply@anthropic.com