feat(config): implement verbosity settings with normal and concise modes (#3052)
* feat(config): implement verbosity settings with normal and concise modes * fix(config): wrap unsafe env calls in tests and fix clippy/fmt errors for CI * perf(config): avoid verbosity prompt allocations
This commit is contained in:
@@ -92,6 +92,12 @@ struct Cli {
|
||||
model: Option<String>,
|
||||
#[arg(long = "output-mode")]
|
||||
output_mode: Option<String>,
|
||||
#[arg(
|
||||
long = "verbosity",
|
||||
value_name = "LEVEL",
|
||||
help = "Controls transcript and output verbosity (normal, concise)"
|
||||
)]
|
||||
verbosity: Option<String>,
|
||||
#[arg(long = "log-level")]
|
||||
log_level: Option<String>,
|
||||
#[arg(long)]
|
||||
@@ -517,6 +523,7 @@ fn run() -> Result<()> {
|
||||
approval_policy: cli.approval_policy.clone(),
|
||||
sandbox_mode: cli.sandbox_mode.clone(),
|
||||
yolo: Some(cli.yolo),
|
||||
verbosity: cli.verbosity.clone(),
|
||||
};
|
||||
let command = cli.command.take();
|
||||
|
||||
@@ -1674,6 +1681,14 @@ fn build_tui_command(
|
||||
passthrough: Vec<String>,
|
||||
) -> Result<Command> {
|
||||
let tui = locate_sibling_tui_binary()?;
|
||||
let mut verbosity = resolved_runtime.verbosity.clone();
|
||||
if verbosity.is_none()
|
||||
&& passthrough
|
||||
.iter()
|
||||
.any(|arg| matches!(arg.as_str(), "exec" | "swebench" | "eval"))
|
||||
{
|
||||
verbosity = Some("concise".to_string());
|
||||
}
|
||||
|
||||
let mut cmd = Command::new(&tui);
|
||||
if let Some(config) = cli.config.as_ref() {
|
||||
@@ -1753,6 +1768,9 @@ fn build_tui_command(
|
||||
if let Some(output_mode) = cli.output_mode.as_ref() {
|
||||
cmd.env("DEEPSEEK_OUTPUT_MODE", output_mode);
|
||||
}
|
||||
if let Some(v) = verbosity.as_ref() {
|
||||
cmd.env("DEEPSEEK_VERBOSITY", v);
|
||||
}
|
||||
if let Some(log_level) = cli.log_level.as_ref() {
|
||||
cmd.env("DEEPSEEK_LOG_LEVEL", log_level);
|
||||
}
|
||||
@@ -3124,6 +3142,7 @@ mod tests {
|
||||
approval_policy: None,
|
||||
sandbox_mode: None,
|
||||
yolo: None,
|
||||
verbosity: None,
|
||||
http_headers: std::collections::BTreeMap::new(),
|
||||
};
|
||||
|
||||
@@ -3182,6 +3201,7 @@ mod tests {
|
||||
approval_policy: None,
|
||||
sandbox_mode: None,
|
||||
yolo: None,
|
||||
verbosity: None,
|
||||
http_headers: std::collections::BTreeMap::new(),
|
||||
};
|
||||
|
||||
@@ -3221,6 +3241,7 @@ mod tests {
|
||||
approval_policy: None,
|
||||
sandbox_mode: None,
|
||||
yolo: None,
|
||||
verbosity: None,
|
||||
http_headers: std::collections::BTreeMap::new(),
|
||||
};
|
||||
|
||||
@@ -3260,6 +3281,7 @@ mod tests {
|
||||
approval_policy: None,
|
||||
sandbox_mode: None,
|
||||
yolo: None,
|
||||
verbosity: None,
|
||||
http_headers: resolved_headers,
|
||||
};
|
||||
|
||||
@@ -3316,6 +3338,7 @@ mod tests {
|
||||
approval_policy: None,
|
||||
sandbox_mode: None,
|
||||
yolo: None,
|
||||
verbosity: None,
|
||||
http_headers: std::collections::BTreeMap::new(),
|
||||
};
|
||||
|
||||
@@ -3381,6 +3404,7 @@ mod tests {
|
||||
approval_policy: None,
|
||||
sandbox_mode: None,
|
||||
yolo: None,
|
||||
verbosity: None,
|
||||
http_headers: std::collections::BTreeMap::new(),
|
||||
};
|
||||
|
||||
@@ -3447,6 +3471,7 @@ mod tests {
|
||||
approval_policy: None,
|
||||
sandbox_mode: None,
|
||||
yolo: None,
|
||||
verbosity: None,
|
||||
http_headers: std::collections::BTreeMap::new(),
|
||||
};
|
||||
|
||||
@@ -3543,6 +3568,7 @@ mod tests {
|
||||
approval_policy: None,
|
||||
sandbox_mode: None,
|
||||
yolo: None,
|
||||
verbosity: None,
|
||||
http_headers: std::collections::BTreeMap::new(),
|
||||
};
|
||||
|
||||
|
||||
@@ -565,6 +565,7 @@ pub struct ConfigToml {
|
||||
pub model: Option<String>,
|
||||
pub auth_mode: Option<String>,
|
||||
pub output_mode: Option<String>,
|
||||
pub verbosity: Option<String>,
|
||||
pub log_level: Option<String>,
|
||||
pub telemetry: Option<bool>,
|
||||
pub approval_policy: Option<String>,
|
||||
@@ -1077,6 +1078,9 @@ impl ConfigToml {
|
||||
if project.output_mode.is_some() {
|
||||
self.output_mode = project.output_mode;
|
||||
}
|
||||
if project.verbosity.is_some() {
|
||||
self.verbosity = project.verbosity;
|
||||
}
|
||||
if project.log_level.is_some() {
|
||||
self.log_level = project.log_level;
|
||||
}
|
||||
@@ -1147,6 +1151,7 @@ impl ConfigToml {
|
||||
"model" => self.model.clone(),
|
||||
"auth.mode" => self.auth_mode.clone(),
|
||||
"output_mode" => self.output_mode.clone(),
|
||||
"verbosity" => self.verbosity.clone(),
|
||||
"log_level" => self.log_level.clone(),
|
||||
"telemetry" => self.telemetry.map(|v| v.to_string()),
|
||||
"approval_policy" => self.approval_policy.clone(),
|
||||
@@ -1295,6 +1300,7 @@ impl ConfigToml {
|
||||
"model" => self.model = Some(value.to_string()),
|
||||
"auth.mode" => self.auth_mode = Some(value.to_string()),
|
||||
"output_mode" => self.output_mode = Some(value.to_string()),
|
||||
"verbosity" => self.verbosity = Some(value.to_string()),
|
||||
"log_level" => self.log_level = Some(value.to_string()),
|
||||
"telemetry" => {
|
||||
self.telemetry = Some(parse_bool(value)?);
|
||||
@@ -1548,6 +1554,7 @@ impl ConfigToml {
|
||||
"model" => self.model = None,
|
||||
"auth.mode" => self.auth_mode = None,
|
||||
"output_mode" => self.output_mode = None,
|
||||
"verbosity" => self.verbosity = None,
|
||||
"log_level" => self.log_level = None,
|
||||
"telemetry" => self.telemetry = None,
|
||||
"approval_policy" => self.approval_policy = None,
|
||||
@@ -1686,6 +1693,9 @@ impl ConfigToml {
|
||||
if let Some(v) = self.output_mode.as_ref() {
|
||||
out.insert("output_mode".to_string(), v.clone());
|
||||
}
|
||||
if let Some(v) = self.verbosity.as_ref() {
|
||||
out.insert("verbosity".to_string(), v.clone());
|
||||
}
|
||||
if let Some(v) = self.log_level.as_ref() {
|
||||
out.insert("log_level".to_string(), v.clone());
|
||||
}
|
||||
@@ -2150,6 +2160,11 @@ impl ConfigToml {
|
||||
.or_else(|| env.sandbox_mode.clone())
|
||||
.or_else(|| self.sandbox_mode.clone());
|
||||
let yolo = cli.yolo.or(env.yolo);
|
||||
let verbosity = cli
|
||||
.verbosity
|
||||
.clone()
|
||||
.or_else(|| env.verbosity.clone())
|
||||
.or_else(|| self.verbosity.clone());
|
||||
|
||||
ResolvedRuntimeOptions {
|
||||
provider,
|
||||
@@ -2165,6 +2180,7 @@ impl ConfigToml {
|
||||
approval_policy,
|
||||
sandbox_mode,
|
||||
yolo,
|
||||
verbosity,
|
||||
http_headers,
|
||||
}
|
||||
}
|
||||
@@ -2770,6 +2786,7 @@ pub struct CliRuntimeOverrides {
|
||||
pub approval_policy: Option<String>,
|
||||
pub sandbox_mode: Option<String>,
|
||||
pub yolo: Option<bool>,
|
||||
pub verbosity: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
@@ -2807,6 +2824,7 @@ pub struct ResolvedRuntimeOptions {
|
||||
pub approval_policy: Option<String>,
|
||||
pub sandbox_mode: Option<String>,
|
||||
pub yolo: Option<bool>,
|
||||
pub verbosity: Option<String>,
|
||||
pub http_headers: BTreeMap<String, String>,
|
||||
}
|
||||
|
||||
@@ -3237,6 +3255,7 @@ struct EnvRuntimeOverrides {
|
||||
approval_policy: Option<String>,
|
||||
sandbox_mode: Option<String>,
|
||||
yolo: Option<bool>,
|
||||
verbosity: Option<String>,
|
||||
http_headers: Option<BTreeMap<String, String>>,
|
||||
deepseek_base_url: Option<String>,
|
||||
nvidia_base_url: Option<String>,
|
||||
@@ -3311,6 +3330,9 @@ impl EnvRuntimeOverrides {
|
||||
arcee_model: std::env::var("ARCEE_MODEL")
|
||||
.ok()
|
||||
.filter(|v| !v.trim().is_empty()),
|
||||
verbosity: std::env::var("CODEWHALE_VERBOSITY")
|
||||
.or_else(|_| std::env::var("DEEPSEEK_VERBOSITY"))
|
||||
.ok(),
|
||||
output_mode: std::env::var("DEEPSEEK_OUTPUT_MODE").ok(),
|
||||
auth_mode: std::env::var("DEEPSEEK_AUTH_MODE").ok(),
|
||||
log_level: std::env::var("DEEPSEEK_LOG_LEVEL").ok(),
|
||||
@@ -6761,4 +6783,36 @@ unknown_policy = "surprise"
|
||||
|
||||
assert!(err.to_string().contains("unknown_policy"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_verbosity_resolution() {
|
||||
let _lock = env_lock();
|
||||
// Test TOML parsing
|
||||
let toml_str = r#"
|
||||
verbosity = "concise"
|
||||
"#;
|
||||
let config: ConfigToml = toml::from_str(toml_str).unwrap();
|
||||
assert_eq!(config.verbosity, Some("concise".to_string()));
|
||||
|
||||
// Test Env overrides
|
||||
let _env = EnvGuard::without_deepseek_runtime_overrides();
|
||||
unsafe {
|
||||
std::env::set_var("CODEWHALE_VERBOSITY", "normal");
|
||||
}
|
||||
let env_overrides = EnvRuntimeOverrides::load();
|
||||
assert_eq!(env_overrides.verbosity, Some("normal".to_string()));
|
||||
unsafe {
|
||||
std::env::remove_var("CODEWHALE_VERBOSITY");
|
||||
}
|
||||
|
||||
// Test fallback to DEEPSEEK_VERBOSITY
|
||||
unsafe {
|
||||
std::env::set_var("DEEPSEEK_VERBOSITY", "concise");
|
||||
}
|
||||
let env_overrides = EnvRuntimeOverrides::load();
|
||||
assert_eq!(env_overrides.verbosity, Some("concise".to_string()));
|
||||
unsafe {
|
||||
std::env::remove_var("DEEPSEEK_VERBOSITY");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1638,6 +1638,7 @@ pub struct Config {
|
||||
pub approval_policy: Option<String>,
|
||||
pub sandbox_mode: Option<String>,
|
||||
pub yolo: Option<bool>,
|
||||
pub verbosity: Option<String>,
|
||||
/// External sandbox backend: `"none"` or `"opensandbox"`.
|
||||
/// When set, exec_shell routes commands through the backend's HTTP API
|
||||
/// instead of spawning a local process.
|
||||
@@ -2217,6 +2218,12 @@ impl Config {
|
||||
);
|
||||
}
|
||||
}
|
||||
if let Some(v) = self.verbosity.as_deref() {
|
||||
let normalized = v.trim().to_ascii_lowercase();
|
||||
if !matches!(normalized.as_str(), "normal" | "concise") {
|
||||
anyhow::bail!("Invalid verbosity '{v}': expected normal or concise.");
|
||||
}
|
||||
}
|
||||
if let Some(mode) = self.sandbox_mode.as_deref() {
|
||||
let normalized = mode.trim().to_ascii_lowercase();
|
||||
if !matches!(
|
||||
@@ -4058,6 +4065,11 @@ fn apply_env_overrides(config: &mut Config) {
|
||||
if let Ok(value) = std::env::var("DEEPSEEK_YOLO") {
|
||||
config.yolo = Some(value == "1" || value.eq_ignore_ascii_case("true"));
|
||||
}
|
||||
if let Ok(value) =
|
||||
std::env::var("CODEWHALE_VERBOSITY").or_else(|_| std::env::var("DEEPSEEK_VERBOSITY"))
|
||||
{
|
||||
config.verbosity = Some(value);
|
||||
}
|
||||
if let Ok(value) = std::env::var("DEEPSEEK_SANDBOX_BACKEND") {
|
||||
config.sandbox_backend = Some(value);
|
||||
}
|
||||
@@ -4660,6 +4672,7 @@ fn merge_config(base: Config, override_cfg: Config) -> Config {
|
||||
allow_shell: override_cfg.allow_shell.or(base.allow_shell),
|
||||
prompt_suggestion: override_cfg.prompt_suggestion.or(base.prompt_suggestion),
|
||||
yolo: override_cfg.yolo.or(base.yolo),
|
||||
verbosity: override_cfg.verbosity.or(base.verbosity),
|
||||
approval_policy: override_cfg.approval_policy.or(base.approval_policy),
|
||||
sandbox_mode: override_cfg.sandbox_mode.or(base.sandbox_mode),
|
||||
sandbox_backend: override_cfg.sandbox_backend.or(base.sandbox_backend),
|
||||
@@ -4790,7 +4803,12 @@ fn warn_on_misplaced_top_level_keys(raw: &str) -> Option<String> {
|
||||
// Sections CodeWhale does not recognize but users nest settings under.
|
||||
const UNKNOWN_SECTIONS: &[&str] = &["general", "sandbox"];
|
||||
// Keys that are only ever read from the top level of the config.
|
||||
const TOP_LEVEL_KEYS: &[&str] = &["allow_shell", "sandbox_mode", "approval_policy"];
|
||||
const TOP_LEVEL_KEYS: &[&str] = &[
|
||||
"allow_shell",
|
||||
"sandbox_mode",
|
||||
"approval_policy",
|
||||
"verbosity",
|
||||
];
|
||||
|
||||
let mut hits: Vec<String> = Vec::new();
|
||||
for section in UNKNOWN_SECTIONS {
|
||||
|
||||
@@ -274,6 +274,7 @@ pub struct EngineConfig {
|
||||
/// Whether user-visible transcript rendering shows thinking blocks.
|
||||
/// Prompt assembly uses this to avoid localizing hidden reasoning.
|
||||
pub show_thinking: bool,
|
||||
pub verbosity: Option<String>,
|
||||
/// Maximum number of assistant steps before stopping.
|
||||
pub max_steps: u32,
|
||||
/// Maximum number of concurrently active subagents.
|
||||
@@ -430,6 +431,7 @@ impl Default for EngineConfig {
|
||||
),
|
||||
tools_always_load: HashSet::new(),
|
||||
prefer_bwrap: false,
|
||||
verbosity: None,
|
||||
tools: None,
|
||||
}
|
||||
}
|
||||
@@ -700,6 +702,7 @@ impl Engine {
|
||||
translation_enabled: config.translation_enabled,
|
||||
model_id: &config.model,
|
||||
show_thinking: config.show_thinking,
|
||||
verbosity: config.verbosity.as_deref(),
|
||||
},
|
||||
);
|
||||
let stable_prompt = Some(system_prompt);
|
||||
@@ -1104,6 +1107,7 @@ impl Engine {
|
||||
show_thinking,
|
||||
allowed_tools,
|
||||
hook_executor,
|
||||
verbosity,
|
||||
} => {
|
||||
self.handle_send_message(
|
||||
content,
|
||||
@@ -1121,6 +1125,7 @@ impl Engine {
|
||||
show_thinking,
|
||||
allowed_tools,
|
||||
hook_executor,
|
||||
verbosity,
|
||||
)
|
||||
.await;
|
||||
}
|
||||
@@ -1378,6 +1383,7 @@ impl Engine {
|
||||
self.config.show_thinking,
|
||||
self.config.allowed_tools.clone(),
|
||||
self.config.hook_executor.clone(),
|
||||
self.config.verbosity.clone(),
|
||||
)
|
||||
.await;
|
||||
}
|
||||
@@ -1532,6 +1538,7 @@ impl Engine {
|
||||
show_thinking: bool,
|
||||
allowed_tools: Option<Vec<String>>,
|
||||
hook_executor: Option<std::sync::Arc<crate::hooks::HookExecutor>>,
|
||||
verbosity: Option<String>,
|
||||
) {
|
||||
// Reset cancel token for fresh turn (in case previous was cancelled)
|
||||
self.reset_cancel_token();
|
||||
@@ -1660,6 +1667,7 @@ impl Engine {
|
||||
self.config.trust_mode = trust_mode;
|
||||
self.config.translation_enabled = translation_enabled;
|
||||
self.config.show_thinking = show_thinking;
|
||||
self.config.verbosity = verbosity;
|
||||
|
||||
// Refresh stable prompt context. Current mode is carried by the
|
||||
// request-time runtime prompt projection.
|
||||
@@ -2448,6 +2456,7 @@ impl Engine {
|
||||
translation_enabled: self.config.translation_enabled,
|
||||
model_id: &self.config.model,
|
||||
show_thinking: self.config.show_thinking,
|
||||
verbosity: self.config.verbosity.as_deref(),
|
||||
},
|
||||
);
|
||||
let mut stable_prompt =
|
||||
|
||||
@@ -41,6 +41,7 @@ pub enum Op {
|
||||
/// Hook executor for control-plane hooks.
|
||||
/// `ToolCallBefore` hooks may deny a tool call with exit code 2.
|
||||
hook_executor: Option<std::sync::Arc<crate::hooks::HookExecutor>>,
|
||||
verbosity: Option<String>,
|
||||
},
|
||||
|
||||
/// Execute a user-submitted composer shell command (`! <command>`) without
|
||||
|
||||
@@ -5903,6 +5903,7 @@ async fn run_exec_agent(
|
||||
search_base_url: config.search.as_ref().and_then(|s| s.base_url.clone()),
|
||||
tools_always_load: config.tools_always_load(),
|
||||
tools: config.tools.clone(),
|
||||
verbosity: config.verbosity.clone(),
|
||||
};
|
||||
|
||||
let engine_handle = spawn_engine(engine_config, config);
|
||||
@@ -5969,6 +5970,7 @@ async fn run_exec_agent(
|
||||
.and_then(crate::tui::approval::ApprovalMode::from_config_value)
|
||||
.unwrap_or_default()
|
||||
},
|
||||
verbosity: config.verbosity.clone(),
|
||||
})
|
||||
.await?;
|
||||
|
||||
|
||||
@@ -38,6 +38,7 @@ pub struct PromptSessionContext<'a> {
|
||||
/// When false, the prompt should not spend localization pressure on
|
||||
/// `reasoning_content` the user will never see.
|
||||
pub show_thinking: bool,
|
||||
pub verbosity: Option<&'a str>,
|
||||
}
|
||||
|
||||
impl Default for PromptSessionContext<'_> {
|
||||
@@ -50,6 +51,7 @@ impl Default for PromptSessionContext<'_> {
|
||||
translation_enabled: false,
|
||||
model_id: "codewhale",
|
||||
show_thinking: true,
|
||||
verbosity: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -92,6 +94,18 @@ so any English prose in your response will block their decision-making."
|
||||
)
|
||||
}
|
||||
|
||||
fn concise_output_discipline_instruction() -> &'static str {
|
||||
"\
|
||||
## Concise Output Discipline
|
||||
|
||||
To minimize token usage and optimize speed:
|
||||
- Output only direct, actionable code, technical steps, or final answers.
|
||||
- Eliminate all conversational filler, fluff, introductions, transitions, or summarizing conclusions.
|
||||
- Do NOT explain what you are about to do or what you have just completed.
|
||||
- Do NOT provide conversational status updates before or after running tools.
|
||||
- Keep explanations and comments extremely brief and technical, explaining only non-obvious reasoning."
|
||||
}
|
||||
|
||||
fn translation_target_language_for_tag(locale_tag: &str) -> &'static str {
|
||||
let normalized = locale_tag.trim().to_ascii_lowercase();
|
||||
if normalized.starts_with("ja") {
|
||||
@@ -1035,6 +1049,7 @@ pub fn system_prompt_for_mode_with_context_and_skills(
|
||||
translation_enabled: false,
|
||||
model_id: "codewhale",
|
||||
show_thinking: true,
|
||||
verbosity: None,
|
||||
},
|
||||
)
|
||||
}
|
||||
@@ -1117,6 +1132,13 @@ pub fn system_prompt_for_mode_with_context_skills_session_and_approval(
|
||||
);
|
||||
}
|
||||
|
||||
if session_context.verbosity == Some("concise") {
|
||||
full_prompt = format!(
|
||||
"{full_prompt}\n\n{}",
|
||||
concise_output_discipline_instruction()
|
||||
);
|
||||
}
|
||||
|
||||
// 3. Skills block. #432: walks every candidate workspace
|
||||
// skills directory (`.agents/skills`, `skills`,
|
||||
// `.opencode/skills`, `.claude/skills`, `.cursor/skills`) plus global
|
||||
@@ -1909,6 +1931,7 @@ mod tests {
|
||||
translation_enabled: false,
|
||||
model_id: "codewhale",
|
||||
show_thinking: true,
|
||||
verbosity: None,
|
||||
},
|
||||
) {
|
||||
SystemPrompt::Text(text) => text,
|
||||
@@ -1978,6 +2001,7 @@ mod tests {
|
||||
translation_enabled: false,
|
||||
model_id: "codewhale",
|
||||
show_thinking: true,
|
||||
verbosity: None,
|
||||
},
|
||||
) {
|
||||
SystemPrompt::Text(text) => text,
|
||||
@@ -2020,6 +2044,7 @@ mod tests {
|
||||
translation_enabled: false,
|
||||
model_id: "codewhale",
|
||||
show_thinking: false,
|
||||
verbosity: None,
|
||||
},
|
||||
) {
|
||||
SystemPrompt::Text(text) => text,
|
||||
@@ -2072,6 +2097,7 @@ mod tests {
|
||||
translation_enabled: false,
|
||||
model_id: "codewhale",
|
||||
show_thinking: true,
|
||||
verbosity: None,
|
||||
},
|
||||
) {
|
||||
SystemPrompt::Text(text) => text,
|
||||
@@ -2175,6 +2201,7 @@ mod tests {
|
||||
translation_enabled: false,
|
||||
model_id: "codewhale",
|
||||
show_thinking: true,
|
||||
verbosity: None,
|
||||
},
|
||||
) {
|
||||
SystemPrompt::Text(text) => text,
|
||||
@@ -2211,6 +2238,7 @@ mod tests {
|
||||
translation_enabled: false,
|
||||
model_id: "codewhale",
|
||||
show_thinking: true,
|
||||
verbosity: None,
|
||||
},
|
||||
) {
|
||||
SystemPrompt::Text(text) => text,
|
||||
@@ -2239,6 +2267,7 @@ mod tests {
|
||||
translation_enabled: false,
|
||||
model_id: "codewhale",
|
||||
show_thinking: true,
|
||||
verbosity: None,
|
||||
},
|
||||
) {
|
||||
SystemPrompt::Text(text) => text,
|
||||
@@ -2296,6 +2325,7 @@ mod tests {
|
||||
translation_enabled: false,
|
||||
model_id: "codewhale",
|
||||
show_thinking: true,
|
||||
verbosity: None,
|
||||
},
|
||||
) {
|
||||
SystemPrompt::Text(text) => text,
|
||||
@@ -2324,6 +2354,7 @@ mod tests {
|
||||
translation_enabled: false,
|
||||
model_id: "codewhale",
|
||||
show_thinking: true,
|
||||
verbosity: None,
|
||||
},
|
||||
) {
|
||||
SystemPrompt::Text(text) => text,
|
||||
@@ -2530,6 +2561,7 @@ mod tests {
|
||||
translation_enabled: false,
|
||||
model_id: "codewhale",
|
||||
show_thinking: true,
|
||||
verbosity: None,
|
||||
},
|
||||
) {
|
||||
SystemPrompt::Text(text) => text,
|
||||
@@ -2564,6 +2596,7 @@ mod tests {
|
||||
translation_enabled: false,
|
||||
model_id: "codewhale",
|
||||
show_thinking: true,
|
||||
verbosity: None,
|
||||
},
|
||||
) {
|
||||
SystemPrompt::Text(text) => text,
|
||||
@@ -3088,4 +3121,34 @@ mod tests {
|
||||
"instructions block must annotate its source path"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn verbosity_concise_appends_discipline_block() {
|
||||
let tmp = tempdir().expect("tempdir");
|
||||
let workspace = tmp.path();
|
||||
let prompt = match super::system_prompt_for_mode_with_context_skills_session_and_approval(
|
||||
workspace,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
PromptSessionContext {
|
||||
user_memory_block: None,
|
||||
goal_objective: None,
|
||||
project_context_pack_enabled: false,
|
||||
locale_tag: "en",
|
||||
translation_enabled: false,
|
||||
model_id: "codewhale",
|
||||
show_thinking: true,
|
||||
verbosity: Some("concise"),
|
||||
},
|
||||
) {
|
||||
SystemPrompt::Text(text) => text,
|
||||
SystemPrompt::Blocks(_) => panic!("expected text system prompt"),
|
||||
};
|
||||
|
||||
assert!(
|
||||
prompt.contains("## Concise Output Discipline"),
|
||||
"Concise Output Discipline should be appended"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,3 +29,5 @@ Long sessions accumulate context. To stay fast:
|
||||
- Suggest `/compact` or Ctrl+L when context nears 60% during sustained work — the compaction relay preserves open blockers
|
||||
- Use `note` for decisions you'll need across compaction boundaries
|
||||
- A 3-turn session that fans out to sub-agents finishes faster AND stays responsive longer than a 15-turn sequential grind
|
||||
|
||||
Do NOT explain, announce, or mention to the user that you are running in Agent mode or how the approval policy works. Act silently on this mode instruction.
|
||||
|
||||
@@ -15,3 +15,5 @@ can't change it. Shell and code execution are unavailable.
|
||||
Use this mode to build a thorough plan. Spawn read-only sub-agents for parallel investigation.
|
||||
After `update_plan` presents the plan, wait for the user's next action instead of continuing to
|
||||
tool around in Plan mode.
|
||||
|
||||
Do NOT explain, announce, or mention to the user that you are running in Plan mode, or describe the transition. Act silently on this mode instruction.
|
||||
|
||||
@@ -9,3 +9,5 @@ Even with auto-approval, use `checklist_write` for work that has several concret
|
||||
visible and trackable in the sidebar. Keep simple commands and focused edits direct.
|
||||
For multi-step initiatives, keep `checklist_write` current. Add `update_plan` only when a high-level strategy
|
||||
would help and do not duplicate the checklist there.
|
||||
|
||||
Do NOT announce or mention to the user that you are running in YOLO mode. Act silently on this mode instruction.
|
||||
|
||||
@@ -1708,6 +1708,7 @@ impl RuntimeThreadManager {
|
||||
} else {
|
||||
crate::tui::approval::ApprovalMode::Suggest
|
||||
},
|
||||
verbosity: self.config.verbosity.clone(),
|
||||
})
|
||||
.await
|
||||
.map_err(|e| anyhow!("Failed to start turn: {e}"))?;
|
||||
@@ -2093,6 +2094,7 @@ impl RuntimeThreadManager {
|
||||
search_base_url: self.config.search.as_ref().and_then(|s| s.base_url.clone()),
|
||||
tools_always_load: self.config.tools_always_load(),
|
||||
tools: self.config.tools.clone(),
|
||||
verbosity: self.config.verbosity.clone(),
|
||||
};
|
||||
|
||||
let engine = spawn_engine(engine_cfg, &self.config);
|
||||
|
||||
@@ -1438,6 +1438,7 @@ pub struct App {
|
||||
pub compact_threshold: usize,
|
||||
pub max_input_history: usize,
|
||||
pub allow_shell: bool,
|
||||
pub verbosity: Option<String>,
|
||||
pub max_subagents: usize,
|
||||
/// Per-SSE-chunk idle timeout for streamed turns, in seconds.
|
||||
pub stream_chunk_timeout_secs: u64,
|
||||
@@ -2203,6 +2204,7 @@ impl App {
|
||||
compact_threshold,
|
||||
max_input_history,
|
||||
allow_shell,
|
||||
verbosity: config.verbosity.clone(),
|
||||
max_subagents,
|
||||
stream_chunk_timeout_secs: config.stream_chunk_timeout_secs(),
|
||||
subagent_cache: Vec::new(),
|
||||
|
||||
@@ -873,6 +873,7 @@ fn build_engine_config(app: &App, config: &Config) -> EngineConfig {
|
||||
project_context_pack_enabled: config.project_context_pack_enabled(),
|
||||
translation_enabled: app.translation_enabled,
|
||||
show_thinking: app.show_thinking,
|
||||
verbosity: app.verbosity.clone(),
|
||||
// Effectively unlimited. V4 has a 1M context window and the user
|
||||
// wants the model running until it's actually done. The previous cap
|
||||
// of 100 hit the ceiling on long multi-step plans (wide refactors,
|
||||
@@ -5516,6 +5517,7 @@ async fn dispatch_user_message(
|
||||
translation_enabled: app.translation_enabled,
|
||||
model_id: &app.model,
|
||||
show_thinking: app.show_thinking,
|
||||
verbosity: app.verbosity.as_deref(),
|
||||
},
|
||||
),
|
||||
);
|
||||
@@ -5618,6 +5620,7 @@ async fn dispatch_user_message(
|
||||
show_thinking: app.show_thinking,
|
||||
allowed_tools: app.active_allowed_tools.clone(),
|
||||
hook_executor: app.runtime_services.hook_executor.clone(),
|
||||
verbosity: app.verbosity.clone(),
|
||||
})
|
||||
.await
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user