@@ -585,6 +585,110 @@ describe("runCodexAppServerSideQuestion", () => {
585585 expect ( toolOptions ) . toHaveProperty ( "requireExplicitMessageTarget" , true ) ;
586586 } ) ;
587587
588+ it ( "replays app-scoped reviewer policy into side-thread forks" , async ( ) => {
589+ const client = createFakeClient ( ) ;
590+ getSharedCodexAppServerClientMock . mockResolvedValue ( client ) ;
591+ readCodexAppServerBindingMock . mockResolvedValue ( {
592+ schemaVersion : 2 ,
593+ threadId : "parent-thread" ,
594+ sessionFile : "/tmp/session-1.jsonl" ,
595+ cwd : "/tmp/workspace" ,
596+ authProfileId : "openai:work" ,
597+ model : "gpt-5.5" ,
598+ approvalPolicy : "on-request" ,
599+ sandbox : "workspace-write" ,
600+ pluginAppPolicyContext : {
601+ fingerprint : "mixed-plugin-policy" ,
602+ apps : {
603+ "ask-app" : {
604+ configKey : "ask" ,
605+ marketplaceName : "openai" ,
606+ pluginName : "ask" ,
607+ allowDestructiveActions : true ,
608+ destructiveApprovalMode : "ask" ,
609+ mcpServerNames : [ "ask" ] ,
610+ } ,
611+ "true-app" : {
612+ configKey : "true" ,
613+ marketplaceName : "openai" ,
614+ pluginName : "true" ,
615+ allowDestructiveActions : true ,
616+ destructiveApprovalMode : "allow" ,
617+ mcpServerNames : [ "true" ] ,
618+ } ,
619+ "false-app" : {
620+ configKey : "false" ,
621+ marketplaceName : "openai" ,
622+ pluginName : "false" ,
623+ allowDestructiveActions : false ,
624+ destructiveApprovalMode : "deny" ,
625+ mcpServerNames : [ "false" ] ,
626+ } ,
627+ "auto-app" : {
628+ configKey : "auto" ,
629+ marketplaceName : "openai" ,
630+ pluginName : "auto" ,
631+ allowDestructiveActions : true ,
632+ destructiveApprovalMode : "auto" ,
633+ mcpServerNames : [ "auto" ] ,
634+ } ,
635+ } ,
636+ pluginAppIds : {
637+ ask : [ "ask-app" ] ,
638+ true : [ "true-app" ] ,
639+ false : [ "false-app" ] ,
640+ auto : [ "auto-app" ] ,
641+ } ,
642+ } ,
643+ createdAt : new Date ( 0 ) . toISOString ( ) ,
644+ updatedAt : new Date ( 0 ) . toISOString ( ) ,
645+ } ) ;
646+
647+ await expect (
648+ runCodexAppServerSideQuestion ( sideParams ( ) , {
649+ pluginConfig : { appServer : { mode : "guardian" } } ,
650+ } ) ,
651+ ) . resolves . toEqual ( { text : "Side answer." } ) ;
652+
653+ const forkParams = mockCall ( client . request ) [ 1 ] as Record < string , unknown > | undefined ;
654+ expect ( forkParams ?. approvalsReviewer ) . toBe ( "auto_review" ) ;
655+ const config = forkParams ?. config as Record < string , unknown > | undefined ;
656+ expect ( config ) . not . toHaveProperty ( "approvals_reviewer" ) ;
657+ expect ( config ?. [ "features.code_mode" ] ) . toBe ( true ) ;
658+ expect ( config ?. apps ) . toEqual ( {
659+ _default : {
660+ enabled : false ,
661+ destructive_enabled : false ,
662+ open_world_enabled : false ,
663+ } ,
664+ "ask-app" : {
665+ enabled : true ,
666+ approvals_reviewer : "user" ,
667+ destructive_enabled : true ,
668+ open_world_enabled : true ,
669+ default_tools_approval_mode : "auto" ,
670+ } ,
671+ "auto-app" : {
672+ enabled : true ,
673+ destructive_enabled : true ,
674+ open_world_enabled : true ,
675+ default_tools_approval_mode : "auto" ,
676+ } ,
677+ "false-app" : {
678+ enabled : true ,
679+ destructive_enabled : false ,
680+ open_world_enabled : true ,
681+ default_tools_approval_mode : "auto" ,
682+ } ,
683+ "true-app" : {
684+ enabled : true ,
685+ destructive_enabled : true ,
686+ open_world_enabled : true ,
687+ default_tools_approval_mode : "auto" ,
688+ } ,
689+ } ) ;
690+ } ) ;
691+
588692 it ( "disables hosted search when side-question sender policy removes managed web_search" , async ( ) => {
589693 createOpenClawCodingToolsMock . mockImplementation ( ( options : { senderId ?: string } ) =>
590694 options . senderId === "restricted-sender"
0 commit comments