diff --git a/refact-agent/engine/src/tools/tools_execute.rs b/refact-agent/engine/src/tools/tools_execute.rs index 5f78fee30..bb45f147a 100644 --- a/refact-agent/engine/src/tools/tools_execute.rs +++ b/refact-agent/engine/src/tools/tools_execute.rs @@ -203,7 +203,7 @@ pub async fn run_tools( let command_to_match = cmd .command_to_match_against_confirm_deny(&args) .unwrap_or("".to_string()); - generated_tool.push(tool_answer(format!("tool use: command '{command_to_match}' has been denied by the user"), t_call.id.to_string())); + generated_tool.push(tool_answer(format!("Whoops the user didn't like the command '{command_to_match}'. Stop and ask for correction from the user."), t_call.id.to_string())); continue; } _ => {} diff --git a/refact-agent/gui/src/app/middleware.ts b/refact-agent/gui/src/app/middleware.ts index ee4efbaa1..2a416322d 100644 --- a/refact-agent/gui/src/app/middleware.ts +++ b/refact-agent/gui/src/app/middleware.ts @@ -12,6 +12,9 @@ import { newIntegrationChat, chatResponse, setThreadUsage, + setIsWaitingForResponse, + upsertToolCall, + sendCurrentChatToLspAfterToolCallUpdate, } from "../features/Chat/Thread"; import { statisticsApi } from "../services/refact/statistics"; import { integrationsApi } from "../services/refact/integrations"; @@ -32,12 +35,17 @@ import { resetAttachedImagesSlice } from "../features/AttachedImages"; import { nextTip } from "../features/TipOfTheDay"; import { telemetryApi } from "../services/refact/telemetry"; import { CONFIG_PATH_URL, FULL_PATH_URL } from "../services/refact/consts"; -import { resetConfirmationInteractedState } from "../features/ToolConfirmation/confirmationSlice"; +import { + resetConfirmationInteractedState, + updateConfirmationAfterIdeToolUse, +} from "../features/ToolConfirmation/confirmationSlice"; import { updateAgentUsage, updateMaxAgentUsageAmount, } from "../features/AgentUsage/agentUsageSlice"; import { isChatResponseChoice } from "../services/refact"; +import { ideToolCallResponse } from "../hooks/useEventBusForIDE"; +import { upsertToolCallIntoHistory } from "../features/History/historySlice"; const AUTH_ERROR_MESSAGE = "There is an issue with your API key. Check out your API Key or re-login"; @@ -501,3 +509,33 @@ startListening({ } }, }); + +// Tool Call results from ide. +startListening({ + actionCreator: ideToolCallResponse, + effect: (action, listenerApi) => { + const state = listenerApi.getState(); + + listenerApi.dispatch(upsertToolCallIntoHistory(action.payload)); + listenerApi.dispatch(upsertToolCall(action.payload)); + listenerApi.dispatch(updateConfirmationAfterIdeToolUse(action.payload)); + + const pauseReasons = state.confirmation.pauseReasons.filter( + (reason) => reason.tool_call_id !== action.payload.toolCallId, + ); + + if (pauseReasons.length === 0) { + listenerApi.dispatch(resetConfirmationInteractedState()); + listenerApi.dispatch(setIsWaitingForResponse(false)); + } + + if (pauseReasons.length === 0 && action.payload.accepted) { + void listenerApi.dispatch( + sendCurrentChatToLspAfterToolCallUpdate({ + chatId: action.payload.chatId, + toolCallId: action.payload.toolCallId, + }), + ); + } + }, +}); diff --git a/refact-agent/gui/src/components/ChatContent/ChatContent.tsx b/refact-agent/gui/src/components/ChatContent/ChatContent.tsx index 3030a8d22..3f1d80ce9 100644 --- a/refact-agent/gui/src/components/ChatContent/ChatContent.tsx +++ b/refact-agent/gui/src/components/ChatContent/ChatContent.tsx @@ -34,6 +34,7 @@ import { ChatLinks, UncommittedChangesWarning } from "../ChatLinks"; import { telemetryApi } from "../../services/refact/telemetry"; import { PlaceHolderText } from "./PlaceHolderText"; import { UsageCounter } from "../UsageCounter"; +import { getConfirmationPauseStatus } from "../../features/ToolConfirmation/confirmationSlice"; export type ChatContentProps = { onRetry: (index: number, question: UserMessage["content"]) => void; @@ -55,6 +56,7 @@ export const ChatContent: React.FC = ({ const [sendTelemetryEvent] = telemetryApi.useLazySendTelemetryChatEventQuery(); const integrationMeta = useAppSelector(selectIntegration); + const isWaitingForConfirmation = useAppSelector(getConfirmationPauseStatus); const { handleScroll, @@ -119,7 +121,7 @@ export const ChatContent: React.FC = ({ {threadUsage && } - + {showFollowButton && ( diff --git a/refact-agent/gui/src/components/ChatForm/ToolConfirmation.tsx b/refact-agent/gui/src/components/ChatForm/ToolConfirmation.tsx index ece33dfd2..1295ec711 100644 --- a/refact-agent/gui/src/components/ChatForm/ToolConfirmation.tsx +++ b/refact-agent/gui/src/components/ChatForm/ToolConfirmation.tsx @@ -1,4 +1,4 @@ -import React, { useMemo } from "react"; +import React, { useCallback, useMemo } from "react"; import { PATCH_LIKE_FUNCTIONS, useAppDispatch, @@ -88,6 +88,10 @@ export const ToolConfirmation: React.FC = ({ confirmToolUsage(); }; + const handleReject = useCallback(() => { + rejectToolUsage(toolCallIds); + }, [rejectToolUsage, toolCallIds]); + const message = getConfirmationMessage( commands, rules, @@ -101,7 +105,7 @@ export const ToolConfirmation: React.FC = ({ return ( ); @@ -167,9 +171,9 @@ export const ToolConfirmation: React.FC = ({ color="red" variant="surface" size="1" - onClick={rejectToolUsage} + onClick={handleReject} > - Deny + Stop )} @@ -258,7 +262,7 @@ const PatchConfirmation: React.FC = ({ size="1" onClick={rejectToolUsage} > - Deny + Stop diff --git a/refact-agent/gui/src/components/Tools/Texdoc.module.css b/refact-agent/gui/src/components/Tools/Texdoc.module.css index fb6fabb2d..344b8188d 100644 --- a/refact-agent/gui/src/components/Tools/Texdoc.module.css +++ b/refact-agent/gui/src/components/Tools/Texdoc.module.css @@ -18,15 +18,9 @@ margin-top: 0; } -:global(.radix-themes) .textdoc.textdoc__update :global(.hljs.language-diff) { +/* :global(.radix-themes) .textdoc.textdoc__update :global(.hljs.language-diff) { --hlbg: var(--gray-3); --hlcolor1: var(--gray-12); - /* --hlcolor2: #000000; - --hlcolor3: #000080; - --hlcolor4: #800080; - --hlcolor5: #808000; - --hlcolor6: #800000; - --hlcolor7: #0055af; */ --hlcolor8: var(--green-9); --hlcolor9: var(--red-9); } @@ -44,4 +38,4 @@ .textdoc__update .textdoc__diffbox { box-shadow: var(--shadow-1); -} +} */ diff --git a/refact-agent/gui/src/components/Tools/Textdoc.tsx b/refact-agent/gui/src/components/Tools/Textdoc.tsx index 17b63209a..dfafe949e 100644 --- a/refact-agent/gui/src/components/Tools/Textdoc.tsx +++ b/refact-agent/gui/src/components/Tools/Textdoc.tsx @@ -12,17 +12,17 @@ import { isUpdateTextDocToolCall, parseRawTextDocToolCall, } from "./types"; -import { Box, Button, Card, Flex } from "@radix-ui/themes"; +import { Box, Card, Flex, Button } from "@radix-ui/themes"; import { TruncateLeft } from "../Text"; import { Link } from "../Link"; import { useEventsBusForIDE } from "../../hooks/useEventBusForIDE"; import { Markdown } from "../Markdown"; import { filename } from "../../utils/filename"; import styles from "./Texdoc.module.css"; -import { createPatch } from "diff"; -import classNames from "classnames"; +import { useCopyToClipboard } from "../../hooks/useCopyToClipboard"; +import { Reveal } from "../Reveal"; import { useAppSelector } from "../../hooks"; -import { selectCanPaste } from "../../features/Chat"; +import { selectCanPaste, selectChatId } from "../../features/Chat"; import { toolsApi } from "../../services/refact"; import { ErrorCallout } from "../Callout"; import { isRTKResponseErrorWithDetailMessage } from "../../utils"; @@ -56,12 +56,14 @@ export const TextDocTool: React.FC<{ toolCall: RawTextDocTool }> = ({ const TextDocHeader: React.FC<{ toolCall: TextDocToolCall; }> = ({ toolCall }) => { - const { openFile, diffPasteBack, sendToolEditToIde } = useEventsBusForIDE(); + const { openFile, diffPasteBack, sendToolCallToIde } = useEventsBusForIDE(); const [requestDryRun, dryRunResult] = toolsApi.useDryRunForEditToolMutation(); const [errorMessage, setErrorMessage] = useState(""); const canPaste = useAppSelector(selectCanPaste); + const chatId = useAppSelector(selectChatId); const clearErrorMessage = useCallback(() => setErrorMessage(""), []); + // move this const handleOpenFile = useCallback(() => { if (!toolCall.function.arguments.path) return; @@ -70,9 +72,9 @@ const TextDocHeader: React.FC<{ const handleReplace = useCallback( (content: string) => { - diffPasteBack(content); + diffPasteBack(content, chatId, toolCall.id); }, - [diffPasteBack], + [chatId, diffPasteBack, toolCall.id], ); const replaceContent = useMemo(() => { @@ -90,7 +92,7 @@ const TextDocHeader: React.FC<{ }) .then((results) => { if (results.data) { - sendToolEditToIde(toolCall.function.arguments.path, results.data); + sendToolCallToIde(toolCall, results.data, chatId); } else if (isRTKResponseErrorWithDetailMessage(results)) { setErrorMessage(results.error.data.detail); } @@ -107,12 +109,7 @@ const TextDocHeader: React.FC<{ setErrorMessage("Error with patch: " + JSON.stringify(error)); } }); - }, [ - requestDryRun, - sendToolEditToIde, - toolCall.function.arguments, - toolCall.function.name, - ]); + }, [chatId, requestDryRun, sendToolCallToIde, toolCall]); return ( @@ -140,6 +137,7 @@ const TextDocHeader: React.FC<{ {replaceContent && (