From b7057b1eda97417960c00ca4748e2eacff88285e Mon Sep 17 00:00:00 2001
From: Zane <75694352+zanesq@users.noreply.github.com>
Date: Thu, 17 Jul 2025 19:43:35 -0700
Subject: [PATCH] Fix tool call allow still showing initial state in chat after
navigating back (#3498)
---
ui/desktop/src/components/GooseMessage.tsx | 2 +-
.../src/components/ToolCallConfirmation.tsx | 98 ++++++++++++++++---
2 files changed, 84 insertions(+), 16 deletions(-)
diff --git a/ui/desktop/src/components/GooseMessage.tsx b/ui/desktop/src/components/GooseMessage.tsx
index 1fea8eee..62bbe953 100644
--- a/ui/desktop/src/components/GooseMessage.tsx
+++ b/ui/desktop/src/components/GooseMessage.tsx
@@ -220,7 +220,7 @@ export default function GooseMessage({
{hasToolConfirmation && (
diff --git a/ui/desktop/src/components/ToolCallConfirmation.tsx b/ui/desktop/src/components/ToolCallConfirmation.tsx
index f2f9dfcc..764a4604 100644
--- a/ui/desktop/src/components/ToolCallConfirmation.tsx
+++ b/ui/desktop/src/components/ToolCallConfirmation.tsx
@@ -1,4 +1,4 @@
-import { useState } from 'react';
+import { useState, useEffect } from 'react';
import { snakeToTitleCase } from '../utils';
import PermissionModal from './settings/permission/PermissionModal';
import { ChevronRight } from 'lucide-react';
@@ -9,6 +9,17 @@ const ALWAYS_ALLOW = 'always_allow';
const ALLOW_ONCE = 'allow_once';
const DENY = 'deny';
+// Global state to track tool confirmation decisions
+// This persists across navigation within the same session
+const toolConfirmationState = new Map<
+ string,
+ {
+ clicked: boolean;
+ status: string;
+ actionDisplay: string;
+ }
+>();
+
interface ToolConfirmationProps {
isCancelledMessage: boolean;
isClicked: boolean;
@@ -22,30 +33,75 @@ export default function ToolConfirmation({
toolConfirmationId,
toolName,
}: ToolConfirmationProps) {
- const [clicked, setClicked] = useState(isClicked);
- const [status, setStatus] = useState('unknown');
- const [actionDisplay, setActionDisplay] = useState('');
+ // Check if we have a stored state for this tool confirmation
+ const storedState = toolConfirmationState.get(toolConfirmationId);
+
+ // Initialize state from stored state if available, otherwise use props/defaults
+ const [clicked, setClicked] = useState(storedState?.clicked ?? isClicked);
+ const [status, setStatus] = useState(storedState?.status ?? 'unknown');
+ const [actionDisplay, setActionDisplay] = useState(storedState?.actionDisplay ?? '');
const [isModalOpen, setIsModalOpen] = useState(false);
- const handleButtonClick = async (action: string) => {
- setClicked(true);
- setStatus(action);
- if (action === ALWAYS_ALLOW) {
- setActionDisplay('always allowed');
- } else if (action === ALLOW_ONCE) {
- setActionDisplay('allowed once');
- } else {
- setActionDisplay('denied');
+ // Sync internal state with stored state and props
+ useEffect(() => {
+ const currentStoredState = toolConfirmationState.get(toolConfirmationId);
+
+ // If we have stored state, use it
+ if (currentStoredState) {
+ setClicked(currentStoredState.clicked);
+ setStatus(currentStoredState.status);
+ setActionDisplay(currentStoredState.actionDisplay);
+ } else if (isClicked && !clicked) {
+ // Fallback to prop-based logic for historical confirmations
+ setClicked(isClicked);
+ if (status === 'unknown') {
+ setStatus('confirmed');
+ setActionDisplay('confirmed');
+
+ // Store this state for future renders
+ toolConfirmationState.set(toolConfirmationId, {
+ clicked: true,
+ status: 'confirmed',
+ actionDisplay: 'confirmed',
+ });
+ }
}
+ }, [isClicked, clicked, status, toolName, toolConfirmationId]);
+
+ const handleButtonClick = async (action: string) => {
+ const newClicked = true;
+ const newStatus = action;
+ let newActionDisplay = '';
+
+ if (action === ALWAYS_ALLOW) {
+ newActionDisplay = 'always allowed';
+ } else if (action === ALLOW_ONCE) {
+ newActionDisplay = 'allowed once';
+ } else {
+ newActionDisplay = 'denied';
+ }
+
+ // Update local state
+ setClicked(newClicked);
+ setStatus(newStatus);
+ setActionDisplay(newActionDisplay);
+
+ // Store in global state for persistence across navigation
+ toolConfirmationState.set(toolConfirmationId, {
+ clicked: newClicked,
+ status: newStatus,
+ actionDisplay: newActionDisplay,
+ });
+
try {
const response = await confirmPermission({
body: { id: toolConfirmationId, action, principal_type: 'Tool' },
});
if (response.error) {
- console.error('Failed to confirm permission: ', response.error);
+ console.error('Failed to confirm permission:', response.error);
}
} catch (err) {
- console.error('Error fetching tools:', err);
+ console.error('Error confirming permission:', err);
}
};
@@ -106,6 +162,18 @@ export default function ToolConfirmation({
)}
+ {status === 'confirmed' && (
+
+ )}
{isClicked
? 'Tool confirmation is not available'