From ac23a02836d07800d682538f78f22cfb87b9d92b Mon Sep 17 00:00:00 2001 From: Roo Code Date: Fri, 9 Jan 2026 11:30:16 +0000 Subject: [PATCH 1/2] fix: prevent showing project root when pathname lags in native tool streaming Fixes ROO-419 When using native tool calling, JSON arguments stream incrementally and content/diff parameters often arrive before path. This caused the UI to render file editing operations with undefined path, displaying as the project root and creating confusing UX. Changes: - Modified createPartialToolUse() in NativeToolCallParser.ts to require BOTH path and content/diff before creating nativeArgs for write_to_file and apply_diff tools - Changed condition from OR (||) to AND (&&) for these file editing tools - Prevents rendering partial tool use until complete file path is available Impact: - Users no longer see confusing "project root" as edit target - File editing operations wait until path is fully streamed before display - Improves trust and clarity in file modification operations - No impact on final rendering or execution, only affects partial updates --- INVESTIGATION-ROO-419.md | 199 ++++++++++++++++++ .../assistant-message/NativeToolCallParser.ts | 8 +- 2 files changed, 205 insertions(+), 2 deletions(-) create mode 100644 INVESTIGATION-ROO-419.md diff --git a/INVESTIGATION-ROO-419.md b/INVESTIGATION-ROO-419.md new file mode 100644 index 00000000000..b35d6231bcc --- /dev/null +++ b/INVESTIGATION-ROO-419.md @@ -0,0 +1,199 @@ +# Investigation: ROO-419 - Roo shows project root as edit target when pathname lags + +## Issue Description + +User occasionally sees Roo showing "wants to edit this file" with the project root selected instead of a specific file. In these cases, the edit step takes a very long time before the actual intended file edit occurs. + +This is related to the order of native tool calling (NTC) arguments: the streamed content arrives before the pathname, so the UI temporarily renders the root as the target until the pathname is received. + +## Root Cause Analysis + +### 1. Native Tool Call Streaming Flow + +When using native tool calling, the API streams JSON arguments incrementally: + +```json +// First chunk - content arrives first +{"content": "function hello() {\n return 'world';\n}"} + +// Second chunk - path arrives later +{"content": "function hello() {\n return 'world';\n}", "path": "src/hello.ts"} +``` + +### 2. Code Flow + +#### A. NativeToolCallParser.createPartialToolUse() +**Location**: [`Roo-Code/src/core/assistant-message/NativeToolCallParser.ts:344-564`](Roo-Code/src/core/assistant-message/NativeToolCallParser.ts:344) + +This function creates partial ToolUse objects as JSON streams in. The problem occurs here: + +**For `write_to_file` (lines 387-394):** +```typescript +case "write_to_file": + if (partialArgs.path || partialArgs.content) { + nativeArgs = { + path: partialArgs.path, // ⚠️ Can be undefined! + content: partialArgs.content, + } + } + break; +``` + +**For `apply_diff` (lines 405-412):** +```typescript +case "apply_diff": + if (partialArgs.path !== undefined || partialArgs.diff !== undefined) { + nativeArgs = { + path: partialArgs.path, // ⚠️ Can be undefined! + diff: partialArgs.diff, + } + } + break; +``` + +**Problem**: The condition allows nativeArgs creation even when only `content`/`diff` is present but `path` is undefined. This creates a partial ToolUse with incomplete data. + +#### B. ChatRow.tsx +**Location**: [`Roo-Code/webview-ui/src/components/chat/ChatRow.tsx:427-459`](Roo-Code/webview-ui/src/components/chat/ChatRow.tsx:427) + +Renders the file edit UI using `tool.path` from the partial ToolUse: + +```tsx + +``` + +**Problem**: No check for undefined path before rendering, so the component renders with incomplete data. + +#### C. CodeAccordian.tsx +**Location**: [`Roo-Code/webview-ui/src/components/common/CodeAccordian.tsx:27-131`](Roo-Code/webview-ui/src/components/common/CodeAccordian.tsx:27) + +Receives the `path` prop and formats it for display: + +```tsx + + + {formatPathTooltip(path)} + + +``` + +**Problem**: When path is undefined, `formatPathTooltip` returns an empty string, causing the UI to show no file target or the project root. + +#### D. formatPathTooltip.ts +**Location**: [`Roo-Code/webview-ui/src/utils/formatPathTooltip.ts:18-19`](Roo-Code/webview-ui/src/utils/formatPathTooltip.ts:18) + +```typescript +export function formatPathTooltip(path?: string, additionalContent?: string): string { + if (!path) return "" // ⚠️ Returns empty string for undefined path + // ... +} +``` + +**Problem**: Returns empty string when path is undefined, which results in empty/root directory display in UI. + +## Impact + +1. **Confusing UX**: Users see "wants to edit this file" with the project root selected, not understanding what file will be modified +2. **Long perceived wait times**: The UI appears frozen/unresponsive while waiting for the pathname to arrive +3. **Trust issues**: Users may not trust what Roo is doing when they see incorrect file targets + +## Solution Approaches + +### Option 1: Delay Rendering Until Path Arrives ⭐ (Recommended) + +**Strategy**: Don't create partial nativeArgs for file editing tools until the required `path` parameter is present. + +**Changes needed**: Modify [`NativeToolCallParser.createPartialToolUse()`](Roo-Code/src/core/assistant-message/NativeToolCallParser.ts:387) conditions: + +```typescript +case "write_to_file": + // Only create nativeArgs when BOTH path and content are present + if (partialArgs.path && partialArgs.content) { + nativeArgs = { + path: partialArgs.path, + content: partialArgs.content, + } + } + break; + +case "apply_diff": + // Only create nativeArgs when BOTH path and diff are present + if (partialArgs.path && partialArgs.diff) { + nativeArgs = { + path: partialArgs.path, + diff: partialArgs.diff, + } + } + break; +``` + +**Pros**: +- Simple fix with minimal code changes +- Prevents confusing UI states entirely +- No new UI components needed +- Matches user expectations (don't show incomplete information) + +**Cons**: +- User sees no visual feedback until path arrives +- May feel slightly less responsive (but more accurate) + +### Option 2: Show "Loading..." Indicator + +**Strategy**: Render a loading state when content is present but path is undefined. + +**Changes needed**: Modify [`ChatRow.tsx`](Roo-Code/webview-ui/src/components/chat/ChatRow.tsx:446) or [`CodeAccordian.tsx`](Roo-Code/webview-ui/src/components/common/CodeAccordian.tsx:74) to detect missing path and show loading indicator. + +**Pros**: +- Provides immediate visual feedback +- Shows that something is happening +- More responsive feel + +**Cons**: +- Requires new UI component/state +- More complex implementation +- Still shows incomplete information (loading instead of target) + +### Option 3: Suppress Partial Updates for File Tools + +**Strategy**: Don't emit partial ToolUse updates for tools that require a path parameter. + +**Changes needed**: Add logic to [`processStreamingChunk`](Roo-Code/src/core/assistant-message/NativeToolCallParser.ts:234) to suppress partial updates for specific tools. + +**Pros**: +- Completely prevents the issue at the source +- Clear separation between tools that support partial rendering vs. those that don't + +**Cons**: +- No visual feedback during streaming +- Requires tool categorization/configuration +- More significant code changes + +## Recommendation + +**Option 1** is the best approach because: + +1. **Simplest implementation**: Single logical condition change in one function +2. **Prevents bad UX**: Users never see incorrect/incomplete information +3. **No new UI needed**: Leverages existing rendering flow +4. **Consistent behavior**: Aligns with how other tools handle required parameters +5. **Easy to test**: Clear before/after behavior + +The fix should be applied in [`NativeToolCallParser.createPartialToolUse()`](Roo-Code/src/core/assistant-message/NativeToolCallParser.ts:344) by requiring BOTH path and content/diff to be present before creating nativeArgs for file editing tools. + +## Files to Modify + +1. **Primary Fix**: [`Roo-Code/src/core/assistant-message/NativeToolCallParser.ts`](Roo-Code/src/core/assistant-message/NativeToolCallParser.ts) + - Line 387-394: `write_to_file` case + - Line 405-412: `apply_diff` case + +## Testing Considerations + +1. Test with a model that uses native tool calling +2. Verify that file edits don't show UI until path is present +3. Ensure other tools (without required path) still show partial updates correctly +4. Check that final rendering works correctly once path arrives +5. Test with slow network conditions to simulate delayed path arrival diff --git a/src/core/assistant-message/NativeToolCallParser.ts b/src/core/assistant-message/NativeToolCallParser.ts index f6eac36a9c1..0c800952e75 100644 --- a/src/core/assistant-message/NativeToolCallParser.ts +++ b/src/core/assistant-message/NativeToolCallParser.ts @@ -385,7 +385,9 @@ export class NativeToolCallParser { break case "write_to_file": - if (partialArgs.path || partialArgs.content) { + // Only create nativeArgs when BOTH path and content are present + // This prevents showing undefined path (project root) in UI during streaming + if (partialArgs.path && partialArgs.content) { nativeArgs = { path: partialArgs.path, content: partialArgs.content, @@ -403,7 +405,9 @@ export class NativeToolCallParser { break case "apply_diff": - if (partialArgs.path !== undefined || partialArgs.diff !== undefined) { + // Only create nativeArgs when BOTH path and diff are present + // This prevents showing undefined path (project root) in UI during streaming + if (partialArgs.path && partialArgs.diff) { nativeArgs = { path: partialArgs.path, diff: partialArgs.diff, From c2aed491e2f5ca67724b55dfbdac25393d5c7a3e Mon Sep 17 00:00:00 2001 From: Roo Code Date: Fri, 9 Jan 2026 11:36:40 +0000 Subject: [PATCH 2/2] chore: remove investigation documentation per review feedback --- INVESTIGATION-ROO-419.md | 199 --------------------------------------- 1 file changed, 199 deletions(-) delete mode 100644 INVESTIGATION-ROO-419.md diff --git a/INVESTIGATION-ROO-419.md b/INVESTIGATION-ROO-419.md deleted file mode 100644 index b35d6231bcc..00000000000 --- a/INVESTIGATION-ROO-419.md +++ /dev/null @@ -1,199 +0,0 @@ -# Investigation: ROO-419 - Roo shows project root as edit target when pathname lags - -## Issue Description - -User occasionally sees Roo showing "wants to edit this file" with the project root selected instead of a specific file. In these cases, the edit step takes a very long time before the actual intended file edit occurs. - -This is related to the order of native tool calling (NTC) arguments: the streamed content arrives before the pathname, so the UI temporarily renders the root as the target until the pathname is received. - -## Root Cause Analysis - -### 1. Native Tool Call Streaming Flow - -When using native tool calling, the API streams JSON arguments incrementally: - -```json -// First chunk - content arrives first -{"content": "function hello() {\n return 'world';\n}"} - -// Second chunk - path arrives later -{"content": "function hello() {\n return 'world';\n}", "path": "src/hello.ts"} -``` - -### 2. Code Flow - -#### A. NativeToolCallParser.createPartialToolUse() -**Location**: [`Roo-Code/src/core/assistant-message/NativeToolCallParser.ts:344-564`](Roo-Code/src/core/assistant-message/NativeToolCallParser.ts:344) - -This function creates partial ToolUse objects as JSON streams in. The problem occurs here: - -**For `write_to_file` (lines 387-394):** -```typescript -case "write_to_file": - if (partialArgs.path || partialArgs.content) { - nativeArgs = { - path: partialArgs.path, // ⚠️ Can be undefined! - content: partialArgs.content, - } - } - break; -``` - -**For `apply_diff` (lines 405-412):** -```typescript -case "apply_diff": - if (partialArgs.path !== undefined || partialArgs.diff !== undefined) { - nativeArgs = { - path: partialArgs.path, // ⚠️ Can be undefined! - diff: partialArgs.diff, - } - } - break; -``` - -**Problem**: The condition allows nativeArgs creation even when only `content`/`diff` is present but `path` is undefined. This creates a partial ToolUse with incomplete data. - -#### B. ChatRow.tsx -**Location**: [`Roo-Code/webview-ui/src/components/chat/ChatRow.tsx:427-459`](Roo-Code/webview-ui/src/components/chat/ChatRow.tsx:427) - -Renders the file edit UI using `tool.path` from the partial ToolUse: - -```tsx - -``` - -**Problem**: No check for undefined path before rendering, so the component renders with incomplete data. - -#### C. CodeAccordian.tsx -**Location**: [`Roo-Code/webview-ui/src/components/common/CodeAccordian.tsx:27-131`](Roo-Code/webview-ui/src/components/common/CodeAccordian.tsx:27) - -Receives the `path` prop and formats it for display: - -```tsx - - - {formatPathTooltip(path)} - - -``` - -**Problem**: When path is undefined, `formatPathTooltip` returns an empty string, causing the UI to show no file target or the project root. - -#### D. formatPathTooltip.ts -**Location**: [`Roo-Code/webview-ui/src/utils/formatPathTooltip.ts:18-19`](Roo-Code/webview-ui/src/utils/formatPathTooltip.ts:18) - -```typescript -export function formatPathTooltip(path?: string, additionalContent?: string): string { - if (!path) return "" // ⚠️ Returns empty string for undefined path - // ... -} -``` - -**Problem**: Returns empty string when path is undefined, which results in empty/root directory display in UI. - -## Impact - -1. **Confusing UX**: Users see "wants to edit this file" with the project root selected, not understanding what file will be modified -2. **Long perceived wait times**: The UI appears frozen/unresponsive while waiting for the pathname to arrive -3. **Trust issues**: Users may not trust what Roo is doing when they see incorrect file targets - -## Solution Approaches - -### Option 1: Delay Rendering Until Path Arrives ⭐ (Recommended) - -**Strategy**: Don't create partial nativeArgs for file editing tools until the required `path` parameter is present. - -**Changes needed**: Modify [`NativeToolCallParser.createPartialToolUse()`](Roo-Code/src/core/assistant-message/NativeToolCallParser.ts:387) conditions: - -```typescript -case "write_to_file": - // Only create nativeArgs when BOTH path and content are present - if (partialArgs.path && partialArgs.content) { - nativeArgs = { - path: partialArgs.path, - content: partialArgs.content, - } - } - break; - -case "apply_diff": - // Only create nativeArgs when BOTH path and diff are present - if (partialArgs.path && partialArgs.diff) { - nativeArgs = { - path: partialArgs.path, - diff: partialArgs.diff, - } - } - break; -``` - -**Pros**: -- Simple fix with minimal code changes -- Prevents confusing UI states entirely -- No new UI components needed -- Matches user expectations (don't show incomplete information) - -**Cons**: -- User sees no visual feedback until path arrives -- May feel slightly less responsive (but more accurate) - -### Option 2: Show "Loading..." Indicator - -**Strategy**: Render a loading state when content is present but path is undefined. - -**Changes needed**: Modify [`ChatRow.tsx`](Roo-Code/webview-ui/src/components/chat/ChatRow.tsx:446) or [`CodeAccordian.tsx`](Roo-Code/webview-ui/src/components/common/CodeAccordian.tsx:74) to detect missing path and show loading indicator. - -**Pros**: -- Provides immediate visual feedback -- Shows that something is happening -- More responsive feel - -**Cons**: -- Requires new UI component/state -- More complex implementation -- Still shows incomplete information (loading instead of target) - -### Option 3: Suppress Partial Updates for File Tools - -**Strategy**: Don't emit partial ToolUse updates for tools that require a path parameter. - -**Changes needed**: Add logic to [`processStreamingChunk`](Roo-Code/src/core/assistant-message/NativeToolCallParser.ts:234) to suppress partial updates for specific tools. - -**Pros**: -- Completely prevents the issue at the source -- Clear separation between tools that support partial rendering vs. those that don't - -**Cons**: -- No visual feedback during streaming -- Requires tool categorization/configuration -- More significant code changes - -## Recommendation - -**Option 1** is the best approach because: - -1. **Simplest implementation**: Single logical condition change in one function -2. **Prevents bad UX**: Users never see incorrect/incomplete information -3. **No new UI needed**: Leverages existing rendering flow -4. **Consistent behavior**: Aligns with how other tools handle required parameters -5. **Easy to test**: Clear before/after behavior - -The fix should be applied in [`NativeToolCallParser.createPartialToolUse()`](Roo-Code/src/core/assistant-message/NativeToolCallParser.ts:344) by requiring BOTH path and content/diff to be present before creating nativeArgs for file editing tools. - -## Files to Modify - -1. **Primary Fix**: [`Roo-Code/src/core/assistant-message/NativeToolCallParser.ts`](Roo-Code/src/core/assistant-message/NativeToolCallParser.ts) - - Line 387-394: `write_to_file` case - - Line 405-412: `apply_diff` case - -## Testing Considerations - -1. Test with a model that uses native tool calling -2. Verify that file edits don't show UI until path is present -3. Ensure other tools (without required path) still show partial updates correctly -4. Check that final rendering works correctly once path arrives -5. Test with slow network conditions to simulate delayed path arrival