Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 10 additions & 3 deletions config.example.toml
Original file line number Diff line number Diff line change
Expand Up @@ -769,9 +769,11 @@ exponential_base = 2.0
# ─────────────────────────────────────────────────────────────────────────────────
# Auto-compaction is a saved UI setting edited with `/config` (`auto_compact`).
# The optional saved threshold setting is `auto_compact_threshold_percent`
# (default 80). There is no config-file
# `[compaction]` table yet; runtime compaction budgets are chosen by the TUI
# from the active model/context window.
# (default 80). `[compaction].enabled` is the engine-level replacement
# compaction switch; when unset, the engine keeps using the saved auto_compact
# behavior.
[compaction]
# enabled = true

# Append-only Flash seams are experimental and opt-in while the v0.7.5
# context/cache audit validates prefix-cache behavior.
Expand All @@ -785,6 +787,11 @@ l2_threshold = 384000
l3_threshold = 576000
seam_model = "deepseek-v4-flash"

# Optional explicit alias for the context seam manager master switch. When set,
# this overrides `[context].enabled`; thresholds and model stay under [context].
# [seam_manager]
# enabled = false

# ─────────────────────────────────────────────────────────────────────────────────
# Workshop / Large-Output Routing (#548)
# ─────────────────────────────────────────────────────────────────────────────────
Expand Down
48 changes: 48 additions & 0 deletions crates/tui/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1682,6 +1682,22 @@ pub struct ContextConfig {
pub seam_model: Option<String>,
}

/// Explicit Flash seam-manager switch.
#[derive(Debug, Clone, Deserialize, Default)]
pub struct SeamManagerConfig {
/// Overrides `[context].enabled` when set. Default: inherit context.enabled.
#[serde(default)]
pub enabled: Option<bool>,
}

/// Engine replacement-compaction switch.
#[derive(Debug, Clone, Deserialize, Default)]
pub struct CompactionRuntimeConfig {
/// Overrides the settings-derived auto-compaction default when set.
#[serde(default)]
pub enabled: Option<bool>,
}

/// Sub-agent model overrides. Keys in `models` can be role names (`worker`,
/// `explorer`, `awaiter`) or type names (`general`, `explore`, `plan`,
/// `review`, `custom`). Per-call explicit model choices still win.
Expand Down Expand Up @@ -1998,6 +2014,16 @@ pub struct Config {
#[serde(default)]
pub context: ContextConfig,

/// Explicit Flash seam-manager switch (#3765). This is a narrow alias for
/// `[context].enabled`; threshold/model fields stay under `[context]`.
#[serde(default, alias = "seamManager")]
pub seam_manager: SeamManagerConfig,

/// Engine replacement-compaction switch (#3765). When unset, the runtime
/// keeps using the settings-derived `auto_compact` behavior.
#[serde(default)]
pub compaction: CompactionRuntimeConfig,

/// Agent Fleet trust/security/role/exec config.
#[serde(default)]
pub fleet: Option<codewhale_config::FleetConfigToml>,
Expand Down Expand Up @@ -3640,6 +3666,19 @@ impl Config {
self.context.project_pack.unwrap_or(true)
}

#[must_use]
pub fn seam_manager_enabled(&self) -> bool {
self.seam_manager
.enabled
.or(self.context.enabled)
.unwrap_or(false)
}

#[must_use]
pub fn compaction_enabled(&self, settings_default: bool) -> bool {
self.compaction.enabled.unwrap_or(settings_default)
}

/// Return whether shell execution is allowed. Defaults to `false`: shell
/// access must be opted into explicitly (GHSA-72w5-pf8h-xfp4).
#[must_use]
Expand Down Expand Up @@ -5536,6 +5575,15 @@ fn merge_config(base: Config, override_cfg: Config) -> Config {
.or(base.context.l3_threshold),
seam_model: override_cfg.context.seam_model.or(base.context.seam_model),
},
seam_manager: SeamManagerConfig {
enabled: override_cfg
.seam_manager
.enabled
.or(base.seam_manager.enabled),
},
compaction: CompactionRuntimeConfig {
enabled: override_cfg.compaction.enabled.or(base.compaction.enabled),
},
fleet: override_cfg.fleet.or(base.fleet),
subagents: override_cfg.subagents.or(base.subagents),
strict_tool_mode: override_cfg.strict_tool_mode.or(base.strict_tool_mode),
Expand Down
74 changes: 74 additions & 0 deletions crates/tui/src/config/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3509,6 +3509,7 @@ fn normalize_model_name_accepts_provider_prefixed_deepseek_ids() {
fn default_context_seams_are_opt_in() {
let config = Config::default();
assert!(!config.context.enabled.unwrap_or(false));
assert!(!config.seam_manager_enabled());
assert_eq!(config.context.l1_threshold.unwrap_or(192_000), 192_000);
assert_eq!(
config
Expand All @@ -3520,6 +3521,57 @@ fn default_context_seams_are_opt_in() {
);
}

#[test]
fn seam_manager_enabled_can_use_dedicated_table() -> Result<()> {
let config: Config = toml::from_str(
r#"
[context]
enabled = true

[seam_manager]
enabled = false
"#,
)?;

assert_eq!(config.context.enabled, Some(true));
assert_eq!(config.seam_manager.enabled, Some(false));
assert!(!config.seam_manager_enabled());

let config: Config = toml::from_str(
r#"
[seam_manager]
enabled = true
"#,
)?;

assert!(config.seam_manager_enabled());
Ok(())
}

#[test]
fn compaction_enabled_uses_config_override_when_present() -> Result<()> {
let config = Config::default();
assert!(config.compaction_enabled(true));
assert!(!config.compaction_enabled(false));

let disabled: Config = toml::from_str(
r#"
[compaction]
enabled = false
"#,
)?;
assert!(!disabled.compaction_enabled(true));

let enabled: Config = toml::from_str(
r#"
[compaction]
enabled = true
"#,
)?;
assert!(enabled.compaction_enabled(false));
Ok(())
}

#[test]
fn profile_without_context_does_not_disable_base_context() {
let mut profiles = HashMap::new();
Expand All @@ -3539,6 +3591,28 @@ fn profile_without_context_does_not_disable_base_context() {
assert_eq!(merged.context.enabled, Some(true));
}

#[test]
fn profile_without_context_gates_does_not_disable_base_gates() {
let mut profiles = HashMap::new();
profiles.insert("work".to_string(), Config::default());
let config = ConfigFile {
base: Config {
seam_manager: SeamManagerConfig {
enabled: Some(true),
},
compaction: CompactionRuntimeConfig {
enabled: Some(false),
},
..Default::default()
},
profiles: Some(profiles),
};

let merged = apply_profile(config, Some("work")).expect("profile");
assert_eq!(merged.seam_manager.enabled, Some(true));
assert_eq!(merged.compaction.enabled, Some(false));
}

#[test]
fn profile_skills_config_merges_individual_fields() {
let mut profiles = HashMap::new();
Expand Down
2 changes: 1 addition & 1 deletion crates/tui/src/core/engine.rs
Original file line number Diff line number Diff line change
Expand Up @@ -916,7 +916,7 @@ impl Engine {
// is worth the extra request and transcript mutation.
let seam_manager = deepseek_client.as_ref().map(|main_client| {
let seam_config = SeamConfig {
enabled: api_config.context.enabled.unwrap_or(false),
enabled: api_config.seam_manager_enabled(),
verbatim_window_turns: api_config
.context
.verbatim_window_turns
Expand Down
2 changes: 1 addition & 1 deletion crates/tui/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7027,7 +7027,7 @@ async fn run_exec_agent(
)
};
let compaction = CompactionConfig {
enabled: auto_compact_enabled,
enabled: execution_config.compaction_enabled(auto_compact_enabled),
model: effective_model.clone(),
token_threshold: crate::route_budget::compaction_threshold_for_route_at_percent(
effective_provider,
Expand Down
4 changes: 2 additions & 2 deletions crates/tui/src/runtime_threads.rs
Original file line number Diff line number Diff line change
Expand Up @@ -874,7 +874,7 @@ impl RuntimeThreadManager {
)
};
let compaction = crate::compaction::CompactionConfig {
enabled: auto_compact_enabled,
enabled: cfg.compaction_enabled(auto_compact_enabled),
model: String::new(), // per-engine, filled below
token_threshold: compaction_threshold_for_model_at_percent(
&cfg.default_text_model.clone().unwrap_or_default(),
Expand Down Expand Up @@ -2506,7 +2506,7 @@ impl RuntimeThreadManager {
auto_compact_default_for_model(&thread.model)
};
let compaction = CompactionConfig {
enabled: auto_compact_enabled,
enabled: cfg.compaction_enabled(auto_compact_enabled),
model: thread.model.clone(),
token_threshold: compaction_threshold_for_model_at_percent(
&thread.model,
Expand Down
6 changes: 5 additions & 1 deletion crates/tui/src/tui/app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1696,6 +1696,7 @@ pub struct App {
#[allow(dead_code)]
pub system_prompt: Option<SystemPrompt>,
pub auto_compact: bool,
pub compaction_enabled_override: Option<bool>,
pub auto_compact_user_configured: bool,
pub auto_compact_threshold_percent: f64,
pub calm_mode: bool,
Expand Down Expand Up @@ -2628,6 +2629,7 @@ impl App {
bracketed_paste_seen: false,
system_prompt: None,
auto_compact,
compaction_enabled_override: config.compaction.enabled,
Comment thread
nightt5879 marked this conversation as resolved.
auto_compact_user_configured,
auto_compact_threshold_percent,
calm_mode,
Expand Down Expand Up @@ -5692,7 +5694,9 @@ impl App {

pub fn compaction_config(&self) -> CompactionConfig {
CompactionConfig {
enabled: self.auto_compact,
enabled: self
.compaction_enabled_override
.unwrap_or(self.auto_compact),
Comment thread
nightt5879 marked this conversation as resolved.
Outdated
token_threshold: self.compact_threshold,
model: self.effective_model_for_budget().to_string(),
..Default::default()
Expand Down
27 changes: 26 additions & 1 deletion crates/tui/src/tui/app/tests.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
use super::*;
use crate::config::{ApiProvider, Config, ProviderConfig, ProvidersConfig};
use crate::config::{
ApiProvider, CompactionRuntimeConfig, Config, ProviderConfig, ProvidersConfig,
};
use crate::test_support::{EnvVarGuard, lock_test_env};
use crate::tools::plan::{PlanItemArg, StepStatus, UpdatePlanArgs};
use crate::tools::todo::TodoStatus;
Expand Down Expand Up @@ -1955,6 +1957,29 @@ fn test_compaction_config() {
assert_eq!(config.model, "deepseek-v4-flash");
}

#[test]
fn compaction_config_respects_config_enabled_override() {
let config = Config {
compaction: CompactionRuntimeConfig {
enabled: Some(false),
},
..Default::default()
};
let mut app = App::new(test_options(false), &config);
app.auto_compact = true;
assert!(!app.compaction_config().enabled);

let config = Config {
compaction: CompactionRuntimeConfig {
enabled: Some(true),
},
..Default::default()
};
let mut app = App::new(test_options(false), &config);
app.auto_compact = false;
assert!(app.compaction_config().enabled);
}

#[test]
fn test_update_model_compaction_budget() {
let mut app = App::new(test_options(false), &Config::default());
Expand Down
26 changes: 19 additions & 7 deletions docs/CONFIGURATION.md
Original file line number Diff line number Diff line change
Expand Up @@ -933,7 +933,9 @@ policy for known context windows up to the 1M-token V4 class. Automatic
compaction runs before the active model limit and carries the compacted summary
forward into the next request. The trigger defaults to
`auto_compact_threshold_percent = 80`. Users who prefer manual continuity can
persist `auto_compact = false`; manual `/compact` / Ctrl+L remains available.
persist `auto_compact = false`; `[compaction].enabled = false` is the
engine-level config.toml switch for replacement compaction. Manual `/compact` /
Ctrl+L remains available.
You can inspect or update these from the TUI with `/settings` and `/config`
(interactive editor).

Expand Down Expand Up @@ -1021,12 +1023,13 @@ separate:
| Cost estimate | Approximate spend from provider usage and configured DeepSeek rates. | Display only. |

For known context-window models, including 1M-class V4 models, replacement
compaction is enabled by default unless the user explicitly configures
`auto_compact = false`. It fires at the active model's compaction threshold and
replays the generated summary through the stable system prompt on the next
request. Unknown model ids remain opt-in. The Flash seam manager remains opt-in
(`[context].enabled = false`), and the capacity controller remains disabled
unless configured.
compaction follows `[compaction].enabled` when explicitly configured; otherwise
it keeps the existing `auto_compact` settings behavior. It fires at the active
model's compaction threshold and replays the generated summary through the
stable system prompt on the next request. Unknown model ids remain opt-in. The
Flash seam manager remains disabled by default; `[context].enabled` opts in,
and `[seam_manager].enabled` can explicitly override that master switch. The
capacity controller remains disabled unless configured.

### Command Migration Notes

Expand Down Expand Up @@ -1266,6 +1269,10 @@ If you are upgrading from older releases:
`~/.codewhale/snapshots/<project_hash>/<worktree_hash>/.git`, with legacy
`~/.deepseek/snapshots/...` fallback when only the legacy state exists, and
never use the workspace's own `.git` directory
- `compaction.*` (optional): replacement compaction engine switch:
- `[compaction].enabled` (bool, default unset): when set, overrides the
settings-derived `auto_compact` engine switch. Set `false` for full manual
control over automatic replacement compaction.
- `context.*` (optional): append-only Fin seam manager, currently opt-in.
Fin is the fast `deepseek-v4-flash` path with thinking off used for
coordination work such as routing, summaries, and context maintenance.
Expand All @@ -1277,6 +1284,11 @@ If you are upgrading from older releases:
- `[context].l2_threshold` (int, default `384000`)
- `[context].l3_threshold` (int, default `576000`)
- `[context].seam_model` (string, default `deepseek-v4-flash`)
- `seam_manager.*` (optional): explicit alias for the Flash seam manager master
switch:
- `[seam_manager].enabled` (bool, default unset): overrides
`[context].enabled` when set. Thresholds and model remain under
`[context]`.
- `retry.*` (optional): retry/backoff settings for API requests:
- `[retry].enabled` (bool, default `true`)
- `[retry].max_retries` (int, default `3`)
Expand Down
Loading