chore(release): start v0.8.35 branch
- Bump workspace, internal crate pins, npm wrapper metadata, generated facts, and docs from 0.8.34 to 0.8.35 - Clarify 60% manual compact guidance vs 80% opt-in automatic guardrail - Expire completed live-tool rows and collapse stale running shell rows in the Tasks sidebar
This commit is contained in:
+32
-9
@@ -7,6 +7,36 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
## [0.8.35] - 2026-05-13
|
||||
|
||||
A post-0.8.34 cleanup release focused on prompt hygiene, context-pressure
|
||||
guidance, and keeping the next release branch clearly separated from the
|
||||
already-published v0.8.34 tag.
|
||||
|
||||
### Changed
|
||||
|
||||
- **First-turn prompt context is leaner and easier to audit.** The
|
||||
generated project context pack now ignores hidden tool/cache state,
|
||||
balances top-level directories before descending, and `/context`
|
||||
shows named prompt layers instead of a single opaque system blob.
|
||||
- **Model-visible prompt policy de-conflicted.** The base and mode
|
||||
prompts no longer forbid useful `deepseek` CLI diagnostics, no
|
||||
longer require checklists for simple one-step work, and align
|
||||
long-session compaction guidance around the 60% suggestion threshold.
|
||||
- **Context-pressure guidance now has one split rule.** Manual
|
||||
`/compact` suggestions start around 60% during sustained work, while
|
||||
automatic replacement compaction remains an opt-in hard guardrail near
|
||||
80% so DeepSeek V4 prefix-cache economics stay intact.
|
||||
- **The Tasks sidebar now ages out stale live-tool noise.** Completed
|
||||
active tool rows linger briefly and then leave the right rail; very old
|
||||
running shell rows collapse to a single row instead of occupying the
|
||||
whole Tasks panel.
|
||||
|
||||
### Fixed
|
||||
|
||||
- **`auto_compact` settings help now reports the real default**, which
|
||||
has been off since v0.8.11 to avoid unnecessary cache-prefix rewrites.
|
||||
|
||||
## [0.8.34] - 2026-05-13
|
||||
|
||||
A polish, terminal-protocol, and internal-cleanup release. The model-facing
|
||||
@@ -65,14 +95,6 @@ mega-files that had grown around the agent loop and TUI.
|
||||
(v0.8.6 era), `PROMPT_ANALYSIS.md`, and the redundant
|
||||
`DEPENDENCY_GRAPH.md` no longer ship in releases; `docs/ARCHITECTURE.md`
|
||||
remains the canonical crate-layout reference.
|
||||
- **First-turn prompt context is leaner and easier to audit.** The
|
||||
generated project context pack now ignores hidden tool/cache state,
|
||||
balances top-level directories before descending, and `/context`
|
||||
shows named prompt layers instead of a single opaque system blob.
|
||||
- **Model-visible prompt policy de-conflicted.** The base and mode
|
||||
prompts no longer forbid useful `deepseek` CLI diagnostics, no
|
||||
longer require checklists for simple one-step work, and align
|
||||
long-session compaction guidance around the 60% threshold.
|
||||
|
||||
### Fixed
|
||||
|
||||
@@ -4078,7 +4100,8 @@ Welcome — and thank you.
|
||||
- Hooks system and config profiles
|
||||
- Example skills and launch assets
|
||||
|
||||
[Unreleased]: https://github.com/Hmbown/DeepSeek-TUI/compare/v0.8.34...HEAD
|
||||
[Unreleased]: https://github.com/Hmbown/DeepSeek-TUI/compare/v0.8.35...HEAD
|
||||
[0.8.35]: https://github.com/Hmbown/DeepSeek-TUI/compare/v0.8.34...v0.8.35
|
||||
[0.8.34]: https://github.com/Hmbown/DeepSeek-TUI/compare/v0.8.33...v0.8.34
|
||||
[0.8.33]: https://github.com/Hmbown/DeepSeek-TUI/compare/v0.8.32...v0.8.33
|
||||
[0.8.32]: https://github.com/Hmbown/DeepSeek-TUI/compare/v0.8.31...v0.8.32
|
||||
|
||||
Generated
+14
-14
@@ -1160,7 +1160,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "deepseek-agent"
|
||||
version = "0.8.34"
|
||||
version = "0.8.35"
|
||||
dependencies = [
|
||||
"deepseek-config",
|
||||
"serde",
|
||||
@@ -1168,7 +1168,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "deepseek-app-server"
|
||||
version = "0.8.34"
|
||||
version = "0.8.35"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"axum",
|
||||
@@ -1190,7 +1190,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "deepseek-config"
|
||||
version = "0.8.34"
|
||||
version = "0.8.35"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"deepseek-secrets",
|
||||
@@ -1202,7 +1202,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "deepseek-core"
|
||||
version = "0.8.34"
|
||||
version = "0.8.35"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"chrono",
|
||||
@@ -1220,7 +1220,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "deepseek-execpolicy"
|
||||
version = "0.8.34"
|
||||
version = "0.8.35"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"deepseek-protocol",
|
||||
@@ -1229,7 +1229,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "deepseek-hooks"
|
||||
version = "0.8.34"
|
||||
version = "0.8.35"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-trait",
|
||||
@@ -1243,7 +1243,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "deepseek-mcp"
|
||||
version = "0.8.34"
|
||||
version = "0.8.35"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"serde",
|
||||
@@ -1252,7 +1252,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "deepseek-protocol"
|
||||
version = "0.8.34"
|
||||
version = "0.8.35"
|
||||
dependencies = [
|
||||
"serde",
|
||||
"serde_json",
|
||||
@@ -1260,7 +1260,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "deepseek-secrets"
|
||||
version = "0.8.34"
|
||||
version = "0.8.35"
|
||||
dependencies = [
|
||||
"dirs",
|
||||
"keyring",
|
||||
@@ -1273,7 +1273,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "deepseek-state"
|
||||
version = "0.8.34"
|
||||
version = "0.8.35"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"chrono",
|
||||
@@ -1285,7 +1285,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "deepseek-tools"
|
||||
version = "0.8.34"
|
||||
version = "0.8.35"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-trait",
|
||||
@@ -1298,7 +1298,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "deepseek-tui"
|
||||
version = "0.8.34"
|
||||
version = "0.8.35"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"arboard",
|
||||
@@ -1361,7 +1361,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "deepseek-tui-cli"
|
||||
version = "0.8.34"
|
||||
version = "0.8.35"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"chrono",
|
||||
@@ -1386,7 +1386,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "deepseek-tui-core"
|
||||
version = "0.8.34"
|
||||
version = "0.8.35"
|
||||
|
||||
[[package]]
|
||||
name = "deltae"
|
||||
|
||||
+1
-1
@@ -19,7 +19,7 @@ default-members = ["crates/cli", "crates/app-server", "crates/tui"]
|
||||
resolver = "2"
|
||||
|
||||
[workspace.package]
|
||||
version = "0.8.34"
|
||||
version = "0.8.35"
|
||||
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
|
||||
|
||||
@@ -241,34 +241,27 @@ deepseek --provider ollama --model deepseek-coder:1.3b
|
||||
|
||||
---
|
||||
|
||||
## What's New In v0.8.34
|
||||
## What's New In v0.8.35
|
||||
|
||||
A polish, terminal-protocol, and internal-cleanup release. The
|
||||
model-facing tool surface stays stable while v0.8.34 improves first-run
|
||||
skills, terminal notifications, prompt-cache visibility, MCP transport
|
||||
compatibility, and the maintainability of the largest TUI files.
|
||||
A post-release cleanup branch for the `v0.8.34` line. It keeps the
|
||||
model-facing surface stable while trimming first-turn context, clarifying
|
||||
context-pressure behavior, and reducing sidebar noise during long runs.
|
||||
[Full changelog](CHANGELOG.md).
|
||||
|
||||
- **Bundled DeepSeek-native workflow skills.** Fresh installs now get
|
||||
first-party skills for delegation, skill creation, MCP/plugin setup,
|
||||
documents, presentations, spreadsheets, PDFs, and Feishu/Lark.
|
||||
- **User skills stay visible.** `/skills` separates user-created skills
|
||||
from built-ins so workspace and global skills do not disappear behind
|
||||
the bundled catalog.
|
||||
- **MCP HTTP defaults are more compatible.** Streamable HTTP requests
|
||||
default to `Accept: application/json, text/event-stream` and preserve
|
||||
`Mcp-Session-Id` across requests.
|
||||
- **Terminal notifications cover more terminals.** Kitty `OSC 99` and
|
||||
Ghostty `OSC 777` join the existing notification paths.
|
||||
- **Prefix-cache stability is visible.** The footer surfaces cache
|
||||
stability so users can notice cache-busting changes before cost climbs.
|
||||
- **`edit_file` handles typographic punctuation drift.** With
|
||||
`fuzz: true`, smart quotes, en/em dashes, and non-breaking spaces no
|
||||
longer prevent a safe replacement when the file uses ASCII punctuation.
|
||||
- **Large internals are getting smaller.** Focused modules now own
|
||||
auto-routing, Vim-mode handling, workspace context, streaming thinking,
|
||||
notifications, file-picker relevance, formatting helpers, and key
|
||||
shortcut predicates.
|
||||
- **First-turn context is leaner.** Hidden tool/cache state is excluded
|
||||
from the generated project pack, and `/context` now names prompt layers
|
||||
instead of showing one opaque blob.
|
||||
- **Prompt rules are de-conflicted.** Useful `deepseek` diagnostics are
|
||||
allowed, simple one-step work no longer forces checklist ceremony, and
|
||||
sustained sessions consistently suggest `/compact` around 60%.
|
||||
- **Automatic compaction stays conservative.** The 80% threshold remains
|
||||
an opt-in hard guardrail so DeepSeek V4 prefix-cache behavior is not
|
||||
disturbed by default.
|
||||
- **The Tasks sidebar settles down.** Completed live-tool rows expire
|
||||
after a short linger, and very old running shell rows collapse instead
|
||||
of filling the right rail.
|
||||
- **`auto_compact` help is honest.** Settings now report the real default:
|
||||
off.
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -7,5 +7,5 @@ repository.workspace = true
|
||||
description = "Model/provider registry and fallback strategy for DeepSeek workspace architecture"
|
||||
|
||||
[dependencies]
|
||||
deepseek-config = { path = "../config", version = "0.8.34" }
|
||||
deepseek-config = { path = "../config", version = "0.8.35" }
|
||||
serde.workspace = true
|
||||
|
||||
@@ -10,15 +10,15 @@ description = "Codex-style app-server transport for DeepSeek workspace architect
|
||||
anyhow.workspace = true
|
||||
axum.workspace = true
|
||||
clap.workspace = true
|
||||
deepseek-agent = { path = "../agent", version = "0.8.34" }
|
||||
deepseek-config = { path = "../config", version = "0.8.34" }
|
||||
deepseek-core = { path = "../core", version = "0.8.34" }
|
||||
deepseek-execpolicy = { path = "../execpolicy", version = "0.8.34" }
|
||||
deepseek-hooks = { path = "../hooks", version = "0.8.34" }
|
||||
deepseek-mcp = { path = "../mcp", version = "0.8.34" }
|
||||
deepseek-protocol = { path = "../protocol", version = "0.8.34" }
|
||||
deepseek-state = { path = "../state", version = "0.8.34" }
|
||||
deepseek-tools = { path = "../tools", version = "0.8.34" }
|
||||
deepseek-agent = { path = "../agent", version = "0.8.35" }
|
||||
deepseek-config = { path = "../config", version = "0.8.35" }
|
||||
deepseek-core = { path = "../core", version = "0.8.35" }
|
||||
deepseek-execpolicy = { path = "../execpolicy", version = "0.8.35" }
|
||||
deepseek-hooks = { path = "../hooks", version = "0.8.35" }
|
||||
deepseek-mcp = { path = "../mcp", version = "0.8.35" }
|
||||
deepseek-protocol = { path = "../protocol", version = "0.8.35" }
|
||||
deepseek-state = { path = "../state", version = "0.8.35" }
|
||||
deepseek-tools = { path = "../tools", version = "0.8.35" }
|
||||
serde.workspace = true
|
||||
serde_json.workspace = true
|
||||
tokio.workspace = true
|
||||
|
||||
@@ -14,13 +14,13 @@ path = "src/main.rs"
|
||||
anyhow.workspace = true
|
||||
clap.workspace = true
|
||||
clap_complete.workspace = true
|
||||
deepseek-agent = { path = "../agent", version = "0.8.34" }
|
||||
deepseek-app-server = { path = "../app-server", version = "0.8.34" }
|
||||
deepseek-config = { path = "../config", version = "0.8.34" }
|
||||
deepseek-execpolicy = { path = "../execpolicy", version = "0.8.34" }
|
||||
deepseek-mcp = { path = "../mcp", version = "0.8.34" }
|
||||
deepseek-secrets = { path = "../secrets", version = "0.8.34" }
|
||||
deepseek-state = { path = "../state", version = "0.8.34" }
|
||||
deepseek-agent = { path = "../agent", version = "0.8.35" }
|
||||
deepseek-app-server = { path = "../app-server", version = "0.8.35" }
|
||||
deepseek-config = { path = "../config", version = "0.8.35" }
|
||||
deepseek-execpolicy = { path = "../execpolicy", version = "0.8.35" }
|
||||
deepseek-mcp = { path = "../mcp", version = "0.8.35" }
|
||||
deepseek-secrets = { path = "../secrets", version = "0.8.35" }
|
||||
deepseek-state = { path = "../state", version = "0.8.35" }
|
||||
chrono.workspace = true
|
||||
dirs.workspace = true
|
||||
serde.workspace = true
|
||||
|
||||
@@ -8,7 +8,7 @@ description = "Config schema and precedence model for DeepSeek workspace archite
|
||||
|
||||
[dependencies]
|
||||
anyhow.workspace = true
|
||||
deepseek-secrets = { path = "../secrets", version = "0.8.34" }
|
||||
deepseek-secrets = { path = "../secrets", version = "0.8.35" }
|
||||
dirs.workspace = true
|
||||
serde.workspace = true
|
||||
toml.workspace = true
|
||||
|
||||
@@ -9,13 +9,13 @@ description = "Core runtime boundaries for DeepSeek workspace architecture"
|
||||
[dependencies]
|
||||
anyhow.workspace = true
|
||||
chrono.workspace = true
|
||||
deepseek-agent = { path = "../agent", version = "0.8.34" }
|
||||
deepseek-config = { path = "../config", version = "0.8.34" }
|
||||
deepseek-execpolicy = { path = "../execpolicy", version = "0.8.34" }
|
||||
deepseek-hooks = { path = "../hooks", version = "0.8.34" }
|
||||
deepseek-mcp = { path = "../mcp", version = "0.8.34" }
|
||||
deepseek-protocol = { path = "../protocol", version = "0.8.34" }
|
||||
deepseek-state = { path = "../state", version = "0.8.34" }
|
||||
deepseek-tools = { path = "../tools", version = "0.8.34" }
|
||||
deepseek-agent = { path = "../agent", version = "0.8.35" }
|
||||
deepseek-config = { path = "../config", version = "0.8.35" }
|
||||
deepseek-execpolicy = { path = "../execpolicy", version = "0.8.35" }
|
||||
deepseek-hooks = { path = "../hooks", version = "0.8.35" }
|
||||
deepseek-mcp = { path = "../mcp", version = "0.8.35" }
|
||||
deepseek-protocol = { path = "../protocol", version = "0.8.35" }
|
||||
deepseek-state = { path = "../state", version = "0.8.35" }
|
||||
deepseek-tools = { path = "../tools", version = "0.8.35" }
|
||||
serde_json.workspace = true
|
||||
uuid.workspace = true
|
||||
|
||||
@@ -8,5 +8,5 @@ description = "Execution policy and approval model parity for DeepSeek workspace
|
||||
|
||||
[dependencies]
|
||||
anyhow.workspace = true
|
||||
deepseek-protocol = { path = "../protocol", version = "0.8.34" }
|
||||
deepseek-protocol = { path = "../protocol", version = "0.8.35" }
|
||||
serde.workspace = true
|
||||
|
||||
@@ -10,7 +10,7 @@ description = "Hook dispatch and notifications parity for DeepSeek workspace arc
|
||||
anyhow.workspace = true
|
||||
async-trait.workspace = true
|
||||
chrono.workspace = true
|
||||
deepseek-protocol = { path = "../protocol", version = "0.8.34" }
|
||||
deepseek-protocol = { path = "../protocol", version = "0.8.35" }
|
||||
reqwest.workspace = true
|
||||
serde.workspace = true
|
||||
serde_json.workspace = true
|
||||
|
||||
@@ -9,7 +9,7 @@ description = "Tool invocation lifecycle, schema validation, and scheduler paral
|
||||
[dependencies]
|
||||
anyhow.workspace = true
|
||||
async-trait.workspace = true
|
||||
deepseek-protocol = { path = "../protocol", version = "0.8.34" }
|
||||
deepseek-protocol = { path = "../protocol", version = "0.8.35" }
|
||||
serde.workspace = true
|
||||
serde_json.workspace = true
|
||||
tokio.workspace = true
|
||||
|
||||
+32
-9
@@ -7,6 +7,36 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
## [0.8.35] - 2026-05-13
|
||||
|
||||
A post-0.8.34 cleanup release focused on prompt hygiene, context-pressure
|
||||
guidance, and keeping the next release branch clearly separated from the
|
||||
already-published v0.8.34 tag.
|
||||
|
||||
### Changed
|
||||
|
||||
- **First-turn prompt context is leaner and easier to audit.** The
|
||||
generated project context pack now ignores hidden tool/cache state,
|
||||
balances top-level directories before descending, and `/context`
|
||||
shows named prompt layers instead of a single opaque system blob.
|
||||
- **Model-visible prompt policy de-conflicted.** The base and mode
|
||||
prompts no longer forbid useful `deepseek` CLI diagnostics, no
|
||||
longer require checklists for simple one-step work, and align
|
||||
long-session compaction guidance around the 60% suggestion threshold.
|
||||
- **Context-pressure guidance now has one split rule.** Manual
|
||||
`/compact` suggestions start around 60% during sustained work, while
|
||||
automatic replacement compaction remains an opt-in hard guardrail near
|
||||
80% so DeepSeek V4 prefix-cache economics stay intact.
|
||||
- **The Tasks sidebar now ages out stale live-tool noise.** Completed
|
||||
active tool rows linger briefly and then leave the right rail; very old
|
||||
running shell rows collapse to a single row instead of occupying the
|
||||
whole Tasks panel.
|
||||
|
||||
### Fixed
|
||||
|
||||
- **`auto_compact` settings help now reports the real default**, which
|
||||
has been off since v0.8.11 to avoid unnecessary cache-prefix rewrites.
|
||||
|
||||
## [0.8.34] - 2026-05-13
|
||||
|
||||
A polish, terminal-protocol, and internal-cleanup release. The model-facing
|
||||
@@ -65,14 +95,6 @@ mega-files that had grown around the agent loop and TUI.
|
||||
(v0.8.6 era), `PROMPT_ANALYSIS.md`, and the redundant
|
||||
`DEPENDENCY_GRAPH.md` no longer ship in releases; `docs/ARCHITECTURE.md`
|
||||
remains the canonical crate-layout reference.
|
||||
- **First-turn prompt context is leaner and easier to audit.** The
|
||||
generated project context pack now ignores hidden tool/cache state,
|
||||
balances top-level directories before descending, and `/context`
|
||||
shows named prompt layers instead of a single opaque system blob.
|
||||
- **Model-visible prompt policy de-conflicted.** The base and mode
|
||||
prompts no longer forbid useful `deepseek` CLI diagnostics, no
|
||||
longer require checklists for simple one-step work, and align
|
||||
long-session compaction guidance around the 60% threshold.
|
||||
|
||||
### Fixed
|
||||
|
||||
@@ -4078,7 +4100,8 @@ Welcome — and thank you.
|
||||
- Hooks system and config profiles
|
||||
- Example skills and launch assets
|
||||
|
||||
[Unreleased]: https://github.com/Hmbown/DeepSeek-TUI/compare/v0.8.34...HEAD
|
||||
[Unreleased]: https://github.com/Hmbown/DeepSeek-TUI/compare/v0.8.35...HEAD
|
||||
[0.8.35]: https://github.com/Hmbown/DeepSeek-TUI/compare/v0.8.34...v0.8.35
|
||||
[0.8.34]: https://github.com/Hmbown/DeepSeek-TUI/compare/v0.8.33...v0.8.34
|
||||
[0.8.33]: https://github.com/Hmbown/DeepSeek-TUI/compare/v0.8.32...v0.8.33
|
||||
[0.8.32]: https://github.com/Hmbown/DeepSeek-TUI/compare/v0.8.31...v0.8.32
|
||||
|
||||
@@ -21,8 +21,8 @@ path = "src/main.rs"
|
||||
[dependencies]
|
||||
anyhow = "1.0.100"
|
||||
arboard = "3.4"
|
||||
deepseek-secrets = { path = "../secrets", version = "0.8.34" }
|
||||
deepseek-tools = { path = "../tools", version = "0.8.34" }
|
||||
deepseek-secrets = { path = "../secrets", version = "0.8.35" }
|
||||
deepseek-tools = { path = "../tools", version = "0.8.35" }
|
||||
schemaui = { version = "0.12.0", default-features = false, optional = true }
|
||||
async-stream = "0.3.6"
|
||||
async-trait = "0.1"
|
||||
|
||||
@@ -53,7 +53,11 @@ impl Default for CompactionConfig {
|
||||
// v0.8.11: 50K was a 128K-era leftover that biased every
|
||||
// unconfigured caller toward "compact almost immediately on V4."
|
||||
// Bumped to 800K (80% of V4's 1M window) so the dead-code
|
||||
// default no longer lies. Real call sites override this via
|
||||
// default matches the hard automatic compaction guardrail. This
|
||||
// is intentionally later than the model-visible 60% "suggest
|
||||
// /compact during sustained work" guidance; automatic replacement
|
||||
// compaction rewrites the cacheable prefix and remains opt-in.
|
||||
// Real call sites override this via
|
||||
// `compaction_threshold_for_model_and_effort`.
|
||||
token_threshold: 800_000,
|
||||
model: DEFAULT_TEXT_MODEL.to_string(),
|
||||
|
||||
@@ -7,8 +7,14 @@ pub const THRESHOLDS: [(f32, &str); 3] = [
|
||||
0.9,
|
||||
"Context at 90%: stop and write relay to .deepseek/handoff.md now",
|
||||
),
|
||||
(0.8, "Context at 80%: draft relay to .deepseek/handoff.md"),
|
||||
(0.7, "Context at 70%: consider wrapping current sub-task"),
|
||||
(
|
||||
0.8,
|
||||
"Context at 80%: urgent hard-limit pressure; compact or write relay now",
|
||||
),
|
||||
(
|
||||
0.6,
|
||||
"Context at 60%: prepare relay or suggest /compact for sustained work",
|
||||
),
|
||||
];
|
||||
#[allow(dead_code)]
|
||||
pub fn threshold_message(ratio: f32) -> Option<&'static str> {
|
||||
|
||||
@@ -263,7 +263,9 @@ fn deepseek_context_window_hint(model_lower: &str) -> Option<u32> {
|
||||
/// Derive a compaction token threshold from model context window.
|
||||
///
|
||||
/// Keeps headroom for tool outputs and assistant completion by defaulting to 80%
|
||||
/// of known context windows.
|
||||
/// of known context windows. This is the hard automatic compaction threshold
|
||||
/// used only when `auto_compact` is enabled; model-facing guidance still
|
||||
/// suggests manual `/compact` earlier (~60%) during sustained work.
|
||||
#[must_use]
|
||||
pub fn compaction_threshold_for_model(model: &str) -> usize {
|
||||
let Some(window) = context_window_for_model(model) else {
|
||||
@@ -279,7 +281,7 @@ pub fn compaction_threshold_for_model(model: &str) -> usize {
|
||||
/// Replacement-style compaction rewrites the stable prefix, which works against
|
||||
/// DeepSeek V4 prefix-cache economics. Reasoning effort must not lower V4's
|
||||
/// automatic replacement threshold; V4-family models use the same late
|
||||
/// 80%-of-window guard as `compaction_threshold_for_model`.
|
||||
/// 80%-of-window hard guard as `compaction_threshold_for_model`.
|
||||
#[must_use]
|
||||
pub fn compaction_threshold_for_model_and_effort(
|
||||
model: &str,
|
||||
|
||||
@@ -702,7 +702,7 @@ impl Settings {
|
||||
vec![
|
||||
(
|
||||
"auto_compact",
|
||||
"Auto-compact near context limit: on/off (default on)",
|
||||
"Auto-compact near the hard context limit: on/off (default off)",
|
||||
),
|
||||
("calm_mode", "Calmer UI defaults: on/off"),
|
||||
(
|
||||
|
||||
@@ -987,6 +987,10 @@ pub struct App {
|
||||
/// virtual index can shift (orphan completions push real cells in
|
||||
/// between). Migrated into `tool_details_by_cell` on flush.
|
||||
pub active_tool_details: HashMap<String, ToolDetailRecord>,
|
||||
/// Completion timestamps for entries still living inside `active_cell`.
|
||||
/// The transcript keeps completed entries until turn flush, but the
|
||||
/// sidebar can use these timestamps to let settled live rows expire.
|
||||
pub active_tool_entry_completed_at: HashMap<usize, Instant>,
|
||||
/// Active exploring cell entry index (within `active_cell.entries`).
|
||||
/// `None` once the active cell flushes or no exploring entry exists.
|
||||
pub exploring_cell: Option<usize>,
|
||||
@@ -1563,6 +1567,7 @@ impl App {
|
||||
active_cell: None,
|
||||
active_cell_revision: 0,
|
||||
active_tool_details: HashMap::new(),
|
||||
active_tool_entry_completed_at: HashMap::new(),
|
||||
exploring_cell: None,
|
||||
exploring_entries: HashMap::new(),
|
||||
ignored_tool_calls: HashSet::new(),
|
||||
@@ -2360,6 +2365,7 @@ impl App {
|
||||
self.exploring_cell = None;
|
||||
self.exploring_entries.clear();
|
||||
self.active_tool_details.clear();
|
||||
self.active_tool_entry_completed_at.clear();
|
||||
self.streaming_thinking_active_entry = None;
|
||||
self.bump_active_cell_revision();
|
||||
return;
|
||||
@@ -2375,6 +2381,7 @@ impl App {
|
||||
let base_index = self.history.len();
|
||||
|
||||
let mut details = std::mem::take(&mut self.active_tool_details);
|
||||
self.active_tool_entry_completed_at.clear();
|
||||
for (tool_id, detail) in details.drain() {
|
||||
self.tool_details_by_cell
|
||||
.entry(self.tool_cells.get(&tool_id).copied().unwrap_or(base_index))
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
//! reads from `App` snapshots; mutation lives in the main app loop.
|
||||
|
||||
use std::fmt::Write;
|
||||
use std::time::Duration;
|
||||
|
||||
use ratatui::{
|
||||
Frame,
|
||||
@@ -31,6 +32,8 @@ use super::ui::truncate_line_to_width;
|
||||
/// does not prematurely hide the session+agents breakdown.
|
||||
const COST_EQ_TOLERANCE: f64 = 1e-6;
|
||||
const RECENT_TOOL_SCAN_LIMIT: usize = 24;
|
||||
const ACTIVE_TOOL_COMPLETED_ROW_TTL: Duration = Duration::from_secs(8);
|
||||
const ACTIVE_TOOL_STALE_RUNNING_ROW_TTL: Duration = Duration::from_secs(600);
|
||||
|
||||
pub fn render_sidebar(f: &mut Frame, area: Rect, app: &App) {
|
||||
if area.width < 24 || area.height < 8 {
|
||||
@@ -660,14 +663,80 @@ fn active_tool_rows(app: &App) -> Vec<SidebarToolRow> {
|
||||
let Some(active) = app.active_cell.as_ref() else {
|
||||
return Vec::new();
|
||||
};
|
||||
let rows: Vec<SidebarToolRow> = active
|
||||
.entries()
|
||||
.iter()
|
||||
.filter_map(sidebar_tool_row_from_cell)
|
||||
.collect();
|
||||
let mut rows: Vec<SidebarToolRow> = Vec::new();
|
||||
let mut stale_running: Vec<SidebarToolRow> = Vec::new();
|
||||
for (entry_idx, cell) in active.entries().iter().enumerate() {
|
||||
let Some(row) = sidebar_tool_row_from_cell(cell) else {
|
||||
continue;
|
||||
};
|
||||
match active_tool_row_visibility(app, entry_idx, &row) {
|
||||
ActiveToolRowVisibility::Visible => rows.push(row),
|
||||
ActiveToolRowVisibility::StaleRunning => stale_running.push(row),
|
||||
ActiveToolRowVisibility::Hidden => {}
|
||||
}
|
||||
}
|
||||
if !stale_running.is_empty() {
|
||||
rows.push(collapsed_stale_running_row(stale_running));
|
||||
}
|
||||
editorial_tool_rows(rows, usize::MAX)
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
enum ActiveToolRowVisibility {
|
||||
Visible,
|
||||
StaleRunning,
|
||||
Hidden,
|
||||
}
|
||||
|
||||
fn active_tool_row_visibility(
|
||||
app: &App,
|
||||
entry_idx: usize,
|
||||
row: &SidebarToolRow,
|
||||
) -> ActiveToolRowVisibility {
|
||||
if row.status == ToolStatus::Running {
|
||||
return if row
|
||||
.duration_ms
|
||||
.is_some_and(|ms| ms >= duration_ms(ACTIVE_TOOL_STALE_RUNNING_ROW_TTL))
|
||||
{
|
||||
ActiveToolRowVisibility::StaleRunning
|
||||
} else {
|
||||
ActiveToolRowVisibility::Visible
|
||||
};
|
||||
}
|
||||
|
||||
let Some(completed_at) = app.active_tool_entry_completed_at.get(&entry_idx) else {
|
||||
return ActiveToolRowVisibility::Hidden;
|
||||
};
|
||||
if completed_at.elapsed() <= ACTIVE_TOOL_COMPLETED_ROW_TTL {
|
||||
ActiveToolRowVisibility::Visible
|
||||
} else {
|
||||
ActiveToolRowVisibility::Hidden
|
||||
}
|
||||
}
|
||||
|
||||
fn collapsed_stale_running_row(rows: Vec<SidebarToolRow>) -> SidebarToolRow {
|
||||
let count = rows.len();
|
||||
let oldest_ms = rows
|
||||
.iter()
|
||||
.filter_map(|row| row.duration_ms)
|
||||
.max()
|
||||
.unwrap_or_default();
|
||||
let first_summary = rows
|
||||
.iter()
|
||||
.find_map(|row| (!row.summary.trim().is_empty()).then(|| row.summary.clone()))
|
||||
.unwrap_or_else(|| "open Activity Detail".to_string());
|
||||
SidebarToolRow {
|
||||
name: if count == 1 {
|
||||
"run".to_string()
|
||||
} else {
|
||||
format!("run x{count}")
|
||||
},
|
||||
status: ToolStatus::Running,
|
||||
summary: format!("long-running · {first_summary}"),
|
||||
duration_ms: (oldest_ms > 0).then_some(oldest_ms),
|
||||
}
|
||||
}
|
||||
|
||||
fn recent_tool_rows(app: &App, limit: usize) -> Vec<SidebarToolRow> {
|
||||
let rows: Vec<SidebarToolRow> = app
|
||||
.history
|
||||
@@ -1014,6 +1083,10 @@ fn format_duration_ms(ms: u64) -> String {
|
||||
}
|
||||
}
|
||||
|
||||
fn duration_ms(duration: Duration) -> u64 {
|
||||
u64::try_from(duration.as_millis()).unwrap_or(u64::MAX)
|
||||
}
|
||||
|
||||
fn render_sidebar_subagents(f: &mut Frame, area: Rect, app: &App) {
|
||||
if area.height < 3 {
|
||||
return;
|
||||
@@ -1489,9 +1562,10 @@ fn render_sidebar_section(
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::{
|
||||
AutoSidebarPanel, AutoSidebarState, SidebarAgentRow, SidebarSubagentSummary,
|
||||
SidebarWorkChecklistItem, SidebarWorkStrategyStep, SidebarWorkSummary, auto_sidebar_panels,
|
||||
subagent_panel_lines, task_panel_lines, work_panel_empty_hint, work_panel_lines,
|
||||
ACTIVE_TOOL_COMPLETED_ROW_TTL, ACTIVE_TOOL_STALE_RUNNING_ROW_TTL, AutoSidebarPanel,
|
||||
AutoSidebarState, SidebarAgentRow, SidebarSubagentSummary, SidebarWorkChecklistItem,
|
||||
SidebarWorkStrategyStep, SidebarWorkSummary, auto_sidebar_panels, subagent_panel_lines,
|
||||
task_panel_lines, work_panel_empty_hint, work_panel_lines,
|
||||
};
|
||||
use crate::config::Config;
|
||||
use crate::palette::PaletteMode;
|
||||
@@ -1504,6 +1578,7 @@ mod tests {
|
||||
};
|
||||
use ratatui::text::Line;
|
||||
use std::path::PathBuf;
|
||||
use std::time::{Duration, Instant};
|
||||
|
||||
fn create_test_app() -> App {
|
||||
let options = TuiOptions {
|
||||
@@ -1728,6 +1803,100 @@ mod tests {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn tasks_panel_expires_completed_active_tool_rows() {
|
||||
let mut app = create_test_app();
|
||||
let mut active = ActiveCell::new();
|
||||
active.push_tool(
|
||||
"tool-1",
|
||||
HistoryCell::Tool(ToolCell::Generic(GenericToolCell {
|
||||
name: "read_file".to_string(),
|
||||
status: ToolStatus::Success,
|
||||
input_summary: Some("src/main.rs".to_string()),
|
||||
output: Some("done".to_string()),
|
||||
prompts: None,
|
||||
spillover_path: None,
|
||||
output_summary: Some("done".to_string()),
|
||||
is_diff: false,
|
||||
})),
|
||||
);
|
||||
app.active_cell = Some(active);
|
||||
app.active_tool_entry_completed_at.insert(
|
||||
0,
|
||||
Instant::now() - ACTIVE_TOOL_COMPLETED_ROW_TTL - Duration::from_secs(1),
|
||||
);
|
||||
|
||||
let text = lines_to_text(&task_panel_lines(&app, 64, 8));
|
||||
|
||||
assert!(
|
||||
!text.iter().any(|line| line.contains("[x] read_file")),
|
||||
"expired completed active row should leave the sidebar: {text:?}"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn tasks_panel_lingers_fresh_completed_active_tool_rows() {
|
||||
let mut app = create_test_app();
|
||||
let mut active = ActiveCell::new();
|
||||
active.push_tool(
|
||||
"tool-1",
|
||||
HistoryCell::Tool(ToolCell::Generic(GenericToolCell {
|
||||
name: "read_file".to_string(),
|
||||
status: ToolStatus::Success,
|
||||
input_summary: Some("src/main.rs".to_string()),
|
||||
output: Some("done".to_string()),
|
||||
prompts: None,
|
||||
spillover_path: None,
|
||||
output_summary: Some("done".to_string()),
|
||||
is_diff: false,
|
||||
})),
|
||||
);
|
||||
app.active_cell = Some(active);
|
||||
app.active_tool_entry_completed_at.insert(0, Instant::now());
|
||||
|
||||
let text = lines_to_text(&task_panel_lines(&app, 64, 8));
|
||||
|
||||
assert!(
|
||||
text.iter().any(|line| line.contains("[x] read_file")),
|
||||
"fresh completed active row should linger briefly: {text:?}"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn tasks_panel_collapses_stale_running_tool_rows() {
|
||||
let mut app = create_test_app();
|
||||
let mut active = ActiveCell::new();
|
||||
for (idx, command) in ["long one", "long two"].into_iter().enumerate() {
|
||||
active.push_tool(
|
||||
format!("shell-{idx}"),
|
||||
HistoryCell::Tool(ToolCell::Exec(ExecCell {
|
||||
command: command.to_string(),
|
||||
status: ToolStatus::Running,
|
||||
output: None,
|
||||
started_at: Some(
|
||||
Instant::now() - ACTIVE_TOOL_STALE_RUNNING_ROW_TTL - Duration::from_secs(1),
|
||||
),
|
||||
duration_ms: None,
|
||||
source: ExecSource::Assistant,
|
||||
interaction: None,
|
||||
output_summary: None,
|
||||
})),
|
||||
);
|
||||
}
|
||||
app.active_cell = Some(active);
|
||||
|
||||
let text = lines_to_text(&task_panel_lines(&app, 80, 8));
|
||||
|
||||
assert!(
|
||||
text.iter().any(|line| line.contains("[~] run x2")),
|
||||
"stale running rows should collapse into one sidebar row: {text:?}"
|
||||
);
|
||||
assert!(
|
||||
!text.iter().any(|line| line.contains("long two")),
|
||||
"second stale command should not take another row: {text:?}"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn tasks_panel_does_not_double_count_running_shell_job_as_live_and_background() {
|
||||
let mut app = create_test_app();
|
||||
|
||||
@@ -53,6 +53,7 @@ pub(super) fn handle_tool_call_started(
|
||||
// starts in a single ExploringCell entry.
|
||||
let active = app.active_cell.as_mut().expect("active_cell just ensured");
|
||||
let entry_idx = active.ensure_exploring();
|
||||
app.active_tool_entry_completed_at.remove(&entry_idx);
|
||||
let inner = active
|
||||
.append_to_exploring(
|
||||
id.clone(),
|
||||
@@ -281,6 +282,7 @@ fn push_active_tool_cell(
|
||||
}
|
||||
let active = app.active_cell.as_mut().expect("active_cell just ensured");
|
||||
let entry_idx = active.push_tool(tool_id.to_string(), cell);
|
||||
app.active_tool_entry_completed_at.remove(&entry_idx);
|
||||
let virtual_index = app.history.len() + entry_idx;
|
||||
register_tool_cell(app, tool_id, tool_name, input, virtual_index);
|
||||
app.mark_history_updated();
|
||||
@@ -490,6 +492,7 @@ pub(super) fn handle_tool_call_complete(
|
||||
}
|
||||
}
|
||||
}
|
||||
refresh_active_tool_completion_timestamp(app, cell_index);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -646,6 +649,7 @@ pub(super) fn handle_tool_call_complete(
|
||||
if let Some(active) = app.active_cell.as_mut() {
|
||||
active.bump_revision();
|
||||
}
|
||||
refresh_active_tool_completion_timestamp(app, cell_index);
|
||||
}
|
||||
|
||||
// #455 (observer-only): fire `tool_call_after` hooks once the
|
||||
@@ -668,6 +672,46 @@ pub(super) fn handle_tool_call_complete(
|
||||
}
|
||||
}
|
||||
|
||||
fn refresh_active_tool_completion_timestamp(app: &mut App, cell_index: usize) {
|
||||
if cell_index < app.history.len() {
|
||||
return;
|
||||
}
|
||||
let entry_idx = cell_index - app.history.len();
|
||||
let Some(cell) = app.cell_at_virtual_index(cell_index) else {
|
||||
app.active_tool_entry_completed_at.remove(&entry_idx);
|
||||
return;
|
||||
};
|
||||
|
||||
if history_cell_has_running_tool(cell) {
|
||||
app.active_tool_entry_completed_at.remove(&entry_idx);
|
||||
} else {
|
||||
app.active_tool_entry_completed_at
|
||||
.entry(entry_idx)
|
||||
.or_insert_with(Instant::now);
|
||||
}
|
||||
}
|
||||
|
||||
fn history_cell_has_running_tool(cell: &HistoryCell) -> bool {
|
||||
let HistoryCell::Tool(tool) = cell else {
|
||||
return false;
|
||||
};
|
||||
match tool {
|
||||
ToolCell::Exec(exec) => exec.status == ToolStatus::Running,
|
||||
ToolCell::Exploring(explore) => explore
|
||||
.entries
|
||||
.iter()
|
||||
.any(|entry| entry.status == ToolStatus::Running),
|
||||
ToolCell::PlanUpdate(plan) => plan.status == ToolStatus::Running,
|
||||
ToolCell::PatchSummary(patch) => patch.status == ToolStatus::Running,
|
||||
ToolCell::Review(review) => review.status == ToolStatus::Running,
|
||||
ToolCell::DiffPreview(_) => false,
|
||||
ToolCell::Mcp(mcp) => mcp.status == ToolStatus::Running,
|
||||
ToolCell::ViewImage(_) => false,
|
||||
ToolCell::WebSearch(search) => search.status == ToolStatus::Running,
|
||||
ToolCell::Generic(generic) => generic.status == ToolStatus::Running,
|
||||
}
|
||||
}
|
||||
|
||||
/// Build a finalized standalone history cell for a tool completion whose
|
||||
/// start was never registered (orphan). This preserves the contract that
|
||||
/// every tool result is visible somewhere; the alternative (silently
|
||||
|
||||
@@ -6192,6 +6192,7 @@ fn apply_loaded_session(app: &mut App, config: &Config, session: &SavedSession)
|
||||
app.tool_details_by_cell.clear();
|
||||
app.active_cell = None;
|
||||
app.active_tool_details.clear();
|
||||
app.active_tool_entry_completed_at.clear();
|
||||
app.active_cell_revision = app.active_cell_revision.wrapping_add(1);
|
||||
app.exploring_cell = None;
|
||||
app.exploring_entries.clear();
|
||||
|
||||
@@ -7,7 +7,7 @@ Current boundary note (v0.8.6):
|
||||
- Other workspace crates are being split out incrementally, but they are not yet the sole runtime source of truth.
|
||||
- The LSP subsystem (`crates/tui/src/lsp/`) is fully wired into the engine's post-tool-execution path
|
||||
(`core/engine/lsp_hooks.rs`), providing inline diagnostics after every edit_file/apply_patch/write_file.
|
||||
- The swarm agent system was removed in v0.8.5. The active v0.8.34 orchestration surface is persistent sub-agent sessions (`agent_open` / `agent_eval` / `agent_close`) and persistent RLM sessions (`rlm_open` / `rlm_eval` / `rlm_configure` / `rlm_close`).
|
||||
- The swarm agent system was removed in v0.8.5. The active v0.8.35 orchestration surface is persistent sub-agent sessions (`agent_open` / `agent_eval` / `agent_close`) and persistent RLM sessions (`rlm_open` / `rlm_eval` / `rlm_configure` / `rlm_close`).
|
||||
No model-visible swarm tool remains in the active codebase.
|
||||
|
||||
## High-Level Overview
|
||||
|
||||
@@ -15,7 +15,7 @@ chosen over the available shell equivalent. Companion to `crates/tui/src/prompts
|
||||
for the same backing operation are a model trap — the LLM will alternate
|
||||
between them and the cache hit rate suffers.
|
||||
|
||||
## Current surface (v0.8.34)
|
||||
## Current surface (v0.8.35)
|
||||
|
||||
### File operations
|
||||
|
||||
@@ -269,7 +269,7 @@ rg -n '"handle_read"|"rlm_open"|"rlm_eval"|"rlm_configure"|"rlm_close"|"agent_op
|
||||
rg -n 'handle_read|rlm_open|rlm_eval|rlm_configure|rlm_close|agent_open|agent_eval|agent_close' docs crates/tui/src/prompts crates/tui/src/tools
|
||||
```
|
||||
|
||||
The canonical v0.8.34 live names are:
|
||||
The canonical v0.8.35 live names are:
|
||||
|
||||
- `handle_read`
|
||||
- `rlm_open`, `rlm_eval`, `rlm_configure`, `rlm_close`
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "deepseek-tui",
|
||||
"version": "0.8.34",
|
||||
"deepseekBinaryVersion": "0.8.34",
|
||||
"version": "0.8.35",
|
||||
"deepseekBinaryVersion": "0.8.35",
|
||||
"description": "Install and run deepseek and deepseek-tui binaries from GitHub release artifacts.",
|
||||
"author": "Hmbown",
|
||||
"license": "MIT",
|
||||
|
||||
@@ -19,7 +19,7 @@ export interface RepoFacts {
|
||||
|
||||
export const FACTS: RepoFacts = {
|
||||
"generatedAt": "2026-05-13T06:15:45.167Z",
|
||||
"version": "0.8.34",
|
||||
"version": "0.8.35",
|
||||
"crates": [
|
||||
"agent",
|
||||
"app-server",
|
||||
|
||||
Reference in New Issue
Block a user