diff --git a/apps/ai-game-creator-shell/src-tauri/src/agent/codex_app_server/mod.rs b/apps/ai-game-creator-shell/src-tauri/src/agent/codex_app_server/mod.rs index 34ea55a4f..b38fe27af 100644 --- a/apps/ai-game-creator-shell/src-tauri/src/agent/codex_app_server/mod.rs +++ b/apps/ai-game-creator-shell/src-tauri/src/agent/codex_app_server/mod.rs @@ -563,6 +563,10 @@ enum CodexTurnEvent { completed: bool, params: serde_json::Value, }, + Request { + event_type: &'static str, + params: serde_json::Value, + }, RawItem(serde_json::Value), Terminal(serde_json::Value), TransportClosed(String), @@ -919,6 +923,16 @@ fn direct_codex_safe_activity_for_notification(method: &str) -> Option<&'static } } +fn direct_codex_request_event_type(method: &str) -> Option<&'static str> { + match method { + "item/fileChange/requestApproval" + | "item/commandExecution/requestApproval" + | "item/permissions/requestApproval" => Some("approval.requested"), + "item/tool/requestUserInput" | "item/mcpToolCall/requestUserInput" => Some("ask.requested"), + _ => None, + } +} + fn direct_codex_intermediate_text_for_notification( method: &str, params: &serde_json::Value, @@ -2964,6 +2978,27 @@ impl CodexAppServerConnection { ); } } + Some(CodexTurnEvent::Request { event_type, params }) => { + if self.inner.workspace_mode == CodexAppServerWorkspaceMode::DirectProject { + let request_id = params + .get("requestId") + .and_then(serde_json::Value::as_str) + .or_else(|| params.get("id").and_then(serde_json::Value::as_str)) + .filter(|value| !value.is_empty()) + .map(str::to_string); + append_direct_thread_event( + &direct_thread_id, + DirectThreadRawEventDraft { + event_type: event_type.to_string(), + turn_id: turn_id.clone(), + item_id: None, + payload: request_id + .map(|id| serde_json::json!({ "requestId": id })) + .unwrap_or_else(|| serde_json::json!({})), + }, + ); + } + } Some(CodexTurnEvent::Activity(activity)) => { if let Some(observer) = direct_observer.as_deref_mut() { observer(DirectCodexTurnObservation::Activity(activity)); @@ -3508,7 +3543,8 @@ async fn read_game_creator_codex_app_server_stdout( | "item/completed" | "rawResponseItem/completed" | "turn/completed" - ) && safe_activity.is_none() + ) && direct_codex_request_event_type(method).is_none() + && safe_activity.is_none() && intermediate_text.is_none() { continue; @@ -3594,6 +3630,13 @@ async fn read_game_creator_codex_app_server_stdout( .cloned() .unwrap_or(serde_json::Value::Null), ), + method if direct_codex_request_event_type(method).is_some() => { + CodexTurnEvent::Request { + event_type: direct_codex_request_event_type(method) + .expect("request event type checked above"), + params, + } + } _ => CodexTurnEvent::Terminal(params), } };