§9 acceptance: grep-zero sweep — every internal x.ai/grok identifier renamed

The PRD's first acceptance gate now holds: grep -RinE '\bx\.ai\b|grok'
crates/ --include='*.rs' → 0 matches (exempt: NOTICE and third-party
license archives, README provenance, and the required 'Based on Grok
Build Open Source' attribution, now sourced from version_attribution.txt).

Wire-visible renames (both sides in this repo, changed in lockstep):
- Auth method id 'grok.com' → 'kimi-code' (AuthMethodKind::KimiCode).
- Every x.ai/* and _x.ai/* ACP ext method and meta key → kigi/* /
  _kigi/* (~200 names; grokShell → kigiShell). Session-file replay keeps
  a read-side alias for the legacy '_x.ai/session/update' method so
  existing updates.jsonl histories load; writes emit only the new name
  (both directions test-pinned).
- Agent types grok-build* → kigi* with a documented legacy-prefix alias
  at resolution time so persisted sessions keep resolving.
- ToolNamespace/BuiltinAgentName GrokBuild* → Kigi* (wire snake_case
  kigi/kigi_concise/kigi_hashline; schema regenerated); grok_build
  implementation dirs renamed to kigi*.
- x-grok-* headers → x-kigi-*, __GROK_* sentinels → __KIGI_*, themes
  grokday/groknight → kigiday/kiginight (old persisted values fall back
  to the default theme), web_fetch allowlist xAI hosts → kimi.com +
  moonshot platforms, changelog CDN → this repo, grok-build changelog
  archives deleted.
- BYOK default endpoint removed: [endpoints] api_base_url is now truly
  optional with NO default — consumers fail fast with the flag name when
  unset (no silent x.ai egress). Mock harnesses inject it explicitly.
- System-prompt identity fixed: 'released by xAI' → 'an unofficial
  community CLI for Kimi' (template + regenerated encrypted form).

Also repaired pre-existing grok-era test debt found by the sweep: the
stale trace_classify default-model pin, the grok-pager UA label test,
pty-harness stale-binary reuse and non-hermetic moonshot routing (a PTY
test could previously reach the real api.moonshot.cn), and the outdated
oauth fixture scope key.

Gates: §9 grep 0; fmt clean; workspace check/clippy 0/0 (-D warnings);
FULL cargo test --workspace: 234 suites, 21,961 passed, 0 failed;
deny advisories ok.
This commit is contained in:
2026-07-18 02:48:46 -04:00
parent 86e3724310
commit 6f31415ed6
1056 changed files with 8410 additions and 18307 deletions
@@ -24,7 +24,7 @@ pub(super) fn dispatch_logout(_app: &mut AppView) -> Vec<Effect> {
/// On the eager-auth path (cached token), login_method_id is never set
/// because the user skipped the login screen.
///
/// Does **not** invent `grok.com` when no interactive method is advertised
/// Does **not** invent `kimi-code` when no interactive method is advertised
/// (e.g. `preferred_method=api_key` with no key — empty `auth_methods`).
/// Callers already surface "No login method available" when this leaves
/// `login_method_id` unset.
@@ -1,5 +1,5 @@
//! Mid-turn interjection dispatch: optimistic local echo, the
//! `x.ai/interject` effect, and prompt-history recording. Split out of
//! `kigi/interject` effect, and prompt-history recording. Split out of
//! `dispatch.rs` verbatim (pure code motion).
use crate::app::actions::Effect;
@@ -9,10 +9,10 @@ use crate::scrollback::block::RenderBlock;
/// Send a mid-turn interjection. Pushes a standard user prompt block locally
/// for instant feedback, records the text in prompt history, clears the
/// prompt, and fires the `x.ai/interject` ext method carrying a client-minted
/// prompt, and fires the `kigi/interject` ext method carrying a client-minted
/// id.
///
/// The shell broadcasts `x.ai/session/interjection` to every attached pane so
/// The shell broadcasts `kigi/session/interjection` to every attached pane so
/// other clients viewing the same session render it too (multi-client /
/// dashboard mode). Our own broadcast echoes back carrying the same id; the id
/// is recorded in `self_interjection_ids` so `handle_interjection` drops the
@@ -43,7 +43,7 @@ pub(super) fn dispatch_interject(
record_interject_prompt_history(agent, &text);
// Push a standard user prompt block locally for instant feedback, and
// record its id so the broadcast echo (`x.ai/session/interjection`) is
// record its id so the broadcast echo (`kigi/session/interjection`) is
// deduped instead of rendering a second copy on this pane.
let interjection_id = uuid::Uuid::new_v4().to_string();
agent.self_interjection_ids.insert(interjection_id.clone());
@@ -69,7 +69,7 @@ pub(super) fn dispatch_send_feedback(app: &mut AppView, text: String) -> Vec<Eff
};
agent.scrollback.push_block(RenderBlock::system(
"Thanks for the feedback! The Grok Build team is on it.".to_string(),
"Thanks for the feedback! The Kigi team is on it.".to_string(),
));
vec![Effect::SendFeedback {
@@ -79,7 +79,7 @@ pub(super) fn dispatch_send_feedback(app: &mut AppView, text: String) -> Vec<Eff
}]
}
/// Send a raw remember note for LLM-powered rewriting via `x.ai/memory/rewrite`.
/// Send a raw remember note for LLM-powered rewriting via `kigi/memory/rewrite`.
/// Clears remember mode and prompts the LLM to reformat the note with session
/// context. Falls back to direct `SaveMemoryNote` when no session is available.
pub(super) fn dispatch_send_remember_note(app: &mut AppView, text: String) -> Vec<Effect> {
@@ -327,7 +327,7 @@ pub(crate) fn scrollback_has_user_messages(
}
/// Request a session recap. Bypasses the prompt queue — works even while the
/// agent is mid-turn. Fires the `x.ai/recap` ext method; the recap arrives
/// agent is mid-turn. Fires the `kigi/recap` ext method; the recap arrives
/// asynchronously as a `SessionRecap` notification (rendered in scrollback).
///
/// `auto` is `false` for an explicit `/recap` and `true` for the automatic
@@ -343,7 +343,7 @@ pub(super) fn dispatch_send_recap(app: &mut AppView, auto: bool) -> Vec<Effect>
};
// Shell is authoritative (remote settings / config / env). Skip client requests
// entirely when the feature is off so we never hit `x.ai/recap`.
// entirely when the feature is off so we never hit `kigi/recap`.
if !app.session_recap_available {
if !auto {
agent.show_toast("Session recap is not enabled");
@@ -420,7 +420,7 @@ pub(super) fn handle_memory_note_saved(
.scrollback
.push_block(crate::scrollback::block::RenderBlock::system(format!(
"Memory saved to {}",
crate::util::display_user_grok_path("memory/MEMORY.md")
crate::util::display_user_kigi_path("memory/MEMORY.md")
)));
}
Err(error) => {
@@ -23,7 +23,7 @@ use agent_client_protocol as acp;
/// existing `set_yolo_mode(true)` flow to flip the local YOLO state, drain
/// any remaining queued permissions, persist `[ui] permission_mode =
/// "always-approve"` to `~/.kigi/config.toml`, and fire the
/// `x.ai/yolo_mode_changed` ACP notification. See the option-id constant
/// `kigi/yolo_mode_changed` ACP notification. See the option-id constant
/// doc-comment for the full client/shell split. Under a managed-policy
/// pin step (b) is refused with a toast — the request is still allowed once.
pub(super) fn dispatch_permission_select(
@@ -32,7 +32,7 @@ pub(super) fn consume_chat_kind(app: &mut AppView) -> bool {
/// The prompt is always pushed to the queue first. If the agent is idle
/// (and has a session), `maybe_drain_queue` pops the front prompt and
/// sends it in the same dispatch call — no deferred ticks.
/// Start (if needed) and submit the initial prompt from `grok "<prompt>"`.
/// Start (if needed) and submit the initial prompt from `kigi "<prompt>"`.
///
/// Shared by the TUI startup path (already authenticated) and the post-login
/// `AuthComplete` path (deferred via `deferred_startup.prompt`). It does nothing
@@ -511,7 +511,7 @@ pub(super) fn dispatch_send_prompt_inner(
// immediately instead of being held in the local drip-feed queue. The
// agent appends it to its authoritative `pending_inputs` (no concurrent
// turn starts — validated keystone) and drives the drain via
// `x.ai/queue/changed`. We render an optimistic echo into the shared
// `kigi/queue/changed`. We render an optimistic echo into the shared
// queue keyed by `prompt_id`; the broadcast reconciles it by id.
//
// The IDLE case is unchanged (falls through to the local path below,
@@ -930,7 +930,7 @@ pub(super) fn handle_prompt_response(
// Server-authoritative queue lifecycle: this prompt's RPC
// resolved without becoming the running turn (removed,
// cancelled, rewound). Retire its optimistic echo so a
// later `x.ai/queue/changed` broadcast can't re-pin a
// later `kigi/queue/changed` broadcast can't re-pin a
// stale placeholder and reorder the queue.
if let Some(sid) = agent.session.session_id.as_ref().map(|s| s.0.to_string()) {
retire_optimistic_echo(
@@ -1171,7 +1171,7 @@ pub(super) fn handle_prompt_response(
// title into the body automatically.
let notif_title = session_name
.map(|s| s.to_string())
.unwrap_or_else(|| "Grok".into());
.unwrap_or_else(|| "Kigi".into());
app.deferred_notification = Some((
NotificationEvent {
@@ -62,7 +62,7 @@ pub(super) fn immediate_server_send_eligible(agent: &AgentView) -> bool {
/// Push the optimistic shared-queue echo for an immediate server-authoritative
/// send and mirror it into the owning agent so the queue pane renders it
/// immediately, before the confirming `x.ai/queue/changed` broadcast.
/// immediately, before the confirming `kigi/queue/changed` broadcast.
pub(super) fn push_server_queue_echo(
app: &mut AppView,
agent_id: AgentId,
@@ -91,7 +91,7 @@ pub(super) fn push_server_queue_echo(
///
/// The agent's `pending_inputs` is the single source of truth for queue
/// contents and order; the only client-side queue state is the optimistic echo
/// that bridges the round-trip before the confirming `x.ai/queue/changed`
/// that bridges the round-trip before the confirming `kigi/queue/changed`
/// broadcast. Once a prompt's RPC resolves (or we pull it back into the input
/// on cancel) it will never reappear in a future broadcast, so its echo must be
/// dropped — otherwise the reconcile in [`AppView::apply_queue_changed`] keeps
@@ -633,7 +633,7 @@ pub(crate) fn apply_turn_start_shim(
agent.session.current_prompt_id = Some(prompt_id.clone());
agent.attached_as_viewer = adopted_from_other_client;
// A new (adopted) turn is starting: drop the prior turn's chips but KEEP the
// seen ring, so a buffer-replayed `x.ai/follow_ups` for an older response
// seen ring, so a buffer-replayed `kigi/follow_ups` for an older response
// stays rejected (no stale revival). This is correct for BOTH passive-viewer
// and self-driven adoption: the adopted turn's OWN follow_ups still
// re-render via the stamped `promptId` match in `apply_follow_ups` (the
@@ -1150,7 +1150,7 @@ mod tests {
}
/// FIX 4 (b) via the shim: after starting a NEW turn, a buffer-replayed
/// `x.ai/follow_ups` for a PRIOR turn's response stays rejected (its
/// `kigi/follow_ups` for a PRIOR turn's response stays rejected (its
/// `promptId` is not the active turn and it is already seen) — no stale
/// revival. Covers the self-driven turn start (`p-self`).
#[test]
@@ -95,8 +95,8 @@ pub(in crate::app::dispatch) fn apply_persist_worktree_mode(
/// Build the two persistence options shared by the fork and new-session
/// worktree question modals ("Always worktree" / "Never worktree").
pub(super) fn worktree_persist_options()
-> [kigi_tools::implementations::grok_build::ask_user_question::QuestionOption; 2] {
use kigi_tools::implementations::grok_build::ask_user_question::QuestionOption;
-> [kigi_tools::implementations::kigi::ask_user_question::QuestionOption; 2] {
use kigi_tools::implementations::kigi::ask_user_question::QuestionOption;
[
QuestionOption {
label: "Always worktree".into(),
@@ -117,7 +117,7 @@ pub(super) fn worktree_persist_options()
/// instead -- the modal-collision protocol.
fn open_fork_question(app: &mut AppView, directive: Option<String>) -> Vec<Effect> {
use crate::views::question_view::{LocalQuestionKind, QuestionViewState};
use kigi_tools::implementations::grok_build::ask_user_question::{Question, QuestionOption};
use kigi_tools::implementations::kigi::ask_user_question::{Question, QuestionOption};
let ActiveView::Agent(id) = app.active_view else {
return vec![];
};
@@ -168,7 +168,7 @@ fn open_fork_question(app: &mut AppView, directive: Option<String>) -> Vec<Effec
/// `worktree == true` reuses the existing
/// [`Effect::CreateWorktreeSession`] pipeline (with `load_session_id`
/// set to the parent session id). `worktree == false` emits the new
/// [`Effect::ForkSession`] which calls `x.ai/session/fork` directly.
/// [`Effect::ForkSession`] which calls `kigi/session/fork` directly.
pub(in crate::app::dispatch) fn dispatch_fork_resolved(
app: &mut AppView,
worktree: bool,
@@ -154,7 +154,7 @@ pub(in crate::app::dispatch) fn dispatch_new_session(app: &mut AppView) -> Vec<E
/// [`dispatch_new_worktree_session`].
pub(in crate::app::dispatch) fn open_new_session_question(app: &mut AppView) -> Vec<Effect> {
use crate::views::question_view::{LocalQuestionKind, QuestionViewState};
use kigi_tools::implementations::grok_build::ask_user_question::{Question, QuestionOption};
use kigi_tools::implementations::kigi::ask_user_question::{Question, QuestionOption};
let ActiveView::Agent(id) = app.active_view else {
return vec![];
};
@@ -211,7 +211,7 @@ pub(in crate::app::dispatch) fn open_agent_type_mismatch_question(
model_name: &str,
) -> Vec<Effect> {
use crate::views::question_view::{LocalQuestionKind, QuestionViewState};
use kigi_tools::implementations::grok_build::ask_user_question::{Question, QuestionOption};
use kigi_tools::implementations::kigi::ask_user_question::{Question, QuestionOption};
let ActiveView::Agent(id) = app.active_view else {
return vec![];
};
@@ -408,7 +408,7 @@ pub(in crate::app::dispatch) fn clear_startup_actions(app: &mut AppView) {
let _ = app.deferred_startup.take();
}
/// Replay the session-startup actions deferred until auth + trust both resolved
/// (`--resume` / `--worktree` / initial-prompt / `grok dashboard`). Extracted
/// (`--resume` / `--worktree` / initial-prompt / `kigi dashboard`). Extracted
/// from the `AuthComplete` handler so the folder-trust answer can run the SAME
/// machinery; whichever gate resolves last drains it (each call site guards on
/// the other gate being `Done`, so it runs exactly once).
@@ -606,7 +606,7 @@ pub(in crate::app::dispatch) fn dispatch_trigger_deep_search(
}
}
/// Chat-mode replacement for local deep search: refetch the session list
/// with the picker query pushed down as `x.ai/session/list` `query`.
/// with the picker query pushed down as `kigi/session/list` `query`.
/// Keystrokes are coalesced through [`Effect::DebounceSessionSearch`]; a
/// forced search (Ctrl+/) or a cleared query fetches immediately. Every
/// trigger bumps `session_picker_list_seq`, so stale in-flight debounces
@@ -68,7 +68,7 @@ pub(in crate::app::dispatch) fn dispatch_sessions_confirm_close(
remove_agent_and_cleanup(app, closed_id);
effects
}
/// Rename the current session via x.ai/session/rename.
/// Rename the current session via kigi/session/rename.
///
/// Produces Effect::RenameSession which spawns an async ACP ext request.
/// On completion, TaskResult::RenameSessionComplete shows the result.
@@ -248,7 +248,7 @@ pub(in crate::app::dispatch) fn set_ask_user_question_timeout_enabled(
app: &mut AppView,
new: bool,
) -> Vec<Effect> {
use kigi_tools::implementations::grok_build::ask_user_question;
use kigi_tools::implementations::kigi::ask_user_question;
let prev_state = app.ask_user_question_timeout_enabled;
let prev_effective =
prev_state.unwrap_or(ask_user_question::DEFAULT_ASK_USER_QUESTION_TIMEOUT_ENABLED);
@@ -1206,8 +1206,8 @@ pub(in crate::app::dispatch) fn set_auto_dark_theme(app: &mut AppView, new: Stri
.as_deref()
.and_then(crate::theme::canonical_name)
.filter(|s| *s != "auto")
// No prior config: fall back to GrokNight (the default).
.unwrap_or_else(|| crate::theme::ThemeKind::GrokNight.display_name());
// No prior config: fall back to KigiNight (the default).
.unwrap_or_else(|| crate::theme::ThemeKind::KigiNight.display_name());
let new_canonical = match crate::theme::canonical_name(&new) {
Some(c) if c != crate::theme::ThemeKind::Auto.display_name() => c,
_ => {
@@ -1320,7 +1320,7 @@ pub(in crate::app::dispatch) fn set_auto_light_theme(
.as_deref()
.and_then(crate::theme::canonical_name)
.filter(|s| *s != "auto")
.unwrap_or_else(|| crate::theme::ThemeKind::GrokDay.display_name());
.unwrap_or_else(|| crate::theme::ThemeKind::KigiDay.display_name());
let new_canonical = match crate::theme::canonical_name(&new) {
Some(c) if c != crate::theme::ThemeKind::Auto.display_name() => c,
_ => {
@@ -1430,7 +1430,7 @@ pub(in crate::app::dispatch) fn set_default_model_inner(
// or `/clear` creates a fresh session by cloning `app.models`
// (`dispatch_new_session_inner_with_id`), so without this the new session —
// and the welcome card it commits — would show the previous default until
// the next `x.ai/models/update` roundtrip.
// the next `kigi/models/update` roundtrip.
if app.models.available.contains_key(id) {
app.models.set_current(id.clone(), None);
}
@@ -1510,7 +1510,7 @@ pub(in crate::app::dispatch) fn set_default_model(
// Persist the **model ID** (catalog key), not the display name.
// The shell's `resolve_default_model` matches by slug / map key,
// so persisting the human-readable name (e.g. "Grok Build")
// so persisting the human-readable name (e.g. "Kigi")
// would silently fail to resolve on the next startup.
//
// Chat (`--chat` / KIGI_CHAT_MODE) catalogs use opaque `/rest/modes`
@@ -1798,7 +1798,7 @@ pub(in crate::app::dispatch) fn set_max_thoughts_width(app: &mut AppView, new: i
/// (`show_tips`, `auto_update`, ask_user_question timeout).
/// Matches the consumer's `.unwrap_or(...)` fallback.
pub(super) fn pr13_effective_default(key: &str) -> Option<bool> {
use kigi_tools::implementations::grok_build::ask_user_question;
use kigi_tools::implementations::kigi::ask_user_question;
match key {
"show_tips" => Some(true),
"auto_update" => Some(true),
@@ -8,7 +8,7 @@ use crate::app::app_view::{ActiveView, AppView};
use crate::notifications::{NotificationEvent, NotificationEventKind};
use crate::scrollback::block::RenderBlock;
/// Show session info: fetch via x.ai/session/info and display in scrollback.
/// Show session info: fetch via kigi/session/info and display in scrollback.
///
/// Produces Effect::ShowSessionInfo which spawns an async ACP ext request.
/// On completion, TaskResult::SessionInfoComplete shows the formatted info.
@@ -49,7 +49,7 @@ pub(super) fn scrub_error_for_toast(error: &str) -> String {
}
}
/// Show context info: fetch via x.ai/session/info and display rich breakdown.
/// Show context info: fetch via kigi/session/info and display rich breakdown.
///
/// Produces Effect::ShowContextInfo which spawns an async ACP ext request.
/// On completion, TaskResult::ContextInfoComplete shows the formatted info.
@@ -72,7 +72,7 @@ pub(super) fn dispatch_show_context_info(app: &mut AppView) -> Vec<Effect> {
/// `/usage` — fetch Kimi usage/quota rows and display them inline.
///
/// Produces [`Effect::FetchUsage`], which asks the shell's `x.ai/billing`
/// Produces [`Effect::FetchUsage`], which asks the shell's `kigi/billing`
/// extension (`GET {base}/usages`); [`handle_usage_fetched`] renders the
/// rows as a system block in scrollback.
pub(super) fn dispatch_show_usage(app: &mut AppView) -> Vec<Effect> {
@@ -225,7 +225,7 @@ pub(super) fn notify_session_ready(
) {
notification_service.notify(NotificationEvent {
kind: NotificationEventKind::SessionReady,
title: "Grok".into(),
title: "Kigi".into(),
body: NotificationEventKind::SessionReady.as_str().into(),
session_id: agent.session.session_id.as_ref().map(|s| s.0.to_string()),
});
@@ -218,7 +218,7 @@ fn cancel_login_strips_reauth_prompt_from_scrollback() {
}
/// Empty `auth_methods` (preferred_method pin unavailable) must not invent
/// `grok.com` or start an OIDC flow the agent did not advertise.
/// `kimi-code` or start an OIDC flow the agent did not advertise.
#[test]
fn login_with_empty_auth_methods_fails_closed() {
let mut app = test_app_with_agent();
@@ -2,7 +2,7 @@
use super::*;
/// `grok dashboard` before login: the startup hook consumes the
/// `kigi dashboard` before login: the startup hook consumes the
/// `KIGI_OPEN_DASHBOARD_AT_STARTUP` env var and stashes
/// `deferred_startup.open_dashboard`; `AuthComplete` must then open the
/// dashboard view. Regression test for the silent drop where the
@@ -30,7 +30,7 @@ fn auth_complete_opens_deferred_dashboard() {
assert!(matches!(app.auth_state, AuthState::Done));
assert!(
matches!(app.active_view, ActiveView::AgentDashboard),
"deferred `grok dashboard` must open the dashboard after login",
"deferred `kigi dashboard` must open the dashboard after login",
);
assert!(
!app.deferred_startup.open_dashboard,
@@ -575,15 +575,15 @@ fn dashboard_confirm_worktree_without_git_repo_creates_nothing() {
#[test]
fn dashboard_confirm_worktree_applies_pending_model_and_plan() {
let mut app = test_app();
seed_model(&mut app, "grok-4.5", "Grok 4.5");
seed_model(&mut app, "kigi-4.5", "Kigi 4.5");
open_dashboard(&mut app);
app.cwd_has_git_ancestor = true;
let model_id = acp::ModelId::new(std::sync::Arc::from("grok-4.5"));
let model_id = acp::ModelId::new(std::sync::Arc::from("kigi-4.5"));
if let Some(d) = app.dashboard.as_mut() {
d.pending_model = Some(crate::views::dashboard::PendingDispatchModel {
id: model_id.clone(),
effort: Some(kigi_shell::sampling::types::ReasoningEffort::High),
display: "Grok 4.5".to_string(),
display: "Kigi 4.5".to_string(),
});
d.pending_mode = crate::views::dashboard::DashboardDispatchMode::Plan;
d.dispatch.set_text("do the thing");
@@ -1120,7 +1120,7 @@ fn dashboard_peek_cycle_does_not_retire_the_nudge() {
/// leader mode. The dashboard renders local sessions regardless; leader
/// mode only adds the roster poll. Every entry point funnels through
/// `Action::OpenDashboard`, so this covers `/dashboard`, `Ctrl+\`,
/// `grok dashboard`, and the startup hook.
/// `kigi dashboard`, and the startup hook.
#[serial_test::serial(KIGI_AGENT_DASHBOARD)]
#[test]
fn dashboard_open_works_without_leader() {
@@ -1237,9 +1237,9 @@ fn seed_model(app: &mut AppView, id: &str, name: &str) {
#[test]
fn dashboard_slash_model_stages_pending_model() {
let mut app = test_app();
seed_model(&mut app, "grok-4.5", "Grok 4.5");
seed_model(&mut app, "kigi-4.5", "Kigi 4.5");
open_dashboard(&mut app);
let effects = dispatch_dashboard_dispatch_slash(&mut app, "/model grok-4.5".into());
let effects = dispatch_dashboard_dispatch_slash(&mut app, "/model kigi-4.5".into());
assert!(
effects.is_empty(),
"staging a model must not spawn a session"
@@ -1252,8 +1252,8 @@ fn dashboard_slash_model_stages_pending_model() {
.pending_model
.as_ref()
.expect("pending_model must be set");
assert_eq!(pending.id.0.as_ref(), "grok-4.5");
assert_eq!(pending.display, "Grok 4.5");
assert_eq!(pending.id.0.as_ref(), "kigi-4.5");
assert_eq!(pending.display, "Kigi 4.5");
assert!(pending.effort.is_none());
// The catalog snapshot's `current` tracks the staged model so the
// next `/model` dropdown marks it `(current)` (not the seeded default).
@@ -1265,7 +1265,7 @@ fn dashboard_slash_model_stages_pending_model() {
.current
.as_ref()
.map(|id| id.0.as_ref()),
Some("grok-4.5"),
Some("kigi-4.5"),
"staging must update the snapshot's current selection",
);
}
@@ -1279,7 +1279,7 @@ fn dashboard_slash_model_stages_pending_model() {
#[test]
fn dashboard_slash_command_error_gets_error_glyph_prefix() {
let mut app = test_app();
seed_model(&mut app, "grok-4.5", "Grok 4.5");
seed_model(&mut app, "kigi-4.5", "Kigi 4.5");
open_dashboard(&mut app);
let effects = dispatch_dashboard_dispatch_slash(&mut app, "/model nonexistent".into());
assert!(effects.is_empty(), "a failed command must not dispatch");
@@ -1512,14 +1512,14 @@ fn dashboard_cycle_mode_skips_always_approve_under_policy_pin() {
fn dashboard_open_reseeds_pending_model_and_mode() {
use crate::views::dashboard::DashboardDispatchMode;
let mut app = test_app();
seed_model(&mut app, "grok-4.5", "Grok 4.5");
seed_model(&mut app, "kigi-4.5", "Kigi 4.5");
open_dashboard(&mut app);
// Stage a model + non-default mode as if from a previous session.
if let Some(d) = app.dashboard.as_mut() {
d.pending_model = Some(crate::views::dashboard::PendingDispatchModel {
id: acp::ModelId::new(std::sync::Arc::from("grok-4.5")),
id: acp::ModelId::new(std::sync::Arc::from("kigi-4.5")),
effort: None,
display: "Grok 4.5".to_string(),
display: "Kigi 4.5".to_string(),
});
d.pending_mode = DashboardDispatchMode::Plan;
}
@@ -1727,14 +1727,14 @@ fn dashboard_dispatch_new_agent_is_working_with_prompt_title() {
#[test]
fn dashboard_dispatch_applies_pending_model_and_plan() {
let mut app = test_app();
seed_model(&mut app, "grok-4.5", "Grok 4.5");
seed_model(&mut app, "kigi-4.5", "Kigi 4.5");
open_dashboard(&mut app);
let model_id = acp::ModelId::new(std::sync::Arc::from("grok-4.5"));
let model_id = acp::ModelId::new(std::sync::Arc::from("kigi-4.5"));
if let Some(d) = app.dashboard.as_mut() {
d.pending_model = Some(crate::views::dashboard::PendingDispatchModel {
id: model_id.clone(),
effort: Some(kigi_shell::sampling::types::ReasoningEffort::High),
display: "Grok 4.5".to_string(),
display: "Kigi 4.5".to_string(),
});
d.pending_mode = crate::views::dashboard::DashboardDispatchMode::Plan;
}
@@ -1770,14 +1770,14 @@ fn dashboard_dispatch_applies_pending_model_and_plan() {
#[test]
fn dashboard_new_agent_button_applies_pending_model_and_plan() {
let mut app = test_app();
seed_model(&mut app, "grok-4.5", "Grok 4.5");
seed_model(&mut app, "kigi-4.5", "Kigi 4.5");
open_dashboard(&mut app);
let model_id = acp::ModelId::new(std::sync::Arc::from("grok-4.5"));
let model_id = acp::ModelId::new(std::sync::Arc::from("kigi-4.5"));
if let Some(d) = app.dashboard.as_mut() {
d.pending_model = Some(crate::views::dashboard::PendingDispatchModel {
id: model_id.clone(),
effort: Some(kigi_shell::sampling::types::ReasoningEffort::High),
display: "Grok 4.5".to_string(),
display: "Kigi 4.5".to_string(),
});
d.pending_mode = crate::views::dashboard::DashboardDispatchMode::Plan;
}
@@ -4624,7 +4624,7 @@ fn dashboard_permission_followup_rejects_with_message() {
fn dashboard_question_answer_sends_and_clears() {
use crate::views::prompt_widget::StashedPrompt;
use crate::views::question_view::QuestionViewState;
use kigi_tools::implementations::grok_build::ask_user_question::{
use kigi_tools::implementations::kigi::ask_user_question::{
AskUserQuestionMode, Question, QuestionOption,
};
@@ -4673,7 +4673,7 @@ fn dashboard_question_answer_walks_multiple_questions() {
use crate::views::dashboard::peek::{PeekPanelState, compute_peek_fields};
use crate::views::prompt_widget::StashedPrompt;
use crate::views::question_view::QuestionViewState;
use kigi_tools::implementations::grok_build::ask_user_question::{
use kigi_tools::implementations::kigi::ask_user_question::{
AskUserQuestionMode, Question, QuestionOption,
};
@@ -116,8 +116,8 @@ fn test_app() -> AppView {
agent_override: None,
bootstrap_acp_commands: Vec::new(),
auth_methods: vec![acp::AuthMethod::Agent(acp::AuthMethodAgent::new(
acp::AuthMethodId::new("grok.com"),
"Grok".to_string(),
acp::AuthMethodId::new("kimi-code"),
"Kigi".to_string(),
))],
auth_state: AuthState::Done,
trust_state: TrustState::Done,
@@ -462,7 +462,7 @@ fn fork_test_app() -> AppView {
app
}
/// Build a minimal `AcpArgs<acp::ExtRequest>` for an
/// `x.ai/ask_user_question` ext-method request. Returns the args
/// `kigi/ask_user_question` ext-method request. Returns the args
/// plus the receiver half of the response oneshot so the test can
/// assert the handler completes the ACP roundtrip.
fn make_ask_user_question_args(
@@ -471,14 +471,13 @@ fn make_ask_user_question_args(
kigi_acp_lib::AcpArgs<acp::ExtRequest>,
tokio::sync::oneshot::Receiver<kigi_acp_lib::AcpResult<acp::ExtResponse>>,
) {
use kigi_tools::implementations::grok_build::ask_user_question::{
use kigi_tools::implementations::kigi::ask_user_question::{
AskUserQuestionExtRequest, Question, QuestionOption,
};
let req = AskUserQuestionExtRequest {
session_id: "test-session".into(),
tool_call_id: tool_call_id.into(),
mode:
kigi_tools::implementations::grok_build::ask_user_question::AskUserQuestionMode::Default,
mode: kigi_tools::implementations::kigi::ask_user_question::AskUserQuestionMode::Default,
questions: vec![Question {
question: "ACP-driven question".into(),
options: vec![QuestionOption {
@@ -493,7 +492,7 @@ fn make_ask_user_question_args(
};
let (tx, rx) = tokio::sync::oneshot::channel();
let ext = acp::ExtRequest::new(
"x.ai/ask_user_question",
"kigi/ask_user_question",
serde_json::value::to_raw_value(&req)
.expect("serialize AskUserQuestionExtRequest")
.into(),
@@ -742,7 +741,7 @@ fn with_theme_test_env(f: impl FnOnce()) {
.unwrap_or_else(|e| e.into_inner());
crate::theme::cache::reset_for_test();
crate::theme::cache::seed_auto_theme_defaults_for_test();
crate::theme::cache::set(crate::theme::ThemeKind::GrokNight);
crate::theme::cache::set(crate::theme::ThemeKind::KigiNight);
crate::theme::system_appearance::clear_mock();
f();
crate::theme::system_appearance::clear_mock();
@@ -598,7 +598,7 @@ fn yolo_on_drain_clears_double_click_tracker() {
/// 2. The dispatcher returns a `PersistPermissionMode` effect with
/// canonical `"always-approve"` — this is what flips
/// `[ui] permission_mode` on disk AND fires the
/// `x.ai/yolo_mode_changed` ACP notification back to the shell.
/// `kigi/yolo_mode_changed` ACP notification back to the shell.
/// 3. The agent's per-session `yolo_mode` flag is flipped to true,
/// so subsequent permission requests are auto-approved by
/// `handle_permission_request`.
@@ -651,7 +651,7 @@ fn enable_always_approve_sends_response_and_flips_yolo_and_persists() {
// (2) The dispatcher returns a PersistPermissionMode effect with
// canonical "always-approve". This is the bridge that writes
// ~/.kigi/config.toml AND fires x.ai/yolo_mode_changed.
// ~/.kigi/config.toml AND fires kigi/yolo_mode_changed.
let persist = effects
.iter()
.find_map(|e| match e {
@@ -725,7 +725,7 @@ fn enable_always_approve_is_idempotent_when_yolo_already_on() {
.any(|e| matches!(e, Effect::PersistPermissionMode { .. })),
"redundant PersistPermissionMode when YOLO already on — the dispatcher \
must short-circuit to avoid double-writing config.toml and double-firing \
x.ai/yolo_mode_changed",
kigi/yolo_mode_changed",
);
}
@@ -24,7 +24,7 @@ fn manual_recap_with_no_messages_toasts_empty_state_and_skips_request() {
assert!(
effects.is_empty(),
"empty session must not fire x.ai/recap: {effects:?}"
"empty session must not fire kigi/recap: {effects:?}"
);
let agent = app.agents.get(&id).unwrap();
assert!(agent.pending_recap_entry.is_none(), "no loading spinner");
@@ -86,7 +86,7 @@ fn manual_recap_during_batch_load_with_prompts_still_requests() {
assert!(
matches!(effects.as_slice(), [Effect::SendRecap { auto: false, .. }]),
"batched resume with user prompts must still fire x.ai/recap: {effects:?}"
"batched resume with user prompts must still fire kigi/recap: {effects:?}"
);
let agent = app.agents.get(&id).unwrap();
assert!(agent.pending_recap_entry.is_some());
@@ -846,7 +846,7 @@ fn prompt_response_resets_turn_state() {
assert_eq!(app.agents[&id].scrollback.len(), 1);
}
/// Turn end with prompt suggestions enabled fires the `x.ai/suggestPrompt`
/// Turn end with prompt suggestions enabled fires the `kigi/suggestPrompt`
/// fetch (before the billing refresh), and the loaded suggestion routes back
/// into the agent's controller by id + generation.
#[test]
@@ -879,8 +879,8 @@ fn turn_end_fetches_prompt_suggestion_when_enabled() {
};
assert_eq!(*agent_id, id);
assert!(session_id.is_some());
// No `grok-build-0.1` in the test catalog and no env override →
// `None` on the wire; the shell then uses its own `grok-build-0.1`
// No `kigi-0.1` in the test catalog and no env override →
// `None` on the wire; the shell then uses its own `kigi-0.1`
// default (suggestion calls never use the session model).
assert_eq!(*model, None);
@@ -1255,7 +1255,7 @@ fn turn_complete_notification_suppressed_when_queue_non_empty() {
/// Regression: cancelling while prompts are queued must hand the queue to
/// the agent untouched. The FRONT queued prompt runs next (promoted
/// server-side), the rest stay queued in order, and the authoritative
/// `x.ai/queue/changed` rebroadcast — not client-side prediction — updates
/// `kigi/queue/changed` rebroadcast — not client-side prediction — updates
/// the mirror. Nothing resurrects or reorders.
#[test]
fn cancel_hands_queue_to_agent_without_reordering() {
@@ -1771,7 +1771,7 @@ fn send_prompt_works_after_reconnect_clears() {
fn switch_model_holds_prompt_until_complete() {
let mut app = test_app_with_agent();
let id = AgentId(0);
let model_id = acp::ModelId::new(std::sync::Arc::from("grok-4.5"));
let model_id = acp::ModelId::new(std::sync::Arc::from("kigi-4.5"));
dispatch(
Action::SwitchModel {
@@ -1905,7 +1905,7 @@ fn submit_question_answers_cancel_clears_local_modal_and_restores_prompt() {
// exercising the prompt.restore + cleanup_question_state contract
// that lives in `submit_question_answers` itself.
use crate::views::question_view::{LocalQuestionKind, QuestionViewState};
use kigi_tools::implementations::grok_build::ask_user_question::{Question, QuestionOption};
use kigi_tools::implementations::kigi::ask_user_question::{Question, QuestionOption};
let mut app = fork_test_app();
let id = AgentId(0);
@@ -255,7 +255,7 @@ fn mark_turn_finished_clears_start_and_stamps_active() {
fn switch_model_dispatch_produces_effect_and_sets_pending() {
let mut app = test_app_with_agent();
let id = AgentId(0);
let model_id = acp::ModelId::new(std::sync::Arc::from("grok-4.5"));
let model_id = acp::ModelId::new(std::sync::Arc::from("kigi-4.5"));
assert!(!app.agents[&id].session.model_switch_pending);
let effects = dispatch(
Action::SwitchModel {
@@ -989,7 +989,7 @@ fn dispatch_fork_no_flag_always_reopens_modal_after_previous_answer() {
#[test]
fn translate_local_submit_skipped_returns_changed_with_no_action() {
use crate::views::question_view::{LocalQuestionKind, QuestionViewState};
use kigi_tools::implementations::grok_build::ask_user_question::{Question, QuestionOption};
use kigi_tools::implementations::kigi::ask_user_question::{Question, QuestionOption};
let q = Question {
question: "?".into(),
options: vec![QuestionOption {
@@ -1018,7 +1018,7 @@ fn translate_local_submit_skipped_returns_changed_with_no_action() {
#[test]
fn translate_local_submit_no_selection_returns_changed_no_action() {
use crate::views::question_view::{LocalQuestionKind, QuestionViewState};
use kigi_tools::implementations::grok_build::ask_user_question::{Question, QuestionOption};
use kigi_tools::implementations::kigi::ask_user_question::{Question, QuestionOption};
let q = Question {
question: "?".into(),
options: (0..2)
@@ -1047,7 +1047,7 @@ fn translate_local_submit_no_selection_returns_changed_no_action() {
#[test]
fn translate_local_submit_out_of_range_index_returns_changed_no_action() {
use crate::views::question_view::{LocalQuestionKind, QuestionViewState};
use kigi_tools::implementations::grok_build::ask_user_question::{Question, QuestionOption};
use kigi_tools::implementations::kigi::ask_user_question::{Question, QuestionOption};
let q = Question {
question: "?".into(),
options: (0..2)
@@ -1077,7 +1077,7 @@ fn translate_local_submit_out_of_range_index_returns_changed_no_action() {
#[test]
fn handle_ask_user_question_does_not_push_system_block_when_displaced_acp_modal() {
use crate::views::question_view::QuestionViewState;
use kigi_tools::implementations::grok_build::ask_user_question::{Question, QuestionOption};
use kigi_tools::implementations::kigi::ask_user_question::{Question, QuestionOption};
let mut app = fork_test_app();
let id = AgentId(0);
let stashed = app.agents.get_mut(&id).unwrap().prompt.stash();
@@ -30,8 +30,8 @@ fn worktree_forked_sets_session_id_eagerly_and_emits_load() {
assert!(app.agents[&id].session.session_id.is_none());
assert!(!app.agents[&id].session.loading_replay);
let worktree_path = PathBuf::from("/tmp/grok-worktrees/pager-fork");
let session_cwd = PathBuf::from("/tmp/grok-worktrees/pager-fork/sub");
let worktree_path = PathBuf::from("/tmp/kigi-worktrees/pager-fork");
let session_cwd = PathBuf::from("/tmp/kigi-worktrees/pager-fork/sub");
let effects = dispatch(
Action::TaskComplete(TaskResult::WorktreeForked {
agent_id: id,
@@ -76,8 +76,8 @@ fn worktree_forked_with_restore_shows_summary_in_scrollback() {
);
let id = AgentId(0);
let worktree_path = PathBuf::from("/tmp/grok-worktrees/pager-fork");
let session_cwd = PathBuf::from("/tmp/grok-worktrees/pager-fork/sub");
let worktree_path = PathBuf::from("/tmp/kigi-worktrees/pager-fork");
let session_cwd = PathBuf::from("/tmp/kigi-worktrees/pager-fork/sub");
let effects = dispatch(
Action::TaskComplete(TaskResult::WorktreeForked {
agent_id: id,
@@ -130,8 +130,8 @@ fn worktree_forked_with_restore_failure_shows_warning_banner() {
);
let id = AgentId(0);
let worktree_path = PathBuf::from("/tmp/grok-worktrees/pager-fail");
let session_cwd = PathBuf::from("/tmp/grok-worktrees/pager-fail/sub");
let worktree_path = PathBuf::from("/tmp/kigi-worktrees/pager-fail");
let session_cwd = PathBuf::from("/tmp/kigi-worktrees/pager-fail/sub");
dispatch(
Action::TaskComplete(TaskResult::WorktreeForked {
agent_id: id,
@@ -472,7 +472,7 @@ fn open_fork_question_refuses_when_existing_question_is_open() {
let mut app = fork_test_app();
// Plant an existing question (e.g. an ACP-driven one).
use crate::views::question_view::QuestionViewState;
use kigi_tools::implementations::grok_build::ask_user_question::{Question, QuestionOption};
use kigi_tools::implementations::kigi::ask_user_question::{Question, QuestionOption};
let q = Question {
question: "existing ACP question?".into(),
options: vec![QuestionOption {
@@ -1047,7 +1047,7 @@ fn fork_session_failed_pushes_turn_failed_block() {
#[test]
fn translate_local_submit_yes_returns_worktree_true_action() {
use crate::views::question_view::{LocalQuestionKind, QuestionViewState};
use kigi_tools::implementations::grok_build::ask_user_question::{Question, QuestionOption};
use kigi_tools::implementations::kigi::ask_user_question::{Question, QuestionOption};
let q = Question {
question: "?".into(),
options: (0..2)
@@ -1090,7 +1090,7 @@ fn translate_local_submit_yes_returns_worktree_true_action() {
#[test]
fn translate_local_submit_no_returns_worktree_false_action() {
use crate::views::question_view::{LocalQuestionKind, QuestionViewState};
use kigi_tools::implementations::grok_build::ask_user_question::{Question, QuestionOption};
use kigi_tools::implementations::kigi::ask_user_question::{Question, QuestionOption};
let q = Question {
question: "?".into(),
options: (0..2)
@@ -1131,7 +1131,7 @@ fn translate_local_submit_no_returns_worktree_false_action() {
#[test]
fn translate_local_submit_always_returns_persist_always_for_fork() {
use crate::views::question_view::{LocalQuestionKind, QuestionViewState};
use kigi_tools::implementations::grok_build::ask_user_question::{Question, QuestionOption};
use kigi_tools::implementations::kigi::ask_user_question::{Question, QuestionOption};
let q = Question {
question: "?".into(),
options: (0..4)
@@ -1173,7 +1173,7 @@ fn translate_local_submit_always_returns_persist_always_for_fork() {
#[test]
fn translate_local_submit_never_returns_persist_never_for_fork() {
use crate::views::question_view::{LocalQuestionKind, QuestionViewState};
use kigi_tools::implementations::grok_build::ask_user_question::{Question, QuestionOption};
use kigi_tools::implementations::kigi::ask_user_question::{Question, QuestionOption};
let q = Question {
question: "?".into(),
options: (0..4)
@@ -1216,7 +1216,7 @@ fn translate_local_submit_never_returns_persist_never_for_fork() {
fn handle_ask_user_question_pushes_system_block_when_displaced_local_fork_modal() {
use crate::scrollback::block::RenderBlock;
use crate::views::question_view::{LocalQuestionKind, QuestionViewState};
use kigi_tools::implementations::grok_build::ask_user_question::{Question, QuestionOption};
use kigi_tools::implementations::kigi::ask_user_question::{Question, QuestionOption};
let mut app = fork_test_app();
let id = AgentId(0);
@@ -172,7 +172,7 @@ fn worktree_session_created_sets_session_and_cwd() {
&mut app,
);
let id = AgentId(0);
let worktree_path = PathBuf::from("/tmp/grok-worktrees/pager-123");
let worktree_path = PathBuf::from("/tmp/kigi-worktrees/pager-123");
let session_cwd = worktree_path.clone();
let effects = dispatch(
Action::TaskComplete(TaskResult::WorktreeSessionCreated {
@@ -353,13 +353,13 @@ fn worktree_session_created_drains_queued_prompts() {
let effects = dispatch(Action::SendPrompt("hello".into()), &mut app);
assert!(effects.is_empty(), "no session_id yet, can't drain");
assert_eq!(app.agents[&id].session.queue_len(), 1);
let worktree_path = PathBuf::from("/tmp/grok-worktrees/pager-abc");
let worktree_path = PathBuf::from("/tmp/kigi-worktrees/pager-abc");
let effects = dispatch(
Action::TaskComplete(TaskResult::WorktreeSessionCreated {
agent_id: id,
session_id: acp::SessionId::new("wt-drain-1"),
worktree_path,
session_cwd: PathBuf::from("/tmp/grok-worktrees/pager-abc"),
session_cwd: PathBuf::from("/tmp/kigi-worktrees/pager-abc"),
models: None,
}),
&mut app,
@@ -485,7 +485,7 @@ fn switch_model_without_session_does_nothing() {
let mut app = test_app_with_agent();
let id = AgentId(0);
app.agents.get_mut(&id).unwrap().session.session_id = None;
let model_id = acp::ModelId::new(std::sync::Arc::from("grok-4.5"));
let model_id = acp::ModelId::new(std::sync::Arc::from("kigi-4.5"));
let effects = dispatch(
Action::SwitchModel {
model_id,
@@ -659,7 +659,7 @@ fn new_session_starts_with_prompt_focused() {
fn switch_model_deferred_when_no_session_id() {
let mut app = test_app_with_agent();
let id = AgentId(0);
let model_id = acp::ModelId::new(std::sync::Arc::from("grok-4.5"));
let model_id = acp::ModelId::new(std::sync::Arc::from("kigi-4.5"));
app.agents.get_mut(&id).unwrap().session.session_id = None;
let effects = dispatch(
Action::SwitchModel {
@@ -679,7 +679,7 @@ fn switch_model_deferred_when_no_session_id() {
fn deferred_model_switch_applied_on_session_created() {
let mut app = test_app_with_agent();
let id = AgentId(0);
let model_id = acp::ModelId::new(std::sync::Arc::from("grok-4.5"));
let model_id = acp::ModelId::new(std::sync::Arc::from("kigi-4.5"));
let session_id: acp::SessionId = "new-session".into();
app.agents.get_mut(&id).unwrap().session.session_id = None;
app.agents
@@ -708,7 +708,7 @@ fn deferred_model_switch_applied_on_session_created() {
#[test]
fn deferred_model_switch_applied_on_worktree_session_created() {
let mut app = test_app_git();
let model_id = acp::ModelId::new(std::sync::Arc::from("grok-4.5"));
let model_id = acp::ModelId::new(std::sync::Arc::from("kigi-4.5"));
dispatch(
Action::NewWorktreeSession {
load_session_id: None,
@@ -1314,7 +1314,7 @@ fn dispatch_new_session_has_empty_scrollback() {
#[test]
fn translate_local_submit_always_returns_persist_always_for_new_session() {
use crate::views::question_view::{LocalQuestionKind, QuestionViewState};
use kigi_tools::implementations::grok_build::ask_user_question::{Question, QuestionOption};
use kigi_tools::implementations::kigi::ask_user_question::{Question, QuestionOption};
let q = Question {
question: "?".into(),
options: (0..4)
@@ -1354,7 +1354,7 @@ fn translate_local_submit_always_returns_persist_always_for_new_session() {
#[test]
fn translate_local_submit_never_returns_persist_never_for_new_session() {
use crate::views::question_view::{LocalQuestionKind, QuestionViewState};
use kigi_tools::implementations::grok_build::ask_user_question::{Question, QuestionOption};
use kigi_tools::implementations::kigi::ask_user_question::{Question, QuestionOption};
let q = Question {
question: "?".into(),
options: (0..4)
@@ -1214,7 +1214,7 @@ fn project_picker_skip_falls_back_to_original_cwd() {
fn project_picker_freeform_path_used_when_no_option_selected() {
use crate::views::prompt_widget::StashedPrompt;
use crate::views::question_view::{LocalQuestionKind, QuestionViewState};
use kigi_tools::implementations::grok_build::ask_user_question::{Question, QuestionOption};
use kigi_tools::implementations::kigi::ask_user_question::{Question, QuestionOption};
let q = Question {
question: "Pick".into(),
id: None,
@@ -1253,7 +1253,7 @@ fn project_picker_freeform_path_used_when_no_option_selected() {
fn project_picker_freeform_overrides_dont_ask() {
use crate::views::prompt_widget::StashedPrompt;
use crate::views::question_view::{LocalQuestionKind, QuestionSelection, QuestionViewState};
use kigi_tools::implementations::grok_build::ask_user_question::{Question, QuestionOption};
use kigi_tools::implementations::kigi::ask_user_question::{Question, QuestionOption};
let opt = |label: &str| QuestionOption {
label: label.into(),
description: String::new(),
@@ -1310,7 +1310,7 @@ fn needs_project_picker_false_when_disabled() {
fn project_picker_dont_ask_again_sets_disable_flag() {
use crate::views::prompt_widget::StashedPrompt;
use crate::views::question_view::{LocalQuestionKind, QuestionSelection, QuestionViewState};
use kigi_tools::implementations::grok_build::ask_user_question::{Question, QuestionOption};
use kigi_tools::implementations::kigi::ask_user_question::{Question, QuestionOption};
let opt = |label: &str| QuestionOption {
label: label.into(),
description: String::new(),
@@ -1348,7 +1348,7 @@ fn project_picker_dont_ask_again_sets_disable_flag() {
fn project_picker_recent_project_selection_uses_that_path() {
use crate::views::prompt_widget::StashedPrompt;
use crate::views::question_view::{LocalQuestionKind, QuestionSelection, QuestionViewState};
use kigi_tools::implementations::grok_build::ask_user_question::{Question, QuestionOption};
use kigi_tools::implementations::kigi::ask_user_question::{Question, QuestionOption};
let opt = |label: &str| QuestionOption {
label: label.into(),
description: String::new(),
@@ -24,7 +24,7 @@ fn model_with_support(id: &str, supports: bool) -> (acp::ModelId, acp::ModelInfo
}
fn models_with_current(supports: bool) -> ModelState {
let (id, info) = model_with_support("grok-build", supports);
let (id, info) = model_with_support("kigi", supports);
let mut models = ModelState::default();
models.available.insert(id.clone(), info);
models.current = Some(id);
@@ -227,7 +227,7 @@ fn set_default_model_allowed_when_agent_chat_kind() {
fn slash_model_valid_dispatches_set_default_model_with_switch_and_persist() {
let mut app = test_app_with_agent();
let id = AgentId(0);
let model_id = acp::ModelId::new(std::sync::Arc::from("grok-4.5"));
let model_id = acp::ModelId::new(std::sync::Arc::from("kigi-4.5"));
app.agents
.get_mut(&id)
.unwrap()
@@ -236,9 +236,9 @@ fn slash_model_valid_dispatches_set_default_model_with_switch_and_persist() {
.available
.insert(
model_id.clone(),
acp::ModelInfo::new(model_id.clone(), "Grok 4.5".to_string()),
acp::ModelInfo::new(model_id.clone(), "Kigi 4.5".to_string()),
);
let effects = dispatch(Action::SendPrompt("/model Grok 4.5".into()), &mut app);
let effects = dispatch(Action::SendPrompt("/model Kigi 4.5".into()), &mut app);
assert_eq!(
effects.len(),
2,
@@ -904,8 +904,8 @@ fn clear_default_model_persists_but_keeps_live_current() {
use agent_client_protocol as acp;
use std::sync::Arc;
let mut app = test_app_with_agent();
let id = acp::ModelId::new(Arc::from("grok-test"));
let info = acp::ModelInfo::new(id.clone(), "Grok Test".to_string());
let id = acp::ModelId::new(Arc::from("kigi-test"));
let info = acp::ModelInfo::new(id.clone(), "Kigi Test".to_string());
let agent_id = AgentId(0);
app.agents
.get_mut(&agent_id)
@@ -949,8 +949,8 @@ fn set_default_model_resolves_known_name() {
use agent_client_protocol as acp;
use std::sync::Arc;
let mut app = test_app_with_agent();
let id = acp::ModelId::new(Arc::from("grok-4.5"));
let info = acp::ModelInfo::new(id.clone(), "Grok 4.5".to_string());
let id = acp::ModelId::new(Arc::from("kigi-4.5"));
let info = acp::ModelInfo::new(id.clone(), "Kigi 4.5".to_string());
let agent_id = AgentId(0);
app.agents
.get_mut(&agent_id)
@@ -963,7 +963,7 @@ fn set_default_model_resolves_known_name() {
assert_eq!(effects.len(), 2);
assert!(
matches!(& effects[0], Effect::PersistSetting { key : "default_model", value :
crate ::settings::SettingValue::String(s), .. } if s == "grok-4.5")
crate ::settings::SettingValue::String(s), .. } if s == "kigi-4.5")
);
assert!(matches!(& effects[1], Effect::SwitchModel { model_id : mid, .. } if mid == & id));
assert_eq!(app.agents[&agent_id].session.models.current, Some(id));
@@ -976,8 +976,8 @@ fn set_default_model_idempotent_when_already_current() {
use agent_client_protocol as acp;
use std::sync::Arc;
let mut app = test_app_with_agent();
let id = acp::ModelId::new(Arc::from("grok-already"));
let info = acp::ModelInfo::new(id.clone(), "Grok Already".to_string());
let id = acp::ModelId::new(Arc::from("kigi-already"));
let info = acp::ModelInfo::new(id.clone(), "Kigi Already".to_string());
let agent_id = AgentId(0);
app.agents
.get_mut(&agent_id)
@@ -2666,16 +2666,16 @@ fn dispatch_cycle_mode_refreshes_open_modal_snapshot() {
"current_value_for must read the refreshed snapshot",
);
}
/// `dispatch(Action::SetTheme("grokday"), &mut app)` emits
/// `dispatch(Action::SetTheme("kigiday"), &mut app)` emits
/// exactly one `Effect::PersistSetting`, mutates
/// `app.current_ui.theme`, fires a toast, and toggles AUTO_MODE
/// off (kind is concrete).
///
/// Note: we persist `grokday` (a non-truecolor theme) here
/// Note: we persist `kigiday` (a non-truecolor theme) here
/// because `Effect::PersistSetting`'s payload is `&'static str`
/// from the registry's canonical table — the persisted CANONICAL
/// is what we're asserting, NOT the live theme cache (which
/// `clamp_to_terminal` might fold to GrokNight in non-truecolor
/// `clamp_to_terminal` might fold to KigiNight in non-truecolor
/// test environments). Persist + canonical contract is the test
/// invariant; the cache contract is exercised separately by the
/// `*_applies_when_*` tests.
@@ -2685,8 +2685,8 @@ fn set_theme_emits_persist_setting_with_correct_payload() {
with_theme_test_env(|| {
let mut app = test_app_with_agent();
assert_eq!(app.current_ui.theme, None);
crate::theme::cache::set(crate::theme::ThemeKind::GrokNight);
let effects = dispatch(Action::SetTheme("grokday".into()), &mut app);
crate::theme::cache::set(crate::theme::ThemeKind::KigiNight);
let effects = dispatch(Action::SetTheme("kigiday".into()), &mut app);
assert_eq!(effects.len(), 1);
match &effects[0] {
Effect::PersistSetting {
@@ -2695,12 +2695,12 @@ fn set_theme_emits_persist_setting_with_correct_payload() {
rollback_value,
} => {
assert_eq!(*key, "theme");
assert_eq!(*value, SettingValue::Enum("grokday"));
assert_eq!(*rollback_value, SettingValue::Enum("groknight"));
assert_eq!(*value, SettingValue::Enum("kigiday"));
assert_eq!(*rollback_value, SettingValue::Enum("kiginight"));
}
other => panic!("expected PersistSetting, got {other:?}"),
}
assert_eq!(app.current_ui.theme.as_deref(), Some("grokday"));
assert_eq!(app.current_ui.theme.as_deref(), Some("kigiday"));
assert!(
!crate::theme::cache::is_auto_mode(),
"concrete theme commit must disable AUTO_MODE",
@@ -2708,7 +2708,7 @@ fn set_theme_emits_persist_setting_with_correct_payload() {
});
}
/// Same payload contract as `set_theme_emits_persist_setting_with_correct_payload`
/// for the auto-dark sibling. Uses `grokday` to avoid the
/// for the auto-dark sibling. Uses `kigiday` to avoid the
/// `clamp_to_terminal` ambiguity in non-truecolor test envs;
/// `apply_kind` doesn't fire here anyway (parent theme is not
/// auto by default) but using a non-truecolor canonical keeps
@@ -2718,7 +2718,7 @@ fn set_auto_dark_theme_emits_persist_setting_with_correct_payload() {
use crate::settings::SettingValue;
with_theme_test_env(|| {
let mut app = test_app_with_agent();
let effects = dispatch(Action::SetAutoDarkTheme("grokday".into()), &mut app);
let effects = dispatch(Action::SetAutoDarkTheme("kigiday".into()), &mut app);
assert_eq!(effects.len(), 1);
match &effects[0] {
Effect::PersistSetting {
@@ -2727,12 +2727,12 @@ fn set_auto_dark_theme_emits_persist_setting_with_correct_payload() {
rollback_value,
} => {
assert_eq!(*key, "auto_dark_theme");
assert_eq!(*value, SettingValue::Enum("grokday"));
assert_eq!(*rollback_value, SettingValue::Enum("groknight"));
assert_eq!(*value, SettingValue::Enum("kigiday"));
assert_eq!(*rollback_value, SettingValue::Enum("kiginight"));
}
other => panic!("expected PersistSetting, got {other:?}"),
}
assert_eq!(app.current_ui.auto_dark_theme.as_deref(), Some("grokday"));
assert_eq!(app.current_ui.auto_dark_theme.as_deref(), Some("kigiday"));
});
}
#[test]
@@ -2740,7 +2740,7 @@ fn set_auto_light_theme_emits_persist_setting_with_correct_payload() {
use crate::settings::SettingValue;
with_theme_test_env(|| {
let mut app = test_app_with_agent();
let effects = dispatch(Action::SetAutoLightTheme("groknight".into()), &mut app);
let effects = dispatch(Action::SetAutoLightTheme("kiginight".into()), &mut app);
assert_eq!(effects.len(), 1);
match &effects[0] {
Effect::PersistSetting {
@@ -2749,14 +2749,14 @@ fn set_auto_light_theme_emits_persist_setting_with_correct_payload() {
rollback_value,
} => {
assert_eq!(*key, "auto_light_theme");
assert_eq!(*value, SettingValue::Enum("groknight"));
assert_eq!(*rollback_value, SettingValue::Enum("grokday"));
assert_eq!(*value, SettingValue::Enum("kiginight"));
assert_eq!(*rollback_value, SettingValue::Enum("kigiday"));
}
other => panic!("expected PersistSetting, got {other:?}"),
}
assert_eq!(
app.current_ui.auto_light_theme.as_deref(),
Some("groknight"),
Some("kiginight"),
);
});
}
@@ -2809,11 +2809,11 @@ fn preview_auto_light_theme_emits_no_persist_and_no_current_ui_mutation() {
/// Auto-theme commit applies the live theme **only** when
/// `theme="auto"` AND the system is in the matching mode.
///
/// Scenario: `theme="groknight"` (concrete) + system=Dark. User
/// commits `auto_dark_theme="grokday"`. The setting is dormant
/// Scenario: `theme="kiginight"` (concrete) + system=Dark. User
/// commits `auto_dark_theme="kigiday"`. The setting is dormant
/// (parent theme is concrete, not auto), so the live display
/// must stay on GrokNight even though we're committing a
/// different theme. Uses `grokday` to avoid `clamp_to_terminal`
/// must stay on KigiNight even though we're committing a
/// different theme. Uses `kigiday` to avoid `clamp_to_terminal`
/// ambiguity in non-truecolor envs.
#[test]
fn set_auto_dark_theme_does_not_apply_when_theme_is_not_auto() {
@@ -2822,28 +2822,28 @@ fn set_auto_dark_theme_does_not_apply_when_theme_is_not_auto() {
crate::theme::system_appearance::SystemAppearance::Dark,
));
let mut app = test_app_with_agent();
let _ = dispatch(Action::SetTheme("groknight".into()), &mut app);
let _ = dispatch(Action::SetTheme("kiginight".into()), &mut app);
assert_eq!(
crate::theme::cache::current_kind(),
crate::theme::ThemeKind::GrokNight,
crate::theme::ThemeKind::KigiNight,
);
let _ = dispatch(Action::SetAutoDarkTheme("grokday".into()), &mut app);
let _ = dispatch(Action::SetAutoDarkTheme("kigiday".into()), &mut app);
assert_eq!(
crate::theme::cache::current_kind(),
crate::theme::ThemeKind::GrokNight,
crate::theme::ThemeKind::KigiNight,
"auto_dark_theme commit must NOT change live display when theme is not auto",
);
assert_eq!(app.current_ui.auto_dark_theme.as_deref(), Some("grokday"));
assert_eq!(app.current_ui.auto_dark_theme.as_deref(), Some("kigiday"));
});
}
/// Auto-theme commit DOES apply the live theme when both
/// (a) parent theme = auto AND (b) system matches.
///
/// Uses `GrokDay` (non-truecolor-requiring) for the dark-mode
/// Uses `KigiDay` (non-truecolor-requiring) for the dark-mode
/// fixture: the test environment's color detection may not report
/// truecolor support, and `Theme::apply_kind` clamps
/// truecolor-only themes (TokyoNight, RosePineMoon) down to
/// GrokNight. Using a non-truecolor theme avoids the clamp
/// KigiNight. Using a non-truecolor theme avoids the clamp
/// uncertainty. The "live apply" contract is what we're testing
/// — the specific theme picked is incidental.
#[test]
@@ -2857,21 +2857,21 @@ fn set_auto_dark_theme_applies_when_theme_is_auto_and_system_is_dark() {
assert!(crate::theme::cache::is_auto_mode());
assert_eq!(
crate::theme::cache::current_kind(),
crate::theme::ThemeKind::GrokNight,
crate::theme::ThemeKind::KigiNight,
);
let _ = dispatch(Action::SetAutoDarkTheme("grokday".into()), &mut app);
let _ = dispatch(Action::SetAutoDarkTheme("kigiday".into()), &mut app);
assert_eq!(
crate::theme::cache::current_kind(),
crate::theme::ThemeKind::GrokDay,
crate::theme::ThemeKind::KigiDay,
"auto_dark_theme commit must update live display when theme=auto + system=Dark",
);
});
}
/// Auto-theme commit does NOT apply when system is in the
/// non-matching mode (auto_dark_theme + system=Light).
/// Uses `groknight` for the auto_dark_theme
/// Uses `kiginight` for the auto_dark_theme
/// value to avoid `clamp_to_terminal` ambiguity (we want a
/// concrete kind that's clearly different from GrokDay, the
/// concrete kind that's clearly different from KigiDay, the
/// active resolved theme).
#[test]
fn set_auto_dark_theme_does_not_apply_when_system_is_light() {
@@ -2883,20 +2883,20 @@ fn set_auto_dark_theme_does_not_apply_when_system_is_light() {
let _ = dispatch(Action::SetTheme("auto".into()), &mut app);
assert_eq!(
crate::theme::cache::current_kind(),
crate::theme::ThemeKind::GrokDay,
crate::theme::ThemeKind::KigiDay,
);
let _ = dispatch(Action::SetAutoDarkTheme("groknight".into()), &mut app);
let _ = dispatch(Action::SetAutoDarkTheme("kiginight".into()), &mut app);
assert_eq!(
crate::theme::cache::current_kind(),
crate::theme::ThemeKind::GrokDay,
crate::theme::ThemeKind::KigiDay,
"auto_dark_theme commit must NOT change live display when system=Light",
);
assert_eq!(app.current_ui.auto_dark_theme.as_deref(), Some("groknight"),);
assert_eq!(app.current_ui.auto_dark_theme.as_deref(), Some("kiginight"),);
});
}
/// Symmetric to the dark test: `set_auto_light_theme` applies only
/// when theme=auto + system=Light. Uses a non-truecolor theme
/// (`groknight`) for the same clamp reason as the dark variant.
/// (`kiginight`) for the same clamp reason as the dark variant.
#[test]
fn set_auto_light_theme_applies_when_theme_is_auto_and_system_is_light() {
with_theme_test_env(|| {
@@ -2907,12 +2907,12 @@ fn set_auto_light_theme_applies_when_theme_is_auto_and_system_is_light() {
let _ = dispatch(Action::SetTheme("auto".into()), &mut app);
assert_eq!(
crate::theme::cache::current_kind(),
crate::theme::ThemeKind::GrokDay,
crate::theme::ThemeKind::KigiDay,
);
let _ = dispatch(Action::SetAutoLightTheme("groknight".into()), &mut app);
let _ = dispatch(Action::SetAutoLightTheme("kiginight".into()), &mut app);
assert_eq!(
crate::theme::cache::current_kind(),
crate::theme::ThemeKind::GrokNight,
crate::theme::ThemeKind::KigiNight,
"auto_light_theme must update display when theme=auto + system=Light",
);
});
@@ -2986,7 +2986,7 @@ fn set_auto_light_theme_rejects_auto_value() {
fn set_theme_toast_format_uses_display_name() {
with_theme_test_env(|| {
let mut app = test_app_with_agent();
let _ = dispatch(Action::SetTheme("grokday".into()), &mut app);
let _ = dispatch(Action::SetTheme("kigiday".into()), &mut app);
let toast = read_toast(&app);
assert!(
toast.contains("Theme"),
@@ -2994,7 +2994,7 @@ fn set_theme_toast_format_uses_display_name() {
);
assert!(
toast.contains("Kigi Day"),
"toast must use display name `Kigi Day`, not canonical `grokday`, got: {toast:?}",
"toast must use display name `Kigi Day`, not canonical `kigiday`, got: {toast:?}",
);
assert!(toast.contains('\u{2713}'), "toast must contain the ✓ glyph");
});
@@ -3003,7 +3003,7 @@ fn set_theme_toast_format_uses_display_name() {
fn set_auto_dark_theme_toast_format_uses_display_name() {
with_theme_test_env(|| {
let mut app = test_app_with_agent();
let _ = dispatch(Action::SetAutoDarkTheme("grokday".into()), &mut app);
let _ = dispatch(Action::SetAutoDarkTheme("kigiday".into()), &mut app);
let toast = read_toast(&app);
assert!(toast.contains("Auto dark theme"));
assert!(toast.contains("Kigi Day"));
@@ -3014,7 +3014,7 @@ fn set_auto_dark_theme_toast_format_uses_display_name() {
fn set_auto_light_theme_toast_format_uses_display_name() {
with_theme_test_env(|| {
let mut app = test_app_with_agent();
let _ = dispatch(Action::SetAutoLightTheme("groknight".into()), &mut app);
let _ = dispatch(Action::SetAutoLightTheme("kiginight".into()), &mut app);
let toast = read_toast(&app);
assert!(toast.contains("Auto light theme"));
assert!(toast.contains("Kigi Night"));
@@ -3024,7 +3024,7 @@ fn set_auto_light_theme_toast_format_uses_display_name() {
/// reverts `app.current_ui.theme` AND the live cache (mirror of
/// `rollback_known_key_reverts_cache_and_no_effect`).
///
/// Uses non-truecolor themes (`grokday` ↔ `groknight`) to avoid
/// Uses non-truecolor themes (`kigiday` ↔ `kiginight`) to avoid
/// the `clamp_to_terminal` interaction in test environments that
/// don't report truecolor support.
#[test]
@@ -3032,28 +3032,28 @@ fn rollback_theme_reverts_current_ui_and_cache() {
use crate::settings::SettingValue;
with_theme_test_env(|| {
let mut app = test_app_with_agent();
let _ = dispatch(Action::SetTheme("grokday".into()), &mut app);
assert_eq!(app.current_ui.theme.as_deref(), Some("grokday"));
let _ = dispatch(Action::SetTheme("kigiday".into()), &mut app);
assert_eq!(app.current_ui.theme.as_deref(), Some("kigiday"));
assert_eq!(
crate::theme::cache::current_kind(),
crate::theme::ThemeKind::GrokDay,
crate::theme::ThemeKind::KigiDay,
);
let _ = dispatch(
Action::TaskComplete(TaskResult::SettingPersistFailed {
key: "theme",
rollback_value: SettingValue::Enum("groknight"),
rollback_value: SettingValue::Enum("kiginight"),
error: "disk full".into(),
}),
&mut app,
);
assert_eq!(
app.current_ui.theme.as_deref(),
Some("groknight"),
Some("kiginight"),
"rollback must update app.current_ui.theme",
);
assert_eq!(
crate::theme::cache::current_kind(),
crate::theme::ThemeKind::GrokNight,
crate::theme::ThemeKind::KigiNight,
"rollback must update the live theme cache too",
);
});
@@ -3063,17 +3063,17 @@ fn rollback_auto_dark_theme_reverts_current_ui() {
use crate::settings::SettingValue;
with_theme_test_env(|| {
let mut app = test_app_with_agent();
let _ = dispatch(Action::SetAutoDarkTheme("grokday".into()), &mut app);
assert_eq!(app.current_ui.auto_dark_theme.as_deref(), Some("grokday"));
let _ = dispatch(Action::SetAutoDarkTheme("kigiday".into()), &mut app);
assert_eq!(app.current_ui.auto_dark_theme.as_deref(), Some("kigiday"));
let _ = dispatch(
Action::TaskComplete(TaskResult::SettingPersistFailed {
key: "auto_dark_theme",
rollback_value: SettingValue::Enum("groknight"),
rollback_value: SettingValue::Enum("kiginight"),
error: "disk full".into(),
}),
&mut app,
);
assert_eq!(app.current_ui.auto_dark_theme.as_deref(), Some("groknight"),);
assert_eq!(app.current_ui.auto_dark_theme.as_deref(), Some("kiginight"),);
});
}
#[test]
@@ -3081,20 +3081,20 @@ fn rollback_auto_light_theme_reverts_current_ui() {
use crate::settings::SettingValue;
with_theme_test_env(|| {
let mut app = test_app_with_agent();
let _ = dispatch(Action::SetAutoLightTheme("groknight".into()), &mut app);
let _ = dispatch(Action::SetAutoLightTheme("kiginight".into()), &mut app);
assert_eq!(
app.current_ui.auto_light_theme.as_deref(),
Some("groknight"),
Some("kiginight"),
);
let _ = dispatch(
Action::TaskComplete(TaskResult::SettingPersistFailed {
key: "auto_light_theme",
rollback_value: SettingValue::Enum("grokday"),
rollback_value: SettingValue::Enum("kigiday"),
error: "disk full".into(),
}),
&mut app,
);
assert_eq!(app.current_ui.auto_light_theme.as_deref(), Some("grokday"));
assert_eq!(app.current_ui.auto_light_theme.as_deref(), Some("kigiday"));
});
}
/// Edge case — if the rollback value is
@@ -3109,8 +3109,8 @@ fn rollback_auto_dark_theme_with_auto_value_clears_to_none() {
use crate::settings::SettingValue;
with_theme_test_env(|| {
let mut app = test_app_with_agent();
let _ = dispatch(Action::SetAutoDarkTheme("grokday".into()), &mut app);
assert_eq!(app.current_ui.auto_dark_theme.as_deref(), Some("grokday"));
let _ = dispatch(Action::SetAutoDarkTheme("kigiday".into()), &mut app);
assert_eq!(app.current_ui.auto_dark_theme.as_deref(), Some("kigiday"));
let _ = dispatch(
Action::TaskComplete(TaskResult::SettingPersistFailed {
key: "auto_dark_theme",
@@ -3131,10 +3131,10 @@ fn rollback_auto_light_theme_with_auto_value_clears_to_none() {
use crate::settings::SettingValue;
with_theme_test_env(|| {
let mut app = test_app_with_agent();
let _ = dispatch(Action::SetAutoLightTheme("groknight".into()), &mut app);
let _ = dispatch(Action::SetAutoLightTheme("kiginight".into()), &mut app);
assert_eq!(
app.current_ui.auto_light_theme.as_deref(),
Some("groknight"),
Some("kiginight"),
);
let _ = dispatch(
Action::TaskComplete(TaskResult::SettingPersistFailed {
@@ -198,16 +198,16 @@ fn dispatch_confirm_reset_setting_reset_dispatches_typed_setter_for_shared_enum(
&mut app,
);
// Reset → SetTheme("groknight") (the registered default).
// Reset → SetTheme("kiginight") (the registered default).
assert_eq!(effects.len(), 1);
match &effects[0] {
Effect::PersistSetting { key, value, .. } => {
assert_eq!(*key, "theme");
assert_eq!(value, &SettingValue::Enum("groknight"));
assert_eq!(value, &SettingValue::Enum("kiginight"));
}
other => panic!("expected PersistSetting, got {other:?}"),
}
assert_eq!(app.current_ui.theme.as_deref(), Some("groknight"));
assert_eq!(app.current_ui.theme.as_deref(), Some("kiginight"));
}
#[test]
@@ -421,7 +421,7 @@ fn uninstall_result_notice_is_footer_only_not_row_anchored() {
);
}
/// Regression (Bugbot): a failed `x.ai/subagent/cancel` RPC must NOT
/// Regression (Bugbot): a failed `kigi/subagent/cancel` RPC must NOT
/// finalize the row — the subagent may still be running. Only a shell
/// response of "nothing live" finalizes it.
#[test]
@@ -509,7 +509,7 @@ fn cancel_complete_does_nothing() {
fn switch_model_complete_success_updates_model_and_pushes_message() {
let mut app = test_app_with_agent();
let id = AgentId(0);
let model_id = acp::ModelId::new(std::sync::Arc::from("grok-4.5"));
let model_id = acp::ModelId::new(std::sync::Arc::from("kigi-4.5"));
// Set up available models so the display name can be resolved.
app.agents
@@ -520,7 +520,7 @@ fn switch_model_complete_success_updates_model_and_pushes_message() {
.available
.insert(
model_id.clone(),
acp::ModelInfo::new(model_id.clone(), "Grok 4.5".to_string()),
acp::ModelInfo::new(model_id.clone(), "Kigi 4.5".to_string()),
);
app.agents
.get_mut(&id)
@@ -562,12 +562,12 @@ fn switch_model_complete_success_updates_model_and_pushes_message() {
fn switch_model_complete_skips_message_and_persist_when_unchanged() {
let mut app = test_app_with_agent();
let id = AgentId(0);
let model_id = acp::ModelId::new(std::sync::Arc::from("grok-4.5"));
let model_id = acp::ModelId::new(std::sync::Arc::from("kigi-4.5"));
let agent = app.agents.get_mut(&id).unwrap();
agent.session.models.available.insert(
model_id.clone(),
acp::ModelInfo::new(model_id.clone(), "Grok 4.5".to_string()),
acp::ModelInfo::new(model_id.clone(), "Kigi 4.5".to_string()),
);
agent.session.models.current = Some(model_id.clone());
agent.session.models.reasoning_effort = None;
@@ -667,7 +667,7 @@ fn switch_to_non_reasoning_model_clears_persisted_effort() {
use kigi_shell::sampling::types::ReasoningEffort;
let mut app = test_app_with_agent();
let id = AgentId(0);
let model_id = acp::ModelId::new(std::sync::Arc::from("grok-4.5"));
let model_id = acp::ModelId::new(std::sync::Arc::from("kigi-4.5"));
// Simulate prior reasoning effort from a previous model.
app.agents
@@ -686,7 +686,7 @@ fn switch_to_non_reasoning_model_clears_persisted_effort() {
.available
.insert(
model_id.clone(),
acp::ModelInfo::new(model_id.clone(), "Grok Build".to_string()),
acp::ModelInfo::new(model_id.clone(), "Kigi".to_string()),
);
app.agents
.get_mut(&id)
@@ -773,7 +773,7 @@ fn switch_model_incompatible_agent_shows_question_modal() {
let err = kigi_shell::agent::config::ModelSwitchIncompatibleAgentError {
code: "MODEL_SWITCH_INCOMPATIBLE_AGENT".into(),
active_agent_type: "grok-build".into(),
active_agent_type: "kigi".into(),
required_agent_type: "cursor".into(),
model_id: "cursor-model".into(),
suggestion: "start_new_session".into(),
@@ -836,7 +836,7 @@ fn incompatible_agent_rollback_restores_previous_model() {
let err = kigi_shell::agent::config::ModelSwitchIncompatibleAgentError {
code: "MODEL_SWITCH_INCOMPATIBLE_AGENT".into(),
active_agent_type: "grok-build".into(),
active_agent_type: "kigi".into(),
required_agent_type: "cursor".into(),
model_id: "cursor-model".into(),
suggestion: "start_new_session".into(),
@@ -882,7 +882,7 @@ fn incompatible_agent_closes_active_modal() {
let err = kigi_shell::agent::config::ModelSwitchIncompatibleAgentError {
code: "MODEL_SWITCH_INCOMPATIBLE_AGENT".into(),
active_agent_type: "grok-build".into(),
active_agent_type: "kigi".into(),
required_agent_type: "cursor".into(),
model_id: "cursor-model".into(),
suggestion: "start_new_session".into(),
@@ -919,19 +919,19 @@ fn same_agent_type_switch_no_modal() {
// should succeed normally — no modal, no IncompatibleAgent error.
let mut app = test_app_with_agent();
let id = AgentId(0);
let model_a = acp::ModelId::new(std::sync::Arc::from("grok-build-a"));
let model_b = acp::ModelId::new(std::sync::Arc::from("grok-build-b"));
let model_a = acp::ModelId::new(std::sync::Arc::from("kigi-a"));
let model_b = acp::ModelId::new(std::sync::Arc::from("kigi-b"));
// Add both models to the catalog (no agentType → both use grok-build).
// Add both models to the catalog (no agentType → both use kigi).
let agent = app.agents.get_mut(&id).unwrap();
agent.session.models.available.insert(
model_a.clone(),
acp::ModelInfo::new(model_a.clone(), "Grok Build A".to_string()),
acp::ModelInfo::new(model_a.clone(), "Kigi A".to_string()),
);
agent.session.models.set_current(model_a, None);
agent.session.models.available.insert(
model_b.clone(),
acp::ModelInfo::new(model_b.clone(), "Grok Build B".to_string()),
acp::ModelInfo::new(model_b.clone(), "Kigi B".to_string()),
);
agent.session.model_switch_pending = true;
@@ -963,7 +963,7 @@ fn switch_model_pending_lifecycle() {
// Full lifecycle: false -> dispatch SwitchModel -> true -> SwitchModelComplete -> false
let mut app = test_app_with_agent();
let id = AgentId(0);
let model_id = acp::ModelId::new(std::sync::Arc::from("grok-4.5"));
let model_id = acp::ModelId::new(std::sync::Arc::from("kigi-4.5"));
// Initially false.
assert!(!app.agents[&id].session.model_switch_pending);
@@ -246,7 +246,7 @@ fn plugins_list_response() -> kigi_hooks_plugins_types::PluginsListResponse {
plugins: vec![
test_plugin_info(
"user-tool",
Some(kigi_hooks_plugins_types::PluginOrigin::UserGrok),
Some(kigi_hooks_plugins_types::PluginOrigin::UserKigi),
),
test_plugin_info(
"claude-tool",
@@ -160,7 +160,7 @@ fn cancel_turn_leaves_shared_queue_for_agent_to_drain() {
// shared queue (broadcast to all attached clients). The agent owns the
// drain: on cancel the FRONT queued prompt runs next (promoted
// server-side), so the pager must NOT pull it back into the input or
// mutate the queue locally — the `x.ai/queue/changed` rebroadcast is the
// mutate the queue locally — the `kigi/queue/changed` rebroadcast is the
// source of truth.
let mut app = test_app_with_agent();
let id = AgentId(0);
@@ -202,7 +202,7 @@ pub(crate) fn dispatch_open_transcript_pager(app: &mut AppView) {
return;
};
let path = std::env::temp_dir().join(format!("grok-transcript-{}.md", uuid::Uuid::new_v4()));
let path = std::env::temp_dir().join(format!("kigi-transcript-{}.md", uuid::Uuid::new_v4()));
match std::fs::write(&path, content) {
Ok(()) => {
app.pending_pager_path = Some(path);
@@ -636,7 +636,7 @@ pub(super) fn handle_skills_toggle_done(
}
}
}
// The toggle effect already called x.ai/skills/refresh-baseline
// The toggle effect already called kigi/skills/refresh-baseline
// which triggers the session to reload skills and push an
// AvailableCommandsUpdate notification with the updated list.
vec![]
@@ -258,7 +258,7 @@ pub(super) fn do_cancel_turn(app: &mut AppView, cancel_subagents: bool) -> Vec<E
// Server-authoritative queue: the agent owns the drain. On an interactive
// cancel we only tear down the running turn and let the agent promote the
// FRONT queued prompt as the next turn — its `x.ai/queue/changed`
// FRONT queued prompt as the next turn — its `kigi/queue/changed`
// rebroadcast (carrying `running_prompt_id`) is the source of truth, and the
// pager adopts it via `handle_queue_changed` / `apply_turn_start_shim`. We
// do NOT pull any queued prompt back into the input or predict the new queue
@@ -277,7 +277,7 @@ pub(super) fn do_cancel_turn(app: &mut AppView, cancel_subagents: bool) -> Vec<E
}]
}
/// Grace window between a driver-side `x.ai/session/prompt_complete`
/// Grace window between a driver-side `kigi/session/prompt_complete`
/// broadcast and that turn's `session/prompt` RPC response, after which
/// [`reconcile_overdue_turn_ends`] finishes the turn from the broadcast. The
/// healthy-path gap is milliseconds (the shell emits the broadcast just
@@ -285,7 +285,7 @@ pub(super) fn do_cancel_turn(app: &mut AppView, cancel_subagents: bool) -> Vec<E
/// genuinely lost, not merely slow.
pub(crate) const TURN_END_RECONCILE_GRACE: std::time::Duration = std::time::Duration::from_secs(2);
/// Finish turns whose end was announced by `x.ai/session/prompt_complete`
/// Finish turns whose end was announced by `kigi/session/prompt_complete`
/// but whose `session/prompt` RPC response never arrived.
///
/// The RPC response is the driver's only turn-state exit, and it can be lost