@@ -23,7 +23,6 @@ import type {
2323 ToolResult ,
2424} from './tools.js' ;
2525import { ToolConfirmationOutcome } from './tools.js' ;
26- import { type EditToolParams } from './edit.js' ;
2726import type { Config } from '../config/config.js' ;
2827import { ApprovalMode } from '../policy/types.js' ;
2928import type { ToolRegistry } from './tool-registry.js' ;
@@ -33,11 +32,7 @@ import fs from 'node:fs';
3332import os from 'node:os' ;
3433import { GeminiClient } from '../core/client.js' ;
3534import type { BaseLlmClient } from '../core/baseLlmClient.js' ;
36- import type { CorrectedEditResult } from '../utils/editCorrector.js' ;
37- import {
38- ensureCorrectEdit ,
39- ensureCorrectFileContent ,
40- } from '../utils/editCorrector.js' ;
35+ import { ensureCorrectFileContent } from '../utils/editCorrector.js' ;
4136import { StandardFileSystemService } from '../services/fileSystemService.js' ;
4237import type { DiffUpdateResult } from '../ide/ide-client.js' ;
4338import { IdeClient } from '../ide/ide-client.js' ;
@@ -61,15 +56,13 @@ vi.mock('../ide/ide-client.js', () => ({
6156let mockGeminiClientInstance : Mocked < GeminiClient > ;
6257let mockBaseLlmClientInstance : Mocked < BaseLlmClient > ;
6358let mockConfig : Config ;
64- const mockEnsureCorrectEdit = vi . fn < typeof ensureCorrectEdit > ( ) ;
6559const mockEnsureCorrectFileContent = vi . fn < typeof ensureCorrectFileContent > ( ) ;
6660const mockIdeClient = {
6761 openDiff : vi . fn ( ) ,
6862 isDiffingEnabled : vi . fn ( ) ,
6963} ;
7064
7165// Wire up the mocked functions to be used by the actual module imports
72- vi . mocked ( ensureCorrectEdit ) . mockImplementation ( mockEnsureCorrectEdit ) ;
7366vi . mocked ( ensureCorrectFileContent ) . mockImplementation (
7467 mockEnsureCorrectFileContent ,
7568) ;
@@ -110,6 +103,7 @@ const mockConfigInternal = {
110103 } ) as unknown as ToolRegistry ,
111104 isInteractive : ( ) => false ,
112105 getDisableLLMCorrection : vi . fn ( ( ) => true ) ,
106+ getActiveModel : ( ) => 'test-model' ,
113107 storage : {
114108 getProjectTempDir : vi . fn ( ) . mockReturnValue ( '/tmp/project' ) ,
115109 } ,
@@ -179,7 +173,6 @@ describe('WriteFileTool', () => {
179173 generateJson : vi . fn ( ) ,
180174 } as unknown as Mocked < BaseLlmClient > ;
181175
182- vi . mocked ( ensureCorrectEdit ) . mockImplementation ( mockEnsureCorrectEdit ) ;
183176 vi . mocked ( ensureCorrectFileContent ) . mockImplementation (
184177 mockEnsureCorrectFileContent ,
185178 ) ;
@@ -199,28 +192,9 @@ describe('WriteFileTool', () => {
199192 // Reset mocks before each test
200193 mockConfigInternal . getApprovalMode . mockReturnValue ( ApprovalMode . DEFAULT ) ;
201194 mockConfigInternal . setApprovalMode . mockClear ( ) ;
202- mockEnsureCorrectEdit . mockReset ( ) ;
203195 mockEnsureCorrectFileContent . mockReset ( ) ;
204196
205197 // Default mock implementations that return valid structures
206- mockEnsureCorrectEdit . mockImplementation (
207- async (
208- filePath : string ,
209- _currentContent : string ,
210- params : EditToolParams ,
211- _client : GeminiClient ,
212- _baseClient : BaseLlmClient ,
213- signal ?: AbortSignal ,
214- ) : Promise < CorrectedEditResult > => {
215- if ( signal ?. aborted ) {
216- return Promise . reject ( new Error ( 'Aborted' ) ) ;
217- }
218- return Promise . resolve ( {
219- params : { ...params , new_string : params . new_string ?? '' } ,
220- occurrences : 1 ,
221- } ) ;
222- } ,
223- ) ;
224198 mockEnsureCorrectFileContent . mockImplementation (
225199 async (
226200 content : string ,
@@ -369,15 +343,43 @@ describe('WriteFileTool', () => {
369343 mockBaseLlmClientInstance ,
370344 abortSignal ,
371345 true ,
346+ true , // aggressiveUnescape
372347 ) ;
373- expect ( mockEnsureCorrectEdit ) . not . toHaveBeenCalled ( ) ;
374348 expect ( result . correctedContent ) . toBe ( correctedContent ) ;
375349 expect ( result . originalContent ) . toBe ( '' ) ;
376350 expect ( result . fileExists ) . toBe ( false ) ;
377351 expect ( result . error ) . toBeUndefined ( ) ;
378352 } ) ;
379353
380- it ( 'should call ensureCorrectEdit for an existing file' , async ( ) => {
354+ it ( 'should set aggressiveUnescape to false for gemini-3 models' , async ( ) => {
355+ const filePath = path . join ( rootDir , 'gemini3_file.txt' ) ;
356+ const proposedContent = 'Proposed new content.' ;
357+ const abortSignal = new AbortController ( ) . signal ;
358+
359+ const mockGemini3Config = {
360+ ...mockConfig ,
361+ getActiveModel : ( ) => 'gemini-3.0-pro' ,
362+ } as unknown as Config ;
363+
364+ mockEnsureCorrectFileContent . mockResolvedValue ( 'Corrected new content.' ) ;
365+
366+ await getCorrectedFileContent (
367+ mockGemini3Config ,
368+ filePath ,
369+ proposedContent ,
370+ abortSignal ,
371+ ) ;
372+
373+ expect ( mockEnsureCorrectFileContent ) . toHaveBeenCalledWith (
374+ proposedContent ,
375+ mockBaseLlmClientInstance ,
376+ abortSignal ,
377+ true ,
378+ false , // aggressiveUnescape
379+ ) ;
380+ } ) ;
381+
382+ it ( 'should call ensureCorrectFileContent for an existing file' , async ( ) => {
381383 const filePath = path . join ( rootDir , 'existing_corrected_file.txt' ) ;
382384 const originalContent = 'Original existing content.' ;
383385 const proposedContent = 'Proposed replacement content.' ;
@@ -386,14 +388,7 @@ describe('WriteFileTool', () => {
386388 fs . writeFileSync ( filePath , originalContent , 'utf8' ) ;
387389
388390 // Ensure this mock is active and returns the correct structure
389- mockEnsureCorrectEdit . mockResolvedValue ( {
390- params : {
391- file_path : filePath ,
392- old_string : originalContent ,
393- new_string : correctedProposedContent ,
394- } ,
395- occurrences : 1 ,
396- } as CorrectedEditResult ) ;
391+ mockEnsureCorrectFileContent . mockResolvedValue ( correctedProposedContent ) ;
397392
398393 const result = await getCorrectedFileContent (
399394 mockConfig ,
@@ -402,20 +397,13 @@ describe('WriteFileTool', () => {
402397 abortSignal ,
403398 ) ;
404399
405- expect ( mockEnsureCorrectEdit ) . toHaveBeenCalledWith (
406- filePath ,
407- originalContent ,
408- {
409- old_string : originalContent ,
410- new_string : proposedContent ,
411- file_path : filePath ,
412- } ,
413- mockGeminiClientInstance ,
400+ expect ( mockEnsureCorrectFileContent ) . toHaveBeenCalledWith (
401+ proposedContent ,
414402 mockBaseLlmClientInstance ,
415403 abortSignal ,
416404 true ,
405+ true , // aggressiveUnescape
417406 ) ;
418- expect ( mockEnsureCorrectFileContent ) . not . toHaveBeenCalled ( ) ;
419407 expect ( result . correctedContent ) . toBe ( correctedProposedContent ) ;
420408 expect ( result . originalContent ) . toBe ( originalContent ) ;
421409 expect ( result . fileExists ) . toBe ( true ) ;
@@ -441,7 +429,6 @@ describe('WriteFileTool', () => {
441429 ) ;
442430
443431 expect ( fsService . readTextFile ) . toHaveBeenCalledWith ( filePath ) ;
444- expect ( mockEnsureCorrectEdit ) . not . toHaveBeenCalled ( ) ;
445432 expect ( mockEnsureCorrectFileContent ) . not . toHaveBeenCalled ( ) ;
446433 expect ( result . correctedContent ) . toBe ( proposedContent ) ;
447434 expect ( result . originalContent ) . toBe ( '' ) ;
@@ -492,6 +479,7 @@ describe('WriteFileTool', () => {
492479 mockBaseLlmClientInstance ,
493480 abortSignal ,
494481 true ,
482+ true , // aggressiveUnescape
495483 ) ;
496484 expect ( confirmation ) . toEqual (
497485 expect . objectContaining ( {
@@ -516,33 +504,20 @@ describe('WriteFileTool', () => {
516504 'Corrected replacement for confirmation.' ;
517505 fs . writeFileSync ( filePath , originalContent , 'utf8' ) ;
518506
519- mockEnsureCorrectEdit . mockResolvedValue ( {
520- params : {
521- file_path : filePath ,
522- old_string : originalContent ,
523- new_string : correctedProposedContent ,
524- } ,
525- occurrences : 1 ,
526- } ) ;
507+ mockEnsureCorrectFileContent . mockResolvedValue ( correctedProposedContent ) ;
527508
528509 const params = { file_path : filePath , content : proposedContent } ;
529510 const invocation = tool . build ( params ) ;
530511 const confirmation = ( await invocation . shouldConfirmExecute (
531512 abortSignal ,
532513 ) ) as ToolEditConfirmationDetails ;
533514
534- expect ( mockEnsureCorrectEdit ) . toHaveBeenCalledWith (
535- filePath ,
536- originalContent ,
537- {
538- old_string : originalContent ,
539- new_string : proposedContent ,
540- file_path : filePath ,
541- } ,
542- mockGeminiClientInstance ,
515+ expect ( mockEnsureCorrectFileContent ) . toHaveBeenCalledWith (
516+ proposedContent ,
543517 mockBaseLlmClientInstance ,
544518 abortSignal ,
545519 true ,
520+ true , // aggressiveUnescape
546521 ) ;
547522 expect ( confirmation ) . toEqual (
548523 expect . objectContaining ( {
@@ -738,6 +713,7 @@ describe('WriteFileTool', () => {
738713 mockBaseLlmClientInstance ,
739714 abortSignal ,
740715 true ,
716+ true , // aggressiveUnescape
741717 ) ;
742718 expect ( result . llmContent ) . toMatch (
743719 / S u c c e s s f u l l y c r e a t e d a n d w r o t e t o n e w f i l e / ,
@@ -768,14 +744,7 @@ describe('WriteFileTool', () => {
768744 const correctedProposedContent = 'Corrected overwrite for execute.' ;
769745 fs . writeFileSync ( filePath , initialContent , 'utf8' ) ;
770746
771- mockEnsureCorrectEdit . mockResolvedValue ( {
772- params : {
773- file_path : filePath ,
774- old_string : initialContent ,
775- new_string : correctedProposedContent ,
776- } ,
777- occurrences : 1 ,
778- } ) ;
747+ mockEnsureCorrectFileContent . mockResolvedValue ( correctedProposedContent ) ;
779748
780749 const params = { file_path : filePath , content : proposedContent } ;
781750 const invocation = tool . build ( params ) ;
@@ -784,18 +753,12 @@ describe('WriteFileTool', () => {
784753
785754 const result = await invocation . execute ( abortSignal ) ;
786755
787- expect ( mockEnsureCorrectEdit ) . toHaveBeenCalledWith (
788- filePath ,
789- initialContent ,
790- {
791- old_string : initialContent ,
792- new_string : proposedContent ,
793- file_path : filePath ,
794- } ,
795- mockGeminiClientInstance ,
756+ expect ( mockEnsureCorrectFileContent ) . toHaveBeenCalledWith (
757+ proposedContent ,
796758 mockBaseLlmClientInstance ,
797759 abortSignal ,
798760 true ,
761+ true , // aggressiveUnescape
799762 ) ;
800763 expect ( result . llmContent ) . toMatch ( / S u c c e s s f u l l y o v e r w r o t e f i l e / ) ;
801764 const writtenContent = await fsService . readTextFile ( filePath ) ;
@@ -892,14 +855,7 @@ describe('WriteFileTool', () => {
892855 newLines [ 50 ] = 'Line 51 Modified' ; // Modify one line in the middle
893856
894857 const newContent = newLines . join ( '\n' ) ;
895- mockEnsureCorrectEdit . mockResolvedValue ( {
896- params : {
897- file_path : filePath ,
898- old_string : originalContent ,
899- new_string : newContent ,
900- } ,
901- occurrences : 1 ,
902- } ) ;
858+ mockEnsureCorrectFileContent . mockResolvedValue ( newContent ) ;
903859
904860 const params = { file_path : filePath , content : newContent } ;
905861 const invocation = tool . build ( params ) ;
@@ -1072,28 +1028,21 @@ describe('WriteFileTool', () => {
10721028 mockBaseLlmClientInstance ,
10731029 abortSignal ,
10741030 true ,
1031+ true , // aggressiveUnescape
10751032 ) ;
1076- expect ( mockEnsureCorrectEdit ) . not . toHaveBeenCalled ( ) ;
10771033 expect ( result . correctedContent ) . toBe ( proposedContent ) ;
10781034 expect ( result . fileExists ) . toBe ( false ) ;
10791035 } ) ;
10801036
1081- it ( 'should call ensureCorrectEdit with disableLLMCorrection=true for an existing file when disabled' , async ( ) => {
1037+ it ( 'should call ensureCorrectFileContent with disableLLMCorrection=true for an existing file when disabled' , async ( ) => {
10821038 const filePath = path . join ( rootDir , 'existing_file_no_correction.txt' ) ;
10831039 const originalContent = 'Original content.' ;
10841040 const proposedContent = 'Proposed content.' ;
10851041 fs . writeFileSync ( filePath , originalContent , 'utf8' ) ;
10861042
10871043 mockConfigInternal . getDisableLLMCorrection . mockReturnValue ( true ) ;
10881044 // Ensure the mock returns the content passed to it
1089- mockEnsureCorrectEdit . mockResolvedValue ( {
1090- params : {
1091- file_path : filePath ,
1092- old_string : originalContent ,
1093- new_string : proposedContent ,
1094- } ,
1095- occurrences : 1 ,
1096- } ) ;
1045+ mockEnsureCorrectFileContent . mockResolvedValue ( proposedContent ) ;
10971046
10981047 const result = await getCorrectedFileContent (
10991048 mockConfig ,
@@ -1102,16 +1051,13 @@ describe('WriteFileTool', () => {
11021051 abortSignal ,
11031052 ) ;
11041053
1105- expect ( mockEnsureCorrectEdit ) . toHaveBeenCalledWith (
1106- filePath ,
1107- originalContent ,
1108- expect . anything ( ) , // params object
1109- mockGeminiClientInstance ,
1054+ expect ( mockEnsureCorrectFileContent ) . toHaveBeenCalledWith (
1055+ proposedContent ,
11101056 mockBaseLlmClientInstance ,
11111057 abortSignal ,
11121058 true ,
1059+ true , // aggressiveUnescape
11131060 ) ;
1114- expect ( mockEnsureCorrectFileContent ) . not . toHaveBeenCalled ( ) ;
11151061 expect ( result . correctedContent ) . toBe ( proposedContent ) ;
11161062 expect ( result . originalContent ) . toBe ( originalContent ) ;
11171063 expect ( result . fileExists ) . toBe ( true ) ;
0 commit comments