Answer the review on the blocking-pool switch
Three points from the automated review: The JoinHandle unwrap_or_else was flagged as a panic risk. It is the opposite: unwrap_or_else on a Result cannot panic, and turning a JoinError into a tool result is what keeps a panicking tool from unwinding through the turn — which is what would have happened when these tools ran inline. Said so in a comment rather than changing the code. execute_sync_tool now documents that it runs on a blocking thread with no reactor, so a future tool that needs to await belongs with task and web_search in execute_tool_impl. The timing assertion gets padding: four seconds of command against a one-second floor. A loaded runner only widens the gap being measured, so the one real flake vector is this thread being descheduled between reading the two updates, and three seconds of slack covers that without weakening what the assertion separates — four seconds from the 164µs the blocking version produced. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>