Refs #1722
Preserves auto_compact as opt-in, adds the saved threshold setting, keeps the 500K hard floor, and wires Ctrl+L as a manual compaction shortcut for context-pressure recovery.
Harvested from PR #1723 by @aboimpinto
Co-authored-by: Paulo Aboim Pinto <aboimpinto@gmail.com>
Remove unneeded return in utils.rs (crates/tui/src/utils.rs:256) that was caught by clippy on the new commit. Also run cargo fmt to satisfy format checks.
- Remove ToolCallBefore observer firing from tool_routing.rs (the turn-loop gate now handles it) to prevent double-firing hooks for each tool call (greptile P1).
- Wrap hook_executor.execute() call in tokio::task::spawn_blocking so the Tokio worker thread is not blocked by child.wait_timeout() during hook execution (greptile P1).
Override `supports_parallel()` to return `true` in `WebSearchTool`,
allowing the engine to batch multiple concurrent web_search calls
into a `FuturesUnordered` parallel group instead of serializing them.
The tool is already read-only, auto-approved, and non-interactive —
parallel-safe by all other criteria. This change removes the final
gate (`supports_parallel() -> false` default) so co-issued searches
run concurrently rather than one-at-a-time.
Closes the ~55s serial wall-clock for 3 simultaneous web searches
(now ~20s, the slowest individual call).
Co-authored-by: Cursor <cursoragent@cursor.com>
(cherry picked from commit a7dcf63c556268b53ff430747ae2e141e4cd4451)
When MCP servers return tool schemas, the field order within each schema
object and the order of entries in required / dependentRequired arrays
can vary across reconnections. This causes the serialized tool catalog
bytes to change even when the logical schema is unchanged, busting
DeepSeek's KV prefix cache.
Add schema_canonicalize::canonicalize_schema which recursively:
- Sorts every required array alphabetically
- Sorts every dependentRequired sub-array alphabetically
- Rebuilds object keys in alphabetical order
- Recurses into all nested objects and arrays
The canonicalize step runs after schema_sanitize in build_api_tools,
so each tool's input_schema is first cleaned then byte-stabilized.
The existing OnceLock api_cache pins the result, ensuring the tool
catalog bytes are identical across reads and across process restarts.
8 unit tests cover: required sorting, dependentRequired sorting,
equivalent-ordering byte match, recursive nesting, empty schemas,
deeply nested schemas, non-required array preservation, and key
ordering.
(cherry picked from commit 7cee9cd5e12a74e8072bf2f6a1b18555ed0db0bf)
Addresses chatgpt-codex review: the previous full serde_json::to_string
included internal-only fields (allowed_callers, defer_loading,
input_examples, cache_control) that are never sent to the chat API.
This caused spurious drift detection when those fields changed.
- New tool_to_api_json() helper mirrors tool_to_chat() serialization:
only type, name, description, parameters, strict
- Doc comment fixed: 'sorted by name' → 'sorted lexicographically
by JSON text' (greptile review)
Phase 1.5 — upgrade PrefixFingerprint::compute() to hash the full tool
JSON serialization (name + description + schema) instead of just tool
names. This catches schema/description drift in addition to name changes.
- Serialize each tool via serde_json::to_string, sort by name, join
- New test: fingerprint_detects_schema_change_not_just_name_change
- All 21 prefix_cache tests pass
- Aligned with prompt_zones.rs tool_catalog_digest approach
Treat a missing tui.status_items field as None even when the [tui]
table exists, preserving the documented default footer behavior.
Add a regression test for configs that define [tui] without status_items.
with_agent_tools() unconditionally registered web_search/fetch_url/web.run
(via with_web_tools) and apply_patch (via with_patch_tools), but tool_setup.rs
conditionally registered them again based on Feature::WebSearch and
Feature::ApplyPatch flags. This caused double registration (overwritten
with a warning log on the second insert).
Changes:
- Remove with_web_tools() and with_patch_tools() from with_agent_tools()
- Move finance tool out of with_web_tools() into its own with_finance_tool()
(finance is market data, not web search — it should not be gated behind
the web-search feature flag)
- Add with_finance_tool() to with_agent_tools() so finance stays always
available
- Update tests: new test for with_finance_tool(), updated web_tools test
to verify finance is no longer in the web group
- Fix false 'Turn stalled' during long active turns with running tools.
Add turn_last_activity_at tracking and active-tool awareness to
reconcile_turn_liveness(). Three new tests cover the fix.
- Remove Qwen 3.7 Max OpenRouter preset from registry, picker, docs,
and tests. Qwen 3.7 Max is a hosted model; the preset will return
when an open-weight Qwen 3.7 release ships. MiniMax M3 remains as
a full 1M-context multimodal route.
- Sync root CHANGELOG to crates/tui/CHANGELOG for crates.io packaging.
Update docs/CONFIGURATION.md, docs/PROVIDERS.md, and README to
reflect the Qwen 3.7 removal. Regenerate web facts timestamp.
All dead_code in prompt_zones.rs is intentional — these types are
scaffolding awaiting future integration. Pre-existing schema_migration
warnings remain as-is.
Narrower slice per Hmbown's review: typed zone structs as foundation
without wiring into the request path. Future phases will integrate
AppendLog/TurnScratch/ThreeZoneRequest into turn_loop.
- prompt_zones.rs (663 lines, 16 tests): PinnedPrefix / FrozenPrefix /
PrefixDrift (ready), AppendLog / TurnScratch / ThreeZoneRequest
(scaffolding, #[allow(dead_code)])
- FrozenPrefix: full tool JSON hash (name+desc+schema), raw-text
fast-path in verify(), cache_control preserved in build_messages()
- /cache zones subcommand with three-zone status display
- merge_compaction_summary: zone affiliation doc comment
- No turn_loop/session changes — engine continues using
PrefixStabilityManager / MessageRequest as before
Clear stale busy state and retry/title animations on local cancel.\n\nLocal verification:\n- cargo test -p codewhale-tui\n- codewhale doctor\n- codewhale --provider deepseek --model deepseek-v4-pro exec "Reply with exactly: OK"