Skip to content

Fix conversation auto-scroll and yolo mode tool approvals#190

Merged
georpar merged 6 commits intoPiebald-AI:mainfrom
lnorton89:fix/conversation-scroll-and-yolo-mode
Mar 8, 2026
Merged

Fix conversation auto-scroll and yolo mode tool approvals#190
georpar merged 6 commits intoPiebald-AI:mainfrom
lnorton89:fix/conversation-scroll-and-yolo-mode

Conversation

@lnorton89
Copy link
Contributor

@lnorton89 lnorton89 commented Feb 16, 2026

Changes

1. Fix Conversation Auto-Scroll

The conversation window now auto-scrolls to new messages, but only when the user is near the bottom. This prevents annoying jumps when scrolling up to read previous messages.

  • Auto-scroll follows new messages when user is within 100px of bottom
  • Scrolling up disables auto-scroll until user returns to bottom
  • Uses local ref and scroll event listener for reliable behavior

2. Fix Yolo Mode Auto-Approve

When yolo mode is enabled, tool calls are now automatically approved without requiring manual confirmation.

  • Detects yolo mode from backend config (supports Gemini and Qwen)
  • Automatically sends approval response to backend for tool call requests
  • Prevents confirmation dialogs from appearing when yolo is enabled

Testing

  • Verified auto-scroll works when at bottom of conversation
  • Verified auto-scroll is disabled when scrolled up
  • Verified yolo mode auto-approves tool calls without prompts

Summary by CodeRabbit

  • New Features

    • YOLO mode: optionally auto-approves tool calls based on backend configuration to streamline confirmations.
    • Smarter auto-scroll: only scrolls when the user is near the bottom, improving chat UX.
  • Refactor

    • Simplified conversation context by removing the exposed messages container ref and providing a convenience hook for access.
  • Documentation

    • Added docstrings/documentation for the conversation hook and event-handling behavior.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Feb 16, 2026

Warning

Rate limit exceeded

@georpar has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 16 minutes and 8 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: dee5096a-b2e6-42b5-9be4-907eed4757f3

📥 Commits

Reviewing files that changed from the base of the PR and between 1f83746 and 62b9e83.

📒 Files selected for processing (1)
  • frontend/src/hooks/useConversationEvents.ts
📝 Walkthrough

Walkthrough

Adds an isYoloEnabled flag propagated into conversation event handling (auto-approves tool calls when enabled) and removes the shared messagesContainerRef, replacing it with local scroll/resize refs and observers for conditional auto-scrolling.

Changes

Cohort / File(s) Summary
App & Context
frontend/src/App.tsx, frontend/src/contexts/ConversationContext.tsx
Derives isYoloEnabled from backend config, passes it into useConversationEvents, removes messagesContainerRef from the public context value, and adds a useConversation() helper hook.
Event Handling (YOLO-aware)
frontend/src/hooks/useConversationEvents.ts
Extends useConversationEvents signature to accept isYoloEnabled?: boolean; tracks state in a ref; when YOLO enabled, auto-selects an approval outcome and immediately calls api.send_tool_call_confirmation_response instead of storing confirmation requests.
Chat Auto-Scroll
frontend/src/pages/HomeDashboard.tsx
Removes shared messagesContainerRef, introduces localContainerRef, isNearBottomRef, and prevScrollHeightRef; installs scroll listener and ResizeObserver to auto-scroll only when user is near the bottom.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant Frontend
    participant Hook as useConversationEvents
    participant Backend

    User->>Frontend: Trigger action causing tool_call
    Frontend->>Hook: Emit tool_call event (hook configured with isYoloEnabled)
    alt YOLO enabled
        Hook->>Hook: Select approval outcome (prefer allow_always/allow_once or proceed_always)
        Hook->>Backend: api.send_tool_call_confirmation_response(auto-approve)
        Backend-->>Hook: Ack
        Hook-->>Frontend: Continue processing (no user prompt)
    else YOLO disabled
        Hook->>Hook: Store confirmation request in Map
        Hook-->>Frontend: Update confirmation requests state
        Frontend->>User: Render confirmation prompt
        User->>Frontend: Submit choice
        Frontend->>Hook: Deliver user's confirmation
        Hook->>Backend: api.send_tool_call_confirmation_response(user_choice)
        Backend-->>Hook: Ack
    end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Poem

🐰 I nudged the flags and scurried through,
Approvals hopped when YOLO flew,
The chat now listens, scrolls with care,
Only when you're near the chair,
A tiny hop—smooth flow anew. 🌿

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely summarizes the two main changes: fixing conversation auto-scroll behavior and implementing YOLO mode tool approvals.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Fix all issues with AI agents
Verify each finding against the current code and only fix it if needed.


In `@frontend/src/App.tsx`:
- Around line 454-491: The context currently includes messagesContainerRef even
though no consumer uses it; remove messagesContainerRef from the object returned
by the useMemo (contextValue) and from its dependency array in App.tsx, and also
remove it from the corresponding Context type/interface and any provider prop
definitions (so things like messagesContainerRef are not exported from the app
context). Ensure no other code references messagesContainerRef after removal; if
any remaining usages exist, switch them to localContainerRef (as used in
HomeDashboard) or update those callers accordingly.

In `@frontend/src/hooks/useConversationEvents.ts`:
- Around line 273-274: The callback setupEventListenerForConversation currently
includes isYoloEnabled in its dependency array which causes a full
teardown/re-register of listeners when YOLO toggles; change isYoloEnabled to a
ref: create isYoloEnabledRef via useRef<boolean>(), update that ref wherever the
toggle changes, replace direct reads of isYoloEnabled inside
setupEventListenerForConversation with isYoloEnabledRef.current, and remove
isYoloEnabled from the useCallback dependency array so the callback identity
remains stable and doesn't trigger the App.tsx effect to re-register all
listeners.

In `@frontend/src/pages/HomeDashboard.tsx`:
- Around line 70-84: The auto-scroll effect in HomeDashboard (the
React.useEffect that reads localContainerRef,
currentConversation?.messages.length and currentConversation?.isStreaming) won't
run for appended text chunks because messages.length doesn't change; update the
effect to also depend on the tail message's dynamic content (e.g., include
currentConversation?.messages?.[currentConversation.messages.length-1]?.parts?.length
or a computed lastMessageContentLength) or attach a
MutationObserver/ResizeObserver to localContainerRef to trigger scrolling when
the container's scrollHeight changes; ensure you still check isNearBottomRef
before setting container.scrollTop.
- Around line 42-43: Remove the leftover debug console.log in HomeDashboard.tsx
that prints currentConversation on every render; locate the statement
referencing currentConversation (console.log("🏠 HomeDashboard -
currentConversation:", currentConversation)) inside the HomeDashboard component
and delete it (or replace it with a conditional debug logger that only runs in
development, e.g., gated by process.env.NODE_ENV === "development"). Ensure no
other stray debug logs remain in the HomeDashboard component.
🧹 Nitpick comments (2)
🤖 Fix all nitpicks with AI agents
Verify each finding against the current code and only fix it if needed.


In `@frontend/src/App.tsx`:
- Around line 454-491: The context currently includes messagesContainerRef even
though no consumer uses it; remove messagesContainerRef from the object returned
by the useMemo (contextValue) and from its dependency array in App.tsx, and also
remove it from the corresponding Context type/interface and any provider prop
definitions (so things like messagesContainerRef are not exported from the app
context). Ensure no other code references messagesContainerRef after removal; if
any remaining usages exist, switch them to localContainerRef (as used in
HomeDashboard) or update those callers accordingly.

In `@frontend/src/pages/HomeDashboard.tsx`:
- Around line 42-43: Remove the leftover debug console.log in HomeDashboard.tsx
that prints currentConversation on every render; locate the statement
referencing currentConversation (console.log("🏠 HomeDashboard -
currentConversation:", currentConversation)) inside the HomeDashboard component
and delete it (or replace it with a conditional debug logger that only runs in
development, e.g., gated by process.env.NODE_ENV === "development"). Ensure no
other stray debug logs remain in the HomeDashboard component.
frontend/src/App.tsx (1)

454-491: messagesContainerRef is still provided in context but no longer consumed.

HomeDashboard now uses localContainerRef instead of messagesContainerRef from context. If nothing else consumes messagesContainerRef, it's dead code in the context value (lines 461, 479). Consider removing it from the context to avoid confusion.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@frontend/src/App.tsx` around lines 454 - 491, The context currently includes
messagesContainerRef even though no consumer uses it; remove
messagesContainerRef from the object returned by the useMemo (contextValue) and
from its dependency array in App.tsx, and also remove it from the corresponding
Context type/interface and any provider prop definitions (so things like
messagesContainerRef are not exported from the app context). Ensure no other
code references messagesContainerRef after removal; if any remaining usages
exist, switch them to localContainerRef (as used in HomeDashboard) or update
those callers accordingly.
frontend/src/pages/HomeDashboard.tsx (1)

42-43: Remove debug console.log left in production code.

Line 43 logs currentConversation on every render, which is noisy in production.

🧹 Proposed fix
-  // Debug logging for currentConversation
-  console.log("🏠 HomeDashboard - currentConversation:", currentConversation);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@frontend/src/pages/HomeDashboard.tsx` around lines 42 - 43, Remove the
leftover debug console.log in HomeDashboard.tsx that prints currentConversation
on every render; locate the statement referencing currentConversation
(console.log("🏠 HomeDashboard - currentConversation:", currentConversation))
inside the HomeDashboard component and delete it (or replace it with a
conditional debug logger that only runs in development, e.g., gated by
process.env.NODE_ENV === "development"). Ensure no other stray debug logs remain
in the HomeDashboard component.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Fix all issues with AI agents
Verify each finding against the current code and only fix it if needed.


In `@frontend/src/hooks/useConversationEvents.ts`:
- Around line 923-958: The YOLO auto-approve logic currently picks the first
matching option (so it prefers allow_once) and on API failure it never stores
the confirmation request, leaving the call stuck; update the block that handles
isYoloEnabledRef.current to (1) prefer an option with kind === "allow_always",
then fall back to "allow_once", then to "proceed_always" for outcome selection
(use legacyConfirmationRequest.options to find those), and (2) when calling
api.send_tool_call_confirmation_response (the call using sessionId, requestId,
toolCallId, outcome), catch errors and in the catch handler store the
legacyConfirmationRequest into the confirmationRequests Map via
setConfirmationRequests (same logic as the non-YOLO branch) so the UI can
present the dialog and retry; keep error logging in the catch path.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
frontend/src/pages/HomeDashboard.tsx (1)

231-233: ⚠️ Potential issue | 🟡 Minor

Avoid Date.now() in React keys — causes unnecessary remounts.

Including Date.now() in the key forces React to treat the component as new on every render, unmounting and remounting it. This defeats React's reconciliation and can cause lost state, performance issues, and visual flicker.

🐛 Proposed fix
                                    return (
                                      <ToolCallDisplay
-                                       key={`${
-                                         msgPart.toolCall.id
-                                       }-${hasConfirmation}-${Date.now()}`}
+                                       key={`${msgPart.toolCall.id}-${hasConfirmation}`}
                                        toolCall={msgPart.toolCall}

If the intent is to force re-render when confirmation state changes, hasConfirmation already provides that differentiation.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@frontend/src/pages/HomeDashboard.tsx` around lines 231 - 233, The key
currently uses Date.now() which forces remounts; change the key to a stable
value derived from the message part and confirmation state instead (e.g.,
combine msgPart.toolCall.id with hasConfirmation) so React can reconcile
correctly—update the key expression that references msgPart.toolCall.id and
hasConfirmation to remove Date.now() and use a stable unique identifier (or
include an existing stable field like a part id/index if needed).
♻️ Duplicate comments (1)
frontend/src/hooks/useConversationEvents.ts (1)

923-943: ⚠️ Potential issue | 🟠 Major

YOLO auto-approve still picks allow_once over allow_always and lacks error recovery.

Two concerns from the previous review remain:

  1. Option priority: find() returns the first match. Per getOptionKind() (lines 250-262), options are ordered [allow_once, allow_always, ...]. This always picks allow_once when both exist. For YOLO mode, allow_always better matches the intent and reduces future prompts.

  2. Silent failure: If the API call fails, the error is logged but the confirmation request isn't stored in the Map. The UI won't show a dialog, and the tool call remains stuck with no recovery path.

🐛 Proposed fix
             // If yolo mode is enabled, auto-approve the tool call
             if (isYoloEnabledRef.current) {
               console.log(`🤖 [YOLO] Auto-approving tool call: ${toolCallId}`);
-              // 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"
+              // Prefer "allow_always" for YOLO mode, fall back to "allow_once"
+              const autoApproveOption =
+                legacyConfirmationRequest.options?.find(
+                  (opt) => opt.kind === "allow_always"
+                ) ??
+                legacyConfirmationRequest.options?.find(
+                  (opt) => opt.kind === "allow_once"
               );
               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);
+                  // Fallback: store in confirmation requests so user can manually approve
+                  setConfirmationRequests((prev) => {
+                    const newMap = new Map(prev);
+                    newMap.set(toolCallId, legacyConfirmationRequest);
+                    return newMap;
+                  });
                 });
             } else {
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@frontend/src/hooks/useConversationEvents.ts` around lines 923 - 943, The YOLO
auto-approve currently picks the first matching option (which ends up being
allow_once) and silently drops the confirmation on API failure; update the
selection to prefer "allow_always" over "allow_once" (e.g., check for
allow_always first, then allow_once) when computing outcome for
legacyConfirmationRequest, and in the send_tool_call_confirmation_response.catch
handler, persist the legacyConfirmationRequest into the confirmationRequests Map
(the same map used to surface confirmation dialogs) keyed by requestId or
toolCallId so the UI can show the dialog and the tool call can be recovered.
🧹 Nitpick comments (1)
frontend/src/App.tsx (1)

160-166: Ensure isYoloEnabled is strictly boolean to avoid passing undefined.

The yolo field is optional (yolo?: boolean) in both GeminiConfig and QwenConfig. When the config exists but yolo is not set, this expression evaluates to undefined rather than false. While useConversationEvents handles this with ?? false, explicitly coercing to boolean here improves type safety and clarity.

♻️ Proposed fix
   // Get yolo mode status from backend config
   const isYoloEnabled =
     backendState.selectedBackend === "gemini"
-      ? backendState.configs.gemini.yolo
+      ? backendState.configs.gemini.yolo ?? false
       : backendState.selectedBackend === "qwen"
-        ? backendState.configs.qwen.yolo
+        ? backendState.configs.qwen.yolo ?? false
         : false;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@frontend/src/App.tsx` around lines 160 - 166, The computed isYoloEnabled can
be undefined because GeminiConfig.yolo and QwenConfig.yolo are optional; update
the expression that derives isYoloEnabled (the const isYoloEnabled using
backendState.selectedBackend and backendState.configs.gemini.yolo /
backendState.configs.qwen.yolo) to coerce the result to a strict boolean (e.g.,
use the nullish coalescing operator ?? false or a Boolean/!! cast) so it always
yields true or false before being passed into useConversationEvents.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Outside diff comments:
In `@frontend/src/pages/HomeDashboard.tsx`:
- Around line 231-233: The key currently uses Date.now() which forces remounts;
change the key to a stable value derived from the message part and confirmation
state instead (e.g., combine msgPart.toolCall.id with hasConfirmation) so React
can reconcile correctly—update the key expression that references
msgPart.toolCall.id and hasConfirmation to remove Date.now() and use a stable
unique identifier (or include an existing stable field like a part id/index if
needed).

---

Duplicate comments:
In `@frontend/src/hooks/useConversationEvents.ts`:
- Around line 923-943: The YOLO auto-approve currently picks the first matching
option (which ends up being allow_once) and silently drops the confirmation on
API failure; update the selection to prefer "allow_always" over "allow_once"
(e.g., check for allow_always first, then allow_once) when computing outcome for
legacyConfirmationRequest, and in the send_tool_call_confirmation_response.catch
handler, persist the legacyConfirmationRequest into the confirmationRequests Map
(the same map used to surface confirmation dialogs) keyed by requestId or
toolCallId so the UI can show the dialog and the tool call can be recovered.

---

Nitpick comments:
In `@frontend/src/App.tsx`:
- Around line 160-166: The computed isYoloEnabled can be undefined because
GeminiConfig.yolo and QwenConfig.yolo are optional; update the expression that
derives isYoloEnabled (the const isYoloEnabled using
backendState.selectedBackend and backendState.configs.gemini.yolo /
backendState.configs.qwen.yolo) to coerce the result to a strict boolean (e.g.,
use the nullish coalescing operator ?? false or a Boolean/!! cast) so it always
yields true or false before being passed into useConversationEvents.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: d9d4ec41-a6a8-49e4-ab6e-03348cbde576

📥 Commits

Reviewing files that changed from the base of the PR and between dac683e and 899075f.

📒 Files selected for processing (3)
  • frontend/src/App.tsx
  • frontend/src/hooks/useConversationEvents.ts
  • frontend/src/pages/HomeDashboard.tsx

@georpar
Copy link
Member

georpar commented Mar 8, 2026

Excellent contribution.  Thank you!

@georpar georpar merged commit 5fff2ea into Piebald-AI:main Mar 8, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants