chore: introduce CodexServer as a clearinghouse for all conversations

This commit is contained in:
Michael Bolin
2025-08-13 09:21:35 -07:00
parent e8670ad840
commit 1152f4fdd6
23 changed files with 372 additions and 555 deletions
+8
View File
@@ -9,6 +9,7 @@ use crate::onboarding::onboarding_screen::OnboardingScreenArgs;
use crate::should_show_login_screen;
use crate::slash_command::SlashCommand;
use crate::tui;
use codex_core::ConversationManager;
use codex_core::config::Config;
use codex_core::protocol::Event;
use codex_core::protocol::Op;
@@ -48,6 +49,7 @@ enum AppState<'a> {
}
pub(crate) struct App<'a> {
server: Arc<ConversationManager>,
app_event_tx: AppEventSender,
app_event_rx: Receiver<AppEvent>,
app_state: AppState<'a>,
@@ -82,6 +84,8 @@ impl App<'_> {
initial_images: Vec<std::path::PathBuf>,
show_trust_screen: bool,
) -> Self {
let conversation_manager = Arc::new(ConversationManager::default());
let (app_event_tx, app_event_rx) = channel();
let app_event_tx = AppEventSender::new(app_event_tx);
let pending_redraw = Arc::new(AtomicBool::new(false));
@@ -152,6 +156,7 @@ impl App<'_> {
} else {
let chat_widget = ChatWidget::new(
config.clone(),
conversation_manager.clone(),
app_event_tx.clone(),
initial_prompt,
initial_images,
@@ -164,6 +169,7 @@ impl App<'_> {
let file_search = FileSearchManager::new(config.cwd.clone(), app_event_tx.clone());
Self {
server: conversation_manager,
app_event_tx,
pending_history_lines: Vec::new(),
app_event_rx,
@@ -316,6 +322,7 @@ impl App<'_> {
// User accepted – switch to chat view.
let new_widget = Box::new(ChatWidget::new(
self.config.clone(),
self.server.clone(),
self.app_event_tx.clone(),
None,
Vec::new(),
@@ -437,6 +444,7 @@ impl App<'_> {
self.app_state = AppState::Chat {
widget: Box::new(ChatWidget::new(
config,
self.server.clone(),
app_event_tx.clone(),
initial_prompt,
initial_images,
+18 -14
View File
@@ -2,8 +2,7 @@ use std::collections::HashMap;
use std::path::PathBuf;
use std::sync::Arc;
use codex_core::codex_wrapper::CodexConversation;
use codex_core::codex_wrapper::init_codex;
use codex_core::NewConversation;
use codex_core::config::Config;
use codex_core::parse_command::ParsedCommand;
use codex_core::protocol::AgentMessageDeltaEvent;
@@ -53,6 +52,7 @@ use crate::history_cell::HistoryCell;
use crate::history_cell::PatchEventType;
use crate::live_wrap::RowBuilder;
use crate::user_approval_widget::ApprovalRequest;
use codex_core::ConversationManager;
use codex_file_search::FileMatch;
use ratatui::style::Stylize;
@@ -161,6 +161,7 @@ impl ChatWidget<'_> {
}
pub(crate) fn new(
config: Config,
server: Arc<ConversationManager>,
app_event_tx: AppEventSender,
initial_prompt: Option<String>,
initial_images: Vec<PathBuf>,
@@ -172,12 +173,12 @@ impl ChatWidget<'_> {
// Create the Codex asynchronously so the UI loads as quickly as possible.
let config_for_agent_loop = config.clone();
tokio::spawn(async move {
let CodexConversation {
codex,
let NewConversation {
conversation_id: _,
conversation,
session_configured,
..
} = match init_codex(config_for_agent_loop).await {
Ok(vals) => vals,
} = match server.new_conversation(config_for_agent_loop).await {
Ok(v) => v,
Err(e) => {
// TODO: surface this error to the user.
tracing::error!("failed to initialize codex: {e}");
@@ -185,21 +186,24 @@ impl ChatWidget<'_> {
}
};
// Forward the captured `SessionInitialized` event that was consumed
// inside `init_codex()` so it can be rendered in the UI.
app_event_tx_clone.send(AppEvent::CodexEvent(session_configured.clone()));
let codex = Arc::new(codex);
let codex_clone = codex.clone();
// Forward the captured `SessionConfigured` event so it can be rendered in the UI.
let ev = codex_core::protocol::Event {
// The `id` does not matter for rendering, so we can use a fake value.
id: "".to_string(),
msg: codex_core::protocol::EventMsg::SessionConfigured(session_configured),
};
app_event_tx_clone.send(AppEvent::CodexEvent(ev));
let conversation_clone = conversation.clone();
tokio::spawn(async move {
while let Some(op) = codex_op_rx.recv().await {
let id = codex_clone.submit(op).await;
let id = conversation_clone.submit(op).await;
if let Err(e) = id {
tracing::error!("failed to submit op: {e}");
}
}
});
while let Ok(event) = codex.next_event().await {
while let Ok(event) = conversation.next_event().await {
app_event_tx_clone.send(AppEvent::CodexEvent(event));
}
});