M2 audit: excise managed connectors and xAI media-gen tools
Managed connectors (grok.com MCP admin) removed root-and-branch: - The managed-MCP fetch/injection pipeline is gone, including the whole kigi-shell-session-support crate (managed-config fetch client, gateway tool catalog + dispatch, header injection, refresh task), reactive managed re-auth, mcp_doctor's grok.com-source discovery, and the [managed_mcps] config surface. - TUI: the 'Managed by grok.com' section, connectors URL/deep-link, Action::OpenManagedConnectors, and session_team_id are gone. Local MCP management (list/toggle/add/remove/auth/tools) is fully intact. - Kept as LOCAL policy: managed-settings.json MCP allow/deny enforcement, the multi-source local MCP merge, folder-trust gating. PluginOrigin Project/User labels kept (they tag locally discovered plugin dirs). imagine/media-gen tools (xAI image/video generation) removed: - image_gen, image_edit, video_gen, image_to_video, reference_to_video implementations, registrations, ToolKind/ToolInput/Output variants (serde-safe), config plumbing end to end, ZDR video machinery, /imagine + /imagine-video commands and guidance text, the bundled imagine skill (added to legacy cleanup so user installs delete it), and the media-gen render path. - Kept: image INPUT (paste/attach, [Image #N] meta, pdf/image fetch, clipboard wrap), generic media-ref rendering, and the generic tool 401-retry machinery (tests renamed, assertions unweakened). - deploy_app stays: it is a permanently-disabled local stub deploying nowhere. 121 files changed, 8 deleted. Gates: workspace check/clippy 0/0, fmt, deny ok; suites green (tools 2554, shell 4862, tui 6608, workspace 1042). Remaining grok.com strings live only in the auth-method ids and changelog archives (§9/M3 sweep).
This commit is contained in:
@@ -474,62 +474,6 @@ impl SubagentsConfig {
|
||||
result
|
||||
}
|
||||
}
|
||||
/// Managed MCP connector fetching config (`[managed_mcps]` in config.toml).
|
||||
///
|
||||
/// See [`Self::resolve`] for full priority chain.
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Deserialize)]
|
||||
#[serde(default)]
|
||||
pub struct ManagedMcpsConfig {
|
||||
pub enabled: bool,
|
||||
pub gateway_tools_enabled: bool,
|
||||
}
|
||||
impl Default for ManagedMcpsConfig {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
enabled: true,
|
||||
gateway_tools_enabled: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
impl ManagedMcpsConfig {
|
||||
/// Priority: env var > TOML > remote > default (enabled interactive, disabled headless).
|
||||
pub fn resolve(
|
||||
config: &toml::Value,
|
||||
remote: Option<&crate::util::config::RemoteSettings>,
|
||||
is_headless: bool,
|
||||
) -> Self {
|
||||
let mut result: Self = config
|
||||
.get("managed_mcps")
|
||||
.and_then(|v| v.clone().try_into().ok())
|
||||
.unwrap_or(Self {
|
||||
enabled: !is_headless,
|
||||
gateway_tools_enabled: false,
|
||||
});
|
||||
let managed_mcps_table = config.get("managed_mcps").and_then(|v| v.as_table());
|
||||
let has_local_enabled = managed_mcps_table.is_some_and(|t| t.contains_key("enabled"));
|
||||
let resolved = crate::agent::config::resolve_enabled(
|
||||
None,
|
||||
"KIGI_MANAGED_MCPS_ENABLED",
|
||||
result.enabled,
|
||||
has_local_enabled,
|
||||
remote.and_then(|r| r.managed_mcps_enabled),
|
||||
!is_headless,
|
||||
);
|
||||
result.enabled = resolved.value;
|
||||
let has_local_gateway_tools =
|
||||
managed_mcps_table.is_some_and(|t| t.contains_key("gateway_tools_enabled"));
|
||||
let gateway_resolved = crate::agent::config::resolve_enabled(
|
||||
None,
|
||||
"KIGI_MANAGED_MCP_GATEWAY_TOOLS_ENABLED",
|
||||
result.gateway_tools_enabled,
|
||||
has_local_gateway_tools,
|
||||
remote.and_then(|r| r.managed_mcp_gateway_tools_enabled),
|
||||
false,
|
||||
);
|
||||
result.gateway_tools_enabled = result.enabled && gateway_resolved.value;
|
||||
result
|
||||
}
|
||||
}
|
||||
/// Auxiliary model overrides under `[models]`.
|
||||
#[derive(Debug, Clone, Default, PartialEq, Eq, Deserialize)]
|
||||
#[serde(default)]
|
||||
@@ -656,8 +600,7 @@ impl ModelOverrideConfig {
|
||||
///
|
||||
/// ```toml
|
||||
/// [tools]
|
||||
/// disable_zdr_incompatible_tools = true
|
||||
/// # [tools.zdr_video_output_s3] — see ZdrVideoOutputS3Config
|
||||
/// respect_gitignore = true
|
||||
/// ```
|
||||
#[derive(Debug, Clone, Default, PartialEq, Eq, Deserialize)]
|
||||
#[serde(default)]
|
||||
@@ -665,29 +608,12 @@ pub struct ToolsConfig {
|
||||
/// When `true`, all tools (including `read_file`) filter gitignored
|
||||
/// files. When `false` (default), each tool picks its own default.
|
||||
pub respect_gitignore: bool,
|
||||
/// Drop tools whose xAI API requires server-side artifact storage
|
||||
/// (currently just `video_gen`). Intended for ZDR-bound teams via
|
||||
/// `~/.kigi/managed_config.toml`. Defaults to `false`.
|
||||
pub disable_zdr_incompatible_tools: bool,
|
||||
/// Optional S3 bucket config for ZDR video output. When present (and
|
||||
/// valid), video tools presign an upload URL and pass it to the API so
|
||||
/// the generated video lands in a team-owned bucket instead of being
|
||||
/// downloaded locally. Only effective when `disable_zdr_incompatible_tools`
|
||||
/// is `true`. Populated from `[tools.zdr_video_output_s3]` in config.
|
||||
pub zdr_video_output_s3:
|
||||
Option<kigi_tools::implementations::grok_build::video_gen::ZdrVideoOutputS3Config>,
|
||||
}
|
||||
impl ToolsConfig {
|
||||
/// Resolve the final tools config, in priority order:
|
||||
/// 1. Env vars `KIGI_RESPECT_GITIGNORE` and
|
||||
/// `KIGI_DISABLE_ZDR_INCOMPATIBLE_TOOLS` (`0`/`false` off,
|
||||
/// `1`/`true` on).
|
||||
/// 1. Env var `KIGI_RESPECT_GITIGNORE` (`0`/`false` off, `1`/`true` on).
|
||||
/// 2. `[tools]` block from the merged effective config.
|
||||
/// 3. Defaults (both `false`).
|
||||
///
|
||||
/// Fields are read individually so a malformed
|
||||
/// `[tools.zdr_video_output_s3]` cannot wipe `disable_zdr_incompatible_tools`
|
||||
/// (or any other tools flag) via whole-table deserialize failure.
|
||||
/// 3. Default (`false`).
|
||||
pub fn resolve(config: &toml::Value) -> Self {
|
||||
let tools = config.get("tools");
|
||||
let mut result = Self {
|
||||
@@ -695,33 +621,6 @@ impl ToolsConfig {
|
||||
.and_then(|t| t.get("respect_gitignore"))
|
||||
.and_then(|v| v.as_bool())
|
||||
.unwrap_or(false),
|
||||
disable_zdr_incompatible_tools: tools
|
||||
.and_then(|t| t.get("disable_zdr_incompatible_tools"))
|
||||
.and_then(|v| v.as_bool())
|
||||
.unwrap_or(false),
|
||||
zdr_video_output_s3: tools
|
||||
.and_then(|t| t.get("zdr_video_output_s3"))
|
||||
.and_then(|s3_val| match s3_val
|
||||
.clone()
|
||||
.try_into::<
|
||||
kigi_tools::implementations::grok_build::video_gen::ZdrVideoOutputS3Config,
|
||||
>()
|
||||
{
|
||||
Ok(cfg) if cfg.is_valid() => Some(cfg),
|
||||
Ok(_) => {
|
||||
tracing::warn!(
|
||||
"tools.zdr_video_output_s3 is present but incomplete; ignoring ZDR video output config"
|
||||
);
|
||||
None
|
||||
}
|
||||
Err(e) => {
|
||||
tracing::warn!(
|
||||
error = % e,
|
||||
"tools.zdr_video_output_s3 failed to parse; ignoring ZDR video output config"
|
||||
);
|
||||
None
|
||||
}
|
||||
}),
|
||||
};
|
||||
match std::env::var("KIGI_RESPECT_GITIGNORE").as_deref() {
|
||||
Ok("0") | Ok("false") => {
|
||||
@@ -732,15 +631,6 @@ impl ToolsConfig {
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
match std::env::var("KIGI_DISABLE_ZDR_INCOMPATIBLE_TOOLS").as_deref() {
|
||||
Ok("0") | Ok("false") => {
|
||||
result.disable_zdr_incompatible_tools = false;
|
||||
}
|
||||
Ok("1") | Ok("true") => {
|
||||
result.disable_zdr_incompatible_tools = true;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
result
|
||||
}
|
||||
}
|
||||
@@ -1009,9 +899,6 @@ fn apply_requirements_inner(
|
||||
pin_feature!(tool_search);
|
||||
pin_feature!(web_fetch);
|
||||
pin_feature!(ask_user_question);
|
||||
pin_requirement_only!(image_gen);
|
||||
pin_requirement_only!(image_edit);
|
||||
pin_feature!(video_gen);
|
||||
pin_feature!(write_file);
|
||||
pin_feature!(voice_mode);
|
||||
pin_requirement_only!(remote_fetch);
|
||||
@@ -1020,7 +907,6 @@ fn apply_requirements_inner(
|
||||
enforce_opt!("cli", "show_tips", config.cli.show_tips);
|
||||
enforce_val!("memory", "enabled", config.memory.enabled);
|
||||
enforce_val!("subagents", "enabled", config.subagents.enabled);
|
||||
enforce_val!("managed_mcps", "enabled", config.managed_mcps.enabled);
|
||||
if let Some(val) = req_bool(req, "tools", "respect_gitignore") {
|
||||
config
|
||||
.requirements
|
||||
|
||||
@@ -1201,154 +1201,6 @@ fn subagents_config_is_subagent_enabled_false_when_toggled_off() {
|
||||
);
|
||||
assert!(sa.is_subagent_enabled("explore"), "explore = true should return enabled");
|
||||
}
|
||||
fn with_managed_mcp_env<T>(
|
||||
managed_mcps: Option<&str>,
|
||||
gateway_tools: Option<&str>,
|
||||
f: impl FnOnce() -> T,
|
||||
) -> T {
|
||||
static LOCK: std::sync::Mutex<()> = std::sync::Mutex::new(());
|
||||
let _guard = LOCK.lock().unwrap_or_else(|e| e.into_inner());
|
||||
with_env_var_opt(
|
||||
"KIGI_MANAGED_MCPS_ENABLED",
|
||||
managed_mcps,
|
||||
|| with_env_var_opt("KIGI_MANAGED_MCP_GATEWAY_TOOLS_ENABLED", gateway_tools, f),
|
||||
)
|
||||
}
|
||||
#[test]
|
||||
#[serial_test::serial]
|
||||
fn managed_mcps_interactive_default_enabled() {
|
||||
with_managed_mcp_env(
|
||||
None,
|
||||
None,
|
||||
|| {
|
||||
let empty = toml::Value::Table(toml::map::Map::new());
|
||||
let cfg = ManagedMcpsConfig::resolve(&empty, None, false);
|
||||
assert!(cfg.enabled);
|
||||
},
|
||||
);
|
||||
}
|
||||
#[test]
|
||||
#[serial_test::serial]
|
||||
fn managed_mcps_headless_default_disabled() {
|
||||
with_managed_mcp_env(
|
||||
None,
|
||||
None,
|
||||
|| {
|
||||
let empty = toml::Value::Table(toml::map::Map::new());
|
||||
let cfg = ManagedMcpsConfig::resolve(&empty, None, true);
|
||||
assert!(! cfg.enabled);
|
||||
},
|
||||
);
|
||||
}
|
||||
#[test]
|
||||
#[serial_test::serial]
|
||||
fn managed_mcp_gateway_tools_default_disabled() {
|
||||
with_managed_mcp_env(
|
||||
None,
|
||||
None,
|
||||
|| {
|
||||
let empty = toml::Value::Table(toml::map::Map::new());
|
||||
let cfg = ManagedMcpsConfig::resolve(&empty, None, false);
|
||||
assert!(! cfg.gateway_tools_enabled);
|
||||
},
|
||||
);
|
||||
}
|
||||
#[test]
|
||||
#[serial_test::serial]
|
||||
fn managed_mcp_gateway_tools_require_managed_master() {
|
||||
with_managed_mcp_env(
|
||||
None,
|
||||
None,
|
||||
|| {
|
||||
let config: toml::Value = toml::from_str(
|
||||
r#"
|
||||
[managed_mcps]
|
||||
gateway_tools_enabled = true
|
||||
"#,
|
||||
)
|
||||
.unwrap();
|
||||
let remote = crate::util::config::RemoteSettings {
|
||||
managed_mcps_enabled: Some(false),
|
||||
..Default::default()
|
||||
};
|
||||
let cfg = ManagedMcpsConfig::resolve(&config, Some(&remote), true);
|
||||
assert!(! cfg.enabled);
|
||||
assert!(! cfg.gateway_tools_enabled);
|
||||
},
|
||||
);
|
||||
}
|
||||
#[test]
|
||||
#[serial_test::serial]
|
||||
fn managed_mcp_gateway_tools_remote_enabled() {
|
||||
with_managed_mcp_env(
|
||||
None,
|
||||
None,
|
||||
|| {
|
||||
let empty = toml::Value::Table(toml::map::Map::new());
|
||||
let remote = crate::util::config::RemoteSettings {
|
||||
managed_mcp_gateway_tools_enabled: Some(true),
|
||||
..Default::default()
|
||||
};
|
||||
let cfg = ManagedMcpsConfig::resolve(&empty, Some(&remote), false);
|
||||
assert!(cfg.gateway_tools_enabled);
|
||||
},
|
||||
);
|
||||
}
|
||||
#[test]
|
||||
#[serial_test::serial]
|
||||
fn managed_mcp_gateway_tools_env_overrides_remote() {
|
||||
with_managed_mcp_env(
|
||||
None,
|
||||
Some("0"),
|
||||
|| {
|
||||
let empty = toml::Value::Table(toml::map::Map::new());
|
||||
let remote = crate::util::config::RemoteSettings {
|
||||
managed_mcp_gateway_tools_enabled: Some(true),
|
||||
..Default::default()
|
||||
};
|
||||
let cfg = ManagedMcpsConfig::resolve(&empty, Some(&remote), false);
|
||||
assert!(! cfg.gateway_tools_enabled);
|
||||
},
|
||||
);
|
||||
}
|
||||
#[test]
|
||||
#[serial_test::serial]
|
||||
fn managed_mcp_gateway_tools_env_on_overrides_remote_off() {
|
||||
with_managed_mcp_env(
|
||||
None,
|
||||
Some("1"),
|
||||
|| {
|
||||
let empty = toml::Value::Table(toml::map::Map::new());
|
||||
let remote = crate::util::config::RemoteSettings {
|
||||
managed_mcp_gateway_tools_enabled: Some(false),
|
||||
..Default::default()
|
||||
};
|
||||
let cfg = ManagedMcpsConfig::resolve(&empty, Some(&remote), false);
|
||||
assert!(cfg.gateway_tools_enabled);
|
||||
},
|
||||
);
|
||||
}
|
||||
#[test]
|
||||
#[serial_test::serial]
|
||||
fn managed_mcp_gateway_tools_enabled_with_managed_master() {
|
||||
with_managed_mcp_env(
|
||||
None,
|
||||
None,
|
||||
|| {
|
||||
let config: toml::Value = toml::from_str(
|
||||
r#"
|
||||
[managed_mcps]
|
||||
enabled = true
|
||||
gateway_tools_enabled = true
|
||||
"#,
|
||||
)
|
||||
.unwrap();
|
||||
let cfg = ManagedMcpsConfig::resolve(&config, None, false);
|
||||
assert!(cfg.enabled);
|
||||
assert!(cfg.gateway_tools_enabled);
|
||||
},
|
||||
);
|
||||
}
|
||||
fn with_model_overrides_env_full<T>(
|
||||
ss: Option<&str>,
|
||||
id: Option<&str>,
|
||||
@@ -1777,28 +1629,16 @@ fn model_overrides_prompt_suggestion_blank_values_are_unset() {
|
||||
},
|
||||
);
|
||||
}
|
||||
/// Lock shared by every test that touches the env vars read by
|
||||
/// `ToolsConfig::resolve`, so tests across both fields can't race.
|
||||
/// Lock shared by every test that touches the env var read by
|
||||
/// `ToolsConfig::resolve`, so tests can't race.
|
||||
static TOOLS_ENV_LOCK: std::sync::Mutex<()> = std::sync::Mutex::new(());
|
||||
/// Set both `ToolsConfig` env vars for the duration of `f`, then
|
||||
/// restore. `None` clears the var.
|
||||
fn with_tools_env<T>(
|
||||
respect_gitignore: Option<&str>,
|
||||
disable_zdr: Option<&str>,
|
||||
f: impl FnOnce() -> T,
|
||||
) -> T {
|
||||
let _guard = TOOLS_ENV_LOCK.lock().unwrap_or_else(|e| e.into_inner());
|
||||
with_env_var_opt(
|
||||
"KIGI_RESPECT_GITIGNORE",
|
||||
respect_gitignore,
|
||||
|| with_env_var_opt("KIGI_DISABLE_ZDR_INCOMPATIBLE_TOOLS", disable_zdr, f),
|
||||
)
|
||||
}
|
||||
fn without_grok_respect_gitignore<T>(f: impl FnOnce() -> T) -> T {
|
||||
with_tools_env(None, None, f)
|
||||
let _guard = TOOLS_ENV_LOCK.lock().unwrap_or_else(|e| e.into_inner());
|
||||
with_env_var_opt("KIGI_RESPECT_GITIGNORE", None, f)
|
||||
}
|
||||
fn with_grok_respect_gitignore<T>(value: &str, f: impl FnOnce() -> T) -> T {
|
||||
with_tools_env(Some(value), None, f)
|
||||
let _guard = TOOLS_ENV_LOCK.lock().unwrap_or_else(|e| e.into_inner());
|
||||
with_env_var_opt("KIGI_RESPECT_GITIGNORE", Some(value), f)
|
||||
}
|
||||
#[test]
|
||||
fn tools_config_default_disabled() {
|
||||
@@ -1858,87 +1698,6 @@ fn tools_config_env_false_overrides_toml_true() {
|
||||
);
|
||||
}
|
||||
#[test]
|
||||
fn zdr_incompatible_tools_env_overrides_toml_false() {
|
||||
with_tools_env(
|
||||
None,
|
||||
Some("true"),
|
||||
|| {
|
||||
let config: toml::Value = toml::from_str(
|
||||
"[tools]\ndisable_zdr_incompatible_tools = false",
|
||||
)
|
||||
.unwrap();
|
||||
let tc = ToolsConfig::resolve(&config);
|
||||
assert!(tc.disable_zdr_incompatible_tools, "env must override TOML");
|
||||
},
|
||||
);
|
||||
}
|
||||
#[test]
|
||||
fn zdr_video_output_s3_deserializes_from_tools_block() {
|
||||
let config: toml::Value = toml::from_str(
|
||||
r#"
|
||||
[tools]
|
||||
disable_zdr_incompatible_tools = true
|
||||
|
||||
[tools.zdr_video_output_s3]
|
||||
bucket = "team-videos"
|
||||
endpoint = "https://s3.example.com"
|
||||
region = "us-east-1"
|
||||
|
||||
[tools.zdr_video_output_s3.read_write]
|
||||
access_key_id = "AKIA..."
|
||||
secret_access_key = "secret"
|
||||
"#,
|
||||
)
|
||||
.unwrap();
|
||||
let tc = ToolsConfig::resolve(&config);
|
||||
let s3 = tc.zdr_video_output_s3.expect("zdr_video_output_s3 should deserialize");
|
||||
assert_eq!(s3.bucket, "team-videos");
|
||||
assert!(s3.is_valid());
|
||||
}
|
||||
#[test]
|
||||
fn incomplete_zdr_video_output_s3_is_ignored() {
|
||||
without_grok_respect_gitignore(|| {
|
||||
let config: toml::Value = toml::from_str(
|
||||
r#"
|
||||
[tools]
|
||||
disable_zdr_incompatible_tools = true
|
||||
|
||||
[tools.zdr_video_output_s3]
|
||||
bucket = "team-videos"
|
||||
"#,
|
||||
)
|
||||
.unwrap();
|
||||
let tc = ToolsConfig::resolve(&config);
|
||||
assert!(tc.zdr_video_output_s3.is_none());
|
||||
assert!(
|
||||
tc.disable_zdr_incompatible_tools,
|
||||
"incomplete zdr_video_output_s3 must not drop disable_zdr_incompatible_tools"
|
||||
);
|
||||
});
|
||||
}
|
||||
#[test]
|
||||
fn malformed_zdr_video_output_s3_preserves_zdr_flag() {
|
||||
without_grok_respect_gitignore(|| {
|
||||
let config: toml::Value = toml::from_str(
|
||||
r#"
|
||||
[tools]
|
||||
disable_zdr_incompatible_tools = true
|
||||
respect_gitignore = true
|
||||
|
||||
[tools.zdr_video_output_s3]
|
||||
bucket = "team-videos"
|
||||
endpoint = "https://s3.example.com"
|
||||
region = "us-east-1"
|
||||
"#,
|
||||
)
|
||||
.unwrap();
|
||||
let tc = ToolsConfig::resolve(&config);
|
||||
assert!(tc.zdr_video_output_s3.is_none());
|
||||
assert!(tc.disable_zdr_incompatible_tools);
|
||||
assert!(tc.respect_gitignore);
|
||||
});
|
||||
}
|
||||
#[test]
|
||||
fn roles_parse_from_toml() {
|
||||
let toml_str = r#"
|
||||
[roles.researcher]
|
||||
|
||||
Reference in New Issue
Block a user