@@ -493,7 +493,7 @@ describe("message tool gateway timeout", () => {
493493describe ( "poll vote echo guard" , ( ) => {
494494 const currentChat = "iMessage;-;+15550001111" ;
495495
496- function createPollVoteTool ( ) {
496+ function createPollVoteTool ( votedOption = "Blue" ) {
497497 setActivePluginRegistry (
498498 createTestRegistry ( [
499499 {
@@ -525,7 +525,7 @@ describe("poll vote echo guard", () => {
525525 payload : { } ,
526526 toolResult : {
527527 content : [ { type : "text" , text : "vote cast" } ] ,
528- details : { pollVotedOption : "Blue" } ,
528+ details : { pollVotedOption : votedOption } ,
529529 } ,
530530 dryRun : false ,
531531 } as MessageActionRunResult )
@@ -575,6 +575,37 @@ describe("poll vote echo guard", () => {
575575 expect ( mocks . runMessageAction ) . toHaveBeenCalledTimes ( 1 ) ;
576576 } ) ;
577577
578+ it ( "suppresses an emoji-suffixed option echoed with a leading emoji" , async ( ) => {
579+ // Live regression: iMessage poll options carry a trailing emoji
580+ // ("Lobster 🦞 ") while the agent echoes a leading one ("🦞 Lobster.").
581+ // A leading-only emoji strip left "lobster 🦞" != "lobster" and leaked.
582+ const tool = createPollVoteTool ( "Lobster 🦞 " ) ;
583+ await castBlueVote ( tool ) ;
584+
585+ const result = await tool . execute ( "send" , {
586+ action : "send" ,
587+ channel : "imessage" ,
588+ message : "🦞 Lobster." ,
589+ } ) ;
590+
591+ expect ( result . details ) . toMatchObject ( { status : "suppressed" , reason : "poll_vote_echo" } ) ;
592+ expect ( mocks . runMessageAction ) . toHaveBeenCalledTimes ( 1 ) ;
593+ } ) ;
594+
595+ it ( "does not suppress a different keycap option with the same words" , async ( ) => {
596+ const tool = createPollVoteTool ( "Option 1️⃣" ) ;
597+ await castBlueVote ( tool ) ;
598+
599+ const result = await tool . execute ( "send" , {
600+ action : "send" ,
601+ channel : "imessage" ,
602+ message : "2️⃣ Option." ,
603+ } ) ;
604+
605+ expect ( result . details ) . not . toMatchObject ( { status : "suppressed" } ) ;
606+ expect ( mocks . runMessageAction ) . toHaveBeenCalledTimes ( 2 ) ;
607+ } ) ;
608+
578609 it ( "does not cross accounts, delivery targets, or conflicting target fields" , async ( ) => {
579610 const accountTool = createPollVoteTool ( ) ;
580611 await castBlueVote ( accountTool ) ;
0 commit comments