diff --git a/crates/protocol/src/runtime/mod.rs b/crates/protocol/src/runtime/mod.rs index 1586bb81..0388b187 100644 --- a/crates/protocol/src/runtime/mod.rs +++ b/crates/protocol/src/runtime/mod.rs @@ -275,8 +275,10 @@ mod tests { r#"{"type":"input_text","text":"x"}"# ); assert_eq!( - serde_json::to_string(&DynamicToolCallContent::InputImage { image_url: "y".into() }) - .unwrap(), + serde_json::to_string(&DynamicToolCallContent::InputImage { + image_url: "y".into() + }) + .unwrap(), r#"{"type":"input_image","image_url":"y"}"# ); } @@ -293,7 +295,9 @@ mod tests { fn dynamic_tool_call_result_roundtrip_with_content() { let result = DynamicToolCallResult { success: true, - content: vec![DynamicToolCallContent::InputText { text: "done".into() }], + content: vec![DynamicToolCallContent::InputText { + text: "done".into(), + }], }; let serialized = serde_json::to_string(&result).unwrap(); @@ -352,6 +356,9 @@ mod tests { "payload": {} }"#; let envelope: RuntimeEventEnvelope = serde_json::from_str(json).unwrap(); - assert_eq!(envelope.schema_version, RUNTIME_EVENT_ENVELOPE_SCHEMA_VERSION); + assert_eq!( + envelope.schema_version, + RUNTIME_EVENT_ENVELOPE_SCHEMA_VERSION + ); } } diff --git a/crates/secrets/src/lib.rs b/crates/secrets/src/lib.rs index 81c7047e..9e97286f 100644 --- a/crates/secrets/src/lib.rs +++ b/crates/secrets/src/lib.rs @@ -127,7 +127,11 @@ impl DefaultKeyringStore { #[cfg(any( target_os = "macos", target_os = "windows", - all(target_os = "linux", not(target_env = "ohos"), not(target_env = "musl")) + all( + target_os = "linux", + not(target_env = "ohos"), + not(target_env = "musl") + ) ))] { // `Entry::new` is enough to validate the native macOS/Windows @@ -156,7 +160,11 @@ impl DefaultKeyringStore { #[cfg(not(any( target_os = "macos", target_os = "windows", - all(target_os = "linux", not(target_env = "ohos"), not(target_env = "musl")) + all( + target_os = "linux", + not(target_env = "ohos"), + not(target_env = "musl") + ) )))] { let _ = &self.service; @@ -170,7 +178,11 @@ impl KeyringStore for DefaultKeyringStore { #[cfg(any( target_os = "macos", target_os = "windows", - all(target_os = "linux", not(target_env = "ohos"), not(target_env = "musl")) + all( + target_os = "linux", + not(target_env = "ohos"), + not(target_env = "musl") + ) ))] { let entry = keyring::Entry::new(&self.service, key) @@ -184,7 +196,11 @@ impl KeyringStore for DefaultKeyringStore { #[cfg(not(any( target_os = "macos", target_os = "windows", - all(target_os = "linux", not(target_env = "ohos"), not(target_env = "musl")) + all( + target_os = "linux", + not(target_env = "ohos"), + not(target_env = "musl") + ) )))] { let _ = key; @@ -196,7 +212,11 @@ impl KeyringStore for DefaultKeyringStore { #[cfg(any( target_os = "macos", target_os = "windows", - all(target_os = "linux", not(target_env = "ohos"), not(target_env = "musl")) + all( + target_os = "linux", + not(target_env = "ohos"), + not(target_env = "musl") + ) ))] { let entry = keyring::Entry::new(&self.service, key) @@ -208,7 +228,11 @@ impl KeyringStore for DefaultKeyringStore { #[cfg(not(any( target_os = "macos", target_os = "windows", - all(target_os = "linux", not(target_env = "ohos"), not(target_env = "musl")) + all( + target_os = "linux", + not(target_env = "ohos"), + not(target_env = "musl") + ) )))] { let _ = (key, value); @@ -220,7 +244,11 @@ impl KeyringStore for DefaultKeyringStore { #[cfg(any( target_os = "macos", target_os = "windows", - all(target_os = "linux", not(target_env = "ohos"), not(target_env = "musl")) + all( + target_os = "linux", + not(target_env = "ohos"), + not(target_env = "musl") + ) ))] { let entry = keyring::Entry::new(&self.service, key) @@ -233,7 +261,11 @@ impl KeyringStore for DefaultKeyringStore { #[cfg(not(any( target_os = "macos", target_os = "windows", - all(target_os = "linux", not(target_env = "ohos"), not(target_env = "musl")) + all( + target_os = "linux", + not(target_env = "ohos"), + not(target_env = "musl") + ) )))] { let _ = key; @@ -249,7 +281,11 @@ impl KeyringStore for DefaultKeyringStore { #[cfg(not(any( target_os = "macos", target_os = "windows", - all(target_os = "linux", not(target_env = "ohos"), not(target_env = "musl")) + all( + target_os = "linux", + not(target_env = "ohos"), + not(target_env = "musl") + ) )))] fn unsupported_keyring_message() -> String { "system keyring backend is unsupported on this platform".to_string() diff --git a/crates/tui/src/commands/groups/core/provider.rs b/crates/tui/src/commands/groups/core/provider.rs index 47b32060..2bd96a2a 100644 --- a/crates/tui/src/commands/groups/core/provider.rs +++ b/crates/tui/src/commands/groups/core/provider.rs @@ -83,7 +83,10 @@ fn provider_fallback(app: &mut App, subcommand: Option<&str>) -> CommandResult { ); }; CommandResult::with_message_and_action( - format!("Fallback chain reset to primary provider: {}.", primary.as_str()), + format!( + "Fallback chain reset to primary provider: {}.", + primary.as_str() + ), AppAction::SwitchProvider { provider: primary, model: None, diff --git a/crates/tui/src/main.rs b/crates/tui/src/main.rs index c748e693..371554e8 100644 --- a/crates/tui/src/main.rs +++ b/crates/tui/src/main.rs @@ -1764,8 +1764,8 @@ fn resolve_cors_origins(config: &Config, flag_origins: &[String]) -> Vec for o in flag_origins { push(o); } - if let Ok(env_value) = std::env::var("CODEWHALE_CORS_ORIGINS") - .or_else(|_| std::env::var("DEEPSEEK_CORS_ORIGINS")) + if let Ok(env_value) = + std::env::var("CODEWHALE_CORS_ORIGINS").or_else(|_| std::env::var("DEEPSEEK_CORS_ORIGINS")) { for piece in env_value.split(',') { push(piece); diff --git a/crates/tui/src/runtime_api.rs b/crates/tui/src/runtime_api.rs index c1f4f0d0..4de55951 100644 --- a/crates/tui/src/runtime_api.rs +++ b/crates/tui/src/runtime_api.rs @@ -528,7 +528,9 @@ pub async fn run_http_server( if let Some(token) = runtime_token.as_deref() { println!("Runtime API auth: generated bearer token for this process."); println!(" Authorization: Bearer {token}"); - println!(" Set CODEWHALE_RUNTIME_TOKEN (or DEEPSEEK_RUNTIME_TOKEN as an alias) or pass --auth-token for a stable token."); + println!( + " Set CODEWHALE_RUNTIME_TOKEN (or DEEPSEEK_RUNTIME_TOKEN as an alias) or pass --auth-token for a stable token." + ); } } else if auth_enabled { println!("Runtime API auth: bearer token required for /v1/* routes."); diff --git a/crates/tui/src/runtime_threads.rs b/crates/tui/src/runtime_threads.rs index 75de6a9c..de7dc72c 100644 --- a/crates/tui/src/runtime_threads.rs +++ b/crates/tui/src/runtime_threads.rs @@ -3785,7 +3785,7 @@ mod tests { archived: false, system_prompt: None, task_id: None, - ..Default::default() + ..Default::default() }) .await?; @@ -3893,7 +3893,7 @@ mod tests { archived: false, system_prompt: None, task_id: None, - ..Default::default() + ..Default::default() }) .await?; @@ -3918,7 +3918,7 @@ mod tests { archived: false, system_prompt: None, task_id: None, - ..Default::default() + ..Default::default() }) .await?; @@ -3985,7 +3985,7 @@ mod tests { archived: false, system_prompt: None, task_id: None, - ..Default::default() + ..Default::default() }) .await?; @@ -4019,7 +4019,7 @@ mod tests { archived: false, system_prompt: None, task_id: None, - ..Default::default() + ..Default::default() }) .await?; @@ -4082,7 +4082,7 @@ mod tests { archived: false, system_prompt: None, task_id: None, - ..Default::default() + ..Default::default() }) .await?; @@ -4127,7 +4127,7 @@ mod tests { archived: false, system_prompt: None, task_id: None, - ..Default::default() + ..Default::default() }) .await?; @@ -4172,7 +4172,7 @@ mod tests { archived: false, system_prompt: None, task_id: None, - ..Default::default() + ..Default::default() }) .await?; @@ -4206,7 +4206,7 @@ mod tests { archived: false, system_prompt: None, task_id: None, - ..Default::default() + ..Default::default() }) .await?; @@ -4257,7 +4257,7 @@ mod tests { archived: false, system_prompt: None, task_id: None, - ..Default::default() + ..Default::default() }) .await?; @@ -4384,7 +4384,7 @@ mod tests { archived: false, system_prompt: None, task_id: None, - ..Default::default() + ..Default::default() }) .await?; @@ -4460,7 +4460,7 @@ mod tests { archived: false, system_prompt: None, task_id: None, - ..Default::default() + ..Default::default() }) .await?; @@ -4554,7 +4554,7 @@ mod tests { archived: false, system_prompt: None, task_id: None, - ..Default::default() + ..Default::default() }) .await?; @@ -4642,7 +4642,7 @@ mod tests { archived: false, system_prompt: None, task_id: None, - ..Default::default() + ..Default::default() }) .await?; @@ -4739,7 +4739,7 @@ mod tests { archived: false, system_prompt: None, task_id: None, - ..Default::default() + ..Default::default() }) .await?; @@ -4821,7 +4821,7 @@ mod tests { archived: false, system_prompt: None, task_id: None, - ..Default::default() + ..Default::default() }) .await?; let mut harness = install_mock_engine(&manager, &thread.id).await; @@ -4934,7 +4934,7 @@ mod tests { archived: false, system_prompt: None, task_id: None, - ..Default::default() + ..Default::default() }) .await?; assert!(!manager.store.load_thread(&thread.id)?.auto_approve); @@ -5020,7 +5020,7 @@ mod tests { archived: false, system_prompt: None, task_id: None, - ..Default::default() + ..Default::default() }) .await?; @@ -5107,7 +5107,7 @@ mod tests { archived: false, system_prompt: None, task_id: None, - ..Default::default() + ..Default::default() }) .await?; @@ -5218,7 +5218,7 @@ mod tests { archived: false, system_prompt: None, task_id: None, - ..Default::default() + ..Default::default() }) .await?; @@ -5734,7 +5734,7 @@ mod tests { archived: false, system_prompt: None, task_id: None, - ..Default::default() + ..Default::default() }) .await?; seed_turns_with_user_messages(&manager, &thread.id, &["first", "second", "third"])?; @@ -5771,7 +5771,7 @@ mod tests { archived: false, system_prompt: None, task_id: None, - ..Default::default() + ..Default::default() }) .await?; seed_turns_with_user_messages(&manager, &thread.id, &["a", "b", "c", "d"])?; @@ -5801,7 +5801,7 @@ mod tests { archived: false, system_prompt: None, task_id: None, - ..Default::default() + ..Default::default() }) .await?; seed_turns_with_user_messages(&manager, &thread.id, &["only"])?; @@ -5828,7 +5828,7 @@ mod tests { archived: false, system_prompt: None, task_id: None, - ..Default::default() + ..Default::default() }) .await?; let turn_ids = seed_turns_with_user_messages(&manager, &thread.id, &["x", "y", "z"])?; diff --git a/release-notes-v0.8.59.md b/release-notes-v0.8.59.md new file mode 100644 index 00000000..794b7fdf --- /dev/null +++ b/release-notes-v0.8.59.md @@ -0,0 +1,107 @@ +v0.8.59 + +CodeWhale v0.8.59 is a stability and integration release that hardens the TUI, +improves sidebar interactivity, localizes notifications, cleans up user-facing +naming, and adds experimental config and runtime API foundations. + +--- + +## TUI stability and interactivity + +- **Sidebar resize stays live during active turns** — the split pane no longer + freezes while the model is generating. +- **Sidebar hover stays live while loading** — detail popovers update in real + time instead of dropping mid-turn. +- **Hover highlight cleared on exit** — stale highlight state no longer + persists after leaving the sidebar. +- **Ghostty motion override kept live** — mouse motion events continue to + work correctly in Ghostty terminals. +- **Mouse-report sanitizer added** — raw SGR mouse reports are sanitized + defensively so corrupted composer input is blocked. +- **Sidebar Copy action** — the right-click context menu now includes Copy + alongside existing actions. +- **Richer Work overflow and Agents hover detail** — sidebar detail + popovers surface more information for Work and Agents entries. +- **Provider-wait state is now observable** — the TUI shows route, idle + budget, and fanout-preflight state during provider waits. +- **fanout launches are queued behind a visible launch gate** — interactive + fanout no longer stalls the TUI without feedback. + +## Sub-agent safety and worker-ledger substrate + +- **Sub-agents survive backgrounded waits** — worker cards are no longer + dropped when the TUI yields during long waits. +- **Interrupted sub-agent lifecycle events are emitted** — stale running + cards are reconciled with actual state. +- **Runtime-prompt stand-still guard** — the TUI no longer spins in an + autonomous loop when the launch gate is closed. + +## i18n and user-facing naming + +- **Notifications are now localized** — notification text respects the + configured language instead of always rendering in English. +- **Tool family labels are localized** — 10 tool family labels now use + MessageId-based i18n. +- **Config editor labels are localized** — config section editor labels + follow the configured language. +- **"Bash" shown in user-facing UI** — shell execution is surfaced as "Bash" + in the TUI while `exec_shell` remains the internal tool name. + +## Provider and model updates + +- **Kimi OAuth credentials aligned with Kimi Code** — the config path for + Kimi OAuth credentials now matches the Kimi Code provider surface. +- **Kimi K2.7 Code defaults added** — model metadata for Kimi K2.7 Code is + included. +- **SiliconFlow CN provider config split** — a separate provider entry for + SiliconFlow China is available. +- **Provider metadata registry refactored** — provider metadata is now + data-driven and easier to extend. +- **OpenRouter Nemotron preset fixed** — the invalid model ID is corrected. +- **Provider fallback chain activated** — harvested from community PR #2773. + +## Experimental config and runtime API + +- **Experimental feature flags** — `[experimental]` config section for goal + and WhaleFlow opt-ins, surfaced through normal config paths. +- **Runtime API Phase 0 + Phase 1** — brand-neutral naming, capabilities + advertisement, and dynamic tool protocol types for editor/GUI clients. +- **Command strategy registry** — harvested from community PR #2851. +- **Context source map report** — visibility into rules, tools, memory, and + skills contributions to prompt cost. + +## Community harvests + +- **PR #3010** — lock slim default prompt with calm-overlay regression test. +- **PR #2808** — thread undo/retry and snapshot restore endpoints. +- **PR #3051** — voice input commands and hotbar integration. +- **PR #2773** — activate provider fallback chain. +- **PR #2851** — command strategy registry. + +## Other fixes and improvements + +- **macOS Command modifier normalized to Control** for keyboard shortcuts + (#2938). +- **Hotbar slots dispatched from number keys**. +- **Thread goals persisted through the app server**. +- **Concise verbosity mode added** to config. +- **Workspace trust required for project hooks** — safety boundary + enforced. +- **Thread detail item reads batched** — N+1 query fix. +- **Legacy deepseek users guided to codewhale** in update paths. +- **Static Linux x64 musl binaries** now built. +- **Approval rule metadata exposed at runtime**. +- **Codex response errors clarified** in TUI. +- **Microsoft Build Tools / cmake --build** shell compatibility fix. +- **PDF extraction hardened** for non-Identity-H CMap fonts. + +--- + +**Full headless sub-agents (fleet manager, worker runtime, durable inbox/ledger) +are deferred to v0.8.60 / #3096.** The v0.8.59 release includes the +sub-agent safety and worker-ledger substrate that v0.8.60 builds on. + +**v0.8.60+ tracking issues remain open:** +- #3096 — Full headless sub-agents and worker runtime +- #1310 — MiniMax first-party provider +- #3187 — Z.ai / StepFlash first-party providers