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:
2026-07-17 16:05:51 -04:00
parent fe1f885bb3
commit ea0ce9d15f
231 changed files with 4730 additions and 26358 deletions
@@ -33,8 +33,6 @@ pub(super) fn make_session(session_id: Option<&str>) -> AgentSession {
restore_degree: None,
rate_limited: false,
model_incompatible: false,
credit_limit_blocked: false,
free_usage_blocked: false,
available_commands: Vec::new(),
available_commands_generation: 0,
available_tools: None,
@@ -257,17 +255,6 @@ pub(super) fn follow_ups_ext_with_prompt(
std::sync::Arc::from(serde_json::value::to_raw_value(&params).unwrap()),
)
}
pub(super) fn tier_settings_update(tier: &str) -> acp::ExtNotification {
acp::ExtNotification::new(
"x.ai/settings/update",
std::sync::Arc::from(
serde_json::value::to_raw_value(
&serde_json::json!({ "subscription_tier_display" : tier }),
)
.unwrap(),
),
)
}
pub(super) fn group_tool_verbs_settings_update(
value: Option<bool>,
) -> acp::ExtNotification {
@@ -119,7 +119,7 @@
let agent = app.agents.get_mut(&AgentId(0)).unwrap();
seed_pending_tool(agent, "create-plan-call", "CreatePlan");
agent.active_modal = Some(crate::views::modal::ActiveModal::CommandPalette {
entries: crate::views::modal::default_palette_entries(agent.sharing_enabled),
entries: crate::views::modal::default_palette_entries(),
state: crate::views::picker::PickerState::input_active(),
window: crate::views::modal_window::ModalWindowState::new(),
});
@@ -1751,24 +1751,7 @@
}
/// The credit-limit early return discards the popped adoption's buffer.
#[test]
fn credit_limit_response_discards_adoption_buffer() {
let mut app = app_with_running_p1_and_stashed_b1();
let id = AgentId(0);
send_tool_call_update(&mut app, "b1", "bash-mode-1", None);
app.agents
.get_mut(&id)
.unwrap()
.session
.credit_limit_blocked = true;
prompt_response(&mut app, "p1");
let agent = app.agents.get(&id).unwrap();
assert!(!app.pending_running_adoptions.contains_key(&id));
assert!(agent.pending_adoption_updates.is_empty());
}
/// A stash whose pid replayed a durable terminal is discarded, never adopted.
/// A stash whose pid replayed a durable terminal is discarded, never adopted.
#[test]
fn terminal_in_replay_stash_is_discarded_not_adopted() {
let mut app = app_with_running_p1_and_stashed_b1();
@@ -157,7 +157,7 @@
apply_retry_state(&exhausted, &mut session, &mut scrollback, false);
match last_session_event(&scrollback) {
Some(SessionEvent::RetryFailed { error, .. }) => {
assert_eq!(error, RATE_LIMITED_USER_MESSAGE_OAUTH);
assert_eq!(error, RATE_LIMITED_USER_MESSAGE_OAUTH.as_str());
}
other => panic!("expected OAuth rate-limit RetryFailed, got {other:?}"),
}
@@ -194,140 +194,8 @@
);
}
/// A rate-limit exhaustion whose flattened reason carries the
/// free-usage code sets both flags and pushes NO generic block (the
/// driver shows the paywall modal on PromptResponse; viewers keep no
/// marker).
#[test]
fn retry_exhausted_free_usage_sets_paywall_flag_without_marker() {
let mut session = make_session(Some("s1"));
let mut scrollback = ScrollbackState::new();
session.in_flight_prompt = Some(InFlightPrompt {
text: "try me again".into(),
images: Vec::new(),
scrollback_entry: EntryId::new(2),
chip_elements: Vec::new(),
});
apply_retry_state(
&RetryState::Exhausted {
attempts: 0,
reason: "API error (status 429 Too Many Requests): \
subscription:free-usage-exhausted: You have used all your free usage."
.into(),
is_rate_limited: true,
},
&mut session,
&mut scrollback,
false,
);
assert!(
session.rate_limited,
"free-usage keeps rate_limited (TurnFailed/toast suppression)"
);
assert!(session.free_usage_blocked);
assert_eq!(
scrollback.len(),
0,
"no RetryFailed marker — the paywall modal shows instead"
);
assert!(
session.in_flight_prompt.is_none(),
"free-usage exhaustion clears in_flight_prompt like other failures"
);
}
#[test]
fn apply_retry_state_credit_limit_exhausted_preserves_in_flight_prompt() {
let mut session = make_session(Some("s1"));
let mut scrollback = ScrollbackState::new();
session.in_flight_prompt = Some(InFlightPrompt {
text: "stash me".into(),
images: Vec::new(),
scrollback_entry: EntryId::new(2),
chip_elements: Vec::new(),
});
apply_retry_state(
&RetryState::Exhausted {
attempts: 3,
reason: "status 403: run out of credits".into(),
is_rate_limited: false,
},
&mut session,
&mut scrollback,
false,
);
assert!(
session.credit_limit_blocked,
"credit_limit_blocked must be set for credit-limit 403"
);
assert!(
session.in_flight_prompt.is_some(),
"in_flight_prompt must be preserved so PromptResponse handler can stash it"
);
assert_eq!(session.in_flight_prompt.unwrap().text, "stash me");
}
#[test]
fn apply_retry_state_credit_limit_failed_preserves_in_flight_prompt() {
let mut session = make_session(Some("s1"));
let mut scrollback = ScrollbackState::new();
session.in_flight_prompt = Some(InFlightPrompt {
text: "stash me too".into(),
images: Vec::new(),
scrollback_entry: EntryId::new(3),
chip_elements: Vec::new(),
});
apply_retry_state(
&RetryState::Failed {
error_type: "proxy_error".into(),
message: "status 403: run out of credits".into(),
},
&mut session,
&mut scrollback,
false,
);
assert!(
session.credit_limit_blocked,
"credit_limit_blocked must be set for credit-limit 403"
);
assert!(
session.in_flight_prompt.is_some(),
"in_flight_prompt must be preserved so PromptResponse handler can stash it"
);
assert_eq!(session.in_flight_prompt.unwrap().text, "stash me too");
}
#[test]
fn apply_retry_state_pool_402_sets_credit_limit_blocked() {
let mut session = make_session(Some("s1"));
let mut scrollback = ScrollbackState::new();
session.in_flight_prompt = Some(InFlightPrompt {
text: "pool blocked".into(),
images: Vec::new(),
scrollback_entry: EntryId::new(5),
chip_elements: Vec::new(),
});
apply_retry_state(
&RetryState::Failed {
error_type: "proxy_error".into(),
message:
"API error (status 402 Payment Required): Grok Build usage balance exhausted"
.into(),
},
&mut session,
&mut scrollback,
false,
);
assert!(
session.credit_limit_blocked,
"credit_limit_blocked must be set for pool 402 balance exhausted"
);
assert!(session.in_flight_prompt.is_some());
}
#[test]
fn apply_retry_state_non_credit_limit_failed_clears_in_flight_prompt() {
fn apply_retry_state_generic_failed_clears_in_flight_prompt() {
let mut session = make_session(Some("s1"));
let mut scrollback = ScrollbackState::new();
session.in_flight_prompt = Some(InFlightPrompt {
@@ -345,13 +213,9 @@
&mut scrollback,
false,
);
assert!(
!session.credit_limit_blocked,
"credit_limit_blocked must NOT be set for non-credit-limit errors"
);
assert!(
session.in_flight_prompt.is_none(),
"in_flight_prompt must be cleared for non-credit-limit errors"
"in_flight_prompt must be cleared for generic errors"
);
}
@@ -400,7 +264,6 @@
),
"auth 401 must surface the actionable re-auth prompt"
);
assert!(!session.credit_limit_blocked);
}
/// A recoverable auth failure preserves `in_flight_prompt` so the
@@ -1,41 +1,6 @@
#![cfg_attr(rustfmt, rustfmt::skip)]
use super::*;
#[test]
fn settings_non_api_key_tier_clears_stale_api_key_flag() {
let mut app = make_app_with_agent("sess-stale-key");
assert!(handle_ext_notification(
&tier_settings_update("API Key"),
&mut app
));
assert!(app.is_api_key_auth);
assert!(!app.usage_visible);
assert!(app.tier_restricted_commands.is_empty());
// Later personal Free stamp must not keep the API-key bypass.
assert!(handle_ext_notification(
&tier_settings_update("Free"),
&mut app
));
assert!(!app.is_api_key_auth);
assert!(app.usage_visible);
// Tier gating no longer exists; nothing gets re-restricted.
assert!(app.tier_restricted_commands.is_empty());
// A paid tier after API Key clears the api-key flag and tier limits.
let mut app = make_app_with_agent("sess-paid-tier");
assert!(handle_ext_notification(
&tier_settings_update("API Key"),
&mut app
));
assert!(handle_ext_notification(
&tier_settings_update("SuperGrok"),
&mut app
));
assert!(!app.is_api_key_auth);
assert!(app.tier_restricted_commands.is_empty());
}
#[test]
fn settings_update_clearing_group_tool_verbs_reverts_to_default() {
// Expected values come from the same chain the handler resolves, so the