feat: approval for sub-agent in the TUI (#12995)

<img width="766" height="290" alt="Screenshot 2026-02-27 at 10 50 48"
src="https://github.com/user-attachments/assets/3bc96cd9-ed2c-4d67-a317-8f7b60abbbb1"
/>
This commit is contained in:
jif-oai
2026-02-28 14:07:07 +01:00
committed by GitHub
parent 83177ed7a8
commit 2b38b4e03b
7 changed files with 669 additions and 87 deletions

View File

@@ -2854,7 +2854,11 @@ async fn exec_approval_uses_approval_id_when_present() {
let mut found = false;
while let Ok(app_ev) = rx.try_recv() {
if let AppEvent::CodexOp(Op::ExecApproval { id, decision, .. }) = app_ev {
if let AppEvent::SubmitThreadOp {
op: Op::ExecApproval { id, decision, .. },
..
} = app_ev
{
assert_eq!(id, "approval-subcommand");
assert_matches!(decision, codex_protocol::protocol::ReviewDecision::Approved);
found = true;
@@ -7637,10 +7641,14 @@ async fn apply_patch_approval_sends_op_with_call_id() {
// Approve via key press 'y'
chat.handle_key_event(KeyEvent::new(KeyCode::Char('y'), KeyModifiers::NONE));
// Expect a CodexOp with PatchApproval carrying the call id.
// Expect a thread-scoped PatchApproval op carrying the call id.
let mut found = false;
while let Ok(app_ev) = rx.try_recv() {
if let AppEvent::CodexOp(Op::PatchApproval { id, decision }) = app_ev {
if let AppEvent::SubmitThreadOp {
op: Op::PatchApproval { id, decision },
..
} = app_ev
{
assert_eq!(id, "call-999");
assert_matches!(decision, codex_protocol::protocol::ReviewDecision::Approved);
found = true;
@@ -7671,16 +7679,16 @@ async fn apply_patch_full_flow_integration_like() {
}),
});
// 2) User approves via 'y' and App receives a CodexOp
// 2) User approves via 'y' and App receives a thread-scoped op
chat.handle_key_event(KeyEvent::new(KeyCode::Char('y'), KeyModifiers::NONE));
let mut maybe_op: Option<Op> = None;
while let Ok(app_ev) = rx.try_recv() {
if let AppEvent::CodexOp(op) = app_ev {
if let AppEvent::SubmitThreadOp { op, .. } = app_ev {
maybe_op = Some(op);
break;
}
}
let op = maybe_op.expect("expected CodexOp after key press");
let op = maybe_op.expect("expected thread-scoped op after key press");
// 3) App forwards to widget.submit_op, which pushes onto codex_op_tx
chat.submit_op(op);