Synced from monorepo

Changes:
- Stop hooks for session lifecycle
- Add x.ai/session/state and x.ai/session/import ACP methods
- Deny-and-continue for auto-mode classifier blocks with denial limits
- Drop codebase-upload from dhat soak test
- scheduler_create upsert via task_id; retire one-shot tasks
- Clipboard: copy file fallback + honest toasts for SSH/Apple Terminal
- Polarity-safe syntax colors in minimal mode
- Auto mode classifies unvetted env prefixes instead of hard-prompting
- Add GROK_CLIPBOARD_NO_OSC52 kill switch to force OSC 52 off
This commit is contained in:
grokkybara[bot] 2026-07-19 18:40:33 +01:00
commit ba76b0a683
143 changed files with 9465 additions and 3419 deletions

View file

@ -193,6 +193,142 @@ mod login_shell_capture_tests {
}
}
const ENV_SCHEDULER_BACKGROUND_LOOPS: &str = "GROK_SCHEDULER_BACKGROUND_LOOPS";
fn scheduler_background_loops_from_toml(v: Option<&TomlValue>) -> Option<bool> {
v?.get("scheduler")?.get("background_loops")?.as_bool()
}
/// Resolve whether scheduled task fires run in background loop subagents.
///
/// Precedence: requirements > env (`GROK_SCHEDULER_BACKGROUND_LOOPS`) > user
/// `config.toml` `[scheduler] background_loops` > managed layers > remote
/// settings > default `true`.
pub fn resolve_scheduler_background_loops(remote: Option<bool>) -> bool {
let requirements = crate::config::load_merged_requirements();
let layers = match crate::config::ConfigLayers::load() {
Ok(l) => Some(l),
Err(e) => {
tracing::warn!(error = %e, "scheduler_background_loops: failed to load config layers");
None
}
};
resolve_scheduler_background_loops_tiers(
requirements.as_ref(),
layers.as_ref().map(|l| &l.user),
layers.as_ref().map(|l| &l.managed),
layers.as_ref().map(|l| &l.system_managed),
remote,
)
}
fn resolve_scheduler_background_loops_tiers(
requirements: Option<&TomlValue>,
user: Option<&TomlValue>,
managed: Option<&TomlValue>,
system_managed: Option<&TomlValue>,
remote: Option<bool>,
) -> bool {
use crate::agent::config::BoolFlag;
BoolFlag::env(ENV_SCHEDULER_BACKGROUND_LOOPS)
.requirement(scheduler_background_loops_from_toml(requirements))
.config(scheduler_background_loops_from_toml(user))
.managed(
scheduler_background_loops_from_toml(managed)
.or_else(|| scheduler_background_loops_from_toml(system_managed)),
)
.feature_flag(remote)
.default(true)
.resolve()
.value
}
#[cfg(test)]
mod scheduler_background_loops_tests {
use super::{ENV_SCHEDULER_BACKGROUND_LOOPS, resolve_scheduler_background_loops_tiers};
use toml::Value as TomlValue;
static ENV_LOCK: std::sync::Mutex<()> = std::sync::Mutex::new(());
fn guard() -> std::sync::MutexGuard<'static, ()> {
let g = ENV_LOCK.lock().unwrap_or_else(|p| p.into_inner());
unsafe { std::env::remove_var(ENV_SCHEDULER_BACKGROUND_LOOPS) };
g
}
fn cfg(enabled: bool) -> TomlValue {
toml::from_str(&format!("[scheduler]\nbackground_loops = {enabled}\n")).unwrap()
}
#[test]
fn defaults_on() {
let _g = guard();
assert!(resolve_scheduler_background_loops_tiers(
None, None, None, None, None
));
}
#[test]
fn remote_flag_can_disable() {
let _g = guard();
assert!(!resolve_scheduler_background_loops_tiers(
None,
None,
None,
None,
Some(false)
));
}
#[test]
fn user_config_beats_remote() {
let _g = guard();
assert!(resolve_scheduler_background_loops_tiers(
None,
Some(&cfg(true)),
None,
None,
Some(false)
));
assert!(!resolve_scheduler_background_loops_tiers(
None,
Some(&cfg(false)),
None,
None,
Some(true)
));
}
#[test]
fn env_beats_config_and_remote() {
let _g = guard();
unsafe { std::env::set_var(ENV_SCHEDULER_BACKGROUND_LOOPS, "0") };
let off = resolve_scheduler_background_loops_tiers(
None,
Some(&cfg(true)),
None,
None,
Some(true),
);
unsafe { std::env::remove_var(ENV_SCHEDULER_BACKGROUND_LOOPS) };
assert!(!off);
}
#[test]
fn requirements_win_outright() {
let _g = guard();
unsafe { std::env::set_var(ENV_SCHEDULER_BACKGROUND_LOOPS, "1") };
let off = resolve_scheduler_background_loops_tiers(
Some(&cfg(false)),
Some(&cfg(true)),
None,
None,
Some(true),
);
unsafe { std::env::remove_var(ENV_SCHEDULER_BACKGROUND_LOOPS) };
assert!(!off);
}
}
/// Env override for `[toolset.ask_user_question] timeout_enabled` (parsed by
/// the shared [`xai_grok_config::env_bool`] via `BoolFlag`). The secs env var
/// lives in the tools crate (`RESPONSE_TIMEOUT_ENV`), parsed once there.