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
@@ -678,7 +678,6 @@ impl AgentView {
.hit_plan_approval_status
.update_hover(mouse.column, mouse.row);
changed |= self.hit_context.update_hover(mouse.column, mouse.row);
changed |= self.hit_credits.update_hover(mouse.column, mouse.row);
}
MouseEventKind::Down(MouseButton::Left) => {
if self.hit_plan_button.contains(mouse.column, mouse.row) {
@@ -943,7 +942,7 @@ impl AgentView {
|| (key.code == KeyCode::Char('/') && key.modifiers.contains(KeyModifiers::SHIFT)))
{
self.active_modal = Some(crate::views::modal::ActiveModal::CommandPalette {
entries: crate::views::modal::default_palette_entries(self.sharing_enabled),
entries: crate::views::modal::default_palette_entries(),
state: crate::views::picker::PickerState::input_active(),
window: crate::views::modal_window::ModalWindowState::new(),
});
@@ -1016,7 +1015,7 @@ impl AgentView {
}
ActionId::CommandPalette => {
self.active_modal = Some(crate::views::modal::ActiveModal::CommandPalette {
entries: crate::views::modal::default_palette_entries(self.sharing_enabled),
entries: crate::views::modal::default_palette_entries(),
state: crate::views::picker::PickerState::input_active(),
window: crate::views::modal_window::ModalWindowState::new(),
});
@@ -1462,7 +1461,7 @@ mod focus_gained_restore_tests {
agent.session.state = AgentState::TurnRunning;
with_permission(&mut agent);
agent.active_modal = Some(ActiveModal::CommandPalette {
entries: crate::views::modal::default_palette_entries(false),
entries: crate::views::modal::default_palette_entries(),
state: crate::views::picker::PickerState::input_active(),
window: crate::views::modal_window::ModalWindowState::new(),
});
@@ -1261,8 +1261,6 @@ mod cancel_turn_mouse_tests {
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,
@@ -727,10 +727,6 @@ pub struct AgentView {
/// Stashed normal prompt state while editing a queued prompt.
/// Restored when editing ends.
pub stashed_prompt: Option<StashedPrompt>,
/// Complete prompt stashed from a credit-limit-blocked turn. Used by
/// `CreditLimitRecheckComplete` to retry the prompt after a tier
/// upgrade instead of showing a stale upsell.
pub credit_limit_stashed_prompt: Option<crate::app::agent::InFlightPrompt>,
/// Complete prompt stashed from a turn that failed because the login
/// expired (401 / re-auth). Used by the `AuthComplete` handler to
/// auto-resubmit the prompt after a successful mid-session re-auth so
@@ -754,10 +750,6 @@ pub struct AgentView {
/// Unlike `chat_kind`, stays `false` for a `/chat` one-shot session in
/// a Build process, whose picker still lists local sessions.
pub app_chat_mode: bool,
/// Mocked credit balance for the status bar indicator.
pub credit_balance: Option<crate::views::credit_bar::CreditBalance>,
/// Auto top-up rule paired with `credit_balance` for the prompt warning.
pub auto_topup: Option<crate::views::credit_bar::AutoTopupInfo>,
/// Current goal orchestration state. Set by `GoalUpdated` session
/// notifications, cleared when a new session starts.
pub goal_state: Option<super::agent::GoalDisplayState>,
@@ -947,7 +939,6 @@ pub struct AgentView {
pub hovered_prompt: bool,
pub hit_badge: HitArea,
pub hit_context: HitArea,
pub hit_credits: HitArea,
pub hit_todo_close: HitArea,
pub hit_bg_close: HitArea,
pub hit_subagent_close: HitArea,
@@ -1235,8 +1226,6 @@ pub struct AgentView {
/// Hit area for the [✗] close button in the subagent frame title bar.
pub hit_subagent_frame_close: HitArea,
/// Whether the `/share` slash command is available (mirrors
/// `AppView::sharing_enabled`). Used to gate palette entries.
pub sharing_enabled: bool,
/// Input flight recorder — rolling buffer of recent key events.
/// Dumped to file via Esc→d combo for debugging.
pub(crate) input_log: crate::input_log::InputRingBuffer,
@@ -1512,23 +1501,6 @@ fn translate_local_submit(
persist_mode,
})
}
LocalQuestionKind::CreditLimitUpsell => {
let q = qv.questions.first();
let url = q
.and_then(|q| q.options.get(*idx))
.and_then(|o| o.id.as_deref())
.unwrap_or(super::dispatch::UPSELL_URL_PAYG);
InputOutcome::Action(Action::OpenUrl(url.to_string()))
}
LocalQuestionKind::FreeUsageUpsell => {
let url = qv
.questions
.first()
.and_then(|q| q.options.get(*idx))
.and_then(|o| o.id.as_deref())
.unwrap_or(super::dispatch::UPSELL_URL_UPGRADE);
InputOutcome::Action(Action::OpenUrl(url.to_string()))
}
LocalQuestionKind::AgentTypeMismatch { model_id, effort } => {
let start_new = *idx == 0;
InputOutcome::Action(Action::AgentTypeMismatchAnswered {
@@ -2217,8 +2189,6 @@ pub(super) mod test_fixtures {
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,
@@ -2278,8 +2248,6 @@ pub(super) mod test_fixtures {
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,
@@ -3010,8 +2978,6 @@ pub(crate) fn test_agent_view(session_id: Option<&str>, cwd: std::path::PathBuf)
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,
@@ -454,8 +454,6 @@ pub(super) mod paste_key_tests {
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,
@@ -703,8 +703,6 @@ mod plan_chip_tests {
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,
@@ -329,7 +329,6 @@ impl AgentView {
let thinking_label = self.scrollback.thinking_fold_label();
let selected_is_user_prompt = selected_entry.is_some_and(|e| e.block.is_user_prompt());
let selected_is_agent_message = selected_entry.is_some_and(|e| e.block.is_agent_message());
let selected_is_credit_limit = selected_entry.is_some_and(|e| e.block.is_credit_limit());
let mut hints = agent::build_hints(
self.active_pane,
&self.prompt,
@@ -355,7 +354,6 @@ impl AgentView {
!self.visible_queue_is_empty(),
selected_is_user_prompt,
selected_is_agent_message,
selected_is_credit_limit,
crate::terminal::terminal_context().shift_enter_unavailable(),
self.scrollback_search.as_ref(),
);
@@ -1300,7 +1298,6 @@ impl AgentView {
self.hit_bg_status.rect = areas.get("bg_tasks").copied();
self.hit_goal_status.rect = areas.get("goal").copied();
self.hit_context.rect = areas.get("context").copied();
self.hit_credits.rect = areas.get("credits").copied();
self.hit_plan_button.rect = areas.get("plan").copied();
self.hit_queue_badge.rect = areas.get("queue").copied();
self.hit_badge.rect = areas.get("badge").copied();
@@ -2041,23 +2038,6 @@ impl AgentView {
}
let mode_flags: &[PromptFlag] = &mode_flags_vec;
let multiline = self.multiline_mode;
let usage_visible = self
.prompt
.slash_controller
.registry()
.get("usage")
.is_some();
let warning = self.credit_balance.as_ref().and_then(|bal| {
crate::views::credit_bar::usage_warning_for_session(
bal,
self.auto_topup.as_ref(),
usage_visible,
self.chat_kind,
)
});
let usage_warning_text: Option<String> = warning.as_ref().map(|(t, _)| t.clone());
let usage_warning = usage_warning_text.as_deref();
let usage_warning_critical = warning.is_some_and(|(_, critical)| critical);
let model_label = match self.session.models.reasoning_effort {
Some(eff) => format!("{model_id} ({eff})"),
None => model_id,
@@ -2067,8 +2047,6 @@ impl AgentView {
model_name: &model_label,
flags: mode_flags,
multiline,
usage_warning,
usage_warning_critical,
},
PromptMode::EditingQueued { id, .. } => {
let pos = self.session.queue_position(*id).map(|i| i + 1).unwrap_or(1);
@@ -2077,8 +2055,6 @@ impl AgentView {
model_name: &editing_label,
flags: mode_flags,
multiline,
usage_warning,
usage_warning_critical,
}
}
};
@@ -2087,8 +2063,6 @@ impl AgentView {
model_name: label,
flags: &[],
multiline: false,
usage_warning,
usage_warning_critical,
}
} else {
info
@@ -206,8 +206,6 @@ mod sync_rewind_anchor_to_picker_tests {
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,
@@ -1,5 +1,5 @@
//! Session lifecycle: bind/reload/replay bookkeeping, turn activity
//! resolution, context/credit updates, and app-scoped gates.
//! resolution, context updates, and app-scoped gates.
#[cfg(test)]
use super::test_agent_view;
use super::{
@@ -85,7 +85,6 @@ impl AgentView {
bash_turn: false,
cron_task_id: None,
stashed_prompt: None,
credit_limit_stashed_prompt: None,
reauth_stashed_prompt: None,
active_modal: None,
modal_buttons: Vec::new(),
@@ -93,8 +92,6 @@ impl AgentView {
context_state: None,
chat_kind: false,
app_chat_mode: false,
credit_balance: None,
auto_topup: None,
goal_state: None,
parked_wait_marker_for: None,
end_work_announced: false,
@@ -152,7 +149,6 @@ impl AgentView {
hovered_prompt: false,
hit_badge: Default::default(),
hit_context: Default::default(),
hit_credits: Default::default(),
hit_todo_close: Default::default(),
hit_bg_close: Default::default(),
hit_subagent_close: Default::default(),
@@ -257,7 +253,6 @@ impl AgentView {
active_subagent: None,
is_subagent_view: false,
hit_subagent_frame_close: Default::default(),
sharing_enabled: false,
input_log: crate::input_log::InputRingBuffer::new(),
esc_pressed_at: None,
pending_first_prompt: None,
@@ -740,21 +735,6 @@ impl AgentView {
}
}
}
/// Apply Build coding-credit balance only for non-chat agents.
/// Gateway/chat-kind sessions keep credits unset so bars/warnings stay off.
pub fn apply_credit_balance(
&mut self,
balance: Option<crate::views::credit_bar::CreditBalance>,
auto_topup: Option<crate::views::credit_bar::AutoTopupInfo>,
) {
if self.chat_kind {
self.credit_balance = None;
self.auto_topup = None;
return;
}
self.credit_balance = balance;
self.auto_topup = auto_topup;
}
/// Record a key event to the input flight recorder.
///
/// Zero heap allocations — stores raw `Copy` types in the ring buffer.
@@ -801,18 +781,6 @@ impl AgentView {
textarea_changed: delta.textarea_changed,
});
}
/// Set the sharing-enabled flag on this view and propagate it to the
/// slash-command registry so the `/share` entry stays hidden/visible in
/// lockstep with `AgentView::sharing_enabled`. Use this instead of
/// mutating `sharing_enabled` directly when a new agent is created or a
/// session is loaded, so the field and registry can't drift.
pub fn set_sharing_enabled(&mut self, enabled: bool) {
self.sharing_enabled = enabled;
self.prompt
.slash_controller
.registry_mut()
.set_share_visible(enabled);
}
/// Show or hide the `/usage` slash command in this agent's registry.
pub fn set_usage_visible(&mut self, visible: bool) {
self.prompt
@@ -839,13 +807,11 @@ impl AgentView {
/// One place for the app-scoped gates a new/adopted session inherits so the session-creation sites cannot drift.
pub(crate) fn apply_app_scoped_gates(
&mut self,
sharing_enabled: bool,
usage_visible: bool,
chat_mode: bool,
screen_mode: crate::app::ScreenMode,
restricted_commands: &[String],
) {
self.set_sharing_enabled(sharing_enabled);
self.set_usage_visible(usage_visible);
self.app_chat_mode = chat_mode;
self.prompt.set_screen_mode(screen_mode);