mirror of
https://github.com/pchuan98/codex.git
synced 2026-07-01 00:31:56 +08:00
Remove core protocol dependency [2/2] (#20325)
## Why With the local model layer and app-server routing in place from PR1, this PR moves the active TUI runtime onto app-server notifications. The affected pieces share the same event flow, so the command surface, session state, bottom-pane prompts, chat rendering, history/status views, and tests move together to keep the stacked branch buildable. This PR also removes the obsolete compatibility surface that is no longer used after the migration. The proposed protocol-boundary verifier layer was dropped from the stack; enforcing that final boundary will be simpler once `codex-tui` no longer needs any `codex_protocol` references. This PR is part 2 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 - Rewired app command and session handling to use app-server request and notification shapes. - Moved approval overlays, request-user-input flows, MCP elicitation, realtime events, and review commands onto the app-server-facing model surface. - Updated chat rendering, history cells, status views, multi-agent UI, replay state, and TUI tests to use app-server notifications plus the local models introduced in PR1. - Deleted `codex-rs/tui/src/app/app_server_adapter.rs` and the superseded `chatwidget/tests/background_events.rs` fixture path. ## Verification - `cargo check -p codex-tui --tests` - Top of stack: `cargo test -p codex-tui`
This commit is contained in:
committed by
GitHub
Unverified
parent
5cc5f12efc
commit
f2bc2f26a9
@@ -3,9 +3,12 @@ use crate::history_cell::HistoryCell;
|
||||
use crate::history_cell::PlainHistoryCell;
|
||||
use crate::history_cell::with_border_with_inner_width;
|
||||
use crate::legacy_core::config::Config;
|
||||
use crate::token_usage::TokenUsage;
|
||||
use crate::token_usage::TokenUsageInfo;
|
||||
use crate::version::CODEX_CLI_VERSION;
|
||||
use chrono::DateTime;
|
||||
use chrono::Local;
|
||||
use codex_app_server_protocol::AskForApproval;
|
||||
use codex_model_provider_info::WireApi;
|
||||
use codex_protocol::ThreadId;
|
||||
use codex_protocol::account::PlanType;
|
||||
@@ -14,9 +17,6 @@ use codex_protocol::models::ActivePermissionProfile;
|
||||
use codex_protocol::models::ActivePermissionProfileModification;
|
||||
use codex_protocol::models::PermissionProfile;
|
||||
use codex_protocol::openai_models::ReasoningEffort;
|
||||
use codex_protocol::protocol::AskForApproval;
|
||||
use codex_protocol::protocol::TokenUsage;
|
||||
use codex_protocol::protocol::TokenUsageInfo;
|
||||
use codex_utils_sandbox_summary::summarize_permission_profile;
|
||||
use ratatui::prelude::*;
|
||||
use ratatui::style::Stylize;
|
||||
@@ -247,6 +247,8 @@ impl StatusHistoryCell {
|
||||
agents_summary: String,
|
||||
refreshing_rate_limits: bool,
|
||||
) -> (Self, StatusHistoryHandle) {
|
||||
let approval_policy = AskForApproval::from(config.permissions.approval_policy.value());
|
||||
let permission_profile = config.permissions.permission_profile();
|
||||
let mut config_entries = vec![
|
||||
("workdir", config.cwd.display().to_string()),
|
||||
("model", model_name.to_string()),
|
||||
@@ -257,10 +259,7 @@ impl StatusHistoryCell {
|
||||
),
|
||||
(
|
||||
"sandbox",
|
||||
summarize_permission_profile(
|
||||
&config.permissions.permission_profile(),
|
||||
config.cwd.as_path(),
|
||||
),
|
||||
summarize_permission_profile(&permission_profile, config.cwd.as_path()),
|
||||
),
|
||||
];
|
||||
if config.model_provider.wire_api == WireApi::Responses {
|
||||
@@ -283,18 +282,13 @@ impl StatusHistoryCell {
|
||||
.find(|(k, _)| *k == "approval")
|
||||
.map(|(_, v)| v.clone())
|
||||
.unwrap_or_else(|| "<unknown>".to_string());
|
||||
let permission_profile = config.permissions.permission_profile();
|
||||
let active_permission_profile = config.permissions.active_permission_profile();
|
||||
let sandbox = status_permission_summary(&permission_profile, config.cwd.as_path());
|
||||
let approval = status_approval_label(
|
||||
config.permissions.approval_policy.value(),
|
||||
config.approvals_reviewer,
|
||||
&approval,
|
||||
);
|
||||
let approval = status_approval_label(approval_policy, config.approvals_reviewer, &approval);
|
||||
let permissions = status_permissions_label(
|
||||
active_permission_profile.as_ref(),
|
||||
&permission_profile,
|
||||
config.permissions.approval_policy.value(),
|
||||
approval_policy,
|
||||
&sandbox,
|
||||
&approval,
|
||||
);
|
||||
|
||||
@@ -13,9 +13,9 @@ use chrono::DateTime;
|
||||
use chrono::Duration as ChronoDuration;
|
||||
use chrono::Local;
|
||||
use chrono::Utc;
|
||||
use codex_protocol::protocol::CreditsSnapshot as CoreCreditsSnapshot;
|
||||
use codex_protocol::protocol::RateLimitSnapshot;
|
||||
use codex_protocol::protocol::RateLimitWindow;
|
||||
use codex_app_server_protocol::CreditsSnapshot as CoreCreditsSnapshot;
|
||||
use codex_app_server_protocol::RateLimitSnapshot;
|
||||
use codex_app_server_protocol::RateLimitWindow;
|
||||
|
||||
const STATUS_LIMIT_BAR_SEGMENTS: usize = 20;
|
||||
const STATUS_LIMIT_BAR_FILLED: &str = "█";
|
||||
@@ -79,9 +79,9 @@ impl RateLimitWindowDisplay {
|
||||
let resets_at = resets_at_utc.map(|dt| format_reset_timestamp(dt, captured_at));
|
||||
|
||||
Self {
|
||||
used_percent: window.used_percent,
|
||||
used_percent: f64::from(window.used_percent),
|
||||
resets_at,
|
||||
window_minutes: window.window_minutes,
|
||||
window_minutes: window.window_duration_mins,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,29 +8,73 @@ use crate::legacy_core::config::ConfigBuilder;
|
||||
use crate::status::StatusAccountDisplay;
|
||||
use crate::test_support::PathBufExt;
|
||||
use crate::test_support::test_path_buf;
|
||||
use crate::token_usage::TokenUsage;
|
||||
use crate::token_usage::TokenUsageInfo;
|
||||
use chrono::Duration as ChronoDuration;
|
||||
use chrono::TimeZone;
|
||||
use chrono::Utc;
|
||||
use codex_app_server_protocol::AskForApproval;
|
||||
use codex_app_server_protocol::CreditsSnapshot;
|
||||
use codex_app_server_protocol::FileSystemAccessMode;
|
||||
use codex_app_server_protocol::FileSystemPath;
|
||||
use codex_app_server_protocol::FileSystemSandboxEntry;
|
||||
use codex_app_server_protocol::FileSystemSpecialPath;
|
||||
use codex_app_server_protocol::PermissionProfile as AppServerPermissionProfile;
|
||||
use codex_app_server_protocol::PermissionProfileFileSystemPermissions;
|
||||
use codex_app_server_protocol::PermissionProfileNetworkPermissions;
|
||||
use codex_app_server_protocol::RateLimitSnapshot;
|
||||
use codex_app_server_protocol::RateLimitWindow;
|
||||
use codex_protocol::ThreadId;
|
||||
use codex_protocol::config_types::ApprovalsReviewer;
|
||||
use codex_protocol::config_types::ReasoningSummary;
|
||||
use codex_protocol::models::ActivePermissionProfile;
|
||||
use codex_protocol::models::ActivePermissionProfileModification;
|
||||
use codex_protocol::models::ManagedFileSystemPermissions;
|
||||
use codex_protocol::models::PermissionProfile;
|
||||
use codex_protocol::openai_models::ReasoningEffort;
|
||||
use codex_protocol::protocol::AskForApproval;
|
||||
use codex_protocol::protocol::CreditsSnapshot;
|
||||
use codex_protocol::protocol::NetworkSandboxPolicy;
|
||||
use codex_protocol::protocol::RateLimitSnapshot;
|
||||
use codex_protocol::protocol::RateLimitWindow;
|
||||
use codex_protocol::protocol::TokenUsage;
|
||||
use codex_protocol::protocol::TokenUsageInfo;
|
||||
use codex_protocol::permissions::NetworkSandboxPolicy;
|
||||
use insta::assert_snapshot;
|
||||
use pretty_assertions::assert_eq;
|
||||
use ratatui::prelude::*;
|
||||
use tempfile::TempDir;
|
||||
|
||||
fn app_server_workspace_write_profile(network_enabled: bool) -> PermissionProfile {
|
||||
AppServerPermissionProfile::Managed {
|
||||
network: PermissionProfileNetworkPermissions {
|
||||
enabled: network_enabled,
|
||||
},
|
||||
file_system: PermissionProfileFileSystemPermissions::Restricted {
|
||||
entries: vec![
|
||||
FileSystemSandboxEntry {
|
||||
path: FileSystemPath::Special {
|
||||
value: FileSystemSpecialPath::Root,
|
||||
},
|
||||
access: FileSystemAccessMode::Read,
|
||||
},
|
||||
FileSystemSandboxEntry {
|
||||
path: FileSystemPath::Special {
|
||||
value: FileSystemSpecialPath::ProjectRoots { subpath: None },
|
||||
},
|
||||
access: FileSystemAccessMode::Write,
|
||||
},
|
||||
FileSystemSandboxEntry {
|
||||
path: FileSystemPath::Special {
|
||||
value: FileSystemSpecialPath::SlashTmp,
|
||||
},
|
||||
access: FileSystemAccessMode::Write,
|
||||
},
|
||||
FileSystemSandboxEntry {
|
||||
path: FileSystemPath::Special {
|
||||
value: FileSystemSpecialPath::Tmpdir,
|
||||
},
|
||||
access: FileSystemAccessMode::Write,
|
||||
},
|
||||
],
|
||||
glob_scan_max_depth: None,
|
||||
},
|
||||
}
|
||||
.into()
|
||||
}
|
||||
|
||||
async fn test_config(temp_home: &TempDir) -> Config {
|
||||
let mut config = ConfigBuilder::default()
|
||||
.codex_home(temp_home.path().to_path_buf())
|
||||
@@ -40,11 +84,8 @@ async fn test_config(temp_home: &TempDir) -> Config {
|
||||
config.approvals_reviewer = ApprovalsReviewer::User;
|
||||
config
|
||||
.permissions
|
||||
.set_permission_profile(PermissionProfile::workspace_write_with(
|
||||
&[],
|
||||
NetworkSandboxPolicy::Enabled,
|
||||
/*exclude_tmpdir_env_var*/ false,
|
||||
/*exclude_slash_tmp*/ false,
|
||||
.set_permission_profile(app_server_workspace_write_profile(
|
||||
/*network_enabled*/ true,
|
||||
))
|
||||
.expect("set permission profile");
|
||||
config
|
||||
@@ -171,13 +212,13 @@ async fn status_snapshot_includes_reasoning_details() {
|
||||
limit_id: None,
|
||||
limit_name: None,
|
||||
primary: Some(RateLimitWindow {
|
||||
used_percent: 72.5,
|
||||
window_minutes: Some(300),
|
||||
used_percent: 72,
|
||||
window_duration_mins: Some(300),
|
||||
resets_at: Some(reset_at_from(&captured_at, /*seconds*/ 600)),
|
||||
}),
|
||||
secondary: Some(RateLimitWindow {
|
||||
used_percent: 45.0,
|
||||
window_minutes: Some(10080),
|
||||
used_percent: 45,
|
||||
window_duration_mins: Some(10080),
|
||||
resets_at: Some(reset_at_from(&captured_at, /*seconds*/ 1_200)),
|
||||
}),
|
||||
credits: None,
|
||||
@@ -224,16 +265,13 @@ async fn status_permissions_non_default_workspace_write_uses_workspace_label() {
|
||||
config
|
||||
.permissions
|
||||
.approval_policy
|
||||
.set(AskForApproval::OnRequest)
|
||||
.set(AskForApproval::OnRequest.to_core())
|
||||
.expect("set approval policy");
|
||||
config.cwd = test_path_buf("/workspace/tests").abs();
|
||||
config
|
||||
.permissions
|
||||
.set_permission_profile(PermissionProfile::workspace_write_with(
|
||||
&[],
|
||||
NetworkSandboxPolicy::Enabled,
|
||||
/*exclude_tmpdir_env_var*/ false,
|
||||
/*exclude_slash_tmp*/ false,
|
||||
.set_permission_profile(app_server_workspace_write_profile(
|
||||
/*network_enabled*/ true,
|
||||
))
|
||||
.expect("set permission profile");
|
||||
|
||||
@@ -250,7 +288,7 @@ async fn status_permissions_named_read_only_profile_shows_builtin_label() {
|
||||
config
|
||||
.permissions
|
||||
.approval_policy
|
||||
.set(AskForApproval::OnRequest)
|
||||
.set(AskForApproval::OnRequest.to_core())
|
||||
.expect("set approval policy");
|
||||
config
|
||||
.permissions
|
||||
@@ -273,7 +311,7 @@ async fn status_permissions_read_only_profile_shows_additional_writable_roots()
|
||||
config
|
||||
.permissions
|
||||
.approval_policy
|
||||
.set(AskForApproval::OnRequest)
|
||||
.set(AskForApproval::OnRequest.to_core())
|
||||
.expect("set approval policy");
|
||||
let extra_root = test_path_buf("/workspace/extra").abs();
|
||||
let file_system_policy = PermissionProfile::read_only()
|
||||
@@ -309,7 +347,7 @@ async fn status_permissions_named_workspace_profile_shows_builtin_label() {
|
||||
config
|
||||
.permissions
|
||||
.approval_policy
|
||||
.set(AskForApproval::OnRequest)
|
||||
.set(AskForApproval::OnRequest.to_core())
|
||||
.expect("set approval policy");
|
||||
config
|
||||
.permissions
|
||||
@@ -333,7 +371,7 @@ async fn status_permissions_workspace_auto_review_shows_reviewer_label() {
|
||||
config
|
||||
.permissions
|
||||
.approval_policy
|
||||
.set(AskForApproval::OnRequest)
|
||||
.set(AskForApproval::OnRequest.to_core())
|
||||
.expect("set approval policy");
|
||||
config
|
||||
.permissions
|
||||
@@ -356,7 +394,7 @@ async fn status_permissions_named_profile_shows_additional_writable_roots() {
|
||||
config
|
||||
.permissions
|
||||
.approval_policy
|
||||
.set(AskForApproval::OnRequest)
|
||||
.set(AskForApproval::OnRequest.to_core())
|
||||
.expect("set approval policy");
|
||||
let extra_root = test_path_buf("/workspace/extra").abs();
|
||||
config
|
||||
@@ -391,7 +429,7 @@ async fn status_permissions_broadened_workspace_profile_shows_builtin_label() {
|
||||
config
|
||||
.permissions
|
||||
.approval_policy
|
||||
.set(AskForApproval::OnRequest)
|
||||
.set(AskForApproval::OnRequest.to_core())
|
||||
.expect("set approval policy");
|
||||
config
|
||||
.permissions
|
||||
@@ -532,14 +570,17 @@ async fn status_permissions_full_disk_managed_with_network_is_danger_full_access
|
||||
config
|
||||
.permissions
|
||||
.approval_policy
|
||||
.set(AskForApproval::OnRequest)
|
||||
.set(AskForApproval::OnRequest.to_core())
|
||||
.expect("set approval policy");
|
||||
config
|
||||
.permissions
|
||||
.set_permission_profile(PermissionProfile::Managed {
|
||||
file_system: ManagedFileSystemPermissions::Unrestricted,
|
||||
network: NetworkSandboxPolicy::Enabled,
|
||||
})
|
||||
.set_permission_profile(
|
||||
AppServerPermissionProfile::Managed {
|
||||
network: PermissionProfileNetworkPermissions { enabled: true },
|
||||
file_system: PermissionProfileFileSystemPermissions::Unrestricted,
|
||||
}
|
||||
.into(),
|
||||
)
|
||||
.expect("set permission profile");
|
||||
|
||||
assert_eq!(
|
||||
@@ -555,14 +596,17 @@ async fn status_permissions_full_disk_managed_without_network_is_external_sandbo
|
||||
config
|
||||
.permissions
|
||||
.approval_policy
|
||||
.set(AskForApproval::OnRequest)
|
||||
.set(AskForApproval::OnRequest.to_core())
|
||||
.expect("set approval policy");
|
||||
config
|
||||
.permissions
|
||||
.set_permission_profile(PermissionProfile::Managed {
|
||||
file_system: ManagedFileSystemPermissions::Unrestricted,
|
||||
network: NetworkSandboxPolicy::Restricted,
|
||||
})
|
||||
.set_permission_profile(
|
||||
AppServerPermissionProfile::Managed {
|
||||
network: PermissionProfileNetworkPermissions { enabled: false },
|
||||
file_system: PermissionProfileFileSystemPermissions::Unrestricted,
|
||||
}
|
||||
.into(),
|
||||
)
|
||||
.expect("set permission profile");
|
||||
|
||||
assert_eq!(
|
||||
@@ -650,8 +694,8 @@ async fn status_snapshot_includes_monthly_limit() {
|
||||
limit_id: None,
|
||||
limit_name: None,
|
||||
primary: Some(RateLimitWindow {
|
||||
used_percent: 12.0,
|
||||
window_minutes: Some(43_200),
|
||||
used_percent: 12,
|
||||
window_duration_mins: Some(43_200),
|
||||
resets_at: Some(reset_at_from(&captured_at, /*seconds*/ 86_400)),
|
||||
}),
|
||||
secondary: None,
|
||||
@@ -956,8 +1000,8 @@ async fn status_snapshot_truncates_in_narrow_terminal() {
|
||||
limit_id: None,
|
||||
limit_name: None,
|
||||
primary: Some(RateLimitWindow {
|
||||
used_percent: 72.5,
|
||||
window_minutes: Some(300),
|
||||
used_percent: 72,
|
||||
window_duration_mins: Some(300),
|
||||
resets_at: Some(reset_at_from(&captured_at, /*seconds*/ 600)),
|
||||
}),
|
||||
secondary: None,
|
||||
@@ -1116,13 +1160,13 @@ async fn status_snapshot_shows_refreshing_limits_notice() {
|
||||
limit_id: None,
|
||||
limit_name: None,
|
||||
primary: Some(RateLimitWindow {
|
||||
used_percent: 45.0,
|
||||
window_minutes: Some(300),
|
||||
used_percent: 45,
|
||||
window_duration_mins: Some(300),
|
||||
resets_at: Some(reset_at_from(&captured_at, /*seconds*/ 900)),
|
||||
}),
|
||||
secondary: Some(RateLimitWindow {
|
||||
used_percent: 30.0,
|
||||
window_minutes: Some(10_080),
|
||||
used_percent: 30,
|
||||
window_duration_mins: Some(10_080),
|
||||
resets_at: Some(reset_at_from(&captured_at, /*seconds*/ 2_700)),
|
||||
}),
|
||||
credits: None,
|
||||
@@ -1183,13 +1227,13 @@ async fn status_snapshot_includes_credits_and_limits() {
|
||||
limit_id: None,
|
||||
limit_name: None,
|
||||
primary: Some(RateLimitWindow {
|
||||
used_percent: 45.0,
|
||||
window_minutes: Some(300),
|
||||
used_percent: 45,
|
||||
window_duration_mins: Some(300),
|
||||
resets_at: Some(reset_at_from(&captured_at, /*seconds*/ 900)),
|
||||
}),
|
||||
secondary: Some(RateLimitWindow {
|
||||
used_percent: 30.0,
|
||||
window_minutes: Some(10_080),
|
||||
used_percent: 30,
|
||||
window_duration_mins: Some(10_080),
|
||||
resets_at: Some(reset_at_from(&captured_at, /*seconds*/ 2_700)),
|
||||
}),
|
||||
credits: Some(CreditsSnapshot {
|
||||
@@ -1369,13 +1413,13 @@ async fn status_snapshot_shows_stale_limits_message() {
|
||||
limit_id: None,
|
||||
limit_name: None,
|
||||
primary: Some(RateLimitWindow {
|
||||
used_percent: 72.5,
|
||||
window_minutes: Some(300),
|
||||
used_percent: 72,
|
||||
window_duration_mins: Some(300),
|
||||
resets_at: Some(reset_at_from(&captured_at, /*seconds*/ 600)),
|
||||
}),
|
||||
secondary: Some(RateLimitWindow {
|
||||
used_percent: 40.0,
|
||||
window_minutes: Some(10_080),
|
||||
used_percent: 40,
|
||||
window_duration_mins: Some(10_080),
|
||||
resets_at: Some(reset_at_from(&captured_at, /*seconds*/ 1_800)),
|
||||
}),
|
||||
credits: None,
|
||||
@@ -1436,13 +1480,13 @@ async fn status_snapshot_cached_limits_hide_credits_without_flag() {
|
||||
limit_id: None,
|
||||
limit_name: None,
|
||||
primary: Some(RateLimitWindow {
|
||||
used_percent: 60.0,
|
||||
window_minutes: Some(300),
|
||||
used_percent: 60,
|
||||
window_duration_mins: Some(300),
|
||||
resets_at: Some(reset_at_from(&captured_at, /*seconds*/ 1_200)),
|
||||
}),
|
||||
secondary: Some(RateLimitWindow {
|
||||
used_percent: 35.0,
|
||||
window_minutes: Some(10_080),
|
||||
used_percent: 35,
|
||||
window_duration_mins: Some(10_080),
|
||||
resets_at: Some(reset_at_from(&captured_at, /*seconds*/ 2_400)),
|
||||
}),
|
||||
credits: Some(CreditsSnapshot {
|
||||
|
||||
Reference in New Issue
Block a user