@@ -402,4 +402,164 @@ describe("ToolRepetitionDetector", () => {
402402 }
403403 } )
404404 } )
405+
406+ // ===== Browser Scroll Action Exclusion tests =====
407+ describe ( "browser scroll action exclusion" , ( ) => {
408+ it ( "should not count browser scroll_down actions as repetitions" , ( ) => {
409+ const detector = new ToolRepetitionDetector ( 2 )
410+
411+ // Create browser_action tool use with scroll_down
412+ const scrollDownTool : ToolUse = {
413+ type : "tool_use" ,
414+ name : "browser_action" as ToolName ,
415+ params : { action : "scroll_down" } ,
416+ partial : false ,
417+ }
418+
419+ // Should allow unlimited scroll_down actions
420+ for ( let i = 0 ; i < 10 ; i ++ ) {
421+ const result = detector . check ( scrollDownTool )
422+ expect ( result . allowExecution ) . toBe ( true )
423+ expect ( result . askUser ) . toBeUndefined ( )
424+ }
425+ } )
426+
427+ it ( "should not count browser scroll_up actions as repetitions" , ( ) => {
428+ const detector = new ToolRepetitionDetector ( 2 )
429+
430+ // Create browser_action tool use with scroll_up
431+ const scrollUpTool : ToolUse = {
432+ type : "tool_use" ,
433+ name : "browser_action" as ToolName ,
434+ params : { action : "scroll_up" } ,
435+ partial : false ,
436+ }
437+
438+ // Should allow unlimited scroll_up actions
439+ for ( let i = 0 ; i < 10 ; i ++ ) {
440+ const result = detector . check ( scrollUpTool )
441+ expect ( result . allowExecution ) . toBe ( true )
442+ expect ( result . askUser ) . toBeUndefined ( )
443+ }
444+ } )
445+
446+ it ( "should not count alternating scroll_down and scroll_up as repetitions" , ( ) => {
447+ const detector = new ToolRepetitionDetector ( 2 )
448+
449+ const scrollDownTool : ToolUse = {
450+ type : "tool_use" ,
451+ name : "browser_action" as ToolName ,
452+ params : { action : "scroll_down" } ,
453+ partial : false ,
454+ }
455+
456+ const scrollUpTool : ToolUse = {
457+ type : "tool_use" ,
458+ name : "browser_action" as ToolName ,
459+ params : { action : "scroll_up" } ,
460+ partial : false ,
461+ }
462+
463+ // Alternate between scroll_down and scroll_up
464+ for ( let i = 0 ; i < 5 ; i ++ ) {
465+ let result = detector . check ( scrollDownTool )
466+ expect ( result . allowExecution ) . toBe ( true )
467+ expect ( result . askUser ) . toBeUndefined ( )
468+
469+ result = detector . check ( scrollUpTool )
470+ expect ( result . allowExecution ) . toBe ( true )
471+ expect ( result . askUser ) . toBeUndefined ( )
472+ }
473+ } )
474+
475+ it ( "should still apply repetition detection to other browser_action types" , ( ) => {
476+ const detector = new ToolRepetitionDetector ( 2 )
477+
478+ // Create browser_action tool use with click action
479+ const clickTool : ToolUse = {
480+ type : "tool_use" ,
481+ name : "browser_action" as ToolName ,
482+ params : { action : "click" , coordinate : "[100, 200]" } ,
483+ partial : false ,
484+ }
485+
486+ // First call allowed
487+ expect ( detector . check ( clickTool ) . allowExecution ) . toBe ( true )
488+
489+ // Second call allowed
490+ expect ( detector . check ( clickTool ) . allowExecution ) . toBe ( true )
491+
492+ // Third identical call should be blocked (limit is 2)
493+ const result = detector . check ( clickTool )
494+ expect ( result . allowExecution ) . toBe ( false )
495+ expect ( result . askUser ) . toBeDefined ( )
496+ } )
497+
498+ it ( "should still apply repetition detection to non-browser tools" , ( ) => {
499+ const detector = new ToolRepetitionDetector ( 2 )
500+
501+ const readFileTool = createToolUse ( "read_file" , "read_file" , { path : "test.txt" } )
502+
503+ // First call allowed
504+ expect ( detector . check ( readFileTool ) . allowExecution ) . toBe ( true )
505+
506+ // Second call allowed
507+ expect ( detector . check ( readFileTool ) . allowExecution ) . toBe ( true )
508+
509+ // Third identical call should be blocked (limit is 2)
510+ const result = detector . check ( readFileTool )
511+ expect ( result . allowExecution ) . toBe ( false )
512+ expect ( result . askUser ) . toBeDefined ( )
513+ } )
514+
515+ it ( "should not interfere with repetition detection of other tools when scroll actions are interspersed" , ( ) => {
516+ const detector = new ToolRepetitionDetector ( 2 )
517+
518+ const scrollTool : ToolUse = {
519+ type : "tool_use" ,
520+ name : "browser_action" as ToolName ,
521+ params : { action : "scroll_down" } ,
522+ partial : false ,
523+ }
524+
525+ const otherTool = createToolUse ( "execute_command" , "execute_command" , { command : "ls" } )
526+
527+ // First execute_command
528+ expect ( detector . check ( otherTool ) . allowExecution ) . toBe ( true )
529+
530+ // Scroll actions in between (should not affect counter)
531+ expect ( detector . check ( scrollTool ) . allowExecution ) . toBe ( true )
532+ expect ( detector . check ( scrollTool ) . allowExecution ) . toBe ( true )
533+
534+ // Second execute_command
535+ expect ( detector . check ( otherTool ) . allowExecution ) . toBe ( true )
536+
537+ // More scroll actions
538+ expect ( detector . check ( scrollTool ) . allowExecution ) . toBe ( true )
539+
540+ // Third execute_command should be blocked
541+ const result = detector . check ( otherTool )
542+ expect ( result . allowExecution ) . toBe ( false )
543+ expect ( result . askUser ) . toBeDefined ( )
544+ } )
545+
546+ it ( "should handle browser_action with missing or invalid action parameter gracefully" , ( ) => {
547+ const detector = new ToolRepetitionDetector ( 2 )
548+
549+ // Browser action without action parameter
550+ const noActionTool : ToolUse = {
551+ type : "tool_use" ,
552+ name : "browser_action" as ToolName ,
553+ params : { } ,
554+ partial : false ,
555+ }
556+
557+ // Should apply normal repetition detection
558+ expect ( detector . check ( noActionTool ) . allowExecution ) . toBe ( true )
559+ expect ( detector . check ( noActionTool ) . allowExecution ) . toBe ( true )
560+ const result = detector . check ( noActionTool )
561+ expect ( result . allowExecution ) . toBe ( false )
562+ expect ( result . askUser ) . toBeDefined ( )
563+ } )
564+ } )
405565} )
0 commit comments