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:
@@ -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.
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user