§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:
ZacharyZhang-NY committed 2026-07-18 02:48:46 -04:00
1 parent 86e3724310
commit 6f31415ed6
1056 files changed
+8410 -18307

No files matched your search

@@ -103,7 +103,7 @@ pub(super) fn parse_session_load_restore_meta(
.and_then(|v| serde_json::from_value(v).ok());
(code_restored, restore_summary, restore_degree)
}
/// CANONICAL wire parser for `LoadSessionResponse._meta["x.ai/runningPromptId"]`.
/// CANONICAL wire parser for `LoadSessionResponse._meta["kigi/runningPromptId"]`.
///
/// Returns the session's in-flight running prompt id when the session was
/// loaded MID-turn (some other client is driving), otherwise `None`. The
@@ -114,7 +114,7 @@ pub(crate) fn parse_session_load_running_prompt_id(
resp_meta: Option<&acp::Meta>,
) -> Option<String> {
resp_meta
.and_then(|m| m.get("x.ai/runningPromptId"))
.and_then(|m| m.get("kigi/runningPromptId"))
.and_then(|v| v.as_str())
.map(String::from)
}
@@ -133,7 +133,7 @@ pub(crate) fn sanitize_user_error(raw: &str) -> String {
("inference_api", "server"),
("research-api", "server"),
("research_api", "server"),
("grok-code-backend", "server"),
("kigi-code-backend", "server"),
("ACP error:", "error:"),
("ACP request failed:", "request failed:"),
("JSON-RPC error", "request error"),
@@ -165,17 +165,17 @@ pub(crate) fn sanitize_user_error(raw: &str) -> String {
///
/// | plan | subagents | ask-user | agentProfile | askUserQuestion |
/// |-------|-----------|----------|--------------------------------|--------------------|
/// | false | false | false | `grok-build` (default) | `false` |
/// | false | true | false | `grok-build` (default) | `false` |
/// | false | false | true | `grok-build-ask-user` | omitted (shell gate) |
/// | false | true | true | `grok-build-ask-user` | omitted (shell gate) |
/// | true | false | false | `grok-build-plan-no-subagents` | `false` |
/// | true | true | false | `grok-build-plan` | `false` |
/// | true | false | true | `grok-build-plan-no-subagents` | omitted (shell gate) |
/// | true | true | true | `grok-build-plan` | omitted (shell gate) |
/// | false | false | false | `kigi` (default) | `false` |
/// | false | true | false | `kigi` (default) | `false` |
/// | false | false | true | `kigi-ask-user` | omitted (shell gate) |
/// | false | true | true | `kigi-ask-user` | omitted (shell gate) |
/// | true | false | false | `kigi-plan-no-subagents` | `false` |
/// | true | true | false | `kigi-plan` | `false` |
/// | true | false | true | `kigi-plan-no-subagents` | omitted (shell gate) |
/// | true | true | true | `kigi-plan` | omitted (shell gate) |
///
/// When [`Self::chat_mode`] is set (gateway light-frontend / `--chat`), Build
/// `agentProfile` injection is omitted (K12) and `_meta["x.ai/session"].kind`
/// `agentProfile` injection is omitted (K12) and `_meta["kigi/session"].kind`
/// is stamped `"chat"` so the shell takes `require_gateway` / thin profile.
#[derive(Debug, Clone, Default)]
pub(crate) struct SessionFlags {
@@ -183,7 +183,7 @@ pub(crate) struct SessionFlags {
pub subagents: bool,
pub ask_user: bool,
/// Restore code state on resume (`--restore-code`).
/// Injected as `x.ai/restore_code` into `LoadSession` meta, or passed
/// Injected as `kigi/restore_code` into `LoadSession` meta, or passed
/// as `restoreCode` in the `resume_session` ACP payload for worktrees.
pub restore_code: Option<bool>,
pub agent_override: Option<serde_json::Value>,
@@ -208,7 +208,7 @@ pub(crate) struct SessionFlags {
impl SessionFlags {
/// Resolve the agent profile name from the flags.
///
/// Returns `None` for the default `grok-build` profile (no `_meta`
/// Returns `None` for the default `kigi` profile (no `_meta`
/// needed; it already includes TaskTool). Chat mode never injects a
/// Build profile (remote owns agent behavior).
pub(super) fn agent_profile(&self) -> Option<&'static str> {
@@ -216,9 +216,9 @@ impl SessionFlags {
return None;
}
match (self.plan_mode, self.subagents, self.ask_user) {
(true, true, _) => Some("grok-build-plan"),
(true, false, _) => Some("grok-build-plan-no-subagents"),
(false, _, true) => Some("grok-build-ask-user"),
(true, true, _) => Some("kigi-plan"),
(true, false, _) => Some("kigi-plan-no-subagents"),
(false, _, true) => Some("kigi-ask-user"),
(false, _, false) => None,
}
}
@@ -229,7 +229,7 @@ impl SessionFlags {
/// emit-site comment below). `--no-ask-user` always forces
/// `askUserQuestion: false` into the meta, even when paired with
/// `KIGI_AGENT` — the env var chooses the *agent*, but the tool-strip is
/// independent. Chat mode additionally stamps `x.ai/session.kind`.
/// independent. Chat mode additionally stamps `kigi/session.kind`.
pub(super) fn to_meta(&self) -> Option<acp::Meta> {
let mut meta = serde_json::Map::new();
if self.chat_mode {
@@ -247,7 +247,7 @@ impl SessionFlags {
meta.insert("agentProfile".into(), serde_json::json!(profile));
}
if self.chat_mode {
meta.insert("x.ai/session".into(), serde_json::json!({ "kind" : "chat" }));
meta.insert("kigi/session".into(), serde_json::json!({ "kind" : "chat" }));
}
if !self.ask_user {
meta.insert("askUserQuestion".into(), serde_json::json!(false));
@@ -266,13 +266,13 @@ impl SessionFlags {
/// workspace for `kind=chat`; the client must not bind Direct/envId/attach.
pub(super) const CHAT_FORBIDDEN_WORKSPACE_BIND_KEYS: &[&str] = &[
"envId",
"x.ai/cloud_server_id",
"x.ai/cloud_existing_workspace",
"kigi/cloud_server_id",
"kigi/cloud_existing_workspace",
];
/// Stamp `_meta["x.ai/session"].kind = "chat"` and strip Build `agentProfile` (K12).
/// Stamp `_meta["kigi/session"].kind = "chat"` and strip Build `agentProfile` (K12).
pub(super) fn apply_chat_kind_meta(meta: &mut Option<acp::Meta>) {
let obj = meta.get_or_insert_with(acp::Meta::new);
obj.insert("x.ai/session".into(), serde_json::json!({ "kind" : "chat" }));
obj.insert("kigi/session".into(), serde_json::json!({ "kind" : "chat" }));
obj.remove("agentProfile");
}
/// Remove client workspace-bind keys from chat create/load meta (defense in depth).
@@ -360,7 +360,7 @@ pub(super) fn count_chat_history_stats(history_path: &Path) -> (usize, usize) {
}
(turn_count, tool_call_count)
}
/// Parse the `x.ai/session/list` response payload (the unwrapped
/// Parse the `kigi/session/list` response payload (the unwrapped
/// `{ "sessions": [...] }` object) into [`SessionPickerEntry`] rows.
///
/// Shared by the resume picker ([`Effect::FetchSessionList`]) and the
@@ -399,7 +399,7 @@ pub(super) fn parse_session_picker_entries(
.map(String::from);
let is_conversation = v
.get("_meta")
.and_then(|m| m.get("x.ai/session"))
.and_then(|m| m.get("kigi/session"))
.and_then(|s| s.get("kind"))
.and_then(|k| k.as_str()) == Some("chat");
let parsed_updated: Option<chrono::DateTime<chrono::Utc>> = v
@@ -548,7 +548,7 @@ pub(super) fn session_picker_entry_to_roster(
}
pub(super) async fn send_logout(tx: &AcpAgentTx) {
let req = acp::ExtRequest::new(
"x.ai/auth/logout",
"kigi/auth/logout",
serde_json::value::to_raw_value(&serde_json::json!({}))
.expect("serialize auth/logout params")
.into(),
@@ -927,7 +927,7 @@ pub(crate) async fn persist_setting(
/// Body for `Effect::PersistPermissionMode`. Factored out for testability.
///
/// 1. Persist `ui.permission_mode` to disk.
/// 2. Fire ACP `x.ai/yolo_mode_changed` (gated on disk success for
/// 2. Fire ACP `kigi/yolo_mode_changed` (gated on disk success for
/// `WithRollback`; always for `BestEffort`).
/// 3. Return the matching `TaskResult`.
pub(crate) async fn persist_permission_mode_and_notify(
@@ -951,7 +951,7 @@ pub(crate) async fn persist_permission_mode_and_notify(
config_str, }
);
let notification = acp::ExtNotification::new(
"x.ai/yolo_mode_changed",
"kigi/yolo_mode_changed",
serde_json::value::to_raw_value(&params)
.expect("serialize yolo_mode_changed params")
.into(),
@@ -962,7 +962,7 @@ pub(crate) async fn persist_permission_mode_and_notify(
}
route_permission_mode_result(disk_outcome, persist, config_str)
}
/// Whether to fire the ACP `x.ai/yolo_mode_changed` notification.
/// Whether to fire the ACP `kigi/yolo_mode_changed` notification.
/// `WithRollback` suppresses on disk failure (agent must not see the
/// optimistic value). `BestEffort` always fires.
pub(super) fn should_send_yolo_acp_notification(
@@ -975,7 +975,7 @@ pub(super) fn should_send_yolo_acp_notification(
(Err(_), PermissionModePersist::WithRollback(_)) => false,
}
}
/// Extract the typed kill outcome from an `x.ai/task/kill` ext response.
/// Extract the typed kill outcome from an `kigi/task/kill` ext response.
///
/// The agent serializes `ExtMethodResult<KillTaskResponse>`, so the outcome
/// lives at `result.outcome` (`{"result":{"taskId":..,"outcome":
@@ -997,7 +997,7 @@ pub(super) fn parse_kill_outcome(
.and_then(|envelope| envelope.result)
.map(|payload| payload.outcome)
}
/// Map an `x.ai/subagent/cancel` response (payload under `result`) to a kill
/// Map an `kigi/subagent/cancel` response (payload under `result`) to a kill
/// outcome. Prefers the typed `outcome`; falls back to the legacy `cancelled`
/// bool for an older shell or an unknown future `kind`. An error/unparseable
/// body is `RpcFailed` (subagent may still be running — leave the row alone).
@@ -1092,7 +1092,7 @@ pub(super) fn persist_hint(
TaskResult::CancelComplete
});
}
/// Parse an `x.ai/billing` ext response body (the unwrapped `result`
/// Parse an `kigi/billing` ext response body (the unwrapped `result`
/// payload) into Kimi usage rows. A body that fails to deserialize is an
/// error, not an empty quota list, so a malformed response can't render
/// as "no usage data".
+56 -56
View File
@@ -200,7 +200,7 @@ pub(crate) fn execute(
if chat_kind || session_flags.chat_mode {
meta.get_or_insert_with(acp::Meta::new)
.insert(
"x.ai/session".into(),
"kigi/session".into(),
serde_json::json!({ "kind" : "chat" }),
);
}
@@ -238,7 +238,7 @@ pub(crate) fn execute(
payload["gitRef"] = serde_json::Value::String(r.clone());
}
let ext_req = acp::ExtRequest::new(
"x.ai/git/worktree/resume_session",
"kigi/git/worktree/resume_session",
serde_json::value::to_raw_value(&payload)
.expect("serialize resume params")
.into(),
@@ -342,7 +342,7 @@ pub(crate) fn execute(
params["gitRef"] = serde_json::Value::String(r.clone());
}
let ext_req = acp::ExtRequest::new(
"x.ai/git/worktree/create_from_worktree_sync",
"kigi/git/worktree/create_from_worktree_sync",
serde_json::value::to_raw_value(&params)
.expect("serialize worktree params")
.into(),
@@ -470,7 +470,7 @@ pub(crate) fn execute(
}
if let Some(true) = session_flags.restore_code {
meta.get_or_insert_with(acp::Meta::new)
.insert("x.ai/restore_code".into(), serde_json::Value::Bool(true));
.insert("kigi/restore_code".into(), serde_json::Value::Bool(true));
}
let cwd = session_cwd.unwrap_or_else(|| cwd.to_path_buf());
let mcp_started = std::time::Instant::now();
@@ -670,7 +670,7 @@ pub(crate) fn execute(
params["query"] = serde_json::Value::String(q.clone());
}
let request = acp::ExtRequest::new(
"x.ai/session/list",
"kigi/session/list",
serde_json::value::to_raw_value(&params)
.expect("serialize session list params")
.into(),
@@ -725,7 +725,7 @@ pub(crate) fn execute(
tasks
.spawn(async move {
let request = acp::ExtRequest::new(
"x.ai/sessions/list",
"kigi/sessions/list",
serde_json::value::to_raw_value(&serde_json::json!({}))
.expect("serialize roster list params")
.into(),
@@ -743,7 +743,7 @@ pub(crate) fn execute(
}
None => {
tracing::warn!(
"failed to parse x.ai/sessions/list response"
"failed to parse kigi/sessions/list response"
);
TaskResult::RosterFailed {
error: "parse error".to_string(),
@@ -768,7 +768,7 @@ pub(crate) fn execute(
{ "cwd" : cwd.to_string_lossy(), "limit" : 30, }
);
let request = acp::ExtRequest::new(
"x.ai/session/list",
"kigi/session/list",
serde_json::value::to_raw_value(&params)
.expect("serialize session list params")
.into(),
@@ -1211,7 +1211,7 @@ pub(crate) fn execute(
{ "sessionId" : session_id.0.to_string(), }
);
let notification = acp::ExtNotification::new(
"x.ai/toggle_plan_mode",
"kigi/toggle_plan_mode",
serde_json::value::to_raw_value(&params)
.expect("serialize toggle_plan_mode params")
.into(),
@@ -1233,7 +1233,7 @@ pub(crate) fn execute(
"expectedVersion" : expected_version, }
);
let notification = acp::ExtNotification::new(
"x.ai/queue/remove",
"kigi/queue/remove",
serde_json::value::to_raw_value(&params)
.expect("serialize queue/remove params")
.into(),
@@ -1253,7 +1253,7 @@ pub(crate) fn execute(
ordered_ids, }
);
let notification = acp::ExtNotification::new(
"x.ai/queue/reorder",
"kigi/queue/reorder",
serde_json::value::to_raw_value(&params)
.expect("serialize queue/reorder params")
.into(),
@@ -1272,7 +1272,7 @@ pub(crate) fn execute(
{ "sessionId" : session_id.0.to_string(), }
);
let notification = acp::ExtNotification::new(
"x.ai/queue/clear",
"kigi/queue/clear",
serde_json::value::to_raw_value(&params)
.expect("serialize queue/clear params")
.into(),
@@ -1292,7 +1292,7 @@ pub(crate) fn execute(
new_text, }
);
let notification = acp::ExtNotification::new(
"x.ai/queue/edit",
"kigi/queue/edit",
serde_json::value::to_raw_value(&params)
.expect("serialize queue/edit params")
.into(),
@@ -1315,7 +1315,7 @@ pub(crate) fn execute(
params["newText"] = serde_json::Value::String(new_text);
}
let notification = acp::ExtNotification::new(
"x.ai/queue/interject",
"kigi/queue/interject",
serde_json::value::to_raw_value(&params)
.expect("serialize queue/interject params")
.into(),
@@ -1396,7 +1396,7 @@ pub(crate) fn execute(
{ "sessionId" : session_id.0.to_string(), }
);
let req = acp::ExtRequest::new(
"x.ai/compact_conversation",
"kigi/compact_conversation",
serde_json::value::to_raw_value(&params)
.expect("serialize compact params")
.into(),
@@ -1419,7 +1419,7 @@ pub(crate) fn execute(
session_id, }
);
let req = acp::ExtRequest::new(
"x.ai/prompt_history",
"kigi/prompt_history",
serde_json::value::to_raw_value(&params)
.expect("serialize prompt_history params")
.into(),
@@ -1467,7 +1467,7 @@ pub(crate) fn execute(
task_id: task_id.clone(),
};
let req = acp::ExtRequest::new(
"x.ai/task/kill",
"kigi/task/kill",
serde_json::value::to_raw_value(&params)
.expect("serialize kill params")
.into(),
@@ -1500,7 +1500,7 @@ pub(crate) fn execute(
subagent_id, }
);
let req = acp::ExtRequest::new(
"x.ai/subagent/cancel",
"kigi/subagent/cancel",
serde_json::value::to_raw_value(&params)
.expect("serialize cancel params")
.into(),
@@ -1527,7 +1527,7 @@ pub(crate) fn execute(
{ "sessionId" : session_id.0.to_string(), "taskId" : task_id, }
);
let req = acp::ExtRequest::new(
"x.ai/scheduler/delete",
"kigi/scheduler/delete",
serde_json::value::to_raw_value(&params)
.expect("serialize scheduler delete params")
.into(),
@@ -1547,7 +1547,7 @@ pub(crate) fn execute(
tool_call_id, }
);
let req = acp::ExtRequest::new(
"x.ai/terminal/background",
"kigi/terminal/background",
serde_json::value::to_raw_value(&params)
.expect("serialize background params")
.into(),
@@ -1862,7 +1862,7 @@ pub(crate) fn execute(
}
let params = serde_json::json!({});
let req = acp::ExtRequest::new(
"x.ai/auth/get_url",
"kigi/auth/get_url",
serde_json::value::to_raw_value(&params)
.expect("serialize auth_url params")
.into(),
@@ -1901,7 +1901,7 @@ pub(crate) fn execute(
.spawn(async move {
let params = serde_json::json!({ "code" : code });
let req = acp::ExtRequest::new(
"x.ai/auth/submit_code",
"kigi/auth/submit_code",
serde_json::value::to_raw_value(&params)
.expect("serialize auth code params")
.into(),
@@ -1935,7 +1935,7 @@ pub(crate) fn execute(
{ "sessionId" : session_id.0.to_string(), "cache" : cache, }
);
let req = acp::ExtRequest::new(
"x.ai/mcp/list",
"kigi/mcp/list",
serde_json::value::to_raw_value(&params)
.expect("serialize mcp/list params")
.into(),
@@ -1976,7 +1976,7 @@ pub(crate) fn execute(
server_name, }
);
let req = acp::ExtRequest::new(
"x.ai/mcp/auth_trigger",
"kigi/mcp/auth_trigger",
serde_json::value::to_raw_value(&params)
.expect("serialize mcp/auth_trigger params")
.into(),
@@ -2024,7 +2024,7 @@ pub(crate) fn execute(
{ "sessionId" : session_id.0.to_string(), }
);
let req = acp::ExtRequest::new(
"x.ai/hooks/list",
"kigi/hooks/list",
serde_json::value::to_raw_value(&params)
.expect("serialize hooks/list params")
.into(),
@@ -2061,7 +2061,7 @@ pub(crate) fn execute(
{ "sessionId" : session_id.0.to_string(), }
);
let req = acp::ExtRequest::new(
"x.ai/plugins/list",
"kigi/plugins/list",
serde_json::value::to_raw_value(&params)
.expect("serialize plugins/list params")
.into(),
@@ -2099,7 +2099,7 @@ pub(crate) fn execute(
action,
};
let req = acp::ExtRequest::new(
"x.ai/hooks/action",
"kigi/hooks/action",
serde_json::value::to_raw_value(&req_body)
.expect("serialize hooks/action params")
.into(),
@@ -2139,7 +2139,7 @@ pub(crate) fn execute(
action,
};
let req = acp::ExtRequest::new(
"x.ai/plugins/action",
"kigi/plugins/action",
serde_json::value::to_raw_value(&req_body)
.expect("serialize plugins/action params")
.into(),
@@ -2176,7 +2176,7 @@ pub(crate) fn execute(
.spawn(async move {
let params = serde_json::json!({ "cwd" : "." });
let req = acp::ExtRequest::new(
"x.ai/skills/list",
"kigi/skills/list",
serde_json::value::to_raw_value(&params)
.expect("serialize skills/list params")
.into(),
@@ -2215,7 +2215,7 @@ pub(crate) fn execute(
{ "name" : skill_name, "enabled" : enabled, "cwd" : ".", }
);
let req = acp::ExtRequest::new(
"x.ai/skills/toggle",
"kigi/skills/toggle",
serde_json::value::to_raw_value(&params)
.expect("serialize skills/toggle params")
.into(),
@@ -2235,7 +2235,7 @@ pub(crate) fn execute(
.map_err(|_| "couldn't toggle skill".to_string());
if parsed.is_ok() {
let refresh = acp::ExtRequest::new(
"x.ai/skills/refresh-baseline",
"kigi/skills/refresh-baseline",
serde_json::value::to_raw_value(&serde_json::json!({}))
.expect("serialize empty params")
.into(),
@@ -2273,7 +2273,7 @@ pub(crate) fn execute(
config: *config,
};
let req = acp::ExtRequest::new(
"x.ai/mcp/upsert",
"kigi/mcp/upsert",
serde_json::value::to_raw_value(&req_body)
.expect("serialize mcp/upsert params")
.into(),
@@ -2308,7 +2308,7 @@ pub(crate) fn execute(
server_name,
};
let req = acp::ExtRequest::new(
"x.ai/mcp/delete",
"kigi/mcp/delete",
serde_json::value::to_raw_value(&req_body)
.expect("serialize mcp/delete params")
.into(),
@@ -2337,7 +2337,7 @@ pub(crate) fn execute(
server_name, "enabled" : enabled, }
);
let req = acp::ExtRequest::new(
"x.ai/mcp/toggle",
"kigi/mcp/toggle",
serde_json::value::to_raw_value(&params)
.expect("serialize mcp/toggle params")
.into(),
@@ -2367,7 +2367,7 @@ pub(crate) fn execute(
server_name, "tool_name" : tool_name, "enabled" : enabled, }
);
let req = acp::ExtRequest::new(
"x.ai/mcp/toggle_tool",
"kigi/mcp/toggle_tool",
serde_json::value::to_raw_value(&params)
.expect("serialize mcp/toggle_tool params")
.into(),
@@ -2446,7 +2446,7 @@ pub(crate) fn execute(
cwd: String,
}
let request = acp::ExtRequest::new(
"x.ai/session/rename",
"kigi/session/rename",
serde_json::value::to_raw_value(
&RenameRequest {
session_id: session_id.0.to_string(),
@@ -2503,7 +2503,7 @@ pub(crate) fn execute(
cwd: String,
}
let request = acp::ExtRequest::new(
"x.ai/session/delete",
"kigi/session/delete",
serde_json::value::to_raw_value(
&DeleteRequest {
session_id: session_id.clone(),
@@ -2605,7 +2605,7 @@ pub(crate) fn execute(
}
};
let request = acp::ExtRequest::new(
"x.ai/feedback",
"kigi/feedback",
raw_params.into(),
);
match acp_send(request, &tx).await {
@@ -2636,7 +2636,7 @@ pub(crate) fn execute(
tasks
.spawn(async move {
let request = acp::ExtRequest::new(
"x.ai/memory/rewrite",
"kigi/memory/rewrite",
serde_json::value::to_raw_value(
&serde_json::json!(
{ "sessionId" : session_id.0.to_string(), "rawText" :
@@ -2703,7 +2703,7 @@ pub(crate) fn execute(
tasks
.spawn(async move {
let request = acp::ExtRequest::new(
"x.ai/btw",
"kigi/btw",
serde_json::value::to_raw_value(
&serde_json::json!(
{ "sessionId" : session_id.0.to_string(), "question" :
@@ -2746,7 +2746,7 @@ pub(crate) fn execute(
tasks
.spawn(async move {
let request = acp::ExtRequest::new(
"x.ai/recap",
"kigi/recap",
serde_json::value::to_raw_value(
&serde_json::json!(
{ "sessionId" : session_id.0.to_string(), "auto" : auto, }
@@ -2790,7 +2790,7 @@ pub(crate) fn execute(
blocks.as_deref(),
);
let request = acp::ExtRequest::new(
"x.ai/interject",
"kigi/interject",
serde_json::value::to_raw_value(&params)
.expect("serialize interject params")
.into(),
@@ -2820,7 +2820,7 @@ pub(crate) fn execute(
.spawn(async move {
let params = serde_json::json!({ "kind" : kind, "name" : name });
let request = acp::ExtRequest::new(
"x.ai/bundle/entry/get",
"kigi/bundle/entry/get",
serde_json::value::to_raw_value(&params)
.expect("serialize bundle/entry/get params")
.into(),
@@ -2876,7 +2876,7 @@ pub(crate) fn execute(
tasks
.spawn(async move {
let request = acp::ExtRequest::new(
"x.ai/bundle/status",
"kigi/bundle/status",
serde_json::value::to_raw_value(&serde_json::json!({}))
.expect("serialize bundle/status params")
.into(),
@@ -2935,7 +2935,7 @@ pub(crate) fn execute(
.spawn(async move {
let params = serde_json::json!({ "cwd" : cwd });
let req = acp::ExtRequest::new(
"x.ai/commands/list",
"kigi/commands/list",
serde_json::value::to_raw_value(&params)
.expect("serialize commands/list params")
.into(),
@@ -2971,7 +2971,7 @@ pub(crate) fn execute(
tasks
.spawn(async move {
let request = acp::ExtRequest::new(
"x.ai/rewind/points",
"kigi/rewind/points",
serde_json::value::to_raw_value(
&serde_json::json!(
{ "sessionId" : session_id.0.to_string() }
@@ -3030,7 +3030,7 @@ pub(crate) fn execute(
tasks
.spawn(async move {
let request = acp::ExtRequest::new(
"x.ai/rewind/execute",
"kigi/rewind/execute",
serde_json::value::to_raw_value(
&serde_json::json!(
{ "sessionId" : session_id.0.to_string(),
@@ -3084,7 +3084,7 @@ pub(crate) fn execute(
tasks
.spawn(async move {
let request = acp::ExtRequest::new(
"x.ai/rewind/execute",
"kigi/rewind/execute",
serde_json::value::to_raw_value(
&serde_json::json!(
{ "sessionId" : session_id.0.to_string(),
@@ -3144,7 +3144,7 @@ pub(crate) fn execute(
{ "query" : query, "limit" : 20, "includeContent" : true, }
);
let request = acp::ExtRequest::new(
"x.ai/session/search",
"kigi/session/search",
serde_json::value::to_raw_value(&params)
.expect("serialize deep search params")
.into(),
@@ -3229,7 +3229,7 @@ pub(crate) fn execute(
parent_is_worktree,
);
let req = acp::ExtRequest::new(
"x.ai/session/fork",
"kigi/session/fork",
serde_json::value::to_raw_value(&payload)
.expect("serialize fork params")
.into(),
@@ -3308,7 +3308,7 @@ pub(crate) fn execute(
tasks
.spawn(async move {
let req = acp::ExtRequest::new(
"x.ai/billing",
"kigi/billing",
serde_json::value::to_raw_value(&serde_json::json!({}))
.expect("serialize usage params")
.into(),
@@ -3362,7 +3362,7 @@ pub(crate) fn execute(
token_only, }
);
let req = acp::ExtRequest::new(
"x.ai/suggest",
"kigi/suggest",
serde_json::value::to_raw_value(&params)
.expect("serialize suggest params")
.into(),
@@ -3401,7 +3401,7 @@ pub(crate) fn execute(
session_id, }
);
let req = acp::ExtRequest::new(
"x.ai/suggestPrompt",
"kigi/suggestPrompt",
serde_json::value::to_raw_value(&params)
.expect("serialize suggestPrompt params")
.into(),
@@ -3428,13 +3428,13 @@ pub(crate) fn execute(
}
(false, meta)
}
/// Fetch session info from ACP via `x.ai/session/info`.
/// Fetch session info from ACP via `kigi/session/info`.
async fn fetch_session_info(
session_id: &acp::SessionId,
tx: &AcpAgentTx,
) -> Result<SessionInfoResponse, String> {
let request = acp::ExtRequest::new(
"x.ai/session/info",
"kigi/session/info",
serde_json::value::to_raw_value(
&serde_json::json!({ "sessionId" : session_id.0.to_string() }),
)
@@ -3584,7 +3584,7 @@ fn prompt_request_meta(
}
serde_json::Value::Object(map)
}
/// Build the `x.ai/interject` params. The optional structured `content`
/// Build the `kigi/interject` params. The optional structured `content`
/// (text + images) is omitted ENTIRELY when `None` so the legacy wire
/// shape stays byte-identical. Extracted from the spawn for testability.
fn build_interject_params(
@@ -67,7 +67,7 @@ fn prompt_request_meta_omits_screen_mode_when_unset() {
assert_eq!(meta, serde_json::json!({ "promptId" : "p-2" }));
}
/// Text-only interjections must omit the `content` key entirely — the
/// legacy `x.ai/interject` wire shape stays byte-identical.
/// legacy `kigi/interject` wire shape stays byte-identical.
#[test]
fn interject_params_omit_content_when_no_blocks() {
let sid = acp::SessionId::new("s1");
@@ -83,7 +83,7 @@ fn interject_params_omit_content_when_no_blocks() {
fn picker_keeps_conversation_with_empty_cwd_and_missing_updated_at() {
let payload = serde_json::json!(
{ "sessions" : [{ "sessionId" : "conv_abc", "cwd" : "", "summary" :
"Compare GPU vendors", "source" : "conversation", "_meta" : { "x.ai/session" : {
"Compare GPU vendors", "source" : "conversation", "_meta" : { "kigi/session" : {
"kind" : "chat" } } }] }
);
let entries = parse_session_picker_entries(&payload);
@@ -97,7 +97,7 @@ fn picker_keeps_old_conversation_past_cutoff() {
let payload = serde_json::json!(
{ "sessions" : [{ "sessionId" : "conv_old", "cwd" : "", "summary" :
"Ancient chat", "source" : "conversation", "updatedAt" : "2020-01-01T00:00:00Z",
"_meta" : { "x.ai/session" : { "kind" : "chat" } } }] }
"_meta" : { "kigi/session" : { "kind" : "chat" } } }] }
);
let entries = parse_session_picker_entries(&payload);
assert_eq!(entries.len(), 1, "old conversation must still render");
@@ -112,13 +112,13 @@ fn picker_drops_local_with_missing_updated_at() {
let entries = parse_session_picker_entries(&payload);
assert!(entries.is_empty(), "local rows still require a parseable updatedAt");
}
/// Untitled grok.com chats must stay listed, rendered as "Untitled".
/// Untitled kimi.com chats must stay listed, rendered as "Untitled".
#[test]
fn picker_keeps_untitled_conversation_as_untitled() {
let payload = serde_json::json!(
{ "sessions" : [{ "sessionId" : "conv_untitled", "cwd" : "", "summary" : "",
"source" : "conversation", "updatedAt" : "2026-07-01T00:00:00Z", "_meta" : {
"x.ai/session" : { "kind" : "chat" } } }] }
"kigi/session" : { "kind" : "chat" } } }] }
);
let entries = parse_session_picker_entries(&payload);
assert_eq!(entries.len(), 1, "untitled conversation must not vanish");
@@ -277,7 +277,7 @@ fn interject_params_carry_content_when_blocks_present() {
assert_eq!(content.len(), 1);
assert_eq!(content[0] ["text"], "look at [Image #1]");
}
/// `x.ai/billing` ext result (a serialized shell `UsageResponse`) parses
/// `kigi/billing` ext result (a serialized shell `UsageResponse`) parses
/// into typed rows; unknown labels/reset hints survive the round trip.
#[test]
fn parse_usage_response_reads_rows_from_fixture() {
@@ -473,7 +473,7 @@ async fn persist_setting_type_mismatch_errors_simple_mode() {
}
use std::sync::Arc;
use std::sync::atomic::{AtomicUsize, Ordering};
/// Spawn a fake ACP agent that counts `x.ai/yolo_mode_changed`
/// Spawn a fake ACP agent that counts `kigi/yolo_mode_changed`
/// notifications. Exits when the channel closes.
fn spawn_fake_acp_agent(
mut rx: tokio::sync::mpsc::UnboundedReceiver<kigi_acp_lib::AcpAgentMessage>,
@@ -483,7 +483,7 @@ fn spawn_fake_acp_agent(
tokio::spawn(async move {
while let Some(msg) = rx.recv().await {
if let kigi_acp_lib::AcpAgentMessage::ExtNotification(args) = msg {
if args.request.method.as_ref() == "x.ai/yolo_mode_changed" {
if args.request.method.as_ref() == "kigi/yolo_mode_changed" {
counter_clone.fetch_add(1, Ordering::SeqCst);
}
let _ = args.response_tx.send(Ok(()));
@@ -590,7 +590,7 @@ async fn persist_permission_mode_acp_notification_fires_once_on_best_effort() {
tokio::time::sleep(std::time::Duration::from_millis(50)).await;
assert_eq!(
counter.load(Ordering::SeqCst), 1,
"ACP `x.ai/yolo_mode_changed` notification must fire exactly once \
"ACP `kigi/yolo_mode_changed` notification must fire exactly once \
on BestEffort path (regardless of disk outcome)",
);
assert!(
@@ -972,7 +972,7 @@ async fn fetch_session_list_pushes_query_and_echoes_seq() {
tokio::spawn(async move {
while let Some(msg) = rx.recv().await {
if let AcpAgentMessage::ExtMethod(args) = msg {
assert_eq!(args.request.method.as_ref(), "x.ai/session/list");
assert_eq!(args.request.method.as_ref(), "kigi/session/list");
let params: serde_json::Value = serde_json::from_str(
args.request.params.get(),
)
@@ -1094,7 +1094,7 @@ fn agent_profile_names_are_valid_builtins() {
ask_user: false,
..Default::default()
},
"grok-build-plan",
"kigi-plan",
),
(
SessionFlags {
@@ -1103,7 +1103,7 @@ fn agent_profile_names_are_valid_builtins() {
ask_user: false,
..Default::default()
},
"grok-build-plan-no-subagents",
"kigi-plan-no-subagents",
),
(
SessionFlags {
@@ -1112,7 +1112,7 @@ fn agent_profile_names_are_valid_builtins() {
ask_user: true,
..Default::default()
},
"grok-build-plan",
"kigi-plan",
),
(
SessionFlags {
@@ -1121,7 +1121,7 @@ fn agent_profile_names_are_valid_builtins() {
ask_user: true,
..Default::default()
},
"grok-build-plan-no-subagents",
"kigi-plan-no-subagents",
),
(
SessionFlags {
@@ -1130,7 +1130,7 @@ fn agent_profile_names_are_valid_builtins() {
ask_user: true,
..Default::default()
},
"grok-build-ask-user",
"kigi-ask-user",
),
(
SessionFlags {
@@ -1139,7 +1139,7 @@ fn agent_profile_names_are_valid_builtins() {
ask_user: true,
..Default::default()
},
"grok-build-ask-user",
"kigi-ask-user",
),
];
for (flags, expected_name) in test_cases {
@@ -1156,13 +1156,13 @@ fn agent_profile_names_are_valid_builtins() {
);
}
}
/// Default flags produce no agent profile (uses grok-build default).
/// Default flags produce no agent profile (uses kigi default).
#[test]
fn default_flags_produce_no_profile() {
let flags = SessionFlags::default();
assert_eq!(flags.agent_profile(), None);
}
/// --subagents alone produces no profile (grok-build already has TaskTool).
/// --subagents alone produces no profile (kigi already has TaskTool).
#[test]
fn subagents_without_plan_produces_no_profile() {
let flags = SessionFlags {
@@ -1178,7 +1178,7 @@ fn subagents_without_plan_produces_no_profile() {
/// escape hatch and drops `agentProfile` — the tests would then assert the
/// wrong branch. Empty string counts as unset (`!s.trim().is_empty()`).
/// Callers must be `#[serial_test::serial(KIGI_AGENT)]` (process-global env).
fn without_grok_agent() -> crate::test_util::EnvVarGuard {
fn without_kigi_agent() -> crate::test_util::EnvVarGuard {
crate::test_util::EnvVarGuard::set("KIGI_AGENT", "")
}
/// At the runtime defaults (every `--no-*` flag false → every
@@ -1187,7 +1187,7 @@ fn without_grok_agent() -> crate::test_util::EnvVarGuard {
#[serial_test::serial(KIGI_AGENT)]
#[test]
fn runtime_default_flags_produce_plan_meta() {
let _env = without_grok_agent();
let _env = without_kigi_agent();
let flags = SessionFlags {
plan_mode: true,
subagents: true,
@@ -1195,7 +1195,7 @@ fn runtime_default_flags_produce_plan_meta() {
..Default::default()
};
let meta = flags.to_meta().unwrap();
assert_eq!(meta["agentProfile"], "grok-build-plan");
assert_eq!(meta["agentProfile"], "kigi-plan");
assert!(meta.get("askUserQuestion").is_none());
assert_eq!(meta["yoloMode"], false);
}
@@ -1204,7 +1204,7 @@ fn runtime_default_flags_produce_plan_meta() {
#[serial_test::serial(KIGI_AGENT)]
#[test]
fn plan_only_meta() {
let _env = without_grok_agent();
let _env = without_kigi_agent();
let flags = SessionFlags {
plan_mode: true,
subagents: false,
@@ -1212,7 +1212,7 @@ fn plan_only_meta() {
..Default::default()
};
let meta = flags.to_meta().unwrap();
assert_eq!(meta["agentProfile"], "grok-build-plan-no-subagents");
assert_eq!(meta["agentProfile"], "kigi-plan-no-subagents");
assert_eq!(meta["askUserQuestion"], false);
assert_eq!(meta["yoloMode"], false);
}
@@ -1220,7 +1220,7 @@ fn plan_only_meta() {
#[serial_test::serial(KIGI_AGENT)]
#[test]
fn plan_with_subagents_meta() {
let _env = without_grok_agent();
let _env = without_kigi_agent();
let flags = SessionFlags {
plan_mode: true,
subagents: true,
@@ -1228,15 +1228,15 @@ fn plan_with_subagents_meta() {
..Default::default()
};
let meta = flags.to_meta().unwrap();
assert_eq!(meta["agentProfile"], "grok-build-plan");
assert_eq!(meta["agentProfile"], "kigi-plan");
assert_eq!(meta["askUserQuestion"], false);
assert_eq!(meta["yoloMode"], false);
}
/// --ask-user alone selects the grok-build-ask-user profile.
/// --ask-user alone selects the kigi-ask-user profile.
#[serial_test::serial(KIGI_AGENT)]
#[test]
fn ask_user_alone_meta() {
let _env = without_grok_agent();
let _env = without_kigi_agent();
let flags = SessionFlags {
plan_mode: false,
subagents: false,
@@ -1244,7 +1244,7 @@ fn ask_user_alone_meta() {
..Default::default()
};
let meta = flags.to_meta().unwrap();
assert_eq!(meta["agentProfile"], "grok-build-ask-user");
assert_eq!(meta["agentProfile"], "kigi-ask-user");
assert!(meta.get("askUserQuestion").is_none());
assert_eq!(meta["yoloMode"], false);
}
@@ -1252,7 +1252,7 @@ fn ask_user_alone_meta() {
#[serial_test::serial(KIGI_AGENT)]
#[test]
fn plan_with_ask_user_uses_plan_profile() {
let _env = without_grok_agent();
let _env = without_kigi_agent();
let flags = SessionFlags {
plan_mode: true,
subagents: false,
@@ -1260,14 +1260,14 @@ fn plan_with_ask_user_uses_plan_profile() {
..Default::default()
};
let meta = flags.to_meta().unwrap();
assert_eq!(meta["agentProfile"], "grok-build-plan-no-subagents");
assert_eq!(meta["agentProfile"], "kigi-plan-no-subagents");
assert!(meta.get("askUserQuestion").is_none());
assert_eq!(meta["yoloMode"], false);
}
/// --no-plan --no-subagents --no-ask-user picks the default profile but
/// must still emit `askUserQuestion: false` so the shell can strip the
/// tool at the builder. Mirrors the runtime: `subagents` toggle alone
/// does not need an `agentProfile` (default `grok-build` already has it).
/// does not need an `agentProfile` (default `kigi` already has it).
#[test]
fn subagents_alone_emits_only_ask_user_question_disable() {
let flags = SessionFlags {
@@ -1280,12 +1280,12 @@ fn subagents_alone_emits_only_ask_user_question_disable() {
assert!(meta.get("agentProfile").is_none());
assert_eq!(meta["askUserQuestion"], false);
}
/// All three flags on at the runtime default produce grok-build-plan
/// All three flags on at the runtime default produce kigi-plan
/// and no `askUserQuestion` field.
#[serial_test::serial(KIGI_AGENT)]
#[test]
fn all_flags_meta() {
let _env = without_grok_agent();
let _env = without_kigi_agent();
let flags = SessionFlags {
plan_mode: true,
subagents: true,
@@ -1293,7 +1293,7 @@ fn all_flags_meta() {
..Default::default()
};
let meta = flags.to_meta().unwrap();
assert_eq!(meta["agentProfile"], "grok-build-plan");
assert_eq!(meta["agentProfile"], "kigi-plan");
assert!(meta.get("askUserQuestion").is_none());
assert_eq!(meta["yoloMode"], false);
}
@@ -1388,7 +1388,7 @@ fn to_meta_chat_mode_stamps_kind_and_omits_agent_profile() {
..Default::default()
};
let meta = flags.to_meta().expect("chat_mode must emit meta");
assert_eq!(meta["x.ai/session"] ["kind"], "chat");
assert_eq!(meta["kigi/session"] ["kind"], "chat");
assert!(
meta.get("agentProfile").is_none(), "K12: chat mode must omit Build agentProfile"
);
@@ -1414,7 +1414,7 @@ fn load_meta_chat_kind_alone_stamps_kind_and_strips_profile() {
scrub_chat_workspace_bind_meta(&mut meta);
}
let meta = meta.expect("chat_kind must produce meta");
assert_eq!(meta["x.ai/session"] ["kind"], "chat");
assert_eq!(meta["kigi/session"] ["kind"], "chat");
assert!(
meta.get("agentProfile").is_none(),
"entry chat_kind must strip Build agentProfile"
@@ -1444,7 +1444,7 @@ fn chat_create_meta_never_includes_workspace_bind_keys_when_cloud_fields_set() {
apply_chat_kind_meta(&mut meta);
scrub_chat_workspace_bind_meta(&mut meta);
let meta = meta.expect("chat create must emit meta");
assert_eq!(meta["x.ai/session"] ["kind"], "chat");
assert_eq!(meta["kigi/session"] ["kind"], "chat");
assert_chat_meta_has_no_workspace_bind_keys(
&serde_json::Value::Object(meta.clone()),
);
@@ -1457,15 +1457,15 @@ fn chat_load_meta_never_includes_workspace_bind_keys() {
{
let obj = meta.get_or_insert_with(acp::Meta::new);
obj.insert("envId".into(), serde_json::json!("env-poison"));
obj.insert("x.ai/cloud_server_id".into(), serde_json::json!("srv-poison"));
obj.insert("kigi/cloud_server_id".into(), serde_json::json!("srv-poison"));
obj.insert(
"x.ai/cloud_existing_workspace".into(),
"kigi/cloud_existing_workspace".into(),
serde_json::json!({ "server_id" : "srv-poison", "cwd" : "/ws", }),
);
}
scrub_chat_workspace_bind_meta(&mut meta);
let meta = meta.expect("chat load must emit meta");
assert_eq!(meta["x.ai/session"] ["kind"], "chat");
assert_eq!(meta["kigi/session"] ["kind"], "chat");
assert_chat_meta_has_no_workspace_bind_keys(
&serde_json::Value::Object(meta.clone()),
);
@@ -1491,9 +1491,9 @@ fn agent_profile_definitions_have_correct_names() {
use std::str::FromStr;
use kigi_agent::config::BuiltinAgentName;
for name in [
"grok-build-plan",
"grok-build-plan-no-subagents",
"grok-build-ask-user",
"kigi-plan",
"kigi-plan-no-subagents",
"kigi-ask-user",
] {
let builtin = BuiltinAgentName::from_str(name).unwrap();
let def = builtin.definition();
@@ -1542,23 +1542,23 @@ fn format_session_info_shows_conversation_id_when_present() {
}
#[test]
fn format_session_info_shows_resolved_when_enabled_and_different() {
let info = make_session_info("grok-4.5", Some("grok-4.3"), 1000, 10000);
let info = make_session_info("kigi-4.5", Some("kigi-4.3"), 1000, 10000);
let text = format_session_info(&info, None, true);
assert!(text.contains("Model: grok-4.5 (grok-4.3)"));
assert!(text.contains("Model: kigi-4.5 (kigi-4.3)"));
}
#[test]
fn format_session_info_hides_resolved_when_disabled() {
let info = make_session_info("grok-4.5", Some("grok-4.3"), 1000, 10000);
let info = make_session_info("kigi-4.5", Some("kigi-4.3"), 1000, 10000);
let text = format_session_info(&info, None, false);
assert!(text.contains("Model: grok-4.5"));
assert!(! text.contains("grok-4.3"));
assert!(text.contains("Model: kigi-4.5"));
assert!(! text.contains("kigi-4.3"));
}
#[test]
fn format_session_info_no_parens_when_resolved_matches_requested() {
let info = make_session_info("grok-4.5", Some("grok-4.5"), 1000, 10000);
let info = make_session_info("kigi-4.5", Some("kigi-4.5"), 1000, 10000);
let text = format_session_info(&info, None, true);
assert!(text.contains("Model: grok-4.5"));
assert!(! text.contains("(grok-4.5)"));
assert!(text.contains("Model: kigi-4.5"));
assert!(! text.contains("(kigi-4.5)"));
}
#[test]
fn format_session_info_shows_model_hash_when_catalog_flag_set() {
@@ -1647,7 +1647,7 @@ fn session_picker_entry_maps_to_dormant_roster_row() {
cwd: "/repo/app".to_string(),
hostname: Some("box".to_string()),
source: "local".to_string(),
model_id: Some("grok-4".to_string()),
model_id: Some("kigi-4".to_string()),
num_messages: 3,
last_active_at: Some(updated),
branch: None,
@@ -1660,7 +1660,7 @@ fn session_picker_entry_maps_to_dormant_roster_row() {
assert_eq!(roster.title.as_deref(), Some("Wire up dashboard"));
assert_eq!(roster.cwd, "/repo/app");
assert!(roster.is_worktree, "worktree_label present → is_worktree");
assert_eq!(roster.model_id.as_deref(), Some("grok-4"));
assert_eq!(roster.model_id.as_deref(), Some("kigi-4"));
assert_eq!(roster.activity, RosterActivity::Dormant);
assert!(! roster.resident);
assert_eq!(roster.last_change_unix_ms, updated.timestamp_millis());