Merge branch 'release/v0.8.46'

This commit is contained in:
Hunter Bown
2026-05-26 12:00:08 -05:00
18 changed files with 106 additions and 97 deletions
+3 -2
View File
@@ -83,7 +83,7 @@ Thanks to new contributors whose PRs landed in this release:
**@zhuangbiaowei** (#2145),
**@aboimpinto** (#1872),
and continuing contributors **@reidliu41**, **@cyq1017**, **@idling11**,
**@h3c-hexin**, **@wdw8276**, and **@zlh124**.
**@h3c-hexin**, **@wdw8276**, **@zlh124**, and **@jeoor**.
## [0.8.45] - 2026-05-25
@@ -4968,7 +4968,8 @@ Welcome — and thank you.
- Hooks system and config profiles
- Example skills and launch assets
[Unreleased]: https://github.com/Hmbown/CodeWhale/compare/v0.8.45...HEAD
[Unreleased]: https://github.com/Hmbown/CodeWhale/compare/v0.8.46...HEAD
[0.8.46]: https://github.com/Hmbown/CodeWhale/compare/v0.8.45...v0.8.46
[0.8.45]: https://github.com/Hmbown/CodeWhale/compare/v0.8.44...v0.8.45
[0.8.44]: https://github.com/Hmbown/CodeWhale/compare/v0.8.43...v0.8.44
[0.8.43]: https://github.com/Hmbown/CodeWhale/compare/v0.8.42...v0.8.43
Generated
+14 -14
View File
@@ -803,7 +803,7 @@ checksum = "e9b18233253483ce2f65329a24072ec414db782531bdbb7d0bbc4bd2ce6b7e21"
[[package]]
name = "codewhale-agent"
version = "0.8.45"
version = "0.8.46"
dependencies = [
"codewhale-config",
"serde",
@@ -811,7 +811,7 @@ dependencies = [
[[package]]
name = "codewhale-app-server"
version = "0.8.45"
version = "0.8.46"
dependencies = [
"anyhow",
"axum",
@@ -836,7 +836,7 @@ dependencies = [
[[package]]
name = "codewhale-cli"
version = "0.8.45"
version = "0.8.46"
dependencies = [
"anyhow",
"chrono",
@@ -862,7 +862,7 @@ dependencies = [
[[package]]
name = "codewhale-config"
version = "0.8.45"
version = "0.8.46"
dependencies = [
"anyhow",
"codewhale-secrets",
@@ -875,7 +875,7 @@ dependencies = [
[[package]]
name = "codewhale-core"
version = "0.8.45"
version = "0.8.46"
dependencies = [
"anyhow",
"chrono",
@@ -893,7 +893,7 @@ dependencies = [
[[package]]
name = "codewhale-execpolicy"
version = "0.8.45"
version = "0.8.46"
dependencies = [
"anyhow",
"codewhale-protocol",
@@ -902,7 +902,7 @@ dependencies = [
[[package]]
name = "codewhale-hooks"
version = "0.8.45"
version = "0.8.46"
dependencies = [
"anyhow",
"async-trait",
@@ -916,7 +916,7 @@ dependencies = [
[[package]]
name = "codewhale-mcp"
version = "0.8.45"
version = "0.8.46"
dependencies = [
"anyhow",
"serde",
@@ -925,7 +925,7 @@ dependencies = [
[[package]]
name = "codewhale-protocol"
version = "0.8.45"
version = "0.8.46"
dependencies = [
"serde",
"serde_json",
@@ -933,7 +933,7 @@ dependencies = [
[[package]]
name = "codewhale-secrets"
version = "0.8.45"
version = "0.8.46"
dependencies = [
"dirs",
"keyring",
@@ -946,7 +946,7 @@ dependencies = [
[[package]]
name = "codewhale-state"
version = "0.8.45"
version = "0.8.46"
dependencies = [
"anyhow",
"chrono",
@@ -958,7 +958,7 @@ dependencies = [
[[package]]
name = "codewhale-tools"
version = "0.8.45"
version = "0.8.46"
dependencies = [
"anyhow",
"async-trait",
@@ -971,7 +971,7 @@ dependencies = [
[[package]]
name = "codewhale-tui"
version = "0.8.45"
version = "0.8.46"
dependencies = [
"anyhow",
"arboard",
@@ -1037,7 +1037,7 @@ dependencies = [
[[package]]
name = "codewhale-tui-core"
version = "0.8.45"
version = "0.8.46"
[[package]]
name = "colorchoice"
+1 -1
View File
@@ -19,7 +19,7 @@ default-members = ["crates/cli", "crates/app-server", "crates/tui"]
resolver = "2"
[workspace.package]
version = "0.8.45"
version = "0.8.46"
edition = "2024"
# Rust 1.88 stabilized `let_chains` in `if`/`while` conditions, which the
# codebase relies on extensively. Cargo enforces this so users on older
+1 -1
View File
@@ -7,5 +7,5 @@ repository.workspace = true
description = "Model/provider registry and fallback strategy for DeepSeek workspace architecture"
[dependencies]
codewhale-config = { path = "../config", version = "0.8.45" }
codewhale-config = { path = "../config", version = "0.8.46" }
serde.workspace = true
+9 -9
View File
@@ -10,15 +10,15 @@ description = "Codex-style app-server transport for DeepSeek workspace architect
anyhow.workspace = true
axum.workspace = true
clap.workspace = true
codewhale-agent = { path = "../agent", version = "0.8.45" }
codewhale-config = { path = "../config", version = "0.8.45" }
codewhale-core = { path = "../core", version = "0.8.45" }
codewhale-execpolicy = { path = "../execpolicy", version = "0.8.45" }
codewhale-hooks = { path = "../hooks", version = "0.8.45" }
codewhale-mcp = { path = "../mcp", version = "0.8.45" }
codewhale-protocol = { path = "../protocol", version = "0.8.45" }
codewhale-state = { path = "../state", version = "0.8.45" }
codewhale-tools = { path = "../tools", version = "0.8.45" }
codewhale-agent = { path = "../agent", version = "0.8.46" }
codewhale-config = { path = "../config", version = "0.8.46" }
codewhale-core = { path = "../core", version = "0.8.46" }
codewhale-execpolicy = { path = "../execpolicy", version = "0.8.46" }
codewhale-hooks = { path = "../hooks", version = "0.8.46" }
codewhale-mcp = { path = "../mcp", version = "0.8.46" }
codewhale-protocol = { path = "../protocol", version = "0.8.46" }
codewhale-state = { path = "../state", version = "0.8.46" }
codewhale-tools = { path = "../tools", version = "0.8.46" }
serde.workspace = true
serde_json.workspace = true
tokio.workspace = true
+7 -7
View File
@@ -25,13 +25,13 @@ path = "src/bin/deepseek_legacy_shim.rs"
anyhow.workspace = true
clap.workspace = true
clap_complete.workspace = true
codewhale-agent = { path = "../agent", version = "0.8.45" }
codewhale-app-server = { path = "../app-server", version = "0.8.45" }
codewhale-config = { path = "../config", version = "0.8.45" }
codewhale-execpolicy = { path = "../execpolicy", version = "0.8.45" }
codewhale-mcp = { path = "../mcp", version = "0.8.45" }
codewhale-secrets = { path = "../secrets", version = "0.8.45" }
codewhale-state = { path = "../state", version = "0.8.45" }
codewhale-agent = { path = "../agent", version = "0.8.46" }
codewhale-app-server = { path = "../app-server", version = "0.8.46" }
codewhale-config = { path = "../config", version = "0.8.46" }
codewhale-execpolicy = { path = "../execpolicy", version = "0.8.46" }
codewhale-mcp = { path = "../mcp", version = "0.8.46" }
codewhale-secrets = { path = "../secrets", version = "0.8.46" }
codewhale-state = { path = "../state", version = "0.8.46" }
chrono.workspace = true
dirs.workspace = true
serde.workspace = true
+1 -1
View File
@@ -8,7 +8,7 @@ description = "Config schema and precedence model for DeepSeek workspace archite
[dependencies]
anyhow.workspace = true
codewhale-secrets = { path = "../secrets", version = "0.8.45" }
codewhale-secrets = { path = "../secrets", version = "0.8.46" }
dirs.workspace = true
serde.workspace = true
serde_json.workspace = true
+8 -8
View File
@@ -9,13 +9,13 @@ description = "Core runtime boundaries for DeepSeek workspace architecture"
[dependencies]
anyhow.workspace = true
chrono.workspace = true
codewhale-agent = { path = "../agent", version = "0.8.45" }
codewhale-config = { path = "../config", version = "0.8.45" }
codewhale-execpolicy = { path = "../execpolicy", version = "0.8.45" }
codewhale-hooks = { path = "../hooks", version = "0.8.45" }
codewhale-mcp = { path = "../mcp", version = "0.8.45" }
codewhale-protocol = { path = "../protocol", version = "0.8.45" }
codewhale-state = { path = "../state", version = "0.8.45" }
codewhale-tools = { path = "../tools", version = "0.8.45" }
codewhale-agent = { path = "../agent", version = "0.8.46" }
codewhale-config = { path = "../config", version = "0.8.46" }
codewhale-execpolicy = { path = "../execpolicy", version = "0.8.46" }
codewhale-hooks = { path = "../hooks", version = "0.8.46" }
codewhale-mcp = { path = "../mcp", version = "0.8.46" }
codewhale-protocol = { path = "../protocol", version = "0.8.46" }
codewhale-state = { path = "../state", version = "0.8.46" }
codewhale-tools = { path = "../tools", version = "0.8.46" }
serde_json.workspace = true
uuid.workspace = true
+1 -1
View File
@@ -8,5 +8,5 @@ description = "Execution policy and approval model parity for DeepSeek workspace
[dependencies]
anyhow.workspace = true
codewhale-protocol = { path = "../protocol", version = "0.8.45" }
codewhale-protocol = { path = "../protocol", version = "0.8.46" }
serde.workspace = true
+1 -1
View File
@@ -10,7 +10,7 @@ description = "Hook dispatch and notifications parity for DeepSeek workspace arc
anyhow.workspace = true
async-trait.workspace = true
chrono.workspace = true
codewhale-protocol = { path = "../protocol", version = "0.8.45" }
codewhale-protocol = { path = "../protocol", version = "0.8.46" }
reqwest.workspace = true
serde.workspace = true
serde_json.workspace = true
+1 -1
View File
@@ -9,7 +9,7 @@ description = "Tool invocation lifecycle, schema validation, and scheduler paral
[dependencies]
anyhow.workspace = true
async-trait.workspace = true
codewhale-protocol = { path = "../protocol", version = "0.8.45" }
codewhale-protocol = { path = "../protocol", version = "0.8.46" }
serde.workspace = true
serde_json.workspace = true
tokio.workspace = true
+3 -2
View File
@@ -83,7 +83,7 @@ Thanks to new contributors whose PRs landed in this release:
**@zhuangbiaowei** (#2145),
**@aboimpinto** (#1872),
and continuing contributors **@reidliu41**, **@cyq1017**, **@idling11**,
**@h3c-hexin**, **@wdw8276**, and **@zlh124**.
**@h3c-hexin**, **@wdw8276**, **@zlh124**, and **@jeoor**.
## [0.8.45] - 2026-05-25
@@ -4968,7 +4968,8 @@ Welcome — and thank you.
- Hooks system and config profiles
- Example skills and launch assets
[Unreleased]: https://github.com/Hmbown/CodeWhale/compare/v0.8.45...HEAD
[Unreleased]: https://github.com/Hmbown/CodeWhale/compare/v0.8.46...HEAD
[0.8.46]: https://github.com/Hmbown/CodeWhale/compare/v0.8.45...v0.8.46
[0.8.45]: https://github.com/Hmbown/CodeWhale/compare/v0.8.44...v0.8.45
[0.8.44]: https://github.com/Hmbown/CodeWhale/compare/v0.8.43...v0.8.44
[0.8.43]: https://github.com/Hmbown/CodeWhale/compare/v0.8.42...v0.8.43
+3 -3
View File
@@ -27,9 +27,9 @@ path = "src/bin/deepseek_tui_legacy_shim.rs"
[dependencies]
anyhow = "1.0.100"
arboard = "3.4"
codewhale-config = { path = "../config", version = "0.8.45" }
codewhale-secrets = { path = "../secrets", version = "0.8.45" }
codewhale-tools = { path = "../tools", version = "0.8.45" }
codewhale-config = { path = "../config", version = "0.8.46" }
codewhale-secrets = { path = "../secrets", version = "0.8.46" }
codewhale-tools = { path = "../tools", version = "0.8.46" }
schemaui = { version = "0.12.0", default-features = false, optional = true }
async-stream = "0.3.6"
async-trait = "0.1"
+9 -7
View File
@@ -12,7 +12,9 @@
mod tests {
use crate::palette::{
CATPPUCCIN_MOCHA_UI_THEME, DRACULA_UI_THEME, GRAYSCALE_UI_THEME, GRUVBOX_DARK_UI_THEME,
LIGHT_UI_THEME, TOKYO_NIGHT_UI_THEME, UI_THEME, UiTheme,
LIGHT_UI_THEME, TOKYO_NIGHT_UI_THEME, UI_THEME, UiTheme, WHALE_ACCENT_ACTION_RGB,
WHALE_ACCENT_PRIMARY_RGB, WHALE_ACCENT_SECONDARY_RGB, WHALE_BG_RGB, WHALE_TEXT_BODY_RGB,
WHALE_TEXT_MUTED_RGB,
};
use ratatui::style::Color;
@@ -288,30 +290,30 @@ mod tests {
fn whale_dark_uses_proposed_palette() {
// Issue #2012: verify the default Whale dark uses proposed tokens.
let t = UI_THEME;
assert_eq!(rgb(t.surface_bg), Some((13, 21, 37)), "Deep Navy #0D1525");
assert_eq!(rgb(t.surface_bg), Some(WHALE_BG_RGB), "Deep Navy #0A1120");
assert_eq!(
rgb(t.text_body),
Some((246, 242, 232)),
Some(WHALE_TEXT_BODY_RGB),
"Whale Ivory #F6F2E8"
);
assert_eq!(
rgb(t.text_muted),
Some((169, 180, 199)),
Some(WHALE_TEXT_MUTED_RGB),
"Mist Gray #A9B4C7"
);
assert_eq!(
rgb(t.accent_primary),
Some((246, 196, 83)),
Some(WHALE_ACCENT_PRIMARY_RGB),
"Signal Gold #F6C453"
);
assert_eq!(
rgb(t.accent_secondary),
Some((79, 209, 197)),
Some(WHALE_ACCENT_SECONDARY_RGB),
"Seafoam #4FD1C5"
);
assert_eq!(
rgb(t.accent_action),
Some((255, 122, 89)),
Some(WHALE_ACCENT_ACTION_RGB),
"Coral Spark #FF7A59"
);
assert_eq!(rgb(t.error_fg), Some((255, 92, 122)), "Rose Red #FF5C7A");
+29 -25
View File
@@ -3145,13 +3145,10 @@ impl App {
self.insert_str(&normalized);
}
self.paste_burst.clear_after_explicit_paste();
// Visible-before-submit consolidation: when the post-paste input
// is over the cap, swap it for an @paste-…md mention immediately
// (instead of waiting until the user presses Enter and getting
// surprised by an auto-sent @mention). The same logic runs as a
// safety-net at submit time so any other code path that fills
// self.input above the cap still consolidates rather than
// silently truncating.
// Large pasted input stays editable and visible until submit. The
// submit-time safety net consolidates oversized composer content into
// an @paste-...md mention before dispatch, so no path silently
// truncates user input.
// self.consolidate_large_input_if_oversized(); // deferred to submit time
}
@@ -5324,12 +5321,10 @@ mod tests {
}
#[test]
fn paste_consolidates_oversized_text_into_paste_file_visibly() {
// Visible-before-submit consolidation (paste UX): when a single
// bracketed paste exceeds the safety cap, the @mention must
// replace the input *immediately*, so the user sees what's
// about to be sent before pressing Enter — not as a side effect
// of submit.
fn paste_defers_oversized_text_consolidation_until_submit() {
// #2168: a large paste stays inline so the user can still edit it.
// Submit-time consolidation then writes the paste file and sends the
// @mention instead of the raw oversized content.
let tmp = tempfile::TempDir::new().expect("tempdir");
let mut opts = test_options(false);
opts.workspace = tmp.path().to_path_buf();
@@ -5338,26 +5333,35 @@ mod tests {
app.insert_paste_text(&full_content);
// Composer should now contain the @mention, not the full text.
assert!(
app.input.starts_with("@.deepseek/pastes/paste-") && app.input.ends_with(".md"),
"expected @mention in composer after large paste, got: {}",
app.input
);
// The cursor moves to the end of the @mention.
assert_eq!(app.input, full_content);
assert_eq!(app.cursor_position, app.input.chars().count());
// The paste file must exist with the full content.
let rel_path = &app.input[1..];
let pastes_dir = tmp.path().join(".deepseek/pastes");
assert!(
!pastes_dir.exists() || std::fs::read_dir(&pastes_dir).unwrap().next().is_none(),
"paste file should not be written before submit"
);
assert!(
app.status_toasts
.iter()
.all(|toast| !toast.text.contains("consolidated")),
"consolidation toast should not appear before submit"
);
let submitted = app.submit_input().expect("expected submitted input");
assert!(
submitted.starts_with("@.deepseek/pastes/paste-") && submitted.ends_with(".md"),
"expected @mention after submit, got: {submitted}"
);
let rel_path = &submitted[1..];
let abs = tmp.path().join(rel_path);
assert!(abs.is_file(), "paste file must exist at {abs:?}");
let written = std::fs::read_to_string(&abs).expect("read");
assert_eq!(written, full_content);
// A toast confirms what happened so the user isn't surprised.
assert!(
app.status_toasts
.iter()
.any(|t| t.text.contains("consolidated")),
"expected consolidation toast"
.any(|toast| toast.text.contains("consolidated")),
"expected consolidation toast after submit"
);
}
+12 -11
View File
@@ -8,8 +8,8 @@
//! - **BEL** — audible bell (`\x07`) as a last-resort fallback.
//!
//! When `method = "auto"`, the resolver picks the best method for the
//! current terminal; Windows falls back to `Off` to avoid the error chime
//! (#583).
//! current terminal; Windows falls back to `Bel`, which is routed through
//! `MessageBeep(MB_OK)` for an audible default notification sound.
#[cfg(target_os = "windows")]
use windows::Win32::System::Diagnostics::Debug::MessageBeep;
@@ -68,7 +68,7 @@ fn windows_bell() {
/// - `$TERM` contains `ghostty` → `Osc9` (cmux etc.)
/// - `$TERM` contains `kitty` → `Kitty`
/// - Unix unknown → `Bel`
/// - Windows unknown → `Off`
/// - Windows unknown → `Bel`
#[must_use]
fn resolve_method() -> Method {
let term_program = std::env::var("TERM_PROGRAM").unwrap_or_default();
@@ -199,8 +199,8 @@ pub fn notify_done_to<W: Write>(
///
/// With `method = Auto`, selects the best protocol for the current terminal
/// (OSC 9, Kitty OSC 99, Ghostty OSC 777, or Bel). The unknown-terminal
/// fallback is platform-aware `Bel` on macOS / Linux, `Off` on Windows
/// (where BEL maps to the `SystemAsterisk` / `MB_OK` error chime, #583).
/// fallback is platform-aware: `Bel` on every platform, with Windows routing
/// it through `MessageBeep(MB_OK)` for a default system notification sound.
/// See [`resolve_method`] for the canonical resolution table. Pass
/// `in_tmux = true` (i.e. `$TMUX` is non-empty at runtime) to wrap OSC
/// sequences in a DCS passthrough.
@@ -620,7 +620,9 @@ mod tests {
/// when the test harness runs them in parallel threads.
fn env_lock() -> std::sync::MutexGuard<'static, ()> {
static LOCK: OnceLock<Mutex<()>> = OnceLock::new();
LOCK.get_or_init(|| Mutex::new(())).lock().unwrap()
LOCK.get_or_init(|| Mutex::new(()))
.lock()
.unwrap_or_else(|poisoned| poisoned.into_inner())
}
fn capture(
@@ -827,12 +829,11 @@ mod tests {
assert_eq!(resolved, Method::Bel);
}
/// #583: on Windows, an unknown TERM_PROGRAM resolves to `Off`
/// (not `Bel`) so the post-turn notification doesn't ring the
/// `SystemAsterisk` / `MB_OK` chime.
/// #2166: on Windows, an unknown TERM_PROGRAM resolves to `Bel` so
/// `windows_bell()` can route the notification through `MessageBeep`.
#[test]
#[cfg(target_os = "windows")]
fn auto_detect_picks_off_for_unknown_on_windows() {
fn auto_detect_picks_bel_for_unknown_on_windows() {
let _lock = env_lock();
let prev = std::env::var_os("TERM_PROGRAM");
// SAFETY: test-only; serialised by env_lock().
@@ -845,7 +846,7 @@ mod tests {
None => std::env::remove_var("TERM_PROGRAM"),
}
}
assert_eq!(resolved, Method::Off);
assert_eq!(resolved, Method::Bel);
}
/// #583: known OSC-9 terminals must still resolve to `Osc9` on
+2 -2
View File
@@ -1,7 +1,7 @@
{
"name": "codewhale",
"version": "0.8.45",
"codewhaleBinaryVersion": "0.8.45",
"version": "0.8.46",
"codewhaleBinaryVersion": "0.8.46",
"description": "Install and run CodeWhale, the agentic terminal for open-source and open-weight coding models, from GitHub release artifacts.",
"author": "Hmbown",
"license": "MIT",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "deepseek-tui",
"version": "0.8.45",
"version": "0.8.46",
"description": "Legacy compatibility package. Renamed to `codewhale`; run `npm install -g codewhale` for new installs.",
"author": "Hmbown",
"license": "MIT",