TUI: Remove core protocol dependency [3/7] (#20174)

## Why

This is part 3 of a 7-PR stack to remove direct
`codex_protocol::protocol` usage from `codex-tui` while keeping each
layer reviewable and shippable.

With `AppCommand` now explicit, the internal app event bus can carry TUI
commands directly instead of bouncing through core `Op` values.

## What changed

- Changed `AppEvent::CodexOp` and `AppEvent::SubmitThreadOp` to carry
`AppCommand`.
- Updated app-event senders and direct emitters to submit `AppCommand`
values.
- Adjusted tests to match `AppCommand` or convert back through
`into_core()` where they intentionally assert legacy payload equality.

## Verification

- `cargo test -p codex-tui --no-run`
This commit is contained in:
Eric Traut
2026-04-29 10:52:10 -07:00
committed by GitHub
Unverified
parent 445629815c
commit d0204c3dcc
19 changed files with 165 additions and 152 deletions
+3 -5
View File
@@ -351,8 +351,8 @@ impl App {
#[cfg(target_os = "windows")]
{
let windows_sandbox_level = WindowsSandboxLevel::from_config(&self.config);
self.app_event_tx.send(AppEvent::CodexOp(
AppCommand::override_turn_context(
self.app_event_tx
.send(AppEvent::CodexOp(AppCommand::override_turn_context(
/*cwd*/ None,
/*approval_policy*/ None,
/*approvals_reviewer*/ None,
@@ -365,9 +365,7 @@ impl App {
/*service_tier*/ None,
/*collaboration_mode*/ None,
/*personality*/ None,
)
.into_core(),
));
)));
}
}
+4 -7
View File
@@ -313,15 +313,14 @@ impl App {
return Ok(AppRunControl::Exit(ExitReason::Fatal(message)));
}
AppEvent::CodexOp(op) => {
self.submit_active_thread_op(app_server, op.into()).await?;
self.submit_active_thread_op(app_server, op).await?;
}
AppEvent::ApproveRecentAutoReviewDenial { thread_id, id } => {
self.chat_widget
.approve_recent_auto_review_denial(thread_id, id);
}
AppEvent::SubmitThreadOp { thread_id, op } => {
self.submit_thread_op(app_server, thread_id, op.into())
.await?;
self.submit_thread_op(app_server, thread_id, op).await?;
}
AppEvent::ThreadHistoryEntryResponse { thread_id, event } => {
self.enqueue_thread_history_entry_response(thread_id, event)
@@ -989,8 +988,7 @@ impl App {
/*service_tier*/ None,
/*collaboration_mode*/ None,
/*personality*/ None,
)
.into(),
),
));
self.app_event_tx.send(
AppEvent::OpenWorldWritableWarningConfirmation {
@@ -1015,8 +1013,7 @@ impl App {
/*service_tier*/ None,
/*collaboration_mode*/ None,
/*personality*/ None,
)
.into(),
),
));
self.app_event_tx
.send(AppEvent::UpdateAskForApprovalPolicy(preset.approval));
+3 -2
View File
@@ -6,6 +6,7 @@ use super::*;
use crate::app_backtrack::BacktrackSelection;
use crate::app_backtrack::BacktrackState;
use crate::app_backtrack::user_count;
use crate::app_command::AppCommand;
use crate::chatwidget::ChatWidgetInit;
use crate::chatwidget::create_initial_user_message;
@@ -441,12 +442,12 @@ async fn enqueue_primary_thread_session_replays_turns_before_initial_prompt_subm
}
AppEvent::SubmitThreadOp {
thread_id: op_thread_id,
op: Op::UserTurn { items, .. },
op: AppCommand::UserTurn { items, .. },
} => {
assert_eq!(op_thread_id, thread_id);
submitted_items = Some(items);
}
AppEvent::CodexOp(Op::UserTurn { items, .. }) => {
AppEvent::CodexOp(AppCommand::UserTurn { items, .. }) => {
submitted_items = Some(items);
}
_ => {}
+2 -1
View File
@@ -706,7 +706,8 @@ impl App {
Ok(true)
}
AppCommandView::OverrideTurnContext { .. } => Ok(true),
AppCommandView::Other(Op::ApproveGuardianDeniedAction { event }) => {
AppCommandView::ApproveGuardianDeniedAction { event }
| AppCommandView::Other(Op::ApproveGuardianDeniedAction { event }) => {
app_server
.thread_approve_guardian_denied_action(thread_id, event)
.await?;
+28
View File
@@ -2,6 +2,7 @@ use std::path::PathBuf;
use codex_config::types::ApprovalsReviewer;
use codex_protocol::approvals::ElicitationAction;
use codex_protocol::approvals::GuardianAssessmentEvent;
use codex_protocol::config_types::CollaborationMode;
use codex_protocol::config_types::Personality;
use codex_protocol::config_types::ReasoningSummary as ReasoningSummaryConfig;
@@ -104,6 +105,9 @@ pub(crate) enum AppCommand {
Review {
review_request: ReviewRequest,
},
ApproveGuardianDeniedAction {
event: GuardianAssessmentEvent,
},
Other(Op),
}
@@ -186,6 +190,9 @@ pub(crate) enum AppCommandView<'a> {
Review {
review_request: &'a ReviewRequest,
},
ApproveGuardianDeniedAction {
event: &'a GuardianAssessmentEvent,
},
Other(&'a Op),
}
@@ -450,6 +457,9 @@ impl AppCommand {
Self::Shutdown => Op::Shutdown,
Self::ThreadRollback { num_turns } => Op::ThreadRollback { num_turns },
Self::Review { review_request } => Op::Review { review_request },
Self::ApproveGuardianDeniedAction { event } => {
Op::ApproveGuardianDeniedAction { event }
}
Self::Other(op) => op,
}
}
@@ -568,6 +578,9 @@ impl AppCommand {
num_turns: *num_turns,
},
Self::Review { review_request } => AppCommandView::Review { review_request },
Self::ApproveGuardianDeniedAction { event } => {
AppCommandView::ApproveGuardianDeniedAction { event }
}
Self::Other(op) => AppCommandView::Other(op),
}
}
@@ -716,11 +729,26 @@ impl From<Op> for AppCommand {
Op::Shutdown => Self::Shutdown,
Op::ThreadRollback { num_turns } => Self::ThreadRollback { num_turns },
Op::Review { review_request } => Self::Review { review_request },
Op::ApproveGuardianDeniedAction { event } => {
Self::ApproveGuardianDeniedAction { event }
}
op => Self::Other(op),
}
}
}
impl PartialEq<Op> for AppCommand {
fn eq(&self, other: &Op) -> bool {
self.clone().into_core() == *other
}
}
impl PartialEq<AppCommand> for Op {
fn eq(&self, other: &AppCommand) -> bool {
*self == other.clone().into_core()
}
}
impl From<&Op> for AppCommand {
fn from(value: &Op) -> Self {
Self::from(value.clone())
+3 -3
View File
@@ -27,11 +27,11 @@ use codex_file_search::FileMatch;
use codex_protocol::ThreadId;
use codex_protocol::openai_models::ModelPreset;
use codex_protocol::protocol::GetHistoryEntryResponseEvent;
use codex_protocol::protocol::Op;
use codex_protocol::protocol::RateLimitSnapshot;
use codex_utils_absolute_path::AbsolutePathBuf;
use codex_utils_approval_presets::ApprovalPreset;
use crate::app_command::AppCommand;
use crate::bottom_pane::ApprovalRequest;
use crate::bottom_pane::StatusLineItem;
use crate::bottom_pane::TerminalTitleItem;
@@ -132,7 +132,7 @@ pub(crate) enum AppEvent {
/// Submit an op to the specified thread, regardless of current focus.
SubmitThreadOp {
thread_id: ThreadId,
op: Op,
op: AppCommand,
},
/// Deliver a synthetic history lookup response to a specific thread channel.
@@ -182,7 +182,7 @@ pub(crate) enum AppEvent {
/// Forward an `Op` to the Agent. Using an `AppEvent` for this avoids
/// bubbling channels through layers of widgets.
CodexOp(Op),
CodexOp(AppCommand),
/// Approve one retry of a recent auto-review denial selected in the TUI.
ApproveRecentAutoReviewDenial {
+18 -22
View File
@@ -38,48 +38,45 @@ impl AppEventSender {
}
pub(crate) fn interrupt(&self) {
self.send(AppEvent::CodexOp(AppCommand::interrupt().into_core()));
self.send(AppEvent::CodexOp(AppCommand::interrupt()));
}
pub(crate) fn compact(&self) {
self.send(AppEvent::CodexOp(AppCommand::compact().into_core()));
self.send(AppEvent::CodexOp(AppCommand::compact()));
}
pub(crate) fn set_thread_name(&self, name: String) {
self.send(AppEvent::CodexOp(
AppCommand::set_thread_name(name).into_core(),
));
self.send(AppEvent::CodexOp(AppCommand::set_thread_name(name)));
}
pub(crate) fn review(&self, review_request: ReviewRequest) {
self.send(AppEvent::CodexOp(
AppCommand::review(review_request).into_core(),
));
self.send(AppEvent::CodexOp(AppCommand::review(review_request)));
}
pub(crate) fn list_skills(&self, cwds: Vec<PathBuf>, force_reload: bool) {
self.send(AppEvent::CodexOp(
AppCommand::list_skills(cwds, force_reload).into_core(),
));
self.send(AppEvent::CodexOp(AppCommand::list_skills(
cwds,
force_reload,
)));
}
#[cfg_attr(target_os = "linux", allow(dead_code))]
pub(crate) fn realtime_conversation_audio(&self, params: ConversationAudioParams) {
self.send(AppEvent::CodexOp(
AppCommand::realtime_conversation_audio(params).into_core(),
));
self.send(AppEvent::CodexOp(AppCommand::realtime_conversation_audio(
params,
)));
}
pub(crate) fn user_input_answer(&self, id: String, response: RequestUserInputResponse) {
self.send(AppEvent::CodexOp(
AppCommand::user_input_answer(id, response).into_core(),
));
self.send(AppEvent::CodexOp(AppCommand::user_input_answer(
id, response,
)));
}
pub(crate) fn exec_approval(&self, thread_id: ThreadId, id: String, decision: ReviewDecision) {
self.send(AppEvent::SubmitThreadOp {
thread_id,
op: AppCommand::exec_approval(id, /*turn_id*/ None, decision).into_core(),
op: AppCommand::exec_approval(id, /*turn_id*/ None, decision),
});
}
@@ -91,14 +88,14 @@ impl AppEventSender {
) {
self.send(AppEvent::SubmitThreadOp {
thread_id,
op: AppCommand::request_permissions_response(id, response).into_core(),
op: AppCommand::request_permissions_response(id, response),
});
}
pub(crate) fn patch_approval(&self, thread_id: ThreadId, id: String, decision: ReviewDecision) {
self.send(AppEvent::SubmitThreadOp {
thread_id,
op: AppCommand::patch_approval(id, decision).into_core(),
op: AppCommand::patch_approval(id, decision),
});
}
@@ -113,8 +110,7 @@ impl AppEventSender {
) {
self.send(AppEvent::SubmitThreadOp {
thread_id,
op: AppCommand::resolve_elicitation(server_name, request_id, decision, content, meta)
.into_core(),
op: AppCommand::resolve_elicitation(server_name, request_id, decision, content, meta),
});
}
}
@@ -46,8 +46,6 @@ use codex_protocol::protocol::ElicitationAction;
use codex_protocol::protocol::FileChange;
use codex_protocol::protocol::NetworkApprovalContext;
use codex_protocol::protocol::NetworkPolicyRuleAction;
#[cfg(test)]
use codex_protocol::protocol::Op;
use codex_protocol::protocol::ReviewDecision;
use codex_protocol::request_permissions::PermissionGrantScope;
use codex_protocol::request_permissions::RequestPermissionProfile;
@@ -1017,6 +1015,7 @@ fn elicitation_options(keymap: &ApprovalKeymap) -> Vec<ApprovalOption> {
#[cfg(test)]
mod tests {
use super::*;
use crate::app_command::AppCommand;
use crate::app_event::AppEvent;
use codex_protocol::models::FileSystemPermissions;
use codex_protocol::models::NetworkPermissions;
@@ -1166,7 +1165,7 @@ mod tests {
let mut decision = None;
while let Ok(ev) = rx.try_recv() {
if let AppEvent::SubmitThreadOp {
op: Op::ExecApproval { decision: d, .. },
op: AppCommand::ExecApproval { decision: d, .. },
..
} = ev
{
@@ -1197,7 +1196,7 @@ mod tests {
let mut decision = None;
while let Ok(ev) = rx.try_recv() {
if let AppEvent::SubmitThreadOp {
op: Op::ResolveElicitation { decision: d, .. },
op: AppCommand::ResolveElicitation { decision: d, .. },
..
} = ev
{
@@ -1250,7 +1249,7 @@ mod tests {
let mut saw_denied = false;
while let Ok(ev) = rx.try_recv() {
if let AppEvent::SubmitThreadOp {
op: Op::ExecApproval { decision, .. },
op: AppCommand::ExecApproval { decision, .. },
..
} = ev
{
@@ -1298,7 +1297,7 @@ mod tests {
let mut saw_deny = false;
while let Ok(ev) = rx.try_recv() {
if let AppEvent::SubmitThreadOp {
op: Op::ExecApproval { decision, .. },
op: AppCommand::ExecApproval { decision, .. },
..
} = ev
{
@@ -1461,7 +1460,7 @@ mod tests {
let mut saw_op = false;
while let Ok(ev) = rx.try_recv() {
if let AppEvent::SubmitThreadOp {
op: Op::ExecApproval { decision, .. },
op: AppCommand::ExecApproval { decision, .. },
..
} = ev
{
@@ -1706,7 +1705,7 @@ mod tests {
let mut saw_op = false;
while let Ok(ev) = rx.try_recv() {
if let AppEvent::SubmitThreadOp {
op: Op::RequestPermissionsResponse { response, .. },
op: AppCommand::RequestPermissionsResponse { response, .. },
..
} = ev
{
@@ -1741,7 +1740,7 @@ mod tests {
let mut saw_op = false;
while let Ok(ev) = rx.try_recv() {
if let AppEvent::SubmitThreadOp {
op: Op::RequestPermissionsResponse { response, .. },
op: AppCommand::RequestPermissionsResponse { response, .. },
..
} = ev
{
@@ -1769,7 +1768,7 @@ mod tests {
let mut saw_op = false;
while let Ok(ev) = rx.try_recv() {
if let AppEvent::SubmitThreadOp {
op: Op::RequestPermissionsResponse { response, .. },
op: AppCommand::RequestPermissionsResponse { response, .. },
..
} = ev
{
@@ -2032,7 +2031,7 @@ mod tests {
let mut decision = None;
while let Ok(ev) = rx.try_recv() {
if let AppEvent::SubmitThreadOp {
op: Op::ResolveElicitation { decision: d, .. },
op: AppCommand::ResolveElicitation { decision: d, .. },
..
} = ev
{
@@ -2066,7 +2065,7 @@ mod tests {
let mut esc_decision = None;
while let Ok(ev) = rx.try_recv() {
if let AppEvent::SubmitThreadOp {
op: Op::ResolveElicitation { decision, .. },
op: AppCommand::ResolveElicitation { decision, .. },
..
} = ev
{
@@ -2096,7 +2095,7 @@ mod tests {
let mut n_decision = None;
while let Ok(ev) = rx.try_recv() {
if let AppEvent::SubmitThreadOp {
op: Op::ResolveElicitation { decision, .. },
op: AppCommand::ResolveElicitation { decision, .. },
..
} = ev
{
@@ -2122,7 +2121,7 @@ mod tests {
let mut decision = None;
while let Ok(ev) = rx.try_recv() {
if let AppEvent::SubmitThreadOp {
op: Op::ExecApproval { decision: d, .. },
op: AppCommand::ExecApproval { decision: d, .. },
..
} = ev
{
@@ -15,6 +15,7 @@ use std::collections::HashMap;
use std::collections::HashSet;
use std::path::PathBuf;
use crate::app_command::AppCommand;
use crate::app_event::AppEvent;
use crate::app_event_sender::AppEventSender;
use crate::bottom_pane::MentionBinding;
@@ -598,10 +599,9 @@ impl ChatComposerHistory {
boundary_if_exhausted,
});
}
app_event_tx.send(AppEvent::CodexOp(Op::GetHistoryEntryRequest {
offset,
log_id,
}));
app_event_tx.send(AppEvent::CodexOp(AppCommand::from(
Op::GetHistoryEntryRequest { offset, log_id },
)));
return HistorySearchResult::Pending;
}
@@ -719,10 +719,12 @@ impl ChatComposerHistory {
self.last_history_text = Some(entry.text.clone());
return Some(entry);
} else if let Some(log_id) = self.history_log_id {
app_event_tx.send(AppEvent::CodexOp(Op::GetHistoryEntryRequest {
offset: global_idx,
log_id,
}));
app_event_tx.send(AppEvent::CodexOp(AppCommand::from(
Op::GetHistoryEntryRequest {
offset: global_idx,
log_id,
},
)));
}
None
}
+7 -7
View File
@@ -1567,12 +1567,12 @@ impl Renderable for BottomPane {
mod tests {
use super::*;
use crate::app::app_server_requests::ResolvedAppServerRequest;
use crate::app_command::AppCommand;
use crate::app_event::AppEvent;
use crate::status_indicator_widget::STATUS_DETAILS_DEFAULT_MAX_LINES;
use crate::status_indicator_widget::StatusDetailsCapitalization;
use crate::test_support::PathBufExt;
use crate::test_support::test_path_buf;
use codex_protocol::protocol::Op;
use codex_protocol::protocol::ReviewDecision;
use codex_protocol::protocol::SkillScope;
use crossterm::event::KeyCode;
@@ -1890,7 +1890,7 @@ mod tests {
let mut approval_decision = None;
while let Ok(event) = rx.try_recv() {
if let AppEvent::SubmitThreadOp {
op: Op::ExecApproval { decision, .. },
op: AppCommand::ExecApproval { decision, .. },
..
} = event
{
@@ -2371,7 +2371,7 @@ mod tests {
while let Ok(ev) = rx.try_recv() {
assert!(
!matches!(ev, AppEvent::CodexOp(Op::Interrupt)),
!matches!(ev, AppEvent::CodexOp(AppCommand::Interrupt)),
"expected Esc to not send Op::Interrupt when dismissing skill popup"
);
}
@@ -2409,7 +2409,7 @@ mod tests {
while let Ok(ev) = rx.try_recv() {
assert!(
!matches!(ev, AppEvent::CodexOp(Op::Interrupt)),
!matches!(ev, AppEvent::CodexOp(AppCommand::Interrupt)),
"expected Esc to not send Op::Interrupt while command popup is active"
);
}
@@ -2445,7 +2445,7 @@ mod tests {
while let Ok(ev) = rx.try_recv() {
assert!(
!matches!(ev, AppEvent::CodexOp(Op::Interrupt)),
!matches!(ev, AppEvent::CodexOp(AppCommand::Interrupt)),
"expected Esc to not send Op::Interrupt while typing `/agent`"
);
}
@@ -2490,7 +2490,7 @@ mod tests {
while let Ok(ev) = rx.try_recv() {
assert!(
!matches!(ev, AppEvent::CodexOp(Op::Interrupt)),
!matches!(ev, AppEvent::CodexOp(AppCommand::Interrupt)),
"expected Esc release after dismissing agent picker to not interrupt"
);
}
@@ -2520,7 +2520,7 @@ mod tests {
pane.handle_key_event(KeyEvent::new(KeyCode::Esc, KeyModifiers::NONE));
assert!(
matches!(rx.try_recv(), Ok(AppEvent::CodexOp(Op::Interrupt))),
matches!(rx.try_recv(), Ok(AppEvent::CodexOp(AppCommand::Interrupt))),
"expected Esc to send Op::Interrupt while a task is running"
);
}
@@ -1308,6 +1308,7 @@ impl BottomPaneView for RequestUserInputOverlay {
#[cfg(test)]
mod tests {
use super::*;
use crate::app_command::AppCommand;
use crate::app_event::AppEvent;
use crate::bottom_pane::selection_popup_common::menu_surface_inset;
use crate::render::renderable::Renderable;
@@ -1688,7 +1689,7 @@ mod tests {
overlay.submit_answers();
let event = rx.try_recv().expect("expected AppEvent");
let AppEvent::CodexOp(Op::UserInputAnswer { id, response, .. }) = event else {
let AppEvent::CodexOp(AppCommand::UserInputAnswer { id, response, .. }) = event else {
panic!("expected UserInputAnswer");
};
assert_eq!(id, "turn-1");
@@ -1710,7 +1711,7 @@ mod tests {
overlay.handle_key_event(KeyEvent::from(KeyCode::Enter));
let event = rx.try_recv().expect("expected AppEvent");
let AppEvent::CodexOp(Op::UserInputAnswer { response, .. }) = event else {
let AppEvent::CodexOp(AppCommand::UserInputAnswer { response, .. }) = event else {
panic!("expected UserInputAnswer");
};
let answer = response.answers.get("q1").expect("answer missing");
@@ -1746,7 +1747,7 @@ mod tests {
overlay.handle_key_event(KeyEvent::from(KeyCode::Enter));
let event = rx.try_recv().expect("expected AppEvent");
let AppEvent::CodexOp(Op::UserInputAnswer { response, .. }) = event else {
let AppEvent::CodexOp(AppCommand::UserInputAnswer { response, .. }) = event else {
panic!("expected UserInputAnswer");
};
let mut expected = HashMap::new();
@@ -1779,7 +1780,7 @@ mod tests {
overlay.handle_key_event(KeyEvent::from(KeyCode::Char('2')));
let event = rx.try_recv().expect("expected AppEvent");
let AppEvent::CodexOp(Op::UserInputAnswer { response, .. }) = event else {
let AppEvent::CodexOp(AppCommand::UserInputAnswer { response, .. }) = event else {
panic!("expected UserInputAnswer");
};
let answer = response.answers.get("q1").expect("answer missing");
@@ -2029,7 +2030,7 @@ mod tests {
overlay.handle_key_event(KeyEvent::from(KeyCode::Enter));
let event = rx.try_recv().expect("expected AppEvent");
let AppEvent::CodexOp(Op::UserInputAnswer { response, .. }) = event else {
let AppEvent::CodexOp(AppCommand::UserInputAnswer { response, .. }) = event else {
panic!("expected UserInputAnswer");
};
let answer = response.answers.get("q1").expect("answer missing");
@@ -2329,7 +2330,7 @@ mod tests {
overlay.submit_answers();
let event = rx.try_recv().expect("expected AppEvent");
let AppEvent::CodexOp(Op::UserInputAnswer { response, .. }) = event else {
let AppEvent::CodexOp(AppCommand::UserInputAnswer { response, .. }) = event else {
panic!("expected UserInputAnswer");
};
let answer = response.answers.get("q1").expect("answer missing");
@@ -2354,7 +2355,7 @@ mod tests {
overlay.submit_answers();
let event = rx.try_recv().expect("expected AppEvent");
let AppEvent::CodexOp(Op::UserInputAnswer { response, .. }) = event else {
let AppEvent::CodexOp(AppCommand::UserInputAnswer { response, .. }) = event else {
panic!("expected UserInputAnswer");
};
let answer = response.answers.get("q1").expect("answer missing");
@@ -2397,7 +2398,7 @@ mod tests {
overlay.submit_answers();
let event = rx.try_recv().expect("expected AppEvent");
let AppEvent::CodexOp(Op::UserInputAnswer { response, .. }) = event else {
let AppEvent::CodexOp(AppCommand::UserInputAnswer { response, .. }) = event else {
panic!("expected UserInputAnswer");
};
let answer = response.answers.get("q1").expect("answer missing");
@@ -2433,7 +2434,7 @@ mod tests {
overlay.submit_answers();
let event = rx.try_recv().expect("expected AppEvent");
let AppEvent::CodexOp(Op::UserInputAnswer { response, .. }) = event else {
let AppEvent::CodexOp(AppCommand::UserInputAnswer { response, .. }) = event else {
panic!("expected UserInputAnswer");
};
let answer = response.answers.get("q1").expect("answer missing");
@@ -2518,7 +2519,7 @@ mod tests {
overlay.submit_answers();
let event = rx.try_recv().expect("expected AppEvent");
let AppEvent::CodexOp(Op::UserInputAnswer { response, .. }) = event else {
let AppEvent::CodexOp(AppCommand::UserInputAnswer { response, .. }) = event else {
panic!("expected UserInputAnswer");
};
let answer = response.answers.get("q1").expect("answer missing");
+44 -55
View File
@@ -8313,22 +8313,19 @@ impl ChatWidget {
let default_effort: ReasoningEffortConfig = preset.default_reasoning_effort;
let switch_actions: Vec<SelectionAction> = vec![Box::new(move |tx| {
tx.send(AppEvent::CodexOp(
AppCommand::override_turn_context(
/*cwd*/ None,
/*approval_policy*/ None,
/*approvals_reviewer*/ None,
/*permission_profile*/ None,
/*windows_sandbox_level*/ None,
Some(switch_model_for_events.clone()),
Some(Some(default_effort)),
/*summary*/ None,
/*service_tier*/ None,
/*collaboration_mode*/ None,
/*personality*/ None,
)
.into_core(),
));
tx.send(AppEvent::CodexOp(AppCommand::override_turn_context(
/*cwd*/ None,
/*approval_policy*/ None,
/*approvals_reviewer*/ None,
/*permission_profile*/ None,
/*windows_sandbox_level*/ None,
Some(switch_model_for_events.clone()),
Some(Some(default_effort)),
/*summary*/ None,
/*service_tier*/ None,
/*collaboration_mode*/ None,
/*personality*/ None,
)));
tx.send(AppEvent::UpdateModel(switch_model_for_events.clone()));
tx.send(AppEvent::UpdateReasoningEffort(Some(default_effort)));
})];
@@ -8537,22 +8534,19 @@ impl ChatWidget {
let name = Self::personality_label(personality).to_string();
let description = Some(Self::personality_description(personality).to_string());
let actions: Vec<SelectionAction> = vec![Box::new(move |tx| {
tx.send(AppEvent::CodexOp(
AppCommand::override_turn_context(
/*cwd*/ None,
/*approval_policy*/ None,
/*approvals_reviewer*/ None,
/*permission_profile*/ None,
/*windows_sandbox_level*/ None,
/*model*/ None,
/*effort*/ None,
/*summary*/ None,
/*service_tier*/ None,
/*collaboration_mode*/ None,
Some(personality),
)
.into_core(),
));
tx.send(AppEvent::CodexOp(AppCommand::override_turn_context(
/*cwd*/ None,
/*approval_policy*/ None,
/*approvals_reviewer*/ None,
/*permission_profile*/ None,
/*windows_sandbox_level*/ None,
/*model*/ None,
/*effort*/ None,
/*summary*/ None,
/*service_tier*/ None,
/*collaboration_mode*/ None,
Some(personality),
)));
tx.send(AppEvent::UpdatePersonality(personality));
tx.send(AppEvent::PersistPersonalitySelection { personality });
})];
@@ -9567,7 +9561,7 @@ impl ChatWidget {
self.app_event_tx.send(AppEvent::SubmitThreadOp {
thread_id,
op: Op::ApproveGuardianDeniedAction { event },
op: AppCommand::from(Op::ApproveGuardianDeniedAction { event }),
});
self.add_info_message(
"Approval recorded for one retry of the selected auto-review denial.".to_string(),
@@ -9605,22 +9599,19 @@ impl ChatWidget {
) -> Vec<SelectionAction> {
vec![Box::new(move |tx| {
let permission_profile_clone = permission_profile.clone();
tx.send(AppEvent::CodexOp(
AppCommand::override_turn_context(
/*cwd*/ None,
Some(approval),
Some(approvals_reviewer),
Some(permission_profile_clone.clone()),
/*windows_sandbox_level*/ None,
/*model*/ None,
/*effort*/ None,
/*summary*/ None,
/*service_tier*/ None,
/*collaboration_mode*/ None,
/*personality*/ None,
)
.into_core(),
));
tx.send(AppEvent::CodexOp(AppCommand::override_turn_context(
/*cwd*/ None,
Some(approval),
Some(approvals_reviewer),
Some(permission_profile_clone.clone()),
/*windows_sandbox_level*/ None,
/*model*/ None,
/*effort*/ None,
/*summary*/ None,
/*service_tier*/ None,
/*collaboration_mode*/ None,
/*personality*/ None,
)));
tx.send(AppEvent::UpdateAskForApprovalPolicy(approval));
tx.send(AppEvent::UpdatePermissionProfile(permission_profile_clone));
tx.send(AppEvent::UpdateApprovalsReviewer(approvals_reviewer));
@@ -10457,8 +10448,8 @@ impl ChatWidget {
self.config.notices.fast_default_opt_out = Some(true);
}
self.set_service_tier(service_tier);
self.app_event_tx.send(AppEvent::CodexOp(
AppCommand::override_turn_context(
self.app_event_tx
.send(AppEvent::CodexOp(AppCommand::override_turn_context(
/*cwd*/ None,
/*approval_policy*/ None,
/*approvals_reviewer*/ None,
@@ -10470,9 +10461,7 @@ impl ChatWidget {
Some(service_tier),
/*collaboration_mode*/ None,
/*personality*/ None,
)
.into_core(),
));
)));
self.app_event_tx
.send(AppEvent::PersistServiceTierSelection { service_tier });
}
@@ -11539,7 +11528,7 @@ impl ChatWidget {
}
}
CodexOpTarget::AppEvent => {
self.app_event_tx.send(AppEvent::CodexOp(op.into()));
self.app_event_tx.send(AppEvent::CodexOp(op));
}
}
true
+1
View File
@@ -5,6 +5,7 @@
//! changes show up as stable, reviewable diffs.
pub(super) use super::*;
pub(super) use crate::app_command::AppCommand;
pub(super) use crate::app_event::AppEvent;
pub(super) use crate::app_event::ExitMode;
#[cfg(not(target_os = "linux"))]
@@ -217,7 +217,7 @@ async fn exec_approval_uses_approval_id_when_present() {
let mut found = false;
while let Ok(app_ev) = rx.try_recv() {
if let AppEvent::SubmitThreadOp {
op: Op::ExecApproval { id, decision, .. },
op: AppCommand::ExecApproval { id, decision, .. },
..
} = app_ev
{
@@ -114,7 +114,7 @@ async fn exec_approval_uses_approval_id_when_present() {
let mut found = false;
while let Ok(app_ev) = rx.try_recv() {
if let AppEvent::SubmitThreadOp {
op: Op::ExecApproval { id, decision, .. },
op: AppCommand::ExecApproval { id, decision, .. },
..
} = app_ev
{
@@ -1712,7 +1712,7 @@ async fn apply_patch_approval_sends_op_with_call_id() {
let mut found = false;
while let Ok(app_ev) = rx.try_recv() {
if let AppEvent::SubmitThreadOp {
op: Op::PatchApproval { id, decision },
op: AppCommand::PatchApproval { id, decision },
..
} = app_ev
{
@@ -1751,7 +1751,7 @@ async fn apply_patch_full_flow_integration_like() {
let mut maybe_op: Option<Op> = None;
while let Ok(app_ev) = rx.try_recv() {
if let AppEvent::SubmitThreadOp { op, .. } = app_ev {
maybe_op = Some(op);
maybe_op = Some(op.into_core());
break;
}
}
@@ -52,7 +52,7 @@ async fn approving_recent_denial_emits_structured_core_op_once() {
rx.try_recv(),
Ok(AppEvent::SubmitThreadOp {
thread_id: submitted_thread_id,
op: Op::ApproveGuardianDeniedAction { event }
op: AppCommand::ApproveGuardianDeniedAction { event }
}) if submitted_thread_id == thread_id
&& event.id == "auto-review-recent-1"
&& event.status == GuardianAssessmentStatus::Denied
@@ -315,7 +315,7 @@ async fn approvals_popup_navigation_skips_disabled() {
assert!(
app_events.iter().any(|ev| matches!(
ev,
AppEvent::CodexOp(Op::OverrideTurnContext {
AppEvent::CodexOp(AppCommand::OverrideTurnContext {
approval_policy: Some(AskForApproval::OnRequest),
personality: None,
..
@@ -326,7 +326,7 @@ async fn approvals_popup_navigation_skips_disabled() {
assert!(
!app_events.iter().any(|ev| matches!(
ev,
AppEvent::CodexOp(Op::OverrideTurnContext {
AppEvent::CodexOp(AppCommand::OverrideTurnContext {
approval_policy: Some(AskForApproval::Never),
personality: None,
..
@@ -697,7 +697,7 @@ async fn permissions_selection_sends_approvals_reviewer_in_override_turn_context
let op = std::iter::from_fn(|| rx.try_recv().ok())
.find_map(|event| match event {
AppEvent::CodexOp(op @ Op::OverrideTurnContext { .. }) => Some(op),
AppEvent::CodexOp(op @ AppCommand::OverrideTurnContext { .. }) => Some(op),
_ => None,
})
.expect("expected OverrideTurnContext op");
@@ -1221,10 +1221,10 @@ async fn custom_prompt_submit_sends_review_op() {
chat.handle_paste(" please audit dependencies ".to_string());
chat.handle_key_event(KeyEvent::new(KeyCode::Enter, KeyModifiers::NONE));
// Expect AppEvent::CodexOp(Op::Review { .. }) with trimmed prompt
// Expect AppEvent::CodexOp(AppCommand::Review { .. }) with trimmed prompt
let evt = rx.try_recv().expect("expected one app event");
match evt {
AppEvent::CodexOp(Op::Review { review_request }) => {
AppEvent::CodexOp(AppCommand::Review { review_request }) => {
assert_eq!(
review_request,
ReviewRequest {
@@ -51,7 +51,7 @@ async fn slash_compact_eagerly_queues_follow_up_before_turn_start() {
assert!(chat.bottom_pane.is_task_running());
match rx.try_recv() {
Ok(AppEvent::CodexOp(Op::Compact)) => {}
Ok(AppEvent::CodexOp(AppCommand::Compact)) => {}
other => panic!("expected compact op to be submitted, got {other:?}"),
}
@@ -103,7 +103,7 @@ async fn queued_slash_compact_dispatches_after_active_turn() {
assert!(
events
.iter()
.any(|event| matches!(event, AppEvent::CodexOp(Op::Compact))),
.any(|event| matches!(event, AppEvent::CodexOp(AppCommand::Compact))),
"expected queued /compact to submit compact op; events: {events:?}"
);
}
@@ -446,7 +446,7 @@ async fn queued_bare_rename_drains_next_input_after_name_update() {
assert!(
events.iter().any(|event| matches!(
event,
AppEvent::CodexOp(Op::SetThreadName { name }) if name == "Queued rename"
AppEvent::CodexOp(AppCommand::SetThreadName { name }) if name == "Queued rename"
)),
"expected rename prompt to submit thread name; events: {events:?}"
);
@@ -500,7 +500,7 @@ async fn queued_inline_rename_does_not_drain_again_before_turn_started() {
assert!(
events.iter().any(|event| matches!(
event,
AppEvent::CodexOp(Op::SetThreadName { name }) if name == "Queued rename"
AppEvent::CodexOp(AppCommand::SetThreadName { name }) if name == "Queued rename"
)),
"expected queued /rename to submit thread name; events: {events:?}"
);
@@ -1137,7 +1137,7 @@ async fn slash_rename_prefills_existing_thread_name() {
assert_matches!(
rx.try_recv(),
Ok(AppEvent::CodexOp(Op::SetThreadName { name })) if name == "Current project title"
Ok(AppEvent::CodexOp(AppCommand::SetThreadName { name })) if name == "Current project title"
);
}
@@ -2056,7 +2056,7 @@ async fn fast_slash_command_updates_and_persists_local_service_tier() {
assert!(
events.iter().any(|event| matches!(
event,
AppEvent::CodexOp(Op::OverrideTurnContext {
AppEvent::CodexOp(AppCommand::OverrideTurnContext {
service_tier: Some(Some(ServiceTier::Fast)),
..
})
@@ -2128,7 +2128,7 @@ async fn queued_fast_slash_applies_before_next_queued_message() {
assert!(
events.iter().any(|event| matches!(
event,
AppEvent::CodexOp(Op::OverrideTurnContext {
AppEvent::CodexOp(AppCommand::OverrideTurnContext {
service_tier: Some(Some(ServiceTier::Fast)),
..
})
@@ -2167,7 +2167,7 @@ async fn user_turn_sends_standard_override_after_fast_is_turned_off() {
assert!(
events.iter().any(|event| matches!(
event,
AppEvent::CodexOp(Op::OverrideTurnContext {
AppEvent::CodexOp(AppCommand::OverrideTurnContext {
service_tier: Some(None),
..
})