Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
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
10 changes: 9 additions & 1 deletion frontend/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -158,10 +158,18 @@ function RootLayoutContent() {
updateConversation,
});

// Get yolo mode status from backend config
const isYoloEnabled = backendState.selectedBackend === "gemini"
? backendState.configs.gemini.yolo
: backendState.selectedBackend === "qwen"
? backendState.configs.qwen.yolo
: false;

const { setupEventListenerForConversation } = useConversationEvents(
setCliIOLogs,
setConfirmationRequests,
updateConversation
updateConversation,
isYoloEnabled
);

const { input, handleInputChange, handleSendMessage } = useMessageHandler({
Expand Down
49 changes: 37 additions & 12 deletions frontend/src/hooks/useConversationEvents.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import React, { useCallback, useRef } from "react";
import type { TextMessagePart } from "../types";
import { listen } from "@/lib/listen";
import { getWebSocketManager } from "../lib/webApi";
import { api } from "../lib/api";
import { Conversation, Message, CliIO } from "../types";
import { ToolCallConfirmationRequest } from "../utils/toolCallParser";
import { type ToolCall } from "../utils/toolCallParser";
Expand Down Expand Up @@ -269,7 +270,8 @@ export const useConversationEvents = (
updateConversation: (
conversationId: string,
updateFn: (conv: Conversation, lastMsg: Message) => void
) => void
) => void,
isYoloEnabled?: boolean
) => {
// Buffer agent text chunks seen on CLI output in case the UI misses
// ai-output streaming events (web WS race). Cleared on turn finish.
Expand Down Expand Up @@ -898,19 +900,42 @@ export const useConversationEvents = (
: [],
};

setConfirmationRequests((prev) => {
const newMap = new Map(prev);
newMap.set(toolCallId, legacyConfirmationRequest);
// If yolo mode is enabled, auto-approve the tool call
if (isYoloEnabled) {
console.log(
`✅ Stored confirmation request in Map for toolCallId:`,
toolCallId
`🤖 [YOLO] Auto-approving tool call: ${toolCallId}`
);
console.log(
`✅ Total confirmation requests in Map:`,
newMap.size
// Find the "allow_always" or "allow_once" option and use it
const autoApproveOption = legacyConfirmationRequest.options?.find(
(opt) => opt.kind === "allow_always" || opt.kind === "allow_once"
);
return newMap;
});
const outcome = autoApproveOption?.optionId || "proceed_always";

// Send approval response to backend
api.send_tool_call_confirmation_response({
sessionId: conversationId,
requestId: legacyConfirmationRequest.requestId,
toolCallId: toolCallId,
outcome: outcome,
}).catch((err) => {
console.error("Failed to send auto-approve response:", err);
});
} else {
// Only store confirmation request if yolo mode is disabled
setConfirmationRequests((prev) => {
const newMap = new Map(prev);
newMap.set(toolCallId, legacyConfirmationRequest);
console.log(
`✅ Stored confirmation request in Map for toolCallId:`,
toolCallId
);
console.log(
`✅ Total confirmation requests in Map:`,
newMap.size
);
return newMap;
});
}
}
);
unlistenFunctions.push(unlistenAcpPermissionRequest);
Expand Down Expand Up @@ -981,7 +1006,7 @@ export const useConversationEvents = (
sawAiOutputRef.current.delete(conversationId);
};
},
[setCliIOLogs, setConfirmationRequests, updateConversation]
[setCliIOLogs, setConfirmationRequests, updateConversation, isYoloEnabled]
);

return { setupEventListenerForConversation };
Expand Down
41 changes: 39 additions & 2 deletions frontend/src/pages/HomeDashboard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,6 @@ export const HomeDashboard: React.FC = () => {
const navigate = useNavigate();
const {
currentConversation,
messagesContainerRef,
handleConfirmToolCall,
confirmationRequests,
} = useConversation();
Expand All @@ -46,14 +45,52 @@ export const HomeDashboard: React.FC = () => {
const { selectedBackend } = useBackend();
const backendText = getBackendText(selectedBackend);

// Use a local ref for auto-scrolling
const localContainerRef = React.useRef<HTMLDivElement>(null);

// Track if user is near bottom (for auto-scroll behavior)
const isNearBottomRef = React.useRef(true);

// Listen for scroll events to track user position
React.useEffect(() => {
const container = localContainerRef.current;
if (!container) return;

const handleScroll = () => {
const threshold = 100; // pixels from bottom
const position = container.scrollTop + container.clientHeight;
const height = container.scrollHeight;
isNearBottomRef.current = height - position < threshold;
};

container.addEventListener('scroll', handleScroll, { passive: true });
return () => container.removeEventListener('scroll', handleScroll);
}, []);

// Auto-scroll to bottom when new messages arrive (only if user is near bottom)
React.useEffect(() => {
const container = localContainerRef.current;
if (container && currentConversation?.messages.length) {
// Use setTimeout with 0 to ensure DOM has fully rendered
setTimeout(() => {
if (container && isNearBottomRef.current) {
container.scrollTop = container.scrollHeight;
}
}, 0);
}
}, [
currentConversation?.messages.length,
currentConversation?.isStreaming
]);

return (
<>
{currentConversation ? (
currentConversation.messages.length === 0 ? (
<NewChatPlaceholder />
) : (
<div
ref={messagesContainerRef as React.RefObject<HTMLDivElement>}
ref={localContainerRef}
className="flex-1 min-h-0 overflow-y-auto p-6 relative"
>
<div className="space-y-8 pb-4">
Expand Down
Loading