From 71967d847427ecf2b12ee324c53e1fefd656a741 Mon Sep 17 00:00:00 2001 From: Kayshen-X Date: Fri, 15 May 2026 21:48:09 +0800 Subject: [PATCH] fix(ai): surface error for unwired chat agents instead of rerouting MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `provider_for_agent` silently substituted Claude Code when the user picked Codex or OpenCode — so the transcript looked like the chosen CLI answered when it hadn't. Codex stop-gate. It now returns `Option`: `None` for Codex / OpenCode (HTTP-server transport not yet bridged). `launch_if_pending` writes an explicit `error: … not wired yet` into the assistant bubble and starts no session, so the user knows their pick didn't run. --- crates/openpencil-desktop/src/chat_session.rs | 40 ++++++++++++++----- 1 file changed, 31 insertions(+), 9 deletions(-) diff --git a/crates/openpencil-desktop/src/chat_session.rs b/crates/openpencil-desktop/src/chat_session.rs index 785f73a0b..290d2db7b 100644 --- a/crates/openpencil-desktop/src/chat_session.rs +++ b/crates/openpencil-desktop/src/chat_session.rs @@ -114,7 +114,28 @@ pub fn launch_if_pending( let Some(user_text) = host.document_mut().chat.pending_send.take() else { return false; }; - let provider = provider_for_agent(host.document().ui.chat_selected_agent); + let agent_idx = host.document().ui.chat_selected_agent; + let Some(provider) = provider_for_agent(agent_idx) else { + // Selected agent has no `ChatProvider` bridge yet (Codex / + // OpenCode HTTP-server transport). Surface that honestly in + // the assistant bubble instead of silently running a + // different agent (codex stop-gate: silent reroute to + // Claude misled the user about which CLI answered). + let name = openpencil_shell_core::agent_settings_state::AgentProvider::ALL + .get(agent_idx) + .map(|a| a.name()) + .unwrap_or("This agent"); + if let Some(msg) = host.document_mut().chat.messages.last_mut() { + msg.content = format!( + "error: {name} chat is not wired yet — its HTTP-server \ + transport is still pending. Pick Claude Code, GitHub \ + Copilot, or Gemini CLI via the model chip." + ); + } + // No session started; report the transcript change so the + // caller repaints the error. + return true; + }; let req = ChatRequest { system_prompt: String::new(), user_message: user_text, @@ -128,16 +149,17 @@ pub fn launch_if_pending( /// `AgentProvider::ALL`: 0 ClaudeCode, 1 CodexCli, 2 OpenCode, /// 3 GithubCopilot, 4 GeminiCli). Claude Code uses its dedicated /// SDK adapter; Copilot / Gemini use the subprocess transport. -/// Codex + OpenCode are HTTP-server CLIs whose `ChatProvider` -/// bridge isn't wired yet — they fall back to Claude Code so the -/// chat still functions rather than dead-ending. -fn provider_for_agent(agent_idx: usize) -> Box { +/// Returns `None` for Codex + OpenCode — HTTP-server CLIs whose +/// `ChatProvider` bridge isn't wired yet; the caller surfaces an +/// explicit error rather than rerouting to a different agent. +fn provider_for_agent(agent_idx: usize) -> Option> { match agent_idx { - 3 => Box::new(CopilotProvider::new()), + 0 => Some(Box::new(ClaudeCodeProvider::new())), + 3 => Some(Box::new(CopilotProvider::new())), 4 => SubprocessProvider::for_cli(CliName::Gemini) - .map(|p| Box::new(p) as Box) - .unwrap_or_else(|| Box::new(ClaudeCodeProvider::new())), - _ => Box::new(ClaudeCodeProvider::new()), + .map(|p| Box::new(p) as Box), + // 1 CodexCli, 2 OpenCode — no bridge yet. + _ => None, } }