Follow-up to the previous commit that removed two leaked handoff
files from `.claude/`. The actual removal landed, but the
`.gitignore` rules that would prevent a future accidental re-add
were left in the working tree by mistake.
Adds:
.claude/HANDOFF_*
.claude/CODEMAP_*
.claude/handoff_* (case-insensitive belt-and-suspenders)
.claude/codemap_*
so `git add .claude/HANDOFF_v0.8.32_*` etc. is silently dropped at
add time instead of slipping through review.
Verified with `git check-ignore`:
.gitignore:89:.claude/handoff_* .claude/HANDOFF_test_scratch.md
Two coordinated changes that stop the engine from routinely rewriting the
prompt prefix and burning DeepSeek V4's prefix-cache discount:
1. `Settings::default().auto_compact` flips from `true` to `false`. The
`auto_compact = on` opt-in and the explicit `/compact` slash command
stay available for users / agents that decide their workload benefits
from compaction more than from cache stability. With V4's 1M-token
window the user has plenty of headroom to run long sessions without
auto-trimming, and aggressive compaction has been the dominant
cost-spike vector in long sessions (the rewritten prefix invalidates
~90% of the cache discount on every compaction event).
2. `DEFAULT_COMPACTION_TOKEN_THRESHOLD` raised from `50_000` to
`102_400` (80% of `DEFAULT_CONTEXT_WINDOW_TOKENS = 128_000`). This is
the last-resort threshold used when `context_window_for_model` returns
`None` — i.e. an unrecognised model id. Pre-v0.8.11 the fallback
compacted at ~5% of a V4 window when model detection silently fell
through. Now the fallback inherits the same late-trigger discipline as
the V4 path, so model-detection drift doesn't quietly burn cache.
Together: the two changes mean compaction never fires automatically by
default, and even when explicitly opted in (or when the runtime-thread /
capacity-flow paths invoke compaction with their own `enabled = true`
config), the threshold is anchored at 80% of the model's context window
(or 80% of the 128K default if the model is unknown), never below.
Tests
=====
- `default_settings_disable_auto_compact_to_protect_v4_prefix_cache` —
pins the new default and explains the rationale inline.
- `auto_compact_remains_explicitly_configurable` — unchanged; still
asserts the `set("auto_compact", "on" | "off")` round-trip works.
- `compaction_threshold_scales_with_context_window` — updated to assert
`compaction_threshold_for_model("unknown-model") == 102_400`.
- `v4_soft_caps_only_apply_to_v4_models` — updated to assert the
unknown-model + reasoning-effort path also lands on the new floor.
Verification
============
- `cargo fmt --all -- --check` clean.
- `cargo clippy -p deepseek-tui --bin deepseek-tui --all-features
--locked -- -D warnings` clean.
- `cargo test -p deepseek-tui --bin deepseek-tui --locked` →
2028 passed, 2 ignored.
Refs #664 (handoff-instead-of-compact pattern, full implementation
deferred). Behaviour-only change for v0.8.11; the larger
agent-aware-handoff mechanism is its own design surface.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Resolves the post-#514/#517/#518 conflicts:
- CHANGELOG.md: kept both polish-stack and Linux ARM64 entries under
[Unreleased]; reordered so the ARM64/install-message Changed/Docs
sections precede the Releases footer.
- config.example.toml: kept both the `instructions = [...]` example
and the `[memory]` opt-in stanza in sequence.
- crates/tui/src/config.rs: kept both `instructions_paths()` (#454)
and `memory_enabled()` (#489) on the Config impl.
- crates/tui/src/prompts.rs: extended
`system_prompt_for_mode_with_context_and_skills` to take BOTH
`instructions: Option<&[PathBuf]>` and `user_memory_block:
Option<&str>`. Section 2.5a renders instructions; 2.5b renders the
memory block — both above the skills block so KV prefix caching
still wins.
- crates/tui/src/core/engine.rs: thread both args through the two
call sites.
- crates/tui/src/prompts.rs: update the `system_prompt_for_mode_with_context`
forwarder and the test caller to pass `None` for the new arg.
- .gitignore: ignore `.claude/*.local.md` and `*.local.json` so
local ralph / Claude-Code notes can't leak into commits.
Folds in two valid suggestions from the gemini-code-assist review on #519:
- `client.rs`: collapse the duplicated `LlmError → label` match and the
`human_retry_reason` body into a single
`retry_reason_label_and_human(err) -> (&'static str, String)` helper.
- `widgets/footer.rs::retry_banner_spans`: merge the two separate
`match &props.retry` blocks into one that returns both `(label, color)`.
Behavior is unchanged; refactor is a pure DRY win.
* feat: add config UI support for TUI and web modes
- Introduced a new `config_ui.rs` module to handle configuration UI for TUI and web.
- Updated `TuiOptions` and `App` structures to include `config_path` and `config_profile`.
- Implemented functions to build and apply configuration documents.
- Added tests to ensure the new configuration UI behaves as expected.
- Integrated web configuration session handling into the event loop.
- Updated various modules to accommodate the new configuration options and UI.
* refactor(tui): remove local path reference for schemaui dependency
Remove the local file system path reference for schemaui in favor of
using the published crate from the registry. This change updates the
Cargo.toml to use only the version specification and adds the source
and checksum information to Cargo.lock.
* fix: add AGENTS.md guide and improve config error handling
- Add comprehensive AGENTS.md file with project instructions for AI
assistants, including build commands, dependencies, and GitHub
operations guidance
- Introduce is_error field to CommandResult struct for better error
tracking
- Refactor config application logic to properly handle errors using
the new is_error flag
- Add test utilities for WebConfigSession to support testing
- Optimize web config event polling by extracting drain logic into
separate function
- Add unit tests for session-only config application and engine sync
requirements
* fix(security): add SSRF protection to fetch_url (#261)
Block private, link-local, and cloud metadata IPs in fetch_url HTTP requests. Co-authored-by: JasonOA888
* test(portability): inject paths instead of mutating HOME (Windows fix)
CI's `Test (windows-latest)` job failed because both my new tests
(composer_history and the spawn_supervised crash-dump test) mutated
HOME to redirect `dirs::home_dir()`. That works on macOS / Linux but
not on Windows, where dirs::home_dir() reads USERPROFILE / queries
SHGetKnownFolderPath rather than HOME.
Fix: refactor both modules to expose path-injecting helpers so tests
never need to touch the env var:
- composer_history: split load_history / append_history into thin
wrappers around load_history_from(&Path) / append_history_to(&Path).
Tests use the *_to / *_from form with a tempdir path.
- utils::write_panic_dump: same pattern — write_panic_dump_to(&Path)
takes the crash dir directly. The spawn_supervised end-to-end test
splits into two: one verifies panic-doesn't-propagate (no on-disk
side effect needed), one verifies write_panic_dump_to writes the
expected log format.
Production callers continue to use the env-driven default (`HOME`/
`USERPROFILE` via `dirs::home_dir()`) so no behavior change. Tests
work identically on every platform now.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix(tui): clear chat area each frame so stale cells don't bleed into sidebar
ChatWidget's render path was `Paragraph::new(lines).render(content_area, buf)`
with no Block and no Clear — ratatui's Paragraph only writes cells that
contain text, leaving any cell the current frame's paragraph doesn't
touch holding the *previous* frame's contents. With wide tool output
(`gh pr list`, `git log`) emitting ISO-8601 timestamps like
`2026-05-02T07:29:24Z`, then a subsequent shorter-paragraph frame, the
old timestamp tails (`:24Z`, `7:29:24Z`, etc.) persisted on the right
edge of the chat area, visually colliding with the section headers in
the sidebar (`Plan` rendering as `:24Zan`, `Agents` as `:24Zents`).
Fix: render `Clear` over the full content_area before drawing the
Paragraph. Cheap (one buffer-fill per frame) and guarantees stale cells
can never persist into the next frame's render.
Reported in v0.8.5 testing right after install. The other v0.8.5
bordered widgets (composer, sidebar sections, footer) already render
into a Block with a solid background style, so they were never
affected — only the chat area used a bare Paragraph.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* feat(theme): vendor + theme schemaui to deepseek navy palette (config UI)
The schemaui-0.12.0 crate the contributor brought in via #365 ships
hardcoded Color::Gray / Color::DarkGray / Color::White / Color::Yellow
references across its rendering components. Visually it clashed with
the rest of deepseek-tui — the editor area read as gray-on-black on a
TUI that's otherwise navy ink + sky accents. Two ship-day options
weren't acceptable: defaulting back to the legacy modal lost the new
editor's UX, and living with gray was off-brand.
This commit forks schemaui at 0.12.0 into vendor/schemaui-0.12.0 and
themes the rendering layer to match deepseek-tui's palette. The patch
is wired in via a workspace-level [patch.crates-io] override so the
deepseek-tui Cargo.toml continues to depend on `schemaui = "0.12.0"`
and would automatically resolve back to crates.io if we ever drop the
override (e.g. once upstream lands a ColorTheme API).
Changes inside the vendored fork:
- New `src/deepseek_palette.rs` with the brand RGB values:
SURFACE_INK / SURFACE_RAISED for backgrounds, BORDER_DIM /
BORDER_ACTIVE for chrome, TEXT_PRIMARY / TEXT_MUTED / TEXT_DIM,
ACCENT_SKY / ACCENT_BLUE / ACCENT_PURPLE, and STATUS_OK / WARN /
ERROR. Values mirror crates/tui/src/palette.rs in the workspace.
- `src/lib.rs` exposes the palette module under `cfg(feature = "tui")`.
- `src/tui/view/frame.rs::draw` paints a navy backdrop across the
full frame area before any child widget renders, so any cell that
doesn't get explicitly written reads as ink instead of the terminal
default.
- `tabstrip.rs`, `overlay.rs`, `popup.rs`, `body.rs`, `sections.rs`,
`footer.rs`, `help.rs`, `fields.rs`: every Color::Gray / DarkGray /
White / Yellow / Cyan / Blue / Magenta / Red / Green / LightBlue
swapped out for a deepseek_palette token, plus explicit `bg(...)`
fills on the top-level Block styles and Paragraph wrappers.
- `Cargo.toml` adds an empty `[workspace]` so the vendored crate
builds standalone (its dev-deps don't drift into ours).
Workspace-level changes:
- `Cargo.toml` adds `[patch.crates-io] schemaui = { path =
"vendor/schemaui-0.12.0" }`. Production deepseek-tui builds pick up
the themed fork transparently.
- `.gitignore` excludes `vendor/.../web/ui/node_modules/` (15 MB of
npm artefacts the Rust build doesn't need) and the vendored
Cargo.lock (regenerated locally per build).
Verification:
- cargo build --workspace --all-features: clean
- cargo clippy --workspace --all-targets --all-features --locked: clean
- cargo test --workspace: 1777 passed, 0 failed
- /config inside `deepseek` now opens a navy-themed editor matching
the rest of the TUI; tabs, body panel, footer, popup, and help
overlay all read on brand.
Future work tracked separately: upstream a `with_theme(ColorTheme)`
builder API to schemaui so we can drop the fork. Until then, sync the
fork against new schemaui releases when we want their fixes.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* Revert "feat(theme): vendor + theme schemaui to deepseek navy palette"
This reverts ed597ccc — vendoring 28,913 lines of schemaui to recolor
a config editor was the wrong tradeoff. Maintenance cost for a
cosmetic match wasn't worth it, and the recolor wasn't even fully
working (terminal-default bg kept bleeding through Style::default()
calls in the form fields).
The simpler path: keep the schemaui-driven editor available as
`/config tui` for users who want the form-style UX, but make bare
`/config` open the legacy native modal that already matches the
deepseek-tui navy chrome by inheritance. No fork, no vendored copy,
no ongoing sync burden.
Changes:
- `git rm -r vendor/schemaui-0.12.0/` (28,913 lines gone)
- Drop `[patch.crates-io]` from workspace Cargo.toml — schemaui
resolves back to crates.io v0.12.0 unmodified.
- Drop the corresponding `.gitignore` exclusions (no more vendor dir
to filter).
- `config_ui::parse_mode` default mode flipped from `Tui` to `Native`.
Bare `/config` → legacy navy modal. Explicit `/config tui` → the
contributor's schemaui editor (still available, gray-on-default
chrome, but opt-in). `/config web` and `/config <key>` /
`/config <key> <value>` unchanged.
- Help text updated to list `[native|tui|web]` in that order.
Verified: cargo build / clippy --workspace --all-features --locked
with -D warnings: clean.
The contributor's work (#365) ships and gets credit; users discover
the alternate editor via the help text.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix(tui): paint chat area with explicit navy ink instead of Clear
The Clear-instead-of-fill in 0ae2cead reset cells to the terminal's
default background, which read as a brown-gray on most user setups
even though the rest of the TUI chrome is navy. Replace the Clear
with an explicit Block fill at palette::DEEPSEEK_INK, and pass the
same bg through to the Paragraph itself so streamed text cells
inherit ink rather than bouncing back to terminal default.
Net effect: the chat area visually unifies with the sidebar /
composer / footer instead of showing as a contrasting brown-gray
panel in the middle of an otherwise navy frame.
Stale-cell guarantee from #372-followup is preserved — the Block
fills every cell in the area on each frame, so wide tool output
(`gh pr list` ISO timestamps, etc.) still can't bleed past the
current frame's actual text.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix(config): update tests for Native default + fix default_model override in session-only apply
- Update test_show_config_defaults_to_native and
execute_config_opens_config_view_action to expect
OpenConfigView (Native) instead of OpenConfigEditor(Tui),
matching the parse_mode default change from ce98f054.
- Fix apply_document bug where default_model was processed
in the main key-value loop after model, causing
set_config_value('default_model') to overwrite the
runtime model. default_model is now only applied when
persist=true, preventing session-only edits from being
silently reverted.
* style: cargo fmt
* chore: remove end-of-night report (session artifact)
---------
Co-authored-by: unic <yuniqueunic@gmail.com>
Co-authored-by: Jason <jason@aveoresearchlabs.com>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-authored-by: YuniqueUnic <YuniqueUnic@users.noreply.github.com>
- Remove the `publish-npm` job from `release.yml`. It has been failing on
every release with `npm error code EOTP` because the configured `NPM_TOKEN`
doesn't bypass 2FA. Manual publish from a developer machine is the actual
ship path; codify that.
- Update `docs/RELEASE_RUNBOOK.md` "npm Wrapper Release" to describe the
manual flow (`npm publish --access public` + OTP) and explain why the auto
path is gone, with a recovery note for future Trusted-Publishing migration.
- Refresh stale cross-reference comment in `publish-npm.yml` (the workflow
remains as inert plumbing for an eventual Trusted Publishing setup).
- Stop tracking `docs/DeepSeek_V4.pdf` (4.4 MB). It was never referenced
outside test fixture filenames; the tests synthesize their own fake PDF.
Add to `.gitignore` so a local copy can sit there without nagging.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Captures the full RLM-fundamental story across the design doc, MODES.md,
and the Hetun prompt. Tracking issues are now #46–#55 (helper layer
filed as #53, Hetun as #54, vendoring as #55).
What this nails down:
- **Hetun mode** is added at the END of the Tab cycle (Plan → Agent →
YOLO → Hetun → Plan), not as a Plan replacement. Default landing mode
is unchanged so people don't accidentally start there. Plan stays as
it is.
- **Mission-level approval, not block-level.** Hetun runs a research
phase, presents one mission card, and only executes after explicit
user approval. Inside the execution turn the repl block runs straight
through with no per-block prompts — that's the whole point of the
mode.
- **The user's configured model is left alone on enter/exit.** Pro/max
users stay on Pro/max. The flash-as-coordinator behaviour is internal
to the runtime (ZIGRLM_RLM_CMD always points to flash regardless of
mode). No global model swap.
- **No /hetun slash command.** Tab cycles into the mode; /plan keeps
switching to Plan as today.
- **The helper layer (#53) is fundamental, not aleph-derived.** A
curated ~20-function ctx-helper module + AST-validated Python sandbox
baked into the repl runtime so a single block can load → slice → fan
out flash queries → aggregate without crossing tool boundaries.
Inspired by aleph's pattern but our own native primitive — not a port.
- **Hetun research methodology adopts Sakana's Fugu patterns.** The
research phase is recursive novelty sampling + hierarchical narrative
tree synthesis + multi-detector cross-verification (flash for
breadth, Pro for depth) + hypothesis-verification loop. Not "fan out
8 fixed queries". This is what makes "Plan + Recursive Agents"
meaningful versus a flash-coordinator wrapper.
- **No version-number framing anywhere.** The plan ships as one cohesive
RLM landing across #46/#48/#49/#50/#53/#54/#55 — order is dependency,
not release schedule. We keep shipping.
- **Auto-compaction stays automatic.** Removed a manual /compact nag
from the Hetun prompt; the existing coherence + capacity system
already handles this.
Files:
docs/rlm-design.md new — full design doc with Hetun details
docs/research-react-vs-rlm.md new — supporting research treatment
docs/MODES.md 4-mode cycle, Hetun added at end, Plan kept
crates/tui/src/prompts/hetun.txt prompt teaching the recursive-novelty
+ hierarchical-synthesis + verification-loop
rhythm, mission-card structure, two-step gate
.gitignore ignore .claude/scheduled_tasks.lock runtime
Closes nothing yet — implementation lands across the tracking issues.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Add Yahoo Finance quote tool with chart fallback, redesign header widget
with proportional truncation and context bar, refactor footer status strip,
expand test suite to 680+ tests, and fix blocking issues (usize underflow
in header, tempdir leak in finance tests, per-call HTTP client creation).
- Move src/* into crates/tui/src/ to create a proper workspace structure
- Add .claude/ and .trimtab/ directories for Trimtab closed-loop workflow
- Add DEPENDENCY_GRAPH.md and update documentation
- Update Cargo.toml files to reflect new crate dependencies
- Update CI workflows and npm package scripts
- All tests pass, release build works
- Add npm/deepseek-tui package that downloads prebuilt binaries from
GitHub releases (supports macOS, Linux, Windows)
- Published as deepseek-tui@0.3.28 on npmjs.com
- Update README to feature npm as primary install method
- Add npm badge
- Redesign footer: live clock, lowercase mode badges, color-coded context %
- Stream thinking/reasoning blocks in real-time with sidebar style and cursor
- Replace ThinkingSummary with richer Thinking variant in history cells
- Remove dead code (unused footer helpers, context bar, copy hint)
- Bump version to 0.3.3