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:
2026-07-17 23:45:05 -04:00
parent fa75eb139a
commit 5e4e24db99
120 changed files with 301 additions and 11327 deletions
@@ -50,9 +50,7 @@
tools: Vec::new(),
enabled: true,
source: "local".into(),
wire_source: crate::views::mcps_modal::McpWireSource::Local,
plugin_name: None,
is_managed_gateway: false,
},
]));
}
@@ -520,9 +518,7 @@
tools: Vec::new(),
enabled: true,
source: "local".into(),
wire_source: crate::views::mcps_modal::McpWireSource::Local,
plugin_name: None,
is_managed_gateway: false,
},
]));
}
@@ -1818,7 +1818,7 @@ pub(super) fn make_mcps_modal_with_servers(
state
}
pub(super) fn seed_owner_agent_with_open_modal(app: &mut AppView) {
use crate::views::mcps_modal::{McpServerDisplayStatus, McpServerInfo, McpWireSource};
use crate::views::mcps_modal::{McpServerDisplayStatus, McpServerInfo};
let owner = app.agents.get_mut(&AgentId(0)).expect("owner present");
owner.extensions_modal = Some(
make_mcps_modal_with_servers(
@@ -1826,8 +1826,7 @@ pub(super) fn seed_owner_agent_with_open_modal(app: &mut AppView) {
McpServerInfo { name : "alpha".into(), display_name : None, status :
McpServerDisplayStatus::Initializing, tool_count : 0, auth_required :
false, tools : Vec::new(), enabled : true, source : "local".into(),
wire_source : McpWireSource::Local, plugin_name : None,
is_managed_gateway : false, }
plugin_name : None, }
],
),
);
@@ -58,8 +58,6 @@ pub enum Action {
ExitSessionConfirmed,
/// Open an arbitrary URL in the system browser (with scheme validation).
OpenUrl(String),
/// Open grok.com managed connectors, appending session teamId when set.
OpenManagedConnectors,
/// Cycle to the next visible link (or highlight the first if none selected).
OpenNextLink,
/// Cycle to the previous visible link.
+3 -3
View File
@@ -100,7 +100,7 @@ impl QueuedPrompt {
/// `true` for plain rows (no `wire_blocks`) and for raw skill slash rows
/// (`/find-session args` — a single Text block equal to `text`, expanded
/// shell-side at delivery), so interjecting `text` loses nothing. `false`
/// when the payload was expanded client-side (`/imagine`, `/loop`):
/// when the payload was expanded client-side (`/loop`):
/// interjecting those by `text` would drop the expansion, and by payload
/// would render the raw instruction.
pub fn wire_matches_display(&self) -> bool {
@@ -1272,7 +1272,7 @@ mod tests {
}
/// `wire_matches_display` splits interjectable rows (no payload, or a raw
/// skill slash payload equal to the display text) from client-expanded
/// payloads (`/imagine`, `/loop`) that must run as their own turn.
/// payloads (`/loop`) that must run as their own turn.
#[test]
fn wire_matches_display_classifies_payload_shapes() {
let text_block = |t: &str| acp::ContentBlock::Text(acp::TextContent::new(t.to_string()));
@@ -1285,7 +1285,7 @@ mod tests {
assert!(raw_skill.wire_matches_display(), "raw slash payload");
let expanded = QueuedPrompt {
wire_blocks: Some(vec![text_block("<skill>body</skill>")]),
..QueuedPrompt::plain(3, "/imagine cat", QueueEntryKind::Prompt)
..QueuedPrompt::plain(3, "/loop cat", QueueEntryKind::Prompt)
};
assert!(!expanded.wire_matches_display(), "expanded payload");
let multi_block = QueuedPrompt {
@@ -7,7 +7,7 @@ use super::test_fixtures;
use crate::app::actions::Action;
use crate::app::app_view::InputOutcome;
use crate::views::file_search::line_viewer::LineViewerState;
use crossterm::event::{KeyCode, KeyEvent, KeyModifiers};
use crossterm::event::{KeyCode, KeyEvent};
impl AgentView {
// -- Agents modal input handling --
@@ -227,19 +227,6 @@ impl AgentView {
return self.handle_modal_input_key(key);
}
// Ctrl+O opens grok.com connectors on the MCP servers tab.
if key.code == KeyCode::Char('o')
&& key.modifiers == KeyModifiers::CONTROL
&& self.extensions_modal.as_ref().is_some_and(|s| {
s.active_tab == crate::views::extensions_modal::ExtensionsTab::McpServers
&& !s.picker_state.search_active
})
{
return self.execute_modal_button_action(
crate::views::extensions_modal::ButtonAction::OpenManagedConnectors,
);
}
// Route chrome keys through ModalWindow first (mirrors the mouse path).
// Handles Esc -> CloseRequested and h/l (or L/R when not tabs-focused)
// -> fold outcomes when FoldInfo provided.
@@ -768,22 +755,6 @@ impl AgentView {
&config,
);
// Open the connectors URL on mouse-down (parity with Ctrl+O). A section-row
// click routes as Selected or NonSelectableClick, so intercept both here.
let clicked_entry = match &outcome {
crate::views::picker::PickerOutcome::Selected(i)
| crate::views::picker::PickerOutcome::Expand(i)
| crate::views::picker::PickerOutcome::NonSelectableClick(i) => Some(*i),
_ => None,
};
if let Some(idx) = clicked_entry
&& self.extensions_modal_click_opens_connectors(idx, mouse.row)
{
return self.execute_modal_button_action(
crate::views::extensions_modal::ButtonAction::OpenManagedConnectors,
);
}
// Hover states are managed by ModalWindow (close) and picker (filter).
match outcome {
@@ -864,20 +835,6 @@ impl AgentView {
state.picker_state.scroll_offset = None;
}
/// Whether a click at `mouse_row` on entry `entry_idx` hit the connectors URL
/// link band recorded at last paint (opens the URL instead of folding).
fn extensions_modal_click_opens_connectors(&self, entry_idx: usize, mouse_row: u16) -> bool {
self.extensions_modal.as_ref().is_some_and(|state| {
// Parity with the Ctrl+O guard: don't open while the search bar has focus.
!state.picker_state.search_active
&& state
.picker_state
.link_band
.as_ref()
.is_some_and(|(idx, band)| *idx == entry_idx && band.contains(&mouse_row))
})
}
/// Non-selectable mask for the extensions modal picker (from last render).
fn extensions_modal_non_selectable_mask(
state: &crate::views::extensions_modal::ExtensionsModalState,
@@ -1094,9 +1051,6 @@ impl AgentView {
&& let Some(idx) = state.selected_data_index()
&& let Some(server) = servers.get(idx)
{
if server.is_managed_gateway {
return InputOutcome::Action(Action::OpenManagedConnectors);
}
// Drop repeats while an action is in flight on the same
// row to avoid double-spawning the OAuth browser flow.
let sel = state.picker_state.selected;
@@ -1120,9 +1074,6 @@ impl AgentView {
InputOutcome::Action(Action::ReloadSkills)
}
ButtonAction::RefreshMcpList => InputOutcome::Action(Action::RefreshMcpList),
ButtonAction::OpenManagedConnectors => {
InputOutcome::Action(Action::OpenManagedConnectors)
}
ButtonAction::ToggleSelectedMcpServer => {
if let Some(ref mut state) = self.extensions_modal {
use crate::views::extensions_modal::TabDataState;
@@ -1179,29 +1130,15 @@ impl AgentView {
ButtonAction::RemoveSelectedMcpServer => {
let resolved = self.extensions_modal.as_ref().and_then(|state| {
use crate::views::extensions_modal::TabDataState;
use crate::views::mcps_modal::is_removable;
let TabDataState::Loaded(ref servers) = state.mcps_data else {
return None;
};
let idx = state.selected_data_index()?;
let server = servers.get(idx)?;
if is_removable(server) {
Some(Ok(server.name.clone()))
} else {
Some(Err(server.name.clone()))
}
Some(server.name.clone())
});
match resolved {
Some(Err(name)) => {
if let Some(ref mut s) = self.extensions_modal {
s.modal_message =
Some(crate::views::extensions_modal::ModalMessage::Error(
format!("Cannot remove managed server '{name}'"),
));
}
InputOutcome::Changed
}
Some(Ok(server_name)) => {
Some(server_name) => {
if let Some(ref mut s) = self.extensions_modal {
s.pending_action = Some("removing...".into());
s.pending_entry_index = Some(s.picker_state.selected);
@@ -1629,148 +1566,3 @@ mod extensions_modal_search_key_tests {
assert!(state.picker_state.search_active);
}
}
#[cfg(test)]
mod connectors_url_click_tests {
use super::AgentView;
use crate::app::actions::Action;
use crate::app::app_view::InputOutcome;
use crate::views::extensions_modal::{
ExtensionsModalState, ExtensionsTab, TabDataState, render_extensions_modal,
};
use crate::views::mcps_modal::{McpServerDisplayStatus, McpServerInfo, McpWireSource};
use crossterm::event::{KeyModifiers, MouseButton, MouseEvent, MouseEventKind};
use ratatui::buffer::Buffer;
use ratatui::layout::Rect;
fn managed_server() -> McpServerInfo {
McpServerInfo {
name: "grok_com_linear".into(),
display_name: None,
status: McpServerDisplayStatus::Ready,
tool_count: 0,
auth_required: false,
tools: vec![],
enabled: true,
source: "managed".into(),
wire_source: McpWireSource::Managed,
plugin_name: None,
is_managed_gateway: false,
}
}
// Build an agent whose extensions modal shows an expanded Managed section,
// then paint it so `hit_areas` + `link_band` reflect the real layout.
fn rendered_agent() -> AgentView {
let mut agent = super::test_fixtures::make_agent();
let mut state = ExtensionsModalState::new(ExtensionsTab::McpServers);
state.mcps_data = TabDataState::Loaded(vec![managed_server()]);
agent.extensions_modal = Some(state);
let area = Rect::new(0, 0, 100, 40);
let mut buf = Buffer::empty(area);
render_extensions_modal(
&mut buf,
area,
agent.extensions_modal.as_mut().unwrap(),
None,
false,
0,
);
agent
}
fn left_down(column: u16, row: u16) -> MouseEvent {
MouseEvent {
kind: MouseEventKind::Down(MouseButton::Left),
column,
row,
modifiers: KeyModifiers::NONE,
}
}
// (column inside the Managed row, its recorded URL band) from the last paint.
fn managed_url_hit(agent: &AgentView) -> (u16, std::ops::Range<u16>) {
let state = agent.extensions_modal.as_ref().unwrap();
let (entry_idx, band) = state
.picker_state
.link_band
.clone()
.expect("expanded Managed section records a connectors URL band");
let hit = state.picker_state.hit_areas.as_ref().unwrap();
let pos = hit
.entry_indices
.iter()
.position(|&e| e == entry_idx)
.unwrap();
(hit.item_rects[pos].x + 2, band)
}
#[test]
fn mouse_down_on_url_row_opens_connectors() {
let mut agent = rendered_agent();
let (col, band) = managed_url_hit(&agent);
let outcome = agent.handle_extensions_modal_mouse(&left_down(col, band.start));
assert!(matches!(
outcome,
InputOutcome::Action(Action::OpenManagedConnectors)
));
// The section stays expanded (opened, did not fold).
assert!(
!agent
.extensions_modal
.as_ref()
.unwrap()
.mcps_collapsed_sections
.contains("mcp-section:managed")
);
}
#[test]
fn mouse_down_on_label_row_folds_not_opens() {
let mut agent = rendered_agent();
let (col, label_row) = {
let state = agent.extensions_modal.as_ref().unwrap();
let (entry_idx, _band) = state.picker_state.link_band.clone().unwrap();
let hit = state.picker_state.hit_areas.as_ref().unwrap();
let pos = hit
.entry_indices
.iter()
.position(|&e| e == entry_idx)
.unwrap();
let rect = hit.item_rects[pos];
(rect.x + 2, rect.y) // first row of the item rect is the fold-toggle label
};
let outcome = agent.handle_extensions_modal_mouse(&left_down(col, label_row));
assert!(!matches!(
outcome,
InputOutcome::Action(Action::OpenManagedConnectors)
));
// Fold happened: the Managed section is now collapsed.
assert!(
agent
.extensions_modal
.as_ref()
.unwrap()
.mcps_collapsed_sections
.contains("mcp-section:managed")
);
}
#[test]
fn mouse_down_on_url_row_while_searching_does_not_open() {
// Parity with the Ctrl+O guard: opening is suppressed while search is active.
let mut agent = rendered_agent();
let (col, band) = managed_url_hit(&agent);
agent
.extensions_modal
.as_mut()
.unwrap()
.picker_state
.search_active = true;
let outcome = agent.handle_extensions_modal_mouse(&left_down(col, band.start));
assert!(!matches!(
outcome,
InputOutcome::Action(Action::OpenManagedConnectors)
));
}
}
@@ -498,7 +498,7 @@ impl AgentView {
/// can't be resolved. Prompt-like rows may interject: plain prompts, plus
/// raw skill slash rows (`/find-session args`) whose wire payload IS the
/// display text — the shell expands those at the interjection drain. Rows
/// with a client-expanded payload (`/imagine`, `/loop`) and non-prompt
/// with a client-expanded payload (`/loop`) and non-prompt
/// kinds stay queued: interjecting them would send the display text, not
/// the payload.
pub(in crate::app) fn queue_row_prompt_like(&self, id: u64) -> Option<bool> {
@@ -1309,13 +1309,13 @@ mod queue_edit_routing_tests {
);
}
/// A client-expanded row (`/imagine`-shaped: wire payload != display
/// A client-expanded row (`/loop`-shaped: wire payload != display
/// text) stays queued — interjecting it would send the display text,
/// not the payload.
#[test]
fn force_interject_local_expanded_row_keeps_it_queued() {
let mut agent =
running_agent_with_local_skill("/imagine a cat", "<expanded imagine instructions>");
running_agent_with_local_skill("/loop 5m check", "<expanded loop instructions>");
let registry = non_vscode_registry();
let ids = agent.queue.entry_ids();
@@ -869,12 +869,6 @@ pub(crate) fn dispatch(action: Action, app: &mut AppView) -> Vec<Effect> {
}
vec![]
}
Action::OpenManagedConnectors => {
use crate::terminal::hyperlinks::SchemeFilter;
let url = crate::views::mcps_modal::managed_connectors_url(None);
crate::app::link_opener::open_url_if_safe(&url, SchemeFilter::Standard);
vec![]
}
Action::OpenNextLink => {
with_active_agent(app, |agent| agent.cycle_highlighted_link(true));
vec![]
@@ -130,7 +130,7 @@ fn unknown_non_restricted_command_still_passes_through() {
app.agents
.get_mut(&id)
.unwrap()
.set_restricted_commands(&["imagine".to_string()]);
.set_restricted_commands(&["loop".to_string()]);
let effects = dispatch(Action::SendPrompt("/frobnicate arg".into()), &mut app);
@@ -1212,7 +1212,6 @@ impl AgentView {
badge: "",
badge_color: None,
collapsible: false,
underline_last_desc: false,
fields: &[],
description_lines: &[],
summary_lines: &[],
@@ -1644,7 +1643,6 @@ impl AgentView {
badge: "",
badge_color: None,
collapsible: false,
underline_last_desc: false,
})
}
})
@@ -1716,7 +1714,6 @@ impl AgentView {
badge: "",
badge_color: None,
collapsible: false,
underline_last_desc: false,
})
})
.collect();
@@ -2008,7 +2005,6 @@ impl AgentView {
badge: if has_snippet { "match" } else { "" },
badge_color: Some(theme.accent_user),
collapsible: true,
underline_last_desc: false,
}));
non_sel_flags.push(false);
}