diff --git a/builds/typescript/client_web/src/api/useGatewayChat.ts b/builds/typescript/client_web/src/api/useGatewayChat.ts index bf3ba9b..35070f8 100644 --- a/builds/typescript/client_web/src/api/useGatewayChat.ts +++ b/builds/typescript/client_web/src/api/useGatewayChat.ts @@ -181,7 +181,10 @@ export function useGatewayChat(options: UseGatewayChatOptions = {}): { conversationIdRef.current = restored.conversationId; backgroundStates.delete(cacheKey); } else { - setMessages(externalMessages); + // Always start empty when switching conversations — externalMessages may be stale + // from the previous project's history that hasn't cleared yet. The secondary effect + // (below) will apply the correct history once it arrives from the async fetch. + setMessages(EMPTY_MESSAGES); setIsLoading(false); setError(null); setToolStatus(null); @@ -234,9 +237,12 @@ export function useGatewayChat(options: UseGatewayChatOptions = {}): { } async function resolveApproval(requestId: string, decision: ApprovalDecision): Promise { + // Capture the tool name before removing the approval so we can show + // a user-friendly status ("Writing to your library...") instead of "Approval approved" + const approvalToolName = pendingApprovals.find((a) => a.requestId === requestId)?.toolName; await submitApprovalDecision(requestId, decision); setPendingApprovals((current) => current.filter((approval) => approval.requestId !== requestId)); - setToolStatus(`Approval ${decision}`); + setToolStatus(decision === "approved" && approvalToolName ? approvalToolName : null); setActivity((current) => appendActivity(current, { id: nextActivityId(), @@ -509,9 +515,9 @@ export function useGatewayChat(options: UseGatewayChatOptions = {}): { message: `Approval required for ${humanizeToolName(event.tool_name)}`, }); if (isActive()) { - setToolStatus(`Approval required: ${event.tool_name}`); + setToolStatus(event.tool_name); } else { - updateBackground(() => ({ toolStatus: `Approval required: ${event.tool_name}` })); + updateBackground(() => ({ toolStatus: event.tool_name })); } break; case "approval-result": @@ -521,11 +527,8 @@ export function useGatewayChat(options: UseGatewayChatOptions = {}): { message: `Approval ${event.decision}`, status: event.decision, }); - if (isActive()) { - setToolStatus(`Approval ${event.decision}`); - } else { - updateBackground(() => ({ toolStatus: `Approval ${event.decision}` })); - } + // Don't set toolStatus here — resolveApproval already set it to the + // tool name (so it shows "Writing to your library..." not "Approval approved") break; } } diff --git a/builds/typescript/client_web/src/components/chat/ChatPanel.tsx b/builds/typescript/client_web/src/components/chat/ChatPanel.tsx index b1a64c9..1b16dba 100644 --- a/builds/typescript/client_web/src/components/chat/ChatPanel.tsx +++ b/builds/typescript/client_web/src/components/chat/ChatPanel.tsx @@ -92,7 +92,6 @@ export default function ChatPanel({ conversationId, toolStatus, pendingApprovals, - activity, append, resolveApproval, stop @@ -173,8 +172,9 @@ export default function ChatPanel({ const lastMessage = messages.length > 0 ? messages[messages.length - 1] : null; const hasStartedAssistantReply = isLoading && lastMessage?.role === "assistant"; - const isTyping = isLoading && !hasStartedAssistantReply; - const typingStatus = isTyping + const isWaitingForReply = isLoading && !hasStartedAssistantReply; + const showTypingFeedback = isLoading && pendingApprovals.length === 0; + const typingStatus = isLoading ? toolStatus ? formatToolStatus(toolStatus) : "Thinking..." @@ -272,7 +272,7 @@ export default function ChatPanel({ onRemoveAttachment: () => setAttachment(null), fileError, onClearFileError: () => setFileError(null), - isStreaming: isTyping, + isStreaming: isWaitingForReply, onStop: stop }; @@ -339,7 +339,7 @@ export default function ChatPanel({ ) : ( {visibleChatError && ( @@ -415,26 +415,6 @@ export default function ChatPanel({ )} - {activity.length > 0 && ( -
-
- Action Timeline -
-
- {activity.slice(-8).map((item) => ( -
- {item.message} - - {item.type} - -
- ))} -
-
- )}
)) : contentOverride}