@@ -1557,3 +1557,134 @@ async fn run_scenario(scenario: &ScenarioSpec) -> Result<()> {
15571557
15581558 Ok ( ( ) )
15591559}
1560+
1561+ #[ tokio:: test( flavor = "multi_thread" , worker_threads = 2 ) ]
1562+ async fn approving_allow_prefix_persists_policy_and_skips_future_prompts ( ) -> Result < ( ) > {
1563+ let server = start_mock_server ( ) . await ;
1564+ let approval_policy = AskForApproval :: UnlessTrusted ;
1565+ let sandbox_policy = SandboxPolicy :: DangerFullAccess ;
1566+ let sandbox_policy_for_config = sandbox_policy. clone ( ) ;
1567+ let mut builder = test_codex ( ) . with_config ( move |config| {
1568+ config. approval_policy = approval_policy;
1569+ config. sandbox_policy = sandbox_policy_for_config;
1570+ } ) ;
1571+ let test = builder. build ( & server) . await ?;
1572+
1573+ let call_id_first = "allow-prefix-first" ;
1574+ let ( first_event, expected_command) = ActionKind :: RunCommand {
1575+ command : & [ "printf" , "allow-prefix-ok" ] ,
1576+ }
1577+ . prepare ( & test, & server, call_id_first, false )
1578+ . await ?;
1579+ let expected_command =
1580+ expected_command. expect ( "allow prefix scenario should produce a shell command" ) ;
1581+
1582+ let _ = mount_sse_once (
1583+ & server,
1584+ sse ( vec ! [
1585+ ev_response_created( "resp-allow-prefix-1" ) ,
1586+ first_event,
1587+ ev_completed( "resp-allow-prefix-1" ) ,
1588+ ] ) ,
1589+ )
1590+ . await ;
1591+ let first_results = mount_sse_once (
1592+ & server,
1593+ sse ( vec ! [
1594+ ev_assistant_message( "msg-allow-prefix-1" , "done" ) ,
1595+ ev_completed( "resp-allow-prefix-2" ) ,
1596+ ] ) ,
1597+ )
1598+ . await ;
1599+
1600+ submit_turn (
1601+ & test,
1602+ "allow-prefix-first" ,
1603+ approval_policy,
1604+ sandbox_policy. clone ( ) ,
1605+ )
1606+ . await ?;
1607+
1608+ let approval = expect_exec_approval ( & test, & expected_command) . await ;
1609+ assert_eq ! ( approval. allow_prefix, Some ( expected_command. clone( ) ) ) ;
1610+
1611+ test. codex
1612+ . submit ( Op :: ExecApproval {
1613+ id : "0" . into ( ) ,
1614+ decision : ReviewDecision :: ApprovedAllowPrefix {
1615+ allow_prefix : expected_command. clone ( ) ,
1616+ } ,
1617+ } )
1618+ . await ?;
1619+ wait_for_completion ( & test) . await ;
1620+
1621+ let policy_path = test. home . path ( ) . join ( "policy" ) . join ( "default.codexpolicy" ) ;
1622+ let policy_contents = fs:: read_to_string ( & policy_path) ?;
1623+ assert ! (
1624+ policy_contents
1625+ . contains( "prefix_rule(pattern=[\" printf\" ,\" allow-prefix-ok\" ], decision=\" allow\" )" ) ,
1626+ "unexpected policy contents: {policy_contents}"
1627+ ) ;
1628+
1629+ let first_output = parse_result (
1630+ & first_results
1631+ . single_request ( )
1632+ . function_call_output ( call_id_first) ,
1633+ ) ;
1634+ assert_eq ! ( first_output. exit_code. unwrap_or( 0 ) , 0 ) ;
1635+ assert ! (
1636+ first_output. stdout. contains( "allow-prefix-ok" ) ,
1637+ "unexpected stdout: {}" ,
1638+ first_output. stdout
1639+ ) ;
1640+
1641+ let call_id_second = "allow-prefix-second" ;
1642+ let ( second_event, second_command) = ActionKind :: RunCommand {
1643+ command : & [ "printf" , "allow-prefix-ok" ] ,
1644+ }
1645+ . prepare ( & test, & server, call_id_second, false )
1646+ . await ?;
1647+ assert_eq ! ( second_command, Some ( expected_command. clone( ) ) ) ;
1648+
1649+ let _ = mount_sse_once (
1650+ & server,
1651+ sse ( vec ! [
1652+ ev_response_created( "resp-allow-prefix-3" ) ,
1653+ second_event,
1654+ ev_completed( "resp-allow-prefix-3" ) ,
1655+ ] ) ,
1656+ )
1657+ . await ;
1658+ let second_results = mount_sse_once (
1659+ & server,
1660+ sse ( vec ! [
1661+ ev_assistant_message( "msg-allow-prefix-2" , "done" ) ,
1662+ ev_completed( "resp-allow-prefix-4" ) ,
1663+ ] ) ,
1664+ )
1665+ . await ;
1666+
1667+ submit_turn (
1668+ & test,
1669+ "allow-prefix-second" ,
1670+ approval_policy,
1671+ sandbox_policy. clone ( ) ,
1672+ )
1673+ . await ?;
1674+
1675+ wait_for_completion_without_approval ( & test) . await ;
1676+
1677+ let second_output = parse_result (
1678+ & second_results
1679+ . single_request ( )
1680+ . function_call_output ( call_id_second) ,
1681+ ) ;
1682+ assert_eq ! ( second_output. exit_code. unwrap_or( 0 ) , 0 ) ;
1683+ assert ! (
1684+ second_output. stdout. contains( "allow-prefix-ok" ) ,
1685+ "unexpected stdout: {}" ,
1686+ second_output. stdout
1687+ ) ;
1688+
1689+ Ok ( ( ) )
1690+ }
0 commit comments