F3: Kimi inference pipeline + full grok cloud-surface excision
Sampler / inference (PRD F3):
- kimi_compat.rs: single adaptation point for the Kimi chat/completions
dialect (thinking-field mapping, model_id stripping, empty-content
tool-call message fix, stream_options.include_usage), with kimi-cli
source citations
- Rate-limit handling reworked for Kimi/Moonshot semantics; UA kigi/{version}
- /models replaces the xAI models-v2 endpoint everywhere; idle model
refresh carries X-Msh-* device headers only (X-XAI-Token-Auth and
x-grok-client-mode/CLIENT_MODE_HEADER machinery deleted)
Cloud-surface excision (PRD §5, zero-egress):
- remote/ conversations lane, cli-chat-proxy-types crate, prod/ dir,
share command, credit bar: deleted (single local session lane;
paginate() replaces merge_and_paginate)
- Subscription/tier gate stack deleted end-to-end: AppView
gate/tier/team/ZDR fields, app/subscription.rs watch loop,
dispatch/billing.rs paywall + SuperGrok upsell, free-usage-exhausted
chain, tier-restricted commands, GateInfo, RemoteSettings gate fields,
SettingsUpdateNotification gate fields
- /privacy + coding-data-sharing setting deleted (backed by a dead xAI
RPC; Kigi is zero-egress — nothing to share or retain remotely)
Auth UX correctness (user-reported):
- Device-flow fixtures now mirror the live Kimi payload shape
(https://www.kimi.com/code/authorize_device?user_code=..., verified
against auth.kimi.com); the fabricated auth.kimi.com/device?code=...
URLs are gone
- open_browser_detached is a no-op under cfg(test): unit tests drove
wiremock fixture URLs into the real browser (root cause of the
"garbage mock link" ABCD-1234 tabs)
- Welcome/pager-minimal rebrand: Grok Build -> Kigi, grok.com ->
kimi.com, "Sign in to Grok" -> "Sign in to Kimi"
This commit is contained in:
@@ -50,7 +50,6 @@ const ALL_SETTINGS_EXERCISED: &[&str] = &[
|
||||
"scroll_lines",
|
||||
"invert_scroll",
|
||||
"display_refresh_auto_cadence",
|
||||
"coding_data_sharing",
|
||||
"default_selected_permission",
|
||||
"plan_mode",
|
||||
"show_tips",
|
||||
@@ -1608,7 +1607,6 @@ fn registry_kind_membership_through_pr_14() {
|
||||
vec![
|
||||
"auto_dark_theme",
|
||||
"auto_light_theme",
|
||||
"coding_data_sharing",
|
||||
"default_selected_permission",
|
||||
"hunk_tracker_mode",
|
||||
"keep_text_selection",
|
||||
@@ -1675,7 +1673,6 @@ fn enum_settings_membership_through_pr_14() {
|
||||
vec![
|
||||
"auto_dark_theme",
|
||||
"auto_light_theme",
|
||||
"coding_data_sharing",
|
||||
"default_selected_permission",
|
||||
"hunk_tracker_mode",
|
||||
"keep_text_selection",
|
||||
@@ -1736,7 +1733,6 @@ fn defaults_round_trip_through_registry() {
|
||||
"scroll_lines" => SettingValue::Int(3),
|
||||
"invert_scroll" => SettingValue::Bool(false),
|
||||
"display_refresh_auto_cadence" => SettingValue::Bool(false),
|
||||
"coding_data_sharing" => SettingValue::Enum("opt-in"),
|
||||
"default_selected_permission" => SettingValue::Enum("always_allow_all_sessions"),
|
||||
"hunk_tracker_mode" => SettingValue::Enum("agent_only"),
|
||||
"plan_mode" => SettingValue::Enum("off"),
|
||||
@@ -4380,382 +4376,6 @@ fn pr8_default_model_and_max_thoughts_width_defaults_roundtrip() {
|
||||
);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// coding_data_sharing (Privacy Enum, no preview — async ACP)
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/// `coding_data_sharing` lives under `Privacy`.
|
||||
#[test]
|
||||
fn pr9_coding_data_sharing_renders_under_privacy_category() {
|
||||
let reg = SettingsRegistry::defaults();
|
||||
let meta = reg
|
||||
.find("coding_data_sharing")
|
||||
.expect("coding_data_sharing must be registered");
|
||||
assert_eq!(
|
||||
meta.category,
|
||||
SettingCategory::Privacy,
|
||||
"coding_data_sharing must live under Privacy"
|
||||
);
|
||||
assert_eq!(
|
||||
meta.owner,
|
||||
SettingOwner::Shell,
|
||||
"coding_data_sharing is SHELL-owned (auth-metadata-backed, persists via ACP)"
|
||||
);
|
||||
}
|
||||
|
||||
/// `coding_data_sharing` must be `supports_preview: false` (async ACP).
|
||||
#[test]
|
||||
fn pr9_coding_data_sharing_does_not_support_preview() {
|
||||
let reg = SettingsRegistry::defaults();
|
||||
let meta = reg
|
||||
.find("coding_data_sharing")
|
||||
.expect("coding_data_sharing must be registered");
|
||||
match &meta.kind {
|
||||
SettingKind::Enum {
|
||||
supports_preview, ..
|
||||
} => {
|
||||
assert!(
|
||||
!supports_preview,
|
||||
"coding_data_sharing MUST be supports_preview: false — every preview \
|
||||
would fire an async ACP round-trip OR commit-on-every-nav, both \
|
||||
unacceptable",
|
||||
);
|
||||
}
|
||||
other => panic!("expected Enum kind for coding_data_sharing, got {other:?}"),
|
||||
}
|
||||
}
|
||||
|
||||
/// Reads from pager snapshot; inverts `_opt_out` bool.
|
||||
#[test]
|
||||
fn pr9_current_value_for_reads_pager_snapshot_inverts_opt_out() {
|
||||
use kigi_tui::settings::current_value_for;
|
||||
|
||||
let ui = UiConfig::default();
|
||||
|
||||
let opted_in_snap = PagerLocalSnapshot {
|
||||
coding_data_sharing_opt_out: false,
|
||||
..PagerLocalSnapshot::default()
|
||||
};
|
||||
let opted_out_snap = PagerLocalSnapshot {
|
||||
coding_data_sharing_opt_out: true,
|
||||
..PagerLocalSnapshot::default()
|
||||
};
|
||||
|
||||
assert_eq!(
|
||||
current_value_for("coding_data_sharing", &ui, &opted_in_snap),
|
||||
Some(SettingValue::Enum("opt-in")),
|
||||
"opt_out=false → canonical 'opt-in' (user IS sharing data)",
|
||||
);
|
||||
assert_eq!(
|
||||
current_value_for("coding_data_sharing", &ui, &opted_out_snap),
|
||||
Some(SettingValue::Enum("opt-out")),
|
||||
"opt_out=true → canonical 'opt-out' (user opted OUT of sharing)",
|
||||
);
|
||||
}
|
||||
|
||||
/// Enter opens picker seeded to current state.
|
||||
#[test]
|
||||
fn pr9_enter_on_coding_data_sharing_row_enters_picking_enum() {
|
||||
let mut s = make_state();
|
||||
navigate_to(&mut s, "coding_data_sharing");
|
||||
let outcome = handle_settings_key(&mut s, &press(KeyCode::Enter));
|
||||
assert!(
|
||||
matches!(outcome, SettingsKeyOutcome::Changed),
|
||||
"Enter on coding_data_sharing row must transition to PickingEnum, got {outcome:?}"
|
||||
);
|
||||
match &s.mode {
|
||||
SettingsModalMode::PickingEnum {
|
||||
key,
|
||||
original_value,
|
||||
..
|
||||
} => {
|
||||
assert_eq!(*key, "coding_data_sharing");
|
||||
assert_eq!(
|
||||
original_value,
|
||||
&SettingValue::Enum("opt-in"),
|
||||
"default snapshot opt_out=false → original 'opt-in'"
|
||||
);
|
||||
}
|
||||
other => panic!("expected PickingEnum mode, got {other:?}"),
|
||||
}
|
||||
}
|
||||
|
||||
/// Nav in picker must NOT dispatch preview (async ACP).
|
||||
#[test]
|
||||
fn pr9_coding_data_sharing_picker_nav_does_not_dispatch_preview() {
|
||||
for nav_key in &[
|
||||
KeyCode::Down,
|
||||
KeyCode::Char('j'),
|
||||
KeyCode::Up,
|
||||
KeyCode::Char('k'),
|
||||
] {
|
||||
let mut s = make_state();
|
||||
navigate_to(&mut s, "coding_data_sharing");
|
||||
let _ = handle_settings_key(&mut s, &press(KeyCode::Enter));
|
||||
assert!(matches!(s.mode, SettingsModalMode::PickingEnum { .. }));
|
||||
|
||||
if matches!(nav_key, KeyCode::Up | KeyCode::Char('k')) {
|
||||
let _ = handle_settings_key(&mut s, &press(KeyCode::Down));
|
||||
}
|
||||
|
||||
let outcome = handle_settings_key(&mut s, &press(*nav_key));
|
||||
assert!(
|
||||
matches!(outcome, SettingsKeyOutcome::Changed),
|
||||
"Nav key {nav_key:?} in coding_data_sharing picker MUST NOT dispatch a preview \
|
||||
Action — that would fire a network round-trip per keystroke. Got {outcome:?}",
|
||||
);
|
||||
assert!(matches!(s.mode, SettingsModalMode::PickingEnum { .. }));
|
||||
}
|
||||
}
|
||||
|
||||
/// Enter commits `SetCodingDataSharing { opted_in }` (opt-in→true).
|
||||
#[test]
|
||||
fn pr9_coding_data_sharing_picker_enter_dispatches_set_commit() {
|
||||
let reg = SettingsRegistry::defaults();
|
||||
let meta = reg.find("coding_data_sharing").unwrap();
|
||||
let (default_canonical, choices) = match &meta.kind {
|
||||
SettingKind::Enum {
|
||||
default, choices, ..
|
||||
} => (*default, *choices),
|
||||
_ => panic!("coding_data_sharing must be Enum"),
|
||||
};
|
||||
// Resolve "the other" canonical from the registry rather than
|
||||
// hardcoding — robust against future catalog additions.
|
||||
let other_canonical = choices
|
||||
.iter()
|
||||
.map(|c| c.canonical)
|
||||
.find(|c| *c != default_canonical)
|
||||
.expect("coding_data_sharing must have ≥2 choices");
|
||||
let expected_opted_in = match other_canonical {
|
||||
"opt-in" => true,
|
||||
"opt-out" => false,
|
||||
_ => panic!("unexpected canonical: {other_canonical:?}"),
|
||||
};
|
||||
|
||||
let mut s = make_state();
|
||||
navigate_to(&mut s, "coding_data_sharing");
|
||||
let _ = handle_settings_key(&mut s, &press(KeyCode::Enter));
|
||||
// Nav to the OTHER choice.
|
||||
let _ = handle_settings_key(&mut s, &press(KeyCode::Down));
|
||||
// Enter → commit.
|
||||
let outcome = handle_settings_key(&mut s, &press(KeyCode::Enter));
|
||||
match outcome {
|
||||
SettingsKeyOutcome::Action(Action::SetCodingDataSharing { opted_in }) => {
|
||||
assert_eq!(
|
||||
opted_in, expected_opted_in,
|
||||
"Enter must commit `{other_canonical}` → SetCodingDataSharing(opted_in={expected_opted_in})"
|
||||
);
|
||||
}
|
||||
other => panic!("expected Action::SetCodingDataSharing commit, got {other:?}"),
|
||||
}
|
||||
assert!(
|
||||
matches!(s.mode, SettingsModalMode::Browse),
|
||||
"Enter commit must return to Browse"
|
||||
);
|
||||
}
|
||||
|
||||
/// Esc in non-preview picker returns to Browse without Action.
|
||||
#[test]
|
||||
fn pr9_coding_data_sharing_picker_esc_does_not_dispatch_action() {
|
||||
let mut s = make_state();
|
||||
navigate_to(&mut s, "coding_data_sharing");
|
||||
let _ = handle_settings_key(&mut s, &press(KeyCode::Enter));
|
||||
let _ = handle_settings_key(&mut s, &press(KeyCode::Down));
|
||||
|
||||
let outcome = handle_settings_key(&mut s, &press(KeyCode::Esc));
|
||||
assert!(
|
||||
matches!(outcome, SettingsKeyOutcome::Changed),
|
||||
"Esc on non-preview Enum picker must NOT emit an Action — \
|
||||
doing so would fire an ACP round-trip on every Esc. Got {outcome:?}"
|
||||
);
|
||||
assert!(
|
||||
matches!(s.mode, SettingsModalMode::Browse),
|
||||
"Esc must return to Browse"
|
||||
);
|
||||
}
|
||||
|
||||
/// Picker seeds at "opt-out" when `coding_data_sharing_opt_out: true`.
|
||||
#[test]
|
||||
fn pr9_picker_seeds_choices_idx_from_pager_snapshot_opt_out_true() {
|
||||
let snapshot = PagerLocalSnapshot {
|
||||
coding_data_sharing_opt_out: true,
|
||||
..PagerLocalSnapshot::default()
|
||||
};
|
||||
let mut s = SettingsModalState::new(
|
||||
Arc::new(SettingsRegistry::defaults()),
|
||||
UiConfig::default(),
|
||||
snapshot,
|
||||
);
|
||||
navigate_to(&mut s, "coding_data_sharing");
|
||||
let _ = handle_settings_key(&mut s, &press(KeyCode::Enter));
|
||||
let reg = SettingsRegistry::defaults();
|
||||
let opt_out_idx = match ®.find("coding_data_sharing").unwrap().kind {
|
||||
SettingKind::Enum { choices, .. } => choices
|
||||
.iter()
|
||||
.position(|c| c.canonical == "opt-out")
|
||||
.expect("coding_data_sharing must have 'opt-out' choice"),
|
||||
_ => panic!("coding_data_sharing must be Enum"),
|
||||
};
|
||||
match s.mode {
|
||||
SettingsModalMode::PickingEnum {
|
||||
choices_idx,
|
||||
ref original_value,
|
||||
..
|
||||
} => {
|
||||
assert_eq!(
|
||||
choices_idx, opt_out_idx,
|
||||
"picker must seed at the 'opt-out' index when snapshot says opt_out=true"
|
||||
);
|
||||
assert_eq!(
|
||||
original_value,
|
||||
&SettingValue::Enum("opt-out"),
|
||||
"original_value must match the live snapshot"
|
||||
);
|
||||
}
|
||||
ref other => panic!("expected PickingEnum mode, got {other:?}"),
|
||||
}
|
||||
}
|
||||
|
||||
/// Exactly 2 canonical choices: {opt-in, opt-out}.
|
||||
#[test]
|
||||
fn pr9_coding_data_sharing_choices_use_canonical_strings() {
|
||||
let reg = SettingsRegistry::defaults();
|
||||
let meta = reg.find("coding_data_sharing").unwrap();
|
||||
let canonicals: Vec<&str> = match &meta.kind {
|
||||
SettingKind::Enum { choices, .. } => choices.iter().map(|c| c.canonical).collect(),
|
||||
_ => panic!("coding_data_sharing must be Enum"),
|
||||
};
|
||||
assert_eq!(
|
||||
canonicals.len(),
|
||||
2,
|
||||
"coding_data_sharing catalog must be exactly {{opt-in, opt-out}} — adding a \
|
||||
choice requires updating the action_for_enum_commit arm in \
|
||||
views/settings_modal.rs AND the action_for_reset arm in dispatch.rs",
|
||||
);
|
||||
assert!(
|
||||
canonicals.contains(&"opt-in"),
|
||||
"coding_data_sharing must include 'opt-in' canonical"
|
||||
);
|
||||
assert!(
|
||||
canonicals.contains(&"opt-out"),
|
||||
"coding_data_sharing must include 'opt-out' canonical"
|
||||
);
|
||||
}
|
||||
|
||||
/// Search "privacy" finds exactly `coding_data_sharing`.
|
||||
#[test]
|
||||
fn pr9_search_privacy_matches_coding_data_sharing() {
|
||||
let reg = SettingsRegistry::defaults();
|
||||
let hits = reg.search("privacy");
|
||||
// The category label "Privacy" appears as a header but is not
|
||||
// part of `search()`'s haystack (search ignores categories);
|
||||
// matches come from the meta's keywords + label + description.
|
||||
let hit_keys: Vec<&str> = hits.iter().map(|m| m.key).collect();
|
||||
assert_eq!(
|
||||
hits.len(),
|
||||
1,
|
||||
"search('privacy') must return EXACTLY one result (coding_data_sharing). \
|
||||
Found {} results: {hit_keys:?}. \
|
||||
If this fails because another setting added 'privacy' to its keywords/label/\
|
||||
description, decide: (a) is 'privacy' a real keyword for that setting? If yes, \
|
||||
loosen this assertion to a presence-only check `hit_keys.contains(&\"coding_data_sharing\")`. \
|
||||
(b) If no, remove 'privacy' from the other setting's haystack — search relevance \
|
||||
is more important than tag promiscuity.",
|
||||
hits.len(),
|
||||
);
|
||||
assert_eq!(
|
||||
hits[0].key, "coding_data_sharing",
|
||||
"search('privacy') unique result must be coding_data_sharing"
|
||||
);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Mouse path tests for coding_data_sharing
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/// First click on unselected row only selects.
|
||||
#[test]
|
||||
fn pr9_mouse_click_on_unselected_coding_data_sharing_row_only_selects() {
|
||||
let mut s = make_state();
|
||||
synth_rects(&mut s);
|
||||
let row_y = row_idx_for(&s, "coding_data_sharing") as u16;
|
||||
|
||||
let outcome = handle_settings_mouse(
|
||||
&mut s,
|
||||
MouseEventKind::Down(crossterm::event::MouseButton::Left),
|
||||
10,
|
||||
row_y,
|
||||
);
|
||||
assert!(
|
||||
matches!(outcome, SettingsKeyOutcome::Changed),
|
||||
"first body-click on unselected coding_data_sharing row should only select, got: {outcome:?}",
|
||||
);
|
||||
assert_eq!(s.selected, row_y as usize);
|
||||
assert!(matches!(s.mode, SettingsModalMode::Browse));
|
||||
}
|
||||
|
||||
/// Second click on selected row opens picker.
|
||||
#[test]
|
||||
fn pr9_mouse_click_on_selected_coding_data_sharing_row_opens_picker() {
|
||||
let mut s = make_state();
|
||||
synth_rects(&mut s);
|
||||
let row_y = row_idx_for(&s, "coding_data_sharing") as u16;
|
||||
|
||||
// First click: select.
|
||||
let _ = handle_settings_mouse(
|
||||
&mut s,
|
||||
MouseEventKind::Down(crossterm::event::MouseButton::Left),
|
||||
10,
|
||||
row_y,
|
||||
);
|
||||
assert_eq!(s.selected, row_y as usize);
|
||||
|
||||
// Second click on the focused row: open the picker.
|
||||
let outcome = handle_settings_mouse(
|
||||
&mut s,
|
||||
MouseEventKind::Down(crossterm::event::MouseButton::Left),
|
||||
10,
|
||||
row_y,
|
||||
);
|
||||
assert!(
|
||||
matches!(outcome, SettingsKeyOutcome::Changed),
|
||||
"second click on focused Enum row must open picker, got: {outcome:?}",
|
||||
);
|
||||
match &s.mode {
|
||||
SettingsModalMode::PickingEnum { key, .. } => {
|
||||
assert_eq!(*key, "coding_data_sharing");
|
||||
}
|
||||
_ => panic!("second click on focused coding_data_sharing row must enter PickingEnum"),
|
||||
}
|
||||
}
|
||||
|
||||
/// Value-column click opens picker in one click.
|
||||
#[test]
|
||||
fn pr9_mouse_click_on_coding_data_sharing_indicator_opens_picker_in_one_click() {
|
||||
let mut s = make_state();
|
||||
synth_rects(&mut s);
|
||||
let row_y = row_idx_for(&s, "coding_data_sharing") as u16;
|
||||
|
||||
let outcome = handle_settings_mouse(
|
||||
&mut s,
|
||||
MouseEventKind::Down(crossterm::event::MouseButton::Left),
|
||||
72,
|
||||
row_y,
|
||||
);
|
||||
assert!(
|
||||
matches!(outcome, SettingsKeyOutcome::Changed),
|
||||
"value click must open picker in one click, got: {outcome:?}",
|
||||
);
|
||||
match &s.mode {
|
||||
SettingsModalMode::PickingEnum { key, .. } => {
|
||||
assert_eq!(*key, "coding_data_sharing");
|
||||
}
|
||||
_ => {
|
||||
panic!("value click on coding_data_sharing must enter PickingEnum")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// default_selected_permission (Agent Enum, no preview — SHELL-owned, persists)
|
||||
// ---------------------------------------------------------------------------
|
||||
@@ -5024,53 +4644,6 @@ fn default_selected_permission_mouse_click_on_indicator_opens_picker_in_one_clic
|
||||
}
|
||||
}
|
||||
|
||||
/// The `/privacy` slash command's argument parser
|
||||
/// is case-insensitive and supports a deliberately-pared-down list of
|
||||
/// unambiguous-semantic aliases. The unit-level coverage lives in the
|
||||
/// slash command module; this e2e test pins the integration contract
|
||||
/// (the parser is reachable from the slash command and produces the
|
||||
/// expected `Action`).
|
||||
///
|
||||
/// Ambiguous aliases
|
||||
/// (`on/off/true/false/enable/disable`) were DROPPED because they
|
||||
/// could be read either as "turn on privacy" (=opt-out) or "turn on
|
||||
/// sharing" (=opt-in). For a privacy-critical setting we err on the
|
||||
/// side of explicit, unambiguous arguments. The test below verifies
|
||||
/// both the accept list AND the reject list.
|
||||
#[test]
|
||||
fn pr9_privacy_slash_command_parses_aliases() {
|
||||
use kigi_tui::slash::commands::privacy::parse_privacy_arg;
|
||||
|
||||
// Canonical names.
|
||||
assert_eq!(parse_privacy_arg("opt-in"), Some(true));
|
||||
assert_eq!(parse_privacy_arg("opt-out"), Some(false));
|
||||
|
||||
// Case-insensitive (sample).
|
||||
assert_eq!(parse_privacy_arg("Opt-In"), Some(true));
|
||||
assert_eq!(parse_privacy_arg("OPT-OUT"), Some(false));
|
||||
|
||||
// Unambiguous-semantic aliases (pruned list).
|
||||
assert_eq!(parse_privacy_arg("in"), Some(true));
|
||||
assert_eq!(parse_privacy_arg("out"), Some(false));
|
||||
assert_eq!(parse_privacy_arg("share"), Some(true));
|
||||
assert_eq!(parse_privacy_arg("private"), Some(false));
|
||||
|
||||
// Ambiguous aliases MUST be rejected. `/privacy on`
|
||||
// could be read as "turn on privacy" (=opt-out, the OPPOSITE of
|
||||
// what an earlier mapping returned). For a privacy
|
||||
// setting, ambiguity = silent data-exfiltration risk.
|
||||
for ambiguous in &["on", "off", "true", "false", "enable", "disable"] {
|
||||
assert_eq!(
|
||||
parse_privacy_arg(ambiguous),
|
||||
None,
|
||||
"ambiguous alias `{ambiguous}` MUST be rejected (PR 9 R1, Security Issue 10)",
|
||||
);
|
||||
}
|
||||
|
||||
// Unknown.
|
||||
assert_eq!(parse_privacy_arg("maybe"), None);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// `plan_mode` (Agent-category Enum, PAGER-owned + ACP-mediated,
|
||||
// supports_preview: false)
|
||||
|
||||
Reference in New Issue
Block a user