Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
983490c
ui(patches): remove apply and replace buttons and make the code revea…
MarcMcIntosh Feb 20, 2025
84a0996
ui(new patches): remove new event for sending a patchto the ide.
MarcMcIntosh Feb 20, 2025
5d301e4
wip(ide tool edit): send enough info to the ide to know which tool ed…
MarcMcIntosh Feb 21, 2025
cab72c0
feat(chat): append tool call accepted message to chat when the user a…
MarcMcIntosh Feb 21, 2025
edf0f48
wip(manual patch): remove the tool confiormation when manually applyi…
MarcMcIntosh Feb 21, 2025
e0e1f49
fix: inconstancy with auto sending when accpetting or reject a patch …
MarcMcIntosh Feb 21, 2025
e0e1471
fix(updating tool messages): handle multi model.
MarcMcIntosh Feb 21, 2025
5ededfd
remove unused import.
MarcMcIntosh Feb 21, 2025
27226c4
fix(apply): auto calls when user manually presses apply then accept/r…
MarcMcIntosh Feb 21, 2025
8ed9b39
fix(apply confirmation): add the correct message if the user accepts …
MarcMcIntosh Feb 24, 2025
bb02970
fix(acceot / reject): prevent sending message no rejection.
MarcMcIntosh Feb 24, 2025
d4082b6
fix: don't continue streaming if the user rejects the tool use.
MarcMcIntosh Feb 24, 2025
e883a97
ui(confirmation): remove loading dots.
MarcMcIntosh Feb 24, 2025
e057d9a
ui(markdown): remove custom stye for update textdoc
MarcMcIntosh Feb 24, 2025
e04b0d3
ci: update version bump logic for alpha branch & npm publish in CI (#…
alashchev17 Feb 23, 2025
2b3d4e7
fix(chat): remove unused import.
MarcMcIntosh Feb 24, 2025
cb2e643
a different Stop message without confirmation
olegklimov Feb 25, 2025
2219089
fix(accept / reject): continue when accepting or rejecting the changes.
MarcMcIntosh Feb 25, 2025
3cafef1
refactor(ideToolCallResponse): remove chat id.
MarcMcIntosh Feb 25, 2025
b5443f2
remove comment.
MarcMcIntosh Feb 25, 2025
a52ac40
fix(patch): continue if accepted or allow clicked, stop if rejected o…
MarcMcIntosh Feb 25, 2025
52aaad8
feat(apply + accept): send the chat when the recent tool result has b…
MarcMcIntosh Feb 25, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion refact-agent/engine/src/tools/tools_execute.rs
Original file line number Diff line number Diff line change
Expand Up @@ -203,7 +203,7 @@ pub async fn run_tools(
let command_to_match = cmd
.command_to_match_against_confirm_deny(&args)
.unwrap_or("<error_command>".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()));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This one doesn't work, model still tries to recreate file, it doesn't stop responding
image

continue;
}
_ => {}
Expand Down
40 changes: 39 additions & 1 deletion refact-agent/gui/src/app/middleware.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand All @@ -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";
Expand Down Expand Up @@ -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,
}),
);
}
},
});
4 changes: 3 additions & 1 deletion refact-agent/gui/src/components/ChatContent/ChatContent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -55,6 +56,7 @@ export const ChatContent: React.FC<ChatContentProps> = ({
const [sendTelemetryEvent] =
telemetryApi.useLazySendTelemetryChatEventQuery();
const integrationMeta = useAppSelector(selectIntegration);
const isWaitingForConfirmation = useAppSelector(getConfirmationPauseStatus);

const {
handleScroll,
Expand Down Expand Up @@ -119,7 +121,7 @@ export const ChatContent: React.FC<ChatContentProps> = ({
{threadUsage && <UsageCounter usage={threadUsage} />}

<Container py="4">
<Spinner spinning={isWaiting} />
<Spinner spinning={isWaiting && !isWaitingForConfirmation} />
</Container>
</Flex>
{showFollowButton && (
Expand Down
14 changes: 9 additions & 5 deletions refact-agent/gui/src/components/ChatForm/ToolConfirmation.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { useMemo } from "react";
import React, { useCallback, useMemo } from "react";
import {
PATCH_LIKE_FUNCTIONS,
useAppDispatch,
Expand Down Expand Up @@ -88,6 +88,10 @@ export const ToolConfirmation: React.FC<ToolConfirmationProps> = ({
confirmToolUsage();
};

const handleReject = useCallback(() => {
rejectToolUsage(toolCallIds);
}, [rejectToolUsage, toolCallIds]);

const message = getConfirmationMessage(
commands,
rules,
Expand All @@ -101,7 +105,7 @@ export const ToolConfirmation: React.FC<ToolConfirmationProps> = ({
return (
<PatchConfirmation
handleAllowForThisChat={handleAllowForThisChat}
rejectToolUsage={rejectToolUsage}
rejectToolUsage={handleReject}
confirmToolUsage={confirmToolUsage}
/>
);
Expand Down Expand Up @@ -167,9 +171,9 @@ export const ToolConfirmation: React.FC<ToolConfirmationProps> = ({
color="red"
variant="surface"
size="1"
onClick={rejectToolUsage}
onClick={handleReject}
>
Deny
Stop
</Button>
)}
</Flex>
Expand Down Expand Up @@ -258,7 +262,7 @@ const PatchConfirmation: React.FC<PatchConfirmationProps> = ({
size="1"
onClick={rejectToolUsage}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are we trying to prompt model to stop responding, or should we stop the stream if user clicks this button?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stop => "The user rejected the changes.", don't add this, should go for /v1/chat/completions with tools_confirmation==false

>
Deny
Stop
</Button>
</Flex>
</Flex>
Expand Down
10 changes: 2 additions & 8 deletions refact-agent/gui/src/components/Tools/Texdoc.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
Expand All @@ -44,4 +38,4 @@

.textdoc__update .textdoc__diffbox {
box-shadow: var(--shadow-1);
}
} */
86 changes: 55 additions & 31 deletions refact-agent/gui/src/components/Tools/Textdoc.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand Down Expand Up @@ -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<string>("");
const canPaste = useAppSelector(selectCanPaste);
const chatId = useAppSelector(selectChatId);

const clearErrorMessage = useCallback(() => setErrorMessage(""), []);

// move this
const handleOpenFile = useCallback(() => {
if (!toolCall.function.arguments.path) return;
Expand All @@ -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(() => {
Expand All @@ -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);
}
Expand All @@ -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 (
<Card size="1" variant="surface" mt="4" className={styles.textdoc__header}>
Expand Down Expand Up @@ -140,6 +137,7 @@ const TextDocHeader: React.FC<{
{replaceContent && (
<Button
size="1"
// this one can directly dismiss the tool confirmation.
onClick={() => handleReplace(replaceContent)}
disabled={dryRunResult.isLoading || !canPaste}
title="Replace the current selection in the ide."
Expand All @@ -166,11 +164,17 @@ const CreateTextDoc: React.FC<{
"```" + extension + "\n" + toolCall.function.arguments.content + "\n```"
);
}, [toolCall.function.arguments.content, toolCall.function.arguments.path]);
const handleCopy = useCopyToClipboard();

const lineCount = useMemo(() => code.split("\n").length, [code]);

return (
// TODO: move this box up a bit, or make it generic
<Box className={styles.textdoc}>
<TextDocHeader toolCall={toolCall} />
<Markdown>{code}</Markdown>
<Reveal isRevealingCode defaultOpen={lineCount < 9}>
<Markdown onCopyClick={handleCopy}>{code}</Markdown>
</Reveal>
</Box>
);
};
Expand All @@ -191,11 +195,20 @@ const ReplaceTextDoc: React.FC<{
toolCall.function.arguments.path,
toolCall.function.arguments.replacement,
]);

const copyToClipBoard = useCopyToClipboard();
const handleCopy = useCallback(() => {
copyToClipBoard(toolCall.function.arguments.replacement);
}, [copyToClipBoard, toolCall.function.arguments.replacement]);

const lineCount = useMemo(() => code.split("\n").length, [code]);
return (
// TODO: move this box up a bit, or make it generic
<Box className={styles.textdoc}>
<TextDocHeader toolCall={toolCall} />
<Markdown>{code}</Markdown>
<Reveal isRevealingCode defaultOpen={lineCount < 9}>
<Markdown onCopyClick={handleCopy}>{code}</Markdown>
</Reveal>
</Box>
);
};
Expand All @@ -219,37 +232,48 @@ const UpdateRegexTextDoc: React.FC<{
toolCall.function.arguments.replacement,
]);

const lineCount = useMemo(() => code.split("\n").length, [code]);

return (
<Box className={styles.textdoc}>
<TextDocHeader toolCall={toolCall} />
<Markdown>{code}</Markdown>
<Reveal isRevealingCode defaultOpen={lineCount < 9}>
<Markdown>{code}</Markdown>
</Reveal>
</Box>
);
};

const UpdateTextDoc: React.FC<{
toolCall: UpdateTextDocToolCall;
}> = ({ toolCall }) => {
const diff = useMemo(() => {
const patch = createPatch(
toolCall.function.arguments.path,
toolCall.function.arguments.old_str,
toolCall.function.arguments.replacement,
const code = useMemo(() => {
const extension = getFileExtension(toolCall.function.arguments.path);
return (
"```" +
extension +
"\n" +
toolCall.function.arguments.replacement +
"\n```"
);

return "```diff\n" + patch + "\n```";
}, [
toolCall.function.arguments.replacement,
toolCall.function.arguments.old_str,
toolCall.function.arguments.path,
toolCall.function.arguments.replacement,
]);
// TODO: don't use markdown for this, it's two bright

const lineCount = useMemo(() => code.split("\n").length, [code]);

const copyToClipBoard = useCopyToClipboard();
const handleCopy = useCallback(() => {
copyToClipBoard(toolCall.function.arguments.replacement);
}, [copyToClipBoard, toolCall.function.arguments.replacement]);

return (
<Box className={classNames(styles.textdoc, styles.textdoc__update)}>
<Box className={styles.textdoc}>
<TextDocHeader toolCall={toolCall} />
<Box className={classNames(styles.textdoc__diffbox)}>
<Markdown useInlineStyles={false}>{diff}</Markdown>
</Box>
<Reveal isRevealingCode defaultOpen={lineCount < 9}>
<Markdown onCopyClick={handleCopy}>{code}</Markdown>
</Reveal>
</Box>
);
};
Expand Down
2 changes: 1 addition & 1 deletion refact-agent/gui/src/contexts/AbortControllers.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ export const AbortControllerProvider: React.FC<{
const abort = (key: string, reason?: string) => {
if (key in abortControllers) {
const fn = abortControllers[key];
fn(reason);
fn(reason ?? "aborted");
removeController(key);
}
};
Expand Down
4 changes: 3 additions & 1 deletion refact-agent/gui/src/events/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ export {
setCurrentProjectInfo,
type CurrentProjectInfo,
} from "../features/Chat/currentProject";
export type { TextDocToolCall } from "../components/Tools/types";

export type {
ToolCommand,
Expand Down Expand Up @@ -73,7 +74,8 @@ export {
ideEscapeKeyPressed,
ideIsChatStreaming,
ideIsChatReady,
ideToolEdit,
ideToolCall,
ideToolCallResponse,
} from "../hooks/useEventBusForIDE";

export const fim = {
Expand Down
Loading