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:
@@ -917,15 +917,6 @@ pub async fn run_leader(
|
||||
}
|
||||
ConfigUpdate::AuthCleared => {
|
||||
auth_manager_for_config.clear_in_memory();
|
||||
let line = internal_reload_request_line(
|
||||
"config-auth-cleared",
|
||||
"x.ai/internal/auth_cleared",
|
||||
serde_json::json!({}),
|
||||
);
|
||||
let mut tx = acp_tx_for_config.lock().await;
|
||||
if let Err(e) = tx.write_all(line.as_bytes()).await {
|
||||
warn!(error = %e, "failed to inject auth-cleared cleanup into ACP stream");
|
||||
}
|
||||
models_manager_for_config.on_auth_changed().await;
|
||||
kigi_log::unified_log::warn(
|
||||
"auth cleared from disk",
|
||||
@@ -1144,14 +1135,6 @@ mod tests {
|
||||
);
|
||||
let msg: serde_json::Value = serde_json::from_str(line.trim_end()).unwrap();
|
||||
assert_eq!(msg["params"]["cwd"], "/repo/x");
|
||||
|
||||
let line = internal_reload_request_line(
|
||||
"config-auth-cleared",
|
||||
"x.ai/internal/auth_cleared",
|
||||
serde_json::json!({}),
|
||||
);
|
||||
let msg: serde_json::Value = serde_json::from_str(line.trim_end()).unwrap();
|
||||
assert_eq!(msg["method"], "_x.ai/internal/auth_cleared");
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
|
||||
@@ -301,9 +301,6 @@ pub struct Requirements {
|
||||
pub tool_search: Constrained<bool>,
|
||||
pub web_fetch: Constrained<bool>,
|
||||
pub ask_user_question: Constrained<bool>,
|
||||
pub image_gen: Constrained<bool>,
|
||||
pub image_edit: Constrained<bool>,
|
||||
pub video_gen: Constrained<bool>,
|
||||
pub write_file: Constrained<bool>,
|
||||
/// Voice dictation (STT). Pin via requirements/managed `[features] voice_mode`.
|
||||
pub voice_mode: Constrained<bool>,
|
||||
@@ -1075,8 +1072,6 @@ pub struct Config {
|
||||
pub memory: crate::config::MemoryConfig,
|
||||
#[serde(default, skip_serializing)]
|
||||
pub compaction: CompactionConfig,
|
||||
#[serde(default, skip_serializing)]
|
||||
pub managed_mcps: crate::config::ManagedMcpsConfig,
|
||||
/// `[auth]` alias — consumed by `expand_auth_alias` before serde.
|
||||
/// Typed as `KimiCodeConfig` (same schema) so sub-field typos are caught.
|
||||
#[serde(default, skip_serializing)]
|
||||
@@ -1206,18 +1201,6 @@ pub struct Config {
|
||||
/// Resolved by [`crate::config::ToolsConfig::resolve`].
|
||||
#[serde(skip)]
|
||||
pub respect_gitignore: bool,
|
||||
/// When `true`, `MvpAgent::prepare_video_gen_config` returns
|
||||
/// `VideoGenConfig::Disabled`, dropping `video_gen` (and any
|
||||
/// future ZDR-incompatible tools) from the model's tool set.
|
||||
/// Resolved by [`crate::config::ToolsConfig::resolve`].
|
||||
#[serde(skip)]
|
||||
pub disable_zdr_incompatible_tools: bool,
|
||||
/// S3 config for ZDR video output (presigned upload to team bucket).
|
||||
/// Only used when `disable_zdr_incompatible_tools` is `true` and the
|
||||
/// config is valid. Resolved by [`crate::config::ToolsConfig::resolve`].
|
||||
#[serde(skip)]
|
||||
pub zdr_video_output_s3:
|
||||
Option<kigi_tools::implementations::grok_build::video_gen::ZdrVideoOutputS3Config>,
|
||||
/// Whether to enrich path-not-found errors with CWD reminders,
|
||||
/// "dropped repo folder" correction, and similar-name suggestions.
|
||||
/// Default `false`. Enabled via remote settings.
|
||||
@@ -1225,13 +1208,6 @@ pub struct Config {
|
||||
/// which sessions had path-not-found hints active.
|
||||
#[serde(default)]
|
||||
pub path_not_found_hints: bool,
|
||||
/// Whether to fetch managed MCP configs from the managed connectors service at startup.
|
||||
/// Resolved by [`crate::config::ManagedMcpsConfig::resolve`]: env var >
|
||||
/// config.toml > remote settings > default (off in headless, on in interactive).
|
||||
#[serde(skip)]
|
||||
pub managed_mcps_enabled: bool,
|
||||
#[serde(skip)]
|
||||
pub managed_mcp_gateway_tools_enabled: bool,
|
||||
/// Whether auto-wake is enabled: when a background task or subagent
|
||||
/// completes, immediately inject a synthetic prompt instead of waiting
|
||||
/// for the idle-gated notification drain.
|
||||
@@ -1446,7 +1422,6 @@ impl Default for Config {
|
||||
subagents: crate::config::SubagentsConfig::default(),
|
||||
memory: crate::config::MemoryConfig::default(),
|
||||
compaction: CompactionConfig::default(),
|
||||
managed_mcps: crate::config::ManagedMcpsConfig::default(),
|
||||
auth: None,
|
||||
desktop: None,
|
||||
tips: None,
|
||||
@@ -1476,15 +1451,11 @@ impl Default for Config {
|
||||
todo_gate: false,
|
||||
laziness_debug_log: None,
|
||||
respect_gitignore: false,
|
||||
disable_zdr_incompatible_tools: false,
|
||||
zdr_video_output_s3: None,
|
||||
path_not_found_hints: false,
|
||||
cli_experimental_memory: false,
|
||||
cli_no_memory: false,
|
||||
cli_subagents: None,
|
||||
memory_config: None,
|
||||
managed_mcps_enabled: true,
|
||||
managed_mcp_gateway_tools_enabled: false,
|
||||
auto_wake_enabled: true,
|
||||
compat_resolved: CompatConfig::default(),
|
||||
requirements: Requirements::default(),
|
||||
@@ -1603,8 +1574,6 @@ impl Config {
|
||||
/// Call immediately after `new_from_toml_cfg()`. Fields resolved:
|
||||
/// - subagents (6 fields) via `SubagentsConfig::resolve`
|
||||
/// - respect_gitignore via `ToolsConfig::resolve`
|
||||
/// - disable_zdr_incompatible_tools via `ToolsConfig::resolve`
|
||||
/// - managed_mcps_enabled via `ManagedMcpsConfig::resolve`
|
||||
/// - session_summary_model / image_description_model /
|
||||
/// prompt_suggest_model_pin via `ModelOverrideConfig::resolve`
|
||||
/// - memory_config via `MemoryConfig::resolve`
|
||||
@@ -1624,15 +1593,6 @@ impl Config {
|
||||
Some(pinned) => pinned,
|
||||
None => tools.respect_gitignore,
|
||||
};
|
||||
self.disable_zdr_incompatible_tools = tools.disable_zdr_incompatible_tools;
|
||||
self.zdr_video_output_s3 = tools.zdr_video_output_s3;
|
||||
let mcps = crate::config::ManagedMcpsConfig::resolve(
|
||||
ctx.raw_config,
|
||||
ctx.remote_settings,
|
||||
ctx.is_headless,
|
||||
);
|
||||
self.managed_mcps_enabled = mcps.enabled;
|
||||
self.managed_mcp_gateway_tools_enabled = mcps.gateway_tools_enabled;
|
||||
let models = crate::config::ModelOverrideConfig::resolve(
|
||||
ctx.cli_session_summary_model,
|
||||
ctx.raw_config,
|
||||
@@ -1867,51 +1827,6 @@ impl Config {
|
||||
.default(true)
|
||||
.resolve()
|
||||
}
|
||||
/// `image_gen` tool gate. Default on; gated only by the `KIGI_IMAGE_GEN`
|
||||
/// env var and managed-config requirement pin.
|
||||
pub(crate) fn resolve_image_gen(&self) -> Resolved<bool> {
|
||||
BoolFlag::env("KIGI_IMAGE_GEN")
|
||||
.requirement(self.requirements.image_gen.pinned())
|
||||
.default(true)
|
||||
.resolve()
|
||||
}
|
||||
/// `image_edit` tool gate.
|
||||
///
|
||||
/// The remote settings `imagine_tools_disabled` denylist is authoritative:
|
||||
/// when it lists `image_edit`, the tool is force-removed and local
|
||||
/// env/config can't re-enable it. A managed requirement pin still outranks
|
||||
/// it; otherwise the tool defaults on and is overridable via
|
||||
/// `KIGI_IMAGE_EDIT`.
|
||||
pub(crate) fn resolve_image_edit(&self) -> Resolved<bool> {
|
||||
use kigi_tools::implementations::grok_build::IMAGE_EDIT_TOOL_NAME;
|
||||
if let Some(pinned) = self.requirements.image_edit.pinned() {
|
||||
return Resolved::new(pinned, ConfigSource::Requirement);
|
||||
}
|
||||
if self
|
||||
.remote_settings
|
||||
.as_ref()
|
||||
.is_some_and(|s| s.imagine_tool_disabled(IMAGE_EDIT_TOOL_NAME))
|
||||
{
|
||||
return Resolved::new(false, ConfigSource::Remote);
|
||||
}
|
||||
BoolFlag::env("KIGI_IMAGE_EDIT").default(true).resolve()
|
||||
}
|
||||
/// Optional Imagine model override for `image_gen`. When set (non-empty),
|
||||
/// `image_gen` calls this model slug instead of the default quality model.
|
||||
/// Precedence: env `KIGI_IMAGE_GEN_MODEL_OVERRIDE` > `[features]
|
||||
/// image_gen_model_override` config > remote settings `image_gen_model_override`.
|
||||
/// `None` → default model (`grok-imagine-image-quality`).
|
||||
pub(crate) fn resolve_image_gen_model_override(&self) -> Option<String> {
|
||||
resolve_string_flag(
|
||||
None,
|
||||
"KIGI_IMAGE_GEN_MODEL_OVERRIDE",
|
||||
self.features.image_gen_model_override.as_deref(),
|
||||
self.remote_settings
|
||||
.as_ref()
|
||||
.and_then(|s| s.image_gen_model_override.as_deref()),
|
||||
)
|
||||
.map(|r| r.value)
|
||||
}
|
||||
/// Goal mode (`/goal`) master switch. Default ON: deployments that can't
|
||||
/// reach cli-chat-proxy `/v1/settings` (custom `models_base_url`, external
|
||||
/// `auth_provider_command`, air-gapped proxies) never receive the
|
||||
@@ -3646,13 +3561,6 @@ pub struct Features {
|
||||
/// compaction. `None` = defer to remote settings / env / default (`false`).
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub two_pass_compaction: Option<bool>,
|
||||
/// Video generation tool. `None` = defer to remote settings / env / default (false).
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub video_gen: Option<bool>,
|
||||
/// `image_gen` Imagine model override. `None`/empty = defer to remote settings
|
||||
/// (`image_gen_model_override`) / env / default (`grok-imagine-image-quality`).
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub image_gen_model_override: Option<String>,
|
||||
/// Write file tool. `None` = defer to remote settings / env / default (true).
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub write_file: Option<bool>,
|
||||
@@ -4472,34 +4380,6 @@ reasoning_effort = "low"
|
||||
assert_eq!(cfg.toolset.bash.timeout_secs, Some(30.5));
|
||||
}
|
||||
#[test]
|
||||
fn resolve_runtime_fields_propagates_disable_zdr_incompatible_tools() {
|
||||
fn ctx(raw: &toml::Value) -> RuntimeResolutionContext<'_> {
|
||||
RuntimeResolutionContext {
|
||||
raw_config: raw,
|
||||
remote_settings: None,
|
||||
cwd: None,
|
||||
is_headless: false,
|
||||
cli_subagents: None,
|
||||
cli_session_summary_model: None,
|
||||
cli_experimental_memory: false,
|
||||
cli_no_memory: false,
|
||||
disable_web_search: false,
|
||||
todo_gate: false,
|
||||
laziness_debug_log: None,
|
||||
storage_mode: None,
|
||||
}
|
||||
}
|
||||
let empty: toml::Value = toml::Value::Table(toml::map::Map::new());
|
||||
let mut cfg = Config::new_from_toml_cfg(&empty).unwrap();
|
||||
cfg.resolve_runtime_fields(&ctx(&empty));
|
||||
assert!(!cfg.disable_zdr_incompatible_tools);
|
||||
let zdr: toml::Value =
|
||||
toml::from_str("[tools]\ndisable_zdr_incompatible_tools = true").unwrap();
|
||||
let mut cfg = Config::new_from_toml_cfg(&zdr).unwrap();
|
||||
cfg.resolve_runtime_fields(&ctx(&zdr));
|
||||
assert!(cfg.disable_zdr_incompatible_tools);
|
||||
}
|
||||
#[test]
|
||||
fn resolve_runtime_fields_propagates_disable_web_search() {
|
||||
fn ctx(raw: &toml::Value, disable_web_search: bool) -> RuntimeResolutionContext<'_> {
|
||||
RuntimeResolutionContext {
|
||||
@@ -7215,51 +7095,6 @@ reasoning_effort = "low"
|
||||
assert_eq!(r.source, ConfigSource::Remote);
|
||||
assert!(!r.value);
|
||||
}
|
||||
#[test]
|
||||
#[serial]
|
||||
fn resolve_image_gen_model_override_remote_settings_or_config() {
|
||||
unsafe { std::env::remove_var("KIGI_IMAGE_GEN_MODEL_OVERRIDE") };
|
||||
let with = |config: Option<&str>, gb: Option<&str>| Config {
|
||||
features: Features {
|
||||
image_gen_model_override: config.map(String::from),
|
||||
..Default::default()
|
||||
},
|
||||
remote_settings: Some(crate::util::config::RemoteSettings {
|
||||
image_gen_model_override: gb.map(String::from),
|
||||
..Default::default()
|
||||
}),
|
||||
..Default::default()
|
||||
};
|
||||
assert_eq!(Config::default().resolve_image_gen_model_override(), None);
|
||||
assert_eq!(
|
||||
with(None, Some("grok-imagine-image")).resolve_image_gen_model_override(),
|
||||
Some("grok-imagine-image".to_owned())
|
||||
);
|
||||
assert_eq!(
|
||||
with(Some("grok-imagine-image-pro"), Some("grok-imagine-image"))
|
||||
.resolve_image_gen_model_override(),
|
||||
Some("grok-imagine-image-pro".to_owned())
|
||||
);
|
||||
}
|
||||
#[test]
|
||||
#[serial]
|
||||
fn imagine_tools_disabled_gates_image_edit() {
|
||||
unsafe { std::env::remove_var("KIGI_IMAGE_EDIT") };
|
||||
let with_list = |tools: Vec<&str>| Config {
|
||||
remote_settings: Some(crate::util::config::RemoteSettings {
|
||||
imagine_tools_disabled: Some(tools.into_iter().map(String::from).collect()),
|
||||
..Default::default()
|
||||
}),
|
||||
..Default::default()
|
||||
};
|
||||
unsafe { std::env::set_var("KIGI_IMAGE_EDIT", "1") };
|
||||
let off = with_list(vec!["image_edit"]).resolve_image_edit();
|
||||
assert!(!off.value);
|
||||
assert_eq!(off.source, ConfigSource::Remote);
|
||||
unsafe { std::env::remove_var("KIGI_IMAGE_EDIT") };
|
||||
assert!(with_list(vec!["image_to_video"]).resolve_image_edit().value);
|
||||
assert!(Config::default().resolve_image_edit().value);
|
||||
}
|
||||
/// Clear every env var the goal/companion resolvers read so tests
|
||||
/// start from a known baseline regardless of run order.
|
||||
fn clear_goal_envs() {
|
||||
@@ -8103,8 +7938,6 @@ agent_type = "cursor"
|
||||
secret = "value"
|
||||
[worktree_pool]
|
||||
pool_size = 4
|
||||
[managed_mcps]
|
||||
enabled = true
|
||||
[mcp_servers.test]
|
||||
url = "https://mcp.test.com"
|
||||
[toolset.bash]
|
||||
@@ -8239,12 +8072,6 @@ agent_type = "cursor"
|
||||
std::env::remove_var("KIGI_CLAUDE_AGENTS_ENABLED");
|
||||
}
|
||||
}
|
||||
fn clear_managed_mcp_env_vars() {
|
||||
unsafe {
|
||||
std::env::remove_var("KIGI_MANAGED_MCPS_ENABLED");
|
||||
std::env::remove_var("KIGI_MANAGED_MCP_GATEWAY_TOOLS_ENABLED");
|
||||
}
|
||||
}
|
||||
fn isolate_compat_env() -> Vec<EnvGuard> {
|
||||
COMPAT_CELLS
|
||||
.into_iter()
|
||||
@@ -8531,7 +8358,6 @@ hooks = true
|
||||
#[serial]
|
||||
fn resolve_runtime_fields_interactive_defaults() {
|
||||
clear_runtime_env_vars();
|
||||
clear_managed_mcp_env_vars();
|
||||
let raw = empty_config();
|
||||
let mut cfg = Config::new_from_toml_cfg(&raw).unwrap();
|
||||
cfg.resolve_runtime_fields(&RuntimeResolutionContext {
|
||||
@@ -8550,8 +8376,6 @@ hooks = true
|
||||
});
|
||||
assert!(cfg.subagents_enabled);
|
||||
assert!(!cfg.respect_gitignore);
|
||||
assert!(cfg.managed_mcps_enabled);
|
||||
assert!(!cfg.managed_mcp_gateway_tools_enabled);
|
||||
assert_eq!(
|
||||
cfg.session_summary_model,
|
||||
Some(crate::models::default_session_summary_model().to_owned())
|
||||
@@ -8560,60 +8384,6 @@ hooks = true
|
||||
}
|
||||
#[test]
|
||||
#[serial]
|
||||
fn resolve_runtime_fields_headless_defaults() {
|
||||
clear_runtime_env_vars();
|
||||
clear_managed_mcp_env_vars();
|
||||
let raw = empty_config();
|
||||
let mut cfg = Config::new_from_toml_cfg(&raw).unwrap();
|
||||
cfg.resolve_runtime_fields(&RuntimeResolutionContext {
|
||||
raw_config: &raw,
|
||||
remote_settings: None,
|
||||
cwd: None,
|
||||
is_headless: true,
|
||||
cli_subagents: None,
|
||||
cli_session_summary_model: None,
|
||||
cli_experimental_memory: false,
|
||||
cli_no_memory: false,
|
||||
disable_web_search: false,
|
||||
todo_gate: false,
|
||||
laziness_debug_log: None,
|
||||
storage_mode: None,
|
||||
});
|
||||
assert!(
|
||||
!cfg.managed_mcps_enabled,
|
||||
"headless should default managed_mcps to false"
|
||||
);
|
||||
assert!(!cfg.managed_mcp_gateway_tools_enabled);
|
||||
}
|
||||
#[test]
|
||||
#[serial]
|
||||
fn resolve_runtime_fields_managed_gateway_tools_from_remote() {
|
||||
clear_runtime_env_vars();
|
||||
clear_managed_mcp_env_vars();
|
||||
let raw = empty_config();
|
||||
let remote = crate::util::config::RemoteSettings {
|
||||
managed_mcp_gateway_tools_enabled: Some(true),
|
||||
..Default::default()
|
||||
};
|
||||
let mut cfg = Config::new_from_toml_cfg(&raw).unwrap();
|
||||
cfg.resolve_runtime_fields(&RuntimeResolutionContext {
|
||||
raw_config: &raw,
|
||||
remote_settings: Some(&remote),
|
||||
cwd: None,
|
||||
is_headless: false,
|
||||
cli_subagents: None,
|
||||
cli_session_summary_model: None,
|
||||
cli_experimental_memory: false,
|
||||
cli_no_memory: false,
|
||||
disable_web_search: false,
|
||||
todo_gate: false,
|
||||
laziness_debug_log: None,
|
||||
storage_mode: None,
|
||||
});
|
||||
assert!(cfg.managed_mcp_gateway_tools_enabled);
|
||||
}
|
||||
#[test]
|
||||
#[serial]
|
||||
fn resolve_runtime_fields_subagents_from_config() {
|
||||
clear_runtime_env_vars();
|
||||
let raw: toml::Value = toml::from_str("[subagents]\nenabled = true").unwrap();
|
||||
@@ -8751,12 +8521,10 @@ hooks = true
|
||||
cfg.resolve_runtime_fields(&ctx);
|
||||
let first_subagents = cfg.subagents_enabled;
|
||||
let first_gitignore = cfg.respect_gitignore;
|
||||
let first_mcps = cfg.managed_mcps_enabled;
|
||||
let first_ss = cfg.session_summary_model.clone();
|
||||
cfg.resolve_runtime_fields(&ctx);
|
||||
assert_eq!(cfg.subagents_enabled, first_subagents);
|
||||
assert_eq!(cfg.respect_gitignore, first_gitignore);
|
||||
assert_eq!(cfg.managed_mcps_enabled, first_mcps);
|
||||
assert_eq!(cfg.session_summary_model, first_ss);
|
||||
}
|
||||
#[test]
|
||||
|
||||
@@ -270,13 +270,7 @@ impl acp::Agent for MvpAgent {
|
||||
let current_working_directory = self.launch_cwd.clone();
|
||||
let hostname = gethostname::gethostname();
|
||||
let mcp_servers: Vec<crate::extensions::mcp::McpServerEntry> = Vec::new();
|
||||
let fetch_managed_mcps = self.cfg.borrow().managed_mcps_enabled
|
||||
&& self.can_fetch_managed_mcps();
|
||||
if self.cfg.borrow().managed_mcps_enabled && !fetch_managed_mcps {
|
||||
tracing::info!("Managed MCP fetch: DISABLED");
|
||||
}
|
||||
self.spawn_initialize_launch_mcp_setup(fetch_managed_mcps);
|
||||
self.spawn_managed_gateway_tool_catalog_fetch();
|
||||
self.spawn_initialize_launch_mcp_setup();
|
||||
let init_model_state = self.model_state(None);
|
||||
Ok(
|
||||
acp::InitializeResponse::new(acp::ProtocolVersion::V1)
|
||||
@@ -545,9 +539,7 @@ impl acp::Agent for MvpAgent {
|
||||
let remote_settings = self.cfg.borrow().remote_settings.clone();
|
||||
folder_trust::resolve_and_record(cwd.as_path(), remote_settings.as_ref(), false);
|
||||
let initial_client_mcp_servers = arguments.mcp_servers.clone();
|
||||
let (mcp_servers, managed_mcp_expires_at) = self
|
||||
.resolve_mcp_servers(arguments.mcp_servers, cwd.as_path())
|
||||
.await;
|
||||
let mcp_servers = self.resolve_mcp_servers(arguments.mcp_servers, cwd.as_path());
|
||||
let mcp_meta_config_map = parse_mcp_meta_config(arguments.meta.as_ref());
|
||||
let client_session_id = arguments
|
||||
.meta
|
||||
@@ -756,7 +748,6 @@ impl acp::Agent for MvpAgent {
|
||||
persisted_goal_mode: None,
|
||||
persisted_announcement_state: None,
|
||||
session_meta: arguments.meta.as_ref(),
|
||||
managed_mcp_expires_at,
|
||||
model_agent_type: model_agent_type.as_deref(),
|
||||
session_model_id,
|
||||
session_yolo_mode,
|
||||
@@ -902,9 +893,7 @@ impl acp::Agent for MvpAgent {
|
||||
let remote_settings = self.cfg.borrow().remote_settings.clone();
|
||||
folder_trust::resolve_and_record(cwd.as_path(), remote_settings.as_ref(), false);
|
||||
let initial_client_mcp_servers = client_mcp_servers.clone();
|
||||
let (mcp_servers, managed_mcp_expires_at) = self
|
||||
.resolve_mcp_servers(client_mcp_servers, cwd.as_path())
|
||||
.await;
|
||||
let mcp_servers = self.resolve_mcp_servers(client_mcp_servers, cwd.as_path());
|
||||
let mcp_meta_config_map = parse_mcp_meta_config(request_meta.as_ref());
|
||||
let mut load_timer = crate::instrumentation_timer!("session.load_session");
|
||||
load_timer.with_field("session_id", session_id.0.as_ref());
|
||||
@@ -1212,7 +1201,6 @@ impl acp::Agent for MvpAgent {
|
||||
persisted_goal_mode: _persisted_goal_mode,
|
||||
persisted_announcement_state,
|
||||
session_meta: request_meta.as_ref(),
|
||||
managed_mcp_expires_at,
|
||||
model_agent_type: persisted_agent_name.as_deref(),
|
||||
session_model_id: summary.current_model_id.clone(),
|
||||
session_yolo_mode,
|
||||
@@ -2083,7 +2071,7 @@ impl acp::Agent for MvpAgent {
|
||||
| "x.ai/internal/reload_all_mcp_servers"
|
||||
| "x.ai/internal/reload_project_mcp_servers" | "x.ai/internal/reload_skills"
|
||||
| "x.ai/internal/reload_models" | "x.ai/internal/reload_models_cache"
|
||||
| "x.ai/internal/auth_cleared" | "x.ai/plugins/reload"
|
||||
| "x.ai/plugins/reload"
|
||||
| "x.ai/commands/list" => {
|
||||
crate::extensions::session_admin::handle(self, &args).await
|
||||
}
|
||||
|
||||
@@ -84,123 +84,6 @@ impl MvpAgent {
|
||||
}
|
||||
})
|
||||
}
|
||||
fn has_managed_mcp_auth(&self) -> bool {
|
||||
self.auth_manager
|
||||
.current_or_expired()
|
||||
.is_some_and(|a| a.is_session_auth())
|
||||
}
|
||||
/// Requires feature flag AND xAI authentication (OIDC or legacy WebLogin).
|
||||
pub(super) fn can_fetch_managed_mcps(&self) -> bool {
|
||||
let cfg = self.cfg.borrow();
|
||||
cfg.managed_mcps_enabled && !cfg.managed_mcp_gateway_tools_enabled
|
||||
&& self.has_managed_mcp_auth()
|
||||
}
|
||||
fn can_fetch_managed_mcp_gateway_tools(&self) -> bool {
|
||||
self.cfg.borrow().managed_mcp_gateway_tools_enabled
|
||||
&& self.has_managed_mcp_auth()
|
||||
}
|
||||
pub async fn get_managed_mcp_configs(
|
||||
&self,
|
||||
) -> Vec<crate::session::managed_mcp::ManagedMcpConfig> {
|
||||
if !self.can_fetch_managed_mcps() {
|
||||
return vec![];
|
||||
}
|
||||
let proxy_url = self.cfg.borrow().endpoints.proxy_url();
|
||||
crate::session::managed_mcp::fetch_managed_mcp_configs(
|
||||
&self.managed_mcp_cache,
|
||||
&proxy_url,
|
||||
&self.auth_manager,
|
||||
)
|
||||
.await
|
||||
}
|
||||
pub async fn get_managed_mcp_gateway_tool_catalog(
|
||||
&self,
|
||||
) -> Option<crate::session::managed_mcp::GatewayToolCatalog> {
|
||||
if !self.can_fetch_managed_mcp_gateway_tools() {
|
||||
self.managed_mcp_cache.lock().await.disable_gateway_tools();
|
||||
return None;
|
||||
}
|
||||
self.managed_mcp_cache.lock().await.enable_gateway_tools();
|
||||
let proxy_url = self.cfg.borrow().endpoints.proxy_url();
|
||||
let auth_key = self
|
||||
.auth_manager
|
||||
.get_valid_token()
|
||||
.await
|
||||
.ok()
|
||||
.or_else(|| self.auth_manager.current_or_expired().map(|a| a.key));
|
||||
crate::session::managed_mcp::get_or_fetch_gateway_tool_catalog(
|
||||
&self.managed_mcp_cache,
|
||||
&proxy_url,
|
||||
auth_key.as_deref(),
|
||||
)
|
||||
.await
|
||||
}
|
||||
pub fn managed_mcp_cache(
|
||||
&self,
|
||||
) -> &crate::session::managed_mcp::ManagedMcpStateHandle {
|
||||
&self.managed_mcp_cache
|
||||
}
|
||||
pub(crate) fn disable_managed_gateway_tools_and_refresh_sessions(&self) {
|
||||
self.disable_managed_gateway_tools_and_refresh_sessions_with_txs(
|
||||
self.sessions.borrow().values().map(|handle| handle.cmd_tx.clone()).collect(),
|
||||
);
|
||||
}
|
||||
fn disable_managed_gateway_tools_and_refresh_sessions_with_txs(
|
||||
&self,
|
||||
session_txs: Vec<tokio::sync::mpsc::UnboundedSender<SessionCommand>>,
|
||||
) {
|
||||
let cache = self.managed_mcp_cache.clone();
|
||||
tokio::task::spawn_local(async move {
|
||||
cache.lock().await.disable_gateway_tools();
|
||||
for tx in session_txs {
|
||||
let _ = tx.send(SessionCommand::RefreshMcpSearchIndex);
|
||||
}
|
||||
});
|
||||
}
|
||||
pub(crate) fn spawn_managed_gateway_tool_catalog_fetch(&self) {
|
||||
let session_txs: Vec<_> = self
|
||||
.sessions
|
||||
.borrow()
|
||||
.values()
|
||||
.map(|handle| handle.cmd_tx.clone())
|
||||
.collect();
|
||||
if !self.can_fetch_managed_mcp_gateway_tools() {
|
||||
self.disable_managed_gateway_tools_and_refresh_sessions_with_txs(
|
||||
session_txs,
|
||||
);
|
||||
return;
|
||||
}
|
||||
let cache = self.managed_mcp_cache.clone();
|
||||
let proxy_url = self.cfg.borrow().endpoints.proxy_url();
|
||||
let auth_manager = self.auth_manager.clone();
|
||||
tokio::task::spawn_local(async move {
|
||||
let auth_key = auth_manager
|
||||
.get_valid_token()
|
||||
.await
|
||||
.ok()
|
||||
.or_else(|| auth_manager.current_or_expired().map(|a| a.key));
|
||||
if !auth_manager
|
||||
.current_or_expired()
|
||||
.is_some_and(|a| a.is_session_auth())
|
||||
{
|
||||
cache.lock().await.disable_gateway_tools();
|
||||
for tx in session_txs {
|
||||
let _ = tx.send(SessionCommand::RefreshMcpSearchIndex);
|
||||
}
|
||||
return;
|
||||
}
|
||||
cache.lock().await.enable_gateway_tools();
|
||||
crate::session::managed_mcp::get_or_fetch_gateway_tool_catalog(
|
||||
&cache,
|
||||
&proxy_url,
|
||||
auth_key.as_deref(),
|
||||
)
|
||||
.await;
|
||||
for tx in session_txs {
|
||||
let _ = tx.send(SessionCommand::RefreshMcpSearchIndex);
|
||||
}
|
||||
});
|
||||
}
|
||||
/// Resolve the launch dir's project-scope trust verdict ONCE and return it
|
||||
/// with its path.
|
||||
///
|
||||
@@ -226,15 +109,12 @@ impl MvpAgent {
|
||||
/// Resolve folder trust and load launch-dir MCP configs after `initialize`
|
||||
/// returns. The walks are synchronous and expensive in large monorepos; they
|
||||
/// must not block the ACP response (grok-desktop sends `initialize` immediately).
|
||||
pub(super) fn spawn_initialize_launch_mcp_setup(&self, fetch_managed_mcps: bool) {
|
||||
pub(super) fn spawn_initialize_launch_mcp_setup(&self) {
|
||||
let cwd = self.launch_cwd.clone();
|
||||
let compat = self.cfg.borrow().compat_resolved;
|
||||
let remote_settings = self.cfg.borrow().remote_settings.clone();
|
||||
let gateway = self.gateway.clone();
|
||||
let agent_mcp_state = self.agent_mcp_state.clone();
|
||||
let managed_mcp_cache = self.managed_mcp_cache.clone();
|
||||
let proxy_url = self.cfg.borrow().endpoints.proxy_url();
|
||||
let auth_manager = self.auth_manager.clone();
|
||||
tokio::task::spawn_local(async move {
|
||||
let local_mcp_servers = match tokio::task::spawn_blocking(move || {
|
||||
let local = crate::util::config::load_mcp_servers(&cwd, &compat);
|
||||
@@ -258,27 +138,9 @@ impl MvpAgent {
|
||||
}
|
||||
crate::extensions::mcp::notify_servers_updated(
|
||||
&gateway,
|
||||
&[],
|
||||
&local_mcp_servers,
|
||||
)
|
||||
.await;
|
||||
if !fetch_managed_mcps {
|
||||
return;
|
||||
}
|
||||
let managed = crate::session::managed_mcp::fetch_managed_mcp_configs(
|
||||
&managed_mcp_cache,
|
||||
&proxy_url,
|
||||
&auth_manager,
|
||||
)
|
||||
.await;
|
||||
if !managed.is_empty() {
|
||||
crate::extensions::mcp::notify_servers_updated(
|
||||
&gateway,
|
||||
&managed,
|
||||
&local_mcp_servers,
|
||||
)
|
||||
.await;
|
||||
}
|
||||
});
|
||||
}
|
||||
pub fn agent_mcp_state(
|
||||
@@ -311,23 +173,19 @@ impl MvpAgent {
|
||||
plugin_count = count, "lazily populated plugin registry snapshot"
|
||||
);
|
||||
}
|
||||
/// Fetch managed configs, merge with client servers, return merged list + earliest expiry.
|
||||
pub(super) async fn resolve_mcp_servers(
|
||||
/// Merge on-disk/plugin MCP servers with client servers.
|
||||
pub(super) fn resolve_mcp_servers(
|
||||
&self,
|
||||
client_servers: Vec<acp::McpServer>,
|
||||
cwd: &std::path::Path,
|
||||
) -> (Vec<acp::McpServer>, Option<chrono::DateTime<chrono::Utc>>) {
|
||||
) -> Vec<acp::McpServer> {
|
||||
self.ensure_plugin_registry();
|
||||
let managed = self.get_managed_mcp_configs().await;
|
||||
let expires_at = managed.iter().filter_map(|c| c.token_expires_at).min();
|
||||
let merged = crate::session::managed_mcp::merge_managed_mcp_servers(
|
||||
crate::session::managed_mcp::merge_managed_mcp_servers(
|
||||
client_servers,
|
||||
cwd,
|
||||
&managed,
|
||||
self.plugin_registry_handle.snapshot().as_deref(),
|
||||
&self.cfg.borrow().compat_resolved,
|
||||
);
|
||||
(merged, expires_at)
|
||||
)
|
||||
}
|
||||
/// Set the memory configuration (called from TUI after config resolution).
|
||||
pub fn set_memory_config(&mut self, config: crate::config::MemoryConfig) {
|
||||
@@ -760,39 +618,6 @@ impl MvpAgent {
|
||||
);
|
||||
(id.clone(), new_config)
|
||||
}
|
||||
/// Build image generation config.
|
||||
///
|
||||
/// Both BYOK and session (OAuth) users go direct to `api_base_url`.
|
||||
/// `sampling_config.api_key` carries the OAuth bearer for session users (the
|
||||
/// `api_key_provider` refreshes it per request), so IC authenticates and
|
||||
/// meters Imagine usage per-user.
|
||||
pub(super) fn prepare_image_gen_config(
|
||||
&self,
|
||||
) -> kigi_tools::implementations::grok_build::image_gen::ImageGenConfig {
|
||||
use kigi_tools::implementations::grok_build::image_gen::ImageGenConfig;
|
||||
let sampling_config = self.sampling_config.borrow();
|
||||
let Some(ref api_key) = sampling_config.api_key else {
|
||||
return ImageGenConfig::Disabled;
|
||||
};
|
||||
let cfg = self.cfg.borrow();
|
||||
let base_url = cfg.endpoints.api_base_url.clone();
|
||||
let version = cfg
|
||||
.client_version
|
||||
.clone()
|
||||
.unwrap_or_else(|| kigi_version::VERSION.to_string());
|
||||
let alpha_test_key = cfg.endpoints.alpha_test_key.clone();
|
||||
let mut headers = indexmap::IndexMap::new();
|
||||
headers.insert("user-agent".to_string(), format!("kigi/{version}"));
|
||||
ImageGenConfig::Enabled {
|
||||
api_key: api_key.clone(),
|
||||
base_url,
|
||||
extra_headers: headers,
|
||||
image_gen_enabled: cfg.resolve_image_gen().value,
|
||||
image_edit_enabled: cfg.resolve_image_edit().value,
|
||||
model_override: cfg.resolve_image_gen_model_override(),
|
||||
tier_restricted: false,
|
||||
}
|
||||
}
|
||||
/// Build deploy-service config. The tool talks directly to the deployer service.
|
||||
pub(super) fn prepare_app_builder_deployer_config(
|
||||
&self,
|
||||
@@ -800,40 +625,6 @@ impl MvpAgent {
|
||||
use kigi_tools::implementations::grok_build::deploy_app::AppBuilderDeployerConfig;
|
||||
AppBuilderDeployerConfig::Disabled
|
||||
}
|
||||
/// Build video generation config. Video tools call the xAI API directly.
|
||||
pub(super) fn prepare_video_gen_config(
|
||||
&self,
|
||||
) -> kigi_tools::implementations::grok_build::video_gen::VideoGenConfig {
|
||||
use kigi_tools::implementations::grok_build::video_gen::VideoGenConfig;
|
||||
let Some(api_key) = self.sampling_config.borrow().api_key.clone() else {
|
||||
return VideoGenConfig::Disabled;
|
||||
};
|
||||
let cfg = self.cfg.borrow();
|
||||
let zdr_video_output_s3 = cfg
|
||||
.disable_zdr_incompatible_tools
|
||||
.then(|| cfg.zdr_video_output_s3.clone())
|
||||
.flatten()
|
||||
.filter(|s3| s3.is_valid());
|
||||
if cfg.disable_zdr_incompatible_tools && zdr_video_output_s3.is_none() {
|
||||
tracing::info!("video_gen disabled by tools.disable_zdr_incompatible_tools");
|
||||
return VideoGenConfig::Disabled;
|
||||
}
|
||||
let base_url = cfg.endpoints.api_base_url.clone();
|
||||
let version = cfg
|
||||
.client_version
|
||||
.clone()
|
||||
.unwrap_or_else(|| kigi_version::VERSION.to_string());
|
||||
let alpha_test_key = cfg.endpoints.alpha_test_key.clone();
|
||||
let mut headers = indexmap::IndexMap::new();
|
||||
headers.insert("user-agent".to_string(), format!("kigi/{version}"));
|
||||
VideoGenConfig::Enabled {
|
||||
api_key,
|
||||
base_url,
|
||||
extra_headers: headers,
|
||||
zdr_video_output_s3: zdr_video_output_s3.map(Box::new),
|
||||
tier_restricted: false,
|
||||
}
|
||||
}
|
||||
/// Web search config (PRD F5). The Kimi search service exists only on
|
||||
/// the Kimi Code subscription channel (`POST {coding_base}/search`,
|
||||
/// kimi-cli `auth/platforms.py`), so this is `Enabled` only for OAuth
|
||||
@@ -1016,7 +807,6 @@ impl MvpAgent {
|
||||
worktree_type,
|
||||
restore_code,
|
||||
session_registry_local,
|
||||
managed_mcp_cache: Default::default(),
|
||||
agent_mcp_state: std::sync::Arc::new(
|
||||
tokio::sync::Mutex::new(
|
||||
crate::session::mcp_servers::McpState::new(vec![]),
|
||||
@@ -1829,7 +1619,6 @@ impl MvpAgent {
|
||||
persisted_goal_mode,
|
||||
persisted_announcement_state,
|
||||
session_meta,
|
||||
managed_mcp_expires_at,
|
||||
model_agent_type,
|
||||
session_model_id,
|
||||
session_yolo_mode,
|
||||
@@ -2258,8 +2047,6 @@ impl MvpAgent {
|
||||
.and_then(|entry| entry.info.max_retries);
|
||||
let origin_client = self.origin_client_info_from_meta(init.meta.as_ref());
|
||||
let web_search_config = self.prepare_web_search_config();
|
||||
let image_gen_config = self.prepare_image_gen_config();
|
||||
let video_gen_config = self.prepare_video_gen_config();
|
||||
let app_builder_deployer_config = self.prepare_app_builder_deployer_config();
|
||||
let web_fetch_config = self.prepare_web_fetch_config();
|
||||
let write_file_enabled = self.cfg.borrow().resolve_write_file().value;
|
||||
@@ -2309,7 +2096,6 @@ impl MvpAgent {
|
||||
let cfg = self.cfg.borrow();
|
||||
cfg.resolve_backend_tools().value
|
||||
};
|
||||
let managed_mcp_proxy_url = self.cfg.borrow().endpoints.proxy_url();
|
||||
let init_meta = self
|
||||
.initialize_request
|
||||
.get()
|
||||
@@ -2471,9 +2257,6 @@ impl MvpAgent {
|
||||
persisted_announcement_state,
|
||||
self.memory_config.clone(),
|
||||
feedback_flags,
|
||||
self.managed_mcp_cache.clone(),
|
||||
managed_mcp_expires_at,
|
||||
managed_mcp_proxy_url,
|
||||
session_model_id,
|
||||
session_yolo_mode,
|
||||
session_auto_mode,
|
||||
@@ -2482,8 +2265,6 @@ impl MvpAgent {
|
||||
model_max_retries,
|
||||
web_search_config,
|
||||
web_fetch_config,
|
||||
image_gen_config,
|
||||
video_gen_config,
|
||||
app_builder_deployer_config,
|
||||
write_file_enabled,
|
||||
goal_enabled,
|
||||
@@ -2617,7 +2398,6 @@ impl MvpAgent {
|
||||
self.notify_session_cwd_for_watch(std::path::Path::new(&session_info.cwd));
|
||||
self.activity.register_session(&session_info.id.0, &handle);
|
||||
self.sessions.borrow_mut().insert(session_info.id.clone(), handle);
|
||||
self.spawn_managed_gateway_tool_catalog_fetch();
|
||||
let cwd_for_maintenance = session_info.cwd.clone();
|
||||
tokio::spawn(async move {
|
||||
crate::session::prompt_history::truncate_if_needed_async(cwd_for_maintenance)
|
||||
|
||||
@@ -150,10 +150,6 @@ impl MvpAgent {
|
||||
|
||||
let gateway = self.gateway.clone();
|
||||
let plugin_handle = self.plugin_registry_handle.clone();
|
||||
let managed_mcp_cache = self.managed_mcp_cache.clone();
|
||||
let auth_manager = self.auth_manager.clone();
|
||||
let can_fetch_managed = self.can_fetch_managed_mcps();
|
||||
let proxy_url = self.cfg.borrow().endpoints.proxy_url();
|
||||
let compat = self.cfg.borrow().compat_resolved;
|
||||
let remote = remote.cloned();
|
||||
let cwd = cwd.to_path_buf();
|
||||
@@ -258,10 +254,6 @@ impl MvpAgent {
|
||||
gateway: &gateway,
|
||||
targets,
|
||||
plugin_handle: &plugin_handle,
|
||||
managed_mcp_cache: &managed_mcp_cache,
|
||||
auth_manager: &auth_manager,
|
||||
can_fetch_managed,
|
||||
proxy_url: &proxy_url,
|
||||
compat: &compat,
|
||||
prompt_cwd: &cwd,
|
||||
})
|
||||
@@ -291,10 +283,6 @@ struct ReloadAfterGrant<'a> {
|
||||
/// Every session sharing the granted workspace, each with its own cwd.
|
||||
targets: Vec<ReloadTarget>,
|
||||
plugin_handle: &'a kigi_agent::plugins::SharedPluginRegistryHandle,
|
||||
managed_mcp_cache: &'a crate::session::managed_mcp::ManagedMcpStateHandle,
|
||||
auth_manager: &'a std::sync::Arc<AuthManager>,
|
||||
can_fetch_managed: bool,
|
||||
proxy_url: &'a str,
|
||||
compat: &'a kigi_tools::types::CompatConfig,
|
||||
/// The prompting session's cwd — used only for the client catalog push.
|
||||
prompt_cwd: &'a std::path::Path,
|
||||
@@ -303,27 +291,15 @@ struct ReloadAfterGrant<'a> {
|
||||
/// Reload each granted-workspace session's now-trusted project servers in place
|
||||
/// (no restart), driving the canonical primitives the normal spawn/reload paths
|
||||
/// use — PER SESSION CWD, like `handle_reload_project_mcp_servers` /
|
||||
/// `broadcast_plugin_registry_to_sessions`: `fetch_managed_mcp_configs` +
|
||||
/// `broadcast_plugin_registry_to_sessions`:
|
||||
/// `merge_managed_mcp_servers` (`SessionCommand::UpdateMcpServers`), `build_for_cwd`
|
||||
/// (`SessionCommand::ReloadPlugins`), and `reload_hooks_impl`
|
||||
/// (`SessionCommand::ReloadHooks`), then push the refreshed MCP catalog. LSP is
|
||||
/// spawn-baked and applies on the next session open (see module docs). Caller
|
||||
/// must have granted + recorded trust first.
|
||||
async fn reload_project_servers_after_grant(ctx: ReloadAfterGrant<'_>) {
|
||||
// Managed (gateway/Toolbox) servers must survive the re-merge; fetch them once
|
||||
// (cwd-independent) via the shared helper (single-sources the auth-key dance
|
||||
// with `MvpAgent::get_managed_mcp_configs`). The plugin MCP snapshot is also
|
||||
// global, so it is fine to reuse across cwds for the merge.
|
||||
let managed = if ctx.can_fetch_managed {
|
||||
crate::session::managed_mcp::fetch_managed_mcp_configs(
|
||||
ctx.managed_mcp_cache,
|
||||
ctx.proxy_url,
|
||||
ctx.auth_manager,
|
||||
)
|
||||
.await
|
||||
} else {
|
||||
vec![]
|
||||
};
|
||||
// The plugin MCP snapshot is global, so it is fine to reuse across cwds
|
||||
// for the merge.
|
||||
let plugin_snapshot = ctx.plugin_handle.snapshot();
|
||||
|
||||
for target in ctx.targets {
|
||||
@@ -336,7 +312,6 @@ async fn reload_project_servers_after_grant(ctx: ReloadAfterGrant<'_>) {
|
||||
let merged = crate::session::managed_mcp::merge_managed_mcp_servers(
|
||||
target.initial_client_mcp_servers,
|
||||
session_cwd,
|
||||
&managed,
|
||||
plugin_snapshot.as_deref(),
|
||||
ctx.compat,
|
||||
);
|
||||
@@ -375,7 +350,7 @@ async fn reload_project_servers_after_grant(ctx: ReloadAfterGrant<'_>) {
|
||||
ctx.prompt_cwd,
|
||||
crate::util::config::load_mcp_servers(ctx.prompt_cwd, ctx.compat),
|
||||
);
|
||||
crate::extensions::mcp::notify_servers_updated(ctx.gateway, &managed, &local).await;
|
||||
crate::extensions::mcp::notify_servers_updated(ctx.gateway, &local).await;
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
||||
@@ -124,7 +124,6 @@ pub(crate) struct SessionSpawnOptions<'a> {
|
||||
crate::session::announcement_state::AnnouncementState,
|
||||
>,
|
||||
pub session_meta: Option<&'a acp::Meta>,
|
||||
pub managed_mcp_expires_at: Option<chrono::DateTime<chrono::Utc>>,
|
||||
pub model_agent_type: Option<&'a str>,
|
||||
pub session_model_id: acp::ModelId,
|
||||
pub session_yolo_mode: bool,
|
||||
@@ -260,7 +259,6 @@ pub(crate) fn chat_session_spawn_options<'a>(
|
||||
persisted_goal_mode: None,
|
||||
persisted_announcement_state: None,
|
||||
session_meta,
|
||||
managed_mcp_expires_at: None,
|
||||
model_agent_type,
|
||||
session_model_id,
|
||||
session_yolo_mode,
|
||||
@@ -591,8 +589,6 @@ pub struct MvpAgent {
|
||||
/// Local config.toml override for session registry (`[cli] session_registry`).
|
||||
/// `Some(true)` enables, `Some(false)` disables, `None` defers to remote settings.
|
||||
session_registry_local: Option<bool>,
|
||||
/// Managed MCP configs and gateway tool catalog; lazily fetched.
|
||||
managed_mcp_cache: crate::session::managed_mcp::ManagedMcpStateHandle,
|
||||
/// Agent-level MCP server state. LEADER-SAFE(shared): MCP servers are
|
||||
/// agent-scoped, not per-client.
|
||||
agent_mcp_state: std::sync::Arc<
|
||||
|
||||
@@ -304,7 +304,6 @@ impl MvpAgent {
|
||||
session_env,
|
||||
parent_attribution_callback,
|
||||
parent_agent_name,
|
||||
parent_managed_mcp_proxy_base_url,
|
||||
) = {
|
||||
let sessions = self.sessions.borrow();
|
||||
let ps = sessions.get(&parent_sid);
|
||||
@@ -339,7 +338,6 @@ impl MvpAgent {
|
||||
.unwrap_or_else(|| std::sync::Arc::new(std::collections::HashMap::new())),
|
||||
ps.and_then(|h| h.attribution_callback.clone()),
|
||||
ps.map(|h| h.agent_name.clone()),
|
||||
ps.map(|h| h.managed_mcp_proxy_base_url.clone()),
|
||||
)
|
||||
};
|
||||
let (
|
||||
@@ -401,8 +399,6 @@ impl MvpAgent {
|
||||
gateway: self.gateway.clone(),
|
||||
client_hooks: Default::default(),
|
||||
sampling_config: self.sampling_config.borrow().clone(),
|
||||
managed_mcp_proxy_base_url: parent_managed_mcp_proxy_base_url
|
||||
.unwrap_or_else(|| self.coding_api_base_url()),
|
||||
alpha_test_key: self.alpha_test_key(),
|
||||
auth_method_id: self
|
||||
.auth_method_id
|
||||
@@ -429,8 +425,6 @@ impl MvpAgent {
|
||||
memory_config: self.memory_config.clone(),
|
||||
web_search_config: self.prepare_web_search_config(),
|
||||
web_fetch_config: self.prepare_web_fetch_config(),
|
||||
image_gen_config: self.prepare_image_gen_config(),
|
||||
video_gen_config: self.prepare_video_gen_config(),
|
||||
app_builder_deployer_config: self.prepare_app_builder_deployer_config(),
|
||||
write_file_enabled: self.cfg.borrow().resolve_write_file().value,
|
||||
goal_enabled: self.cfg.borrow().resolve_goal().value,
|
||||
@@ -505,7 +499,6 @@ impl MvpAgent {
|
||||
.map(|h| h.mcp_servers.clone())
|
||||
.unwrap_or_default()
|
||||
},
|
||||
managed_mcp_state: self.managed_mcp_cache.clone(),
|
||||
parent_mcp_pool: None,
|
||||
parent_tool_snapshot: None,
|
||||
parent_skills: None,
|
||||
|
||||
@@ -648,8 +648,6 @@ async fn file_toolset_override_e2e_to_finalized_toolset() {
|
||||
web_search_config: kigi_tools::implementations::web_search::WebSearchConfig::default(),
|
||||
web_fetch_config: Default::default(),
|
||||
lsp: None,
|
||||
image_gen_config: kigi_tools::implementations::grok_build::image_gen::ImageGenConfig::default(),
|
||||
video_gen_config: kigi_tools::implementations::grok_build::video_gen::VideoGenConfig::default(),
|
||||
app_builder_deployer_config: kigi_tools::implementations::grok_build::deploy_app::AppBuilderDeployerConfig::default(),
|
||||
api_key_provider: None,
|
||||
attribution_callback: None,
|
||||
@@ -744,7 +742,6 @@ fn make_test_handle(
|
||||
permission_handle: kigi_workspace::permission::PermissionHandle::allow_all(),
|
||||
attribution_callback: None,
|
||||
agent_name: "grok-build".to_string(),
|
||||
managed_mcp_proxy_base_url: String::new(),
|
||||
session_default_agent_profile: None,
|
||||
allowed_subagent_types: None,
|
||||
hook_registry: None,
|
||||
@@ -1136,38 +1133,6 @@ fn test_sessionless_request_requires_session_id() {
|
||||
"cwd-only requests with no sessionId must return SessionRequired"
|
||||
);
|
||||
}
|
||||
#[tokio::test(flavor = "current_thread")]
|
||||
async fn ext_method_routes_auth_cleared_and_refreshes_resident_sessions() {
|
||||
let local = tokio::task::LocalSet::new();
|
||||
local
|
||||
.run_until(async {
|
||||
let agent = build_agent_with_auth(crate::auth::KimiAuth {
|
||||
key: "eligible".into(),
|
||||
auth_mode: crate::auth::AuthMode::OAuth,
|
||||
..crate::auth::KimiAuth::test_default()
|
||||
});
|
||||
use acp::Agent as _;
|
||||
agent.managed_mcp_cache.lock().await.enable_gateway_tools();
|
||||
let sid = acp::SessionId::new("sess-auth-cleared");
|
||||
let (handle, _tx, mut cmd_rx) = make_live_session_handle(&sid, None);
|
||||
agent.sessions.borrow_mut().insert(sid, handle);
|
||||
let params = serde_json::json!({});
|
||||
agent
|
||||
.ext_method(acp::ExtRequest::new(
|
||||
"x.ai/internal/auth_cleared",
|
||||
std::sync::Arc::from(serde_json::value::to_raw_value(¶ms).unwrap()),
|
||||
))
|
||||
.await
|
||||
.expect("auth_cleared must route through session-admin");
|
||||
let cmd = tokio::time::timeout(std::time::Duration::from_secs(1), cmd_rx.recv())
|
||||
.await
|
||||
.expect("refresh command should be sent")
|
||||
.expect("channel should stay open until command is received");
|
||||
assert!(matches!(cmd, SessionCommand::RefreshMcpSearchIndex));
|
||||
assert!(!agent.managed_mcp_cache.lock().await.gateway_tools_active);
|
||||
})
|
||||
.await;
|
||||
}
|
||||
/// Build a minimal MvpAgent suitable for testing extension methods.
|
||||
fn build_minimal_agent_for_tests() -> MvpAgent {
|
||||
use crate::agent::config::Config as AgentConfig;
|
||||
@@ -1860,7 +1825,7 @@ async fn auth_type_session_based_no_current_returns_session_token() {
|
||||
/// BYOK guard. Users with `xai.api_key` must continue to report `ApiKey`
|
||||
/// regardless of live-token state -- BYOK sessions have nothing to refresh,
|
||||
/// and reporting `SessionToken` would route through cli-chat-proxy paths
|
||||
/// (image_gen / video_gen base_url) that don't apply to BYOK keys.
|
||||
/// that don't apply to BYOK keys.
|
||||
#[tokio::test(flavor = "current_thread")]
|
||||
async fn auth_type_xai_api_key_no_current_returns_api_key() {
|
||||
let agent = build_minimal_agent_for_tests();
|
||||
@@ -1952,26 +1917,6 @@ async fn cached_token_fallthrough_falls_to_grok_com_without_credentials() {
|
||||
"no API-key creds and no kill switch -> interactive grok.com login",
|
||||
);
|
||||
}
|
||||
/// The imagine tier gate fails **open**: with no resolved auth we can't confirm
|
||||
/// a restricted personal tier, so the tools stay advertised and un-flagged (the
|
||||
/// server 429 remains the authoritative backstop). Guards against accidentally
|
||||
/// disabling a paid feature when tier info hasn't loaded.
|
||||
#[tokio::test(flavor = "current_thread")]
|
||||
async fn prepare_image_gen_config_fails_open_without_auth() {
|
||||
use kigi_tools::implementations::grok_build::image_gen::ImageGenConfig;
|
||||
let agent = build_minimal_agent_for_tests();
|
||||
agent.sampling_config.borrow_mut().api_key = Some("test-key".to_string());
|
||||
let ImageGenConfig::Enabled {
|
||||
tier_restricted, ..
|
||||
} = agent.prepare_image_gen_config()
|
||||
else {
|
||||
panic!("expected Enabled");
|
||||
};
|
||||
assert!(
|
||||
!tier_restricted,
|
||||
"no resolved auth ⇒ fail open (tools not tier-restricted)"
|
||||
);
|
||||
}
|
||||
/// `parse_session_kind` routes `session/load` to the gateway Chat path vs. the
|
||||
/// disk-backed Build path. Anything but an explicit `kind: "chat"` is Build.
|
||||
#[test]
|
||||
@@ -2107,7 +2052,6 @@ fn chat_session_spawn_options_matches_thin_profile() {
|
||||
assert!(!opts.client_fs_read);
|
||||
assert!(!opts.client_fs_write);
|
||||
assert!(opts.chat_history.is_empty());
|
||||
assert!(opts.managed_mcp_expires_at.is_none());
|
||||
assert!(!opts.session_auto_mode);
|
||||
assert!(
|
||||
opts.persistence.is_noop(),
|
||||
|
||||
@@ -1054,9 +1054,6 @@ pub(crate) async fn handle_subagent_request(
|
||||
ctx.memory_config.clone()
|
||||
},
|
||||
Default::default(),
|
||||
ctx.managed_mcp_state.clone(),
|
||||
None,
|
||||
ctx.managed_mcp_proxy_base_url.clone(),
|
||||
effective_model_id,
|
||||
ctx.yolo_mode
|
||||
|| matches!(
|
||||
@@ -1069,8 +1066,6 @@ pub(crate) async fn handle_subagent_request(
|
||||
None,
|
||||
ctx.web_search_config.clone(),
|
||||
ctx.web_fetch_config.clone(),
|
||||
ctx.image_gen_config.clone(),
|
||||
ctx.video_gen_config.clone(),
|
||||
ctx.app_builder_deployer_config.clone(),
|
||||
ctx.write_file_enabled,
|
||||
ctx.goal_enabled,
|
||||
|
||||
@@ -144,7 +144,6 @@ pub(crate) struct SubagentSpawnContext {
|
||||
/// context is built (an async snapshot from the parent session actor).
|
||||
pub client_hooks: crate::extensions::hooks::ClientHooks,
|
||||
pub sampling_config: kigi_sampler::SamplerConfig,
|
||||
pub managed_mcp_proxy_base_url: String,
|
||||
/// The staging auth header value propagated from the parent. Used
|
||||
/// when materialising subagent `SamplerConfig`s for auth-flow tracking
|
||||
/// and for `inject_url_derived_headers` in the construction helpers.
|
||||
@@ -209,10 +208,6 @@ pub(crate) struct SubagentSpawnContext {
|
||||
pub web_search_config: kigi_tools::implementations::WebSearchConfig,
|
||||
/// Resolved config for web fetch.
|
||||
pub web_fetch_config: kigi_tools::implementations::grok_build::web_fetch::WebFetchConfig,
|
||||
/// Image generation config (parent-inherited).
|
||||
pub image_gen_config: kigi_tools::implementations::grok_build::image_gen::ImageGenConfig,
|
||||
/// Resolved config for video generation.
|
||||
pub video_gen_config: kigi_tools::implementations::grok_build::video_gen::VideoGenConfig,
|
||||
/// Resolved config for the deploy service.
|
||||
pub app_builder_deployer_config:
|
||||
kigi_tools::implementations::grok_build::deploy_app::AppBuilderDeployerConfig,
|
||||
@@ -320,8 +315,6 @@ pub(crate) struct SubagentSpawnContext {
|
||||
/// time). Servers added later via `UpdateMcpServers` (managed MCPs, plugin reload)
|
||||
/// will not appear here. Named references only resolve against the initial config.
|
||||
pub parent_mcp_configs: Vec<agent_client_protocol::McpServer>,
|
||||
/// Parent's managed MCP state handle (Arc-shared, no re-fetch).
|
||||
pub managed_mcp_state: crate::session::managed_mcp::ManagedMcpStateHandle,
|
||||
/// Snapshot of the parent session's MCP client pool at spawn time.
|
||||
pub parent_mcp_pool: Option<crate::session::mcp_servers::SharedMcpPool>,
|
||||
/// Snapshot of the parent session's resolved tool schema at spawn time.
|
||||
|
||||
@@ -143,16 +143,6 @@ fn subagent_inherits_parent_lsp_via_context() {
|
||||
);
|
||||
}
|
||||
#[test]
|
||||
fn subagent_inherits_managed_mcp_state_via_context() {
|
||||
let handle = crate::session::managed_mcp::ManagedMcpStateHandle::default();
|
||||
let mut ctx = ctx_with_toggle(HashMap::new());
|
||||
ctx.managed_mcp_state = handle.clone();
|
||||
assert!(
|
||||
Arc::ptr_eq(& handle, & ctx.managed_mcp_state),
|
||||
"child should share parent's managed MCP state (Arc identity)"
|
||||
);
|
||||
}
|
||||
#[test]
|
||||
fn no_parent_lsp_means_child_gets_none() {
|
||||
let ctx = ctx_with_toggle(HashMap::new());
|
||||
assert!(ctx.lsp.is_none());
|
||||
@@ -1110,7 +1100,6 @@ fn dummy_tracker(
|
||||
permission_handle: kigi_workspace::permission::PermissionHandle::allow_all(),
|
||||
attribution_callback: None,
|
||||
agent_name: "grok-build".to_string(),
|
||||
managed_mcp_proxy_base_url: String::new(),
|
||||
session_default_agent_profile: None,
|
||||
allowed_subagent_types: None,
|
||||
hook_registry: None,
|
||||
|
||||
Reference in New Issue
Block a user