chore(release): prepare v0.8.45#2118
Conversation
Replace the sequential-spawn-index whale-nickname system with a deterministic hash-based naming scheme that maps each agent ID to a stable whale species name. The same agent ID always gets the same friendly name — even across session restarts for persisted agents. - whale_name_for_id(id): hash agent ID → WHALE_NICKNAMES index - assign_unique_whale_name(id, active_names): deterministic with collision avoidance, appends numeric suffix when base name is taken - Expand WHALE_NICKNAMES from 25 to ~45 Cetacea species including baleen whales, toothed whales, and select dolphins (Delphinidae); porpoises excluded as labels that don't carry well - SubAgent::new now accepts a pre-generated id parameter so the spawn method can hash it before construction - SubAgentsView popup now shows friendly nickname next to raw agent ID (dimmed) instead of hiding it - live_subagent_result accepts optional nickname parameter - whale_nickname_for_index kept as legacy public API for test snapshots 137 sub-agent tests pass. Taxonomy source: Society for Marine Mammalogy (2025).
There was a problem hiding this comment.
Code Review
This pull request updates the project to version 0.8.45, introducing OpenAI model registry updates, bearer token authentication for the app server, and stricter project-level configuration overrides. UI improvements include a refreshed 'Whale' dark palette, a /balance command scaffold, cache savings displays, and deterministic sub-agent nicknames. Security and stability are enhanced through path validation for skills and the addition of timeouts to file-system tools. Review feedback highlights that background tasks for directory listing and file searching are not signaled to stop upon timeout, potentially leading to orphaned threads and resource exhaustion on large filesystems.
| async fn run_blocking_list_dir<F>( | ||
| timeout: Duration, | ||
| cancel_token: Option<CancellationToken>, | ||
| list_dir: F, | ||
| ) -> Result<Vec<Value>, ToolError> | ||
| where | ||
| F: FnOnce() -> Result<Vec<Value>, ToolError> + Send + 'static, | ||
| { | ||
| if cancel_token | ||
| .as_ref() | ||
| .is_some_and(CancellationToken::is_cancelled) | ||
| { | ||
| return Err(list_dir_cancelled()); | ||
| } | ||
|
|
||
| let task = tokio::task::spawn_blocking(list_dir); | ||
| let result = match cancel_token { | ||
| Some(token) => { | ||
| tokio::select! { | ||
| biased; | ||
| () = token.cancelled() => return Err(list_dir_cancelled()), | ||
| result = tokio::time::timeout(timeout, task) => result, | ||
| } | ||
| } | ||
| None => tokio::time::timeout(timeout, task).await, | ||
| }; | ||
|
|
||
| ToolResult::json(&entries).map_err(|e| ToolError::execution_failed(e.to_string())) | ||
| let joined = result.map_err(|_| list_dir_timeout(timeout))?; | ||
| joined.map_err(|err| { | ||
| ToolError::execution_failed(format!("list_dir worker failed before completion: {err}")) | ||
| })? | ||
| } |
There was a problem hiding this comment.
The spawn_blocking task for directory listing is not signaled to stop when a timeout occurs. While tokio::time::timeout correctly returns an error to the caller, the background thread running the directory walk will continue to execute until it either finishes or the turn's cancel_token is manually triggered by the user. On large or slow filesystems, this can lead to a build-up of orphaned threads. Consider creating a child cancellation token that is explicitly cancelled when the timeout expires to signal the worker to stop immediately.
| async fn run_blocking_file_search<F>( | ||
| timeout: Duration, | ||
| cancel_token: Option<CancellationToken>, | ||
| search: F, | ||
| ) -> Result<Vec<FileSearchMatch>, ToolError> | ||
| where | ||
| F: FnOnce() -> Result<Vec<FileSearchMatch>, ToolError> + Send + 'static, | ||
| { | ||
| if cancel_token | ||
| .as_ref() | ||
| .is_some_and(CancellationToken::is_cancelled) | ||
| { | ||
| return Err(file_search_cancelled()); | ||
| } | ||
|
|
||
| let task = tokio::task::spawn_blocking(search); | ||
| let result = match cancel_token { | ||
| Some(token) => { | ||
| tokio::select! { | ||
| biased; | ||
| () = token.cancelled() => return Err(file_search_cancelled()), | ||
| result = tokio::time::timeout(timeout, task) => result, | ||
| } | ||
| } | ||
| None => tokio::time::timeout(timeout, task).await, | ||
| }; | ||
|
|
||
| let joined = result.map_err(|_| file_search_timeout(timeout))?; | ||
| joined.map_err(|err| { | ||
| ToolError::execution_failed(format!( | ||
| "file_search worker failed before completion: {err}" | ||
| )) | ||
| })? | ||
| } |
There was a problem hiding this comment.
Similar to the directory listing tool, the file search worker is not signaled to stop upon timeout. The spawn_blocking task will continue to consume resources in the background even after the TUI has reported a timeout to the user. This is particularly problematic for deep recursive searches on large volumes. The worker should be provided with a cancellation signal that triggers on both user intervention and tool-specific timeout to prevent thread pool exhaustion.
There was a problem hiding this comment.
Pull request overview
Prepare CodeWhale v0.8.45 as a maintenance release, syncing version metadata while landing hardening and UX improvements across the TUI/tools/config/app-server surfaces.
Changes:
- Release/version bumps and changelog/contributor credit updates for v0.8.45 (Cargo + npm + docs).
- Tooling/UI improvements: cancellable
list_dir/file_search, sub-agent whale nicknames + display updates, cache-savings footer hint, palette/theme audits,/balancescaffold. - Hardening: safer project-config overlay rules, skill install path validation, app-server HTTP auth + constrained CORS, OpenAI/Codex auth & model registry updates.
Reviewed changes
Copilot reviewed 46 out of 47 changed files in this pull request and generated 5 comments.
Show a summary per file
| File | Description |
|---|---|
| web/app/[locale]/faq/page.tsx | Updates FAQ copy for /goal behavior. |
| README.md | Updates contributor credits. |
| npm/deepseek-tui/package.json | Bumps npm wrapper version to 0.8.45. |
| npm/codewhale/package.json | Bumps package + binary pin to 0.8.45. |
| crates/tui/tests/palette_audit.rs | Adjusts palette audit assertions to semantic tokens. |
| crates/tui/src/tui/views/mod.rs | Sub-agent list rendering now shows nickname + id. |
| crates/tui/src/tui/ui/tests.rs | Updates session metadata fixtures with new field(s). |
| crates/tui/src/tui/ui.rs | Restores persisted cumulative turn duration on session load. |
| crates/tui/src/tui/session_picker.rs | Updates session metadata fixtures with new field(s). |
| crates/tui/src/tui/markdown_render.rs | Updates test strings to remove current-release wording. |
| crates/tui/src/tui/footer_ui.rs | Adds cache-savings hint in footer cost display. |
| crates/tui/src/tui/command_palette.rs | Adds “Action” section and related filtering/sorting behavior. |
| crates/tui/src/tui/color_compat.rs | Aligns test to palette alias token. |
| crates/tui/src/tui/app.rs | Persists cumulative turn duration; exposes last-turn cache savings. |
| crates/tui/src/tools/subagent/tests.rs | Updates sub-agent constructor usage to accept caller-provided id. |
| crates/tui/src/tools/subagent/mod.rs | Deterministic whale naming + unique assignment; caller-provided agent id. |
| crates/tui/src/tools/file.rs | Makes list_dir cancellable + timeout-protected via blocking worker. |
| crates/tui/src/tools/file_search.rs | Makes file_search cancellable + timeout-protected via blocking worker. |
| crates/tui/src/theme_qa_audit.rs | Adds theme QA audit tests for palette completeness/contrast. |
| crates/tui/src/skills/install.rs | Hardens skill name/path handling to prevent traversal/escape. |
| crates/tui/src/session_manager.rs | Persists cumulative turn duration in session metadata. |
| crates/tui/src/pricing.rs | Adds helper to estimate cache-hit savings. |
| crates/tui/src/palette.rs | Introduces Whale palette tokens, semantic UiTheme fields, updated mappings. |
| crates/tui/src/models.rs | Updates context-window heuristics for more OpenAI model IDs. |
| crates/tui/src/main.rs | Wires theme QA module; tightens project overlay policy handling. |
| crates/tui/src/localization.rs | Adds localized help text for /balance. |
| crates/tui/src/config.rs | Adds OpenAI model list + additional OpenAI provider aliases. |
| crates/tui/src/commands/mod.rs | Registers /balance command and adds tests. |
| crates/tui/src/commands/balance.rs | Adds /balance scaffold behavior. |
| crates/tui/CHANGELOG.md | Adds v0.8.45 changelog entry and compare link. |
| crates/tui/Cargo.toml | Bumps internal crate dependency versions to 0.8.45. |
| crates/tools/Cargo.toml | Bumps protocol dependency version to 0.8.45. |
| crates/hooks/Cargo.toml | Bumps protocol dependency version to 0.8.45. |
| crates/execpolicy/Cargo.toml | Bumps protocol dependency version to 0.8.45. |
| crates/core/Cargo.toml | Bumps internal crate dependency versions to 0.8.45. |
| crates/config/src/lib.rs | Hardens project overlay merge; adds OpenAI OAuth token sourcing + ranks. |
| crates/config/Cargo.toml | Bumps secrets dep + adds serde_json dependency. |
| crates/cli/src/lib.rs | Adds Codex OAuth login/status support; app-server auth/cors flags; env pass-through. |
| crates/cli/Cargo.toml | Bumps internal crate dependency versions to 0.8.45. |
| crates/app-server/src/main.rs | Adds auth token, insecure-no-auth, and CORS origin CLI flags. |
| crates/app-server/src/lib.rs | Requires bearer token for HTTP routes; constrained CORS; redacts config over HTTP. |
| crates/app-server/Cargo.toml | Bumps internal crate versions; adds uuid + dev-deps for tests. |
| crates/agent/src/lib.rs | Expands OpenAI model registry entries + aliasing; adjusts reasoning support flags. |
| crates/agent/Cargo.toml | Bumps config dep version to 0.8.45. |
| crates/tui/CHANGELOG.md | Adds v0.8.45 entry mirroring root changelog. |
| CHANGELOG.md | Adds v0.8.45 entry and compare link. |
| Cargo.toml | Bumps workspace version to 0.8.45. |
| Cargo.lock | Updates lockfile versions and adds new deps. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| let task = tokio::task::spawn_blocking(list_dir); | ||
| let result = match cancel_token { | ||
| Some(token) => { | ||
| tokio::select! { | ||
| biased; |
| let task = tokio::task::spawn_blocking(search); | ||
| let result = match cancel_token { | ||
| Some(token) => { | ||
| tokio::select! { | ||
| biased; | ||
| () = token.cancelled() => return Err(file_search_cancelled()), | ||
| result = tokio::time::timeout(timeout, task) => result, | ||
| } | ||
| } | ||
| None => tokio::time::timeout(timeout, task).await, | ||
| }; | ||
|
|
||
| let joined = result.map_err(|_| file_search_timeout(timeout))?; | ||
| joined.map_err(|err| { | ||
| ToolError::execution_failed(format!( | ||
| "file_search worker failed before completion: {err}" | ||
| )) | ||
| })? |
| pub fn whale_name_for_id(id: &str) -> String { | ||
| use std::hash::{Hash, Hasher}; | ||
| let mut hasher = std::collections::hash_map::DefaultHasher::new(); | ||
| id.hash(&mut hasher); | ||
| let idx = (hasher.finish() as usize) % WHALE_NICKNAMES.len(); | ||
| WHALE_NICKNAMES[idx].to_string() | ||
| } |
| // Deterministic suffix from the same hash to keep it stable | ||
| use std::hash::{Hash, Hasher}; | ||
| let mut hasher = std::collections::hash_map::DefaultHasher::new(); | ||
| id.hash(&mut hasher); | ||
| let suffix_seed = hasher.finish(); |
| let display_name = agent | ||
| .nickname | ||
| .as_deref() | ||
| .map(|nick| format!("{nick:<12}")) | ||
| .unwrap_or_else(|| format!("{id:<12}")); |
62d1771 to
b982212
Compare
Summary
Prepare CodeWhale v0.8.45 as a focused maintenance release: RLM session objects, cancellable directory/search tools, deterministic whale-species agent names, the /balance scaffold, contributor credit updates, config/runtime hardening, palette audit cleanup, and release metadata sync. Voice input and its hotkey are intentionally deferred to v0.8.46.
Changes since v0.8.44
session://object loading (v0.8.45: add voice input, RLM session objects, and slash recovery fix #2047)./balancescaffold for future provider billing support (feat(file): cancellable list_dir with timeout (control-plane workstream) #2035, v0.8.45 provider billing: audit cost math and add /balance command #2019).list_dirandfile_searchwith fallback timeout (feat(file): cancellable list_dir with timeout (control-plane workstream) #2035).Security
No Security changelog entries in this release.
Contributor thanks
Thanks @gaord, @zlh124, and @reidliu41 for the harvested fixes and reports credited in this release.
Local release checklist
./scripts/release/check-versions.sh— passed (workspace=0.8.45,npm=0.8.45, lockfile in sync)cargo fmt --all -- --check— passedcargo check --workspace --all-targets --locked— passedcargo clippy --workspace --all-targets --all-features --locked -- -D warnings— passedcargo test --workspace --all-features --locked— passed (codewhale-tui:3342 passed; 0 failed; 3 ignored; all integration/doc tests green)./scripts/release/publish-crates.sh dry-run— passed; higher-level crates verified package contents and correctly wait for internal 0.8.45 dependencies to be published firstcargo build --release --locked -p codewhale-cli -p codewhale-tui— passed./target/release/codewhale --version—codewhale 0.8.45 (b9822129968e)./target/release/codewhale-tui --version—codewhale-tui 0.8.45 (b9822129968e)node scripts/release/npm-wrapper-smoke.js— passednpm --prefix web run lint— passedGoal mode,/mode goal, voice input names, and voice hotkeys — no matches outside ignored build/cache dirsKnown issues
None documented for v0.8.45.