mirror of
https://github.com/openai/codex.git
synced 2026-05-24 13:04:29 +00:00
## Why This stack moves `codex-tui` away from the core protocol event surface and toward app-server API shapes plus TUI-owned local models. This first PR sets up the lower-risk foundation: it introduces the local model surface and extracts app-server event routing into focused TUI modules while preserving the existing behavior for the larger migration in PR2. This PR is part 1 of a 2-PR stack: 1. Add TUI-owned replacement models and extract app-server event routing. 2. Move the active TUI flow to app-server notifications and delete obsolete adapter code. ## What changed - Added TUI-owned approval, diff, session state, session resume, token usage, and user-message models. - Added `app/app_server_event_targets.rs` and `app/app_server_events.rs` to hold app-server event targeting and dispatch logic outside `app.rs`. - Updated app/status tests to use the local model layer and added focused routing coverage. - Boxed a few large async TUI test futures so this base layer remains checkable without overflowing the default test stack. ## Verification - `cargo check -p codex-tui --tests`
120 lines
4.9 KiB
Rust
120 lines
4.9 KiB
Rust
//! TUI-owned approval request models used while rendering and queueing prompts.
|
|
//!
|
|
//! These structs normalize app-server request params into the shape the TUI
|
|
//! needs while an approval may be deferred behind streaming output. Exec
|
|
//! approvals keep app-server decision and permission types; patch approvals add
|
|
//! the file-change display model collected from nearby thread items.
|
|
|
|
use std::collections::HashMap;
|
|
use std::path::PathBuf;
|
|
|
|
use crate::diff_model::FileChange;
|
|
use codex_app_server_protocol::AdditionalPermissionProfile;
|
|
use codex_app_server_protocol::CommandExecutionApprovalDecision;
|
|
use codex_app_server_protocol::ExecPolicyAmendment;
|
|
use codex_app_server_protocol::NetworkApprovalContext;
|
|
use codex_app_server_protocol::NetworkPolicyAmendment;
|
|
use codex_app_server_protocol::NetworkPolicyRuleAction;
|
|
use codex_utils_absolute_path::AbsolutePathBuf;
|
|
use serde::Deserialize;
|
|
use serde::Serialize;
|
|
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
pub(crate) struct ExecApprovalRequestEvent {
|
|
pub(crate) call_id: String,
|
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
|
pub(crate) approval_id: Option<String>,
|
|
#[serde(default)]
|
|
pub(crate) turn_id: String,
|
|
pub(crate) command: Vec<String>,
|
|
pub(crate) cwd: AbsolutePathBuf,
|
|
pub(crate) reason: Option<String>,
|
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
|
pub(crate) proposed_execpolicy_amendment: Option<ExecPolicyAmendment>,
|
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
|
pub(crate) proposed_network_policy_amendments: Option<Vec<NetworkPolicyAmendment>>,
|
|
#[serde(default)]
|
|
pub(crate) available_decisions: Option<Vec<CommandExecutionApprovalDecision>>,
|
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
|
pub(crate) network_approval_context: Option<NetworkApprovalContext>,
|
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
|
pub(crate) additional_permissions: Option<AdditionalPermissionProfile>,
|
|
}
|
|
|
|
impl ExecApprovalRequestEvent {
|
|
pub(crate) fn effective_approval_id(&self) -> String {
|
|
self.approval_id
|
|
.clone()
|
|
.unwrap_or_else(|| self.call_id.clone())
|
|
}
|
|
|
|
pub(crate) fn effective_available_decisions(&self) -> Vec<CommandExecutionApprovalDecision> {
|
|
match &self.available_decisions {
|
|
Some(decisions) => decisions.clone(),
|
|
None => Self::default_available_decisions(
|
|
self.network_approval_context.as_ref(),
|
|
self.proposed_execpolicy_amendment.as_ref(),
|
|
self.proposed_network_policy_amendments.as_deref(),
|
|
self.additional_permissions.as_ref(),
|
|
),
|
|
}
|
|
}
|
|
|
|
pub(crate) fn default_available_decisions(
|
|
network_approval_context: Option<&NetworkApprovalContext>,
|
|
proposed_execpolicy_amendment: Option<&ExecPolicyAmendment>,
|
|
proposed_network_policy_amendments: Option<&[NetworkPolicyAmendment]>,
|
|
additional_permissions: Option<&AdditionalPermissionProfile>,
|
|
) -> Vec<CommandExecutionApprovalDecision> {
|
|
if network_approval_context.is_some() {
|
|
let mut decisions = vec![
|
|
CommandExecutionApprovalDecision::Accept,
|
|
CommandExecutionApprovalDecision::AcceptForSession,
|
|
];
|
|
if let Some(amendment) = proposed_network_policy_amendments.and_then(|amendments| {
|
|
amendments
|
|
.iter()
|
|
.find(|amendment| amendment.action == NetworkPolicyRuleAction::Allow)
|
|
}) {
|
|
decisions.push(
|
|
CommandExecutionApprovalDecision::ApplyNetworkPolicyAmendment {
|
|
network_policy_amendment: amendment.clone(),
|
|
},
|
|
);
|
|
}
|
|
decisions.push(CommandExecutionApprovalDecision::Cancel);
|
|
return decisions;
|
|
}
|
|
|
|
if additional_permissions.is_some() {
|
|
return vec![
|
|
CommandExecutionApprovalDecision::Accept,
|
|
CommandExecutionApprovalDecision::Cancel,
|
|
];
|
|
}
|
|
|
|
let mut decisions = vec![CommandExecutionApprovalDecision::Accept];
|
|
if let Some(prefix) = proposed_execpolicy_amendment {
|
|
decisions.push(
|
|
CommandExecutionApprovalDecision::AcceptWithExecpolicyAmendment {
|
|
execpolicy_amendment: prefix.clone(),
|
|
},
|
|
);
|
|
}
|
|
decisions.push(CommandExecutionApprovalDecision::Cancel);
|
|
decisions
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
pub(crate) struct ApplyPatchApprovalRequestEvent {
|
|
pub(crate) call_id: String,
|
|
#[serde(default)]
|
|
pub(crate) turn_id: String,
|
|
pub(crate) changes: HashMap<PathBuf, FileChange>,
|
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
|
pub(crate) reason: Option<String>,
|
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
|
pub(crate) grant_root: Option<PathBuf>,
|
|
}
|