Skip to content

Commit 02a9a38

Browse files
cursoragentechobt
andcommitted
fix(tui): skip local exec for remote tools, default cortex model
Cloud tool_start events are display-only first-class rows. Do not re-run python/skill_view locally. Default the TUI model to cortex-1-mini so the status bar is not a provider brand name. Co-authored-by: Mathis <echobt@users.noreply.github.com>
1 parent ce7a240 commit 02a9a38

5 files changed

Lines changed: 82 additions & 24 deletions

File tree

src/cortex-engine/src/streaming.rs

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,13 @@ pub enum StreamEvent {
5353
/// Tool call delta.
5454
ToolCallDelta { id: String, arguments: String },
5555
/// Tool call complete.
56-
ToolCallComplete { id: String },
56+
ToolCallComplete {
57+
id: String,
58+
#[serde(default)]
59+
success: bool,
60+
#[serde(default)]
61+
output: String,
62+
},
5763
/// Tool call with full info.
5864
ToolCall {
5965
id: String,
@@ -279,7 +285,7 @@ impl StreamProcessor {
279285
StreamEvent::ToolCallDelta { id, arguments } => {
280286
self.content.append_tool_call(id, arguments);
281287
}
282-
StreamEvent::ToolCallComplete { id } => {
288+
StreamEvent::ToolCallComplete { id, .. } => {
283289
self.content.complete_tool_call(id);
284290
}
285291
StreamEvent::TokenUsage { prompt, completion } => {
@@ -811,6 +817,8 @@ mod tests {
811817
});
812818
processor.process(StreamEvent::ToolCallComplete {
813819
id: "tc1".to_string(),
820+
success: true,
821+
output: String::new(),
814822
});
815823

816824
let content = processor.content();

src/cortex-tui/src/app/state.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -245,7 +245,7 @@ impl AppState {
245245
focus: FocusTarget::default(),
246246
session_id: None,
247247
messages: Vec::new(),
248-
model: String::from("gpt-4"),
248+
model: String::from("cortex-1-mini"),
249249
provider: String::from("cortex"),
250250
system_prompt: None,
251251
sidebar_visible: true,

src/cortex-tui/src/providers/config.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ pub const DEFAULT_API_URL: &str = "https://api.cortex.foundation";
1919
pub const DEFAULT_PROVIDER: &str = "cortex";
2020

2121
/// Default model
22-
pub const DEFAULT_MODEL: &str = "anthropic/claude-opus-4.5";
22+
pub const DEFAULT_MODEL: &str = "cortex-1-mini";
2323

2424
/// Configuration file name
2525
pub const CONFIG_FILE: &str = "config.json";
@@ -357,7 +357,7 @@ mod tests {
357357
let config = CortexConfig::default();
358358
assert_eq!(config.api_url, DEFAULT_API_URL);
359359
assert_eq!(config.default_provider, "cortex");
360-
assert_eq!(config.default_model, "anthropic/claude-opus-4.5");
360+
assert_eq!(config.default_model, "cortex-1-mini");
361361
}
362362

363363
#[test]

src/cortex-tui/src/runner/event_loop/streaming.rs

Lines changed: 58 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -252,9 +252,17 @@ impl EventLoop {
252252
}
253253
Ok(Some(Ok(ResponseEvent::ToolCall(tool_call)))) => {
254254
// Parse arguments from JSON string
255-
let arguments = serde_json::from_str(&tool_call.arguments)
256-
.unwrap_or_else(|_| serde_json::json!({"raw": tool_call.arguments, "remote": tool_call.remote}));
255+
let mut arguments = serde_json::from_str(&tool_call.arguments)
256+
.unwrap_or_else(|_| {
257+
serde_json::json!({"raw": tool_call.arguments})
258+
});
259+
if tool_call.remote
260+
&& let Some(obj) = arguments.as_object_mut()
261+
{
262+
obj.insert("remote".into(), serde_json::json!(true));
263+
}
257264
if tool_call.remote {
265+
// Cloud-executed tools: first-class row only, no local re-exec.
258266
if tx
259267
.send(StreamEvent::ToolCallStart {
260268
id: tool_call.id.clone(),
@@ -265,8 +273,7 @@ impl EventLoop {
265273
{
266274
break;
267275
}
268-
}
269-
if tx
276+
} else if tx
270277
.send(StreamEvent::ToolCall {
271278
id: tool_call.id.clone(),
272279
name: tool_call.name.clone(),
@@ -280,13 +287,16 @@ impl EventLoop {
280287
}
281288
Ok(Some(Ok(ResponseEvent::ToolResult { id, success, output }))) => {
282289
if tx
283-
.send(StreamEvent::ToolCallComplete { id: id.clone() })
290+
.send(StreamEvent::ToolCallComplete {
291+
id: id.clone(),
292+
success,
293+
output,
294+
})
284295
.await
285296
.is_err()
286297
{
287298
break;
288299
}
289-
let _ = (success, output);
290300
}
291301
Ok(Some(Err(e))) => {
292302
let _ = tx.send(StreamEvent::Error(e.to_string())).await;
@@ -362,9 +372,23 @@ impl EventLoop {
362372
self.app_state
363373
.update_tool_status(&id, crate::views::tool_call::ToolStatus::Running);
364374
}
365-
StreamEvent::ToolCallComplete { id } => {
366-
self.app_state
367-
.update_tool_status(&id, crate::views::tool_call::ToolStatus::Completed);
375+
StreamEvent::ToolCallComplete {
376+
id,
377+
success,
378+
output,
379+
} => {
380+
let summary = if success {
381+
"↳ done".to_string()
382+
} else {
383+
"↳ error".to_string()
384+
};
385+
if !output.is_empty() || !success {
386+
self.app_state
387+
.update_tool_result(&id, output, success, summary);
388+
} else {
389+
self.app_state
390+
.update_tool_status(&id, crate::views::tool_call::ToolStatus::Completed);
391+
}
368392
}
369393
_ => {
370394
// Other variants not specifically handled
@@ -867,23 +891,38 @@ impl EventLoop {
867891
break;
868892
}
869893
Ok(Some(Ok(ResponseEvent::ToolCall(tool_call)))) => {
870-
let arguments = serde_json::from_str(&tool_call.arguments)
871-
.unwrap_or_else(|_| serde_json::json!({"raw": tool_call.arguments, "remote": tool_call.remote}));
872-
if tx
873-
.send(StreamEvent::ToolCall {
894+
let mut arguments = serde_json::from_str(&tool_call.arguments)
895+
.unwrap_or_else(|_| {
896+
serde_json::json!({"raw": tool_call.arguments})
897+
});
898+
if tool_call.remote
899+
&& let Some(obj) = arguments.as_object_mut()
900+
{
901+
obj.insert("remote".into(), serde_json::json!(true));
902+
}
903+
let event = if tool_call.remote {
904+
StreamEvent::ToolCallStart {
905+
id: tool_call.id.clone(),
906+
name: tool_call.name.clone(),
907+
}
908+
} else {
909+
StreamEvent::ToolCall {
874910
id: tool_call.id.clone(),
875911
name: tool_call.name.clone(),
876912
arguments,
877-
})
878-
.await
879-
.is_err()
880-
{
913+
}
914+
};
915+
if tx.send(event).await.is_err() {
881916
break;
882917
}
883918
}
884-
Ok(Some(Ok(ResponseEvent::ToolResult { id, .. }))) => {
919+
Ok(Some(Ok(ResponseEvent::ToolResult { id, success, output }))) => {
885920
if tx
886-
.send(StreamEvent::ToolCallComplete { id })
921+
.send(StreamEvent::ToolCallComplete {
922+
id,
923+
success,
924+
output,
925+
})
887926
.await
888927
.is_err()
889928
{

src/cortex-tui/src/runner/login_screen.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -929,6 +929,10 @@ mod tests {
929929
let mut terminal = Terminal::new(backend).expect("test backend");
930930
terminal.draw(|f| screen.render(f)).expect("draw");
931931
let text = buffer_text(&terminal);
932+
if let Ok(dir) = std::env::var("CORTEX_DUMP_SNAPSHOTS") {
933+
let _ = std::fs::create_dir_all(&dir);
934+
let _ = std::fs::write(std::path::Path::new(&dir).join("auth.txt"), &text);
935+
}
932936
assert!(text.contains("Welcome to Cortex CLI"), "{text}");
933937
assert!(text.contains("Cortex Foundation account"), "{text}");
934938
assert!(!text.to_lowercase().contains("grok"));
@@ -943,6 +947,13 @@ mod tests {
943947
let mut terminal = Terminal::new(backend).expect("test backend");
944948
terminal.draw(|f| screen.render(f)).expect("draw");
945949
let waiting = buffer_text(&terminal);
950+
if let Ok(dir) = std::env::var("CORTEX_DUMP_SNAPSHOTS") {
951+
let _ = std::fs::create_dir_all(&dir);
952+
let _ = std::fs::write(
953+
std::path::Path::new(&dir).join("auth_waiting.txt"),
954+
&waiting,
955+
);
956+
}
946957
assert!(
947958
waiting.contains("ABCD-1234")
948959
|| waiting.contains("device")

0 commit comments

Comments
 (0)