Merge current main into the 0.10.2 candidate

Brings in b5e270b690 (the TUI accepts MCP status from a replacement
engine), so the squash produces exactly the tree this candidate's CI
qualifies. The merge is textually clean, and the merged tree has no
spawn_tui_engine call with the old signature.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01NBKbhx19BRbZtvWK8rVKjz
This commit is contained in:
CodeWhale BotandClaude Opus 5.5 committed 2026-10-15 20:54:39 -07:00
commit 704d435db1
6 files changed
+14 -18

No files matched your search

+5 -3
View File
@@ -489,8 +489,10 @@ mod memory_quick_add_tests {
}
}
fn spawn_tui_engine(config: EngineConfig, api_config: &Config) -> EngineHandle {
let handle = spawn_engine(config, api_config);
fn spawn_tui_engine(app: &mut App, api_config: &Config) -> EngineHandle {
let handle = spawn_engine(build_engine_config(app, api_config), api_config);
app.mcp_snapshot_generation = 0;
app.mcp_snapshot_generation_invalidated = false;
// Prime durable agent + coordination state through the same engine event
// used by later refreshes. All TUI engine replacements use this wrapper,
// so workspace switches and provider recovery cannot retain stale Work.
@@ -502,7 +504,7 @@ fn spawn_tui_engine(config: EngineConfig, api_config: &Config) -> EngineHandle {
/// before admitting any pending input. The existing engine remains the sole
/// owner of model-facing history and the frozen system prefix.
async fn spawn_tui_engine_with_session(app: &mut App, config: &Config) -> Result<EngineHandle> {
let handle = spawn_tui_engine(build_engine_config(app, config), config);
let handle = spawn_tui_engine(app, config);
let restored = async {
if !app.api_messages.is_empty() {
handle
+5 -7
View File
@@ -1283,8 +1283,7 @@ pub(crate) async fn apply_provider_fallback_switch(
}
let _ = engine_handle.send(Op::Shutdown).await;
let engine_config = build_engine_config(app, config);
*engine_handle = spawn_tui_engine(engine_config, config);
*engine_handle = spawn_tui_engine(app, config);
if !app.api_messages.is_empty() {
let _ = engine_handle
@@ -1593,7 +1592,7 @@ async fn apply_command_result_inner(
sync_runtime_workspace_state(task_manager, app.workspace.clone()).await;
if respawn {
let _ = engine_handle.send(Op::Shutdown).await;
*engine_handle = spawn_tui_engine(build_engine_config(app, config), config);
*engine_handle = spawn_tui_engine(app, config);
} else {
let _ = engine_handle
.send(Op::SetModel {
@@ -1703,7 +1702,7 @@ async fn apply_command_result_inner(
app.update_model_compaction_budget();
if provider_changed || workspace_changed {
let _ = engine_handle.send(Op::Shutdown).await;
*engine_handle = spawn_tui_engine(build_engine_config(app, config), config);
*engine_handle = spawn_tui_engine(app, config);
}
// SyncSession carries the conversation but not resolved route
// limits. Refresh the engine's model first so a loaded,
@@ -1830,7 +1829,7 @@ async fn apply_command_result_inner(
);
}
let _ = engine_handle.send(Op::Shutdown).await;
*engine_handle = spawn_tui_engine(build_engine_config(app, config), config);
*engine_handle = spawn_tui_engine(app, config);
if !app.api_messages.is_empty() {
let _ = engine_handle
.send(Op::SyncSession {
@@ -2929,8 +2928,7 @@ async fn apply_command_result_inner(
crate::config::initialize_cloud_facts(config);
// Rebuild the engine with the new config so API key/model/base URL take effect.
let _ = engine_handle.send(Op::Shutdown).await;
let engine_config = build_engine_config(app, config);
*engine_handle = spawn_tui_engine(engine_config, config);
*engine_handle = spawn_tui_engine(app, config);
if !app.api_messages.is_empty() {
let _ = engine_handle
.send(Op::SyncSession {
+1 -2
View File
@@ -4563,8 +4563,7 @@ pub(crate) async fn run_event_loop(
}
if let Some(rollback_warning) = respawn_after_provider_rollback {
let _ = engine_handle.send(Op::Shutdown).await;
let engine_config = build_engine_config(app, config);
engine_handle = spawn_tui_engine(engine_config, config);
engine_handle = spawn_tui_engine(app, config);
if !app.api_messages.is_empty() {
let _ = engine_handle
.send(Op::SyncSession {
+1 -2
View File
@@ -2030,8 +2030,7 @@ pub(crate) async fn handle_view_events(
// only while a saturated engine finishes its turn.
if respawn {
let _ = engine_handle.send(Op::Shutdown).await;
*engine_handle =
spawn_tui_engine(build_engine_config(app, config), config);
*engine_handle = spawn_tui_engine(app, config);
} else {
let _ = engine_handle
.send(Op::SetModel {
+1 -2
View File
@@ -787,8 +787,7 @@ pub(crate) async fn switch_provider(
}
let _ = engine_handle.send(Op::Shutdown).await;
let engine_config = build_engine_config(app, config);
*engine_handle = spawn_tui_engine(engine_config, config);
*engine_handle = spawn_tui_engine(app, config);
// A successful in-session switch must refresh the same key-scoped live
// catalog as startup. TelecomJS is currently the only provider using this
// seam; failures preserve the existing/static rows.
+1 -2
View File
@@ -915,8 +915,7 @@ pub(crate) async fn switch_workspace(
sync_runtime_workspace_state(task_manager, workspace.clone()).await;
let _ = engine_handle.send(Op::Shutdown).await;
let engine_config = build_engine_config(app, config);
*engine_handle = spawn_tui_engine(engine_config, config);
*engine_handle = spawn_tui_engine(app, config);
if !app.api_messages.is_empty() {
let _ = engine_handle
.send(Op::SyncSession {