feat(settings): tambah mode ringkas opsional (concise_output)

Off by default, diaktifkan lewat settings.json (belum ada UI toggle —
sama seperti review_enabled/session_archive_enabled/lsp_auto_provision
yang juga cuma bisa diedit manual hari ini). Saat aktif, system prompt
diberi instruksi menulis ringkas, dengan pengecualian eksplisit untuk
konfirmasi operasi destruktif dan peringatan keamanan yang tetap harus
detail penuh.
This commit is contained in:
asepharyana
2026-07-16 07:56:11 +07:00
parent 85d16ebe97
commit d6de9735ab
2 changed files with 59 additions and 1 deletions
+18 -1
View File
@@ -702,6 +702,7 @@ fn spawn_turn(state: &AppStateRest) {
}
};
let context_window = crate::app::runtime::context::window::resolve(&state.app_config, &state.settings);
let concise_output = state.settings.concise_output;
let (temperature, max_tokens) = crate::app::mode::effort::generation_params(
state.misc.effort_level,
state.settings.max_tokens,
@@ -746,6 +747,7 @@ fn spawn_turn(state: &AppStateRest) {
max_tokens,
abort_flag,
hive_mind_converged,
concise_output,
};
let result = run_agent_turn(&tc, &messages, &events_q);
if let Err(e) = result {
@@ -778,6 +780,10 @@ struct TurnCtx {
/// of this turn — whether a hive-mind convergence already completed
/// earlier in this session.
hive_mind_converged: bool,
/// Snapshot of `Settings.concise_output` taken at the start of this
/// turn, so the system-prompt assembly above can read it without
/// `TurnCtx` needing a `Settings` reference.
concise_output: bool,
}
/// Build an ASCII tree of the workspace directory structure for the
@@ -964,12 +970,23 @@ fn run_agent_turn(
// workspace tree and reads all memory files each time).
let tree_info = generate_workspace_tree(&tc.workspace_roots);
let memory_section = build_memory_section(&tc.ctx.memory_dir);
let concise_section = if tc.concise_output {
"\n\nWrite tersely: drop articles (a/an/the), filler words (just/really/basically/\
actually/simply), pleasantries (sure/certainly/of course/happy to), and hedging. \
Fragments are fine. Code, commands, file paths, and error text must stay byte-exact \
— never abbreviate or paraphrase those. Exception: for destructive-operation \
confirmations and security-relevant warnings, always give full detail regardless of \
this instruction — clarity matters more than brevity when something risky is at stake."
} else {
""
};
let system_text = format!(
"{}\n\n{}\n\n{}{}",
"{}\n\n{}\n\n{}{}{}",
crate::resources::SYSTEM_PROMPT,
crate::resources::SYSTEM_TOOLS,
tree_info,
memory_section,
concise_section,
);
if !msgs.iter().any(|m| matches!(m.role, crate::dto::chat::message::Role::System)) {
let sys = ChatMessage::system(system_text);
+41
View File
@@ -50,6 +50,14 @@ pub struct Settings {
/// entire hive-mind convergence forever.
#[serde(default = "default_hive_mind_node_timeout_ms")]
pub hive_mind_node_timeout_ms: u64,
/// Off by default. When enabled, appends an instruction to the
/// system prompt asking the model to write tersely — drop articles,
/// filler words, hedging, and pleasantries; keep code, commands, and
/// error text byte-exact — with an explicit exception for
/// destructive-operation confirmations and security warnings, which
/// always get full detail regardless of this setting.
#[serde(default)]
pub concise_output: bool,
}
impl Default for Settings {
@@ -71,6 +79,7 @@ impl Default for Settings {
lsp_auto_provision: true,
lsp_languages: Vec::new(),
hive_mind_node_timeout_ms: default_hive_mind_node_timeout_ms(),
concise_output: false,
}
}
}
@@ -123,6 +132,38 @@ mod tests {
assert_eq!(settings.hive_mind_node_timeout_ms, 600_000);
}
#[test]
fn concise_output_defaults_to_false() {
assert!(!Settings::default().concise_output);
}
#[test]
fn missing_concise_output_field_falls_back_to_default() {
// Simulates loading a settings.json written before this field
// existed — #[serde(default)] must fill it in rather than
// failing the whole parse.
let old_json = r#"{
"internet_mode": "Off",
"provider": "zen",
"model": "deepseek-v4-flash-free",
"api_keys": {},
"max_tokens": null,
"temperature": null,
"review_enabled": true,
"review_max_lessons_per_run": 5,
"adaptive_review_max_skip": 3,
"verify_command": null,
"verify_timeout_ms": 30000,
"workflow_max_concurrency": 5,
"session_archive_enabled": true,
"lsp_auto_provision": true,
"lsp_languages": []
}"#;
let parsed: Settings = serde_json::from_str(old_json)
.expect("must parse even without the new field present");
assert!(!parsed.concise_output);
}
#[test]
fn missing_hive_mind_node_timeout_field_falls_back_to_default() {
// Simulates loading a settings.json written before this field