mirror of
https://github.com/openai/codex.git
synced 2026-06-01 19:02:59 +00:00
Part 8
This commit is contained in:
@@ -274,8 +274,6 @@ pub struct InProcessAppServerClient {
|
||||
command_tx: mpsc::Sender<ClientCommand>,
|
||||
event_rx: mpsc::Receiver<InProcessServerEvent>,
|
||||
worker_handle: tokio::task::JoinHandle<()>,
|
||||
auth_manager: Arc<AuthManager>,
|
||||
thread_manager: Arc<ThreadManager>,
|
||||
}
|
||||
|
||||
impl InProcessAppServerClient {
|
||||
@@ -439,21 +437,9 @@ impl InProcessAppServerClient {
|
||||
command_tx,
|
||||
event_rx,
|
||||
worker_handle,
|
||||
auth_manager: shared_core.auth_manager,
|
||||
thread_manager: shared_core.thread_manager,
|
||||
})
|
||||
}
|
||||
|
||||
/// Temporary bootstrap escape hatch for embedders migrating toward RPC-only usage.
|
||||
pub fn auth_manager(&self) -> Arc<AuthManager> {
|
||||
self.auth_manager.clone()
|
||||
}
|
||||
|
||||
/// Temporary bootstrap escape hatch for embedders migrating toward RPC-only usage.
|
||||
pub fn thread_manager(&self) -> Arc<ThreadManager> {
|
||||
self.thread_manager.clone()
|
||||
}
|
||||
|
||||
/// Sends a typed client request and returns raw JSON-RPC result.
|
||||
///
|
||||
/// Callers that expect a concrete response type should usually prefer
|
||||
@@ -597,6 +583,25 @@ impl InProcessAppServerClient {
|
||||
self.event_rx.recv().await
|
||||
}
|
||||
|
||||
/// Receives the next non-legacy server event from the in-process runtime.
|
||||
///
|
||||
/// Legacy bridge notifications are skipped so fully migrated callers can
|
||||
/// consume only typed app-server notifications and requests.
|
||||
pub async fn next_typed_event(&mut self) -> Option<InProcessServerEvent> {
|
||||
loop {
|
||||
match self.next_event().await {
|
||||
Some(InProcessServerEvent::LegacyNotification(notification)) => {
|
||||
warn!(
|
||||
notification.method = %notification.method,
|
||||
"dropping legacy in-process app-server notification"
|
||||
);
|
||||
}
|
||||
Some(event) => return Some(event),
|
||||
None => return None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Shuts down worker and in-process runtime with bounded wait.
|
||||
///
|
||||
/// If graceful shutdown exceeds timeout, the worker task is aborted to
|
||||
@@ -606,8 +611,6 @@ impl InProcessAppServerClient {
|
||||
command_tx,
|
||||
event_rx,
|
||||
worker_handle,
|
||||
auth_manager: _,
|
||||
thread_manager: _,
|
||||
} = self;
|
||||
let mut worker_handle = worker_handle;
|
||||
// Drop the caller-facing receiver before asking the worker to shut
|
||||
@@ -659,8 +662,6 @@ mod tests {
|
||||
use codex_app_server_protocol::SessionSource as ApiSessionSource;
|
||||
use codex_app_server_protocol::ThreadStartParams;
|
||||
use codex_app_server_protocol::ThreadStartResponse;
|
||||
use codex_core::AuthManager;
|
||||
use codex_core::ThreadManager;
|
||||
use codex_core::config::ConfigBuilder;
|
||||
use pretty_assertions::assert_eq;
|
||||
use tokio::time::Duration;
|
||||
@@ -757,7 +758,7 @@ mod tests {
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn shared_thread_manager_tracks_threads_started_via_app_server() {
|
||||
async fn thread_read_loads_threads_started_via_app_server() {
|
||||
let client = start_test_client(SessionSource::Cli).await;
|
||||
|
||||
let response: ThreadStartResponse = client
|
||||
@@ -770,17 +771,18 @@ mod tests {
|
||||
})
|
||||
.await
|
||||
.expect("thread/start should succeed");
|
||||
let created_thread_id = codex_protocol::ThreadId::from_string(&response.thread.id)
|
||||
.expect("thread id should parse");
|
||||
timeout(
|
||||
Duration::from_secs(2),
|
||||
client.thread_manager().get_thread(created_thread_id),
|
||||
)
|
||||
.await
|
||||
.expect("timed out waiting for retained thread manager to observe started thread")
|
||||
.expect("started thread should be visible through the shared thread manager");
|
||||
let thread_ids = client.thread_manager().list_thread_ids().await;
|
||||
assert!(thread_ids.contains(&created_thread_id));
|
||||
let thread_id = response.thread.id;
|
||||
let read: codex_app_server_protocol::ThreadReadResponse = client
|
||||
.request_typed(ClientRequest::ThreadRead {
|
||||
request_id: RequestId::Integer(4),
|
||||
params: codex_app_server_protocol::ThreadReadParams {
|
||||
thread_id: thread_id.clone(),
|
||||
include_turns: false,
|
||||
},
|
||||
})
|
||||
.await
|
||||
.expect("thread/read should succeed");
|
||||
assert_eq!(read.thread.id, thread_id);
|
||||
|
||||
client.shutdown().await.expect("shutdown should complete");
|
||||
}
|
||||
@@ -829,22 +831,6 @@ mod tests {
|
||||
let (command_tx, _command_rx) = mpsc::channel(1);
|
||||
let (event_tx, event_rx) = mpsc::channel(1);
|
||||
let worker_handle = tokio::spawn(async {});
|
||||
let config = build_test_config().await;
|
||||
let auth_manager = AuthManager::shared(
|
||||
config.codex_home.clone(),
|
||||
false,
|
||||
config.cli_auth_credentials_store_mode,
|
||||
);
|
||||
let thread_manager = Arc::new(ThreadManager::new(
|
||||
&config,
|
||||
auth_manager.clone(),
|
||||
SessionSource::Exec,
|
||||
CollaborationModesConfig {
|
||||
default_mode_request_user_input: config
|
||||
.features
|
||||
.enabled(codex_core::features::Feature::DefaultModeRequestUserInput),
|
||||
},
|
||||
));
|
||||
event_tx
|
||||
.send(InProcessServerEvent::Lagged { skipped: 3 })
|
||||
.await
|
||||
@@ -855,8 +841,6 @@ mod tests {
|
||||
command_tx,
|
||||
event_rx,
|
||||
worker_handle,
|
||||
auth_manager,
|
||||
thread_manager,
|
||||
};
|
||||
|
||||
let event = timeout(Duration::from_secs(2), client.next_event())
|
||||
@@ -901,17 +885,38 @@ mod tests {
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn accessors_expose_retained_shared_managers() {
|
||||
let client = start_test_client(SessionSource::Cli).await;
|
||||
async fn next_typed_event_skips_legacy_notifications() {
|
||||
let (command_tx, _command_rx) = mpsc::channel(1);
|
||||
let (event_tx, event_rx) = mpsc::channel(2);
|
||||
let worker_handle = tokio::spawn(async {});
|
||||
event_tx
|
||||
.send(InProcessServerEvent::LegacyNotification(
|
||||
codex_app_server_protocol::JSONRPCNotification {
|
||||
method: "codex/event/task_complete".to_string(),
|
||||
params: None,
|
||||
},
|
||||
))
|
||||
.await
|
||||
.expect("legacy notification should enqueue");
|
||||
event_tx
|
||||
.send(InProcessServerEvent::Lagged { skipped: 2 })
|
||||
.await
|
||||
.expect("lagged marker should enqueue");
|
||||
drop(event_tx);
|
||||
|
||||
assert!(
|
||||
Arc::ptr_eq(&client.auth_manager(), &client.auth_manager()),
|
||||
"auth_manager accessor should clone the retained shared manager"
|
||||
);
|
||||
assert!(
|
||||
Arc::ptr_eq(&client.thread_manager(), &client.thread_manager()),
|
||||
"thread_manager accessor should clone the retained shared manager"
|
||||
);
|
||||
let mut client = InProcessAppServerClient {
|
||||
command_tx,
|
||||
event_rx,
|
||||
worker_handle,
|
||||
};
|
||||
|
||||
let event = timeout(Duration::from_secs(2), client.next_typed_event())
|
||||
.await
|
||||
.expect("typed event should arrive before timeout");
|
||||
assert!(matches!(
|
||||
event,
|
||||
Some(InProcessServerEvent::Lagged { skipped: 2 })
|
||||
));
|
||||
|
||||
client.shutdown().await.expect("shutdown should complete");
|
||||
}
|
||||
|
||||
@@ -46,6 +46,8 @@ use codex_ansi_escape::ansi_escape_line;
|
||||
use codex_app_server_client::InProcessAppServerClient;
|
||||
use codex_app_server_protocol::ConfigLayerSource;
|
||||
use codex_app_server_protocol::RequestId;
|
||||
use codex_app_server_protocol::SkillErrorInfo;
|
||||
use codex_app_server_protocol::SkillsListEntry;
|
||||
use codex_core::config::Config;
|
||||
use codex_core::config::ConfigBuilder;
|
||||
use codex_core::config::ConfigOverrides;
|
||||
@@ -71,15 +73,11 @@ use codex_protocol::openai_models::ModelPreset;
|
||||
use codex_protocol::openai_models::ModelUpgrade;
|
||||
use codex_protocol::openai_models::ReasoningEffort as ReasoningEffortConfig;
|
||||
use codex_protocol::protocol::AskForApproval;
|
||||
use codex_protocol::protocol::Event;
|
||||
use codex_protocol::protocol::EventMsg;
|
||||
use codex_protocol::protocol::FinalOutput;
|
||||
use codex_protocol::protocol::ListSkillsResponseEvent;
|
||||
use codex_protocol::protocol::Op;
|
||||
use codex_protocol::protocol::SandboxPolicy;
|
||||
use codex_protocol::protocol::SessionConfiguredEvent;
|
||||
use codex_protocol::protocol::SessionSource;
|
||||
use codex_protocol::protocol::SkillErrorInfo;
|
||||
use codex_protocol::protocol::TokenUsage;
|
||||
use codex_utils_absolute_path::AbsolutePathBuf;
|
||||
use color_eyre::eyre::Result;
|
||||
@@ -88,6 +86,11 @@ use crossterm::event::KeyCode;
|
||||
use crossterm::event::KeyEvent;
|
||||
use crossterm::event::KeyEventKind;
|
||||
use ratatui::style::Stylize;
|
||||
|
||||
#[cfg(test)]
|
||||
use codex_protocol::protocol::Event;
|
||||
#[cfg(test)]
|
||||
use codex_protocol::protocol::EventMsg;
|
||||
use ratatui::text::Line;
|
||||
use ratatui::widgets::Paragraph;
|
||||
use ratatui::widgets::Wrap;
|
||||
@@ -201,9 +204,8 @@ fn session_summary(
|
||||
})
|
||||
}
|
||||
|
||||
fn errors_for_cwd(cwd: &Path, response: &ListSkillsResponseEvent) -> Vec<SkillErrorInfo> {
|
||||
response
|
||||
.skills
|
||||
fn errors_for_cwd(cwd: &Path, skills_entries: &[SkillsListEntry]) -> Vec<SkillErrorInfo> {
|
||||
skills_entries
|
||||
.iter()
|
||||
.find(|entry| entry.cwd.as_path() == cwd)
|
||||
.map(|entry| entry.errors.clone())
|
||||
@@ -2274,7 +2276,7 @@ impl App {
|
||||
Err(err) => break Err(err),
|
||||
}
|
||||
}
|
||||
app_server_event = app_server.next_event(), if listen_for_app_server_events => {
|
||||
app_server_event = app_server.next_typed_event(), if listen_for_app_server_events => {
|
||||
match app_server_event {
|
||||
Some(event) => app.handle_app_server_event(&app_server, event).await,
|
||||
None => {
|
||||
@@ -2626,8 +2628,8 @@ impl App {
|
||||
AppEvent::CommitTick => {
|
||||
self.chat_widget.on_commit_tick();
|
||||
}
|
||||
AppEvent::CodexEvent(event) => {
|
||||
self.handle_codex_event_now(event);
|
||||
AppEvent::PushApprovalRequest(request) => {
|
||||
self.chat_widget.push_approval_request(request);
|
||||
}
|
||||
AppEvent::Exit(mode) => {
|
||||
return Ok(self.handle_exit_mode(app_server, mode).await);
|
||||
@@ -3810,6 +3812,15 @@ impl App {
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_skills_list_response_now(&mut self, skills_entries: Vec<SkillsListEntry>) {
|
||||
let cwd = self.chat_widget.config_ref().cwd.clone();
|
||||
let errors = errors_for_cwd(&cwd, &skills_entries);
|
||||
emit_skill_load_warnings(&self.app_event_tx, &errors);
|
||||
self.chat_widget
|
||||
.handle_skills_list_response(&skills_entries);
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
fn handle_codex_event_now(&mut self, event: Event) {
|
||||
let needs_refresh = matches!(
|
||||
event.msg,
|
||||
@@ -3822,11 +3833,6 @@ impl App {
|
||||
self.suppress_shutdown_complete = false;
|
||||
return;
|
||||
}
|
||||
if let EventMsg::ListSkillsResponse(response) = &event.msg {
|
||||
let cwd = self.chat_widget.config_ref().cwd.clone();
|
||||
let errors = errors_for_cwd(&cwd, response);
|
||||
emit_skill_load_warnings(&self.app_event_tx, &errors);
|
||||
}
|
||||
self.handle_backtrack_event(&event.msg);
|
||||
self.chat_widget.handle_codex_event(event);
|
||||
|
||||
@@ -3846,7 +3852,7 @@ impl App {
|
||||
return;
|
||||
}
|
||||
if let ThreadUpdate::ThreadRolledBack { num_turns } = update {
|
||||
self.handle_backtrack_event(&EventMsg::ThreadRolledBack(
|
||||
self.handle_backtrack_event(&codex_protocol::protocol::EventMsg::ThreadRolledBack(
|
||||
codex_protocol::protocol::ThreadRolledBackEvent { num_turns },
|
||||
));
|
||||
self.chat_widget
|
||||
|
||||
@@ -73,9 +73,6 @@ use codex_protocol::openai_models::ModelPreset;
|
||||
use codex_protocol::openai_models::ModelUpgrade;
|
||||
use codex_protocol::openai_models::ReasoningEffortPreset;
|
||||
use codex_protocol::protocol::AskForApproval;
|
||||
use codex_protocol::protocol::Event;
|
||||
use codex_protocol::protocol::EventMsg;
|
||||
use codex_protocol::protocol::ListSkillsResponseEvent;
|
||||
use codex_protocol::protocol::Op;
|
||||
use codex_protocol::protocol::ReviewTarget;
|
||||
use codex_protocol::protocol::SandboxPolicy;
|
||||
@@ -624,15 +621,7 @@ impl App {
|
||||
"skills/list",
|
||||
)
|
||||
.await?;
|
||||
let skills = serde_json::from_value(
|
||||
serde_json::to_value(response.data)
|
||||
.map_err(|err| format!("failed to encode skills/list response: {err}"))?,
|
||||
)
|
||||
.map_err(|err| format!("failed to convert skills/list response: {err}"))?;
|
||||
self.handle_codex_event_now(Event {
|
||||
id: String::new(),
|
||||
msg: EventMsg::ListSkillsResponse(ListSkillsResponseEvent { skills }),
|
||||
});
|
||||
self.handle_skills_list_response_now(response.data);
|
||||
}
|
||||
Op::RefreshMcpServers { config } => {
|
||||
let _: McpServerRefreshResponse = send_request_with_response(
|
||||
@@ -1027,12 +1016,6 @@ impl App {
|
||||
self.handle_server_notification(app_server_client, notification)
|
||||
.await;
|
||||
}
|
||||
InProcessServerEvent::LegacyNotification(notification) => {
|
||||
tracing::warn!(
|
||||
notification.method = %notification.method,
|
||||
"unexpected legacy notification after TUI app-server typed-event migration"
|
||||
);
|
||||
}
|
||||
InProcessServerEvent::ServerRequest(request) => {
|
||||
self.note_server_request(&request);
|
||||
match request.clone() {
|
||||
@@ -1095,6 +1078,7 @@ impl App {
|
||||
tracing::warn!("{err}");
|
||||
}
|
||||
}
|
||||
_ => unreachable!("legacy notifications are filtered by next_typed_event"),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -14,7 +14,6 @@ use codex_chatgpt::connectors::AppInfo;
|
||||
use codex_file_search::FileMatch;
|
||||
use codex_protocol::ThreadId;
|
||||
use codex_protocol::openai_models::ModelPreset;
|
||||
use codex_protocol::protocol::Event;
|
||||
use codex_utils_approval_presets::ApprovalPreset;
|
||||
|
||||
use crate::bottom_pane::ApprovalRequest;
|
||||
@@ -68,7 +67,7 @@ pub(crate) struct ConnectorsSnapshot {
|
||||
#[allow(clippy::large_enum_variant)]
|
||||
#[derive(Debug)]
|
||||
pub(crate) enum AppEvent {
|
||||
CodexEvent(Event),
|
||||
PushApprovalRequest(ApprovalRequest),
|
||||
/// Open the agent picker for switching active threads.
|
||||
OpenAgentPicker,
|
||||
/// Switch the active thread to the selected agent.
|
||||
|
||||
@@ -52,6 +52,8 @@ use crate::text_formatting::proper_join;
|
||||
use crate::thread_update::ThreadUpdate;
|
||||
use crate::version::CODEX_CLI_VERSION;
|
||||
use codex_app_server_protocol::ConfigLayerSource;
|
||||
use codex_app_server_protocol::SkillMetadata as AppServerSkillMetadata;
|
||||
use codex_app_server_protocol::SkillsListEntry;
|
||||
use codex_chatgpt::connectors;
|
||||
use codex_core::config::Config;
|
||||
use codex_core::config::Constrained;
|
||||
@@ -93,21 +95,28 @@ use codex_protocol::items::AgentMessageItem;
|
||||
use codex_protocol::models::MessagePhase;
|
||||
use codex_protocol::models::local_image_label_text;
|
||||
use codex_protocol::parse_command::ParsedCommand;
|
||||
#[cfg(test)]
|
||||
use codex_protocol::protocol::AgentMessageDeltaEvent;
|
||||
#[cfg(test)]
|
||||
use codex_protocol::protocol::AgentMessageEvent;
|
||||
#[cfg(test)]
|
||||
use codex_protocol::protocol::AgentReasoningDeltaEvent;
|
||||
#[cfg(test)]
|
||||
use codex_protocol::protocol::AgentReasoningEvent;
|
||||
#[cfg(test)]
|
||||
use codex_protocol::protocol::AgentReasoningRawContentDeltaEvent;
|
||||
#[cfg(test)]
|
||||
use codex_protocol::protocol::AgentReasoningRawContentEvent;
|
||||
use codex_protocol::protocol::ApplyPatchApprovalRequestEvent;
|
||||
#[cfg(test)]
|
||||
use codex_protocol::protocol::BackgroundEventEvent;
|
||||
use codex_protocol::protocol::CodexErrorInfo;
|
||||
#[cfg(test)]
|
||||
use codex_protocol::protocol::CollabAgentSpawnBeginEvent;
|
||||
use codex_protocol::protocol::CreditsSnapshot;
|
||||
use codex_protocol::protocol::DeprecationNoticeEvent;
|
||||
#[cfg(test)]
|
||||
use codex_protocol::protocol::ErrorEvent;
|
||||
use codex_protocol::protocol::Event;
|
||||
use codex_protocol::protocol::EventMsg;
|
||||
use codex_protocol::protocol::ExecApprovalRequestEvent;
|
||||
use codex_protocol::protocol::ExecCommandBeginEvent;
|
||||
use codex_protocol::protocol::ExecCommandEndEvent;
|
||||
@@ -119,7 +128,6 @@ use codex_protocol::protocol::GuardianAssessmentStatus;
|
||||
use codex_protocol::protocol::ImageGenerationBeginEvent;
|
||||
use codex_protocol::protocol::ImageGenerationEndEvent;
|
||||
use codex_protocol::protocol::ListCustomPromptsResponseEvent;
|
||||
use codex_protocol::protocol::ListSkillsResponseEvent;
|
||||
use codex_protocol::protocol::McpListToolsResponseEvent;
|
||||
use codex_protocol::protocol::McpStartupCompleteEvent;
|
||||
use codex_protocol::protocol::McpStartupStatus;
|
||||
@@ -131,18 +139,21 @@ use codex_protocol::protocol::PatchApplyBeginEvent;
|
||||
use codex_protocol::protocol::RateLimitSnapshot;
|
||||
use codex_protocol::protocol::ReviewRequest;
|
||||
use codex_protocol::protocol::ReviewTarget;
|
||||
use codex_protocol::protocol::SkillMetadata as ProtocolSkillMetadata;
|
||||
#[cfg(test)]
|
||||
use codex_protocol::protocol::StreamErrorEvent;
|
||||
use codex_protocol::protocol::TerminalInteractionEvent;
|
||||
use codex_protocol::protocol::TokenUsage;
|
||||
use codex_protocol::protocol::TokenUsageInfo;
|
||||
use codex_protocol::protocol::TurnAbortReason;
|
||||
#[cfg(test)]
|
||||
use codex_protocol::protocol::TurnCompleteEvent;
|
||||
#[cfg(test)]
|
||||
use codex_protocol::protocol::TurnDiffEvent;
|
||||
use codex_protocol::protocol::UndoCompletedEvent;
|
||||
use codex_protocol::protocol::UndoStartedEvent;
|
||||
use codex_protocol::protocol::UserMessageEvent;
|
||||
use codex_protocol::protocol::ViewImageToolCallEvent;
|
||||
#[cfg(test)]
|
||||
use codex_protocol::protocol::WarningEvent;
|
||||
use codex_protocol::protocol::WebSearchBeginEvent;
|
||||
use codex_protocol::protocol::WebSearchEndEvent;
|
||||
@@ -163,6 +174,13 @@ use ratatui::style::Modifier;
|
||||
use ratatui::style::Style;
|
||||
use ratatui::style::Stylize;
|
||||
use ratatui::text::Line;
|
||||
|
||||
#[cfg(test)]
|
||||
use codex_protocol::protocol::Event;
|
||||
#[cfg(test)]
|
||||
use codex_protocol::protocol::EventMsg;
|
||||
#[cfg(test)]
|
||||
use codex_protocol::protocol::ListSkillsResponseEvent;
|
||||
use ratatui::widgets::Paragraph;
|
||||
use ratatui::widgets::Wrap;
|
||||
use tokio::sync::mpsc::UnboundedSender;
|
||||
@@ -675,7 +693,7 @@ pub(crate) struct ChatWidget {
|
||||
running_commands: HashMap<String, RunningCommand>,
|
||||
pending_collab_spawn_requests: HashMap<String, multi_agents::SpawnRequestSummary>,
|
||||
suppressed_exec_calls: HashSet<String>,
|
||||
skills_all: Vec<ProtocolSkillMetadata>,
|
||||
skills_all: Vec<AppServerSkillMetadata>,
|
||||
skills_initial_state: Option<HashMap<PathBuf, bool>>,
|
||||
last_unified_wait: Option<UnifiedExecWaitState>,
|
||||
unified_exec_wait_streak: Option<UnifiedExecWaitStreak>,
|
||||
@@ -1386,6 +1404,7 @@ impl ChatWidget {
|
||||
Constrained::allow_only(event.sandbox_policy.clone());
|
||||
}
|
||||
self.config.approvals_reviewer = event.approvals_reviewer;
|
||||
#[cfg(test)]
|
||||
let initial_messages = event.initial_messages.clone();
|
||||
self.last_copyable_output = None;
|
||||
let forked_from_id = event.forked_from_id;
|
||||
@@ -1417,6 +1436,7 @@ impl ChatWidget {
|
||||
);
|
||||
self.apply_session_info_cell(session_info_cell);
|
||||
|
||||
#[cfg(test)]
|
||||
if let Some(messages) = initial_messages {
|
||||
self.replay_initial_messages(messages);
|
||||
}
|
||||
@@ -4241,23 +4261,16 @@ impl ChatWidget {
|
||||
}
|
||||
}
|
||||
SlashCommand::TestApproval => {
|
||||
use codex_protocol::protocol::EventMsg;
|
||||
use std::collections::HashMap;
|
||||
|
||||
use codex_protocol::protocol::ApplyPatchApprovalRequestEvent;
|
||||
use codex_protocol::protocol::FileChange;
|
||||
|
||||
self.app_event_tx.send(AppEvent::CodexEvent(Event {
|
||||
id: "1".to_string(),
|
||||
// msg: EventMsg::ExecApprovalRequest(ExecApprovalRequestEvent {
|
||||
// call_id: "1".to_string(),
|
||||
// command: vec!["git".into(), "apply".into()],
|
||||
// cwd: self.config.cwd.clone(),
|
||||
// reason: Some("test".to_string()),
|
||||
// }),
|
||||
msg: EventMsg::ApplyPatchApprovalRequest(ApplyPatchApprovalRequestEvent {
|
||||
call_id: "1".to_string(),
|
||||
turn_id: "turn-1".to_string(),
|
||||
self.app_event_tx.send(AppEvent::PushApprovalRequest(
|
||||
ApprovalRequest::ApplyPatch {
|
||||
thread_id: self.thread_id.unwrap_or_default(),
|
||||
thread_label: None,
|
||||
id: "1".to_string(),
|
||||
reason: None,
|
||||
changes: HashMap::from([
|
||||
(
|
||||
PathBuf::from("/tmp/test.txt"),
|
||||
@@ -4273,10 +4286,9 @@ impl ChatWidget {
|
||||
},
|
||||
),
|
||||
]),
|
||||
reason: None,
|
||||
grant_root: Some(PathBuf::from("/tmp")),
|
||||
}),
|
||||
}));
|
||||
cwd: PathBuf::from("/tmp"),
|
||||
},
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4815,6 +4827,7 @@ impl ChatWidget {
|
||||
self.request_redraw();
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
/// Replay a subset of initial events into the UI to seed the transcript when
|
||||
/// resuming an existing session. This approximates the live event flow and
|
||||
/// is intentionally conservative: only safe-to-replay items are rendered to
|
||||
@@ -4833,6 +4846,7 @@ impl ChatWidget {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub(crate) fn handle_codex_event(&mut self, event: Event) {
|
||||
let Event { id, msg } = event;
|
||||
self.dispatch_event_msg(Some(id), msg, None);
|
||||
@@ -5277,6 +5291,7 @@ impl ChatWidget {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
/// Dispatch a protocol `EventMsg` to the appropriate handler.
|
||||
///
|
||||
/// `id` is `Some` for live events and `None` for replayed events from
|
||||
@@ -8833,11 +8848,20 @@ impl ChatWidget {
|
||||
self.bottom_pane.set_custom_prompts(ev.custom_prompts);
|
||||
}
|
||||
|
||||
fn on_list_skills(&mut self, ev: ListSkillsResponseEvent) {
|
||||
self.set_skills_from_response(&ev);
|
||||
pub(crate) fn handle_skills_list_response(&mut self, entries: &[SkillsListEntry]) {
|
||||
self.set_skills_from_entries(entries);
|
||||
self.refresh_plugin_mentions();
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
fn on_list_skills(&mut self, ev: ListSkillsResponseEvent) {
|
||||
let entries: Vec<SkillsListEntry> = serde_json::from_value(
|
||||
serde_json::to_value(ev.skills).expect("legacy skills list should serialize"),
|
||||
)
|
||||
.expect("legacy skills list should map to app-server skills entries");
|
||||
self.handle_skills_list_response(&entries);
|
||||
}
|
||||
|
||||
pub(crate) fn on_connectors_loaded(
|
||||
&mut self,
|
||||
result: Result<ConnectorsSnapshot, String>,
|
||||
|
||||
@@ -12,6 +12,8 @@ use crate::bottom_pane::SkillsToggleView;
|
||||
use crate::bottom_pane::popup_consts::standard_popup_hint_line;
|
||||
use crate::skills_helpers::skill_description;
|
||||
use crate::skills_helpers::skill_display_name;
|
||||
use codex_app_server_protocol::SkillMetadata as AppServerSkillMetadata;
|
||||
use codex_app_server_protocol::SkillsListEntry;
|
||||
use codex_chatgpt::connectors::AppInfo;
|
||||
use codex_core::connectors::connector_mention_slug;
|
||||
use codex_core::mention_syntax::TOOL_MENTION_SIGIL;
|
||||
@@ -19,9 +21,7 @@ use codex_core::skills::model::SkillDependencies;
|
||||
use codex_core::skills::model::SkillInterface;
|
||||
use codex_core::skills::model::SkillMetadata;
|
||||
use codex_core::skills::model::SkillToolDependency;
|
||||
use codex_protocol::protocol::ListSkillsResponseEvent;
|
||||
use codex_protocol::protocol::SkillMetadata as ProtocolSkillMetadata;
|
||||
use codex_protocol::protocol::SkillsListEntry;
|
||||
use codex_protocol::protocol::SkillScope as ProtocolSkillScope;
|
||||
|
||||
impl ChatWidget {
|
||||
pub(crate) fn open_skills_list(&mut self) {
|
||||
@@ -75,7 +75,7 @@ impl ChatWidget {
|
||||
.skills_all
|
||||
.iter()
|
||||
.map(|skill| {
|
||||
let core_skill = protocol_skill_to_core(skill);
|
||||
let core_skill = app_server_skill_to_core(skill);
|
||||
let display_name = skill_display_name(&core_skill).to_string();
|
||||
let description = skill_description(&core_skill).to_string();
|
||||
let name = core_skill.name.clone();
|
||||
@@ -137,14 +137,14 @@ impl ChatWidget {
|
||||
);
|
||||
}
|
||||
|
||||
pub(crate) fn set_skills_from_response(&mut self, response: &ListSkillsResponseEvent) {
|
||||
let skills = skills_for_cwd(&self.config.cwd, &response.skills);
|
||||
pub(crate) fn set_skills_from_entries(&mut self, entries: &[SkillsListEntry]) {
|
||||
let skills = skills_for_cwd(&self.config.cwd, entries);
|
||||
self.skills_all = skills;
|
||||
self.set_skills(Some(enabled_skills_for_mentions(&self.skills_all)));
|
||||
}
|
||||
}
|
||||
|
||||
fn skills_for_cwd(cwd: &Path, skills_entries: &[SkillsListEntry]) -> Vec<ProtocolSkillMetadata> {
|
||||
fn skills_for_cwd(cwd: &Path, skills_entries: &[SkillsListEntry]) -> Vec<AppServerSkillMetadata> {
|
||||
skills_entries
|
||||
.iter()
|
||||
.find(|entry| entry.cwd.as_path() == cwd)
|
||||
@@ -152,15 +152,15 @@ fn skills_for_cwd(cwd: &Path, skills_entries: &[SkillsListEntry]) -> Vec<Protoco
|
||||
.unwrap_or_default()
|
||||
}
|
||||
|
||||
fn enabled_skills_for_mentions(skills: &[ProtocolSkillMetadata]) -> Vec<SkillMetadata> {
|
||||
fn enabled_skills_for_mentions(skills: &[AppServerSkillMetadata]) -> Vec<SkillMetadata> {
|
||||
skills
|
||||
.iter()
|
||||
.filter(|skill| skill.enabled)
|
||||
.map(protocol_skill_to_core)
|
||||
.map(app_server_skill_to_core)
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn protocol_skill_to_core(skill: &ProtocolSkillMetadata) -> SkillMetadata {
|
||||
fn app_server_skill_to_core(skill: &AppServerSkillMetadata) -> SkillMetadata {
|
||||
SkillMetadata {
|
||||
name: skill.name.clone(),
|
||||
description: skill.description.clone(),
|
||||
@@ -194,7 +194,16 @@ fn protocol_skill_to_core(skill: &ProtocolSkillMetadata) -> SkillMetadata {
|
||||
permission_profile: None,
|
||||
managed_network_override: None,
|
||||
path_to_skills_md: skill.path.clone(),
|
||||
scope: skill.scope,
|
||||
scope: protocol_skill_scope(skill.scope),
|
||||
}
|
||||
}
|
||||
|
||||
fn protocol_skill_scope(scope: codex_app_server_protocol::SkillScope) -> ProtocolSkillScope {
|
||||
match scope {
|
||||
codex_app_server_protocol::SkillScope::User => ProtocolSkillScope::User,
|
||||
codex_app_server_protocol::SkillScope::Repo => ProtocolSkillScope::Repo,
|
||||
codex_app_server_protocol::SkillScope::System => ProtocolSkillScope::System,
|
||||
codex_app_server_protocol::SkillScope::Admin => ProtocolSkillScope::Admin,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -448,7 +448,7 @@ pub(crate) async fn run_onboarding_app(
|
||||
&mut onboarding_screen,
|
||||
).await;
|
||||
}
|
||||
app_server_event = app_server.next_event() => {
|
||||
app_server_event = app_server.next_typed_event() => {
|
||||
if let Some(event) = app_server_event {
|
||||
handle_app_server_event(
|
||||
event,
|
||||
@@ -595,9 +595,9 @@ async fn handle_app_server_event(
|
||||
auth_widget.apply_login_completed(payload);
|
||||
}
|
||||
}
|
||||
InProcessServerEvent::LegacyNotification(_)
|
||||
| InProcessServerEvent::ServerNotification(_)
|
||||
InProcessServerEvent::ServerNotification(_)
|
||||
| InProcessServerEvent::ServerRequest(_)
|
||||
| InProcessServerEvent::Lagged { .. } => {}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -125,9 +125,7 @@ pub(crate) fn log_inbound_app_event(event: &AppEvent) {
|
||||
}
|
||||
|
||||
match event {
|
||||
AppEvent::CodexEvent(ev) => {
|
||||
write_record("to_tui", "codex_event", ev);
|
||||
}
|
||||
AppEvent::PushApprovalRequest(_) => {}
|
||||
AppEvent::NewSession => {
|
||||
let value = json!({
|
||||
"ts": now_ts(),
|
||||
|
||||
Reference in New Issue
Block a user