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:
@@ -1897,13 +1897,6 @@ fn tool_call_to_block(tc: &acp::ToolCall, session_cwd: Option<&Path>) -> RenderB
|
||||
}
|
||||
RenderBlock::ToolCall(ToolCallBlock::UseTool(block))
|
||||
}
|
||||
_ if matches!(
|
||||
extract_raw_field(tc, "variant").as_deref(),
|
||||
Some("ImageGen") | Some("ImageToVideo") | Some("ReferenceToVideo") | Some("ImageEdit")
|
||||
) =>
|
||||
{
|
||||
media_gen_block(tc, success)
|
||||
}
|
||||
_ if tc.title.starts_with("Memory search:") => {
|
||||
let query = tc
|
||||
.title
|
||||
@@ -2007,45 +2000,6 @@ fn tool_call_title(tc: &acp::ToolCall) -> Cow<'_, str> {
|
||||
Cow::Borrowed(&tc.title)
|
||||
}
|
||||
}
|
||||
/// Build the media block from the typed `raw_output` path.
|
||||
fn media_gen_block(tc: &acp::ToolCall, success: bool) -> RenderBlock {
|
||||
let mut block = OtherToolCallBlock::new(tool_call_title(tc), String::new());
|
||||
if !success {
|
||||
let err = content_text(tc);
|
||||
block.error = Some(if err.is_empty() { "Failed".into() } else { err });
|
||||
} else if let Some((path, is_video)) = media_gen_ref(tc) {
|
||||
block = block.with_media_ref(path, is_video);
|
||||
} else if let Some(text) = media_gen_text(tc) {
|
||||
block.set_output_text(text);
|
||||
}
|
||||
RenderBlock::ToolCall(ToolCallBlock::Other(block))
|
||||
}
|
||||
/// Plain-text body of a media-variant tool that returned `ToolOutput::Text`
|
||||
/// rather than a media file (the free / X Basic SuperGrok-upsell short-circuit).
|
||||
/// `None` for real media outputs — including ZDR upload-only results — so their
|
||||
/// typed rendering is untouched.
|
||||
fn media_gen_text(tc: &acp::ToolCall) -> Option<String> {
|
||||
match serde_json::from_value::<ToolOutput>(tc.raw_output.clone()?).ok()? {
|
||||
ToolOutput::Text(t) => (!t.text.is_empty()).then_some(t.text),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
/// Local `(path, is_video)` from typed `raw_output`.
|
||||
///
|
||||
/// Returns `None` when `raw_output` is missing/unparseable, not a media
|
||||
/// variant, or has no openable local file (ZDR `uploaded_url` / empty path).
|
||||
fn media_gen_ref(tc: &acp::ToolCall) -> Option<(std::path::PathBuf, bool)> {
|
||||
let (media, is_video) =
|
||||
match serde_json::from_value::<ToolOutput>(tc.raw_output.clone()?).ok()? {
|
||||
ToolOutput::ImageGen(m) | ToolOutput::ImageEdit(m) => (m, false),
|
||||
ToolOutput::ImageToVideo(m) | ToolOutput::ReferenceToVideo(m) => (m, true),
|
||||
_ => return None,
|
||||
};
|
||||
if media.uploaded_url.is_some() || media.path.as_os_str().is_empty() {
|
||||
return None;
|
||||
}
|
||||
Some((media.path, is_video))
|
||||
}
|
||||
/// Extract text content from a ContentBlock.
|
||||
fn extract_text_from_content(content: &acp::ContentBlock) -> String {
|
||||
match content {
|
||||
@@ -5536,7 +5490,7 @@ mod tests {
|
||||
}
|
||||
#[test]
|
||||
fn build_and_parse_tools_meta_round_trip() {
|
||||
let names = vec!["scheduler_create".to_string(), "image_gen".to_string()];
|
||||
let names = vec!["scheduler_create".to_string(), "web_search".to_string()];
|
||||
let wire = serde_json::json!({ "tools" : names });
|
||||
assert_eq!(parse_tools_meta(wire.as_object()), Some(names));
|
||||
}
|
||||
@@ -6421,112 +6375,4 @@ mod tests {
|
||||
);
|
||||
}
|
||||
}
|
||||
/// Every video ToolInput variant must route through `media_gen_block` so
|
||||
/// `[Open Video]` uses the typed `MediaGenOutput.path` (not a regex scrape
|
||||
/// of the JSON prompt text — fragile on Windows with %-encoded session dirs).
|
||||
#[test]
|
||||
fn video_tool_variants_use_typed_path_not_generic_scrape() {
|
||||
use crate::scrollback::block::BlockContent;
|
||||
let dir = tempfile::tempdir().unwrap();
|
||||
let video_path = dir.path().join("1.mp4");
|
||||
std::fs::write(&video_path, b"fake-mp4").unwrap();
|
||||
let cases: &[(&str, ToolOutput)] = &[
|
||||
(
|
||||
"ImageToVideo",
|
||||
ToolOutput::ImageToVideo(kigi_tools::types::output::MediaGenOutput::new(
|
||||
video_path.clone(),
|
||||
)),
|
||||
),
|
||||
(
|
||||
"ReferenceToVideo",
|
||||
ToolOutput::ReferenceToVideo(kigi_tools::types::output::MediaGenOutput::new(
|
||||
video_path.clone(),
|
||||
)),
|
||||
),
|
||||
];
|
||||
for (variant, output) in cases {
|
||||
let tc = acp::ToolCall::new(
|
||||
acp::ToolCallId::new(Arc::from(format!("media-{variant}"))),
|
||||
variant.to_string(),
|
||||
)
|
||||
.kind(acp::ToolKind::Other)
|
||||
.status(acp::ToolCallStatus::Completed)
|
||||
.content(vec![])
|
||||
.raw_input(Some(serde_json::json!({ "variant" : variant })))
|
||||
.raw_output(serde_json::to_value(output).ok())
|
||||
.locations(vec![]);
|
||||
let block = tool_call_to_block(&tc, None);
|
||||
let open_path = block
|
||||
.inline_open_button()
|
||||
.map(|(p, is_video)| {
|
||||
assert!(is_video, "{variant}: expected video open button");
|
||||
p
|
||||
})
|
||||
.or_else(|| block.video_references().first().map(|r| r.path.clone()))
|
||||
.unwrap_or_else(|| panic!("{variant}: missing media ref / open button"));
|
||||
assert_eq!(
|
||||
open_path, video_path,
|
||||
"{variant}: open path must be the typed MediaGenOutput.path"
|
||||
);
|
||||
}
|
||||
}
|
||||
#[test]
|
||||
fn media_gen_ref_skips_uploaded_only_video() {
|
||||
let output = ToolOutput::ImageToVideo(kigi_tools::types::output::MediaGenOutput::uploaded(
|
||||
"https://bucket.example/videos/x.mp4".into(),
|
||||
));
|
||||
let tc = acp::ToolCall::new(
|
||||
acp::ToolCallId::new(Arc::from("zdr-upload")),
|
||||
"image_to_video",
|
||||
)
|
||||
.kind(acp::ToolKind::Other)
|
||||
.status(acp::ToolCallStatus::Completed)
|
||||
.content(vec![])
|
||||
.raw_input(Some(serde_json::json!({ "variant" : "ImageToVideo" })))
|
||||
.raw_output(serde_json::to_value(output).ok())
|
||||
.locations(vec![]);
|
||||
assert!(
|
||||
media_gen_ref(&tc).is_none(),
|
||||
"uploaded_url-only media must not claim a local open path"
|
||||
);
|
||||
}
|
||||
/// A tier-restricted (free / X Basic) imagine call short-circuits with the
|
||||
/// SuperGrok upsell as `ToolOutput::Text` on a `Completed` status. The media
|
||||
/// renderer has no file to open, so it must surface the upsell text in the
|
||||
/// card body (not a bare title) and must NOT mark the card as an error.
|
||||
#[test]
|
||||
fn tier_restricted_media_shows_upsell_text_not_error() {
|
||||
let upsell = "Image generation is a SuperGrok feature. Upgrade at \
|
||||
https://grok.com/supergrok?referrer=grok-build";
|
||||
let output = ToolOutput::Text(kigi_tools::types::output::TextOutput::from(upsell));
|
||||
let tc = acp::ToolCall::new(
|
||||
acp::ToolCallId::new(Arc::from("tier-restricted-img")),
|
||||
"image_gen",
|
||||
)
|
||||
.kind(acp::ToolKind::Other)
|
||||
.status(acp::ToolCallStatus::Completed)
|
||||
.content(vec![acp::ToolCallContent::Content(acp::Content::new(
|
||||
acp::ContentBlock::Text(acp::TextContent::new(upsell)),
|
||||
))])
|
||||
.raw_input(Some(serde_json::json!({ "variant" : "ImageGen" })))
|
||||
.raw_output(serde_json::to_value(output).ok())
|
||||
.locations(vec![]);
|
||||
let RenderBlock::ToolCall(ToolCallBlock::Other(block)) = tool_call_to_block(&tc, None)
|
||||
else {
|
||||
panic!("expected an Other tool-call block");
|
||||
};
|
||||
assert!(
|
||||
block.is_success(),
|
||||
"the upsell is a successful result, not an error"
|
||||
);
|
||||
assert!(
|
||||
block
|
||||
.output
|
||||
.as_deref()
|
||||
.unwrap_or_default()
|
||||
.contains("SuperGrok"),
|
||||
"upsell text must be shown in the card body, got: {:?}",
|
||||
block.output
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user