mirror of
https://github.com/openai/codex.git
synced 2026-11-13 14:46:14 +00:00
Box app-server event payloads (#35854)
## What changed - Store `ServerNotification` and `ServerRequest` payloads behind `Box` in `AppServerEvent`, `InProcessServerEvent`, and TUI `ThreadBufferedEvent` values. - Update app-server delivery, TUI routing and replay, exec, onboarding, and tests to borrow or consume the boxed payloads as appropriate. GitOrigin-RevId: 7d0dab9aba47487114128ce55c914af5bb46c5b5
This commit is contained in:
1 parent
8707a35113
commit
07490c7523
16 files changed
+267
-190
No files matched your search
@@ -97,8 +97,8 @@ pub type RequestResult = std::result::Result<JsonRpcResult, JSONRPCErrorError>;
|
|||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub enum AppServerEvent {
|
pub enum AppServerEvent {
|
||||||
Lagged { skipped: usize },
|
Lagged { skipped: usize },
|
||||||
ServerNotification(ServerNotification),
|
ServerNotification(Box<ServerNotification>),
|
||||||
ServerRequest(ServerRequest),
|
ServerRequest(Box<ServerRequest>),
|
||||||
Disconnected { message: String },
|
Disconnected { message: String },
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -205,7 +205,7 @@ where
|
|||||||
*skipped_events = skipped_events.saturating_add(1);
|
*skipped_events = skipped_events.saturating_add(1);
|
||||||
warn!("dropping in-process app-server event because consumer queue is full");
|
warn!("dropping in-process app-server event because consumer queue is full");
|
||||||
if let InProcessServerEvent::ServerRequest(request) = event {
|
if let InProcessServerEvent::ServerRequest(request) = event {
|
||||||
reject_server_request(request);
|
reject_server_request(*request);
|
||||||
}
|
}
|
||||||
return ForwardEventResult::Continue;
|
return ForwardEventResult::Continue;
|
||||||
}
|
}
|
||||||
@@ -231,7 +231,7 @@ where
|
|||||||
*skipped_events = skipped_events.saturating_add(1);
|
*skipped_events = skipped_events.saturating_add(1);
|
||||||
warn!("dropping in-process app-server event because consumer queue is full");
|
warn!("dropping in-process app-server event because consumer queue is full");
|
||||||
if let InProcessServerEvent::ServerRequest(request) = event {
|
if let InProcessServerEvent::ServerRequest(request) = event {
|
||||||
reject_server_request(request);
|
reject_server_request(*request);
|
||||||
}
|
}
|
||||||
ForwardEventResult::Continue
|
ForwardEventResult::Continue
|
||||||
}
|
}
|
||||||
@@ -522,9 +522,9 @@ impl InProcessAppServerClient {
|
|||||||
let Some(event) = event else {
|
let Some(event) = event else {
|
||||||
break;
|
break;
|
||||||
};
|
};
|
||||||
if let InProcessServerEvent::ServerRequest(
|
if let InProcessServerEvent::ServerRequest(request) = &event
|
||||||
ServerRequest::ChatgptAuthTokensRefresh { request_id, .. }
|
&& let ServerRequest::ChatgptAuthTokensRefresh { request_id, .. } =
|
||||||
) = &event
|
request.as_ref()
|
||||||
{
|
{
|
||||||
let send_result = request_sender.fail_server_request(
|
let send_result = request_sender.fail_server_request(
|
||||||
request_id.clone(),
|
request_id.clone(),
|
||||||
@@ -1332,9 +1332,9 @@ mod tests {
|
|||||||
async fn forward_in_process_event_preserves_transcript_notifications_under_backpressure() {
|
async fn forward_in_process_event_preserves_transcript_notifications_under_backpressure() {
|
||||||
let (event_tx, mut event_rx) = mpsc::channel(1);
|
let (event_tx, mut event_rx) = mpsc::channel(1);
|
||||||
event_tx
|
event_tx
|
||||||
.send(InProcessServerEvent::ServerNotification(
|
.send(InProcessServerEvent::ServerNotification(Box::new(
|
||||||
command_execution_output_delta_notification("stdout-1"),
|
command_execution_output_delta_notification("stdout-1"),
|
||||||
))
|
)))
|
||||||
.await
|
.await
|
||||||
.expect("initial event should enqueue");
|
.expect("initial event should enqueue");
|
||||||
|
|
||||||
@@ -1342,8 +1342,8 @@ mod tests {
|
|||||||
let result = forward_in_process_event(
|
let result = forward_in_process_event(
|
||||||
&event_tx,
|
&event_tx,
|
||||||
&mut skipped_events,
|
&mut skipped_events,
|
||||||
InProcessServerEvent::ServerNotification(command_execution_output_delta_notification(
|
InProcessServerEvent::ServerNotification(Box::new(
|
||||||
"stdout-2",
|
command_execution_output_delta_notification("stdout-2"),
|
||||||
)),
|
)),
|
||||||
|_| {},
|
|_| {},
|
||||||
)
|
)
|
||||||
@@ -1372,7 +1372,7 @@ mod tests {
|
|||||||
let result = forward_in_process_event(
|
let result = forward_in_process_event(
|
||||||
&event_tx,
|
&event_tx,
|
||||||
&mut skipped_events,
|
&mut skipped_events,
|
||||||
InProcessServerEvent::ServerNotification(notification),
|
InProcessServerEvent::ServerNotification(Box::new(notification)),
|
||||||
|_| {},
|
|_| {},
|
||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
@@ -1385,9 +1385,12 @@ mod tests {
|
|||||||
.expect("receiver task should join successfully");
|
.expect("receiver task should join successfully");
|
||||||
assert!(matches!(
|
assert!(matches!(
|
||||||
&events[0],
|
&events[0],
|
||||||
InProcessServerEvent::ServerNotification(
|
InProcessServerEvent::ServerNotification(notification)
|
||||||
|
if matches!(
|
||||||
|
notification.as_ref(),
|
||||||
ServerNotification::CommandExecutionOutputDelta(notification)
|
ServerNotification::CommandExecutionOutputDelta(notification)
|
||||||
) if notification.delta == "stdout-1"
|
if notification.delta == "stdout-1"
|
||||||
|
)
|
||||||
));
|
));
|
||||||
assert!(matches!(
|
assert!(matches!(
|
||||||
&events[1],
|
&events[1],
|
||||||
@@ -1395,24 +1398,35 @@ mod tests {
|
|||||||
));
|
));
|
||||||
assert!(matches!(
|
assert!(matches!(
|
||||||
&events[2],
|
&events[2],
|
||||||
InProcessServerEvent::ServerNotification(ServerNotification::AgentMessageDelta(
|
InProcessServerEvent::ServerNotification(notification)
|
||||||
notification
|
if matches!(
|
||||||
)) if notification.delta == "hello"
|
notification.as_ref(),
|
||||||
|
ServerNotification::AgentMessageDelta(notification)
|
||||||
|
if notification.delta == "hello"
|
||||||
|
)
|
||||||
));
|
));
|
||||||
assert!(matches!(
|
assert!(matches!(
|
||||||
&events[3],
|
&events[3],
|
||||||
InProcessServerEvent::ServerNotification(ServerNotification::ItemCompleted(
|
InProcessServerEvent::ServerNotification(notification)
|
||||||
notification
|
if matches!(
|
||||||
)) if matches!(
|
notification.as_ref(),
|
||||||
|
ServerNotification::ItemCompleted(notification)
|
||||||
|
if matches!(
|
||||||
¬ification.item,
|
¬ification.item,
|
||||||
codex_app_server_protocol::ThreadItem::AgentMessage { text, .. } if text == "hello"
|
codex_app_server_protocol::ThreadItem::AgentMessage { text, .. }
|
||||||
|
if text == "hello"
|
||||||
|
)
|
||||||
)
|
)
|
||||||
));
|
));
|
||||||
assert!(matches!(
|
assert!(matches!(
|
||||||
&events[4],
|
&events[4],
|
||||||
InProcessServerEvent::ServerNotification(ServerNotification::TurnCompleted(
|
InProcessServerEvent::ServerNotification(notification)
|
||||||
notification
|
if matches!(
|
||||||
)) if notification.turn.status == codex_app_server_protocol::TurnStatus::Completed
|
notification.as_ref(),
|
||||||
|
ServerNotification::TurnCompleted(notification)
|
||||||
|
if notification.turn.status
|
||||||
|
== codex_app_server_protocol::TurnStatus::Completed
|
||||||
|
)
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1753,7 +1767,8 @@ mod tests {
|
|||||||
let event = client.next_event().await.expect("event should arrive");
|
let event = client.next_event().await.expect("event should arrive");
|
||||||
assert!(matches!(
|
assert!(matches!(
|
||||||
event,
|
event,
|
||||||
AppServerEvent::ServerNotification(ServerNotification::AccountUpdated(_))
|
AppServerEvent::ServerNotification(notification)
|
||||||
|
if matches!(notification.as_ref(), ServerNotification::AccountUpdated(_))
|
||||||
));
|
));
|
||||||
|
|
||||||
client.shutdown().await.expect("shutdown should complete");
|
client.shutdown().await.expect("shutdown should complete");
|
||||||
@@ -1799,9 +1814,12 @@ mod tests {
|
|||||||
.expect("event stream should stay open");
|
.expect("event stream should stay open");
|
||||||
assert!(matches!(
|
assert!(matches!(
|
||||||
first_event,
|
first_event,
|
||||||
AppServerEvent::ServerNotification(ServerNotification::CommandExecutionOutputDelta(
|
AppServerEvent::ServerNotification(notification)
|
||||||
notification
|
if matches!(
|
||||||
)) if notification.delta == "stdout-1"
|
notification.as_ref(),
|
||||||
|
ServerNotification::CommandExecutionOutputDelta(notification)
|
||||||
|
if notification.delta == "stdout-1"
|
||||||
|
)
|
||||||
));
|
));
|
||||||
|
|
||||||
let mut remaining_events = Vec::new();
|
let mut remaining_events = Vec::new();
|
||||||
@@ -1818,31 +1836,32 @@ mod tests {
|
|||||||
for event in &remaining_events {
|
for event in &remaining_events {
|
||||||
match event {
|
match event {
|
||||||
AppServerEvent::Lagged { skipped: 1 } => {}
|
AppServerEvent::Lagged { skipped: 1 } => {}
|
||||||
AppServerEvent::ServerNotification(
|
AppServerEvent::ServerNotification(notification) => match notification.as_ref() {
|
||||||
ServerNotification::CommandExecutionOutputDelta(notification),
|
ServerNotification::CommandExecutionOutputDelta(notification)
|
||||||
) if notification.delta == "stdout-2" => {}
|
if notification.delta == "stdout-2" => {}
|
||||||
AppServerEvent::ServerNotification(ServerNotification::AgentMessageDelta(
|
ServerNotification::AgentMessageDelta(notification)
|
||||||
notification,
|
if notification.delta == "hello" =>
|
||||||
)) if notification.delta == "hello" => {
|
{
|
||||||
transcript_event_names.push("agent_message_delta");
|
transcript_event_names.push("agent_message_delta");
|
||||||
}
|
}
|
||||||
AppServerEvent::ServerNotification(ServerNotification::ItemCompleted(
|
ServerNotification::ItemCompleted(notification)
|
||||||
notification,
|
if matches!(
|
||||||
)) if matches!(
|
|
||||||
¬ification.item,
|
¬ification.item,
|
||||||
codex_app_server_protocol::ThreadItem::AgentMessage { text, .. } if text == "hello"
|
codex_app_server_protocol::ThreadItem::AgentMessage { text, .. }
|
||||||
|
if text == "hello"
|
||||||
) =>
|
) =>
|
||||||
{
|
{
|
||||||
transcript_event_names.push("item_completed");
|
transcript_event_names.push("item_completed");
|
||||||
}
|
}
|
||||||
AppServerEvent::ServerNotification(ServerNotification::TurnCompleted(
|
ServerNotification::TurnCompleted(notification)
|
||||||
notification,
|
if notification.turn.status
|
||||||
)) if notification.turn.status
|
|
||||||
== codex_app_server_protocol::TurnStatus::Completed =>
|
== codex_app_server_protocol::TurnStatus::Completed =>
|
||||||
{
|
{
|
||||||
transcript_event_names.push("turn_completed");
|
transcript_event_names.push("turn_completed");
|
||||||
}
|
}
|
||||||
_ => panic!("unexpected remaining event: {event:?}"),
|
_ => panic!("unexpected remaining event: {event:?}"),
|
||||||
|
},
|
||||||
|
_ => panic!("unexpected remaining event: {event:?}"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
@@ -2103,7 +2122,7 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn event_requires_delivery_marks_transcript_and_terminal_events() {
|
fn event_requires_delivery_marks_transcript_and_terminal_events() {
|
||||||
assert!(event_requires_delivery(
|
assert!(event_requires_delivery(
|
||||||
&InProcessServerEvent::ServerNotification(
|
&InProcessServerEvent::ServerNotification(Box::new(
|
||||||
codex_app_server_protocol::ServerNotification::TurnCompleted(
|
codex_app_server_protocol::ServerNotification::TurnCompleted(
|
||||||
codex_app_server_protocol::TurnCompletedNotification {
|
codex_app_server_protocol::TurnCompletedNotification {
|
||||||
thread_id: "thread".to_string(),
|
thread_id: "thread".to_string(),
|
||||||
@@ -2119,10 +2138,10 @@ mod tests {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
)
|
))
|
||||||
));
|
));
|
||||||
assert!(event_requires_delivery(
|
assert!(event_requires_delivery(
|
||||||
&InProcessServerEvent::ServerNotification(
|
&InProcessServerEvent::ServerNotification(Box::new(
|
||||||
codex_app_server_protocol::ServerNotification::AgentMessageDelta(
|
codex_app_server_protocol::ServerNotification::AgentMessageDelta(
|
||||||
codex_app_server_protocol::AgentMessageDeltaNotification {
|
codex_app_server_protocol::AgentMessageDeltaNotification {
|
||||||
thread_id: "thread".to_string(),
|
thread_id: "thread".to_string(),
|
||||||
@@ -2131,10 +2150,10 @@ mod tests {
|
|||||||
delta: "hello".to_string(),
|
delta: "hello".to_string(),
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
)
|
))
|
||||||
));
|
));
|
||||||
assert!(event_requires_delivery(
|
assert!(event_requires_delivery(
|
||||||
&InProcessServerEvent::ServerNotification(
|
&InProcessServerEvent::ServerNotification(Box::new(
|
||||||
codex_app_server_protocol::ServerNotification::ItemCompleted(
|
codex_app_server_protocol::ServerNotification::ItemCompleted(
|
||||||
codex_app_server_protocol::ItemCompletedNotification {
|
codex_app_server_protocol::ItemCompletedNotification {
|
||||||
thread_id: "thread".to_string(),
|
thread_id: "thread".to_string(),
|
||||||
@@ -2148,23 +2167,23 @@ mod tests {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
)
|
))
|
||||||
));
|
));
|
||||||
assert!(event_requires_delivery(
|
assert!(event_requires_delivery(
|
||||||
&InProcessServerEvent::ServerNotification(
|
&InProcessServerEvent::ServerNotification(Box::new(
|
||||||
codex_app_server_protocol::ServerNotification::ExternalAgentConfigImportCompleted(
|
codex_app_server_protocol::ServerNotification::ExternalAgentConfigImportCompleted(
|
||||||
codex_app_server_protocol::ExternalAgentConfigImportCompletedNotification {
|
codex_app_server_protocol::ExternalAgentConfigImportCompletedNotification {
|
||||||
import_id: "import".to_string(),
|
import_id: "import".to_string(),
|
||||||
item_type_results: Vec::new(),
|
item_type_results: Vec::new(),
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
)
|
))
|
||||||
));
|
));
|
||||||
assert!(!event_requires_delivery(&InProcessServerEvent::Lagged {
|
assert!(!event_requires_delivery(&InProcessServerEvent::Lagged {
|
||||||
skipped: 1
|
skipped: 1
|
||||||
}));
|
}));
|
||||||
assert!(!event_requires_delivery(
|
assert!(!event_requires_delivery(
|
||||||
&InProcessServerEvent::ServerNotification(
|
&InProcessServerEvent::ServerNotification(Box::new(
|
||||||
codex_app_server_protocol::ServerNotification::CommandExecutionOutputDelta(
|
codex_app_server_protocol::ServerNotification::CommandExecutionOutputDelta(
|
||||||
codex_app_server_protocol::CommandExecutionOutputDeltaNotification {
|
codex_app_server_protocol::CommandExecutionOutputDeltaNotification {
|
||||||
thread_id: "thread".to_string(),
|
thread_id: "thread".to_string(),
|
||||||
@@ -2173,7 +2192,7 @@ mod tests {
|
|||||||
delta: "stdout".to_string(),
|
delta: "stdout".to_string(),
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
)
|
))
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -348,7 +348,7 @@ impl RemoteAppServerClient {
|
|||||||
Ok(request) => {
|
Ok(request) => {
|
||||||
if let Err(err) = deliver_event(
|
if let Err(err) = deliver_event(
|
||||||
&event_tx,
|
&event_tx,
|
||||||
AppServerEvent::ServerRequest(request),
|
AppServerEvent::ServerRequest(Box::new(request)),
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
warn!(%err, "failed to deliver remote app-server server request");
|
warn!(%err, "failed to deliver remote app-server server request");
|
||||||
@@ -862,7 +862,8 @@ where
|
|||||||
let method = request.method.clone();
|
let method = request.method.clone();
|
||||||
match ServerRequest::try_from(request) {
|
match ServerRequest::try_from(request) {
|
||||||
Ok(request) => {
|
Ok(request) => {
|
||||||
pending_events.push(AppServerEvent::ServerRequest(request));
|
pending_events
|
||||||
|
.push(AppServerEvent::ServerRequest(Box::new(request)));
|
||||||
}
|
}
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
warn!(%err, method, "rejecting unknown remote app-server request during initialize");
|
warn!(%err, method, "rejecting unknown remote app-server request during initialize");
|
||||||
@@ -940,7 +941,7 @@ where
|
|||||||
|
|
||||||
fn app_server_event_from_notification(notification: JSONRPCNotification) -> Option<AppServerEvent> {
|
fn app_server_event_from_notification(notification: JSONRPCNotification) -> Option<AppServerEvent> {
|
||||||
match ServerNotification::try_from(notification) {
|
match ServerNotification::try_from(notification) {
|
||||||
Ok(notification) => Some(AppServerEvent::ServerNotification(notification)),
|
Ok(notification) => Some(AppServerEvent::ServerNotification(Box::new(notification))),
|
||||||
Err(_) => None,
|
Err(_) => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -160,9 +160,9 @@ pub struct InProcessStartArgs {
|
|||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub enum InProcessServerEvent {
|
pub enum InProcessServerEvent {
|
||||||
/// Server request that requires client response/rejection.
|
/// Server request that requires client response/rejection.
|
||||||
ServerRequest(ServerRequest),
|
ServerRequest(Box<ServerRequest>),
|
||||||
/// App-server notification directed to the embedded client.
|
/// App-server notification directed to the embedded client.
|
||||||
ServerNotification(ServerNotification),
|
ServerNotification(Box<ServerNotification>),
|
||||||
/// Indicates one or more events were dropped due to backpressure.
|
/// Indicates one or more events were dropped due to backpressure.
|
||||||
Lagged { skipped: usize },
|
Lagged { skipped: usize },
|
||||||
}
|
}
|
||||||
@@ -663,7 +663,7 @@ async fn start_uninitialized(args: InProcessStartArgs) -> IoResult<InProcessClie
|
|||||||
// Send directly to avoid cloning; on failure the
|
// Send directly to avoid cloning; on failure the
|
||||||
// original value is returned inside the error.
|
// original value is returned inside the error.
|
||||||
if let Err(send_error) = event_tx
|
if let Err(send_error) = event_tx
|
||||||
.try_send(InProcessServerEvent::ServerRequest(request))
|
.try_send(InProcessServerEvent::ServerRequest(Box::new(request)))
|
||||||
{
|
{
|
||||||
let (error, inner) = match send_error {
|
let (error, inner) = match send_error {
|
||||||
mpsc::error::TrySendError::Full(inner) => (
|
mpsc::error::TrySendError::Full(inner) => (
|
||||||
@@ -695,14 +695,18 @@ async fn start_uninitialized(args: InProcessStartArgs) -> IoResult<InProcessClie
|
|||||||
let notification = envelope.notification;
|
let notification = envelope.notification;
|
||||||
if server_notification_requires_delivery(¬ification) {
|
if server_notification_requires_delivery(¬ification) {
|
||||||
if event_tx
|
if event_tx
|
||||||
.send(InProcessServerEvent::ServerNotification(notification))
|
.send(InProcessServerEvent::ServerNotification(Box::new(
|
||||||
|
notification,
|
||||||
|
)))
|
||||||
.await
|
.await
|
||||||
.is_err()
|
.is_err()
|
||||||
{
|
{
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
} else if let Err(send_error) =
|
} else if let Err(send_error) =
|
||||||
event_tx.try_send(InProcessServerEvent::ServerNotification(notification))
|
event_tx.try_send(InProcessServerEvent::ServerNotification(
|
||||||
|
Box::new(notification),
|
||||||
|
))
|
||||||
{
|
{
|
||||||
match send_error {
|
match send_error {
|
||||||
mpsc::error::TrySendError::Full(_) => {
|
mpsc::error::TrySendError::Full(_) => {
|
||||||
|
|||||||
@@ -191,9 +191,8 @@ async fn thread_delete_with_non_local_thread_store_does_not_create_local_persist
|
|||||||
let Some(event) = client.next_event().await else {
|
let Some(event) = client.next_event().await else {
|
||||||
anyhow::bail!("in-process app-server stopped before turn/completed");
|
anyhow::bail!("in-process app-server stopped before turn/completed");
|
||||||
};
|
};
|
||||||
if let InProcessServerEvent::ServerNotification(ServerNotification::TurnCompleted(
|
if let InProcessServerEvent::ServerNotification(notification) = event
|
||||||
completed,
|
&& let ServerNotification::TurnCompleted(completed) = notification.as_ref()
|
||||||
)) = event
|
|
||||||
&& completed.thread_id == thread.id
|
&& completed.thread_id == thread.id
|
||||||
{
|
{
|
||||||
return Ok::<(), anyhow::Error>(());
|
return Ok::<(), anyhow::Error>(());
|
||||||
@@ -332,9 +331,8 @@ async fn cold_thread_resume_reuses_non_local_history_probe() -> Result<()> {
|
|||||||
let Some(event) = client.next_event().await else {
|
let Some(event) = client.next_event().await else {
|
||||||
anyhow::bail!("in-process app-server stopped before turn/completed");
|
anyhow::bail!("in-process app-server stopped before turn/completed");
|
||||||
};
|
};
|
||||||
if let InProcessServerEvent::ServerNotification(ServerNotification::TurnCompleted(
|
if let InProcessServerEvent::ServerNotification(notification) = event
|
||||||
completed,
|
&& let ServerNotification::TurnCompleted(completed) = notification.as_ref()
|
||||||
)) = event
|
|
||||||
&& completed.thread_id == thread.id
|
&& completed.thread_id == thread.id
|
||||||
{
|
{
|
||||||
return Ok::<(), anyhow::Error>(());
|
return Ok::<(), anyhow::Error>(());
|
||||||
|
|||||||
@@ -997,9 +997,10 @@ async fn run_exec_session(args: ExecRunArgs) -> anyhow::Result<()> {
|
|||||||
|
|
||||||
match server_event {
|
match server_event {
|
||||||
InProcessServerEvent::ServerRequest(request) => {
|
InProcessServerEvent::ServerRequest(request) => {
|
||||||
handle_server_request(&client, request, &mut error_seen).await;
|
handle_server_request(&client, *request, &mut error_seen).await;
|
||||||
}
|
}
|
||||||
InProcessServerEvent::ServerNotification(mut notification) => {
|
InProcessServerEvent::ServerNotification(notification) => {
|
||||||
|
let mut notification = *notification;
|
||||||
if let ServerNotification::Error(payload) = ¬ification {
|
if let ServerNotification::Error(payload) = ¬ification {
|
||||||
if payload.thread_id == primary_thread_id_for_requests
|
if payload.thread_id == primary_thread_id_for_requests
|
||||||
&& payload.turn_id == task_id
|
&& payload.turn_id == task_id
|
||||||
|
|||||||
@@ -85,14 +85,12 @@ impl AgentStatusThreadPreview {
|
|||||||
let mut activity = Vec::new();
|
let mut activity = Vec::new();
|
||||||
for event in events {
|
for event in events {
|
||||||
let item = match event {
|
let item = match event {
|
||||||
ThreadBufferedEvent::Notification(ServerNotification::ItemCompleted(event)) => {
|
ThreadBufferedEvent::Notification(notification) => match notification.as_ref() {
|
||||||
&event.item
|
ServerNotification::ItemCompleted(event) => &event.item,
|
||||||
}
|
ServerNotification::ItemStarted(event) => &event.item,
|
||||||
ThreadBufferedEvent::Notification(ServerNotification::ItemStarted(event)) => {
|
_ => continue,
|
||||||
&event.item
|
},
|
||||||
}
|
ThreadBufferedEvent::Request(_)
|
||||||
ThreadBufferedEvent::Notification(_)
|
|
||||||
| ThreadBufferedEvent::Request(_)
|
|
||||||
| ThreadBufferedEvent::HistoryEntryResponse(_)
|
| ThreadBufferedEvent::HistoryEntryResponse(_)
|
||||||
| ThreadBufferedEvent::FeedbackSubmission(_) => continue,
|
| ThreadBufferedEvent::FeedbackSubmission(_) => continue,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -44,11 +44,11 @@ impl App {
|
|||||||
self.chat_widget.finish_mcp_startup_after_lag();
|
self.chat_widget.finish_mcp_startup_after_lag();
|
||||||
}
|
}
|
||||||
AppServerEvent::ServerNotification(notification) => {
|
AppServerEvent::ServerNotification(notification) => {
|
||||||
self.handle_server_notification_event(app_server_client, notification)
|
self.handle_server_notification_event(app_server_client, *notification)
|
||||||
.await;
|
.await;
|
||||||
}
|
}
|
||||||
AppServerEvent::ServerRequest(request) => {
|
AppServerEvent::ServerRequest(request) => {
|
||||||
self.handle_server_request_event(app_server_client, request)
|
self.handle_server_request_event(app_server_client, *request)
|
||||||
.await;
|
.await;
|
||||||
}
|
}
|
||||||
AppServerEvent::Disconnected { message } => {
|
AppServerEvent::Disconnected { message } => {
|
||||||
|
|||||||
@@ -626,7 +626,7 @@ impl App {
|
|||||||
{
|
{
|
||||||
guard
|
guard
|
||||||
.pending_interactive_replay
|
.pending_interactive_replay
|
||||||
.note_evicted_server_request(request);
|
.note_evicted_server_request(request.as_ref());
|
||||||
}
|
}
|
||||||
guard.active
|
guard.active
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -702,8 +702,12 @@ mod tests {
|
|||||||
assert_eq!(snapshot.events.len(), 1);
|
assert_eq!(snapshot.events.len(), 1);
|
||||||
assert!(matches!(
|
assert!(matches!(
|
||||||
snapshot.events.first(),
|
snapshot.events.first(),
|
||||||
Some(ThreadBufferedEvent::Request(ServerRequest::ToolRequestUserInput { params, .. }))
|
Some(ThreadBufferedEvent::Request(request))
|
||||||
|
if matches!(
|
||||||
|
request.as_ref(),
|
||||||
|
ServerRequest::ToolRequestUserInput { params, .. }
|
||||||
if params.item_id == "call-1"
|
if params.item_id == "call-1"
|
||||||
|
)
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -738,7 +742,11 @@ mod tests {
|
|||||||
snapshot.events.iter().all(|event| {
|
snapshot.events.iter().all(|event| {
|
||||||
!matches!(
|
!matches!(
|
||||||
event,
|
event,
|
||||||
ThreadBufferedEvent::Request(ServerRequest::ToolRequestUserInput { .. })
|
ThreadBufferedEvent::Request(request)
|
||||||
|
if matches!(
|
||||||
|
request.as_ref(),
|
||||||
|
ServerRequest::ToolRequestUserInput { .. }
|
||||||
|
)
|
||||||
)
|
)
|
||||||
}),
|
}),
|
||||||
"server-resolved request_user_input prompt should not replay on thread switch"
|
"server-resolved request_user_input prompt should not replay on thread switch"
|
||||||
@@ -783,7 +791,9 @@ mod tests {
|
|||||||
snapshot.events.iter().all(|event| {
|
snapshot.events.iter().all(|event| {
|
||||||
!matches!(
|
!matches!(
|
||||||
event,
|
event,
|
||||||
ThreadBufferedEvent::Request(
|
ThreadBufferedEvent::Request(request)
|
||||||
|
if matches!(
|
||||||
|
request.as_ref(),
|
||||||
ServerRequest::CommandExecutionRequestApproval { .. }
|
ServerRequest::CommandExecutionRequestApproval { .. }
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
@@ -810,8 +820,12 @@ mod tests {
|
|||||||
assert_eq!(snapshot.events.len(), 1);
|
assert_eq!(snapshot.events.len(), 1);
|
||||||
assert!(matches!(
|
assert!(matches!(
|
||||||
snapshot.events.first(),
|
snapshot.events.first(),
|
||||||
Some(ThreadBufferedEvent::Request(ServerRequest::ToolRequestUserInput { params, .. }))
|
Some(ThreadBufferedEvent::Request(request))
|
||||||
|
if matches!(
|
||||||
|
request.as_ref(),
|
||||||
|
ServerRequest::ToolRequestUserInput { params, .. }
|
||||||
if params.item_id == "call-2"
|
if params.item_id == "call-2"
|
||||||
|
)
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -832,8 +846,12 @@ mod tests {
|
|||||||
assert_eq!(snapshot.events.len(), 1);
|
assert_eq!(snapshot.events.len(), 1);
|
||||||
assert!(matches!(
|
assert!(matches!(
|
||||||
snapshot.events.first(),
|
snapshot.events.first(),
|
||||||
Some(ThreadBufferedEvent::Request(ServerRequest::ToolRequestUserInput { params, .. }))
|
Some(ThreadBufferedEvent::Request(request))
|
||||||
|
if matches!(
|
||||||
|
request.as_ref(),
|
||||||
|
ServerRequest::ToolRequestUserInput { params, .. }
|
||||||
if params.item_id == "call-2"
|
if params.item_id == "call-2"
|
||||||
|
)
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -869,8 +887,12 @@ mod tests {
|
|||||||
assert!(snapshot.events.iter().all(|event| {
|
assert!(snapshot.events.iter().all(|event| {
|
||||||
!matches!(
|
!matches!(
|
||||||
event,
|
event,
|
||||||
ThreadBufferedEvent::Request(ServerRequest::CommandExecutionRequestApproval { .. })
|
ThreadBufferedEvent::Request(request)
|
||||||
| ThreadBufferedEvent::Request(ServerRequest::FileChangeRequestApproval { .. })
|
if matches!(
|
||||||
|
request.as_ref(),
|
||||||
|
ServerRequest::CommandExecutionRequestApproval { .. }
|
||||||
|
| ServerRequest::FileChangeRequestApproval { .. }
|
||||||
|
)
|
||||||
)
|
)
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
@@ -935,7 +957,11 @@ mod tests {
|
|||||||
assert!(store.snapshot().events.iter().all(|event| {
|
assert!(store.snapshot().events.iter().all(|event| {
|
||||||
!matches!(
|
!matches!(
|
||||||
event,
|
event,
|
||||||
ThreadBufferedEvent::Request(ServerRequest::CommandExecutionRequestApproval { .. })
|
ThreadBufferedEvent::Request(request)
|
||||||
|
if matches!(
|
||||||
|
request.as_ref(),
|
||||||
|
ServerRequest::CommandExecutionRequestApproval { .. }
|
||||||
|
)
|
||||||
)
|
)
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,7 +10,9 @@ pub(super) fn snapshot_has_pending_interactive_request(snapshot: &ThreadEventSna
|
|||||||
snapshot.events.iter().any(|event| {
|
snapshot.events.iter().any(|event| {
|
||||||
matches!(
|
matches!(
|
||||||
event,
|
event,
|
||||||
ThreadBufferedEvent::Request(
|
ThreadBufferedEvent::Request(request)
|
||||||
|
if matches!(
|
||||||
|
request.as_ref(),
|
||||||
ServerRequest::CommandExecutionRequestApproval { .. }
|
ServerRequest::CommandExecutionRequestApproval { .. }
|
||||||
| ServerRequest::FileChangeRequestApproval { .. }
|
| ServerRequest::FileChangeRequestApproval { .. }
|
||||||
| ServerRequest::McpServerElicitationRequest { .. }
|
| ServerRequest::McpServerElicitationRequest { .. }
|
||||||
@@ -24,7 +26,9 @@ pub(super) fn snapshot_has_pending_interactive_request(snapshot: &ThreadEventSna
|
|||||||
pub(super) fn event_is_notice(event: &ThreadBufferedEvent) -> bool {
|
pub(super) fn event_is_notice(event: &ThreadBufferedEvent) -> bool {
|
||||||
matches!(
|
matches!(
|
||||||
event,
|
event,
|
||||||
ThreadBufferedEvent::Notification(
|
ThreadBufferedEvent::Notification(notification)
|
||||||
|
if matches!(
|
||||||
|
notification.as_ref(),
|
||||||
ServerNotification::Warning(_)
|
ServerNotification::Warning(_)
|
||||||
| ServerNotification::GuardianWarning(_)
|
| ServerNotification::GuardianWarning(_)
|
||||||
| ServerNotification::ConfigWarning(_)
|
| ServerNotification::ConfigWarning(_)
|
||||||
|
|||||||
@@ -198,9 +198,8 @@ async fn next_thread_settings_updated(
|
|||||||
.await
|
.await
|
||||||
.expect("app-server should emit an event")
|
.expect("app-server should emit an event")
|
||||||
.expect("app-server event stream should remain open");
|
.expect("app-server event stream should remain open");
|
||||||
if let codex_app_server_client::AppServerEvent::ServerNotification(
|
if let codex_app_server_client::AppServerEvent::ServerNotification(notification) = event
|
||||||
ServerNotification::ThreadSettingsUpdated(notification),
|
&& let ServerNotification::ThreadSettingsUpdated(notification) = *notification
|
||||||
) = event
|
|
||||||
&& notification.thread_id == thread_id.to_string()
|
&& notification.thread_id == thread_id.to_string()
|
||||||
{
|
{
|
||||||
return notification;
|
return notification;
|
||||||
@@ -289,10 +288,12 @@ async fn enqueue_primary_thread_session_replays_buffered_approval_after_attach()
|
|||||||
|
|
||||||
assert!(matches!(
|
assert!(matches!(
|
||||||
&event,
|
&event,
|
||||||
ThreadBufferedEvent::Request(ServerRequest::CommandExecutionRequestApproval {
|
ThreadBufferedEvent::Request(request)
|
||||||
params,
|
if matches!(
|
||||||
..
|
request.as_ref(),
|
||||||
}) if params.turn_id == "turn-1"
|
ServerRequest::CommandExecutionRequestApproval { params, .. }
|
||||||
|
if params.turn_id == "turn-1"
|
||||||
|
)
|
||||||
));
|
));
|
||||||
|
|
||||||
app.handle_thread_event_now(event);
|
app.handle_thread_event_now(event);
|
||||||
@@ -353,10 +354,12 @@ async fn resolved_buffered_approval_does_not_become_actionable_after_drain() ->
|
|||||||
|
|
||||||
assert!(matches!(
|
assert!(matches!(
|
||||||
&event,
|
&event,
|
||||||
ThreadBufferedEvent::Request(ServerRequest::CommandExecutionRequestApproval {
|
ThreadBufferedEvent::Request(request)
|
||||||
params,
|
if matches!(
|
||||||
..
|
request.as_ref(),
|
||||||
}) if params.turn_id == "turn-1"
|
ServerRequest::CommandExecutionRequestApproval { params, .. }
|
||||||
|
if params.turn_id == "turn-1"
|
||||||
|
)
|
||||||
));
|
));
|
||||||
|
|
||||||
app.handle_thread_event_now(event);
|
app.handle_thread_event_now(event);
|
||||||
@@ -801,9 +804,9 @@ async fn replayed_turn_complete_submits_restored_queued_follow_up() {
|
|||||||
ThreadEventSnapshot {
|
ThreadEventSnapshot {
|
||||||
session: None,
|
session: None,
|
||||||
turns: Vec::new(),
|
turns: Vec::new(),
|
||||||
events: vec![ThreadBufferedEvent::Notification(
|
events: vec![ThreadBufferedEvent::Notification(Box::new(
|
||||||
turn_completed_notification(thread_id, "turn-1", TurnStatus::Completed),
|
turn_completed_notification(thread_id, "turn-1", TurnStatus::Completed),
|
||||||
)],
|
))],
|
||||||
input_state: Some(input_state),
|
input_state: Some(input_state),
|
||||||
},
|
},
|
||||||
/*resume_restored_queue*/ true,
|
/*resume_restored_queue*/ true,
|
||||||
@@ -854,9 +857,9 @@ async fn replay_only_thread_keeps_restored_queue_visible() {
|
|||||||
ThreadEventSnapshot {
|
ThreadEventSnapshot {
|
||||||
session: None,
|
session: None,
|
||||||
turns: Vec::new(),
|
turns: Vec::new(),
|
||||||
events: vec![ThreadBufferedEvent::Notification(
|
events: vec![ThreadBufferedEvent::Notification(Box::new(
|
||||||
turn_completed_notification(thread_id, "turn-1", TurnStatus::Completed),
|
turn_completed_notification(thread_id, "turn-1", TurnStatus::Completed),
|
||||||
)],
|
))],
|
||||||
input_state: Some(input_state),
|
input_state: Some(input_state),
|
||||||
},
|
},
|
||||||
/*resume_restored_queue*/ false,
|
/*resume_restored_queue*/ false,
|
||||||
@@ -1026,12 +1029,14 @@ async fn replay_thread_snapshot_does_not_submit_queue_before_replay_catches_up()
|
|||||||
session: None,
|
session: None,
|
||||||
turns: Vec::new(),
|
turns: Vec::new(),
|
||||||
events: vec![
|
events: vec![
|
||||||
ThreadBufferedEvent::Notification(turn_completed_notification(
|
ThreadBufferedEvent::Notification(Box::new(turn_completed_notification(
|
||||||
thread_id,
|
thread_id,
|
||||||
"turn-0",
|
"turn-0",
|
||||||
TurnStatus::Completed,
|
TurnStatus::Completed,
|
||||||
)),
|
))),
|
||||||
ThreadBufferedEvent::Notification(turn_started_notification(thread_id, "turn-1")),
|
ThreadBufferedEvent::Notification(Box::new(turn_started_notification(
|
||||||
|
thread_id, "turn-1",
|
||||||
|
))),
|
||||||
],
|
],
|
||||||
input_state: Some(input_state),
|
input_state: Some(input_state),
|
||||||
},
|
},
|
||||||
@@ -1294,9 +1299,9 @@ async fn replayed_interrupted_turn_restores_queued_input_to_composer() {
|
|||||||
ThreadEventSnapshot {
|
ThreadEventSnapshot {
|
||||||
session: None,
|
session: None,
|
||||||
turns: Vec::new(),
|
turns: Vec::new(),
|
||||||
events: vec![ThreadBufferedEvent::Notification(
|
events: vec![ThreadBufferedEvent::Notification(Box::new(
|
||||||
turn_completed_notification(thread_id, "turn-1", TurnStatus::Interrupted),
|
turn_completed_notification(thread_id, "turn-1", TurnStatus::Interrupted),
|
||||||
)],
|
))],
|
||||||
input_state: Some(input_state),
|
input_state: Some(input_state),
|
||||||
},
|
},
|
||||||
/*resume_restored_queue*/ true,
|
/*resume_restored_queue*/ true,
|
||||||
@@ -1323,10 +1328,8 @@ async fn token_usage_update_refreshes_status_line_with_runtime_context_window()
|
|||||||
|
|
||||||
assert_eq!(app.chat_widget.status_line_text(), None);
|
assert_eq!(app.chat_widget.status_line_text(), None);
|
||||||
|
|
||||||
app.handle_thread_event_now(ThreadBufferedEvent::Notification(token_usage_notification(
|
app.handle_thread_event_now(ThreadBufferedEvent::Notification(Box::new(
|
||||||
ThreadId::new(),
|
token_usage_notification(ThreadId::new(), "turn-1", Some(950_000)),
|
||||||
"turn-1",
|
|
||||||
Some(950_000),
|
|
||||||
)));
|
)));
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
@@ -1341,7 +1344,7 @@ async fn collab_receiver_notification_caches_thread_without_app_server_read() {
|
|||||||
let receiver_thread_id =
|
let receiver_thread_id =
|
||||||
ThreadId::from_string("00000000-0000-0000-0000-000000000123").expect("valid thread id");
|
ThreadId::from_string("00000000-0000-0000-0000-000000000123").expect("valid thread id");
|
||||||
|
|
||||||
app.handle_thread_event_now(ThreadBufferedEvent::Notification(
|
app.handle_thread_event_now(ThreadBufferedEvent::Notification(Box::new(
|
||||||
ServerNotification::ItemStarted(ItemStartedNotification {
|
ServerNotification::ItemStarted(ItemStartedNotification {
|
||||||
thread_id: ThreadId::new().to_string(),
|
thread_id: ThreadId::new().to_string(),
|
||||||
turn_id: "turn-1".to_string(),
|
turn_id: "turn-1".to_string(),
|
||||||
@@ -1358,7 +1361,7 @@ async fn collab_receiver_notification_caches_thread_without_app_server_read() {
|
|||||||
agents_states: HashMap::new(),
|
agents_states: HashMap::new(),
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
));
|
)));
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
app.agent_navigation.get(&receiver_thread_id),
|
app.agent_navigation.get(&receiver_thread_id),
|
||||||
@@ -1378,7 +1381,7 @@ async fn collab_receiver_notification_does_not_cache_not_found_thread() {
|
|||||||
let receiver_thread_id =
|
let receiver_thread_id =
|
||||||
ThreadId::from_string("00000000-0000-0000-0000-000000000124").expect("valid thread id");
|
ThreadId::from_string("00000000-0000-0000-0000-000000000124").expect("valid thread id");
|
||||||
|
|
||||||
app.handle_thread_event_now(ThreadBufferedEvent::Notification(
|
app.handle_thread_event_now(ThreadBufferedEvent::Notification(Box::new(
|
||||||
ServerNotification::ItemCompleted(codex_app_server_protocol::ItemCompletedNotification {
|
ServerNotification::ItemCompleted(codex_app_server_protocol::ItemCompletedNotification {
|
||||||
thread_id: ThreadId::new().to_string(),
|
thread_id: ThreadId::new().to_string(),
|
||||||
turn_id: "turn-1".to_string(),
|
turn_id: "turn-1".to_string(),
|
||||||
@@ -1401,7 +1404,7 @@ async fn collab_receiver_notification_does_not_cache_not_found_thread() {
|
|||||||
)]),
|
)]),
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
));
|
)));
|
||||||
|
|
||||||
assert_eq!(app.agent_navigation.get(&receiver_thread_id), None);
|
assert_eq!(app.agent_navigation.get(&receiver_thread_id), None);
|
||||||
}
|
}
|
||||||
@@ -2121,9 +2124,8 @@ async fn handle_start_side_seeds_navigation_before_thread_started() -> Result<()
|
|||||||
.await
|
.await
|
||||||
.expect("app-server should emit an event")
|
.expect("app-server should emit an event")
|
||||||
.expect("app-server event stream should remain open");
|
.expect("app-server event stream should remain open");
|
||||||
if let codex_app_server_client::AppServerEvent::ServerNotification(
|
if let codex_app_server_client::AppServerEvent::ServerNotification(notification) = event
|
||||||
ServerNotification::ThreadStarted(notification),
|
&& let ServerNotification::ThreadStarted(notification) = notification.as_ref()
|
||||||
) = event
|
|
||||||
&& notification.thread.id == side_thread_id.to_string()
|
&& notification.thread.id == side_thread_id.to_string()
|
||||||
{
|
{
|
||||||
saw_thread_started = true;
|
saw_thread_started = true;
|
||||||
@@ -2917,18 +2919,18 @@ async fn replay_snapshot_with_pending_request_suppresses_replay_notices() {
|
|||||||
session: Some(test_thread_session(thread_id, test_path_buf("/tmp/main"))),
|
session: Some(test_thread_session(thread_id, test_path_buf("/tmp/main"))),
|
||||||
turns: Vec::new(),
|
turns: Vec::new(),
|
||||||
events: vec![
|
events: vec![
|
||||||
ThreadBufferedEvent::Notification(ServerNotification::Warning(
|
ThreadBufferedEvent::Notification(Box::new(ServerNotification::Warning(
|
||||||
WarningNotification {
|
WarningNotification {
|
||||||
thread_id: Some(thread_id.to_string()),
|
thread_id: Some(thread_id.to_string()),
|
||||||
message: stale_warning.to_string(),
|
message: stale_warning.to_string(),
|
||||||
},
|
},
|
||||||
)),
|
))),
|
||||||
ThreadBufferedEvent::Request(exec_approval_request(
|
ThreadBufferedEvent::Request(Box::new(exec_approval_request(
|
||||||
thread_id,
|
thread_id,
|
||||||
"turn-approval",
|
"turn-approval",
|
||||||
"call-approval",
|
"call-approval",
|
||||||
/*approval_id*/ None,
|
/*approval_id*/ None,
|
||||||
)),
|
))),
|
||||||
],
|
],
|
||||||
input_state: None,
|
input_state: None,
|
||||||
},
|
},
|
||||||
@@ -4082,7 +4084,7 @@ async fn primary_thread_ignores_child_mcp_startup_notifications() {
|
|||||||
|
|
||||||
app.handle_app_server_event(
|
app.handle_app_server_event(
|
||||||
&app_server,
|
&app_server,
|
||||||
codex_app_server_client::AppServerEvent::ServerNotification(
|
codex_app_server_client::AppServerEvent::ServerNotification(Box::new(
|
||||||
ServerNotification::McpServerStatusUpdated(McpServerStatusUpdatedNotification {
|
ServerNotification::McpServerStatusUpdated(McpServerStatusUpdatedNotification {
|
||||||
thread_id: Some(child_thread_id.to_string()),
|
thread_id: Some(child_thread_id.to_string()),
|
||||||
name: "sentry".to_string(),
|
name: "sentry".to_string(),
|
||||||
@@ -4090,7 +4092,7 @@ async fn primary_thread_ignores_child_mcp_startup_notifications() {
|
|||||||
error: Some("sentry is not logged in".to_string()),
|
error: Some("sentry is not logged in".to_string()),
|
||||||
failure_reason: None,
|
failure_reason: None,
|
||||||
}),
|
}),
|
||||||
),
|
)),
|
||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
@@ -4106,9 +4108,11 @@ async fn primary_thread_ignores_child_mcp_startup_notifications() {
|
|||||||
assert!(
|
assert!(
|
||||||
matches!(
|
matches!(
|
||||||
child_snapshot.events.as_slice(),
|
child_snapshot.events.as_slice(),
|
||||||
[ThreadBufferedEvent::Notification(
|
[ThreadBufferedEvent::Notification(notification)]
|
||||||
|
if matches!(
|
||||||
|
notification.as_ref(),
|
||||||
ServerNotification::McpServerStatusUpdated(_)
|
ServerNotification::McpServerStatusUpdated(_)
|
||||||
)]
|
)
|
||||||
),
|
),
|
||||||
"child MCP startup notification should be buffered for the child thread"
|
"child MCP startup notification should be buffered for the child thread"
|
||||||
);
|
);
|
||||||
@@ -4155,7 +4159,7 @@ async fn app_scoped_mcp_startup_notifications_do_not_render_in_active_thread() {
|
|||||||
|
|
||||||
app.handle_app_server_event(
|
app.handle_app_server_event(
|
||||||
&app_server,
|
&app_server,
|
||||||
codex_app_server_client::AppServerEvent::ServerNotification(
|
codex_app_server_client::AppServerEvent::ServerNotification(Box::new(
|
||||||
ServerNotification::McpServerStatusUpdated(McpServerStatusUpdatedNotification {
|
ServerNotification::McpServerStatusUpdated(McpServerStatusUpdatedNotification {
|
||||||
thread_id: None,
|
thread_id: None,
|
||||||
name: "sentry".to_string(),
|
name: "sentry".to_string(),
|
||||||
@@ -4163,7 +4167,7 @@ async fn app_scoped_mcp_startup_notifications_do_not_render_in_active_thread() {
|
|||||||
error: Some("sentry is not logged in".to_string()),
|
error: Some("sentry is not logged in".to_string()),
|
||||||
failure_reason: None,
|
failure_reason: None,
|
||||||
}),
|
}),
|
||||||
),
|
)),
|
||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
@@ -4219,7 +4223,7 @@ async fn active_side_thread_renders_live_mcp_startup_notifications() {
|
|||||||
] {
|
] {
|
||||||
app.handle_app_server_event(
|
app.handle_app_server_event(
|
||||||
&app_server,
|
&app_server,
|
||||||
codex_app_server_client::AppServerEvent::ServerNotification(
|
codex_app_server_client::AppServerEvent::ServerNotification(Box::new(
|
||||||
ServerNotification::McpServerStatusUpdated(McpServerStatusUpdatedNotification {
|
ServerNotification::McpServerStatusUpdated(McpServerStatusUpdatedNotification {
|
||||||
thread_id: Some(side_thread_id.to_string()),
|
thread_id: Some(side_thread_id.to_string()),
|
||||||
name: "sentry".to_string(),
|
name: "sentry".to_string(),
|
||||||
@@ -4228,7 +4232,7 @@ async fn active_side_thread_renders_live_mcp_startup_notifications() {
|
|||||||
.then(|| "sentry is not logged in".to_string()),
|
.then(|| "sentry is not logged in".to_string()),
|
||||||
failure_reason: None,
|
failure_reason: None,
|
||||||
}),
|
}),
|
||||||
),
|
)),
|
||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
}
|
}
|
||||||
@@ -6706,7 +6710,7 @@ async fn replace_chat_widget_reseeds_collab_agent_metadata_for_replay() {
|
|||||||
ThreadEventSnapshot {
|
ThreadEventSnapshot {
|
||||||
session: None,
|
session: None,
|
||||||
turns: Vec::new(),
|
turns: Vec::new(),
|
||||||
events: vec![ThreadBufferedEvent::Notification(
|
events: vec![ThreadBufferedEvent::Notification(Box::new(
|
||||||
ServerNotification::ItemStarted(
|
ServerNotification::ItemStarted(
|
||||||
codex_app_server_protocol::ItemStartedNotification {
|
codex_app_server_protocol::ItemStartedNotification {
|
||||||
thread_id: "thread-1".to_string(),
|
thread_id: "thread-1".to_string(),
|
||||||
@@ -6726,7 +6730,7 @@ async fn replace_chat_widget_reseeds_collab_agent_metadata_for_replay() {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
)],
|
))],
|
||||||
input_state: None,
|
input_state: None,
|
||||||
},
|
},
|
||||||
/*resume_restored_queue*/ false,
|
/*resume_restored_queue*/ false,
|
||||||
@@ -7065,9 +7069,9 @@ async fn override_turn_context_sends_thread_settings_update() {
|
|||||||
|
|
||||||
app.handle_app_server_event(
|
app.handle_app_server_event(
|
||||||
&app_server,
|
&app_server,
|
||||||
codex_app_server_client::AppServerEvent::ServerNotification(
|
codex_app_server_client::AppServerEvent::ServerNotification(Box::new(
|
||||||
ServerNotification::ThreadSettingsUpdated(notification),
|
ServerNotification::ThreadSettingsUpdated(notification),
|
||||||
),
|
)),
|
||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
let updated_session = app
|
let updated_session = app
|
||||||
|
|||||||
@@ -56,11 +56,11 @@ async fn deliver_rolling_rate_limit_snapshot(
|
|||||||
) {
|
) {
|
||||||
app.handle_app_server_event(
|
app.handle_app_server_event(
|
||||||
app_server,
|
app_server,
|
||||||
codex_app_server_client::AppServerEvent::ServerNotification(
|
codex_app_server_client::AppServerEvent::ServerNotification(Box::new(
|
||||||
ServerNotification::AccountRateLimitsUpdated(AccountRateLimitsUpdatedNotification {
|
ServerNotification::AccountRateLimitsUpdated(AccountRateLimitsUpdatedNotification {
|
||||||
rate_limits: snapshot,
|
rate_limits: snapshot,
|
||||||
}),
|
}),
|
||||||
),
|
)),
|
||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -107,13 +107,13 @@ async fn next_turn_started(
|
|||||||
.await
|
.await
|
||||||
.expect("app-server should emit a turn/start event")
|
.expect("app-server should emit a turn/start event")
|
||||||
.expect("app-server event stream should remain open");
|
.expect("app-server event stream should remain open");
|
||||||
let started_turn_id = match &event {
|
let started_turn_id = if let AppServerEvent::ServerNotification(notification) = &event
|
||||||
AppServerEvent::ServerNotification(ServerNotification::TurnStarted(notification))
|
&& let ServerNotification::TurnStarted(notification) = notification.as_ref()
|
||||||
if notification.thread_id == thread_id.to_string() =>
|
&& notification.thread_id == thread_id.to_string()
|
||||||
{
|
{
|
||||||
Some(notification.turn.id.clone())
|
Some(notification.turn.id.clone())
|
||||||
}
|
} else {
|
||||||
_ => None,
|
None
|
||||||
};
|
};
|
||||||
app.handle_app_server_event(app_server, event).await;
|
app.handle_app_server_event(app_server, event).await;
|
||||||
drain_active_thread_events(app);
|
drain_active_thread_events(app);
|
||||||
@@ -138,8 +138,12 @@ async fn wait_for_turn_completed(
|
|||||||
.expect("app-server event stream should remain open");
|
.expect("app-server event stream should remain open");
|
||||||
let completed = matches!(
|
let completed = matches!(
|
||||||
&event,
|
&event,
|
||||||
AppServerEvent::ServerNotification(ServerNotification::TurnCompleted(notification))
|
AppServerEvent::ServerNotification(notification)
|
||||||
|
if matches!(
|
||||||
|
notification.as_ref(),
|
||||||
|
ServerNotification::TurnCompleted(notification)
|
||||||
if notification.thread_id == thread_id.to_string()
|
if notification.thread_id == thread_id.to_string()
|
||||||
|
)
|
||||||
);
|
);
|
||||||
app.handle_app_server_event(app_server, event).await;
|
app.handle_app_server_event(app_server, event).await;
|
||||||
drain_active_thread_events(app);
|
drain_active_thread_events(app);
|
||||||
@@ -237,12 +241,12 @@ stream_max_retries = 0
|
|||||||
}
|
}
|
||||||
|
|
||||||
while app_event_rx.try_recv().is_ok() {}
|
while app_event_rx.try_recv().is_ok() {}
|
||||||
app.handle_thread_event_now(ThreadBufferedEvent::Notification(
|
app.handle_thread_event_now(ThreadBufferedEvent::Notification(Box::new(
|
||||||
ServerNotification::Warning(WarningNotification {
|
ServerNotification::Warning(WarningNotification {
|
||||||
thread_id: Some(thread_id.to_string()),
|
thread_id: Some(thread_id.to_string()),
|
||||||
message: "event handled while interrupt is pending".to_string(),
|
message: "event handled while interrupt is pending".to_string(),
|
||||||
}),
|
}),
|
||||||
));
|
)));
|
||||||
assert!(matches!(
|
assert!(matches!(
|
||||||
app_event_rx.try_recv(),
|
app_event_rx.try_recv(),
|
||||||
Ok(AppEvent::InsertHistoryCell(_))
|
Ok(AppEvent::InsertHistoryCell(_))
|
||||||
@@ -507,7 +511,8 @@ goals = true
|
|||||||
|
|
||||||
app.handle_app_server_event(
|
app.handle_app_server_event(
|
||||||
&app_server,
|
&app_server,
|
||||||
AppServerEvent::ServerNotification(ServerNotification::ModelSafetyBufferingUpdated(
|
AppServerEvent::ServerNotification(Box::new(
|
||||||
|
ServerNotification::ModelSafetyBufferingUpdated(
|
||||||
ModelSafetyBufferingUpdatedNotification {
|
ModelSafetyBufferingUpdatedNotification {
|
||||||
thread_id: source_thread_id.to_string(),
|
thread_id: source_thread_id.to_string(),
|
||||||
turn_id: active_turn_id.clone(),
|
turn_id: active_turn_id.clone(),
|
||||||
@@ -517,6 +522,7 @@ goals = true
|
|||||||
show_buffering_ui: true,
|
show_buffering_ui: true,
|
||||||
faster_model: Some(FASTER_MODEL.to_string()),
|
faster_model: Some(FASTER_MODEL.to_string()),
|
||||||
},
|
},
|
||||||
|
),
|
||||||
)),
|
)),
|
||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
@@ -584,7 +590,8 @@ goals = true
|
|||||||
.await;
|
.await;
|
||||||
app.handle_app_server_event(
|
app.handle_app_server_event(
|
||||||
&app_server,
|
&app_server,
|
||||||
AppServerEvent::ServerNotification(ServerNotification::ModelSafetyBufferingUpdated(
|
AppServerEvent::ServerNotification(Box::new(
|
||||||
|
ServerNotification::ModelSafetyBufferingUpdated(
|
||||||
ModelSafetyBufferingUpdatedNotification {
|
ModelSafetyBufferingUpdatedNotification {
|
||||||
thread_id: first_retry_thread_id.to_string(),
|
thread_id: first_retry_thread_id.to_string(),
|
||||||
turn_id: first_retry_turn_id.clone(),
|
turn_id: first_retry_turn_id.clone(),
|
||||||
@@ -594,6 +601,7 @@ goals = true
|
|||||||
show_buffering_ui: true,
|
show_buffering_ui: true,
|
||||||
faster_model: Some(FASTER_MODEL.to_string()),
|
faster_model: Some(FASTER_MODEL.to_string()),
|
||||||
},
|
},
|
||||||
|
),
|
||||||
)),
|
)),
|
||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
|
|||||||
@@ -18,8 +18,8 @@ pub(super) struct ThreadEventSnapshot {
|
|||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub(super) enum ThreadBufferedEvent {
|
pub(super) enum ThreadBufferedEvent {
|
||||||
Notification(ServerNotification),
|
Notification(Box<ServerNotification>),
|
||||||
Request(ServerRequest),
|
Request(Box<ServerRequest>),
|
||||||
HistoryEntryResponse(HistoryLookupResponse),
|
HistoryEntryResponse(HistoryLookupResponse),
|
||||||
FeedbackSubmission(FeedbackThreadEvent),
|
FeedbackSubmission(FeedbackThreadEvent),
|
||||||
}
|
}
|
||||||
@@ -53,14 +53,16 @@ pub(super) struct ThreadEventStore {
|
|||||||
|
|
||||||
impl ThreadEventStore {
|
impl ThreadEventStore {
|
||||||
pub(super) fn event_survives_session_refresh(event: &ThreadBufferedEvent) -> bool {
|
pub(super) fn event_survives_session_refresh(event: &ThreadBufferedEvent) -> bool {
|
||||||
matches!(
|
match event {
|
||||||
event,
|
ThreadBufferedEvent::Request(_) | ThreadBufferedEvent::FeedbackSubmission(_) => true,
|
||||||
ThreadBufferedEvent::Request(_)
|
ThreadBufferedEvent::Notification(notification) => matches!(
|
||||||
| ThreadBufferedEvent::Notification(ServerNotification::HookStarted(_))
|
notification.as_ref(),
|
||||||
| ThreadBufferedEvent::Notification(ServerNotification::HookCompleted(_))
|
ServerNotification::HookStarted(_)
|
||||||
| ThreadBufferedEvent::Notification(ServerNotification::McpServerStatusUpdated(_))
|
| ServerNotification::HookCompleted(_)
|
||||||
| ThreadBufferedEvent::FeedbackSubmission(_)
|
| ServerNotification::McpServerStatusUpdated(_)
|
||||||
)
|
),
|
||||||
|
ThreadBufferedEvent::HistoryEntryResponse(_) => false,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn new(capacity: usize) -> Self {
|
pub(super) fn new(capacity: usize) -> Self {
|
||||||
@@ -159,26 +161,29 @@ impl ThreadEventStore {
|
|||||||
}
|
}
|
||||||
|
|
||||||
self.buffer
|
self.buffer
|
||||||
.push_back(ThreadBufferedEvent::Notification(notification.into_owned()));
|
.push_back(ThreadBufferedEvent::Notification(Box::new(
|
||||||
|
notification.into_owned(),
|
||||||
|
)));
|
||||||
if self.buffer.len() > self.capacity
|
if self.buffer.len() > self.capacity
|
||||||
&& let Some(removed) = self.buffer.pop_front()
|
&& let Some(removed) = self.buffer.pop_front()
|
||||||
&& let ThreadBufferedEvent::Request(request) = &removed
|
&& let ThreadBufferedEvent::Request(request) = &removed
|
||||||
{
|
{
|
||||||
self.pending_interactive_replay
|
self.pending_interactive_replay
|
||||||
.note_evicted_server_request(request);
|
.note_evicted_server_request(request.as_ref());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn push_request(&mut self, request: ServerRequest) {
|
pub(super) fn push_request(&mut self, request: ServerRequest) {
|
||||||
self.pending_interactive_replay
|
self.pending_interactive_replay
|
||||||
.note_server_request(&request);
|
.note_server_request(&request);
|
||||||
self.buffer.push_back(ThreadBufferedEvent::Request(request));
|
self.buffer
|
||||||
|
.push_back(ThreadBufferedEvent::Request(Box::new(request)));
|
||||||
if self.buffer.len() > self.capacity
|
if self.buffer.len() > self.capacity
|
||||||
&& let Some(removed) = self.buffer.pop_front()
|
&& let Some(removed) = self.buffer.pop_front()
|
||||||
&& let ThreadBufferedEvent::Request(request) = &removed
|
&& let ThreadBufferedEvent::Request(request) = &removed
|
||||||
{
|
{
|
||||||
self.pending_interactive_replay
|
self.pending_interactive_replay
|
||||||
.note_evicted_server_request(request);
|
.note_evicted_server_request(request.as_ref());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -189,9 +194,9 @@ impl ThreadEventStore {
|
|||||||
ThreadBufferedEvent::Request(request)
|
ThreadBufferedEvent::Request(request)
|
||||||
if self
|
if self
|
||||||
.pending_interactive_replay
|
.pending_interactive_replay
|
||||||
.should_replay_snapshot_request(request) =>
|
.should_replay_snapshot_request(request.as_ref()) =>
|
||||||
{
|
{
|
||||||
Some(request.clone())
|
Some(request.as_ref().clone())
|
||||||
}
|
}
|
||||||
ThreadBufferedEvent::Request(_)
|
ThreadBufferedEvent::Request(_)
|
||||||
| ThreadBufferedEvent::Notification(_)
|
| ThreadBufferedEvent::Notification(_)
|
||||||
@@ -210,18 +215,20 @@ impl ThreadEventStore {
|
|||||||
.iter()
|
.iter()
|
||||||
.rev()
|
.rev()
|
||||||
.find_map(|event| match event {
|
.find_map(|event| match event {
|
||||||
ThreadBufferedEvent::Notification(ServerNotification::ItemStarted(
|
ThreadBufferedEvent::Notification(notification) => match notification.as_ref() {
|
||||||
notification,
|
ServerNotification::ItemStarted(notification)
|
||||||
)) if turn_id_matches(turn_id, ¬ification.turn_id) => {
|
if turn_id_matches(turn_id, ¬ification.turn_id) =>
|
||||||
|
{
|
||||||
file_change_item_changes(¬ification.item, item_id)
|
file_change_item_changes(¬ification.item, item_id)
|
||||||
}
|
}
|
||||||
ThreadBufferedEvent::Notification(ServerNotification::ItemCompleted(
|
ServerNotification::ItemCompleted(notification)
|
||||||
notification,
|
if turn_id_matches(turn_id, ¬ification.turn_id) =>
|
||||||
)) if turn_id_matches(turn_id, ¬ification.turn_id) => {
|
{
|
||||||
file_change_item_changes(¬ification.item, item_id)
|
file_change_item_changes(¬ification.item, item_id)
|
||||||
}
|
}
|
||||||
|
_ => None,
|
||||||
|
},
|
||||||
ThreadBufferedEvent::Request(_)
|
ThreadBufferedEvent::Request(_)
|
||||||
| ThreadBufferedEvent::Notification(_)
|
|
||||||
| ThreadBufferedEvent::HistoryEntryResponse(_)
|
| ThreadBufferedEvent::HistoryEntryResponse(_)
|
||||||
| ThreadBufferedEvent::FeedbackSubmission(_) => None,
|
| ThreadBufferedEvent::FeedbackSubmission(_) => None,
|
||||||
})
|
})
|
||||||
@@ -247,7 +254,7 @@ impl ThreadEventStore {
|
|||||||
.filter(|event| match event {
|
.filter(|event| match event {
|
||||||
ThreadBufferedEvent::Request(request) => self
|
ThreadBufferedEvent::Request(request) => self
|
||||||
.pending_interactive_replay
|
.pending_interactive_replay
|
||||||
.should_replay_snapshot_request(request),
|
.should_replay_snapshot_request(request.as_ref()),
|
||||||
ThreadBufferedEvent::Notification(_)
|
ThreadBufferedEvent::Notification(_)
|
||||||
| ThreadBufferedEvent::HistoryEntryResponse(_)
|
| ThreadBufferedEvent::HistoryEntryResponse(_)
|
||||||
| ThreadBufferedEvent::FeedbackSubmission(_) => true,
|
| ThreadBufferedEvent::FeedbackSubmission(_) => true,
|
||||||
|
|||||||
@@ -632,7 +632,9 @@ impl App {
|
|||||||
};
|
};
|
||||||
if should_send
|
if should_send
|
||||||
&& let Err(error) = thread_event_tx
|
&& let Err(error) = thread_event_tx
|
||||||
.send(ThreadBufferedEvent::Notification(notification))
|
.send(ThreadBufferedEvent::Notification(Box::new(
|
||||||
|
notification,
|
||||||
|
)))
|
||||||
.await
|
.await
|
||||||
{
|
{
|
||||||
tracing::warn!(error = %error, "thread event channel closed");
|
tracing::warn!(error = %error, "thread event channel closed");
|
||||||
@@ -1006,7 +1008,7 @@ impl App {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if let Some(notification) = notification {
|
if let Some(notification) = notification {
|
||||||
match sender.try_send(ThreadBufferedEvent::Notification(notification)) {
|
match sender.try_send(ThreadBufferedEvent::Notification(Box::new(notification))) {
|
||||||
Ok(()) => {}
|
Ok(()) => {}
|
||||||
Err(TrySendError::Full(event)) => {
|
Err(TrySendError::Full(event)) => {
|
||||||
tokio::spawn(async move {
|
tokio::spawn(async move {
|
||||||
@@ -1132,7 +1134,7 @@ impl App {
|
|||||||
let request_status = SideParentStatus::for_request(&request);
|
let request_status = SideParentStatus::for_request(&request);
|
||||||
|
|
||||||
if should_send {
|
if should_send {
|
||||||
match sender.try_send(ThreadBufferedEvent::Request(request)) {
|
match sender.try_send(ThreadBufferedEvent::Request(Box::new(request))) {
|
||||||
Ok(()) => {}
|
Ok(()) => {}
|
||||||
Err(TrySendError::Full(event)) => {
|
Err(TrySendError::Full(event)) => {
|
||||||
tokio::spawn(async move {
|
tokio::spawn(async move {
|
||||||
@@ -1183,7 +1185,7 @@ impl App {
|
|||||||
{
|
{
|
||||||
guard
|
guard
|
||||||
.pending_interactive_replay
|
.pending_interactive_replay
|
||||||
.note_evicted_server_request(request);
|
.note_evicted_server_request(request.as_ref());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
should_send
|
should_send
|
||||||
@@ -1267,11 +1269,11 @@ impl App {
|
|||||||
for pending_event in pending {
|
for pending_event in pending {
|
||||||
match pending_event {
|
match pending_event {
|
||||||
ThreadBufferedEvent::Notification(notification) => {
|
ThreadBufferedEvent::Notification(notification) => {
|
||||||
self.enqueue_thread_notification(thread_id, notification)
|
self.enqueue_thread_notification(thread_id, *notification)
|
||||||
.await?;
|
.await?;
|
||||||
}
|
}
|
||||||
ThreadBufferedEvent::Request(request) => {
|
ThreadBufferedEvent::Request(request) => {
|
||||||
self.enqueue_thread_request(thread_id, request).await?;
|
self.enqueue_thread_request(thread_id, *request).await?;
|
||||||
}
|
}
|
||||||
ThreadBufferedEvent::HistoryEntryResponse(event) => {
|
ThreadBufferedEvent::HistoryEntryResponse(event) => {
|
||||||
self.enqueue_thread_history_entry_response(thread_id, event)
|
self.enqueue_thread_history_entry_response(thread_id, event)
|
||||||
@@ -1298,7 +1300,7 @@ impl App {
|
|||||||
.await;
|
.await;
|
||||||
}
|
}
|
||||||
self.pending_primary_events
|
self.pending_primary_events
|
||||||
.push_back(ThreadBufferedEvent::Notification(notification));
|
.push_back(ThreadBufferedEvent::Notification(Box::new(notification)));
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1310,7 +1312,7 @@ impl App {
|
|||||||
return self.enqueue_thread_request(thread_id, request).await;
|
return self.enqueue_thread_request(thread_id, request).await;
|
||||||
}
|
}
|
||||||
self.pending_primary_events
|
self.pending_primary_events
|
||||||
.push_back(ThreadBufferedEvent::Request(request));
|
.push_back(ThreadBufferedEvent::Request(Box::new(request)));
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1541,22 +1543,26 @@ impl App {
|
|||||||
pub(super) fn handle_thread_event_now(&mut self, event: ThreadBufferedEvent) {
|
pub(super) fn handle_thread_event_now(&mut self, event: ThreadBufferedEvent) {
|
||||||
let needs_refresh = matches!(
|
let needs_refresh = matches!(
|
||||||
&event,
|
&event,
|
||||||
ThreadBufferedEvent::Notification(ServerNotification::TurnStarted(_))
|
ThreadBufferedEvent::Notification(notification)
|
||||||
| ThreadBufferedEvent::Notification(ServerNotification::ThreadTokenUsageUpdated(_))
|
if matches!(
|
||||||
|
notification.as_ref(),
|
||||||
|
ServerNotification::TurnStarted(_)
|
||||||
|
| ServerNotification::ThreadTokenUsageUpdated(_)
|
||||||
|
)
|
||||||
);
|
);
|
||||||
match event {
|
match event {
|
||||||
ThreadBufferedEvent::Notification(notification) => {
|
ThreadBufferedEvent::Notification(notification) => {
|
||||||
self.cache_collab_receiver_threads_for_notification(¬ification);
|
self.cache_collab_receiver_threads_for_notification(notification.as_ref());
|
||||||
self.chat_widget
|
self.chat_widget
|
||||||
.handle_server_notification(notification, /*replay_kind*/ None);
|
.handle_server_notification(*notification, /*replay_kind*/ None);
|
||||||
}
|
}
|
||||||
ThreadBufferedEvent::Request(request) => {
|
ThreadBufferedEvent::Request(request) => {
|
||||||
if self
|
if self
|
||||||
.pending_app_server_requests
|
.pending_app_server_requests
|
||||||
.contains_server_request(&request)
|
.contains_server_request(request.as_ref())
|
||||||
{
|
{
|
||||||
self.chat_widget
|
self.chat_widget
|
||||||
.handle_server_request(request, /*replay_kind*/ None);
|
.handle_server_request(*request, /*replay_kind*/ None);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ThreadBufferedEvent::HistoryEntryResponse(event) => {
|
ThreadBufferedEvent::HistoryEntryResponse(event) => {
|
||||||
@@ -1575,10 +1581,10 @@ impl App {
|
|||||||
match event {
|
match event {
|
||||||
ThreadBufferedEvent::Notification(notification) => self
|
ThreadBufferedEvent::Notification(notification) => self
|
||||||
.chat_widget
|
.chat_widget
|
||||||
.handle_server_notification(notification, Some(ReplayKind::ThreadSnapshot)),
|
.handle_server_notification(*notification, Some(ReplayKind::ThreadSnapshot)),
|
||||||
ThreadBufferedEvent::Request(request) => self
|
ThreadBufferedEvent::Request(request) => self
|
||||||
.chat_widget
|
.chat_widget
|
||||||
.handle_server_request(request, Some(ReplayKind::ThreadSnapshot)),
|
.handle_server_request(*request, Some(ReplayKind::ThreadSnapshot)),
|
||||||
ThreadBufferedEvent::HistoryEntryResponse(event) => {
|
ThreadBufferedEvent::HistoryEntryResponse(event) => {
|
||||||
self.chat_widget.handle_history_entry_response(event)
|
self.chat_widget.handle_history_entry_response(event)
|
||||||
}
|
}
|
||||||
@@ -1603,7 +1609,8 @@ impl App {
|
|||||||
// the exit marker when the currently active thread acknowledges shutdown.
|
// the exit marker when the currently active thread acknowledges shutdown.
|
||||||
let pending_shutdown_exit_completed = matches!(
|
let pending_shutdown_exit_completed = matches!(
|
||||||
&event,
|
&event,
|
||||||
ThreadBufferedEvent::Notification(ServerNotification::ThreadClosed(_))
|
ThreadBufferedEvent::Notification(notification)
|
||||||
|
if matches!(notification.as_ref(), ServerNotification::ThreadClosed(_))
|
||||||
) && self.pending_shutdown_exit_thread_id
|
) && self.pending_shutdown_exit_thread_id
|
||||||
== self.active_thread_id;
|
== self.active_thread_id;
|
||||||
|
|
||||||
@@ -1617,7 +1624,7 @@ impl App {
|
|||||||
// failover, while true sub-agent deaths still do.
|
// failover, while true sub-agent deaths still do.
|
||||||
if let ThreadBufferedEvent::Notification(notification) = &event
|
if let ThreadBufferedEvent::Notification(notification) = &event
|
||||||
&& let Some((closed_thread_id, primary_thread_id)) =
|
&& let Some((closed_thread_id, primary_thread_id)) =
|
||||||
self.active_non_primary_shutdown_target(notification)
|
self.active_non_primary_shutdown_target(notification.as_ref())
|
||||||
{
|
{
|
||||||
self.mark_agent_picker_thread_closed(closed_thread_id);
|
self.mark_agent_picker_thread_closed(closed_thread_id);
|
||||||
if self.side_threads.contains_key(&closed_thread_id) {
|
if self.side_threads.contains_key(&closed_thread_id) {
|
||||||
|
|||||||
@@ -556,7 +556,7 @@ pub(crate) async fn run_onboarding_app(
|
|||||||
if let Some(event) = event {
|
if let Some(event) = event {
|
||||||
match event {
|
match event {
|
||||||
AppServerEvent::ServerNotification(notification) => {
|
AppServerEvent::ServerNotification(notification) => {
|
||||||
onboarding_screen.handle_app_server_notification(notification);
|
onboarding_screen.handle_app_server_notification(*notification);
|
||||||
}
|
}
|
||||||
AppServerEvent::Disconnected { message } => {
|
AppServerEvent::Disconnected { message } => {
|
||||||
return Err(color_eyre::eyre::eyre!(message));
|
return Err(color_eyre::eyre::eyre!(message));
|
||||||
|
|||||||
Reference in new issue
Block a user