Skip to content

feat(file): cancellable list_dir with timeout (control-plane workstream)#2035

Merged
Hmbown merged 5 commits into
mainfrom
codex/v0.8.45-control-plane-file-search
May 25, 2026
Merged

feat(file): cancellable list_dir with timeout (control-plane workstream)#2035
Hmbown merged 5 commits into
mainfrom
codex/v0.8.45-control-plane-file-search

Conversation

@Hmbown
Copy link
Copy Markdown
Owner

@Hmbown Hmbown commented May 24, 2026

Summary

Collects the v0.8.45 control-plane slices currently on this branch:

  • Keeps file_search traversal cancellable so user stop/cancel requests can interrupt long walks.
  • Makes list_dir cancellable and timeout-safe with a blocking worker and 30-second fallback timeout.
  • Adds deterministic, stable whale-species nicknames for sub-agents while preserving the raw agent id in the popup.
  • Registers the /balance slash command scaffold for v0.8.45 provider billing: audit cost math and add /balance command #2019 with honest placeholder behavior until provider balance network dispatch lands.

Stewardship / credit

Testing

  • cargo fmt --all -- --check
  • git diff --check
  • cargo clippy -p codewhale-tui --bin codewhale-tui --locked -- -D warnings
  • cargo test -p codewhale-tui --bin codewhale-tui file_search --locked
  • cargo test -p codewhale-tui --bin codewhale-tui list_dir --locked
  • cargo test -p codewhale-tui --bin codewhale-tui subagent --locked
  • cargo test -p codewhale-tui --bin codewhale-tui balance_command --locked
  • cargo test -p codewhale-tui --bin codewhale-tui every_registered_command_dispatches_to_a_handler --locked
  • cargo test -p codewhale-tui --bin codewhale-tui localization --locked

Follow-up

Hmbown added 2 commits May 24, 2026 18:25
Moves the synchronous fs::read_dir loop into a tokio::spawn_blocking
worker with CancellationToken and timeout. The tool honors the standard
context cancel token so in-flight directory listings are interrupted on
user cancel or engine stop, and a 30-second fallback timeout prevents
hung NFS/CIFS mounts from freezing the tool surface.

- list_dir_entries runs the blocking loop with periodic cancellation checks
- run_blocking_list_dir wraps it in spawn_blocking + tokio::select!
- Existing tests pass; adds test_list_dir_respects_cancel_token and
  test_list_dir_blocking_wrapper_reports_timeout
- ToolError::Timeout variant used for timeout reports

Part of the v0.8.45 control-plane cancellation workstream.
Copilot AI review requested due to automatic review settings May 24, 2026 23:47
Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces cancellation and timeout support for the ListDirTool and FileSearchTool. Both tools now execute their blocking file system operations within tokio::task::spawn_blocking and utilize CancellationToken and a 30-second timeout to ensure they do not hang indefinitely or ignore shutdown signals. New tests have been added to verify that both tools correctly respect cancellation tokens and report timeouts. I have no feedback to provide.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR advances the v0.8.45 control-plane cancellation workstream by making filesystem traversal tools stop blocking the async runtime: it moves list_dir (and also file_search) onto tokio::spawn_blocking, adds CancellationToken checks, and enforces a 30s fallback timeout to avoid hung mounts freezing tool execution.

Changes:

  • Refactors list_dir to run the blocking fs::read_dir loop in a spawn_blocking worker with cancellation checks and a 30s timeout.
  • Refactors file_search traversal similarly, adding cancellation checks during walking/scoring and a 30s timeout wrapper.
  • Adds unit tests covering pre-cancelled execution and timeout reporting for both tools.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 5 comments.

File Description
crates/tui/src/tools/file.rs Makes list_dir cancellable/timeout-safe by wrapping blocking directory iteration in spawn_blocking + cancellation/timeout handling; adds tests.
crates/tui/src/tools/file_search.rs Makes file_search cancellable/timeout-safe via spawn_blocking wrapper with cancellation/timeout handling; adds tests.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +838 to +847
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,
Comment on lines +146 to +158
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))?;
Comment on lines +94 to +103
let matches = search_files_async(
query.to_string(),
base_path,
extensions,
exclude_patterns,
limit,
context.cancel_token.clone(),
FILE_SEARCH_TIMEOUT,
)
.await?;
Comment on lines +1753 to +1754
let err = run_blocking_list_dir(Duration::from_millis(1), None, || {
std::thread::sleep(Duration::from_millis(50));
Comment on lines +527 to +528
let err = run_blocking_file_search(Duration::from_millis(1), None, || {
std::thread::sleep(Duration::from_millis(50));
Hmbown added 3 commits May 24, 2026 19:03
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).
Wires the /balance command into the slash-command registry and dispatch
table. The command handler currently returns a placeholder message per
provider; the async HTTP dispatch (calling provider balance endpoints
for DeepSeek, OpenRouter, Novita) lands in a follow-up PR.

- New commands/balance.rs module with provider-aware handler
- Command registration in COMMANDS array
- Dispatch via 'balance' match arm

Scout findings (agent_e154c802) mapped the endpoint URLs and the
AppAction async dispatch pattern; the full implementation follows.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants