mirror of
https://github.com/pchuan98/codex.git
synced 2026-07-01 00:31:56 +08: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`
219 lines
9.6 KiB
Rust
219 lines
9.6 KiB
Rust
//! Thread targeting helpers for app-server requests and notifications.
|
|
|
|
use codex_app_server_protocol::ServerNotification;
|
|
use codex_app_server_protocol::ServerRequest;
|
|
use codex_protocol::ThreadId;
|
|
|
|
pub(super) fn server_request_thread_id(request: &ServerRequest) -> Option<ThreadId> {
|
|
match request {
|
|
ServerRequest::CommandExecutionRequestApproval { params, .. } => {
|
|
ThreadId::from_string(¶ms.thread_id).ok()
|
|
}
|
|
ServerRequest::FileChangeRequestApproval { params, .. } => {
|
|
ThreadId::from_string(¶ms.thread_id).ok()
|
|
}
|
|
ServerRequest::ToolRequestUserInput { params, .. } => {
|
|
ThreadId::from_string(¶ms.thread_id).ok()
|
|
}
|
|
ServerRequest::McpServerElicitationRequest { params, .. } => {
|
|
ThreadId::from_string(¶ms.thread_id).ok()
|
|
}
|
|
ServerRequest::PermissionsRequestApproval { params, .. } => {
|
|
ThreadId::from_string(¶ms.thread_id).ok()
|
|
}
|
|
ServerRequest::DynamicToolCall { params, .. } => {
|
|
ThreadId::from_string(¶ms.thread_id).ok()
|
|
}
|
|
ServerRequest::ChatgptAuthTokensRefresh { .. }
|
|
| ServerRequest::ApplyPatchApproval { .. }
|
|
| ServerRequest::ExecCommandApproval { .. } => None,
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, PartialEq, Eq)]
|
|
pub(super) enum ServerNotificationThreadTarget {
|
|
Thread(ThreadId),
|
|
InvalidThreadId(String),
|
|
Global,
|
|
}
|
|
|
|
pub(super) fn server_notification_thread_target(
|
|
notification: &ServerNotification,
|
|
) -> ServerNotificationThreadTarget {
|
|
let thread_id = match notification {
|
|
ServerNotification::Error(notification) => Some(notification.thread_id.as_str()),
|
|
ServerNotification::ThreadStarted(notification) => Some(notification.thread.id.as_str()),
|
|
ServerNotification::ThreadStatusChanged(notification) => {
|
|
Some(notification.thread_id.as_str())
|
|
}
|
|
ServerNotification::ThreadArchived(notification) => Some(notification.thread_id.as_str()),
|
|
ServerNotification::ThreadUnarchived(notification) => Some(notification.thread_id.as_str()),
|
|
ServerNotification::ThreadClosed(notification) => Some(notification.thread_id.as_str()),
|
|
ServerNotification::ThreadNameUpdated(notification) => {
|
|
Some(notification.thread_id.as_str())
|
|
}
|
|
ServerNotification::ThreadTokenUsageUpdated(notification) => {
|
|
Some(notification.thread_id.as_str())
|
|
}
|
|
ServerNotification::ThreadGoalUpdated(notification) => {
|
|
Some(notification.thread_id.as_str())
|
|
}
|
|
ServerNotification::ThreadGoalCleared(notification) => {
|
|
Some(notification.thread_id.as_str())
|
|
}
|
|
ServerNotification::TurnStarted(notification) => Some(notification.thread_id.as_str()),
|
|
ServerNotification::HookStarted(notification) => Some(notification.thread_id.as_str()),
|
|
ServerNotification::TurnCompleted(notification) => Some(notification.thread_id.as_str()),
|
|
ServerNotification::HookCompleted(notification) => Some(notification.thread_id.as_str()),
|
|
ServerNotification::TurnDiffUpdated(notification) => Some(notification.thread_id.as_str()),
|
|
ServerNotification::TurnPlanUpdated(notification) => Some(notification.thread_id.as_str()),
|
|
ServerNotification::ItemStarted(notification) => Some(notification.thread_id.as_str()),
|
|
ServerNotification::ItemGuardianApprovalReviewStarted(notification) => {
|
|
Some(notification.thread_id.as_str())
|
|
}
|
|
ServerNotification::ItemGuardianApprovalReviewCompleted(notification) => {
|
|
Some(notification.thread_id.as_str())
|
|
}
|
|
ServerNotification::ItemCompleted(notification) => Some(notification.thread_id.as_str()),
|
|
ServerNotification::RawResponseItemCompleted(notification) => {
|
|
Some(notification.thread_id.as_str())
|
|
}
|
|
ServerNotification::AgentMessageDelta(notification) => {
|
|
Some(notification.thread_id.as_str())
|
|
}
|
|
ServerNotification::PlanDelta(notification) => Some(notification.thread_id.as_str()),
|
|
ServerNotification::CommandExecutionOutputDelta(notification) => {
|
|
Some(notification.thread_id.as_str())
|
|
}
|
|
ServerNotification::TerminalInteraction(notification) => {
|
|
Some(notification.thread_id.as_str())
|
|
}
|
|
ServerNotification::FileChangeOutputDelta(notification) => {
|
|
Some(notification.thread_id.as_str())
|
|
}
|
|
ServerNotification::FileChangePatchUpdated(notification) => {
|
|
Some(notification.thread_id.as_str())
|
|
}
|
|
ServerNotification::ServerRequestResolved(notification) => {
|
|
Some(notification.thread_id.as_str())
|
|
}
|
|
ServerNotification::McpToolCallProgress(notification) => {
|
|
Some(notification.thread_id.as_str())
|
|
}
|
|
ServerNotification::ReasoningSummaryTextDelta(notification) => {
|
|
Some(notification.thread_id.as_str())
|
|
}
|
|
ServerNotification::ReasoningSummaryPartAdded(notification) => {
|
|
Some(notification.thread_id.as_str())
|
|
}
|
|
ServerNotification::ReasoningTextDelta(notification) => {
|
|
Some(notification.thread_id.as_str())
|
|
}
|
|
ServerNotification::ContextCompacted(notification) => Some(notification.thread_id.as_str()),
|
|
ServerNotification::ModelRerouted(notification) => Some(notification.thread_id.as_str()),
|
|
ServerNotification::ModelVerification(notification) => {
|
|
Some(notification.thread_id.as_str())
|
|
}
|
|
ServerNotification::ThreadRealtimeStarted(notification) => {
|
|
Some(notification.thread_id.as_str())
|
|
}
|
|
ServerNotification::ThreadRealtimeItemAdded(notification) => {
|
|
Some(notification.thread_id.as_str())
|
|
}
|
|
ServerNotification::ThreadRealtimeTranscriptDelta(notification) => {
|
|
Some(notification.thread_id.as_str())
|
|
}
|
|
ServerNotification::ThreadRealtimeTranscriptDone(notification) => {
|
|
Some(notification.thread_id.as_str())
|
|
}
|
|
ServerNotification::ThreadRealtimeOutputAudioDelta(notification) => {
|
|
Some(notification.thread_id.as_str())
|
|
}
|
|
ServerNotification::ThreadRealtimeSdp(notification) => {
|
|
Some(notification.thread_id.as_str())
|
|
}
|
|
ServerNotification::ThreadRealtimeError(notification) => {
|
|
Some(notification.thread_id.as_str())
|
|
}
|
|
ServerNotification::ThreadRealtimeClosed(notification) => {
|
|
Some(notification.thread_id.as_str())
|
|
}
|
|
ServerNotification::Warning(notification) => notification.thread_id.as_deref(),
|
|
ServerNotification::GuardianWarning(notification) => Some(notification.thread_id.as_str()),
|
|
ServerNotification::SkillsChanged(_)
|
|
| ServerNotification::McpServerStatusUpdated(_)
|
|
| ServerNotification::McpServerOauthLoginCompleted(_)
|
|
| ServerNotification::AccountUpdated(_)
|
|
| ServerNotification::AccountRateLimitsUpdated(_)
|
|
| ServerNotification::AppListUpdated(_)
|
|
| ServerNotification::RemoteControlStatusChanged(_)
|
|
| ServerNotification::ExternalAgentConfigImportCompleted(_)
|
|
| ServerNotification::DeprecationNotice(_)
|
|
| ServerNotification::ConfigWarning(_)
|
|
| ServerNotification::FuzzyFileSearchSessionUpdated(_)
|
|
| ServerNotification::FuzzyFileSearchSessionCompleted(_)
|
|
| ServerNotification::CommandExecOutputDelta(_)
|
|
| ServerNotification::FsChanged(_)
|
|
| ServerNotification::WindowsWorldWritableWarning(_)
|
|
| ServerNotification::WindowsSandboxSetupCompleted(_)
|
|
| ServerNotification::AccountLoginCompleted(_) => None,
|
|
};
|
|
|
|
match thread_id {
|
|
Some(thread_id) => match ThreadId::from_string(thread_id) {
|
|
Ok(thread_id) => ServerNotificationThreadTarget::Thread(thread_id),
|
|
Err(_) => ServerNotificationThreadTarget::InvalidThreadId(thread_id.to_string()),
|
|
},
|
|
None => ServerNotificationThreadTarget::Global,
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::ServerNotificationThreadTarget;
|
|
use super::server_notification_thread_target;
|
|
use codex_app_server_protocol::GuardianWarningNotification;
|
|
use codex_app_server_protocol::ServerNotification;
|
|
use codex_app_server_protocol::WarningNotification;
|
|
use codex_protocol::ThreadId;
|
|
use pretty_assertions::assert_eq;
|
|
|
|
#[test]
|
|
fn warning_notifications_without_threads_are_global() {
|
|
let notification = ServerNotification::Warning(WarningNotification {
|
|
thread_id: None,
|
|
message: "warning".to_string(),
|
|
});
|
|
|
|
let target = server_notification_thread_target(¬ification);
|
|
|
|
assert_eq!(target, ServerNotificationThreadTarget::Global);
|
|
}
|
|
|
|
#[test]
|
|
fn warning_notifications_route_to_threads_when_thread_id_is_present() {
|
|
let thread_id = ThreadId::new();
|
|
let notification = ServerNotification::Warning(WarningNotification {
|
|
thread_id: Some(thread_id.to_string()),
|
|
message: "warning".to_string(),
|
|
});
|
|
|
|
let target = server_notification_thread_target(¬ification);
|
|
|
|
assert_eq!(target, ServerNotificationThreadTarget::Thread(thread_id));
|
|
}
|
|
|
|
#[test]
|
|
fn guardian_warning_notifications_route_to_threads() {
|
|
let thread_id = ThreadId::new();
|
|
let notification = ServerNotification::GuardianWarning(GuardianWarningNotification {
|
|
thread_id: thread_id.to_string(),
|
|
message: "warning".to_string(),
|
|
});
|
|
|
|
let target = server_notification_thread_target(¬ification);
|
|
|
|
assert_eq!(target, ServerNotificationThreadTarget::Thread(thread_id));
|
|
}
|
|
}
|