@@ -402,4 +402,164 @@ describe("ToolRepetitionDetector", () => {
402
402
}
403
403
} )
404
404
} )
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
+ } )
405
565
} )
0 commit comments