mirror of
https://github.com/openai/codex.git
synced 2026-11-08 20:40:33 +00:00
Fix skills catalog budgets and warning delivery
This commit is contained in:
@@ -113,7 +113,7 @@ struct AppServerExtensionEventSink {
|
||||
}
|
||||
|
||||
impl ExtensionEventSink for AppServerExtensionEventSink {
|
||||
fn emit(&self, event: Event) {
|
||||
fn emit(&self, thread_id: ThreadId, event: Event) {
|
||||
match event.msg {
|
||||
EventMsg::ThreadGoalUpdated(thread_goal_event) => {
|
||||
let thread_id = thread_goal_event.thread_id;
|
||||
@@ -148,10 +148,20 @@ impl ExtensionEventSink for AppServerExtensionEventSink {
|
||||
});
|
||||
}
|
||||
EventMsg::Warning(warning_event) => {
|
||||
let Ok(thread_id) = ThreadId::from_string(&event.id) else {
|
||||
tracing::warn!(event_id = %event.id, "dropping extension warning with invalid thread id");
|
||||
return;
|
||||
};
|
||||
if let Some(listener_command_tx) = self
|
||||
.thread_state_manager
|
||||
.current_listener_command_tx(thread_id)
|
||||
{
|
||||
let command = ThreadListenerCommand::EmitWarning {
|
||||
message: warning_event.message.clone(),
|
||||
};
|
||||
if listener_command_tx.send(command).is_ok() {
|
||||
return;
|
||||
}
|
||||
tracing::warn!(
|
||||
"failed to enqueue extension warning for {thread_id}: listener command channel is closed"
|
||||
);
|
||||
}
|
||||
let outgoing = Arc::clone(&self.outgoing);
|
||||
tokio::spawn(async move {
|
||||
outgoing
|
||||
@@ -165,7 +175,7 @@ impl ExtensionEventSink for AppServerExtensionEventSink {
|
||||
});
|
||||
}
|
||||
msg => {
|
||||
tracing::debug!(event_id = %event.id, ?msg, "dropping unsupported extension event");
|
||||
tracing::debug!(%thread_id, event_id = %event.id, ?msg, "dropping unsupported extension event");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -203,7 +213,7 @@ mod tests {
|
||||
use super::*;
|
||||
|
||||
#[tokio::test]
|
||||
async fn app_server_event_sink_uses_listener_fifo_for_goal_updates_and_clears() {
|
||||
async fn app_server_event_sink_uses_listener_fifo_for_extension_events() {
|
||||
let (outgoing_tx, _outgoing_rx) = mpsc::channel(4);
|
||||
let outgoing = Arc::new(OutgoingMessageSender::new(
|
||||
outgoing_tx,
|
||||
@@ -216,14 +226,23 @@ mod tests {
|
||||
let sink = app_server_extension_event_sink(outgoing, thread_state_manager);
|
||||
|
||||
for turn_id in ["turn-1", "turn-2"] {
|
||||
sink.emit(thread_goal_updated_event(thread_id, turn_id));
|
||||
sink.emit(thread_id, thread_goal_updated_event(thread_id, turn_id));
|
||||
}
|
||||
sink.emit(
|
||||
thread_id,
|
||||
Event {
|
||||
id: "turn-2".to_string(),
|
||||
msg: EventMsg::Warning(codex_protocol::protocol::WarningEvent {
|
||||
message: "skill warning".to_string(),
|
||||
}),
|
||||
},
|
||||
);
|
||||
listener_command_tx
|
||||
.send(ThreadListenerCommand::EmitThreadGoalCleared)
|
||||
.expect("listener command channel should be open");
|
||||
|
||||
let mut observed = Vec::new();
|
||||
for _ in 0..3 {
|
||||
for _ in 0..4 {
|
||||
let command = timeout(Duration::from_secs(1), listener_command_rx.recv())
|
||||
.await
|
||||
.expect("timed out waiting for listener command")
|
||||
@@ -235,6 +254,7 @@ mod tests {
|
||||
ThreadListenerCommand::EmitThreadGoalCleared => {
|
||||
observed.push("cleared".to_string())
|
||||
}
|
||||
ThreadListenerCommand::EmitWarning { message } => observed.push(message),
|
||||
_ => panic!("unexpected listener command"),
|
||||
}
|
||||
}
|
||||
@@ -243,6 +263,7 @@ mod tests {
|
||||
vec![
|
||||
"turn-1".to_string(),
|
||||
"turn-2".to_string(),
|
||||
"skill warning".to_string(),
|
||||
"cleared".to_string()
|
||||
],
|
||||
observed
|
||||
|
||||
@@ -276,6 +276,7 @@ use codex_app_server_protocol::TurnStatus;
|
||||
use codex_app_server_protocol::TurnSteerParams;
|
||||
use codex_app_server_protocol::TurnSteerResponse;
|
||||
use codex_app_server_protocol::UserInput as V2UserInput;
|
||||
use codex_app_server_protocol::WarningNotification;
|
||||
use codex_app_server_protocol::WindowsSandboxReadiness;
|
||||
use codex_app_server_protocol::WindowsSandboxReadinessResponse;
|
||||
use codex_app_server_protocol::WindowsSandboxSetupCompletedNotification;
|
||||
|
||||
@@ -505,6 +505,14 @@ pub(super) async fn handle_thread_listener_command(
|
||||
ThreadListenerCommand::EmitThreadGoalSnapshot { state_db } => {
|
||||
send_thread_goal_snapshot_notification(outgoing, conversation_id, &state_db).await;
|
||||
}
|
||||
ThreadListenerCommand::EmitWarning { message } => {
|
||||
outgoing
|
||||
.send_server_notification(ServerNotification::Warning(WarningNotification {
|
||||
thread_id: Some(conversation_id.to_string()),
|
||||
message,
|
||||
}))
|
||||
.await;
|
||||
}
|
||||
ThreadListenerCommand::ResolveServerRequest {
|
||||
request_id,
|
||||
completion_tx,
|
||||
|
||||
@@ -56,6 +56,10 @@ pub(crate) enum ThreadListenerCommand {
|
||||
EmitThreadGoalSnapshot {
|
||||
state_db: StateDbHandle,
|
||||
},
|
||||
// EmitWarning is used to order extension warnings with core turn events.
|
||||
EmitWarning {
|
||||
message: String,
|
||||
},
|
||||
// ResolveServerRequest is used to notify the client that the request has been resolved.
|
||||
// It is executed in the thread listener's context to ensure that the resolved notification is ordered with regard to the request itself.
|
||||
ResolveServerRequest {
|
||||
|
||||
Reference in New Issue
Block a user