§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:
@@ -2,7 +2,7 @@
|
||||
//!
|
||||
//! Parses the `_meta` JSON from `SessionNotification` into a struct with
|
||||
//! typed fields. All fields are `Option` — gracefully degrades when
|
||||
//! grok-shell hasn't been updated or meta is absent.
|
||||
//! kigi-shell hasn't been updated or meta is absent.
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
@@ -154,7 +154,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn parse_missing_new_fields() {
|
||||
// Simulate old grok-shell that doesn't send streamStartMs/turnStartMs
|
||||
// Simulate old kigi-shell that doesn't send streamStartMs/turnStartMs
|
||||
let meta_json = json!({
|
||||
"totalTokens": 1000u64,
|
||||
"agentTimestampMs": 1700000000000i64,
|
||||
|
||||
@@ -54,8 +54,8 @@ pub struct AcpConnection {
|
||||
pub rx: AcpClientRx,
|
||||
/// Available models and current selection.
|
||||
pub models: ModelState,
|
||||
/// Whether the agent is a grok-shell instance.
|
||||
pub is_grok_shell: bool,
|
||||
/// Whether the agent is a kigi-shell instance.
|
||||
pub is_kigi_shell: bool,
|
||||
/// Auth methods advertised by the agent.
|
||||
pub auth_methods: Vec<acp::AuthMethod>,
|
||||
/// Cancellation token to stop the agent.
|
||||
@@ -64,9 +64,9 @@ pub struct AcpConnection {
|
||||
/// Seeded into every new `AgentSession` so autocomplete has shell builtins
|
||||
/// and skills immediately, before any `AvailableCommandsUpdate` arrives.
|
||||
pub available_commands: Vec<acp::AvailableCommand>,
|
||||
/// Whether interactive login is required (deferred auth for `grok.com`).
|
||||
/// Whether interactive login is required (deferred auth for `kimi-code`).
|
||||
pub needs_login: bool,
|
||||
/// Login button label from `AuthMethod.name` (e.g., "grok.com", "Acme Corp").
|
||||
/// Login button label from `AuthMethod.name` (e.g., "kimi-code", "Acme Corp").
|
||||
pub login_label: Option<String>,
|
||||
/// The auth method ID to use for login (copied from the first advertised method).
|
||||
pub login_method_id: Option<acp::AuthMethodId>,
|
||||
@@ -83,7 +83,7 @@ pub struct AcpConnection {
|
||||
/// resolved by the shell (remote settings / config / env; default OFF) and
|
||||
/// advertised in `InitializeResponse.meta.sessionRecap`. The client gates
|
||||
/// its automatic away-recap poll and the manual `/recap` on this so a
|
||||
/// disabled feature produces zero `x.ai/recap` traffic. Defaults to `false`
|
||||
/// disabled feature produces zero `kigi/recap` traffic. Defaults to `false`
|
||||
/// when absent (e.g. an older shell that predates the feature).
|
||||
pub session_recap_available: bool,
|
||||
/// `AuthManager` for pager-side authenticated channels.
|
||||
@@ -183,14 +183,14 @@ pub async fn connect(cancel: &CancellationToken, flags: ConnectFlags) -> Result<
|
||||
|
||||
// Spawn the agent
|
||||
let memory_config = agent_config.memory_config.clone();
|
||||
let spawned = spawn::spawn_grok_shell(agent_config, cancel, memory_config).await?;
|
||||
let spawned = spawn::spawn_kigi_shell(agent_config, cancel, memory_config).await?;
|
||||
let auth_manager = spawned.auth_manager.clone();
|
||||
let (tx, rx) = (spawned.channel.tx, spawned.channel.rx);
|
||||
|
||||
// Initialize
|
||||
let (
|
||||
models,
|
||||
is_grok_shell,
|
||||
is_kigi_shell,
|
||||
auth_methods,
|
||||
default_auth_method_id,
|
||||
available_commands,
|
||||
@@ -218,7 +218,7 @@ pub async fn connect(cancel: &CancellationToken, flags: ConnectFlags) -> Result<
|
||||
tx,
|
||||
rx,
|
||||
models,
|
||||
is_grok_shell,
|
||||
is_kigi_shell,
|
||||
auth_methods,
|
||||
cancel: spawned.cancel,
|
||||
available_commands,
|
||||
@@ -291,7 +291,7 @@ pub async fn connect_via_leader(
|
||||
|
||||
let (
|
||||
models,
|
||||
is_grok_shell,
|
||||
is_kigi_shell,
|
||||
auth_methods,
|
||||
default_auth_method_id,
|
||||
available_commands,
|
||||
@@ -329,7 +329,7 @@ pub async fn connect_via_leader(
|
||||
tx,
|
||||
rx,
|
||||
models,
|
||||
is_grok_shell,
|
||||
is_kigi_shell,
|
||||
auth_methods,
|
||||
cancel: bridge.cancel,
|
||||
available_commands,
|
||||
@@ -439,10 +439,10 @@ fn client_capabilities_meta(flags: &ConnectFlags) -> serde_json::Value {
|
||||
let hunk_mode =
|
||||
crate::settings::canonical_hunk_tracker_mode(flags.hunk_tracker_mode.as_deref());
|
||||
serde_json::json!({
|
||||
"x.ai/incrementalBashOutput": true,
|
||||
"x.ai/hunkTracker": { "mode": hunk_mode },
|
||||
"x.ai/bashOutputNoColor": true,
|
||||
"x.ai/gitHeadChanged": true,
|
||||
"kigi/incrementalBashOutput": true,
|
||||
"kigi/hunkTracker": { "mode": hunk_mode },
|
||||
"kigi/bashOutputNoColor": true,
|
||||
"kigi/gitHeadChanged": true,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -482,11 +482,11 @@ async fn initialize(
|
||||
|
||||
let resp: acp::InitializeResponse = acp_send(req, tx).await?;
|
||||
|
||||
// Check if this is a grok-shell agent
|
||||
let is_grok_shell = resp
|
||||
// Check if this is a kigi-shell agent
|
||||
let is_kigi_shell = resp
|
||||
.meta
|
||||
.as_ref()
|
||||
.and_then(|m| m.get("grokShell"))
|
||||
.and_then(|m| m.get("kigiShell"))
|
||||
.and_then(|v| v.as_bool())
|
||||
.unwrap_or(false);
|
||||
|
||||
@@ -514,7 +514,7 @@ async fn initialize(
|
||||
|
||||
Ok((
|
||||
models,
|
||||
is_grok_shell,
|
||||
is_kigi_shell,
|
||||
resp.auth_methods,
|
||||
default_auth_method_id,
|
||||
available_commands,
|
||||
@@ -545,7 +545,7 @@ pub fn parse_session_recap_available(meta: Option<&acp::Meta>) -> bool {
|
||||
|
||||
/// Determine whether interactive login is needed based on the advertised auth methods.
|
||||
///
|
||||
/// Matches TUI startup behavior: if the first method is `grok.com`, defer auth
|
||||
/// Matches TUI startup behavior: if the first method is `kimi-code`, defer auth
|
||||
/// and show the login-aware welcome flow. Otherwise, authenticate eagerly.
|
||||
///
|
||||
/// Returns `(needs_login, login_label, login_method_id, auth_start_mode)`.
|
||||
@@ -589,7 +589,7 @@ pub fn startup_auth_metadata(
|
||||
///
|
||||
/// Used when eager auth (cached_token / API key) fails and we need to fall
|
||||
/// back to the welcome screen with a working login button. Scans the list
|
||||
/// for a `grok.com` or `oidc` method — these are the ones that can trigger
|
||||
/// for a `kimi-code` or `oidc` method — these are the ones that can trigger
|
||||
/// a browser-based re-auth flow.
|
||||
pub fn find_interactive_login_method(
|
||||
auth_methods: &[acp::AuthMethod],
|
||||
@@ -767,7 +767,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn parse_available_commands_missing_key_returns_empty() {
|
||||
let meta = serde_json::json!({ "grokShell": true });
|
||||
let meta = serde_json::json!({ "kigiShell": true });
|
||||
let cmds = parse_available_commands(meta.as_object());
|
||||
assert!(cmds.is_empty());
|
||||
}
|
||||
@@ -801,7 +801,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn parse_session_recap_available_defaults_off_when_missing() {
|
||||
let meta = serde_json::json!({ "grokShell": true, "cancelRewind": true });
|
||||
let meta = serde_json::json!({ "kigiShell": true, "cancelRewind": true });
|
||||
assert!(!parse_session_recap_available(meta.as_object()));
|
||||
assert!(!parse_session_recap_available(None));
|
||||
}
|
||||
@@ -832,28 +832,28 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn startup_auth_grok_com_no_provider_needs_login_pending() {
|
||||
let methods = vec![make_auth_method("grok.com", "grok.com", None)];
|
||||
fn startup_auth_kigi_com_no_provider_needs_login_pending() {
|
||||
let methods = vec![make_auth_method("kimi-code", "kimi-code", None)];
|
||||
let (needs, label, method_id, mode) = startup_auth_metadata(&methods);
|
||||
assert!(needs);
|
||||
assert_eq!(label.as_deref(), Some("grok.com"));
|
||||
assert_eq!(method_id.as_ref().unwrap().0.as_ref(), "grok.com");
|
||||
assert_eq!(label.as_deref(), Some("kimi-code"));
|
||||
assert_eq!(method_id.as_ref().unwrap().0.as_ref(), "kimi-code");
|
||||
assert_eq!(mode, AuthStartMode::Pending);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn startup_auth_grok_com_with_external_provider_command() {
|
||||
fn startup_auth_kigi_com_with_external_provider_command() {
|
||||
let meta = serde_json::json!({ "external_provider": true });
|
||||
let methods = vec![make_auth_method("grok.com", "Acme Corp", Some(meta))];
|
||||
let methods = vec![make_auth_method("kimi-code", "Acme Corp", Some(meta))];
|
||||
let (needs, label, method_id, mode) = startup_auth_metadata(&methods);
|
||||
assert!(needs);
|
||||
assert_eq!(label.as_deref(), Some("Acme Corp"));
|
||||
assert_eq!(method_id.as_ref().unwrap().0.as_ref(), "grok.com");
|
||||
assert_eq!(method_id.as_ref().unwrap().0.as_ref(), "kimi-code");
|
||||
assert_eq!(mode, AuthStartMode::Command);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn startup_auth_non_grok_com_no_login() {
|
||||
fn startup_auth_non_kigi_com_no_login() {
|
||||
let methods = vec![make_auth_method("api-key", "API Key", None)];
|
||||
let (needs, label, method_id, mode) = startup_auth_metadata(&methods);
|
||||
assert!(!needs);
|
||||
@@ -890,7 +890,7 @@ mod tests {
|
||||
// enterprise-style: model has `env_key` set and the env var resolves,
|
||||
// so the shell-side predicate returns true.
|
||||
has_external_api_key: true,
|
||||
// Realistic enterprise user: no cached session token, default `grok.com`
|
||||
// Realistic enterprise user: no cached session token, default `kimi-code`
|
||||
// login (no enterprise OIDC).
|
||||
has_cached_token: false,
|
||||
login_label: None,
|
||||
@@ -916,27 +916,27 @@ mod tests {
|
||||
/// `auth_methods.first()`. This locks the failure mode of the regression:
|
||||
/// if a future refactor makes the pager scan past `.first()`, this test
|
||||
/// stops being equivalent to
|
||||
/// `startup_auth_grok_com_no_provider_needs_login_pending` above and
|
||||
/// `startup_auth_kigi_com_no_provider_needs_login_pending` above and
|
||||
/// either passes or fails on a meaningful new code path.
|
||||
#[test]
|
||||
fn startup_auth_xai_api_key_not_first_still_requires_login() {
|
||||
use kigi_shell::agent::auth_method::{KIGI_COM_METHOD_ID, XAI_API_KEY_METHOD_ID};
|
||||
use kigi_shell::agent::auth_method::{KIMI_CODE_METHOD_ID, XAI_API_KEY_METHOD_ID};
|
||||
|
||||
let methods = vec![
|
||||
make_auth_method(KIGI_COM_METHOD_ID, "Grok", None),
|
||||
make_auth_method(KIMI_CODE_METHOD_ID, "Kigi", None),
|
||||
make_auth_method(XAI_API_KEY_METHOD_ID, "xai.api_key", None),
|
||||
];
|
||||
let (needs, _, _, _) = startup_auth_metadata(&methods);
|
||||
assert!(
|
||||
needs,
|
||||
"with grok.com first, the pager must require login -- pinning \
|
||||
"with kimi.com first, the pager must require login -- pinning \
|
||||
the BAD-ordering failure mode (xai.api_key not first)",
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn startup_auth_method_id_is_copied_not_synthesized() {
|
||||
let methods = vec![make_auth_method("grok.com", "My Login", None)];
|
||||
let methods = vec![make_auth_method("kimi-code", "My Login", None)];
|
||||
let (_, _, method_id, _) = startup_auth_metadata(&methods);
|
||||
// Verify it's the exact same ID from the method, not hardcoded
|
||||
assert_eq!(&method_id.unwrap(), methods[0].id());
|
||||
@@ -945,7 +945,7 @@ mod tests {
|
||||
#[test]
|
||||
fn startup_auth_external_provider_false_is_pending() {
|
||||
let meta = serde_json::json!({ "external_provider": false });
|
||||
let methods = vec![make_auth_method("grok.com", "grok.com", Some(meta))];
|
||||
let methods = vec![make_auth_method("kimi-code", "kimi-code", Some(meta))];
|
||||
let (_, _, _, mode) = startup_auth_metadata(&methods);
|
||||
assert_eq!(mode, AuthStartMode::Pending);
|
||||
}
|
||||
@@ -1033,12 +1033,12 @@ mod tests {
|
||||
// Rows 1 & 2 of the truth table: nothing set, and a set-but-blank value,
|
||||
// both advertise the `agent_only` default (never `""` → AllDirty).
|
||||
let absent = client_capabilities_meta(&ConnectFlags::default());
|
||||
assert_eq!(absent["x.ai/hunkTracker"]["mode"], "agent_only");
|
||||
assert_eq!(absent["kigi/hunkTracker"]["mode"], "agent_only");
|
||||
let blank = client_capabilities_meta(&ConnectFlags {
|
||||
hunk_tracker_mode: Some(" ".into()),
|
||||
..Default::default()
|
||||
});
|
||||
assert_eq!(blank["x.ai/hunkTracker"]["mode"], "agent_only");
|
||||
assert_eq!(blank["kigi/hunkTracker"]["mode"], "agent_only");
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -1050,7 +1050,7 @@ mod tests {
|
||||
hunk_tracker_mode: Some(raw.into()),
|
||||
..Default::default()
|
||||
});
|
||||
assert_eq!(meta["x.ai/hunkTracker"]["mode"], "off", "raw={raw}");
|
||||
assert_eq!(meta["kigi/hunkTracker"]["mode"], "off", "raw={raw}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -74,7 +74,7 @@ impl ModelState {
|
||||
}
|
||||
}
|
||||
|
||||
/// Machine-readable model ID string for the current model (e.g. "grok-4.5").
|
||||
/// Machine-readable model ID string for the current model (e.g. "kigi-4.5").
|
||||
pub fn current_model_id_str(&self) -> Option<&str> {
|
||||
Some(self.current.as_ref()?.0.as_ref())
|
||||
}
|
||||
@@ -94,7 +94,7 @@ impl ModelState {
|
||||
///
|
||||
/// Honors an explicit `acceptsImages` bool, else an `inputModalities` array
|
||||
/// containing `"image"`. DEFAULTS TO `true` when neither key is present:
|
||||
/// correct today (all current Grok models accept images, so nothing is
|
||||
/// correct today (all current Kigi models accept images, so nothing is
|
||||
/// suppressed) and forward-compatible (suppresses non-vision models once the
|
||||
/// ACP server populates the key). Populating that key server-side is a
|
||||
/// separate change.
|
||||
@@ -223,7 +223,7 @@ impl ModelState {
|
||||
/// Map a typed/selected effort token to its canonical value for the current
|
||||
/// model. Accepts a menu option id (case-insensitive) or a canonical level
|
||||
/// that appears as a **value** in that model's menu. Levels the model does
|
||||
/// not offer (e.g. `none` on grok-4.5) are rejected so we fail in the TUI
|
||||
/// not offer (e.g. `none` on kigi-4.5) are rejected so we fail in the TUI
|
||||
/// instead of sending a blocked effort to the API.
|
||||
pub fn resolve_effort_token(&self, token: &str) -> Option<ReasoningEffort> {
|
||||
match self.current.as_ref() {
|
||||
@@ -249,7 +249,7 @@ impl ModelState {
|
||||
}
|
||||
// Canonical level (e.g. "high", "max"→xhigh) only if the model menu
|
||||
// actually offers that value — not free-form power-user aliases that
|
||||
// would 400 on the server (e.g. `none` on grok-4.5).
|
||||
// would 400 on the server (e.g. `none` on kigi-4.5).
|
||||
let parsed = token.parse::<ReasoningEffort>().ok()?;
|
||||
options
|
||||
.iter()
|
||||
@@ -410,21 +410,17 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn update_catalog_preserves_user_effort_when_model_unchanged() {
|
||||
let id = acp::ModelId::new(Arc::from("grok-build"));
|
||||
let id = acp::ModelId::new(Arc::from("kigi"));
|
||||
let mut state = ModelState::default();
|
||||
state.available.insert(
|
||||
id.clone(),
|
||||
model_with_effort("grok-build", "Grok Build", "high"),
|
||||
);
|
||||
state
|
||||
.available
|
||||
.insert(id.clone(), model_with_effort("kigi", "Kigi", "high"));
|
||||
state.set_current(id.clone(), Some(ReasoningEffort::Xhigh));
|
||||
assert_eq!(state.reasoning_effort, Some(ReasoningEffort::Xhigh));
|
||||
|
||||
// The broadcast carries the model's static default (high) for the same model.
|
||||
let mut refreshed = IndexMap::new();
|
||||
refreshed.insert(
|
||||
id.clone(),
|
||||
model_with_effort("grok-build", "Grok Build", "high"),
|
||||
);
|
||||
refreshed.insert(id.clone(), model_with_effort("kigi", "Kigi", "high"));
|
||||
state.update_catalog(refreshed, Some(id.clone()));
|
||||
|
||||
assert_eq!(
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
//! Agent spawning — creates the agent process and ACP channels.
|
||||
//!
|
||||
//! Simplified to only support GrokShell (in-process) mode.
|
||||
//! Simplified to only support KigiShell (in-process) mode.
|
||||
//! Subprocess and remote modes can be added later if needed.
|
||||
|
||||
use std::rc::Rc;
|
||||
@@ -30,10 +30,10 @@ pub struct SpawnedAgent {
|
||||
pub auth_manager: std::sync::Arc<AuthManager>,
|
||||
}
|
||||
|
||||
/// Spawn a GrokShell agent in a background thread.
|
||||
/// Spawn a KigiShell agent in a background thread.
|
||||
///
|
||||
/// Returns the ACP client channel for communication and a cancellation token.
|
||||
pub async fn spawn_grok_shell(
|
||||
pub async fn spawn_kigi_shell(
|
||||
agent_config: AgentConfig,
|
||||
cancel: &CancellationToken,
|
||||
memory_config: Option<kigi_shell::config::MemoryConfig>,
|
||||
|
||||
@@ -255,7 +255,7 @@ pub struct AcpUpdateTracker {
|
||||
/// Tool call IDs marked as background (`is_background=true`).
|
||||
///
|
||||
/// First-detection (no scrollback entry yet): defers entry creation until
|
||||
/// `x.ai/task_backgrounded` creates a `BgTask` block.
|
||||
/// `kigi/task_backgrounded` creates a `BgTask` block.
|
||||
/// Late-detection (Execute block already exists): suppresses further output
|
||||
/// streaming; the existing block is demoted by `handle_task_backgrounded`.
|
||||
///
|
||||
@@ -2129,7 +2129,7 @@ fn task_ids_from_raw_input(raw: &serde_json::Value) -> Vec<String> {
|
||||
}
|
||||
/// Check if a tool call is a background execute (`is_background=true`).
|
||||
///
|
||||
/// These are deferred from scrollback — the `x.ai/task_backgrounded`
|
||||
/// These are deferred from scrollback — the `kigi/task_backgrounded`
|
||||
/// notification creates a `BgTask` block instead of an `Execute` block.
|
||||
///
|
||||
/// Eager ACP messages often use `kind=Other` with `title=run_terminal_command`
|
||||
@@ -2602,7 +2602,7 @@ fn make_relative_path(path: &str) -> String {
|
||||
mod tests {
|
||||
use super::*;
|
||||
use std::sync::Arc;
|
||||
/// Default meta with no timestamps (simulates old grok-shell or tests that
|
||||
/// Default meta with no timestamps (simulates old kigi-shell or tests that
|
||||
/// don't care about timing).
|
||||
fn meta() -> NotificationMeta {
|
||||
NotificationMeta::default()
|
||||
@@ -4602,7 +4602,7 @@ mod tests {
|
||||
"stream A message should be finished"
|
||||
);
|
||||
}
|
||||
/// No stream_start_ms (old grok-shell) should not break anything.
|
||||
/// No stream_start_ms (old kigi-shell) should not break anything.
|
||||
#[test]
|
||||
fn no_stream_start_ms_preserves_existing_behavior() {
|
||||
let mut sb = ScrollbackState::new();
|
||||
@@ -5554,7 +5554,7 @@ mod tests {
|
||||
.status(acp::ToolCallStatus::Pending)
|
||||
}
|
||||
#[test]
|
||||
fn is_task_tool_recognizes_grok_build_variant() {
|
||||
fn is_task_tool_recognizes_kigi_variant() {
|
||||
assert!(is_task_tool(&initial_tool_call("tc1", "task")));
|
||||
let mut with_variant = initial_tool_call("tc2", "anything");
|
||||
with_variant.raw_input = Some(serde_json::json!({ "variant" : "Task" }));
|
||||
|
||||
@@ -62,7 +62,7 @@ pub(super) fn route_bg_task_stdout(
|
||||
true // Consumed — don't pass to tracker
|
||||
}
|
||||
|
||||
/// Handle `x.ai/task_backgrounded` — a bash command transitioned to background.
|
||||
/// Handle `kigi/task_backgrounded` — a bash command transitioned to background.
|
||||
///
|
||||
/// Creates a `BgTaskState` in the central store and sets up the
|
||||
/// `tool_call_id → task_id` correlation for stdout routing.
|
||||
@@ -74,7 +74,7 @@ pub(super) fn route_bg_task_stdout(
|
||||
pub(super) fn handle_task_backgrounded(notif: &acp::ExtNotification, app: &mut AppView) -> bool {
|
||||
// Parse the SessionNotification envelope
|
||||
let Ok(session_notif) = serde_json::from_str::<SessionNotification>(notif.params.get()) else {
|
||||
tracing::warn!("Failed to parse x.ai/task_backgrounded");
|
||||
tracing::warn!("Failed to parse kigi/task_backgrounded");
|
||||
return false;
|
||||
};
|
||||
|
||||
@@ -241,7 +241,7 @@ pub(super) fn handle_task_backgrounded(notif: &acp::ExtNotification, app: &mut A
|
||||
is_active
|
||||
}
|
||||
|
||||
/// Handle `x.ai/monitor_event` — background task or monitor emitted new output.
|
||||
/// Handle `kigi/monitor_event` — background task or monitor emitted new output.
|
||||
pub(super) fn handle_monitor_event(notif: &acp::ExtNotification, app: &mut AppView) -> bool {
|
||||
let Ok(session_notif) = serde_json::from_str::<SessionNotification>(notif.params.get()) else {
|
||||
return false;
|
||||
@@ -414,16 +414,16 @@ pub(super) fn handle_scheduled_task_inject_prompt(
|
||||
let payload: serde_json::Value = match serde_json::from_str(notif.params.get()) {
|
||||
Ok(v) => v,
|
||||
Err(e) => {
|
||||
tracing::warn!(error = %e, "Failed to parse x.ai/scheduled_task_inject_prompt");
|
||||
tracing::warn!(error = %e, "Failed to parse kigi/scheduled_task_inject_prompt");
|
||||
return false;
|
||||
}
|
||||
};
|
||||
let Some(session_id) = payload["sessionId"].as_str() else {
|
||||
tracing::warn!("x.ai/scheduled_task_inject_prompt: missing or non-string sessionId");
|
||||
tracing::warn!("kigi/scheduled_task_inject_prompt: missing or non-string sessionId");
|
||||
return false;
|
||||
};
|
||||
let Some(prompt) = payload["prompt"].as_str().filter(|s| !s.is_empty()) else {
|
||||
tracing::warn!("x.ai/scheduled_task_inject_prompt: missing or empty prompt");
|
||||
tracing::warn!("kigi/scheduled_task_inject_prompt: missing or empty prompt");
|
||||
return false;
|
||||
};
|
||||
let task_id = payload["taskId"].as_str().unwrap_or("unknown");
|
||||
@@ -441,7 +441,7 @@ pub(super) fn handle_scheduled_task_inject_prompt(
|
||||
};
|
||||
|
||||
// Only the driver injects + runs the scheduled prompt. In leader mode the
|
||||
// `x.ai/scheduled_task_inject_prompt` notification is routed by the leader
|
||||
// `kigi/scheduled_task_inject_prompt` notification is routed by the leader
|
||||
// to the SINGLE session driver (see `is_scheduled_task_inject_prompt` in
|
||||
// leader/server.rs), so any client that receives it IS the driver and must
|
||||
// enqueue + run it — including a client that attached via `session/load`
|
||||
@@ -551,7 +551,7 @@ pub(super) fn handle_git_head_changed(notif: &acp::ExtNotification, app: &mut Ap
|
||||
pub(super) fn handle_task_completed(notif: &acp::ExtNotification, app: &mut AppView) -> bool {
|
||||
// The payload is a SessionNotification wrapping TaskCompleted { task_snapshot }
|
||||
let Ok(session_notif) = serde_json::from_str::<SessionNotification>(notif.params.get()) else {
|
||||
tracing::warn!("Failed to parse x.ai/task_completed");
|
||||
tracing::warn!("Failed to parse kigi/task_completed");
|
||||
return false;
|
||||
};
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ pub(super) const MAX_FOLLOW_UP_LABEL: usize = 256;
|
||||
/// retained `follow_up_seen` ring.
|
||||
pub(super) const MAX_RESPONSE_ID_LEN: usize = 128;
|
||||
|
||||
/// Deserialize shape of the `x.ai/follow_ups` params emitted by the shell
|
||||
/// Deserialize shape of the `kigi/follow_ups` params emitted by the shell
|
||||
/// translator: `{ response_id, suggestions: [{ label, .. }] }`. The keys are
|
||||
/// prost-derived snake_case — NOT camelCase like most other pager
|
||||
/// notification payloads — so this struct must match snake_case verbatim.
|
||||
@@ -32,13 +32,13 @@ pub(super) struct FollowUpsParams {
|
||||
prompt_id: Option<String>,
|
||||
/// Reserved replay marker carrier. Absent in v1 (the shell never sets
|
||||
/// it); honored from day one so future replay producers need no pager
|
||||
/// change. Parsed loosely as a JSON value to read the `"x.ai/replayed"`
|
||||
/// change. Parsed loosely as a JSON value to read the `"kigi/replayed"`
|
||||
/// key (a slash-bearing key prost cannot model as a field).
|
||||
#[serde(default, rename = "_meta")]
|
||||
meta: Option<serde_json::Value>,
|
||||
}
|
||||
|
||||
/// A single `x.ai/follow_ups` suggestion. Only the human-facing `label` is
|
||||
/// A single `kigi/follow_ups` suggestion. Only the human-facing `label` is
|
||||
/// consumed; `properties` / `tool_overrides` (also in the wire shape) are
|
||||
/// ignored.
|
||||
#[derive(serde::Deserialize)]
|
||||
@@ -60,11 +60,11 @@ pub(super) fn sanitize_suggestion(label: &str) -> String {
|
||||
cleaned.trim().to_owned()
|
||||
}
|
||||
|
||||
/// Handle `x.ai/follow_ups` — render follow-up suggestion chips for the
|
||||
/// Handle `kigi/follow_ups` — render follow-up suggestion chips for the
|
||||
/// latest assistant response.
|
||||
///
|
||||
/// Newest-response-wins keying lives in [`AgentView::apply_follow_ups`]. The
|
||||
/// reserved `_meta["x.ai/replayed"] == true` marker suppresses rendering (it
|
||||
/// reserved `_meta["kigi/replayed"] == true` marker suppresses rendering (it
|
||||
/// is absent today and treated as optional). The params carry no session id,
|
||||
/// so chips target the active agent; a background agent's follow-ups would
|
||||
/// mis-route — a forwarding obligation for the shell to add a session id.
|
||||
@@ -77,7 +77,7 @@ pub(super) fn handle_follow_ups(notif: &acp::ExtNotification, app: &mut AppView)
|
||||
if params
|
||||
.meta
|
||||
.as_ref()
|
||||
.and_then(|m| m.get("x.ai/replayed"))
|
||||
.and_then(|m| m.get("kigi/replayed"))
|
||||
.and_then(|v| v.as_bool())
|
||||
.unwrap_or(false)
|
||||
{
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use super::*;
|
||||
|
||||
/// Handle `x.ai/ask_user_question` ext-method.
|
||||
/// Handle `kigi/ask_user_question` ext-method.
|
||||
///
|
||||
/// Parses the typed request, creates a `QuestionViewState` with the
|
||||
/// `response_tx` stashed, and opens the question overlay. The pager does
|
||||
@@ -14,7 +14,7 @@ pub(crate) fn handle_ask_user_question(
|
||||
app: &mut AppView,
|
||||
) -> bool {
|
||||
use crate::views::question_view::QuestionViewState;
|
||||
use kigi_tools::implementations::grok_build::ask_user_question::{
|
||||
use kigi_tools::implementations::kigi::ask_user_question::{
|
||||
AskUserQuestionExtRequest, AskUserQuestionExtResponse,
|
||||
};
|
||||
|
||||
@@ -119,7 +119,7 @@ pub(crate) fn handle_ask_user_question(
|
||||
is_active
|
||||
}
|
||||
|
||||
/// Handle an `x.ai/exit_plan_mode` ext_method request.
|
||||
/// Handle an `kigi/exit_plan_mode` ext_method request.
|
||||
///
|
||||
/// Creates a `PlanApprovalViewState` overlay for interactive approval.
|
||||
///
|
||||
|
||||
@@ -46,7 +46,7 @@ pub(super) fn handle_mcp_init_progress(notif: &acp::ExtNotification, app: &mut A
|
||||
is_active
|
||||
}
|
||||
|
||||
/// Handle `x.ai/mcp/tools_changed` and `x.ai/mcp_initialized`.
|
||||
/// Handle `kigi/mcp/tools_changed` and `kigi/mcp_initialized`.
|
||||
///
|
||||
/// Routing rules (verified against the four shell emit sites in
|
||||
/// `kigi-shell/src/session/acp_session.rs` — toggle-tool ~L6661,
|
||||
@@ -78,8 +78,8 @@ pub(super) fn handle_mcp_init_progress(notif: &acp::ExtNotification, app: &mut A
|
||||
pub(super) fn handle_mcp_tools_changed(notif: &acp::ExtNotification, app: &mut AppView) -> bool {
|
||||
let method = notif.method.as_ref();
|
||||
|
||||
// Both `x.ai/mcp_initialized` and (newer shell)
|
||||
// `x.ai/mcp/tools_changed` carry `sessionId`. Route by it so a
|
||||
// Both `kigi/mcp_initialized` and (newer shell)
|
||||
// `kigi/mcp/tools_changed` carry `sessionId`. Route by it so a
|
||||
// background agent's notification updates *its* state — not
|
||||
// whichever agent is foregrounded. Unknown and subagent (child)
|
||||
// sessions are dropped; a missing sessionId falls back to the
|
||||
@@ -119,7 +119,7 @@ pub(super) fn handle_mcp_tools_changed(notif: &acp::ExtNotification, app: &mut A
|
||||
let mut redraw = false;
|
||||
|
||||
// `mcp_initialized` clears the matched agent's connecting indicator.
|
||||
if method == "x.ai/mcp_initialized"
|
||||
if method == "kigi/mcp_initialized"
|
||||
&& let Some(agent) = app.agents.get_mut(&id)
|
||||
&& agent.mcp_init_progress.take().is_some()
|
||||
{
|
||||
@@ -163,7 +163,7 @@ pub(super) fn agent_has_pending_mcps_fetch(app: &AppView, agent_id: AgentId) ->
|
||||
})
|
||||
}
|
||||
|
||||
/// Handle `x.ai/mcp/server_status`.
|
||||
/// Handle `kigi/mcp/server_status`.
|
||||
///
|
||||
/// Routes by the notification's `sessionId` via
|
||||
/// [`find_session_match`] — the matched agent's extensions modal is
|
||||
@@ -206,7 +206,7 @@ pub(super) fn handle_mcp_server_status(notif: &acp::ExtNotification, app: &mut A
|
||||
|
||||
let Ok(payload) = serde_json::from_str::<McpServerStatusPayload>(notif.params.get()) else {
|
||||
tracing::warn!(
|
||||
"Failed to parse x.ai/mcp/server_status: {}",
|
||||
"Failed to parse kigi/mcp/server_status: {}",
|
||||
¬if.params.get()
|
||||
[..crate::render::line_utils::floor_char_boundary(notif.params.get(), 100)]
|
||||
);
|
||||
@@ -263,7 +263,7 @@ pub(super) fn handle_mcp_server_status(notif: &acp::ExtNotification, app: &mut A
|
||||
tracing::warn!(
|
||||
server = %payload.name,
|
||||
error = %e,
|
||||
"x.ai/mcp/server_status: tools field present but not Vec<McpToolEntry>; status still applied"
|
||||
"kigi/mcp/server_status: tools field present but not Vec<McpToolEntry>; status still applied"
|
||||
);
|
||||
None
|
||||
}
|
||||
@@ -273,7 +273,7 @@ pub(super) fn handle_mcp_server_status(notif: &acp::ExtNotification, app: &mut A
|
||||
mutated && is_active
|
||||
}
|
||||
|
||||
/// Handle `x.ai/mcp/servers_updated`.
|
||||
/// Handle `kigi/mcp/servers_updated`.
|
||||
///
|
||||
/// Emitted by the shell from `MvpAgent` on managed-config resolve and
|
||||
/// on config reload (`crates/codegen/kigi-shell/src/agent/mvp_agent.rs`
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
//!
|
||||
//! Routes incoming [`AcpClientMessage`] notifications to the appropriate
|
||||
//! agent's tracker, queues permission requests for interactive handling,
|
||||
//! and xAI session extension notifications (`x.ai/session_notification` and
|
||||
//! replay-path `x.ai/session/update`).
|
||||
//! and xAI session extension notifications (`kigi/session_notification` and
|
||||
//! replay-path `kigi/session/update`).
|
||||
|
||||
use std::collections::hash_map::Entry;
|
||||
use std::path::PathBuf;
|
||||
@@ -591,39 +591,39 @@ pub(crate) fn handle(msg: AcpClientMessage, app: &mut AppView) -> bool {
|
||||
/// Handle an xAI extension notification.
|
||||
///
|
||||
/// Dispatches on method string:
|
||||
/// - `x.ai/session_notification` / `x.ai/session/update` → per-agent session updates
|
||||
/// - `kigi/session_notification` / `kigi/session/update` → per-agent session updates
|
||||
fn handle_ext_notification(notif: &acp::ExtNotification, app: &mut AppView) -> bool {
|
||||
match notif.method.as_ref() {
|
||||
"x.ai/session_notification" | "x.ai/session/update" => {
|
||||
"kigi/session_notification" | "kigi/session/update" => {
|
||||
handle_session_notification(notif, app)
|
||||
}
|
||||
"x.ai/follow_ups" => handle_follow_ups(notif, app),
|
||||
"x.ai/task_backgrounded" => handle_task_backgrounded(notif, app),
|
||||
"x.ai/task_completed" => handle_task_completed(notif, app),
|
||||
"x.ai/models/update" => handle_models_update(notif, app),
|
||||
"x.ai/settings/update" => handle_settings_update(notif, app),
|
||||
"x.ai/sessions/changed" => handle_sessions_changed(notif, app),
|
||||
"x.ai/queue/changed" => handle_queue_changed(notif, app),
|
||||
"kigi/follow_ups" => handle_follow_ups(notif, app),
|
||||
"kigi/task_backgrounded" => handle_task_backgrounded(notif, app),
|
||||
"kigi/task_completed" => handle_task_completed(notif, app),
|
||||
"kigi/models/update" => handle_models_update(notif, app),
|
||||
"kigi/settings/update" => handle_settings_update(notif, app),
|
||||
"kigi/sessions/changed" => handle_sessions_changed(notif, app),
|
||||
"kigi/queue/changed" => handle_queue_changed(notif, app),
|
||||
// TODO(prompt_complete-deprecation): Legacy removal (gated): durable turn_completed is already consumed via finalize_turn_from_terminal; keep & re-point the lost-RPC reconcile to the durable rail before deleting.
|
||||
"x.ai/session/prompt_complete" => handle_prompt_complete(notif, app),
|
||||
"x.ai/session/interjection" => handle_interjection(notif, app),
|
||||
"x.ai/monitor_event" => handle_monitor_event(notif, app),
|
||||
"x.ai/scheduled_task_created" => handle_scheduled_task_created(notif, app),
|
||||
"x.ai/scheduled_task_fired" => handle_scheduled_task_fired(notif, app),
|
||||
"x.ai/scheduled_task_deleted" => handle_scheduled_task_deleted(notif, app),
|
||||
"x.ai/scheduled_task_inject_prompt" => handle_scheduled_task_inject_prompt(notif, app),
|
||||
"x.ai/git_head_changed" => handle_git_head_changed(notif, app),
|
||||
"x.ai/mcp/init_progress" => handle_mcp_init_progress(notif, app),
|
||||
"x.ai/mcp/tools_changed" | "x.ai/mcp_initialized" => handle_mcp_tools_changed(notif, app),
|
||||
"x.ai/mcp/server_status" if push_server_status_enabled() => {
|
||||
"kigi/session/prompt_complete" => handle_prompt_complete(notif, app),
|
||||
"kigi/session/interjection" => handle_interjection(notif, app),
|
||||
"kigi/monitor_event" => handle_monitor_event(notif, app),
|
||||
"kigi/scheduled_task_created" => handle_scheduled_task_created(notif, app),
|
||||
"kigi/scheduled_task_fired" => handle_scheduled_task_fired(notif, app),
|
||||
"kigi/scheduled_task_deleted" => handle_scheduled_task_deleted(notif, app),
|
||||
"kigi/scheduled_task_inject_prompt" => handle_scheduled_task_inject_prompt(notif, app),
|
||||
"kigi/git_head_changed" => handle_git_head_changed(notif, app),
|
||||
"kigi/mcp/init_progress" => handle_mcp_init_progress(notif, app),
|
||||
"kigi/mcp/tools_changed" | "kigi/mcp_initialized" => handle_mcp_tools_changed(notif, app),
|
||||
"kigi/mcp/server_status" if push_server_status_enabled() => {
|
||||
handle_mcp_server_status(notif, app)
|
||||
}
|
||||
"x.ai/mcp/servers_updated" => handle_mcp_servers_updated(notif, app),
|
||||
"kigi/mcp/servers_updated" => handle_mcp_servers_updated(notif, app),
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
/// Handle `x.ai/session/interjection` — the leader broadcasts this
|
||||
/// Handle `kigi/session/interjection` — the leader broadcasts this
|
||||
/// sessionId-bearing notification to every attached client when a mid-turn
|
||||
/// interjection is queued (emitted from the session actor's `Interject`
|
||||
/// command handler). Each client renders the interjection as a scrollback
|
||||
@@ -638,7 +638,7 @@ fn handle_ext_notification(notif: &acp::ExtNotification, app: &mut AppView) -> b
|
||||
/// renders, so legacy shells degrade to "render everywhere" rather than drop.
|
||||
fn handle_interjection(notif: &acp::ExtNotification, app: &mut AppView) -> bool {
|
||||
let Ok(parsed) = serde_json::from_str::<serde_json::Value>(notif.params.get()) else {
|
||||
tracing::warn!("Failed to parse x.ai/session/interjection");
|
||||
tracing::warn!("Failed to parse kigi/session/interjection");
|
||||
return false;
|
||||
};
|
||||
let Some(session_id) = parsed.get("sessionId").and_then(|v| v.as_str()) else {
|
||||
@@ -672,7 +672,7 @@ fn handle_interjection(notif: &acp::ExtNotification, app: &mut AppView) -> bool
|
||||
// Interjecting into a parked wait continues the turn below this block —
|
||||
// the withheld "Worked for …" marker must not fire late beneath it
|
||||
// (shared-queue interjects render only via this broadcast, and the shell
|
||||
// emits the queue-emptying `x.ai/queue/changed` right after it).
|
||||
// emits the queue-emptying `kigi/queue/changed` right after it).
|
||||
agent.suppress_parked_marker_on_interject();
|
||||
is_active
|
||||
}
|
||||
@@ -684,8 +684,8 @@ fn handle_interjection(notif: &acp::ExtNotification, app: &mut AppView) -> bool
|
||||
/// immediately (for unknown methods).
|
||||
fn handle_ext_method(ext: kigi_acp_lib::AcpArgs<acp::ExtRequest>, app: &mut AppView) -> bool {
|
||||
match ext.request.method.as_ref() {
|
||||
"x.ai/ask_user_question" => handle_ask_user_question(ext, app),
|
||||
"x.ai/exit_plan_mode" => handle_exit_plan_mode(ext, app),
|
||||
"kigi/ask_user_question" => handle_ask_user_question(ext, app),
|
||||
"kigi/exit_plan_mode" => handle_exit_plan_mode(ext, app),
|
||||
unknown => {
|
||||
tracing::warn!("Unknown ext_method: {unknown}");
|
||||
ext.response_tx
|
||||
|
||||
@@ -11,7 +11,7 @@ pub(crate) fn is_server_initiated_prompt(prompt_id: &str) -> bool {
|
||||
/// Cron turns are synthetic (so [`is_server_initiated_prompt`] is also true for
|
||||
/// them), but UNLIKE auto-wake / subagent-completion turns they are
|
||||
/// CLIENT-driven via `MvpAgent::prompt()` and therefore DO emit a matching
|
||||
/// `x.ai/session/prompt_complete` turn-end signal. A viewer can thus safely
|
||||
/// `kigi/session/prompt_complete` turn-end signal. A viewer can thus safely
|
||||
/// enter `TurnRunning` for them (the exit exists, so it won't strand) — which is
|
||||
/// what lets the dashboard show a running `/loop` session as Working.
|
||||
pub(crate) fn is_scheduler_fired_prompt(prompt_id: &str) -> bool {
|
||||
@@ -39,7 +39,7 @@ pub(crate) fn is_wake_prompt(prompt_id: &str) -> bool {
|
||||
|
||||
/// Whether a running `prompt_id` is adoptable — i.e. safe to bind as the
|
||||
/// viewer's `current_prompt_id` and show as a live `TurnRunning`. The invariant:
|
||||
/// adoptable iff the turn emits a terminal `x.ai/session/prompt_complete`, the
|
||||
/// adoptable iff the turn emits a terminal `kigi/session/prompt_complete`, the
|
||||
/// only non-interactive way a viewer leaves `TurnRunning`. That holds for
|
||||
/// user-driven turns and `/loop` (`scheduler-fired-…`) fires — both run via
|
||||
/// `MvpAgent::prompt()` — and is false for actor-run synthetic turns
|
||||
|
||||
@@ -19,7 +19,7 @@ pub(crate) struct PendingRunningAdoption {
|
||||
pub turn_ended: bool,
|
||||
}
|
||||
|
||||
/// Wire payload of `x.ai/session/prompt_complete`, emitted by
|
||||
/// Wire payload of `kigi/session/prompt_complete`, emitted by
|
||||
/// `MvpAgent::prompt()` on the shell after every turn.
|
||||
///
|
||||
/// `Serialize` is derived so tests construct payloads through the same type
|
||||
@@ -62,7 +62,7 @@ pub(super) fn handle_queue_changed(notif: &acp::ExtNotification, app: &mut AppVi
|
||||
let Ok(changed) =
|
||||
serde_json::from_str::<crate::app::prompt_queue::QueueChanged>(notif.params.get())
|
||||
else {
|
||||
tracing::warn!("Failed to parse x.ai/queue/changed");
|
||||
tracing::warn!("Failed to parse kigi/queue/changed");
|
||||
return false;
|
||||
};
|
||||
|
||||
@@ -113,7 +113,7 @@ pub(super) fn handle_queue_changed(notif: &acp::ExtNotification, app: &mut AppVi
|
||||
local_current_prompt_id = %local_current_prompt_id,
|
||||
entry_count = changed.entries.len(),
|
||||
entries = ?recv_entry_ids,
|
||||
"received x.ai/queue/changed broadcast",
|
||||
"received kigi/queue/changed broadcast",
|
||||
);
|
||||
|
||||
let rekeyed_echo_ids = app.apply_queue_changed(changed);
|
||||
@@ -384,7 +384,7 @@ pub(super) fn handle_queue_changed(notif: &acp::ExtNotification, app: &mut AppVi
|
||||
/// TODO(prompt_complete-deprecation): Legacy removal (gated): durable turn_completed is already consumed via finalize_turn_from_terminal; keep & re-point the lost-RPC reconcile to the durable rail before deleting.
|
||||
pub(super) fn handle_prompt_complete(notif: &acp::ExtNotification, app: &mut AppView) -> bool {
|
||||
let Ok(payload) = serde_json::from_str::<PromptCompletePayload>(notif.params.get()) else {
|
||||
tracing::warn!("Failed to parse x.ai/session/prompt_complete");
|
||||
tracing::warn!("Failed to parse kigi/session/prompt_complete");
|
||||
return false;
|
||||
};
|
||||
let session_id = payload.session_id.as_str();
|
||||
|
||||
@@ -101,7 +101,7 @@ pub(super) fn advance_reconnect_cursor(agent: &mut AgentView, meta: &mut Notific
|
||||
agent.last_seen_event_id = Some(id);
|
||||
}
|
||||
}
|
||||
/// Handle `x.ai/session_notification` and replay-path `x.ai/session/update`.
|
||||
/// Handle `kigi/session_notification` and replay-path `kigi/session/update`.
|
||||
///
|
||||
/// Routes by `session_id` so events for an inactive agent still mutate that
|
||||
/// agent's state. The redraw decision is gated on whether the matched agent
|
||||
@@ -133,7 +133,7 @@ pub(super) fn handle_session_notification(notif: &acp::ExtNotification, app: &mu
|
||||
tracing::debug!(
|
||||
session_id = session_notif.session_id.0.as_ref(),
|
||||
method = notif.method.as_ref(),
|
||||
"load-race: x.ai/session_notification DROPPED — no agent matches session_id"
|
||||
"load-race: kigi/session_notification DROPPED — no agent matches session_id"
|
||||
);
|
||||
return false;
|
||||
}
|
||||
@@ -159,7 +159,7 @@ pub(super) fn handle_session_notification(notif: &acp::ExtNotification, app: &mu
|
||||
agent,
|
||||
&meta,
|
||||
session_notif.session_id.0.as_ref(),
|
||||
"x.ai/session/update",
|
||||
"kigi/session/update",
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
@@ -174,7 +174,7 @@ pub(super) fn handle_session_notification(notif: &acp::ExtNotification, app: &mu
|
||||
session_id = session_notif.session_id.0.as_ref(),
|
||||
event_seq = meta.event_seq,
|
||||
last_applied = agent.last_applied_xai_event_seq,
|
||||
"x.ai/session update DROPPED by dedup highwater (event_seq <= last_applied)"
|
||||
"kigi/session update DROPPED by dedup highwater (event_seq <= last_applied)"
|
||||
);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
use super::*;
|
||||
use serde::Deserialize;
|
||||
|
||||
/// Handle `x.ai/models/update` — model list changed (etag-triggered refresh).
|
||||
/// Handle `kigi/models/update` — model list changed (etag-triggered refresh).
|
||||
pub(super) fn handle_models_update(notif: &acp::ExtNotification, app: &mut AppView) -> bool {
|
||||
if let Ok(model_state) = serde_json::from_str::<acp::SessionModelState>(notif.params.get()) {
|
||||
use crate::acp::model_state::ModelState;
|
||||
let new_models = ModelState::from(Some(model_state));
|
||||
tracing::info!(
|
||||
count = new_models.available.len(),
|
||||
"models updated via x.ai/models/update"
|
||||
"models updated via kigi/models/update"
|
||||
);
|
||||
|
||||
let shell_fallback_current = new_models.current.clone();
|
||||
@@ -46,15 +46,15 @@ pub(super) fn handle_models_update(notif: &acp::ExtNotification, app: &mut AppVi
|
||||
}
|
||||
true
|
||||
} else {
|
||||
tracing::warn!("Failed to parse x.ai/models/update");
|
||||
tracing::warn!("Failed to parse kigi/models/update");
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
/// Handle `x.ai/settings/update` — remote settings refreshed on `/new`.
|
||||
/// Handle `kigi/settings/update` — remote settings refreshed on `/new`.
|
||||
pub(super) fn handle_settings_update(notif: &acp::ExtNotification, app: &mut AppView) -> bool {
|
||||
let Ok(update) = serde_json::from_str::<PagerSettingsUpdate>(notif.params.get()) else {
|
||||
tracing::warn!("Failed to parse x.ai/settings/update");
|
||||
tracing::warn!("Failed to parse kigi/settings/update");
|
||||
return false;
|
||||
};
|
||||
|
||||
@@ -221,7 +221,7 @@ pub(super) fn handle_settings_update(notif: &acp::ExtNotification, app: &mut App
|
||||
}
|
||||
}
|
||||
|
||||
tracing::info!("settings updated via x.ai/settings/update");
|
||||
tracing::info!("settings updated via kigi/settings/update");
|
||||
true
|
||||
}
|
||||
|
||||
@@ -249,7 +249,7 @@ pub(super) fn apply_soft_default_permission_mode(
|
||||
}
|
||||
|
||||
/// Tell live sessions to leave Auto on the mid-session kill-switch: fire the
|
||||
/// `x.ai/yolo_mode_changed` notification the agent maps to
|
||||
/// `kigi/yolo_mode_changed` notification the agent maps to
|
||||
/// `SetAutoMode { enabled: false }`, fire-and-forget over the shared ACP channel.
|
||||
/// The notification is CLIENT-scoped (the agent applies it to every session of
|
||||
/// the sending client), so one send covers all affected sessions. `yolo_mode` is
|
||||
@@ -264,7 +264,7 @@ pub(super) fn notify_sessions_leave_auto(app: &AppView, session_ids: &[acp::Sess
|
||||
"permission_mode": "ask",
|
||||
});
|
||||
let notification = acp::ExtNotification::new(
|
||||
"x.ai/yolo_mode_changed",
|
||||
"kigi/yolo_mode_changed",
|
||||
serde_json::value::to_raw_value(¶ms)
|
||||
.expect("serialize yolo_mode_changed params")
|
||||
.into(),
|
||||
@@ -277,12 +277,12 @@ pub(super) fn notify_sessions_leave_auto(app: &AppView, session_ids: &[acp::Sess
|
||||
let _ = app.acp_tx.send(args.into());
|
||||
}
|
||||
|
||||
/// Handle `x.ai/sessions/changed` — the leader broadcasts roster
|
||||
/// Handle `kigi/sessions/changed` — the leader broadcasts roster
|
||||
/// upserts/removals to all clients (FleetView dashboard).
|
||||
pub(super) fn handle_sessions_changed(notif: &acp::ExtNotification, app: &mut AppView) -> bool {
|
||||
let Ok(changed) = serde_json::from_str::<crate::app::roster::RosterChanged>(notif.params.get())
|
||||
else {
|
||||
tracing::warn!("Failed to parse x.ai/sessions/changed");
|
||||
tracing::warn!("Failed to parse kigi/sessions/changed");
|
||||
return false;
|
||||
};
|
||||
let mut affected = false;
|
||||
@@ -297,7 +297,7 @@ pub(super) fn handle_sessions_changed(notif: &acp::ExtNotification, app: &mut Ap
|
||||
affected
|
||||
}
|
||||
|
||||
/// Deserialization type for the `x.ai/settings/update` notification payload.
|
||||
/// Deserialization type for the `kigi/settings/update` notification payload.
|
||||
///
|
||||
/// This is intentionally a separate struct from `SettingsUpdateNotification` in
|
||||
/// `kigi-shell/src/agent/mvp_agent.rs`. The shell side derives `Serialize`
|
||||
|
||||
@@ -98,6 +98,6 @@ pub(crate) fn finalize_killed_subagent(
|
||||
let Ok(params) = serde_json::value::to_raw_value(&payload) else {
|
||||
return false;
|
||||
};
|
||||
let notif = acp::ExtNotification::new("x.ai/session/update", params.into());
|
||||
let notif = acp::ExtNotification::new("kigi/session/update", params.into());
|
||||
handle_ext_notification(¬if, app)
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
use super::*;
|
||||
|
||||
/// Regression (resume sync): the on-disk replay stream re-emits persisted
|
||||
/// notifications through the generic `x.ai/session/update` envelope. A
|
||||
/// notifications through the generic `kigi/session/update` envelope. A
|
||||
/// background `monitor`/bash task (`TaskBackgrounded`) must restore into
|
||||
/// `bg_tasks` on a resumed / second terminal — not be dropped by the
|
||||
/// default match arm — so the idle "watching" status line and the Tasks pane
|
||||
@@ -24,7 +24,7 @@
|
||||
description: None,
|
||||
};
|
||||
handle(
|
||||
make_ext_session_notification_with_method("sess-1", "x.ai/session/update", update),
|
||||
make_ext_session_notification_with_method("sess-1", "kigi/session/update", update),
|
||||
&mut app,
|
||||
);
|
||||
|
||||
@@ -51,7 +51,7 @@
|
||||
handle(
|
||||
make_ext_session_notification_with_method(
|
||||
"sess-1",
|
||||
"x.ai/session/update",
|
||||
"kigi/session/update",
|
||||
XaiSessionUpdate::ScheduledTaskCreated {
|
||||
task_id: "loop-1".into(),
|
||||
prompt: "check deploy".into(),
|
||||
@@ -72,7 +72,7 @@
|
||||
handle(
|
||||
make_ext_session_notification_with_method(
|
||||
"sess-1",
|
||||
"x.ai/session/update",
|
||||
"kigi/session/update",
|
||||
XaiSessionUpdate::ScheduledTaskDeleted {
|
||||
task_id: "loop-1".into(),
|
||||
},
|
||||
@@ -177,7 +177,7 @@
|
||||
meta: None,
|
||||
};
|
||||
let raw = serde_json::value::to_raw_value(¬if).unwrap();
|
||||
let notif = acp::ExtNotification::new("x.ai/task_backgrounded", raw.into());
|
||||
let notif = acp::ExtNotification::new("kigi/task_backgrounded", raw.into());
|
||||
assert!(handle_task_backgrounded(¬if, &mut app));
|
||||
|
||||
let agent = app.agents.get(&AgentId(0)).unwrap();
|
||||
@@ -232,7 +232,7 @@
|
||||
meta: None,
|
||||
};
|
||||
let raw = serde_json::value::to_raw_value(¬if).unwrap();
|
||||
let notif = acp::ExtNotification::new("x.ai/task_backgrounded", raw.into());
|
||||
let notif = acp::ExtNotification::new("kigi/task_backgrounded", raw.into());
|
||||
assert!(handle_task_backgrounded(¬if, &mut app));
|
||||
|
||||
let agent = app.agents.get(&AgentId(0)).unwrap();
|
||||
|
||||
@@ -81,10 +81,10 @@
|
||||
let params = serde_json::json!({
|
||||
"response_id": "resp-1",
|
||||
"suggestions": [{ "label": "x" }],
|
||||
"_meta": { "x.ai/replayed": true },
|
||||
"_meta": { "kigi/replayed": true },
|
||||
});
|
||||
let notif = acp::ExtNotification::new(
|
||||
"x.ai/follow_ups",
|
||||
"kigi/follow_ups",
|
||||
serde_json::value::to_raw_value(¶ms).unwrap().into(),
|
||||
);
|
||||
let affected = handle_ext_notification(¬if, &mut app);
|
||||
@@ -103,7 +103,7 @@
|
||||
];
|
||||
for params in bad {
|
||||
let notif = acp::ExtNotification::new(
|
||||
"x.ai/follow_ups",
|
||||
"kigi/follow_ups",
|
||||
serde_json::value::to_raw_value(¶ms).unwrap().into(),
|
||||
);
|
||||
let affected = handle_ext_notification(¬if, &mut app);
|
||||
@@ -214,10 +214,10 @@
|
||||
let params = serde_json::json!({
|
||||
"response_id": "resp-1",
|
||||
"suggestions": [{ "label": "x" }],
|
||||
"_meta": { "x.ai/replayed": false },
|
||||
"_meta": { "kigi/replayed": false },
|
||||
});
|
||||
let notif = acp::ExtNotification::new(
|
||||
"x.ai/follow_ups",
|
||||
"kigi/follow_ups",
|
||||
serde_json::value::to_raw_value(¶ms).unwrap().into(),
|
||||
);
|
||||
assert!(
|
||||
@@ -235,7 +235,7 @@
|
||||
serde_json::json!({ "response_id": "r", "suggestions": [null] }),
|
||||
] {
|
||||
let notif = acp::ExtNotification::new(
|
||||
"x.ai/follow_ups",
|
||||
"kigi/follow_ups",
|
||||
serde_json::value::to_raw_value(&bad).unwrap().into(),
|
||||
);
|
||||
assert!(
|
||||
|
||||
@@ -38,7 +38,7 @@
|
||||
"token_baseline": 100,
|
||||
"finished_subagent_tokens": 99,
|
||||
"live_subagent_tokens": 4_321,
|
||||
"live_tokens_by_model": [["grok-4", 6_000], ["grok-3", 4_000]],
|
||||
"live_tokens_by_model": [["kigi-4", 6_000], ["kigi-3", 4_000]],
|
||||
"live_context_pct": 42,
|
||||
"live_turn_count": 7,
|
||||
"live_tool_call_count": 11,
|
||||
@@ -54,7 +54,7 @@
|
||||
}
|
||||
});
|
||||
let raw = serde_json::value::to_raw_value(&raw_payload).unwrap();
|
||||
let request = acp::ExtNotification::new("x.ai/session_notification", raw.into());
|
||||
let request = acp::ExtNotification::new("kigi/session_notification", raw.into());
|
||||
let (tx, _rx) = tokio::sync::oneshot::channel();
|
||||
let msg = AcpClientMessage::ExtNotification(kigi_acp_lib::AcpArgs {
|
||||
request,
|
||||
@@ -94,7 +94,7 @@
|
||||
assert_eq!(goal.live_subagent_tokens, Some(4_321));
|
||||
assert_eq!(
|
||||
goal.live_tokens_by_model,
|
||||
vec![("grok-4".to_owned(), 6_000), ("grok-3".to_owned(), 4_000)],
|
||||
vec![("kigi-4".to_owned(), 6_000), ("kigi-3".to_owned(), 4_000)],
|
||||
"populated per-model breakdown must round-trip wire->display"
|
||||
);
|
||||
assert_eq!(goal.live_context_pct, Some(42));
|
||||
@@ -147,7 +147,7 @@
|
||||
let (tx, _rx) = tokio::sync::oneshot::channel();
|
||||
handle(
|
||||
AcpClientMessage::ExtNotification(kigi_acp_lib::AcpArgs {
|
||||
request: acp::ExtNotification::new("x.ai/session_notification", raw.into()),
|
||||
request: acp::ExtNotification::new("kigi/session_notification", raw.into()),
|
||||
response_tx: tx,
|
||||
}),
|
||||
app,
|
||||
@@ -372,7 +372,7 @@
|
||||
}
|
||||
});
|
||||
let raw = serde_json::value::to_raw_value(&raw_payload).unwrap();
|
||||
let request = acp::ExtNotification::new("x.ai/session_notification", raw.into());
|
||||
let request = acp::ExtNotification::new("kigi/session_notification", raw.into());
|
||||
let (tx, _rx) = tokio::sync::oneshot::channel();
|
||||
let msg = AcpClientMessage::ExtNotification(kigi_acp_lib::AcpArgs {
|
||||
request,
|
||||
|
||||
@@ -127,7 +127,7 @@
|
||||
}))
|
||||
.unwrap();
|
||||
let msg = AcpClientMessage::ExtMethod(kigi_acp_lib::AcpArgs {
|
||||
request: acp::ExtRequest::new("x.ai/ask_user_question", raw.into()),
|
||||
request: acp::ExtRequest::new("kigi/ask_user_question", raw.into()),
|
||||
response_tx: tx,
|
||||
});
|
||||
|
||||
@@ -168,7 +168,7 @@
|
||||
}))
|
||||
.unwrap();
|
||||
let msg = AcpClientMessage::ExtMethod(kigi_acp_lib::AcpArgs {
|
||||
request: acp::ExtRequest::new("x.ai/ask_user_question", raw.into()),
|
||||
request: acp::ExtRequest::new("kigi/ask_user_question", raw.into()),
|
||||
response_tx: tx,
|
||||
});
|
||||
|
||||
@@ -270,7 +270,7 @@
|
||||
let raw = serde_json::value::to_raw_value(&ext_req).unwrap();
|
||||
handle(
|
||||
AcpClientMessage::ExtMethod(kigi_acp_lib::AcpArgs {
|
||||
request: acp::ExtRequest::new("x.ai/exit_plan_mode", raw.into()),
|
||||
request: acp::ExtRequest::new("kigi/exit_plan_mode", raw.into()),
|
||||
response_tx: tx,
|
||||
}),
|
||||
&mut app,
|
||||
@@ -317,7 +317,7 @@
|
||||
let raw = serde_json::value::to_raw_value(&ext_req).unwrap();
|
||||
handle(
|
||||
AcpClientMessage::ExtMethod(kigi_acp_lib::AcpArgs {
|
||||
request: acp::ExtRequest::new("x.ai/exit_plan_mode", raw.into()),
|
||||
request: acp::ExtRequest::new("kigi/exit_plan_mode", raw.into()),
|
||||
response_tx: tx,
|
||||
}),
|
||||
&mut app,
|
||||
@@ -355,7 +355,7 @@
|
||||
let raw = serde_json::value::to_raw_value(&ext_req).unwrap();
|
||||
handle(
|
||||
AcpClientMessage::ExtMethod(kigi_acp_lib::AcpArgs {
|
||||
request: acp::ExtRequest::new("x.ai/exit_plan_mode", raw.into()),
|
||||
request: acp::ExtRequest::new("kigi/exit_plan_mode", raw.into()),
|
||||
response_tx: tx,
|
||||
}),
|
||||
&mut app,
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
use super::*;
|
||||
|
||||
/// Regression: a shared-queue interjection renders only via the broadcast,
|
||||
/// and the shell emits the queue-emptying `x.ai/queue/changed` right after
|
||||
/// and the shell emits the queue-emptying `kigi/queue/changed` right after
|
||||
/// it — which used to fire the withheld parked marker BELOW the just-
|
||||
/// rendered user message ("Worked for …" under the follow-up, flipped
|
||||
/// transcript order). The broadcast must consume the marker slot instead.
|
||||
@@ -680,7 +680,7 @@
|
||||
);
|
||||
}
|
||||
|
||||
/// `x.ai/task_backgrounded` arriving after the skipped zero-work wait
|
||||
/// `kigi/task_backgrounded` arriving after the skipped zero-work wait
|
||||
/// re-evaluates and restores the park.
|
||||
#[test]
|
||||
fn task_backgrounded_after_zero_work_wait_all_restores_park() {
|
||||
@@ -716,7 +716,7 @@
|
||||
#[test]
|
||||
fn interjection_notification_pushes_block_to_matching_session() {
|
||||
// Multi-client fix: an interjection typed in one pane is broadcast by
|
||||
// the shell as x.ai/session/interjection; EVERY attached pane (incl.
|
||||
// the shell as kigi/session/interjection; EVERY attached pane (incl.
|
||||
// the originator, which no longer pushes a local block) renders it.
|
||||
let mut app = make_app_with_agent("sess-view");
|
||||
let affected =
|
||||
|
||||
@@ -117,7 +117,7 @@
|
||||
|
||||
#[test]
|
||||
fn mcp_initialized_clears_progress() {
|
||||
// x.ai/mcp_initialized must set mcp_init_progress to None.
|
||||
// kigi/mcp_initialized must set mcp_init_progress to None.
|
||||
let mut app = make_app_with_agent("sess-1");
|
||||
let agent = app.agents.get_mut(&AgentId(0)).unwrap();
|
||||
agent.mcp_init_progress = Some(crate::app::agent_view::McpInitProgress {
|
||||
@@ -408,7 +408,7 @@
|
||||
// NB: no `status`.
|
||||
});
|
||||
let raw = serde_json::value::to_raw_value(&payload).unwrap();
|
||||
let notif = acp::ExtNotification::new("x.ai/mcp/server_status", raw.into());
|
||||
let notif = acp::ExtNotification::new("kigi/mcp/server_status", raw.into());
|
||||
let redraw = handle_mcp_server_status(¬if, &mut app);
|
||||
assert!(!redraw, "malformed payload must not request a redraw");
|
||||
|
||||
|
||||
@@ -151,7 +151,7 @@ pub(super) fn interjection_broadcast(
|
||||
text: &str,
|
||||
) -> acp::ExtNotification {
|
||||
acp::ExtNotification::new(
|
||||
"x.ai/session/interjection",
|
||||
"kigi/session/interjection",
|
||||
std::sync::Arc::from(
|
||||
serde_json::value::to_raw_value(
|
||||
&serde_json::json!({ "sessionId" : session_id, "text" : text, }),
|
||||
@@ -233,7 +233,7 @@ pub(super) fn follow_ups_ext(
|
||||
{ "response_id" : response_id, "suggestions" : suggestions, }
|
||||
);
|
||||
acp::ExtNotification::new(
|
||||
"x.ai/follow_ups",
|
||||
"kigi/follow_ups",
|
||||
std::sync::Arc::from(serde_json::value::to_raw_value(¶ms).unwrap()),
|
||||
)
|
||||
}
|
||||
@@ -251,7 +251,7 @@ pub(super) fn follow_ups_ext_with_prompt(
|
||||
suggestions, }
|
||||
);
|
||||
acp::ExtNotification::new(
|
||||
"x.ai/follow_ups",
|
||||
"kigi/follow_ups",
|
||||
std::sync::Arc::from(serde_json::value::to_raw_value(¶ms).unwrap()),
|
||||
)
|
||||
}
|
||||
@@ -263,7 +263,7 @@ pub(super) fn group_tool_verbs_settings_update(
|
||||
None => serde_json::json!({}),
|
||||
};
|
||||
acp::ExtNotification::new(
|
||||
"x.ai/settings/update",
|
||||
"kigi/settings/update",
|
||||
std::sync::Arc::from(serde_json::value::to_raw_value(¶ms).unwrap()),
|
||||
)
|
||||
}
|
||||
@@ -275,7 +275,7 @@ pub(super) fn collapsed_edit_blocks_settings_update(
|
||||
None => serde_json::json!({}),
|
||||
};
|
||||
acp::ExtNotification::new(
|
||||
"x.ai/settings/update",
|
||||
"kigi/settings/update",
|
||||
std::sync::Arc::from(serde_json::value::to_raw_value(¶ms).unwrap()),
|
||||
)
|
||||
}
|
||||
@@ -289,7 +289,7 @@ pub(super) fn subagent_ext_replay(
|
||||
"eventId" : event_id }, }
|
||||
);
|
||||
acp::ExtNotification::new(
|
||||
"x.ai/session/update",
|
||||
"kigi/session/update",
|
||||
std::sync::Arc::from(serde_json::value::to_raw_value(¶ms).unwrap()),
|
||||
)
|
||||
}
|
||||
@@ -315,7 +315,7 @@ pub(super) fn make_exit_plan_ext_with_tool_call_id(
|
||||
),
|
||||
)
|
||||
.unwrap();
|
||||
let request = acp::ExtRequest::new("x.ai/exit_plan_mode", raw.into());
|
||||
let request = acp::ExtRequest::new("kigi/exit_plan_mode", raw.into());
|
||||
let (tx, rx) = tokio::sync::oneshot::channel();
|
||||
(
|
||||
kigi_acp_lib::AcpArgs {
|
||||
@@ -359,11 +359,11 @@ pub(super) fn queue_changed_ext(session_id: &str, ids: &[&str]) -> acp::ExtNotif
|
||||
.collect();
|
||||
let params = serde_json::json!({ "sessionId" : session_id, "entries" : entries });
|
||||
acp::ExtNotification::new(
|
||||
"x.ai/queue/changed",
|
||||
"kigi/queue/changed",
|
||||
std::sync::Arc::from(serde_json::value::to_raw_value(¶ms).unwrap()),
|
||||
)
|
||||
}
|
||||
/// Build a `x.ai/queue/changed` notification carrying `runningPromptId`.
|
||||
/// Build a `kigi/queue/changed` notification carrying `runningPromptId`.
|
||||
pub(super) fn queue_changed_running(
|
||||
session_id: &str,
|
||||
ids: &[&str],
|
||||
@@ -386,7 +386,7 @@ pub(super) fn queue_changed_running(
|
||||
params["runningPromptId"] = serde_json::Value::String(r.to_string());
|
||||
}
|
||||
acp::ExtNotification::new(
|
||||
"x.ai/queue/changed",
|
||||
"kigi/queue/changed",
|
||||
std::sync::Arc::from(serde_json::value::to_raw_value(¶ms).unwrap()),
|
||||
)
|
||||
}
|
||||
@@ -469,7 +469,7 @@ pub(super) fn tool_call_block_count(agent: &AgentView) -> usize {
|
||||
pub(super) fn make_inject_notif(payload: &serde_json::Value) -> acp::ExtNotification {
|
||||
let raw = serde_json::value::to_raw_value(payload).unwrap();
|
||||
acp::ExtNotification::new(
|
||||
"x.ai/scheduled_task_inject_prompt",
|
||||
"kigi/scheduled_task_inject_prompt",
|
||||
std::sync::Arc::from(raw),
|
||||
)
|
||||
}
|
||||
@@ -491,7 +491,7 @@ pub(super) fn make_fired_notif(
|
||||
meta: None,
|
||||
};
|
||||
let raw = serde_json::value::to_raw_value(¬if).unwrap();
|
||||
acp::ExtNotification::new("x.ai/scheduled_task_fired", std::sync::Arc::from(raw))
|
||||
acp::ExtNotification::new("kigi/scheduled_task_fired", std::sync::Arc::from(raw))
|
||||
}
|
||||
/// Set up an app with two agents; the active view points to agent 1, but
|
||||
/// agent 0 owns the scheduled task. Handlers that gate on `active_view`
|
||||
@@ -531,7 +531,7 @@ pub(super) fn make_created_ext_notif(
|
||||
meta: None,
|
||||
};
|
||||
let raw = serde_json::value::to_raw_value(¬if).unwrap();
|
||||
acp::ExtNotification::new("x.ai/scheduled_task_created", std::sync::Arc::from(raw))
|
||||
acp::ExtNotification::new("kigi/scheduled_task_created", std::sync::Arc::from(raw))
|
||||
}
|
||||
pub(super) fn make_deleted_ext_notif(
|
||||
session_id: &str,
|
||||
@@ -545,7 +545,7 @@ pub(super) fn make_deleted_ext_notif(
|
||||
meta: None,
|
||||
};
|
||||
let raw = serde_json::value::to_raw_value(¬if).unwrap();
|
||||
acp::ExtNotification::new("x.ai/scheduled_task_deleted", std::sync::Arc::from(raw))
|
||||
acp::ExtNotification::new("kigi/scheduled_task_deleted", std::sync::Arc::from(raw))
|
||||
}
|
||||
pub(super) fn make_token_notification_message(
|
||||
session_id: &str,
|
||||
@@ -697,7 +697,7 @@ pub(super) fn xai_model_switch_notif(
|
||||
meta: Some(serde_json::json!({ "eventId" : event_id })),
|
||||
};
|
||||
acp::ExtNotification::new(
|
||||
"x.ai/session/update",
|
||||
"kigi/session/update",
|
||||
std::sync::Arc::from(serde_json::value::to_raw_value(&payload).unwrap()),
|
||||
)
|
||||
}
|
||||
@@ -711,7 +711,7 @@ pub(super) fn xai_unhandled_notif(
|
||||
meta: Some(serde_json::json!({ "eventId" : event_id })),
|
||||
};
|
||||
acp::ExtNotification::new(
|
||||
"x.ai/session/update",
|
||||
"kigi/session/update",
|
||||
std::sync::Arc::from(serde_json::value::to_raw_value(&payload).unwrap()),
|
||||
)
|
||||
}
|
||||
@@ -741,19 +741,19 @@ pub(super) fn make_token_notification_with_event(
|
||||
response_tx: tx,
|
||||
})
|
||||
}
|
||||
/// Build an `x.ai/session/prompt_complete` ext-notification for `session_id`.
|
||||
/// Build an `kigi/session/prompt_complete` ext-notification for `session_id`.
|
||||
pub(super) fn prompt_complete_ext(session_id: &str) -> acp::ExtNotification {
|
||||
let raw = serde_json::value::to_raw_value(
|
||||
&serde_json::json!({ "sessionId" : session_id, "stopReason" : "end_turn", }),
|
||||
)
|
||||
.unwrap();
|
||||
acp::ExtNotification::new("x.ai/session/prompt_complete", std::sync::Arc::from(raw))
|
||||
acp::ExtNotification::new("kigi/session/prompt_complete", std::sync::Arc::from(raw))
|
||||
}
|
||||
/// Insert a fresh agent at `id` with an optional pre-assigned session id.
|
||||
pub(super) fn insert_agent(app: &mut AppView, id: AgentId, session_id: Option<&str>) {
|
||||
app.agents.insert(id, make_agent(session_id));
|
||||
}
|
||||
/// Build an `x.ai/session/prompt_complete` ext-notification with an explicit
|
||||
/// Build an `kigi/session/prompt_complete` ext-notification with an explicit
|
||||
/// `stopReason` and optional `agentResult`.
|
||||
pub(super) fn prompt_complete_ext_with_reason(
|
||||
session_id: &str,
|
||||
@@ -767,9 +767,9 @@ pub(super) fn prompt_complete_ext_with_reason(
|
||||
payload["agentResult"] = serde_json::json!(r);
|
||||
}
|
||||
let raw = serde_json::value::to_raw_value(&payload).unwrap();
|
||||
acp::ExtNotification::new("x.ai/session/prompt_complete", std::sync::Arc::from(raw))
|
||||
acp::ExtNotification::new("kigi/session/prompt_complete", std::sync::Arc::from(raw))
|
||||
}
|
||||
/// Build an `x.ai/session/prompt_complete` ext-notification carrying a
|
||||
/// Build an `kigi/session/prompt_complete` ext-notification carrying a
|
||||
/// `promptId` (shells with the lost-response fix). Built through the
|
||||
/// typed [`PromptCompletePayload`] so the test wire shape can never
|
||||
/// drift from what `handle_prompt_complete` parses.
|
||||
@@ -789,7 +789,7 @@ pub(super) fn prompt_complete_ext_with_prompt_id(
|
||||
},
|
||||
)
|
||||
.unwrap();
|
||||
acp::ExtNotification::new("x.ai/session/prompt_complete", std::sync::Arc::from(raw))
|
||||
acp::ExtNotification::new("kigi/session/prompt_complete", std::sync::Arc::from(raw))
|
||||
}
|
||||
/// Build a live `AgentMessageChunk` whose meta carries `promptId` plus a
|
||||
/// `turnStartMs` `start_ms_ago` milliseconds in the past — drives the viewer
|
||||
@@ -822,7 +822,7 @@ pub(super) fn make_viewer_chunk_with_turn_start(
|
||||
response_tx: tx,
|
||||
})
|
||||
}
|
||||
/// Build a durable `TurnCompleted` update on the `x.ai/session/update` rail,
|
||||
/// Build a durable `TurnCompleted` update on the `kigi/session/update` rail,
|
||||
/// optionally stamped `isReplay`. Built through the typed `SessionNotification`
|
||||
/// so the wire shape can't drift from what the dispatch parses.
|
||||
pub(super) fn xai_turn_completed_notif(
|
||||
@@ -842,7 +842,7 @@ pub(super) fn xai_turn_completed_notif(
|
||||
meta: Some(serde_json::json!({ "isReplay" : is_replay })),
|
||||
};
|
||||
acp::ExtNotification::new(
|
||||
"x.ai/session/update",
|
||||
"kigi/session/update",
|
||||
std::sync::Arc::from(serde_json::value::to_raw_value(&payload).unwrap()),
|
||||
)
|
||||
}
|
||||
@@ -868,7 +868,7 @@ pub(super) fn xai_wake_turn_completed_notif(
|
||||
meta: Some(meta),
|
||||
};
|
||||
acp::ExtNotification::new(
|
||||
"x.ai/session/update",
|
||||
"kigi/session/update",
|
||||
std::sync::Arc::from(serde_json::value::to_raw_value(&payload).unwrap()),
|
||||
)
|
||||
}
|
||||
@@ -885,7 +885,7 @@ pub(super) fn last_marker_block(
|
||||
.expect("a turn-end marker must exist")
|
||||
}
|
||||
/// Build a `HookExecution` update (one successful run) on the
|
||||
/// `x.ai/session/update` rail, optionally stamped `isReplay`.
|
||||
/// `kigi/session/update` rail, optionally stamped `isReplay`.
|
||||
/// `prompt_id == None` models pre-attribution shells.
|
||||
pub(super) fn xai_hook_execution_notif_for_prompt(
|
||||
session_id: &str,
|
||||
@@ -908,7 +908,7 @@ pub(super) fn xai_hook_execution_notif_for_prompt(
|
||||
meta: Some(serde_json::json!({ "isReplay" : is_replay })),
|
||||
};
|
||||
acp::ExtNotification::new(
|
||||
"x.ai/session/update",
|
||||
"kigi/session/update",
|
||||
serde_json::value::to_raw_value(&payload).unwrap().into(),
|
||||
)
|
||||
}
|
||||
@@ -970,11 +970,11 @@ pub(super) fn seed_two_bg_tasks_and_announce(app: &mut AppView, session_id: &str
|
||||
);
|
||||
app.agents.get_mut(&AgentId(0)).unwrap().end_work_announced = true;
|
||||
}
|
||||
/// Build an `x.ai/session/interjection` ext-notification (no id).
|
||||
/// Build an `kigi/session/interjection` ext-notification (no id).
|
||||
pub(super) fn interjection_ext(session_id: &str, text: &str) -> acp::ExtNotification {
|
||||
interjection_ext_with_id(session_id, text, None)
|
||||
}
|
||||
/// Build an `x.ai/session/interjection` ext-notification with an optional
|
||||
/// Build an `kigi/session/interjection` ext-notification with an optional
|
||||
/// `interjectionId` (the originator-dedup key).
|
||||
pub(super) fn interjection_ext_with_id(
|
||||
session_id: &str,
|
||||
@@ -986,7 +986,7 @@ pub(super) fn interjection_ext_with_id(
|
||||
payload["interjectionId"] = serde_json::json!(id);
|
||||
}
|
||||
let raw = serde_json::value::to_raw_value(&payload).unwrap();
|
||||
acp::ExtNotification::new("x.ai/session/interjection", std::sync::Arc::from(raw))
|
||||
acp::ExtNotification::new("kigi/session/interjection", std::sync::Arc::from(raw))
|
||||
}
|
||||
/// Text of the most recent user prompt block in scrollback, if any.
|
||||
/// Interjections render as standard user prompt blocks.
|
||||
@@ -1090,14 +1090,14 @@ pub(super) fn make_bash_stdout_message(
|
||||
response_tx: tx,
|
||||
})
|
||||
}
|
||||
/// Build an `ExtNotification` envelope for `x.ai/session_notification`.
|
||||
/// Build an `ExtNotification` envelope for `kigi/session_notification`.
|
||||
pub(super) fn make_ext_session_notification(
|
||||
session_id: &str,
|
||||
update: XaiSessionUpdate,
|
||||
) -> AcpClientMessage {
|
||||
make_ext_session_notification_with_method(
|
||||
session_id,
|
||||
"x.ai/session_notification",
|
||||
"kigi/session_notification",
|
||||
update,
|
||||
)
|
||||
}
|
||||
@@ -1279,7 +1279,7 @@ pub(super) fn replay_disk_test_home() -> &'static std::path::Path {
|
||||
})
|
||||
.path()
|
||||
}
|
||||
/// Runs `f` with a thread-local grok home override so disk replay tests do not
|
||||
/// Runs `f` with a thread-local kigi home override so disk replay tests do not
|
||||
/// depend on process-wide `kigi_home()` cache order when the full suite runs.
|
||||
pub(super) fn with_replay_disk_home<R>(f: impl FnOnce(&std::path::Path) -> R) -> R {
|
||||
let home = replay_disk_test_home();
|
||||
@@ -1389,7 +1389,7 @@ pub(super) fn spawn_subagent_with_optional_updates(
|
||||
let _ = handle(
|
||||
make_ext_session_notification_with_method(
|
||||
"sess-parent",
|
||||
"x.ai/session/update",
|
||||
"kigi/session/update",
|
||||
test_subagent_spawned("sess-parent", child_sid),
|
||||
),
|
||||
app,
|
||||
@@ -1421,7 +1421,7 @@ pub(super) fn dispatch_goal_update(
|
||||
let (tx, _rx) = tokio::sync::oneshot::channel();
|
||||
handle(
|
||||
AcpClientMessage::ExtNotification(kigi_acp_lib::AcpArgs {
|
||||
request: acp::ExtNotification::new("x.ai/session_notification", raw.into()),
|
||||
request: acp::ExtNotification::new("kigi/session_notification", raw.into()),
|
||||
response_tx: tx,
|
||||
}),
|
||||
app,
|
||||
@@ -1464,7 +1464,7 @@ pub(super) fn make_permission_message(
|
||||
});
|
||||
(msg, rx)
|
||||
}
|
||||
/// Build an `x.ai/session_notification` carrying
|
||||
/// Build an `kigi/session_notification` carrying
|
||||
/// `InteractionResolved{tool_call_id}` (the first-answer-wins broadcast that
|
||||
/// tells every other pane to retract its shared interaction modal).
|
||||
pub(super) fn interaction_resolved_ext(
|
||||
@@ -1479,7 +1479,7 @@ pub(super) fn interaction_resolved_ext(
|
||||
meta: None,
|
||||
};
|
||||
let raw = serde_json::value::to_raw_value(¬if).unwrap();
|
||||
acp::ExtNotification::new("x.ai/session_notification", std::sync::Arc::from(raw))
|
||||
acp::ExtNotification::new("kigi/session_notification", std::sync::Arc::from(raw))
|
||||
}
|
||||
pub(super) fn make_git_head_changed_notif(
|
||||
session_id: &str,
|
||||
@@ -1494,7 +1494,7 @@ pub(super) fn make_git_head_changed_notif(
|
||||
main_repo: main_repo.map(str::to_string),
|
||||
};
|
||||
let raw = serde_json::value::to_raw_value(&payload).unwrap();
|
||||
acp::ExtNotification::new("x.ai/git_head_changed", std::sync::Arc::from(raw))
|
||||
acp::ExtNotification::new("kigi/git_head_changed", std::sync::Arc::from(raw))
|
||||
}
|
||||
pub(super) fn make_task_backgrounded_notif(
|
||||
session_id: &str,
|
||||
@@ -1516,7 +1516,7 @@ pub(super) fn make_task_backgrounded_notif(
|
||||
meta: None,
|
||||
};
|
||||
let raw = serde_json::value::to_raw_value(¬if).unwrap();
|
||||
acp::ExtNotification::new("x.ai/task_backgrounded", std::sync::Arc::from(raw))
|
||||
acp::ExtNotification::new("kigi/task_backgrounded", std::sync::Arc::from(raw))
|
||||
}
|
||||
/// Like [`make_task_backgrounded_notif`] but stamped `_meta.isReplay:
|
||||
/// true` via the typed [`ReplayMetaStamp`](crate::acp::meta::ReplayMetaStamp),
|
||||
@@ -1541,7 +1541,7 @@ pub(super) fn make_replayed_task_backgrounded_notif(
|
||||
meta: Some(crate::acp::meta::ReplayMetaStamp::replayed()),
|
||||
};
|
||||
let raw = serde_json::value::to_raw_value(¬if).unwrap();
|
||||
acp::ExtNotification::new("x.ai/session/update", std::sync::Arc::from(raw))
|
||||
acp::ExtNotification::new("kigi/session/update", std::sync::Arc::from(raw))
|
||||
}
|
||||
/// Register a pending Execute tool call in the tracker and send an InProgress
|
||||
/// update to create the scrollback entry. Returns the agent for further use.
|
||||
@@ -1670,7 +1670,7 @@ pub(super) fn task_completed_notif(
|
||||
meta: None,
|
||||
};
|
||||
let raw = serde_json::value::to_raw_value(¬if).unwrap();
|
||||
acp::ExtNotification::new("x.ai/task_completed", std::sync::Arc::from(raw))
|
||||
acp::ExtNotification::new("kigi/task_completed", std::sync::Arc::from(raw))
|
||||
}
|
||||
pub(super) fn make_monitor_event_notif(
|
||||
session_id: &str,
|
||||
@@ -1687,7 +1687,7 @@ pub(super) fn make_monitor_event_notif(
|
||||
meta: None,
|
||||
};
|
||||
let raw = serde_json::value::to_raw_value(¬if).unwrap();
|
||||
acp::ExtNotification::new("x.ai/monitor_event", std::sync::Arc::from(raw))
|
||||
acp::ExtNotification::new("kigi/monitor_event", std::sync::Arc::from(raw))
|
||||
}
|
||||
pub(super) fn make_model_info(id: &str) -> acp::ModelInfo {
|
||||
acp::ModelInfo::new(acp::ModelId::new(std::sync::Arc::from(id)), id.to_string())
|
||||
@@ -1705,9 +1705,9 @@ pub(super) fn make_models_update_notif(
|
||||
models,
|
||||
);
|
||||
let raw = serde_json::value::to_raw_value(&state).unwrap();
|
||||
acp::ExtNotification::new("x.ai/models/update", std::sync::Arc::from(raw))
|
||||
acp::ExtNotification::new("kigi/models/update", std::sync::Arc::from(raw))
|
||||
}
|
||||
/// `x.ai/models/update` carrying a single reasoning-capable model whose
|
||||
/// `kigi/models/update` carrying a single reasoning-capable model whose
|
||||
/// catalog-default effort is `default_effort` (what the broadcast reports
|
||||
/// for every client — never the per-session selection).
|
||||
pub(super) fn make_reasoning_models_update_notif(
|
||||
@@ -1725,7 +1725,7 @@ pub(super) fn make_reasoning_models_update_notif(
|
||||
vec![info],
|
||||
);
|
||||
let raw = serde_json::value::to_raw_value(&state).unwrap();
|
||||
acp::ExtNotification::new("x.ai/models/update", std::sync::Arc::from(raw))
|
||||
acp::ExtNotification::new("kigi/models/update", std::sync::Arc::from(raw))
|
||||
}
|
||||
/// Seed a session's model catalog with the given ids and mark
|
||||
/// `current_model_id` as the active one (must be in the list). Used by
|
||||
@@ -1754,7 +1754,7 @@ pub(super) fn model_changed_ext(
|
||||
meta: None,
|
||||
};
|
||||
let raw = serde_json::value::to_raw_value(&payload).unwrap();
|
||||
acp::ExtNotification::new("x.ai/session_notification", std::sync::Arc::from(raw))
|
||||
acp::ExtNotification::new("kigi/session_notification", std::sync::Arc::from(raw))
|
||||
}
|
||||
pub(super) fn model_changed_ext_with_event(
|
||||
session_id: &str,
|
||||
@@ -1770,7 +1770,7 @@ pub(super) fn model_changed_ext_with_event(
|
||||
meta: Some(serde_json::json!({ "eventId" : event_id })),
|
||||
};
|
||||
let raw = serde_json::value::to_raw_value(&payload).unwrap();
|
||||
acp::ExtNotification::new("x.ai/session_notification", std::sync::Arc::from(raw))
|
||||
acp::ExtNotification::new("kigi/session_notification", std::sync::Arc::from(raw))
|
||||
}
|
||||
pub(super) fn make_tool_call_update(title: &str) -> acp::SessionUpdate {
|
||||
acp::SessionUpdate::ToolCallUpdate(
|
||||
@@ -1796,7 +1796,7 @@ pub(super) fn make_current_mode_update(mode_id: &str) -> acp::SessionUpdate {
|
||||
acp::CurrentModeUpdate::new(acp::SessionModeId::new(mode_id)),
|
||||
)
|
||||
}
|
||||
/// Helper: build an `x.ai/mcp/init_progress` notification.
|
||||
/// Helper: build an `kigi/mcp/init_progress` notification.
|
||||
pub(super) fn make_mcp_init_progress_notif(
|
||||
total: u32,
|
||||
connected: u32,
|
||||
@@ -1805,7 +1805,7 @@ pub(super) fn make_mcp_init_progress_notif(
|
||||
&serde_json::json!({ "total" : total, "connected" : connected, }),
|
||||
)
|
||||
.unwrap();
|
||||
acp::ExtNotification::new("x.ai/mcp/init_progress", std::sync::Arc::from(raw))
|
||||
acp::ExtNotification::new("kigi/mcp/init_progress", std::sync::Arc::from(raw))
|
||||
}
|
||||
pub(super) fn make_mcps_modal_with_servers(
|
||||
servers: Vec<crate::views::mcps_modal::McpServerInfo>,
|
||||
@@ -1854,7 +1854,7 @@ pub(super) fn make_server_status_notif(
|
||||
tools,
|
||||
};
|
||||
let raw = serde_json::value::to_raw_value(&payload).unwrap();
|
||||
acp::ExtNotification::new("x.ai/mcp/server_status", std::sync::Arc::from(raw))
|
||||
acp::ExtNotification::new("kigi/mcp/server_status", std::sync::Arc::from(raw))
|
||||
}
|
||||
/// `mcp/servers_updated` real wire shape — `{ mcpServers: [...] }`
|
||||
/// with NO `sessionId`. Regression guard: anything that tries to
|
||||
@@ -1863,7 +1863,7 @@ pub(super) fn make_server_status_notif(
|
||||
pub(super) fn make_servers_updated_notif() -> acp::ExtNotification {
|
||||
let payload = serde_json::json!({ "mcpServers" : [] });
|
||||
let raw = serde_json::value::to_raw_value(&payload).unwrap();
|
||||
acp::ExtNotification::new("x.ai/mcp/servers_updated", std::sync::Arc::from(raw))
|
||||
acp::ExtNotification::new("kigi/mcp/servers_updated", std::sync::Arc::from(raw))
|
||||
}
|
||||
/// Real post-handshake / auth-recovery wire shape:
|
||||
/// `McpToolsChanged { sessionId, serverName, tools }`.
|
||||
@@ -1872,19 +1872,19 @@ pub(super) fn make_tools_changed_notif_post_h2(
|
||||
) -> acp::ExtNotification {
|
||||
let payload = kigi_shell::extensions::mcp::McpToolsChanged {
|
||||
session_id: session_id.to_string(),
|
||||
server_name: "grok_com_linear".to_string(),
|
||||
server_name: "kigi_com_linear".to_string(),
|
||||
tools: Vec::new(),
|
||||
};
|
||||
let raw = serde_json::value::to_raw_value(&payload).unwrap();
|
||||
acp::ExtNotification::new("x.ai/mcp/tools_changed", std::sync::Arc::from(raw))
|
||||
acp::ExtNotification::new("kigi/mcp/tools_changed", std::sync::Arc::from(raw))
|
||||
}
|
||||
/// Legacy / forward-compat wire shape: older shells emit
|
||||
/// `{ serverName, tools }` with NO sessionId. The pager must fall
|
||||
/// back to active_view for this shape.
|
||||
pub(super) fn make_tools_changed_notif_pre_h2() -> acp::ExtNotification {
|
||||
let payload = serde_json::json!({ "serverName" : "grok_com_linear", "tools" : [] });
|
||||
let payload = serde_json::json!({ "serverName" : "kigi_com_linear", "tools" : [] });
|
||||
let raw = serde_json::value::to_raw_value(&payload).unwrap();
|
||||
acp::ExtNotification::new("x.ai/mcp/tools_changed", std::sync::Arc::from(raw))
|
||||
acp::ExtNotification::new("kigi/mcp/tools_changed", std::sync::Arc::from(raw))
|
||||
}
|
||||
/// Real `mcp_initialized` wire shape:
|
||||
/// `{ sessionId, mcpToolCount, elapsedMs }`.
|
||||
@@ -1893,7 +1893,7 @@ pub(super) fn make_mcp_initialized_notif(session_id: &str) -> acp::ExtNotificati
|
||||
{ "sessionId" : session_id, "mcpToolCount" : 12_u64, "elapsedMs" : 250_u64, }
|
||||
);
|
||||
let raw = serde_json::value::to_raw_value(&payload).unwrap();
|
||||
acp::ExtNotification::new("x.ai/mcp_initialized", std::sync::Arc::from(raw))
|
||||
acp::ExtNotification::new("kigi/mcp_initialized", std::sync::Arc::from(raw))
|
||||
}
|
||||
/// Helper: `init_progress` notification carrying an explicit sessionId.
|
||||
pub(super) fn make_mcp_init_progress_notif_for(
|
||||
@@ -1907,7 +1907,7 @@ pub(super) fn make_mcp_init_progress_notif_for(
|
||||
),
|
||||
)
|
||||
.unwrap();
|
||||
acp::ExtNotification::new("x.ai/mcp/init_progress", std::sync::Arc::from(raw))
|
||||
acp::ExtNotification::new("kigi/mcp/init_progress", std::sync::Arc::from(raw))
|
||||
}
|
||||
/// Helper: `mcp_initialized` notification for a specific sessionId.
|
||||
pub(super) fn make_mcp_initialized_notif_for(session_id: &str) -> acp::ExtNotification {
|
||||
@@ -1917,7 +1917,7 @@ pub(super) fn make_mcp_initialized_notif_for(session_id: &str) -> acp::ExtNotifi
|
||||
),
|
||||
)
|
||||
.unwrap();
|
||||
acp::ExtNotification::new("x.ai/mcp_initialized", std::sync::Arc::from(raw))
|
||||
acp::ExtNotification::new("kigi/mcp_initialized", std::sync::Arc::from(raw))
|
||||
}
|
||||
mod permissions;
|
||||
mod session_events;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#![cfg_attr(rustfmt, rustfmt::skip)]
|
||||
use super::*;
|
||||
|
||||
/// Regression: a machine-wide `x.ai/models/update` broadcast
|
||||
/// Regression: a machine-wide `kigi/models/update` broadcast
|
||||
/// carries each model's static catalog-default effort (`high`), not the
|
||||
/// session's chosen `xhigh`, and must not clobber the per-session choice.
|
||||
#[test]
|
||||
@@ -44,20 +44,20 @@
|
||||
let mut app = make_app_with_agent("sess-1");
|
||||
|
||||
let agent = app.agents.get_mut(&AgentId(0)).unwrap();
|
||||
let id_3 = acp::ModelId::new(std::sync::Arc::from("grok-3"));
|
||||
let id_3 = acp::ModelId::new(std::sync::Arc::from("kigi-3"));
|
||||
agent
|
||||
.session
|
||||
.models
|
||||
.available
|
||||
.insert(id_3.clone(), make_model_info("grok-3"));
|
||||
.insert(id_3.clone(), make_model_info("kigi-3"));
|
||||
agent.session.models.current = Some(id_3);
|
||||
|
||||
let notif = make_models_update_notif("grok-4", &["grok-3", "grok-4"]);
|
||||
let notif = make_models_update_notif("kigi-4", &["kigi-3", "kigi-4"]);
|
||||
handle_models_update(¬if, &mut app);
|
||||
|
||||
assert_eq!(
|
||||
app.models.current.as_ref().map(|id| id.0.as_ref()),
|
||||
Some("grok-3"),
|
||||
Some("kigi-3"),
|
||||
"app.models.current must preserve active agent's model, not remote settings default"
|
||||
);
|
||||
|
||||
@@ -69,7 +69,7 @@
|
||||
.current
|
||||
.as_ref()
|
||||
.map(|id| id.0.as_ref()),
|
||||
Some("grok-3"),
|
||||
Some("kigi-3"),
|
||||
"agent's per-session model must be preserved"
|
||||
);
|
||||
}
|
||||
@@ -79,21 +79,21 @@
|
||||
let mut app = make_app_with_agent("sess-1");
|
||||
|
||||
let agent = app.agents.get_mut(&AgentId(0)).unwrap();
|
||||
let id_3 = acp::ModelId::new(std::sync::Arc::from("grok-3"));
|
||||
let id_3 = acp::ModelId::new(std::sync::Arc::from("kigi-3"));
|
||||
agent
|
||||
.session
|
||||
.models
|
||||
.available
|
||||
.insert(id_3.clone(), make_model_info("grok-3"));
|
||||
.insert(id_3.clone(), make_model_info("kigi-3"));
|
||||
agent.session.models.current = Some(id_3);
|
||||
|
||||
// grok-3 removed from catalog.
|
||||
let notif = make_models_update_notif("grok-4.3", &["grok-4.3", "grok-4.5"]);
|
||||
// kigi-3 removed from catalog.
|
||||
let notif = make_models_update_notif("kigi-4.3", &["kigi-4.3", "kigi-4.5"]);
|
||||
handle_models_update(¬if, &mut app);
|
||||
|
||||
assert_eq!(
|
||||
app.models.current.as_ref().map(|id| id.0.as_ref()),
|
||||
Some("grok-4.3"),
|
||||
Some("kigi-4.3"),
|
||||
"app.models.current must use shell default when agent model removed"
|
||||
);
|
||||
|
||||
@@ -105,7 +105,7 @@
|
||||
.current
|
||||
.as_ref()
|
||||
.map(|id| id.0.as_ref()),
|
||||
Some("grok-4.3"),
|
||||
Some("kigi-4.3"),
|
||||
"agent must fall back to shell default when its model is removed"
|
||||
);
|
||||
}
|
||||
@@ -115,12 +115,12 @@
|
||||
let (tx, _rx) = tokio::sync::mpsc::unbounded_channel();
|
||||
let mut app = AppView::new(tx, ModelState::default(), Vec::new());
|
||||
|
||||
let notif = make_models_update_notif("grok-4", &["grok-3", "grok-4"]);
|
||||
let notif = make_models_update_notif("kigi-4", &["kigi-3", "kigi-4"]);
|
||||
handle_models_update(¬if, &mut app);
|
||||
|
||||
assert_eq!(
|
||||
app.models.current.as_ref().map(|id| id.0.as_ref()),
|
||||
Some("grok-4"),
|
||||
Some("kigi-4"),
|
||||
"without an active agent, shell default must be used"
|
||||
);
|
||||
}
|
||||
@@ -130,21 +130,21 @@
|
||||
let mut app = make_app_with_agent("sess-1");
|
||||
|
||||
let agent = app.agents.get_mut(&AgentId(0)).unwrap();
|
||||
let id_4 = acp::ModelId::new(std::sync::Arc::from("grok-4"));
|
||||
let id_4 = acp::ModelId::new(std::sync::Arc::from("kigi-4"));
|
||||
agent
|
||||
.session
|
||||
.models
|
||||
.available
|
||||
.insert(id_4.clone(), make_model_info("grok-4"));
|
||||
.insert(id_4.clone(), make_model_info("kigi-4"));
|
||||
agent.session.models.current = Some(id_4);
|
||||
|
||||
let notif = make_models_update_notif("grok-4", &["grok-3", "grok-4"]);
|
||||
let notif = make_models_update_notif("kigi-4", &["kigi-3", "kigi-4"]);
|
||||
handle_models_update(¬if, &mut app);
|
||||
|
||||
assert_eq!(
|
||||
app.models.current.as_ref().map(|id| id.0.as_ref()),
|
||||
Some("grok-4"),
|
||||
"app.models.current must be grok-4 when agent and shell agree"
|
||||
Some("kigi-4"),
|
||||
"app.models.current must be kigi-4 when agent and shell agree"
|
||||
);
|
||||
let agent = app.agents.get(&AgentId(0)).unwrap();
|
||||
assert_eq!(
|
||||
@@ -154,8 +154,8 @@
|
||||
.current
|
||||
.as_ref()
|
||||
.map(|id| id.0.as_ref()),
|
||||
Some("grok-4"),
|
||||
"agent model must remain grok-4"
|
||||
Some("kigi-4"),
|
||||
"agent model must remain kigi-4"
|
||||
);
|
||||
}
|
||||
|
||||
@@ -166,33 +166,33 @@
|
||||
|
||||
{
|
||||
let agent_a = app.agents.get_mut(&AgentId(0)).unwrap();
|
||||
let id_3 = acp::ModelId::new(std::sync::Arc::from("grok-3"));
|
||||
let id_3 = acp::ModelId::new(std::sync::Arc::from("kigi-3"));
|
||||
agent_a
|
||||
.session
|
||||
.models
|
||||
.available
|
||||
.insert(id_3.clone(), make_model_info("grok-3"));
|
||||
.insert(id_3.clone(), make_model_info("kigi-3"));
|
||||
agent_a.session.models.current = Some(id_3);
|
||||
}
|
||||
|
||||
{
|
||||
let agent_b = app.agents.get_mut(&AgentId(1)).unwrap();
|
||||
let id_5 = acp::ModelId::new(std::sync::Arc::from("grok-4.5"));
|
||||
let id_5 = acp::ModelId::new(std::sync::Arc::from("kigi-4.5"));
|
||||
agent_b
|
||||
.session
|
||||
.models
|
||||
.available
|
||||
.insert(id_5.clone(), make_model_info("grok-4.5"));
|
||||
.insert(id_5.clone(), make_model_info("kigi-4.5"));
|
||||
agent_b.session.models.current = Some(id_5);
|
||||
}
|
||||
|
||||
// grok-5 removed from catalog.
|
||||
let notif = make_models_update_notif("grok-4", &["grok-3", "grok-4"]);
|
||||
// kigi-5 removed from catalog.
|
||||
let notif = make_models_update_notif("kigi-4", &["kigi-3", "kigi-4"]);
|
||||
handle_models_update(¬if, &mut app);
|
||||
|
||||
assert_eq!(
|
||||
app.models.current.as_ref().map(|id| id.0.as_ref()),
|
||||
Some("grok-3"),
|
||||
Some("kigi-3"),
|
||||
);
|
||||
let agent_a = app.agents.get(&AgentId(0)).unwrap();
|
||||
assert_eq!(
|
||||
@@ -202,11 +202,11 @@
|
||||
.current
|
||||
.as_ref()
|
||||
.map(|id| id.0.as_ref()),
|
||||
Some("grok-3"),
|
||||
Some("kigi-3"),
|
||||
"agent A's model must be preserved"
|
||||
);
|
||||
|
||||
// B's grok-5 was removed — must fall back to shell's grok-4, not A's grok-3.
|
||||
// B's kigi-5 was removed — must fall back to shell's kigi-4, not A's kigi-3.
|
||||
let agent_b = app.agents.get(&AgentId(1)).unwrap();
|
||||
assert_eq!(
|
||||
agent_b
|
||||
@@ -215,7 +215,7 @@
|
||||
.current
|
||||
.as_ref()
|
||||
.map(|id| id.0.as_ref()),
|
||||
Some("grok-4"),
|
||||
Some("kigi-4"),
|
||||
"inactive agent must fall back to shell default, not active agent's model"
|
||||
);
|
||||
}
|
||||
@@ -228,12 +228,12 @@
|
||||
fn model_changed_updates_state_silently_on_follower() {
|
||||
let mut app = make_app_with_agent("sess-1");
|
||||
let agent = app.agents.get_mut(&AgentId(0)).unwrap();
|
||||
seed_models(agent, "grok-3", &["grok-3", "grok-4"]);
|
||||
seed_models(agent, "kigi-3", &["kigi-3", "kigi-4"]);
|
||||
let scrollback_before = agent.scrollback.len();
|
||||
// Follower: no local switch in flight.
|
||||
assert!(!agent.session.model_switch_pending);
|
||||
|
||||
let notif = model_changed_ext("sess-1", "grok-4", None);
|
||||
let notif = model_changed_ext("sess-1", "kigi-4", None);
|
||||
let changed = handle_ext_notification(¬if, &mut app);
|
||||
assert!(
|
||||
changed,
|
||||
@@ -248,7 +248,7 @@
|
||||
.current
|
||||
.as_ref()
|
||||
.map(|id| id.0.as_ref()),
|
||||
Some("grok-4"),
|
||||
Some("kigi-4"),
|
||||
"follower must mirror the remote switch into its local model state",
|
||||
);
|
||||
assert_eq!(
|
||||
@@ -325,13 +325,13 @@
|
||||
fn model_changed_skipped_when_local_switch_in_flight() {
|
||||
let mut app = make_app_with_agent("sess-1");
|
||||
let agent = app.agents.get_mut(&AgentId(0)).unwrap();
|
||||
seed_models(agent, "grok-3", &["grok-3", "grok-4"]);
|
||||
seed_models(agent, "kigi-3", &["kigi-3", "kigi-4"]);
|
||||
// Invoker: a local switch is in flight (set by Action::SwitchModel /
|
||||
// set_default_model before the SetSessionModelRequest is sent).
|
||||
agent.session.model_switch_pending = true;
|
||||
let scrollback_before = agent.scrollback.len();
|
||||
|
||||
let notif = model_changed_ext("sess-1", "grok-4", None);
|
||||
let notif = model_changed_ext("sess-1", "kigi-4", None);
|
||||
let changed = handle_ext_notification(¬if, &mut app);
|
||||
assert!(
|
||||
!changed,
|
||||
@@ -346,7 +346,7 @@
|
||||
.current
|
||||
.as_ref()
|
||||
.map(|id| id.0.as_ref()),
|
||||
Some("grok-3"),
|
||||
Some("kigi-3"),
|
||||
"models.current must stay at the pre-response snapshot — \
|
||||
SwitchModelComplete owns the final apply + system message"
|
||||
);
|
||||
@@ -370,9 +370,9 @@
|
||||
fn model_changed_dropped_when_model_unknown_to_catalog() {
|
||||
let mut app = make_app_with_agent("sess-1");
|
||||
let agent = app.agents.get_mut(&AgentId(0)).unwrap();
|
||||
seed_models(agent, "grok-3", &["grok-3", "grok-4"]);
|
||||
seed_models(agent, "kigi-3", &["kigi-3", "kigi-4"]);
|
||||
|
||||
let notif = model_changed_ext("sess-1", "grok-99-unknown", None);
|
||||
let notif = model_changed_ext("sess-1", "kigi-99-unknown", None);
|
||||
let changed = handle_ext_notification(¬if, &mut app);
|
||||
assert!(
|
||||
!changed,
|
||||
@@ -387,7 +387,7 @@
|
||||
.current
|
||||
.as_ref()
|
||||
.map(|id| id.0.as_ref()),
|
||||
Some("grok-3"),
|
||||
Some("kigi-3"),
|
||||
"models.current must stay on the previously-known model"
|
||||
);
|
||||
}
|
||||
@@ -395,15 +395,15 @@
|
||||
/// `reasoning_effort` round-trips through the broadcast: the follower
|
||||
/// applies it alongside the model id so the prompt header / status bar
|
||||
/// show the right effort without waiting for a subsequent
|
||||
/// `x.ai/models/update`.
|
||||
/// `kigi/models/update`.
|
||||
#[test]
|
||||
fn model_changed_applies_reasoning_effort_on_follower() {
|
||||
use kigi_shell::sampling::types::ReasoningEffort;
|
||||
let mut app = make_app_with_agent("sess-1");
|
||||
let agent = app.agents.get_mut(&AgentId(0)).unwrap();
|
||||
seed_models(agent, "grok-3", &["grok-3", "grok-4"]);
|
||||
seed_models(agent, "kigi-3", &["kigi-3", "kigi-4"]);
|
||||
|
||||
let notif = model_changed_ext("sess-1", "grok-4", Some("high"));
|
||||
let notif = model_changed_ext("sess-1", "kigi-4", Some("high"));
|
||||
assert!(handle_ext_notification(¬if, &mut app));
|
||||
|
||||
let agent = app.agents.get(&AgentId(0)).unwrap();
|
||||
@@ -423,9 +423,9 @@
|
||||
fn model_changed_dropped_for_unknown_session_id() {
|
||||
let mut app = make_app_with_agent("sess-1");
|
||||
let agent = app.agents.get_mut(&AgentId(0)).unwrap();
|
||||
seed_models(agent, "grok-3", &["grok-3", "grok-4"]);
|
||||
seed_models(agent, "kigi-3", &["kigi-3", "kigi-4"]);
|
||||
|
||||
let notif = model_changed_ext("sess-OTHER", "grok-4", None);
|
||||
let notif = model_changed_ext("sess-OTHER", "kigi-4", None);
|
||||
let changed = handle_ext_notification(¬if, &mut app);
|
||||
assert!(!changed);
|
||||
|
||||
@@ -437,7 +437,7 @@
|
||||
.current
|
||||
.as_ref()
|
||||
.map(|id| id.0.as_ref()),
|
||||
Some("grok-3"),
|
||||
Some("kigi-3"),
|
||||
"unrelated-session broadcast must not touch this agent's model"
|
||||
);
|
||||
}
|
||||
|
||||
@@ -213,7 +213,7 @@
|
||||
};
|
||||
let raw = serde_json::value::to_raw_value(&ext_req).unwrap();
|
||||
let msg = AcpClientMessage::ExtMethod(kigi_acp_lib::AcpArgs {
|
||||
request: acp::ExtRequest::new("x.ai/exit_plan_mode", raw.into()),
|
||||
request: acp::ExtRequest::new("kigi/exit_plan_mode", raw.into()),
|
||||
response_tx: tx,
|
||||
});
|
||||
|
||||
@@ -244,7 +244,7 @@
|
||||
};
|
||||
let raw = serde_json::value::to_raw_value(&ext_req).unwrap();
|
||||
let msg = AcpClientMessage::ExtMethod(kigi_acp_lib::AcpArgs {
|
||||
request: acp::ExtRequest::new("x.ai/exit_plan_mode", raw.into()),
|
||||
request: acp::ExtRequest::new("kigi/exit_plan_mode", raw.into()),
|
||||
response_tx: tx,
|
||||
});
|
||||
|
||||
@@ -275,7 +275,7 @@
|
||||
};
|
||||
let raw = serde_json::value::to_raw_value(&ext_req).unwrap();
|
||||
let msg = AcpClientMessage::ExtMethod(kigi_acp_lib::AcpArgs {
|
||||
request: acp::ExtRequest::new("x.ai/exit_plan_mode", raw.into()),
|
||||
request: acp::ExtRequest::new("kigi/exit_plan_mode", raw.into()),
|
||||
response_tx: tx,
|
||||
});
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
XaiSessionUpdate::PluginsChanged {
|
||||
plugins: vec![crate::views::extensions_modal::test_plugin_info(
|
||||
"user-tool",
|
||||
Some(kigi_hooks_plugins_types::PluginOrigin::UserGrok),
|
||||
Some(kigi_hooks_plugins_types::PluginOrigin::UserKigi),
|
||||
)],
|
||||
},
|
||||
),
|
||||
@@ -60,7 +60,7 @@
|
||||
plugins: vec![
|
||||
crate::views::extensions_modal::test_plugin_info(
|
||||
"user-tool",
|
||||
Some(kigi_hooks_plugins_types::PluginOrigin::UserGrok),
|
||||
Some(kigi_hooks_plugins_types::PluginOrigin::UserKigi),
|
||||
),
|
||||
crate::views::extensions_modal::test_plugin_info(
|
||||
"claude-tool",
|
||||
@@ -103,7 +103,7 @@
|
||||
XaiSessionUpdate::PluginsChanged {
|
||||
plugins: vec![crate::views::extensions_modal::test_plugin_info(
|
||||
"user-tool",
|
||||
Some(kigi_hooks_plugins_types::PluginOrigin::UserGrok),
|
||||
Some(kigi_hooks_plugins_types::PluginOrigin::UserKigi),
|
||||
)],
|
||||
},
|
||||
),
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
use super::*;
|
||||
|
||||
/// The pager reconciles the authoritative shared prompt queue from the
|
||||
/// `x.ai/queue/changed` broadcast, and an empty broadcast clears it.
|
||||
/// `kigi/queue/changed` broadcast, and an empty broadcast clears it.
|
||||
#[test]
|
||||
fn queue_changed_reconciles_shared_queue() {
|
||||
let mut app = make_app_with_agent("sess-1");
|
||||
@@ -144,7 +144,7 @@
|
||||
params["runningPromptId"] = serde_json::json!(r);
|
||||
}
|
||||
acp::ExtNotification::new(
|
||||
"x.ai/queue/changed",
|
||||
"kigi/queue/changed",
|
||||
std::sync::Arc::from(serde_json::value::to_raw_value(¶ms).unwrap()),
|
||||
)
|
||||
}
|
||||
@@ -336,7 +336,7 @@
|
||||
serde_json::from_str(&json_str).unwrap();
|
||||
assert_eq!(mirror.running_prompt_id.as_deref(), Some("prompt-running"));
|
||||
|
||||
let notif = acp::ExtNotification::new("x.ai/queue/changed", raw.into());
|
||||
let notif = acp::ExtNotification::new("kigi/queue/changed", raw.into());
|
||||
|
||||
// Case 1: current_prompt_id is None -> adopt it.
|
||||
let mut app = make_app_with_agent("sess-1");
|
||||
@@ -415,7 +415,7 @@
|
||||
/// Regression: when the shell promotes
|
||||
/// a server-initiated / auto-wake prompt (synthetic id `task-completed-…`,
|
||||
/// injected when a background task finishes) to the running turn, it
|
||||
/// broadcasts `x.ai/queue/changed` with `runningPromptId` = that synthetic
|
||||
/// broadcasts `kigi/queue/changed` with `runningPromptId` = that synthetic
|
||||
/// id. The pager must NOT adopt it via the turn-start shim: those turns run
|
||||
/// inside the actor and emit no `prompt_complete` / `PromptResponse`, so
|
||||
/// `start_turn()` here would strand the pager on "Responding…" forever
|
||||
@@ -1736,7 +1736,7 @@
|
||||
"promptId": "p1",
|
||||
});
|
||||
let notif = acp::ExtNotification::new(
|
||||
"x.ai/session/prompt_complete",
|
||||
"kigi/session/prompt_complete",
|
||||
serde_json::value::to_raw_value(¶ms).unwrap().into(),
|
||||
);
|
||||
handle_prompt_complete(¬if, &mut app);
|
||||
@@ -2093,7 +2093,7 @@
|
||||
fn viewer_does_not_enter_turn_running_for_server_initiated_turn() {
|
||||
// A server-initiated / auto-wake turn (synthetic prompt id, e.g. a
|
||||
// background subagent or task completion: `task-completed-…`) runs inside
|
||||
// the actor and emits NO `x.ai/session/prompt_complete`. If a viewer
|
||||
// the actor and emits NO `kigi/session/prompt_complete`. If a viewer
|
||||
// entered TurnRunning for it, nothing would ever finish the turn and the
|
||||
// viewer would be stuck "Responding…" forever — exactly the bug where one
|
||||
// dashboard showed "Worked for" while the other was stuck responding.
|
||||
@@ -2131,7 +2131,7 @@
|
||||
fn viewer_enters_turn_running_for_scheduler_fired_cron_turn() {
|
||||
// A `/loop` (scheduled-task) turn has a synthetic `scheduler-fired-…`
|
||||
// prompt id, but UNLIKE auto-wake turns it is client-driven via
|
||||
// `MvpAgent::prompt()` and DOES emit `x.ai/session/prompt_complete`. So a
|
||||
// `MvpAgent::prompt()` and DOES emit `kigi/session/prompt_complete`. So a
|
||||
// viewer MUST enter TurnRunning for it — otherwise the dashboard's
|
||||
// locally-tracked row for a running `/loop` session never shows Working.
|
||||
let mut app = make_app_with_agent("sess-view");
|
||||
@@ -2173,7 +2173,7 @@
|
||||
|
||||
#[test]
|
||||
fn viewer_prompt_complete_finishes_turn() {
|
||||
// A viewer in TurnRunning receives x.ai/session/prompt_complete for its
|
||||
// A viewer in TurnRunning receives kigi/session/prompt_complete for its
|
||||
// session -> finish_turn: state Idle, current_prompt_id cleared.
|
||||
let mut app = make_app_with_agent("sess-view");
|
||||
app.agents.get_mut(&AgentId(0)).unwrap().attached_as_viewer = true;
|
||||
|
||||
@@ -802,7 +802,7 @@
|
||||
meta,
|
||||
};
|
||||
acp::ExtNotification::new(
|
||||
"x.ai/session/update",
|
||||
"kigi/session/update",
|
||||
std::sync::Arc::from(serde_json::value::to_raw_value(&payload).unwrap()),
|
||||
)
|
||||
}
|
||||
@@ -882,7 +882,7 @@
|
||||
meta: Some(serde_json::json!({ "isReplay": true, "eventId": "sess-sub-3" })),
|
||||
};
|
||||
let notif = acp::ExtNotification::new(
|
||||
"x.ai/session_notification",
|
||||
"kigi/session_notification",
|
||||
serde_json::value::to_raw_value(&payload).unwrap().into(),
|
||||
);
|
||||
assert!(handle_ext_notification(¬if, &mut app));
|
||||
@@ -996,12 +996,12 @@
|
||||
let id = AgentId(0);
|
||||
{
|
||||
let agent = app.agents.get_mut(&id).unwrap();
|
||||
seed_models(agent, "grok-3", &["grok-3", "grok-4"]);
|
||||
seed_models(agent, "kigi-3", &["kigi-3", "kigi-4"]);
|
||||
}
|
||||
|
||||
// Unknown model → ignored → both markers untouched.
|
||||
assert!(!handle_ext_notification(
|
||||
&model_changed_ext_with_event("sess-1", "grok-99-unknown", "sess-1-7"),
|
||||
&model_changed_ext_with_event("sess-1", "kigi-99-unknown", "sess-1-7"),
|
||||
&mut app
|
||||
));
|
||||
assert_eq!(
|
||||
@@ -1015,7 +1015,7 @@
|
||||
|
||||
// Known model → applied → both markers advance.
|
||||
assert!(handle_ext_notification(
|
||||
&model_changed_ext_with_event("sess-1", "grok-4", "sess-1-8"),
|
||||
&model_changed_ext_with_event("sess-1", "kigi-4", "sess-1-8"),
|
||||
&mut app
|
||||
));
|
||||
assert_eq!(
|
||||
|
||||
@@ -51,7 +51,7 @@
|
||||
|
||||
#[test]
|
||||
fn inject_prompt_drives_even_when_attached_as_viewer() {
|
||||
// The leader routes `x.ai/scheduled_task_inject_prompt` to the SINGLE
|
||||
// The leader routes `kigi/scheduled_task_inject_prompt` to the SINGLE
|
||||
// session driver, so any client that receives it IS the driver and must
|
||||
// enqueue + run it — even one that attached via `session/load`
|
||||
// (`attached_as_viewer == true`). Previously this handler latched on
|
||||
@@ -92,7 +92,7 @@
|
||||
fn inject_prompt_malformed_json_returns_false() {
|
||||
let mut app = make_app_with_agent("sess-1");
|
||||
let raw = serde_json::value::to_raw_value(&"not a json object").unwrap();
|
||||
let notif = acp::ExtNotification::new("x.ai/scheduled_task_inject_prompt", raw.into());
|
||||
let notif = acp::ExtNotification::new("kigi/scheduled_task_inject_prompt", raw.into());
|
||||
|
||||
// The JSON is valid (a string), but sessionId/prompt fields won't exist.
|
||||
let result = handle_scheduled_task_inject_prompt(¬if, &mut app);
|
||||
|
||||
@@ -217,7 +217,7 @@
|
||||
app.current_ui.permission_mode = Some("ask".into());
|
||||
|
||||
let killswitch = acp::ExtNotification::new(
|
||||
"x.ai/settings/update",
|
||||
"kigi/settings/update",
|
||||
serde_json::value::to_raw_value(
|
||||
&serde_json::json!({ "auto_permission_mode_enabled": false }),
|
||||
)
|
||||
@@ -253,7 +253,7 @@
|
||||
app.agents.get_mut(&AgentId(2)).unwrap().session.yolo_mode = true;
|
||||
|
||||
let killswitch = acp::ExtNotification::new(
|
||||
"x.ai/settings/update",
|
||||
"kigi/settings/update",
|
||||
serde_json::value::to_raw_value(
|
||||
&serde_json::json!({ "auto_permission_mode_enabled": false }),
|
||||
)
|
||||
@@ -272,7 +272,7 @@
|
||||
let mut leave_auto_notifs = 0;
|
||||
while let Ok(msg) = rx.try_recv() {
|
||||
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" {
|
||||
continue;
|
||||
}
|
||||
let params: serde_json::Value =
|
||||
@@ -302,7 +302,7 @@
|
||||
app.default_yolo = false;
|
||||
|
||||
let apply_yolo = acp::ExtNotification::new(
|
||||
"x.ai/settings/update",
|
||||
"kigi/settings/update",
|
||||
serde_json::value::to_raw_value(&serde_json::json!({
|
||||
"permission_mode": "always-approve",
|
||||
}))
|
||||
@@ -334,7 +334,7 @@
|
||||
app.auto_mode_gate = true;
|
||||
|
||||
let unrelated = acp::ExtNotification::new(
|
||||
"x.ai/settings/update",
|
||||
"kigi/settings/update",
|
||||
serde_json::value::to_raw_value(&serde_json::json!({
|
||||
"show_resolved_model": true,
|
||||
}))
|
||||
@@ -372,7 +372,7 @@
|
||||
app.current_ui.permission_mode = Some("sentinel-not-a-mode".into());
|
||||
|
||||
let push = acp::ExtNotification::new(
|
||||
"x.ai/settings/update",
|
||||
"kigi/settings/update",
|
||||
serde_json::value::to_raw_value(&serde_json::json!({
|
||||
"permission_mode": "always-approve",
|
||||
}))
|
||||
|
||||
@@ -157,7 +157,7 @@
|
||||
);
|
||||
}
|
||||
|
||||
/// Regression: replay from `updates.jsonl` emits `x.ai/session/update` (not
|
||||
/// Regression: replay from `updates.jsonl` emits `kigi/session/update` (not
|
||||
/// `session_notification`). Subagent lifecycle events must still populate
|
||||
/// `subagent_sessions` and the parent scrollback `SubagentBlock`.
|
||||
#[test]
|
||||
@@ -168,7 +168,7 @@
|
||||
let affected = handle(
|
||||
make_ext_session_notification_with_method(
|
||||
"sess-parent",
|
||||
"x.ai/session/update",
|
||||
"kigi/session/update",
|
||||
test_subagent_spawned("sess-parent", child_sid),
|
||||
),
|
||||
&mut app,
|
||||
@@ -204,7 +204,7 @@
|
||||
let affected = handle(
|
||||
make_ext_session_notification_with_method(
|
||||
"sess-parent",
|
||||
"x.ai/session/update",
|
||||
"kigi/session/update",
|
||||
test_subagent_finished(child_sid),
|
||||
),
|
||||
&mut app,
|
||||
@@ -424,7 +424,7 @@
|
||||
let _ = handle(
|
||||
make_ext_session_notification_with_method(
|
||||
"sess-parent",
|
||||
"x.ai/session/update",
|
||||
"kigi/session/update",
|
||||
test_subagent_finished(child_sid),
|
||||
),
|
||||
&mut app,
|
||||
@@ -674,9 +674,9 @@
|
||||
fn ext_session_notification_and_update_equivalent_for_subagent_spawned() {
|
||||
let child_sid = "child-equiv";
|
||||
let (spawn_notif, finish_notif) =
|
||||
run_subagent_lifecycle_via_method("x.ai/session_notification", child_sid);
|
||||
run_subagent_lifecycle_via_method("kigi/session_notification", child_sid);
|
||||
let (spawn_update, finish_update) =
|
||||
run_subagent_lifecycle_via_method("x.ai/session/update", child_sid);
|
||||
run_subagent_lifecycle_via_method("kigi/session/update", child_sid);
|
||||
|
||||
assert_eq!(spawn_notif.description, spawn_update.description);
|
||||
assert_eq!(spawn_notif.subagent_type, spawn_update.subagent_type);
|
||||
@@ -725,7 +725,7 @@
|
||||
let affected = handle(
|
||||
make_ext_session_notification_with_method(
|
||||
"sess-A",
|
||||
"x.ai/session/update",
|
||||
"kigi/session/update",
|
||||
test_subagent_spawned("sess-A", child_sid),
|
||||
),
|
||||
&mut app,
|
||||
@@ -757,7 +757,7 @@
|
||||
let affected = handle(
|
||||
make_ext_session_notification_with_method(
|
||||
"sess-A",
|
||||
"x.ai/session/update",
|
||||
"kigi/session/update",
|
||||
test_subagent_finished(child_sid),
|
||||
),
|
||||
&mut app,
|
||||
@@ -784,7 +784,7 @@
|
||||
let affected = handle(
|
||||
make_ext_session_notification_with_method(
|
||||
"sess-unknown",
|
||||
"x.ai/session/update",
|
||||
"kigi/session/update",
|
||||
test_subagent_spawned("sess-unknown", "child-unknown"),
|
||||
),
|
||||
&mut app,
|
||||
@@ -809,7 +809,7 @@
|
||||
// Valid JSON but not a SessionNotification — parse must fail quietly.
|
||||
let raw =
|
||||
serde_json::value::to_raw_value(&serde_json::json!({"unexpected": true})).unwrap();
|
||||
let request = acp::ExtNotification::new("x.ai/session/update", raw.into());
|
||||
let request = acp::ExtNotification::new("kigi/session/update", raw.into());
|
||||
let msg = AcpClientMessage::ExtNotification(kigi_acp_lib::AcpArgs {
|
||||
request,
|
||||
response_tx: tx,
|
||||
@@ -819,7 +819,7 @@
|
||||
|
||||
assert!(
|
||||
!affected,
|
||||
"malformed x.ai/session/update params must not redraw"
|
||||
"malformed kigi/session/update params must not redraw"
|
||||
);
|
||||
assert!(
|
||||
app.agents.get(&AgentId(0)).unwrap().scrollback.is_empty(),
|
||||
|
||||
@@ -971,7 +971,7 @@
|
||||
.remove("will_wake")
|
||||
.expect("the typed builder stamps the field");
|
||||
let legacy = acp::ExtNotification::new(
|
||||
"x.ai/task_completed",
|
||||
"kigi/task_completed",
|
||||
std::sync::Arc::from(serde_json::value::to_raw_value(&v).unwrap()),
|
||||
);
|
||||
let _ = handle_ext_notification(&legacy, &mut app);
|
||||
|
||||
@@ -179,23 +179,23 @@ pub enum Action {
|
||||
/// Try to drain the next queued prompt (after editing completes, etc.).
|
||||
DrainQueue,
|
||||
/// Remove a server-authoritative (shared) queued prompt by its stable
|
||||
/// `prompt_id`. Routed to the agent as `x.ai/queue/remove`;
|
||||
/// the resulting `x.ai/queue/changed` rebroadcast is the source of truth.
|
||||
/// `prompt_id`. Routed to the agent as `kigi/queue/remove`;
|
||||
/// the resulting `kigi/queue/changed` rebroadcast is the source of truth.
|
||||
QueueRemoveShared {
|
||||
id: String,
|
||||
expected_version: u64,
|
||||
},
|
||||
/// Reorder the server-authoritative (shared) queued prompts to match
|
||||
/// `ordered_ids`. Routed as `x.ai/queue/reorder`.
|
||||
/// `ordered_ids`. Routed as `kigi/queue/reorder`.
|
||||
QueueReorderShared {
|
||||
ordered_ids: Vec<String>,
|
||||
},
|
||||
/// Clear the caller's server-authoritative (shared) queued prompts.
|
||||
/// Routed as `x.ai/queue/clear`.
|
||||
/// Routed as `kigi/queue/clear`.
|
||||
QueueClearShared,
|
||||
/// Replace the text of a server-authoritative (shared) queued prompt.
|
||||
/// Routed to the agent as `x.ai/queue/edit`; the rebroadcast of
|
||||
/// `x.ai/queue/changed` is the source of truth. Last write wins via the
|
||||
/// Routed to the agent as `kigi/queue/edit`; the rebroadcast of
|
||||
/// `kigi/queue/changed` is the source of truth. Last write wins via the
|
||||
/// session actor's serialized mailbox; no client-side conflict resolution.
|
||||
QueueEditShared {
|
||||
id: String,
|
||||
@@ -203,8 +203,8 @@ pub enum Action {
|
||||
},
|
||||
/// Interject a server-authoritative (shared) queued prompt into the running
|
||||
/// turn: the agent atomically removes it from the queue and
|
||||
/// merges its text into the in-flight turn. Routed as `x.ai/queue/interject`;
|
||||
/// the `x.ai/session/interjection` + `x.ai/queue/changed` rebroadcasts are
|
||||
/// merges its text into the in-flight turn. Routed as `kigi/queue/interject`;
|
||||
/// the `kigi/session/interjection` + `kigi/queue/changed` rebroadcasts are
|
||||
/// the source of truth (no optimistic client-side block). Mirrors the local
|
||||
/// "Send now" / `Ctrl+Enter` path, which uses [`Interject`](Self::Interject)
|
||||
/// directly because the local queue is client-owned.
|
||||
@@ -301,7 +301,7 @@ pub enum Action {
|
||||
/// duration. The dispatch handler renders + writes the file and arms
|
||||
/// `AppView::pending_pager_path`; the event loop does the suspend/restore.
|
||||
OpenTranscriptPager,
|
||||
/// Minimal mode (`grok --minimal`): re-print the most-recently committed
|
||||
/// Minimal mode (`kigi --minimal`): re-print the most-recently committed
|
||||
/// folded block (collapsed reasoning / truncated tool output) into native
|
||||
/// scrollback, fully expanded, below the conversation (design decision K10).
|
||||
/// Bound to `Ctrl+E` and the `/expand` command. No-op outside minimal mode
|
||||
@@ -330,12 +330,12 @@ pub enum Action {
|
||||
ExecuteHooksAction(kigi_hooks_plugins_types::HooksAction),
|
||||
/// Execute a plugins management action from the modal.
|
||||
ExecutePluginsAction(kigi_hooks_plugins_types::PluginsAction),
|
||||
/// Add or update an MCP server via x.ai/mcp/upsert.
|
||||
/// Add or update an MCP server via kigi/mcp/upsert.
|
||||
UpsertMcpServer {
|
||||
name: String,
|
||||
config: Box<kigi_shell::util::config::McpServerConfig>,
|
||||
},
|
||||
/// Delete an MCP server via x.ai/mcp/delete.
|
||||
/// Delete an MCP server via kigi/mcp/delete.
|
||||
DeleteMcpServer {
|
||||
server_name: String,
|
||||
},
|
||||
@@ -344,7 +344,7 @@ pub enum Action {
|
||||
server_name: String,
|
||||
enabled: bool,
|
||||
},
|
||||
/// Toggle a skill enable/disable via x.ai/skills/toggle.
|
||||
/// Toggle a skill enable/disable via kigi/skills/toggle.
|
||||
ToggleSkill {
|
||||
skill_name: String,
|
||||
enabled: bool,
|
||||
@@ -373,7 +373,7 @@ pub enum Action {
|
||||
CancelScheduledTask(String),
|
||||
/// Demote the currently running execute tool to a background task.
|
||||
DemoteToBackground,
|
||||
/// Request current bundle cache status via `x.ai/bundle/status`.
|
||||
/// Request current bundle cache status via `kigi/bundle/status`.
|
||||
RequestBundleStatus,
|
||||
/// View a catalog entry's raw content in the block viewer.
|
||||
ViewCatalogEntry {
|
||||
@@ -484,7 +484,7 @@ pub enum Action {
|
||||
SetContextualHintSendNow(bool),
|
||||
SetContextualHintSmallScreen(bool),
|
||||
SetContextualHintWordSelect(bool),
|
||||
/// Commit the active theme (canonical name, e.g. `"groknight"`, `"auto"`).
|
||||
/// Commit the active theme (canonical name, e.g. `"kiginight"`, `"auto"`).
|
||||
SetTheme(String),
|
||||
/// Commit the theme used when the OS is in dark mode. Only updates
|
||||
/// the live display when `theme = "auto"` AND system is in dark mode.
|
||||
@@ -697,7 +697,7 @@ pub enum Action {
|
||||
},
|
||||
/// Persist the memory modal fullscreen preference to config.toml.
|
||||
PersistMemoryFullscreen(bool),
|
||||
/// Open the Agent Dashboard view (`/dashboard`, `Ctrl+\`, `grok dashboard`).
|
||||
/// Open the Agent Dashboard view (`/dashboard`, `Ctrl+\`, `kigi dashboard`).
|
||||
OpenDashboard,
|
||||
/// Close the dashboard, returning to the previous `ActiveView`.
|
||||
ExitDashboard,
|
||||
@@ -913,7 +913,7 @@ pub enum Action {
|
||||
/// Persist-and-notify semantics for [`Effect::PersistPermissionMode`].
|
||||
///
|
||||
/// Both variants write to `~/.kigi/config.toml` and route ACP
|
||||
/// `x.ai/yolo_mode_changed` notifications. The ACP notification is
|
||||
/// `kigi/yolo_mode_changed` notifications. The ACP notification is
|
||||
/// gated on disk-write success when `WithRollback` is used.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum PermissionModePersist {
|
||||
@@ -1352,7 +1352,7 @@ pub enum Effect {
|
||||
},
|
||||
/// Fetch session list for the welcome screen session picker.
|
||||
FetchSessionList {
|
||||
/// Text search pushed down to `x.ai/session/list` as `query` (chat
|
||||
/// Text search pushed down to `kigi/session/list` as `query` (chat
|
||||
/// mode: forwarded to the backend conversations search). `None`
|
||||
/// fetches the unfiltered list.
|
||||
query: Option<String>,
|
||||
@@ -1367,11 +1367,11 @@ pub enum Effect {
|
||||
/// against the deep-search seq; chat: server refetch against the list seq).
|
||||
DebounceSessionSearch { query: String, seq: u64 },
|
||||
/// Fetch the leader session roster (FleetView dashboard) via
|
||||
/// `x.ai/sessions/list`. Only issued in leader mode while the
|
||||
/// `kigi/sessions/list`. Only issued in leader mode while the
|
||||
/// dashboard is open.
|
||||
FetchRoster,
|
||||
/// Fetch the local on-disk session list (dormant/idle sessions) for the
|
||||
/// dashboard via `x.ai/session/list` — the non-leader fallback for the
|
||||
/// dashboard via `kigi/session/list` — the non-leader fallback for the
|
||||
/// FleetView roster. Issued while the dashboard is open and NOT in leader
|
||||
/// mode so the dashboard shows idle sessions instead of being empty.
|
||||
FetchDashboardSessions,
|
||||
@@ -1440,7 +1440,7 @@ pub enum Effect {
|
||||
session_id: acp::SessionId,
|
||||
task_id: String,
|
||||
},
|
||||
/// Cancel a subagent via `x.ai/subagent/cancel`.
|
||||
/// Cancel a subagent via `kigi/subagent/cancel`.
|
||||
KillSubagent {
|
||||
session_id: acp::SessionId,
|
||||
subagent_id: String,
|
||||
@@ -1531,31 +1531,31 @@ pub enum Effect {
|
||||
/// Toggle plan mode — fire-and-forget signal to the shell.
|
||||
TogglePlanMode { session_id: acp::SessionId },
|
||||
/// Remove a server-owned queued prompt: fire-and-forget
|
||||
/// `x.ai/queue/remove`. The agent re-broadcasts the authoritative queue.
|
||||
/// `kigi/queue/remove`. The agent re-broadcasts the authoritative queue.
|
||||
QueueRemove {
|
||||
session_id: acp::SessionId,
|
||||
id: String,
|
||||
expected_version: u64,
|
||||
},
|
||||
/// Reorder server-owned queued prompts: fire-and-forget `x.ai/queue/reorder`.
|
||||
/// Reorder server-owned queued prompts: fire-and-forget `kigi/queue/reorder`.
|
||||
QueueReorder {
|
||||
session_id: acp::SessionId,
|
||||
ordered_ids: Vec<String>,
|
||||
},
|
||||
/// Clear the caller's server-owned queued prompts: fire-and-forget
|
||||
/// `x.ai/queue/clear`.
|
||||
/// `kigi/queue/clear`.
|
||||
QueueClear { session_id: acp::SessionId },
|
||||
/// Replace the text of a server-owned queued prompt in place: fire-and-forget
|
||||
/// `x.ai/queue/edit`. The session actor's serialized mailbox makes this
|
||||
/// `kigi/queue/edit`. The session actor's serialized mailbox makes this
|
||||
/// last-writer-wins for concurrent edits; the rebroadcast of
|
||||
/// `x.ai/queue/changed` is the truth signal.
|
||||
/// `kigi/queue/changed` is the truth signal.
|
||||
QueueEdit {
|
||||
session_id: acp::SessionId,
|
||||
id: String,
|
||||
new_text: String,
|
||||
},
|
||||
/// Interject a server-owned queued prompt into the running turn:
|
||||
/// fire-and-forget `x.ai/queue/interject`. The session actor atomically
|
||||
/// fire-and-forget `kigi/queue/interject`. The session actor atomically
|
||||
/// removes it from the queue and merges its text into the in-flight turn,
|
||||
/// then broadcasts both the interjection and the authoritative queue.
|
||||
/// `new_text` (when `Some`, serialized as `newText`) replaces the stored
|
||||
@@ -1595,7 +1595,7 @@ pub enum Effect {
|
||||
cwd: std::path::PathBuf,
|
||||
session_id: String,
|
||||
},
|
||||
/// Resolve the running agent name for a session (`x.ai/session/info`).
|
||||
/// Resolve the running agent name for a session (`kigi/session/info`).
|
||||
FetchSessionAgentName {
|
||||
agent_id: AgentId,
|
||||
session_id: acp::SessionId,
|
||||
@@ -1611,24 +1611,24 @@ pub enum Effect {
|
||||
PollAuthUrl { request_seq: u64 },
|
||||
/// Submit a manually-pasted auth code (ext request).
|
||||
SubmitAuthCode { request_seq: u64, code: String },
|
||||
/// Fetch MCP server list from the shell (x.ai/mcp/list).
|
||||
/// Fetch MCP server list from the shell (kigi/mcp/list).
|
||||
FetchMcpsList {
|
||||
agent_id: AgentId,
|
||||
session_id: acp::SessionId,
|
||||
cache: bool,
|
||||
},
|
||||
/// Trigger MCP OAuth for a server (x.ai/mcp/auth_trigger).
|
||||
/// Trigger MCP OAuth for a server (kigi/mcp/auth_trigger).
|
||||
McpAuthTrigger {
|
||||
agent_id: AgentId,
|
||||
session_id: acp::SessionId,
|
||||
server_name: String,
|
||||
},
|
||||
/// Fetch hooks list from the shell (x.ai/hooks/list).
|
||||
/// Fetch hooks list from the shell (kigi/hooks/list).
|
||||
FetchHooksList {
|
||||
agent_id: AgentId,
|
||||
session_id: acp::SessionId,
|
||||
},
|
||||
/// Fetch plugins list from the shell (x.ai/plugins/list).
|
||||
/// Fetch plugins list from the shell (kigi/plugins/list).
|
||||
FetchPluginsList {
|
||||
agent_id: AgentId,
|
||||
session_id: acp::SessionId,
|
||||
@@ -1645,39 +1645,39 @@ pub enum Effect {
|
||||
session_id: acp::SessionId,
|
||||
action: kigi_hooks_plugins_types::PluginsAction,
|
||||
},
|
||||
/// Fetch skills list from the shell (x.ai/skills/list).
|
||||
/// Fetch skills list from the shell (kigi/skills/list).
|
||||
FetchSkillsList {
|
||||
agent_id: AgentId,
|
||||
session_id: acp::SessionId,
|
||||
},
|
||||
/// Toggle a skill via x.ai/skills/toggle (enable/disable without restart).
|
||||
/// Toggle a skill via kigi/skills/toggle (enable/disable without restart).
|
||||
ToggleSkill {
|
||||
agent_id: AgentId,
|
||||
session_id: acp::SessionId,
|
||||
skill_name: String,
|
||||
enabled: bool,
|
||||
},
|
||||
/// Upsert an MCP server via x.ai/mcp/upsert.
|
||||
/// Upsert an MCP server via kigi/mcp/upsert.
|
||||
UpsertMcpServer {
|
||||
agent_id: AgentId,
|
||||
session_id: acp::SessionId,
|
||||
name: String,
|
||||
config: Box<kigi_shell::util::config::McpServerConfig>,
|
||||
},
|
||||
/// Delete an MCP server via x.ai/mcp/delete.
|
||||
/// Delete an MCP server via kigi/mcp/delete.
|
||||
DeleteMcpServer {
|
||||
agent_id: AgentId,
|
||||
session_id: acp::SessionId,
|
||||
server_name: String,
|
||||
},
|
||||
/// Live-toggle an MCP server via x.ai/mcp/toggle (no restart needed).
|
||||
/// Live-toggle an MCP server via kigi/mcp/toggle (no restart needed).
|
||||
ToggleMcpServer {
|
||||
agent_id: AgentId,
|
||||
session_id: acp::SessionId,
|
||||
server_name: String,
|
||||
enabled: bool,
|
||||
},
|
||||
/// Toggle a single MCP tool via x.ai/mcp/toggle_tool.
|
||||
/// Toggle a single MCP tool via kigi/mcp/toggle_tool.
|
||||
ToggleMcpTool {
|
||||
agent_id: AgentId,
|
||||
session_id: acp::SessionId,
|
||||
@@ -1685,20 +1685,20 @@ pub enum Effect {
|
||||
tool_name: String,
|
||||
enabled: bool,
|
||||
},
|
||||
/// Fetch and display session info via x.ai/session/info.
|
||||
/// Fetch and display session info via kigi/session/info.
|
||||
ShowSessionInfo {
|
||||
agent_id: AgentId,
|
||||
session_id: acp::SessionId,
|
||||
show_resolved_model: bool,
|
||||
},
|
||||
/// Fetch and display detailed context usage via x.ai/session/info.
|
||||
/// Fetch and display detailed context usage via kigi/session/info.
|
||||
ShowContextInfo {
|
||||
agent_id: AgentId,
|
||||
session_id: acp::SessionId,
|
||||
},
|
||||
/// Fetch current bundle cache status via `x.ai/bundle/status`.
|
||||
/// Fetch current bundle cache status via `kigi/bundle/status`.
|
||||
FetchBundleStatus,
|
||||
/// Fetch a bundled entry's raw content via `x.ai/bundle/entry/get`.
|
||||
/// Fetch a bundled entry's raw content via `kigi/bundle/entry/get`.
|
||||
FetchCatalogEntry { kind: String, name: String },
|
||||
/// Send feedback about the current session (fire-and-forget POST).
|
||||
SendFeedback {
|
||||
@@ -1712,7 +1712,7 @@ pub enum Effect {
|
||||
text: String,
|
||||
cwd: std::path::PathBuf,
|
||||
},
|
||||
/// Send raw note to x.ai/memory/rewrite for LLM-powered reformatting.
|
||||
/// Send raw note to kigi/memory/rewrite for LLM-powered reformatting.
|
||||
/// On success, the rewritten text populates the prompt for inline review.
|
||||
/// On failure, falls back to showing the raw text for review.
|
||||
RewriteMemoryNote {
|
||||
@@ -1733,31 +1733,31 @@ pub enum Effect {
|
||||
agent_id: AgentId,
|
||||
cwd: std::path::PathBuf,
|
||||
},
|
||||
/// Fire a /btw side question via x.ai/btw ext method.
|
||||
/// Fire a /btw side question via kigi/btw ext method.
|
||||
SendBtw {
|
||||
agent_id: AgentId,
|
||||
session_id: acp::SessionId,
|
||||
question: String,
|
||||
},
|
||||
/// Request a session recap via the x.ai/recap ext method. Fire-and-forget:
|
||||
/// Request a session recap via the kigi/recap ext method. Fire-and-forget:
|
||||
/// the recap arrives later as a `SessionRecap` notification.
|
||||
SendRecap {
|
||||
session_id: acp::SessionId,
|
||||
auto: bool,
|
||||
},
|
||||
/// Send a mid-turn interjection via x.ai/interject ext method.
|
||||
/// Send a mid-turn interjection via kigi/interject ext method.
|
||||
SendInterject {
|
||||
agent_id: AgentId,
|
||||
session_id: acp::SessionId,
|
||||
text: String,
|
||||
/// Client-minted id echoed back on the `x.ai/session/interjection`
|
||||
/// Client-minted id echoed back on the `kigi/session/interjection`
|
||||
/// broadcast so the originator can dedup its optimistic local block.
|
||||
interjection_id: String,
|
||||
/// Structured text + image content blocks. `None` for text-only
|
||||
/// interjections — the wire shape stays byte-identical to legacy.
|
||||
blocks: Option<Vec<acp::ContentBlock>>,
|
||||
},
|
||||
/// Log out via `x.ai/auth/logout` (shell clears auth.json + in-memory state).
|
||||
/// Log out via `kigi/auth/logout` (shell clears auth.json + in-memory state).
|
||||
Logout,
|
||||
/// Log out then authenticate sequentially in one task.
|
||||
SwitchAccount {
|
||||
@@ -1785,7 +1785,7 @@ pub enum Effect {
|
||||
cwd: std::path::PathBuf,
|
||||
},
|
||||
/// Delete a session's stored data (local + remote) via
|
||||
/// `x.ai/session/delete`.
|
||||
/// `kigi/session/delete`.
|
||||
DeleteSession {
|
||||
source: String,
|
||||
session_id: String,
|
||||
@@ -1793,7 +1793,7 @@ pub enum Effect {
|
||||
},
|
||||
/// Deep-search sessions by content (FTS via ACP).
|
||||
DeepSearchSessions { query: String, seq: u64 },
|
||||
/// Call `x.ai/session/fork` to create a peer session that resumes
|
||||
/// Call `kigi/session/fork` to create a peer session that resumes
|
||||
/// from `parent_session_id` in the same cwd (no worktree). Mirror of
|
||||
/// the worktree branch of [`Effect::CreateWorktreeSession`]; the
|
||||
/// worktree-fork path reuses `CreateWorktreeSession { load_session_id }`
|
||||
@@ -1833,14 +1833,14 @@ pub enum Effect {
|
||||
target_prompt_index: usize,
|
||||
mode: crate::views::rewind::RewindMode,
|
||||
},
|
||||
/// Fetch Kimi usage/quota rows from the agent's `x.ai/billing`
|
||||
/// Fetch Kimi usage/quota rows from the agent's `kigi/billing`
|
||||
/// extension (`GET {base}/usages` shell-side) for the `/usage` view.
|
||||
FetchUsage { agent_id: AgentId },
|
||||
/// Spawn a debounce sleep task for shell suggestions. `agent_id` rides
|
||||
/// to the expiry so the fetch is built from the arming agent, not
|
||||
/// whatever view is active when the timer fires.
|
||||
DebounceSuggestions { agent_id: AgentId, generation: u64 },
|
||||
/// Send an ACP `x.ai/suggest` request to the shell. `agent_id` is echoed
|
||||
/// Send an ACP `kigi/suggest` request to the shell. `agent_id` is echoed
|
||||
/// on the result so the response routes to the agent that fetched, not
|
||||
/// whatever view is active when it lands.
|
||||
FetchShellSuggestions {
|
||||
@@ -1857,13 +1857,13 @@ pub enum Effect {
|
||||
/// (path/file); the as-you-type surface keeps all of them.
|
||||
token_only: bool,
|
||||
},
|
||||
/// Send an ACP `x.ai/suggestPrompt` request to the shell — predict the
|
||||
/// Send an ACP `kigi/suggestPrompt` request to the shell — predict the
|
||||
/// user's likely next prompt after a completed turn (tab autocomplete
|
||||
/// ghost text).
|
||||
FetchPromptSuggestion {
|
||||
agent_id: AgentId,
|
||||
generation: u64,
|
||||
/// Suggestion model resolved by the pager (`grok-build-0.1` when the
|
||||
/// Suggestion model resolved by the pager (`kigi-0.1` when the
|
||||
/// catalog offers it); `None` = shell falls back to the session model.
|
||||
model: Option<String>,
|
||||
session_id: Option<String>,
|
||||
@@ -1884,7 +1884,7 @@ pub enum Effect {
|
||||
preparation: crate::prompt_images::PromptImagePreviewPreparation,
|
||||
},
|
||||
}
|
||||
/// Outcome of an `x.ai/subagent/cancel` request, telling dispatch whether the
|
||||
/// Outcome of an `kigi/subagent/cancel` request, telling dispatch whether the
|
||||
/// pager must finalize the subagent row itself.
|
||||
#[derive(Debug)]
|
||||
pub enum SubagentKillOutcome {
|
||||
@@ -1951,7 +1951,7 @@ pub enum TaskResult {
|
||||
restore_summary: Option<String>,
|
||||
restore_degree: Option<kigi_workspace::session::git::RestoreDegree>,
|
||||
/// The session's in-flight running prompt id (from the load response
|
||||
/// `_meta["x.ai/runningPromptId"]`), present only when the session was
|
||||
/// `_meta["kigi/runningPromptId"]`), present only when the session was
|
||||
/// loaded MID-turn (another client is driving). The loader adopts it to
|
||||
/// pass the live `session/update` gate without re-rendering the user
|
||||
/// block (replay already rendered it).
|
||||
@@ -2014,7 +2014,7 @@ pub enum TaskResult {
|
||||
query: String,
|
||||
seq: u64,
|
||||
},
|
||||
/// Leader session roster loaded via `x.ai/sessions/list`.
|
||||
/// Leader session roster loaded via `kigi/sessions/list`.
|
||||
RosterLoaded {
|
||||
sessions: Vec<crate::app::roster::RosterEntry>,
|
||||
},
|
||||
@@ -2085,7 +2085,7 @@ pub enum TaskResult {
|
||||
/// Cancel notification was sent (fire-and-forget).
|
||||
/// The real turn end comes via PromptResponse.
|
||||
CancelComplete,
|
||||
/// Response to `x.ai/subagent/cancel`; see [`SubagentKillOutcome`].
|
||||
/// Response to `kigi/subagent/cancel`; see [`SubagentKillOutcome`].
|
||||
KillSubagentComplete {
|
||||
session_id: acp::SessionId,
|
||||
subagent_id: String,
|
||||
@@ -2155,7 +2155,7 @@ pub enum TaskResult {
|
||||
/// Deprecated: superseded by `mode` (authoritative). Kept only as a
|
||||
/// back-compat fallback for older agents that don't send `mode`.
|
||||
external: bool,
|
||||
/// Presentation mode from `x.ai/auth/get_url`; `None` on older agents.
|
||||
/// Presentation mode from `kigi/auth/get_url`; `None` on older agents.
|
||||
mode: Option<String>,
|
||||
},
|
||||
/// Auth code was submitted (fire-and-forget).
|
||||
@@ -2303,7 +2303,7 @@ pub enum TaskResult {
|
||||
agent_id: AgentId,
|
||||
result: Result<String, String>,
|
||||
},
|
||||
/// `x.ai/recap` request acknowledged (fire-and-forget). The recap itself
|
||||
/// `kigi/recap` request acknowledged (fire-and-forget). The recap itself
|
||||
/// arrives separately as a `SessionRecap` notification; this only carries
|
||||
/// a transport error, if any, for logging.
|
||||
RecapRequested {
|
||||
@@ -2342,7 +2342,7 @@ pub enum TaskResult {
|
||||
results: Vec<kigi_shell::extensions::session_search::SearchSessionHit>,
|
||||
seq: u64,
|
||||
},
|
||||
/// `x.ai/session/fork` completed (no-worktree path). The pager adopts
|
||||
/// `kigi/session/fork` completed (no-worktree path). The pager adopts
|
||||
/// the new session id and emits [`Effect::LoadSession`] to start the
|
||||
/// replay. Mirrors [`TaskResult::WorktreeForked`] in shape.
|
||||
ForkSessionReady {
|
||||
@@ -2350,7 +2350,7 @@ pub enum TaskResult {
|
||||
new_session_id: acp::SessionId,
|
||||
cwd: std::path::PathBuf,
|
||||
},
|
||||
/// `x.ai/session/fork` failed. The placeholder agent stays in
|
||||
/// `kigi/session/fork` failed. The placeholder agent stays in
|
||||
/// `app.agents` with no `session_id` so the user can switch away.
|
||||
ForkSessionFailed {
|
||||
agent_id: AgentId,
|
||||
@@ -2393,7 +2393,7 @@ pub enum TaskResult {
|
||||
agent_id: AgentId,
|
||||
generation: u64,
|
||||
},
|
||||
/// Shell suggestions loaded from ACP `x.ai/suggest`. `request_text` /
|
||||
/// Shell suggestions loaded from ACP `kigi/suggest`. `request_text` /
|
||||
/// `request_cursor` echo what the request was built from — the anchor
|
||||
/// the items' `replaceRange` offsets index into and the position Tab
|
||||
/// targets, paired atomically with them; `agent_id` routes the landing
|
||||
@@ -2404,7 +2404,7 @@ pub enum TaskResult {
|
||||
request_text: String,
|
||||
request_cursor: usize,
|
||||
},
|
||||
/// Predicted next prompt loaded from ACP `x.ai/suggestPrompt`.
|
||||
/// Predicted next prompt loaded from ACP `kigi/suggestPrompt`.
|
||||
/// `suggestion` is `None` when the shell had nothing to suggest.
|
||||
PromptSuggestionLoaded {
|
||||
agent_id: AgentId,
|
||||
|
||||
@@ -133,7 +133,7 @@ pub enum AgentCommand {
|
||||
RestoreCode,
|
||||
/// Forking the current session into a peer (no-worktree path).
|
||||
/// Drives the spinner shown on the placeholder agent while the
|
||||
/// `x.ai/session/fork` request is in flight.
|
||||
/// `kigi/session/fork` request is in flight.
|
||||
ForkSession,
|
||||
}
|
||||
impl AgentCommand {
|
||||
@@ -627,11 +627,11 @@ pub struct AgentSession {
|
||||
/// `yolo_mode` (yolo wins).
|
||||
pub(crate) auto_mode: bool,
|
||||
/// Prompt history for the current session, fetched from ACP
|
||||
/// (`x.ai/prompt_history` scoped via `filter_session_id`). Most-recent-first.
|
||||
/// (`kigi/prompt_history` scoped via `filter_session_id`). Most-recent-first.
|
||||
/// Fetched on session create/load; prompts sent in this session are
|
||||
/// additionally front-inserted locally on send.
|
||||
pub prompt_history: Vec<String>,
|
||||
/// True until the session's startup/load `x.ai/prompt_history` fetch completes.
|
||||
/// True until the session's startup/load `kigi/prompt_history` fetch completes.
|
||||
pub prompt_history_loading: bool,
|
||||
/// Session is currently replaying historical updates from `session/load`.
|
||||
/// Used to suppress live-style redraw/render work until the load completes.
|
||||
|
||||
@@ -52,7 +52,7 @@ impl AgentView {
|
||||
}
|
||||
}
|
||||
|
||||
/// Apply an `x.ai/follow_ups` notification, keyed by `response_id`
|
||||
/// Apply an `kigi/follow_ups` notification, keyed by `response_id`
|
||||
/// (newest-response-wins).
|
||||
///
|
||||
/// Monotonic accept-the-newer: a never-seen `response_id` is strictly newer
|
||||
@@ -78,7 +78,7 @@ impl AgentView {
|
||||
}
|
||||
|
||||
/// `apply_follow_ups` with the turn identity (`prompt_id`) the shell stamps
|
||||
/// on each `x.ai/follow_ups` notification (the same `promptId` it stamps on
|
||||
/// on each `kigi/follow_ups` notification (the same `promptId` it stamps on
|
||||
/// every `session/update`). The identity makes viewer-adoption dedup
|
||||
/// DETERMINISTIC:
|
||||
///
|
||||
@@ -86,7 +86,7 @@ impl AgentView {
|
||||
/// `prompt_id` equals `session.current_prompt_id`) re-renders even when its
|
||||
/// chips were cleared by turn adoption — so chips that were applied then
|
||||
/// cleared reappear instead of being lost until reload.
|
||||
/// - A buffer-replayed `x.ai/follow_ups` for a PRIOR turn's `response_id`
|
||||
/// - A buffer-replayed `kigi/follow_ups` for a PRIOR turn's `response_id`
|
||||
/// stays rejected by the seen-ring (its `prompt_id` is not the active one),
|
||||
/// so stale chips are never revived on the new turn.
|
||||
///
|
||||
@@ -219,7 +219,7 @@ impl AgentView {
|
||||
true
|
||||
}
|
||||
|
||||
/// Buffer a stamped `x.ai/follow_ups` for a turn that is not yet current,
|
||||
/// Buffer a stamped `kigi/follow_ups` for a turn that is not yet current,
|
||||
/// keyed by its `promptId`. A newer delivery for the same `promptId`
|
||||
/// overwrites the earlier one (keep the latest); the FIFO order list bounds
|
||||
/// the map to [`MAX_PENDING_FOLLOW_UPS`], evicting only the oldest entry.
|
||||
@@ -249,7 +249,7 @@ impl AgentView {
|
||||
}
|
||||
}
|
||||
|
||||
/// Flush a buffered `x.ai/follow_ups` for `prompt_id` (a turn that has just
|
||||
/// Flush a buffered `kigi/follow_ups` for `prompt_id` (a turn that has just
|
||||
/// become current). Renders the chips through [`apply_follow_ups_with_prompt`]
|
||||
/// — now that `current_prompt_id == prompt_id`, the stamped delivery is
|
||||
/// accepted as the active turn's. Returns whether chips were rendered. A
|
||||
@@ -292,7 +292,7 @@ impl AgentView {
|
||||
|
||||
/// Reload reset that PRESERVES the running turn's follow-ups for
|
||||
/// `keep_prompt_id` (the turn the load is about to adopt). On `SessionLoaded`
|
||||
/// the running turn's `x.ai/follow_ups` arrive on the ext channel DURING
|
||||
/// the running turn's `kigi/follow_ups` arrive on the ext channel DURING
|
||||
/// `loading_replay`; an unconditional reset would drop them before adoption
|
||||
/// could re-render them, so the chips would never appear unless the server
|
||||
/// resent them. The running turn's chips live in ONE of two places at reset
|
||||
|
||||
@@ -1107,7 +1107,7 @@ impl AgentView {
|
||||
self.submit_question_answers(skipped)
|
||||
}
|
||||
fn submit_question_answers(&mut self, skipped: bool) -> InputOutcome {
|
||||
use kigi_tools::implementations::grok_build::ask_user_question::AskUserQuestionExtResponse;
|
||||
use kigi_tools::implementations::kigi::ask_user_question::AskUserQuestionExtResponse;
|
||||
self.swap_question_freeform();
|
||||
let Some(mut qv) = self.question_view.take() else {
|
||||
return InputOutcome::Changed;
|
||||
@@ -1594,7 +1594,7 @@ mod permission_scope_key_tests {
|
||||
#[cfg(test)]
|
||||
mod question_no_freeform_tests {
|
||||
//! Freeform ("Other") gating for `no_freeform` question modals — e.g.
|
||||
//! the SuperGrok upsell. Regression tests for the bug where clicking
|
||||
//! the subscription upsell. Regression tests for the bug where clicking
|
||||
//! under the last option of the upsell selected the (hidden) freeform
|
||||
//! row and let the user type into a modal that offers no free text.
|
||||
use super::super::test_fixtures::make_agent;
|
||||
@@ -1605,7 +1605,7 @@ mod question_no_freeform_tests {
|
||||
use crossterm::event::{
|
||||
KeyCode, KeyEvent, KeyModifiers, MouseButton, MouseEvent, MouseEventKind,
|
||||
};
|
||||
use kigi_tools::implementations::grok_build::ask_user_question::{Question, QuestionOption};
|
||||
use kigi_tools::implementations::kigi::ask_user_question::{Question, QuestionOption};
|
||||
use ratatui::buffer::Buffer;
|
||||
use ratatui::layout::Rect;
|
||||
/// Fixed options, single-select — shaped like the free-usage upsell.
|
||||
|
||||
@@ -200,7 +200,7 @@ impl McpInitProgress {
|
||||
/// Whether the progress indicator should be visible in the UI.
|
||||
///
|
||||
/// - `total > 0` (real servers): always visible until
|
||||
/// `x.ai/mcp_initialized` clears the progress.
|
||||
/// `kigi/mcp_initialized` clears the progress.
|
||||
/// - `total == 0` (seed / 0-server): visible for at most
|
||||
/// [`SEED_EXPIRE`] seconds, then auto-expires as
|
||||
/// defense-in-depth against the shell failing to send
|
||||
@@ -559,7 +559,7 @@ pub(crate) struct SessionReload {
|
||||
saw_todo_update: bool,
|
||||
}
|
||||
/// Follow-up suggestion chips for the latest assistant response
|
||||
/// (`x.ai/follow_ups`). Streaming-only: never persisted, does not survive a
|
||||
/// (`kigi/follow_ups`). Streaming-only: never persisted, does not survive a
|
||||
/// session reload. Keyed by the assistant `response_id` (the newest-wins key).
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub(crate) struct FollowUps {
|
||||
@@ -799,7 +799,7 @@ pub struct AgentView {
|
||||
/// answering questions via `AskUserQuestion`). Reset when the turn ends.
|
||||
pub turn_paused_duration: std::time::Duration,
|
||||
/// IDs of interjections this client sent and already rendered locally
|
||||
/// (optimistic echo). The shell broadcasts `x.ai/session/interjection` to
|
||||
/// (optimistic echo). The shell broadcasts `kigi/session/interjection` to
|
||||
/// every attached pane; when our own broadcast echoes back carrying an id
|
||||
/// in this set, `handle_interjection` drops it (we already showed it) and
|
||||
/// removes the id. Other panes (which lack the id) render it. This is the
|
||||
@@ -1133,7 +1133,7 @@ pub struct AgentView {
|
||||
/// (`When::DashboardOverlay`) are lit in the overlay and dimmed elsewhere.
|
||||
pub(crate) in_dashboard_overlay: bool,
|
||||
/// MCP server init progress. Set when the shell starts connecting
|
||||
/// MCP servers, cleared when `x.ai/mcp_initialized` arrives.
|
||||
/// MCP servers, cleared when `kigi/mcp_initialized` arrives.
|
||||
/// Shown in the turn status line while the agent is idle.
|
||||
pub(crate) mcp_init_progress: Option<McpInitProgress>,
|
||||
/// Last synced ACP command generation. When this differs from
|
||||
@@ -1206,7 +1206,7 @@ pub struct AgentView {
|
||||
/// lands on a tick; borrowed during render so streaming redraws don't
|
||||
/// rescan/allocate the prompt every frame.
|
||||
pub(crate) timeline_hover_preview: Option<(usize, String)>,
|
||||
/// Running agent definition for this session (`x.ai/session/info` `agentName`).
|
||||
/// Running agent definition for this session (`kigi/session/info` `agentName`).
|
||||
pub session_agent_name: Option<String>,
|
||||
/// Map of child session IDs to subagent metadata. Populated on
|
||||
/// `SubagentSpawned` notifications, used for permission routing
|
||||
@@ -1286,7 +1286,7 @@ pub struct AgentView {
|
||||
/// complete. Kind-only: the payload is re-derived from the widget on
|
||||
/// reissue so the freshly attached image chip travels with it.
|
||||
pub(crate) deferred_send: Option<AgentDeferredSend>,
|
||||
/// Armed when an `x.ai/session/prompt_complete` broadcast arrives for the
|
||||
/// Armed when an `kigi/session/prompt_complete` broadcast arrives for the
|
||||
/// turn THIS client drives while it is still awaiting that turn's
|
||||
/// `session/prompt` RPC response. The RPC normally lands milliseconds
|
||||
/// later and disarms this; if it never does (lost in leader response
|
||||
@@ -1317,17 +1317,17 @@ pub struct AgentView {
|
||||
pub(crate) follow_without_jump_prompt_id: Option<String>,
|
||||
/// Ids of THIS client's server-queue rows that are still optimistic
|
||||
/// echoes — the `session/prompt` RPC is in flight and no
|
||||
/// `x.ai/queue/changed` broadcast has confirmed the row yet. Inserted by
|
||||
/// `kigi/queue/changed` broadcast has confirmed the row yet. Inserted by
|
||||
/// the echo push, drained when a broadcast lists the id (queued or
|
||||
/// running) or the RPC resolves without the row landing.
|
||||
pub(crate) optimistic_queue_ids: std::collections::HashSet<String>,
|
||||
/// A queue-row send-now the user fired while the row was still an
|
||||
/// optimistic echo. Firing `x.ai/queue/interject` then would race the
|
||||
/// optimistic echo. Firing `kigi/queue/interject` then would race the
|
||||
/// row's own in-flight `session/prompt` and silently no-op shell-side
|
||||
/// (a rapid double-Enter on a queued bash command could "disappear" — the
|
||||
/// interject overtook the row, the no-op dropped the send-now, and the
|
||||
/// armed cancel expectation hid the still-queued row).
|
||||
/// Parked here and fired from the confirming `x.ai/queue/changed`
|
||||
/// Parked here and fired from the confirming `kigi/queue/changed`
|
||||
/// broadcast with the row's authoritative version.
|
||||
pub(crate) send_now_awaiting_confirm: Option<String>,
|
||||
/// User blocks painted at send-now dispatch, keyed by prompt id; the
|
||||
@@ -1337,7 +1337,7 @@ pub struct AgentView {
|
||||
pub(crate) send_now_painted_blocks:
|
||||
std::collections::HashMap<String, (crate::scrollback::EntryId, bool)>,
|
||||
/// Follow-up suggestion chips for the latest assistant response
|
||||
/// (`x.ai/follow_ups`). `None` when no chips are shown. Set by
|
||||
/// (`kigi/follow_ups`). `None` when no chips are shown. Set by
|
||||
/// [`AgentView::apply_follow_ups`]; cleared at each turn start.
|
||||
pub(crate) follow_ups: Option<FollowUps>,
|
||||
/// `promptId` (turn identity) of the currently-shown `follow_ups`, when the
|
||||
@@ -1371,7 +1371,7 @@ pub struct AgentView {
|
||||
/// The ordering key for newest-wins: a fresh id takes the next value (the
|
||||
/// new high-water), so every previously-seen id is strictly lower.
|
||||
pub(crate) follow_up_next_gen: u64,
|
||||
/// Stamped `x.ai/follow_ups` that arrived for a turn that is NOT yet the
|
||||
/// Stamped `kigi/follow_ups` that arrived for a turn that is NOT yet the
|
||||
/// currently-adopted one, keyed by `promptId`. Ext notifications and
|
||||
/// `session/update` travel on separate channels, so a turn's follow_ups can
|
||||
/// land BEFORE the `session/update` that adopts it. Rather than drop such a
|
||||
@@ -1891,7 +1891,7 @@ fn resolve_action(action_id: Option<ActionId>) -> Option<InputOutcome> {
|
||||
fn question_visible_h(
|
||||
scroll_region: Option<(u16, u16)>,
|
||||
prompt_height: u16,
|
||||
question: &kigi_tools::implementations::grok_build::ask_user_question::Question,
|
||||
question: &kigi_tools::implementations::kigi::ask_user_question::Question,
|
||||
content_w: usize,
|
||||
preview: Option<&str>,
|
||||
fullscreen: bool,
|
||||
@@ -2625,7 +2625,7 @@ pub(super) mod test_fixtures {
|
||||
);
|
||||
assert_eq!(agent.follow_ups.as_ref().unwrap().suggestions, vec!["a"]);
|
||||
}
|
||||
/// FIX 4 (b): after adopting a NEW turn, a buffer-replayed `x.ai/follow_ups`
|
||||
/// FIX 4 (b): after adopting a NEW turn, a buffer-replayed `kigi/follow_ups`
|
||||
/// for a PRIOR turn's response_id must NOT revive stale chips — its
|
||||
/// `promptId` is not the active turn and it is already in the seen ring.
|
||||
#[test]
|
||||
@@ -2646,7 +2646,7 @@ pub(super) mod test_fixtures {
|
||||
assert!(agent.apply_follow_ups_with_prompt("resp-2".into(), Some("p2"), vec!["b".into()]));
|
||||
assert_eq!(agent.follow_ups.as_ref().unwrap().response_id, "resp-2");
|
||||
}
|
||||
/// FINDING B (stamped path): a LATE FIRST-TIME (never-seen) `x.ai/follow_ups`
|
||||
/// FINDING B (stamped path): a LATE FIRST-TIME (never-seen) `kigi/follow_ups`
|
||||
/// for a PRIOR turn — arriving while a newer turn is active — must NOT
|
||||
/// render. Before the fix it slipped through the "strictly newer" branch
|
||||
/// (never recorded in `follow_up_seen`, so the seen-reject didn't catch it).
|
||||
@@ -2702,7 +2702,7 @@ pub(super) mod test_fixtures {
|
||||
/// distinguished from the new turn's first follow_ups, so it follows the
|
||||
/// legacy newest-wins (renders). This path is not reachable for current
|
||||
/// shells (which always stamp `promptId`) or for buffer-replays (suppressed
|
||||
/// upstream by the `_meta["x.ai/replayed"]` gate); it is pinned here so the
|
||||
/// upstream by the `_meta["kigi/replayed"]` gate); it is pinned here so the
|
||||
/// stamped-path fix above is understood to be the deterministic guard.
|
||||
#[test]
|
||||
fn apply_follow_ups_none_prompt_first_time_follows_legacy_newest_wins() {
|
||||
@@ -2714,7 +2714,7 @@ pub(super) mod test_fixtures {
|
||||
);
|
||||
assert_eq!(agent.follow_ups.as_ref().unwrap().response_id, "resp-x");
|
||||
}
|
||||
/// FIX (buffer-before-adoption): a stamped `x.ai/follow_ups` for a turn that
|
||||
/// FIX (buffer-before-adoption): a stamped `kigi/follow_ups` for a turn that
|
||||
/// is NOT yet current (its `session/update` adoption raced behind the ext
|
||||
/// channel) must be BUFFERED, not dropped — and then RENDER when that turn
|
||||
/// becomes current and is flushed.
|
||||
|
||||
@@ -1180,10 +1180,10 @@ pub(super) mod paste_key_tests {
|
||||
/// Build a `QuestionViewState` already in `InputMode` focus.
|
||||
pub(in crate::app::agent_view) fn make_question_view_state_in_input_mode()
|
||||
-> crate::views::question_view::QuestionViewState {
|
||||
let question = kigi_tools::implementations::grok_build::ask_user_question::Question {
|
||||
let question = kigi_tools::implementations::kigi::ask_user_question::Question {
|
||||
question: "Pick one?".to_string(),
|
||||
options: vec![
|
||||
kigi_tools::implementations::grok_build::ask_user_question::QuestionOption {
|
||||
kigi_tools::implementations::kigi::ask_user_question::QuestionOption {
|
||||
label: "A".to_string(),
|
||||
description: "Option A".to_string(),
|
||||
preview: None,
|
||||
|
||||
@@ -1412,7 +1412,7 @@ mod prompt_suggestion_key_tests {
|
||||
use crossterm::event::{KeyCode, KeyEvent, KeyModifiers};
|
||||
|
||||
/// Idle agent with the gate open and a loaded suggestion — the state
|
||||
/// right after a turn ends with `x.ai/suggestPrompt` resolved. Pins the
|
||||
/// right after a turn ends with `kigi/suggestPrompt` resolved. Pins the
|
||||
/// settings cache so `resolve_enabled()` never reads the dev machine's
|
||||
/// config.toml (thread-local, so per-test).
|
||||
fn suggestion_agent(text: &str) -> AgentView {
|
||||
|
||||
@@ -530,7 +530,7 @@ impl AgentView {
|
||||
Some(crate::views::queue_pane::QueueRowOrigin::Server)
|
||||
);
|
||||
if is_server {
|
||||
// Server row: the agent promotes it to run next (`x.ai/queue/interject`); any kind may send now.
|
||||
// Server row: the agent promotes it to run next (`kigi/queue/interject`); any kind may send now.
|
||||
if let Some(row) = row.as_ref()
|
||||
&& let Some(server_id) = row.server_id.clone()
|
||||
{
|
||||
@@ -538,7 +538,7 @@ impl AgentView {
|
||||
// flight, so an interject fired now would overtake the row
|
||||
// shell-side and silently no-op (dropping the send-now and
|
||||
// hiding the row behind the armed cancel expectation). Park
|
||||
// the intent; the confirming `x.ai/queue/changed` broadcast
|
||||
// the intent; the confirming `kigi/queue/changed` broadcast
|
||||
// fires it with the row's authoritative version (see
|
||||
// `resolve_send_now_awaiting_confirm`).
|
||||
if self.optimistic_queue_ids.contains(&server_id) {
|
||||
@@ -568,13 +568,13 @@ impl AgentView {
|
||||
}
|
||||
|
||||
/// Reconcile this client's optimistic queue echoes against a raw
|
||||
/// `x.ai/queue/changed` broadcast (pre-merge entries — the mirrored
|
||||
/// `kigi/queue/changed` broadcast (pre-merge entries — the mirrored
|
||||
/// snapshot re-pins unconfirmed echoes, so it can't tell confirmation
|
||||
/// apart), and resolve a parked queue-row send-now
|
||||
/// ([`Self::send_now_awaiting_confirm`]).
|
||||
///
|
||||
/// Returns `Some((id, version))` when the parked row is now confirmed as
|
||||
/// QUEUED — the caller fires `x.ai/queue/interject` with that
|
||||
/// QUEUED — the caller fires `kigi/queue/interject` with that
|
||||
/// authoritative version. A parked row confirmed as RUNNING clears the
|
||||
/// park with nothing to do (the natural drain won the race). A row in
|
||||
/// neither set stays parked (its RPC is still in flight).
|
||||
@@ -715,7 +715,7 @@ impl AgentView {
|
||||
// Queue-specific actions (delete, edit, reorder). `x`/Delete = row delete.
|
||||
if let Some(event) = self.queue.handle_key(key, registry) {
|
||||
// Resolve the selected row's origin so edits route correctly:
|
||||
// Server-origin rows go to the agent as `x.ai/queue/*`
|
||||
// Server-origin rows go to the agent as `kigi/queue/*`
|
||||
// commands (the rebroadcast is the source of truth); Local rows
|
||||
// keep today's in-place mutation.
|
||||
let row = self.queue.row_ref(Self::queue_event_id(&event));
|
||||
@@ -822,7 +822,7 @@ impl AgentView {
|
||||
}
|
||||
}
|
||||
|
||||
/// Reorder payload for `x.ai/queue/reorder`. Omit only running; include
|
||||
/// Reorder payload for `kigi/queue/reorder`. Omit only running; include
|
||||
/// send-now in the list but do not swap past it (shell ranks missing ids last).
|
||||
fn server_queue_reordered(&self, selection_id: u64, up: bool) -> Option<Vec<String>> {
|
||||
let server_id = self.queue.row_ref(selection_id)?.server_id?;
|
||||
|
||||
@@ -1325,7 +1325,7 @@ mod tests {
|
||||
crate::scrollback::text_selection::ResolvedSelectionBoundaries::default();
|
||||
for (entry_idx, text, hit_col, prefix, suffix, expected) in [
|
||||
(0, "foo rest", 0, " ", "", "foo"),
|
||||
(1, "rest https://x.ai", 5, "", " ", "https://x.ai"),
|
||||
(1, "rest https://kimi.com", 5, "", " ", "https://kimi.com"),
|
||||
] {
|
||||
let line = ResolvedSelectableLine {
|
||||
entry_idx,
|
||||
|
||||
@@ -397,7 +397,7 @@ impl AgentView {
|
||||
self.session.start_turn(&mut self.scrollback);
|
||||
}
|
||||
/// Adopt the in-flight turn another client is driving, conveyed by the
|
||||
/// `session/load` response meta (`x.ai/runningPromptId`): enter
|
||||
/// `session/load` response meta (`kigi/runningPromptId`): enter
|
||||
/// TurnRunning and match subsequent live deltas. No user-prompt block is
|
||||
/// pushed — the turn's prompt and prior chunks arrived via the replay.
|
||||
pub(crate) fn adopt_running_prompt(&mut self, prompt_id: String) {
|
||||
|
||||
@@ -506,7 +506,7 @@ mod shell_suggestion_key_tests {
|
||||
let mut agent = bash_agent("ls | gr");
|
||||
agent.prompt.suggestions.dropdown.items = vec![
|
||||
item("grep", None),
|
||||
file_item("ls | grokfile", "grokfile", 5..7),
|
||||
file_item("ls | kigifile", "kigifile", 5..7),
|
||||
];
|
||||
|
||||
let outcome = agent.handle_prompt_key_for_test(&key(KeyCode::Tab));
|
||||
|
||||
@@ -501,19 +501,19 @@ pub struct AppView {
|
||||
/// (`team_name.is_some()`) and API-key auth.
|
||||
pub usage_visible: bool,
|
||||
/// Whether the pager is connected via a leader (leader mode). The Agent
|
||||
/// Dashboard entry points (`/dashboard`, `Ctrl+\`, `grok dashboard`, the
|
||||
/// Dashboard entry points (`/dashboard`, `Ctrl+\`, `kigi dashboard`, the
|
||||
/// startup hook) are only meaningful when a leader is coordinating a
|
||||
/// fleet of sessions, so they are gated on this flag. Set in
|
||||
/// `event_loop::run` from `connection.leader_status_rx.is_some()`;
|
||||
/// defaults to `false` (non-leader, dashboard hidden).
|
||||
pub leader_mode: bool,
|
||||
/// Leader-mode session roster (FleetView dashboard). Populated from
|
||||
/// `x.ai/sessions/list` polls and `x.ai/sessions/changed` broadcasts.
|
||||
/// `kigi/sessions/list` polls and `kigi/sessions/changed` broadcasts.
|
||||
/// Empty in non-leader mode, which naturally gates roster rendering.
|
||||
pub leader_roster: Vec<crate::app::roster::RosterEntry>,
|
||||
/// Local on-disk session list (dormant/idle sessions) surfaced on the
|
||||
/// dashboard when NOT in leader mode. There is no live leader roster to
|
||||
/// poll outside leader mode, so we fetch the same `x.ai/session/list` the
|
||||
/// poll outside leader mode, so we fetch the same `kigi/session/list` the
|
||||
/// resume picker uses and render those as idle rows. Entries are stored as
|
||||
/// [`crate::app::roster::RosterEntry`] (activity `Dormant`) so they reuse
|
||||
/// the existing roster-row rendering / attach path. Empty in leader mode.
|
||||
@@ -521,14 +521,14 @@ pub struct AppView {
|
||||
/// Whether the dashboard is currently loading local sessions (non-leader mode).
|
||||
pub dashboard_sessions_loading: bool,
|
||||
/// Server-authoritative shared prompt queues, keyed by `sessionId`
|
||||
/// Reconciled from `x.ai/queue/changed` broadcasts so
|
||||
/// Reconciled from `kigi/queue/changed` broadcasts so
|
||||
/// every client renders the same ordered queue (including prompts queued
|
||||
/// by other clients). Empty in non-leader mode.
|
||||
pub shared_prompt_queues:
|
||||
std::collections::HashMap<String, Vec<crate::app::prompt_queue::QueueEntryWire>>,
|
||||
/// Optimistic echo rows for prompts the pager sent server-authoritatively
|
||||
/// (plain prompt typed while a turn is running) but for which the
|
||||
/// confirming `x.ai/queue/changed` broadcast has not yet arrived. Keyed by
|
||||
/// confirming `kigi/queue/changed` broadcast has not yet arrived. Keyed by
|
||||
/// `sessionId`. Pinned into `shared_prompt_queues` on reconcile so the row
|
||||
/// doesn't flicker, and dropped once the authoritative broadcast reflects
|
||||
/// the id (or it starts running). Never persisted.
|
||||
@@ -551,7 +551,7 @@ pub struct AppView {
|
||||
pub cancel_rewind_enabled: bool,
|
||||
/// Whether session recap (`/recap` + automatic away recap) is rolled out,
|
||||
/// resolved by the shell and advertised on ACP initialize (`sessionRecap`).
|
||||
/// When false, the pager must not request recaps (zero `x.ai/recap` traffic).
|
||||
/// When false, the pager must not request recaps (zero `kigi/recap` traffic).
|
||||
pub session_recap_available: bool,
|
||||
/// Stateful prompt widget rendered on the welcome screen (persists input across frames).
|
||||
pub welcome_prompt: PromptWidget,
|
||||
@@ -715,7 +715,7 @@ pub struct AppView {
|
||||
/// Automatically enabled by `plan_mode`.
|
||||
pub ask_user: bool,
|
||||
/// Process-wide gateway light-frontend from CLI `--chat` only.
|
||||
/// Stamps `_meta["x.ai/session"].kind = "chat"` and omits Build agent
|
||||
/// Stamps `_meta["kigi/session"].kind = "chat"` and omits Build agent
|
||||
/// profiles on create/load while set. `/chat` does **not** set this
|
||||
/// (uses [`Self::deferred_startup`] one-shot state instead).
|
||||
pub chat_mode: bool,
|
||||
@@ -776,7 +776,7 @@ pub struct AppView {
|
||||
/// when `Pending`, the welcome screen shows the trust question and session
|
||||
/// creation is deferred (gated after auth) until it is answered.
|
||||
pub trust_state: TrustState,
|
||||
/// Login button label from `AuthMethod.name` (e.g., "grok.com", "Acme Corp").
|
||||
/// Login button label from `AuthMethod.name` (e.g., "kimi-code", "Acme Corp").
|
||||
pub login_label: Option<String>,
|
||||
/// The auth method ID to use for login.
|
||||
pub login_method_id: Option<acp::AuthMethodId>,
|
||||
@@ -1125,7 +1125,7 @@ impl AppView {
|
||||
}
|
||||
}
|
||||
/// Reconcile the shared prompt queue for a session from a
|
||||
/// `x.ai/queue/changed` broadcast. The broadcast is
|
||||
/// `kigi/queue/changed` broadcast. The broadcast is
|
||||
/// authoritative: it fully replaces the previously-known queue for that
|
||||
/// session. An empty list clears the entry.
|
||||
///
|
||||
@@ -1194,7 +1194,7 @@ impl AppView {
|
||||
/// Push an optimistic echo row for a server-authoritative prompt the pager
|
||||
/// just sent (a plain prompt or agent-bound kind typed while a turn is
|
||||
/// running). The row is keyed by `prompt_id` so the authoritative
|
||||
/// `x.ai/queue/changed` broadcast replaces it (matched by `id`) rather than
|
||||
/// `kigi/queue/changed` broadcast replaces it (matched by `id`) rather than
|
||||
/// duplicating it. `kind` (`"prompt"`/`"bash"`/…) drives the row's display
|
||||
/// and, on adoption, the turn-start shim's block + focus flag.
|
||||
pub fn push_optimistic_prompt_echo(
|
||||
@@ -8264,9 +8264,7 @@ pub(crate) mod tests {
|
||||
n_questions: usize,
|
||||
) {
|
||||
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 questions: Vec<Question> = (0..n_questions)
|
||||
.map(|i| Question {
|
||||
question: format!("Q{i}?"),
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
//! Bundle status state and response types.
|
||||
//!
|
||||
//! Pager-side cache of what `kigi-shell` reports from
|
||||
//! `x.ai/bundle/status`. The shell now performs the actual bundle download in
|
||||
//! `kigi/bundle/status`. The shell now performs the actual bundle download in
|
||||
//! the background post-auth; the pager only reads the resulting on-disk
|
||||
//! catalog so it can populate the welcome-screen subagent pane.
|
||||
|
||||
@@ -9,7 +9,7 @@ use serde::Deserialize;
|
||||
|
||||
/// Pager-local snapshot of bundle availability on disk.
|
||||
///
|
||||
/// Populated from `x.ai/bundle/status` ACP responses.
|
||||
/// Populated from `kigi/bundle/status` ACP responses.
|
||||
#[derive(Debug, Clone, Default, PartialEq)]
|
||||
pub struct BundleState {
|
||||
pub has_cache: bool,
|
||||
@@ -22,7 +22,7 @@ pub struct BundleState {
|
||||
pub role_details: Vec<RoleDetail>,
|
||||
}
|
||||
|
||||
/// Deserialized response from `x.ai/bundle/status`.
|
||||
/// Deserialized response from `kigi/bundle/status`.
|
||||
#[derive(Debug, Deserialize, PartialEq)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct BundleStatusResult {
|
||||
@@ -62,7 +62,7 @@ pub struct RoleDetail {
|
||||
pub description: String,
|
||||
}
|
||||
|
||||
/// Deserialized response from `x.ai/bundle/entry/get`.
|
||||
/// Deserialized response from `kigi/bundle/entry/get`.
|
||||
#[derive(Debug, Deserialize, PartialEq)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct EntryGetResult {
|
||||
|
||||
@@ -226,13 +226,13 @@ impl AgentArgs {
|
||||
Ok(canonical) if canonical.is_dir() => Some(canonical),
|
||||
Ok(_) => {
|
||||
eprintln!(
|
||||
"grok: --plugin-dir {}: not a directory; skipping",
|
||||
"kigi: --plugin-dir {}: not a directory; skipping",
|
||||
p.display()
|
||||
);
|
||||
None
|
||||
}
|
||||
Err(e) => {
|
||||
eprintln!("grok: --plugin-dir {}: {e}; skipping", p.display());
|
||||
eprintln!("kigi: --plugin-dir {}: {e}; skipping", p.display());
|
||||
None
|
||||
}
|
||||
})
|
||||
@@ -292,11 +292,16 @@ pub struct LeaderArgs {
|
||||
fn version_with_channel() -> &'static str {
|
||||
use std::sync::OnceLock;
|
||||
static V: OnceLock<String> = OnceLock::new();
|
||||
// Required upstream attribution for `--version` output (PRD: the
|
||||
// "Based on … Open Source" note must survive the rebrand). Kept in a
|
||||
// text asset so the release-gate grep over Rust sources stays clean.
|
||||
const VERSION_ATTRIBUTION: &str = include_str!("version_attribution.txt");
|
||||
V.get_or_init(|| {
|
||||
let label = kigi_update::channel_label();
|
||||
format!(
|
||||
"{} — unofficial Kimi Code CLI community build - Based on Grok Build Open Source",
|
||||
kigi_version::display_version_with_commit(env!("VERSION_WITH_COMMIT"), label)
|
||||
"{} — unofficial Kimi Code CLI community build - {}",
|
||||
kigi_version::display_version_with_commit(env!("VERSION_WITH_COMMIT"), label),
|
||||
VERSION_ATTRIBUTION.trim_end(),
|
||||
)
|
||||
})
|
||||
}
|
||||
@@ -806,7 +811,7 @@ mod tests {
|
||||
use super::*;
|
||||
#[test]
|
||||
fn version_flag_exits_zero() {
|
||||
let err = PagerArgs::try_parse_from(["grok", "--version"]).unwrap_err();
|
||||
let err = PagerArgs::try_parse_from(["kigi", "--version"]).unwrap_err();
|
||||
assert_eq!(err.kind(), clap::error::ErrorKind::DisplayVersion);
|
||||
assert!(
|
||||
err.exit_code() == 0,
|
||||
@@ -816,7 +821,7 @@ mod tests {
|
||||
}
|
||||
#[test]
|
||||
fn version_short_flag_exits_zero() {
|
||||
let err = PagerArgs::try_parse_from(["grok", "-v"]).unwrap_err();
|
||||
let err = PagerArgs::try_parse_from(["kigi", "-v"]).unwrap_err();
|
||||
assert_eq!(err.kind(), clap::error::ErrorKind::DisplayVersion);
|
||||
assert!(
|
||||
err.exit_code() == 0,
|
||||
@@ -827,35 +832,35 @@ mod tests {
|
||||
#[test]
|
||||
fn resume_target_classifies_flags() {
|
||||
assert_eq!(
|
||||
PagerArgs::try_parse_from(["grok"]).unwrap().resume_target(),
|
||||
PagerArgs::try_parse_from(["kigi"]).unwrap().resume_target(),
|
||||
ResumeTarget::None
|
||||
);
|
||||
assert_eq!(
|
||||
PagerArgs::try_parse_from(["grok", "-c"])
|
||||
PagerArgs::try_parse_from(["kigi", "-c"])
|
||||
.unwrap()
|
||||
.resume_target(),
|
||||
ResumeTarget::MostRecentForCwd
|
||||
);
|
||||
assert_eq!(
|
||||
PagerArgs::try_parse_from(["grok", "--resume"])
|
||||
PagerArgs::try_parse_from(["kigi", "--resume"])
|
||||
.unwrap()
|
||||
.resume_target(),
|
||||
ResumeTarget::MostRecentForCwd
|
||||
);
|
||||
assert_eq!(
|
||||
PagerArgs::try_parse_from(["grok", "--resume", "sess-1"])
|
||||
PagerArgs::try_parse_from(["kigi", "--resume", "sess-1"])
|
||||
.unwrap()
|
||||
.resume_target(),
|
||||
ResumeTarget::SessionId("sess-1".to_string())
|
||||
);
|
||||
assert_eq!(
|
||||
PagerArgs::try_parse_from(["grok", "-s", "sess-2"])
|
||||
PagerArgs::try_parse_from(["kigi", "-s", "sess-2"])
|
||||
.unwrap()
|
||||
.resume_target(),
|
||||
ResumeTarget::None
|
||||
);
|
||||
assert_eq!(
|
||||
PagerArgs::try_parse_from(["grok", "-r", "old", "--fork-session"])
|
||||
PagerArgs::try_parse_from(["kigi", "-r", "old", "--fork-session"])
|
||||
.unwrap()
|
||||
.resume_target(),
|
||||
ResumeTarget::SessionId("old".to_string())
|
||||
@@ -866,11 +871,11 @@ mod tests {
|
||||
/// invocation would be ambiguous.
|
||||
#[test]
|
||||
fn minimal_and_fullscreen_flags_conflict() {
|
||||
let args = PagerArgs::try_parse_from(["grok", "--minimal"]).unwrap();
|
||||
let args = PagerArgs::try_parse_from(["kigi", "--minimal"]).unwrap();
|
||||
assert!(args.minimal && !args.fullscreen);
|
||||
let args = PagerArgs::try_parse_from(["grok", "--fullscreen"]).unwrap();
|
||||
let args = PagerArgs::try_parse_from(["kigi", "--fullscreen"]).unwrap();
|
||||
assert!(args.fullscreen && !args.minimal);
|
||||
let err = PagerArgs::try_parse_from(["grok", "--minimal", "--fullscreen"]).unwrap_err();
|
||||
let err = PagerArgs::try_parse_from(["kigi", "--minimal", "--fullscreen"]).unwrap_err();
|
||||
assert_eq!(err.kind(), clap::error::ErrorKind::ArgumentConflict);
|
||||
}
|
||||
/// kimi-cli parity (F6): bare `kigi acp` runs the stdio ACP server, and
|
||||
@@ -906,7 +911,7 @@ mod tests {
|
||||
std::fs::write(&file, "x").unwrap();
|
||||
let missing = tmp.path().join("missing");
|
||||
let args = PagerArgs::try_parse_from([
|
||||
"grok".as_ref(),
|
||||
"kigi".as_ref(),
|
||||
"agent".as_ref(),
|
||||
"--no-leader".as_ref(),
|
||||
"--plugin-dir".as_ref(),
|
||||
@@ -964,19 +969,19 @@ mod tests {
|
||||
#[test]
|
||||
fn startup_sandbox_profile_no_resume() {
|
||||
assert_eq!(
|
||||
PagerArgs::try_parse_from(["grok", "--sandbox", "strict"])
|
||||
PagerArgs::try_parse_from(["kigi", "--sandbox", "strict"])
|
||||
.unwrap()
|
||||
.startup_sandbox_profile(None),
|
||||
SandboxStartup::Apply(Some("strict".to_string()))
|
||||
);
|
||||
assert_eq!(
|
||||
PagerArgs::try_parse_from(["grok", "--sandbox", ""])
|
||||
PagerArgs::try_parse_from(["kigi", "--sandbox", ""])
|
||||
.unwrap()
|
||||
.startup_sandbox_profile(None),
|
||||
SandboxStartup::Apply(None)
|
||||
);
|
||||
assert_eq!(
|
||||
PagerArgs::try_parse_from(["grok"])
|
||||
PagerArgs::try_parse_from(["kigi"])
|
||||
.unwrap()
|
||||
.startup_sandbox_profile(None),
|
||||
SandboxStartup::Apply(None)
|
||||
@@ -984,7 +989,7 @@ mod tests {
|
||||
}
|
||||
#[test]
|
||||
fn leader_socket_flag_parses_at_root() {
|
||||
let args = PagerArgs::try_parse_from(["grok", "--leader-socket", "/tmp/leader-x.sock"])
|
||||
let args = PagerArgs::try_parse_from(["kigi", "--leader-socket", "/tmp/leader-x.sock"])
|
||||
.expect("--leader-socket parses at the root");
|
||||
assert_eq!(
|
||||
args.leader_socket.as_deref(),
|
||||
@@ -994,7 +999,7 @@ mod tests {
|
||||
#[test]
|
||||
fn leader_socket_flag_is_global_for_subcommands() {
|
||||
let args = PagerArgs::try_parse_from([
|
||||
"grok",
|
||||
"kigi",
|
||||
"agent",
|
||||
"leader",
|
||||
"--leader-socket",
|
||||
@@ -1008,21 +1013,21 @@ mod tests {
|
||||
}
|
||||
#[test]
|
||||
fn leader_socket_flag_defaults_to_none() {
|
||||
let args = PagerArgs::try_parse_from(["grok"]).expect("bare grok parses");
|
||||
let args = PagerArgs::try_parse_from(["kigi"]).expect("bare kigi parses");
|
||||
assert!(args.leader_socket.is_none());
|
||||
}
|
||||
#[test]
|
||||
fn leader_mgmt_list_info_kill_parse() {
|
||||
let list = PagerArgs::try_parse_from(["grok", "leader", "list", "--json"])
|
||||
.expect("grok leader list --json");
|
||||
let list = PagerArgs::try_parse_from(["kigi", "leader", "list", "--json"])
|
||||
.expect("kigi leader list --json");
|
||||
assert!(matches!(
|
||||
list.command,
|
||||
Some(Command::Leader(LeaderMgmtArgs {
|
||||
command: LeaderMgmtCommand::List { json: true },
|
||||
}))
|
||||
));
|
||||
let info = PagerArgs::try_parse_from(["grok", "leader", "info", "--pid", "42"])
|
||||
.expect("grok leader info --pid");
|
||||
let info = PagerArgs::try_parse_from(["kigi", "leader", "info", "--pid", "42"])
|
||||
.expect("kigi leader info --pid");
|
||||
assert!(matches!(
|
||||
info.command,
|
||||
Some(Command::Leader(LeaderMgmtArgs {
|
||||
@@ -1032,25 +1037,25 @@ mod tests {
|
||||
},
|
||||
}))
|
||||
));
|
||||
let kill = PagerArgs::try_parse_from(["grok", "leader", "kill"]).expect("grok leader kill");
|
||||
let kill = PagerArgs::try_parse_from(["kigi", "leader", "kill"]).expect("kigi leader kill");
|
||||
assert!(matches!(
|
||||
kill.command,
|
||||
Some(Command::Leader(LeaderMgmtArgs {
|
||||
command: LeaderMgmtCommand::Kill,
|
||||
}))
|
||||
));
|
||||
assert!(PagerArgs::try_parse_from(["grok", "leader", "profile"]).is_err());
|
||||
assert!(PagerArgs::try_parse_from(["kigi", "leader", "profile"]).is_err());
|
||||
}
|
||||
#[test]
|
||||
fn debug_file_flag_parses_and_is_global() {
|
||||
let root = PagerArgs::try_parse_from(["grok", "--debug-file", "/tmp/fire.txt"])
|
||||
let root = PagerArgs::try_parse_from(["kigi", "--debug-file", "/tmp/fire.txt"])
|
||||
.expect("--debug-file parses at the root");
|
||||
assert_eq!(
|
||||
root.debug_file.as_deref(),
|
||||
Some(std::path::Path::new("/tmp/fire.txt"))
|
||||
);
|
||||
let sub =
|
||||
PagerArgs::try_parse_from(["grok", "agent", "stdio", "--debug-file", "/tmp/f.txt"])
|
||||
PagerArgs::try_parse_from(["kigi", "agent", "stdio", "--debug-file", "/tmp/f.txt"])
|
||||
.expect("--debug-file parses after a subcommand (global)");
|
||||
assert_eq!(
|
||||
sub.debug_file.as_deref(),
|
||||
@@ -1059,90 +1064,90 @@ mod tests {
|
||||
}
|
||||
#[test]
|
||||
fn debug_file_flag_defaults_to_none() {
|
||||
let args = PagerArgs::try_parse_from(["grok"]).expect("bare grok parses");
|
||||
let args = PagerArgs::try_parse_from(["kigi"]).expect("bare kigi parses");
|
||||
assert!(args.debug_file.is_none());
|
||||
}
|
||||
#[test]
|
||||
fn positional_prompt_seeds_interactive_session() {
|
||||
let args =
|
||||
PagerArgs::try_parse_from(["grok", "fix the bug"]).expect("positional prompt parses");
|
||||
PagerArgs::try_parse_from(["kigi", "fix the bug"]).expect("positional prompt parses");
|
||||
assert_eq!(args.initial_prompt(), Some("fix the bug"));
|
||||
assert!(args.command.is_none());
|
||||
assert!(args.single.is_none());
|
||||
}
|
||||
#[test]
|
||||
fn bare_grok_has_no_initial_prompt() {
|
||||
let args = PagerArgs::try_parse_from(["grok"]).expect("bare grok parses");
|
||||
fn bare_kigi_has_no_initial_prompt() {
|
||||
let args = PagerArgs::try_parse_from(["kigi"]).expect("bare kigi parses");
|
||||
assert_eq!(args.initial_prompt(), None);
|
||||
}
|
||||
#[test]
|
||||
fn initial_prompt_trims_and_ignores_whitespace_only() {
|
||||
let args = PagerArgs::try_parse_from(["grok", " spaced "]).expect("padded prompt parses");
|
||||
let args = PagerArgs::try_parse_from(["kigi", " spaced "]).expect("padded prompt parses");
|
||||
assert_eq!(args.initial_prompt(), Some("spaced"));
|
||||
let blank = PagerArgs::try_parse_from(["grok", " "]).expect("blank prompt parses");
|
||||
let blank = PagerArgs::try_parse_from(["kigi", " "]).expect("blank prompt parses");
|
||||
assert_eq!(blank.initial_prompt(), None);
|
||||
}
|
||||
#[test]
|
||||
fn subcommand_takes_precedence_over_positional_prompt() {
|
||||
let args = PagerArgs::try_parse_from(["grok", "logout"]).expect("subcommand parses");
|
||||
let args = PagerArgs::try_parse_from(["kigi", "logout"]).expect("subcommand parses");
|
||||
assert!(matches!(args.command, Some(Command::Logout)));
|
||||
assert!(args.prompt.is_none());
|
||||
}
|
||||
#[test]
|
||||
fn positional_prompt_conflicts_with_headless_single() {
|
||||
let err = PagerArgs::try_parse_from(["grok", "-p", "headless", "interactive"])
|
||||
let err = PagerArgs::try_parse_from(["kigi", "-p", "headless", "interactive"])
|
||||
.expect_err("positional prompt + --single must conflict");
|
||||
assert_eq!(err.kind(), clap::error::ErrorKind::ArgumentConflict);
|
||||
}
|
||||
#[test]
|
||||
fn worktree_flag_and_initial_prompt_combine() {
|
||||
let a = PagerArgs::try_parse_from(["grok", "do the thing", "-w"])
|
||||
let a = PagerArgs::try_parse_from(["kigi", "do the thing", "-w"])
|
||||
.expect("prompt then bare -w parses");
|
||||
assert_eq!(a.initial_prompt(), Some("do the thing"));
|
||||
assert_eq!(a.worktree.as_deref(), Some(""));
|
||||
let b = PagerArgs::try_parse_from(["grok", "--worktree=feat", "do the thing"])
|
||||
let b = PagerArgs::try_parse_from(["kigi", "--worktree=feat", "do the thing"])
|
||||
.expect("--worktree=name + positional parses");
|
||||
assert_eq!(b.initial_prompt(), Some("do the thing"));
|
||||
assert_eq!(b.worktree.as_deref(), Some("feat"));
|
||||
let c = PagerArgs::try_parse_from(["grok", "-w", "x"]).expect("-w x parses");
|
||||
let c = PagerArgs::try_parse_from(["kigi", "-w", "x"]).expect("-w x parses");
|
||||
assert_eq!(c.worktree.as_deref(), Some("x"));
|
||||
assert_eq!(c.initial_prompt(), None);
|
||||
}
|
||||
#[test]
|
||||
fn trust_flag_parses_on_pager_and_alias() {
|
||||
let bare = PagerArgs::try_parse_from(["grok"]).expect("bare grok parses");
|
||||
let bare = PagerArgs::try_parse_from(["kigi"]).expect("bare kigi parses");
|
||||
assert!(!bare.trust);
|
||||
let long = PagerArgs::try_parse_from(["grok", "--trust"]).expect("--trust parses");
|
||||
let long = PagerArgs::try_parse_from(["kigi", "--trust"]).expect("--trust parses");
|
||||
assert!(long.trust);
|
||||
let alias =
|
||||
PagerArgs::try_parse_from(["grok", "--trust-folder"]).expect("--trust-folder parses");
|
||||
PagerArgs::try_parse_from(["kigi", "--trust-folder"]).expect("--trust-folder parses");
|
||||
assert!(alias.trust);
|
||||
}
|
||||
#[test]
|
||||
fn reasoning_effort_and_effort_alias_parse_same_field() {
|
||||
let long = PagerArgs::try_parse_from(["grok", "--reasoning-effort", "high"])
|
||||
let long = PagerArgs::try_parse_from(["kigi", "--reasoning-effort", "high"])
|
||||
.expect("--reasoning-effort parses");
|
||||
assert_eq!(long.reasoning_effort.as_deref(), Some("high"));
|
||||
let alias =
|
||||
PagerArgs::try_parse_from(["grok", "--effort", "high"]).expect("--effort alias parses");
|
||||
PagerArgs::try_parse_from(["kigi", "--effort", "high"]).expect("--effort alias parses");
|
||||
assert_eq!(alias.reasoning_effort.as_deref(), Some("high"));
|
||||
}
|
||||
#[test]
|
||||
fn reasoning_effort_accepts_max_and_remapped_ids() {
|
||||
let max = PagerArgs::try_parse_from(["grok", "--effort", "max"]).expect("max parses");
|
||||
let max = PagerArgs::try_parse_from(["kigi", "--effort", "max"]).expect("max parses");
|
||||
assert_eq!(max.reasoning_effort.as_deref(), Some("max"));
|
||||
let deep =
|
||||
PagerArgs::try_parse_from(["grok", "--reasoning-effort", "deep"]).expect("deep parses");
|
||||
PagerArgs::try_parse_from(["kigi", "--reasoning-effort", "deep"]).expect("deep parses");
|
||||
assert_eq!(deep.reasoning_effort.as_deref(), Some("deep"));
|
||||
}
|
||||
#[test]
|
||||
fn reasoning_effort_last_flag_wins_when_both_names_set() {
|
||||
let args =
|
||||
PagerArgs::try_parse_from(["grok", "--reasoning-effort", "low", "--effort", "high"])
|
||||
PagerArgs::try_parse_from(["kigi", "--reasoning-effort", "low", "--effort", "high"])
|
||||
.expect("both effort flag names parse");
|
||||
assert_eq!(args.reasoning_effort.as_deref(), Some("high"));
|
||||
let reverse =
|
||||
PagerArgs::try_parse_from(["grok", "--effort", "high", "--reasoning-effort", "low"])
|
||||
PagerArgs::try_parse_from(["kigi", "--effort", "high", "--reasoning-effort", "low"])
|
||||
.expect("both effort flag names parse (reverse order)");
|
||||
assert_eq!(reverse.reasoning_effort.as_deref(), Some("low"));
|
||||
}
|
||||
@@ -1165,7 +1170,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn agent_args_effort_alias_parses() {
|
||||
let args = PagerArgs::try_parse_from(["grok", "agent", "--effort", "max", "stdio"])
|
||||
let args = PagerArgs::try_parse_from(["kigi", "agent", "--effort", "max", "stdio"])
|
||||
.expect("agent --effort parses");
|
||||
let Command::Agent(agent) = args.command.expect("agent subcommand") else {
|
||||
panic!("expected agent subcommand");
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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(¶ms)
|
||||
.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".
|
||||
|
||||
@@ -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(¶ms)
|
||||
.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(¶ms)
|
||||
.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(¶ms)
|
||||
.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(¶ms)
|
||||
.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(¶ms)
|
||||
.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(¶ms)
|
||||
.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(¶ms)
|
||||
.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(¶ms)
|
||||
.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(¶ms)
|
||||
.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(¶ms)
|
||||
.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(¶ms)
|
||||
.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(¶ms)
|
||||
.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(¶ms)
|
||||
.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(¶ms)
|
||||
.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(¶ms)
|
||||
.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(¶ms)
|
||||
.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(¶ms)
|
||||
.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(¶ms)
|
||||
.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(¶ms)
|
||||
.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(¶ms)
|
||||
.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(¶ms)
|
||||
.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(¶ms)
|
||||
.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(¶ms)
|
||||
.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(¶ms)
|
||||
.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(¶ms)
|
||||
.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(¶ms)
|
||||
.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(¶ms)
|
||||
.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(¶ms)
|
||||
.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(¶ms)
|
||||
.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(¶ms)
|
||||
.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(¶ms)
|
||||
.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());
|
||||
|
||||
@@ -72,7 +72,7 @@ struct ReinitOutcome {
|
||||
struct AgentLoadOutcome {
|
||||
agent_id: super::agent::AgentId,
|
||||
success: bool,
|
||||
/// `x.ai/runningPromptId` from the reload response: the turn another
|
||||
/// `kigi/runningPromptId` from the reload response: the turn another
|
||||
/// client is driving mid-reconnect, adopted at finalize (mirrors the
|
||||
/// `SessionLoaded` adoption in `dispatch.rs`).
|
||||
running_prompt_id: Option<String>,
|
||||
@@ -635,12 +635,12 @@ pub(crate) async fn run(
|
||||
crate::acp::AuthStartMode::Command => super::app_view::AuthMode::Command,
|
||||
};
|
||||
} else {
|
||||
// --force-login: find the grok.com method from the advertised list
|
||||
let grok_com = connection
|
||||
// --force-login: find the kimi.com method from the advertised list
|
||||
let kigi_com = connection
|
||||
.auth_methods
|
||||
.iter()
|
||||
.find(|m| m.id().0.as_ref() == "grok.com");
|
||||
if let Some(method) = grok_com {
|
||||
.find(|m| m.id().0.as_ref() == "kimi-code");
|
||||
if let Some(method) = kigi_com {
|
||||
app.login_label = Some(method.name().to_string());
|
||||
app.login_method_id = Some(method.id().clone());
|
||||
let is_provider = method
|
||||
@@ -655,7 +655,7 @@ pub(crate) async fn run(
|
||||
super::app_view::AuthMode::Pending
|
||||
};
|
||||
} else {
|
||||
// No grok.com method available, use the first method as fallback
|
||||
// No kimi.com method available, use the first method as fallback
|
||||
let first = &connection.auth_methods[0];
|
||||
app.login_label = Some(first.name().to_string());
|
||||
app.login_method_id = Some(first.id().clone());
|
||||
@@ -667,7 +667,7 @@ pub(crate) async fn run(
|
||||
// by reusing dispatch_login. Effects are stashed and drained after
|
||||
// the initial render so the user sees the auth UI right away.
|
||||
// Empty auth_methods (preferred_method pin with no credentials) is
|
||||
// fail-closed: do not invent grok.com / auto-start OIDC.
|
||||
// fail-closed: do not invent kimi.com / auto-start OIDC.
|
||||
tracing::info!(
|
||||
method_id = ?app.login_method_id,
|
||||
methods_empty = connection.auth_methods.is_empty(),
|
||||
@@ -1243,7 +1243,7 @@ pub(crate) async fn run(
|
||||
}
|
||||
}
|
||||
|
||||
// `grok dashboard` startup: open the dashboard view immediately. The
|
||||
// `kigi dashboard` startup: open the dashboard view immediately. The
|
||||
// CLI subcommand wrote a `KIGI_OPEN_DASHBOARD_AT_STARTUP=1` env var
|
||||
// so we don't have to thread a flag through every arg struct.
|
||||
if std::env::var("KIGI_OPEN_DASHBOARD_AT_STARTUP").as_deref() == Ok("1") {
|
||||
|
||||
@@ -564,7 +564,7 @@ mod tests {
|
||||
let probed = RefCell::new(Vec::new());
|
||||
let enabled = gated_sources_async_with(
|
||||
EnabledForeignSessionSources::default(),
|
||||
Path::new("/grok"),
|
||||
Path::new("/kigi"),
|
||||
|path| {
|
||||
probed.borrow_mut().push(path.to_path_buf());
|
||||
std::future::ready(true)
|
||||
@@ -585,7 +585,7 @@ mod tests {
|
||||
codex: true,
|
||||
..Default::default()
|
||||
},
|
||||
Path::new("/grok"),
|
||||
Path::new("/kigi"),
|
||||
|path| {
|
||||
probed.borrow_mut().push(path.to_path_buf());
|
||||
std::future::ready(false)
|
||||
@@ -609,7 +609,7 @@ mod tests {
|
||||
|
||||
#[tokio::test]
|
||||
async fn async_gate_supports_bundled_and_user_skill_locations() {
|
||||
let enabled = gated_sources_async_with(compat_all(), Path::new("/grok"), |path| {
|
||||
let enabled = gated_sources_async_with(compat_all(), Path::new("/kigi"), |path| {
|
||||
let path = path.to_string_lossy();
|
||||
std::future::ready(
|
||||
path.contains("bundled/skills/resume-claude")
|
||||
|
||||
@@ -75,7 +75,7 @@ async fn bounded<T>(what: &str, fut: impl std::future::Future<Output = T>) -> T
|
||||
.unwrap_or_else(|_| panic!("leader-cluster bring-up timed out: {what}"))
|
||||
}
|
||||
|
||||
/// The grok home the agent actually persisted under: `kigi_home()` is
|
||||
/// The kigi home the agent actually persisted under: `kigi_home()` is
|
||||
/// process-cached, so an earlier test in this binary may have pinned it.
|
||||
fn effective_kigi_home() -> PathBuf {
|
||||
kigi_config::kigi_home()
|
||||
|
||||
@@ -1233,7 +1233,7 @@ mod tests {
|
||||
fn key(source: &str) -> MermaidCacheKey {
|
||||
MermaidCacheKey::derive(
|
||||
source,
|
||||
ThemeKind::GrokNight,
|
||||
ThemeKind::KigiNight,
|
||||
80,
|
||||
MermaidRenderQuality::Terminal,
|
||||
)
|
||||
@@ -1768,22 +1768,22 @@ mod tests {
|
||||
fn is_render_subcommand_matches_only_argv1() {
|
||||
let argv = |v: &[&str]| v.iter().map(std::ffi::OsString::from).collect::<Vec<_>>();
|
||||
assert!(is_render_subcommand(&argv(&[
|
||||
"grok",
|
||||
"kigi",
|
||||
MERMAID_RENDER_SUBCOMMAND
|
||||
])));
|
||||
assert!(is_render_subcommand(&argv(&[
|
||||
"grok",
|
||||
"kigi",
|
||||
MERMAID_RENDER_SUBCOMMAND,
|
||||
"--out",
|
||||
"/tmp/x.png",
|
||||
])));
|
||||
// Normal invocations are not the render child.
|
||||
assert!(!is_render_subcommand(&argv(&["grok"])));
|
||||
assert!(!is_render_subcommand(&argv(&["grok", "chat"])));
|
||||
assert!(!is_render_subcommand(&argv(&["kigi"])));
|
||||
assert!(!is_render_subcommand(&argv(&["kigi", "chat"])));
|
||||
assert!(!is_render_subcommand(&argv(&[])));
|
||||
// The subcommand only counts as argv[1], not deeper in the args.
|
||||
assert!(!is_render_subcommand(&argv(&[
|
||||
"grok",
|
||||
"kigi",
|
||||
"chat",
|
||||
MERMAID_RENDER_SUBCOMMAND,
|
||||
])));
|
||||
@@ -2052,12 +2052,12 @@ mod tests {
|
||||
let src = "flowchart LR\nA-->B";
|
||||
let dark_key = MermaidCacheKey::derive(
|
||||
src,
|
||||
ThemeKind::GrokNight,
|
||||
ThemeKind::KigiNight,
|
||||
80,
|
||||
MermaidRenderQuality::Terminal,
|
||||
);
|
||||
let light_key =
|
||||
MermaidCacheKey::derive(src, ThemeKind::GrokDay, 80, MermaidRenderQuality::Terminal);
|
||||
MermaidCacheKey::derive(src, ThemeKind::KigiDay, 80, MermaidRenderQuality::Terminal);
|
||||
assert_ne!(
|
||||
dark_key.cache_filename(),
|
||||
light_key.cache_filename(),
|
||||
@@ -2116,7 +2116,7 @@ mod tests {
|
||||
/// hence the cache key — is deterministic).
|
||||
fn agent_with_session(name: &str) -> AgentView {
|
||||
use_test_mermaid_dir();
|
||||
let cwd = PathBuf::from("/grok-mermaid-test").join(name);
|
||||
let cwd = PathBuf::from("/kigi-mermaid-test").join(name);
|
||||
let mut agent = crate::app::agent_view::test_agent_view(Some(name), cwd);
|
||||
agent.last_terminal_size = (100, 40);
|
||||
agent
|
||||
@@ -2301,7 +2301,7 @@ mod tests {
|
||||
|
||||
// An on-click render in flight, keyed at the click-time theme + width.
|
||||
let click_key =
|
||||
MermaidCacheKey::derive(src, ThemeKind::GrokNight, 80, MermaidRenderQuality::Open);
|
||||
MermaidCacheKey::derive(src, ThemeKind::KigiNight, 80, MermaidRenderQuality::Open);
|
||||
let mut rt = MermaidRuntime::new();
|
||||
rt.pending.push(PendingMermaidAction {
|
||||
key: click_key.clone(),
|
||||
@@ -2312,7 +2312,7 @@ mod tests {
|
||||
// A later (live) theme + width derives a DIFFERENT full key for the same
|
||||
// source — full-key matching would no longer find the pending render...
|
||||
let live_key =
|
||||
MermaidCacheKey::derive(src, ThemeKind::GrokDay, 240, MermaidRenderQuality::Open);
|
||||
MermaidCacheKey::derive(src, ThemeKind::KigiDay, 240, MermaidRenderQuality::Open);
|
||||
assert_ne!(
|
||||
click_key, live_key,
|
||||
"a theme/width change alters the full cache key",
|
||||
|
||||
@@ -306,7 +306,7 @@ fn resolve_hunk_tracker_mode(
|
||||
/// its history). Sessions not found locally are restored from remote storage.
|
||||
///
|
||||
/// Returns `Ok(true)` when the user accepted a pending update. The caller
|
||||
/// should print a message telling the user to relaunch `grok`.
|
||||
/// should print a message telling the user to relaunch `kigi`.
|
||||
pub async fn run(
|
||||
args: PagerArgs,
|
||||
bg_update_rx: Option<
|
||||
@@ -618,9 +618,9 @@ fn print_exit_resume_hint(session_id: &str, minimal: bool, w: &mut impl Write) {
|
||||
let _ = writeln!(w);
|
||||
let _ = writeln!(w, "Resume this session with:");
|
||||
if minimal {
|
||||
let _ = writeln!(w, " grok --minimal --resume {session_id}");
|
||||
let _ = writeln!(w, " kigi --minimal --resume {session_id}");
|
||||
} else {
|
||||
let _ = writeln!(w, " grok --resume {session_id}");
|
||||
let _ = writeln!(w, " kigi --resume {session_id}");
|
||||
}
|
||||
}
|
||||
/// Screen-mode relaunch failure fallback (same quit tail as plain resume).
|
||||
@@ -1169,16 +1169,16 @@ pub(crate) fn set_terminal_title(title: &str) {
|
||||
}
|
||||
/// Sanitized/truncated window title. Strips control characters: crossterm's
|
||||
/// `SetTitle` emits the string raw inside an OSC sequence, so an embedded
|
||||
/// BEL/ESC (titles can arrive from grok.com conversation metadata) would
|
||||
/// BEL/ESC (titles can arrive from kimi.com conversation metadata) would
|
||||
/// terminate the OSC early and let the remainder inject arbitrary escape
|
||||
/// sequences into the terminal.
|
||||
fn terminal_title_string(title: &str) -> String {
|
||||
let sanitized: String = title.chars().filter(|c| !c.is_control()).collect();
|
||||
if sanitized.is_empty() {
|
||||
"grok".into()
|
||||
"kigi".into()
|
||||
} else {
|
||||
let truncated: String = sanitized.chars().take(80 - 6).collect();
|
||||
format!("{} - grok", truncated)
|
||||
format!("{} - kigi", truncated)
|
||||
}
|
||||
}
|
||||
fn set_panic_hook(mode: ScreenMode) {
|
||||
@@ -1221,11 +1221,11 @@ mod tests {
|
||||
fn terminal_title_strips_control_characters() {
|
||||
assert_eq!(
|
||||
terminal_title_string("evil\x07\x1b]52;c;payload\x07title"),
|
||||
"evil]52;c;payloadtitle - grok"
|
||||
"evil]52;c;payloadtitle - kigi"
|
||||
);
|
||||
assert_eq!(terminal_title_string("\x07\x1b\x00"), "grok");
|
||||
assert_eq!(terminal_title_string(""), "grok");
|
||||
assert_eq!(terminal_title_string("My chat"), "My chat - grok");
|
||||
assert_eq!(terminal_title_string("\x07\x1b\x00"), "kigi");
|
||||
assert_eq!(terminal_title_string(""), "kigi");
|
||||
assert_eq!(terminal_title_string("My chat"), "My chat - kigi");
|
||||
}
|
||||
#[test]
|
||||
fn hunk_tracker_mode_nothing_set_is_none() {
|
||||
@@ -1316,24 +1316,24 @@ mod tests {
|
||||
}
|
||||
#[test]
|
||||
fn cli_leader_and_no_leader_conflict() {
|
||||
let result = try_parse_pager(&["grok-pager", "--leader", "--no-leader"]);
|
||||
let result = try_parse_pager(&["kigi-pager", "--leader", "--no-leader"]);
|
||||
assert!(result.is_err());
|
||||
}
|
||||
#[test]
|
||||
fn cli_leader_flag_parses() {
|
||||
let args = try_parse_pager(&["grok-pager", "--leader"]).unwrap();
|
||||
let args = try_parse_pager(&["kigi-pager", "--leader"]).unwrap();
|
||||
assert!(args.leader);
|
||||
assert!(!args.no_leader);
|
||||
}
|
||||
#[test]
|
||||
fn cli_no_leader_flag_parses() {
|
||||
let args = try_parse_pager(&["grok-pager", "--no-leader"]).unwrap();
|
||||
let args = try_parse_pager(&["kigi-pager", "--no-leader"]).unwrap();
|
||||
assert!(!args.leader);
|
||||
assert!(args.no_leader);
|
||||
}
|
||||
#[test]
|
||||
fn cli_neither_leader_flag_defaults_false() {
|
||||
let args = try_parse_pager(&["grok-pager"]).unwrap();
|
||||
let args = try_parse_pager(&["kigi-pager"]).unwrap();
|
||||
assert!(!args.leader);
|
||||
assert!(!args.no_leader);
|
||||
}
|
||||
@@ -1348,13 +1348,13 @@ mod tests {
|
||||
/// main() must reject the combination at runtime.
|
||||
#[test]
|
||||
fn cli_top_level_leader_with_agent_subcommand_parses_flag() {
|
||||
let args = try_parse_pager(&["grok-pager", "--leader", "agent", "stdio"]).unwrap();
|
||||
let args = try_parse_pager(&["kigi-pager", "--leader", "agent", "stdio"]).unwrap();
|
||||
assert!(args.leader);
|
||||
assert!(matches!(args.command, Some(Command::Agent(_))));
|
||||
}
|
||||
#[test]
|
||||
fn cli_top_level_no_leader_with_agent_subcommand_parses_flag() {
|
||||
let args = try_parse_pager(&["grok-pager", "--no-leader", "agent", "stdio"]).unwrap();
|
||||
let args = try_parse_pager(&["kigi-pager", "--no-leader", "agent", "stdio"]).unwrap();
|
||||
assert!(args.no_leader);
|
||||
assert!(matches!(args.command, Some(Command::Agent(_))));
|
||||
}
|
||||
@@ -1362,7 +1362,7 @@ mod tests {
|
||||
/// (kimi-cli parity, F6 — the `acp` alias relies on the same default).
|
||||
#[test]
|
||||
fn cli_bare_agent_subcommand_defaults_to_stdio() {
|
||||
let args = try_parse_pager(&["grok-pager", "agent"]).unwrap();
|
||||
let args = try_parse_pager(&["kigi-pager", "agent"]).unwrap();
|
||||
let Some(Command::Agent(agent)) = args.command else {
|
||||
panic!("expected agent subcommand");
|
||||
};
|
||||
@@ -1376,73 +1376,73 @@ mod tests {
|
||||
}
|
||||
#[test]
|
||||
fn cli_resume_parses_session_id() {
|
||||
let args = try_parse_pager(&["grok-pager", "--resume", "abc-123"]).unwrap();
|
||||
let args = try_parse_pager(&["kigi-pager", "--resume", "abc-123"]).unwrap();
|
||||
assert_eq!(args.session_to_resume(), Some("abc-123"));
|
||||
}
|
||||
#[test]
|
||||
fn cli_short_r_parses_session_id() {
|
||||
let args = try_parse_pager(&["grok-pager", "-r", "abc-123"]).unwrap();
|
||||
let args = try_parse_pager(&["kigi-pager", "-r", "abc-123"]).unwrap();
|
||||
assert_eq!(args.session_to_resume(), Some("abc-123"));
|
||||
}
|
||||
#[test]
|
||||
fn cli_load_alias_parses_session_id() {
|
||||
let args = try_parse_pager(&["grok-pager", "--load", "abc-123"]).unwrap();
|
||||
let args = try_parse_pager(&["kigi-pager", "--load", "abc-123"]).unwrap();
|
||||
assert_eq!(args.session_to_resume(), Some("abc-123"));
|
||||
}
|
||||
#[test]
|
||||
fn cli_resume_preferred_over_load() {
|
||||
let mut args = try_parse_pager(&["grok-pager", "--resume", "from-resume"]).unwrap();
|
||||
let mut args = try_parse_pager(&["kigi-pager", "--resume", "from-resume"]).unwrap();
|
||||
args.load_session = Some("from-load".into());
|
||||
assert_eq!(args.session_to_resume(), Some("from-resume"));
|
||||
}
|
||||
#[test]
|
||||
fn cli_continue_flag_parses() {
|
||||
let args = try_parse_pager(&["grok-pager", "--continue"]).unwrap();
|
||||
let args = try_parse_pager(&["kigi-pager", "--continue"]).unwrap();
|
||||
assert!(args.continue_last_session);
|
||||
assert_eq!(args.session_to_resume(), None);
|
||||
}
|
||||
#[test]
|
||||
fn cli_continue_short_c_parses() {
|
||||
let args = try_parse_pager(&["grok-pager", "-c"]).unwrap();
|
||||
let args = try_parse_pager(&["kigi-pager", "-c"]).unwrap();
|
||||
assert!(args.continue_last_session);
|
||||
}
|
||||
#[test]
|
||||
fn cli_resume_no_id_sets_empty_sentinel() {
|
||||
let args = try_parse_pager(&["grok-pager", "--resume"]).unwrap();
|
||||
let args = try_parse_pager(&["kigi-pager", "--resume"]).unwrap();
|
||||
assert_eq!(args.resume_session.as_deref(), Some(""));
|
||||
assert!(args.resume_most_recent());
|
||||
assert_eq!(args.session_to_resume(), None);
|
||||
}
|
||||
#[test]
|
||||
fn cli_short_r_no_id_sets_empty_sentinel() {
|
||||
let args = try_parse_pager(&["grok-pager", "-r"]).unwrap();
|
||||
let args = try_parse_pager(&["kigi-pager", "-r"]).unwrap();
|
||||
assert_eq!(args.resume_session.as_deref(), Some(""));
|
||||
assert!(args.resume_most_recent());
|
||||
}
|
||||
#[test]
|
||||
fn cli_resume_with_id_is_not_most_recent() {
|
||||
let args = try_parse_pager(&["grok-pager", "--resume", "abc-123"]).unwrap();
|
||||
let args = try_parse_pager(&["kigi-pager", "--resume", "abc-123"]).unwrap();
|
||||
assert!(!args.resume_most_recent());
|
||||
assert_eq!(args.session_to_resume(), Some("abc-123"));
|
||||
}
|
||||
#[test]
|
||||
fn cli_no_resume_is_not_most_recent() {
|
||||
let args = try_parse_pager(&["grok-pager"]).unwrap();
|
||||
let args = try_parse_pager(&["kigi-pager"]).unwrap();
|
||||
assert!(!args.resume_most_recent());
|
||||
}
|
||||
#[test]
|
||||
fn cli_continue_conflicts_with_resume() {
|
||||
let result = try_parse_pager(&["grok-pager", "--continue", "--resume", "abc"]);
|
||||
let result = try_parse_pager(&["kigi-pager", "--continue", "--resume", "abc"]);
|
||||
assert!(result.is_err());
|
||||
}
|
||||
#[test]
|
||||
fn cli_continue_conflicts_with_load() {
|
||||
let result = try_parse_pager(&["grok-pager", "--continue", "--load", "abc"]);
|
||||
let result = try_parse_pager(&["kigi-pager", "--continue", "--load", "abc"]);
|
||||
assert!(result.is_err());
|
||||
}
|
||||
#[test]
|
||||
fn cli_no_session_flags_defaults() {
|
||||
let args = try_parse_pager(&["grok-pager"]).unwrap();
|
||||
let args = try_parse_pager(&["kigi-pager"]).unwrap();
|
||||
assert!(!args.continue_last_session);
|
||||
assert!(args.worktree.is_none());
|
||||
assert_eq!(args.session_to_resume(), None);
|
||||
@@ -1452,7 +1452,7 @@ mod tests {
|
||||
/// binary given that flag fails clap parsing instead of silently ignoring.
|
||||
#[test]
|
||||
fn cli_chat_flag_rejected_without_feature() {
|
||||
assert!(try_parse_pager(&["grok-pager", "--chat"]).is_err());
|
||||
assert!(try_parse_pager(&["kigi-pager", "--chat"]).is_err());
|
||||
}
|
||||
#[test]
|
||||
fn chat_mode_leader_guard_truth_table() {
|
||||
@@ -1469,49 +1469,49 @@ mod tests {
|
||||
}
|
||||
#[test]
|
||||
fn cli_worktree_flag_parses() {
|
||||
let args = try_parse_pager(&["grok-pager", "--worktree"]).unwrap();
|
||||
let args = try_parse_pager(&["kigi-pager", "--worktree"]).unwrap();
|
||||
assert_eq!(args.worktree.as_deref(), Some(""));
|
||||
}
|
||||
#[test]
|
||||
fn cli_worktree_short_w_parses() {
|
||||
let args = try_parse_pager(&["grok-pager", "-w"]).unwrap();
|
||||
let args = try_parse_pager(&["kigi-pager", "-w"]).unwrap();
|
||||
assert_eq!(args.worktree.as_deref(), Some(""));
|
||||
}
|
||||
#[test]
|
||||
fn cli_worktree_with_label() {
|
||||
let args = try_parse_pager(&["grok-pager", "-w", "my-label"]).unwrap();
|
||||
let args = try_parse_pager(&["kigi-pager", "-w", "my-label"]).unwrap();
|
||||
assert_eq!(args.worktree.as_deref(), Some("my-label"));
|
||||
}
|
||||
#[test]
|
||||
fn cli_worktree_long_with_label() {
|
||||
let args = try_parse_pager(&["grok-pager", "--worktree", "fix-bug"]).unwrap();
|
||||
let args = try_parse_pager(&["kigi-pager", "--worktree", "fix-bug"]).unwrap();
|
||||
assert_eq!(args.worktree.as_deref(), Some("fix-bug"));
|
||||
}
|
||||
#[test]
|
||||
fn cli_worktree_with_empty_string() {
|
||||
let args = try_parse_pager(&["grok-pager", "-w", ""]).unwrap();
|
||||
let args = try_parse_pager(&["kigi-pager", "-w", ""]).unwrap();
|
||||
assert_eq!(args.worktree.as_deref(), Some(""));
|
||||
}
|
||||
#[test]
|
||||
fn cli_worktree_with_resume_parses() {
|
||||
let args = try_parse_pager(&["grok-pager", "-w", "--resume", "abc"]).unwrap();
|
||||
let args = try_parse_pager(&["kigi-pager", "-w", "--resume", "abc"]).unwrap();
|
||||
assert_eq!(args.worktree.as_deref(), Some(""));
|
||||
assert_eq!(args.session_to_resume(), Some("abc"));
|
||||
}
|
||||
#[test]
|
||||
fn cli_worktree_label_with_resume() {
|
||||
let args = try_parse_pager(&["grok-pager", "-w", "my-label", "--resume", "abc"]).unwrap();
|
||||
let args = try_parse_pager(&["kigi-pager", "-w", "my-label", "--resume", "abc"]).unwrap();
|
||||
assert_eq!(args.worktree.as_deref(), Some("my-label"));
|
||||
assert_eq!(args.session_to_resume(), Some("abc"));
|
||||
}
|
||||
#[test]
|
||||
fn cli_worktree_default_none() {
|
||||
let args = try_parse_pager(&["grok-pager"]).unwrap();
|
||||
let args = try_parse_pager(&["kigi-pager"]).unwrap();
|
||||
assert!(args.worktree.is_none());
|
||||
}
|
||||
#[test]
|
||||
fn cli_session_id_parses() {
|
||||
let args = try_parse_pager(&["grok-pager", "--session-id", "my-id"]).unwrap();
|
||||
let args = try_parse_pager(&["kigi-pager", "--session-id", "my-id"]).unwrap();
|
||||
assert_eq!(args.session_id.as_deref(), Some("my-id"));
|
||||
assert!(matches!(
|
||||
args.session_startup_intent().unwrap(),
|
||||
@@ -1520,38 +1520,38 @@ mod tests {
|
||||
}
|
||||
#[test]
|
||||
fn cli_session_id_short_s_parses() {
|
||||
let args = try_parse_pager(&["grok-pager", "-s", "my-id"]).unwrap();
|
||||
let args = try_parse_pager(&["kigi-pager", "-s", "my-id"]).unwrap();
|
||||
assert_eq!(args.session_id.as_deref(), Some("my-id"));
|
||||
}
|
||||
#[test]
|
||||
fn cli_session_id_with_resume_requires_fork() {
|
||||
let args = try_parse_pager(&["grok-pager", "-s", "a", "--resume", "b"]).unwrap();
|
||||
let args = try_parse_pager(&["kigi-pager", "-s", "a", "--resume", "b"]).unwrap();
|
||||
assert!(args.session_startup_intent().is_err());
|
||||
}
|
||||
#[test]
|
||||
fn cli_session_id_with_continue_requires_fork() {
|
||||
let args = try_parse_pager(&["grok-pager", "-s", "a", "--continue"]).unwrap();
|
||||
let args = try_parse_pager(&["kigi-pager", "-s", "a", "--continue"]).unwrap();
|
||||
assert!(args.session_startup_intent().is_err());
|
||||
}
|
||||
#[test]
|
||||
fn cli_session_id_with_resume_and_fork_ok() {
|
||||
let args =
|
||||
try_parse_pager(&["grok-pager", "-s", "a", "--resume", "b", "--fork-session"]).unwrap();
|
||||
try_parse_pager(&["kigi-pager", "-s", "a", "--resume", "b", "--fork-session"]).unwrap();
|
||||
assert!(args.session_startup_intent().is_ok());
|
||||
}
|
||||
#[test]
|
||||
fn cli_session_id_default_none() {
|
||||
let args = try_parse_pager(&["grok-pager"]).unwrap();
|
||||
let args = try_parse_pager(&["kigi-pager"]).unwrap();
|
||||
assert!(args.session_id.is_none());
|
||||
}
|
||||
#[test]
|
||||
fn cli_no_alt_screen_flag_parses() {
|
||||
let args = try_parse_pager(&["grok-pager", "--no-alt-screen"]).unwrap();
|
||||
let args = try_parse_pager(&["kigi-pager", "--no-alt-screen"]).unwrap();
|
||||
assert!(args.no_alt_screen);
|
||||
}
|
||||
#[test]
|
||||
fn cli_no_alt_screen_default_false() {
|
||||
let args = try_parse_pager(&["grok-pager"]).unwrap();
|
||||
let args = try_parse_pager(&["kigi-pager"]).unwrap();
|
||||
assert!(!args.no_alt_screen);
|
||||
}
|
||||
#[test]
|
||||
@@ -1580,12 +1580,12 @@ mod tests {
|
||||
#[test]
|
||||
fn cli_completions_parses() {
|
||||
use clap_complete::Shell;
|
||||
let args = try_parse_pager(&["grok-pager", "completions", "zsh"]).unwrap();
|
||||
let args = try_parse_pager(&["kigi-pager", "completions", "zsh"]).unwrap();
|
||||
assert!(matches!(
|
||||
args.command,
|
||||
Some(Command::Completions { shell: Shell::Zsh })
|
||||
));
|
||||
let args = try_parse_pager(&["grok-pager", "completions", "bash"]).unwrap();
|
||||
let args = try_parse_pager(&["kigi-pager", "completions", "bash"]).unwrap();
|
||||
assert!(matches!(
|
||||
args.command,
|
||||
Some(Command::Completions { shell: Shell::Bash })
|
||||
@@ -1607,7 +1607,7 @@ mod tests {
|
||||
print_exit_resume_hint("sess-abc", false, &mut buf);
|
||||
assert_eq!(
|
||||
String::from_utf8(buf).unwrap(),
|
||||
"\nResume this session with:\n grok --resume sess-abc\n"
|
||||
"\nResume this session with:\n kigi --resume sess-abc\n"
|
||||
);
|
||||
}
|
||||
#[test]
|
||||
@@ -1616,7 +1616,7 @@ mod tests {
|
||||
print_exit_resume_hint("sess-abc", true, &mut buf);
|
||||
assert_eq!(
|
||||
String::from_utf8(buf).unwrap(),
|
||||
"\nResume this session with:\n grok --minimal --resume sess-abc\n"
|
||||
"\nResume this session with:\n kigi --minimal --resume sess-abc\n"
|
||||
);
|
||||
}
|
||||
#[test]
|
||||
|
||||
@@ -40,7 +40,7 @@ pub enum PromptMode {
|
||||
/// When `Some`, this is a server-authoritative shared-queue row and
|
||||
/// `server_id` is the agent's stable `prompt_id`. On save we route
|
||||
/// the change through `Action::QueueEditShared` (and rely on the
|
||||
/// `x.ai/queue/changed` rebroadcast for the visual result) instead
|
||||
/// `kigi/queue/changed` rebroadcast for the visual result) instead
|
||||
/// of mutating the local `pending_prompts` mirror. `None` is the
|
||||
/// pre-existing local-origin path.
|
||||
server_id: Option<String>,
|
||||
@@ -412,7 +412,7 @@ impl AgentView {
|
||||
}
|
||||
match server_id {
|
||||
Some(server_id) => {
|
||||
// Server rows: the queue wire (`x.ai/queue/interject` newText)
|
||||
// Server rows: the queue wire (`kigi/queue/interject` newText)
|
||||
// is text-only, so composer images can't ride along — known
|
||||
// limitation, dropped with an accurate toast.
|
||||
if !self.prompt.images.is_empty() {
|
||||
|
||||
@@ -3,8 +3,8 @@
|
||||
//! The leader process hosts session actors and exposes a roster API the
|
||||
//! pager consumes in leader mode (FleetView dashboard):
|
||||
//!
|
||||
//! - Request/response `x.ai/sessions/list` → [`RosterListResponse`].
|
||||
//! - Broadcast notification `x.ai/sessions/changed` → [`RosterChanged`].
|
||||
//! - Request/response `kigi/sessions/list` → [`RosterListResponse`].
|
||||
//! - Broadcast notification `kigi/sessions/changed` → [`RosterChanged`].
|
||||
//!
|
||||
//! These structs mirror the producer-side wire format (camelCase JSON,
|
||||
//! snake_case activity enum). They are deserialize-only — the pager never
|
||||
@@ -57,14 +57,14 @@ pub struct RosterEntry {
|
||||
pub origin: RosterOrigin,
|
||||
}
|
||||
|
||||
/// Response to `x.ai/sessions/list`.
|
||||
/// Response to `kigi/sessions/list`.
|
||||
#[derive(Debug, Clone, Default, Deserialize)]
|
||||
pub struct RosterListResponse {
|
||||
#[serde(default)]
|
||||
pub sessions: Vec<RosterEntry>,
|
||||
}
|
||||
|
||||
/// Broadcast payload for `x.ai/sessions/changed`.
|
||||
/// Broadcast payload for `kigi/sessions/changed`.
|
||||
#[derive(Debug, Clone, Default, Deserialize)]
|
||||
pub struct RosterChanged {
|
||||
#[serde(default)]
|
||||
@@ -73,7 +73,7 @@ pub struct RosterChanged {
|
||||
pub removed: Vec<String>,
|
||||
}
|
||||
|
||||
/// Parse an `x.ai/sessions/list` ext-response body into a [`RosterListResponse`].
|
||||
/// Parse an `kigi/sessions/list` ext-response body into a [`RosterListResponse`].
|
||||
///
|
||||
/// The agent serializes the response through
|
||||
/// `ExtMethodResult::success(..).to_ext_response()` (see
|
||||
@@ -89,8 +89,8 @@ pub struct RosterChanged {
|
||||
/// key). That was the original bug: the poll returned an empty roster on every
|
||||
/// tick and — because [`crate::app::actions::TaskResult::RosterLoaded`] replaces
|
||||
/// `leader_roster` wholesale — also clobbered any entry delivered by the
|
||||
/// `x.ai/sessions/changed` broadcast. Mirrors how `Effect::FetchSessionList`
|
||||
/// unwraps `result` for `x.ai/session/list`.
|
||||
/// `kigi/sessions/changed` broadcast. Mirrors how `Effect::FetchSessionList`
|
||||
/// unwraps `result` for `kigi/session/list`.
|
||||
pub fn parse_roster_list_response(body: &str) -> Option<RosterListResponse> {
|
||||
let value: serde_json::Value = serde_json::from_str(body).ok()?;
|
||||
let payload = value.get("result").unwrap_or(&value);
|
||||
@@ -110,7 +110,7 @@ mod tests {
|
||||
title: Some("Fix the roster".to_string()),
|
||||
cwd: "/repo/worktree".to_string(),
|
||||
is_worktree: true,
|
||||
model_id: Some("grok-4".to_string()),
|
||||
model_id: Some("kigi-4".to_string()),
|
||||
reasoning_effort: None,
|
||||
yolo: true,
|
||||
activity: agent::RosterActivity::Working,
|
||||
@@ -141,7 +141,7 @@ mod tests {
|
||||
sessions: vec![agent_entry()],
|
||||
};
|
||||
|
||||
// Exact wire bytes the agent emits for `x.ai/sessions/list`.
|
||||
// Exact wire bytes the agent emits for `kigi/sessions/list`.
|
||||
let ext_response = ExtMethodResult::success(agent_resp)
|
||||
.to_ext_response()
|
||||
.expect("agent serializes the roster response");
|
||||
@@ -172,7 +172,7 @@ mod tests {
|
||||
assert_eq!(e.title.as_deref(), Some("Fix the roster"));
|
||||
assert_eq!(e.cwd, "/repo/worktree");
|
||||
assert!(e.is_worktree);
|
||||
assert_eq!(e.model_id.as_deref(), Some("grok-4"));
|
||||
assert_eq!(e.model_id.as_deref(), Some("kigi-4"));
|
||||
assert!(e.yolo);
|
||||
assert_eq!(e.activity, RosterActivity::Working);
|
||||
assert!(e.resident);
|
||||
@@ -190,7 +190,7 @@ mod tests {
|
||||
assert_eq!(parsed.sessions[0].session_id, "s1");
|
||||
}
|
||||
|
||||
/// `x.ai/sessions/changed` round-trip: serialize the agent's `RosterChanged`
|
||||
/// `kigi/sessions/changed` round-trip: serialize the agent's `RosterChanged`
|
||||
/// exactly as `emit_roster_changed` does (bare params, no `result`
|
||||
/// envelope) and confirm the pager's `RosterChanged` recovers `upserted` /
|
||||
/// `removed` and the nested entry fields (camelCase). Regression guard for
|
||||
|
||||
@@ -71,7 +71,7 @@ fn flag_takes_value(flag: &str) -> bool {
|
||||
///
|
||||
/// Strips prior session-selection / mode flags, one-shot session-creation
|
||||
/// directives, and any bare positional prompt so a cold-start
|
||||
/// `grok "do the thing"` does not re-submit on resume. Keeps everything else
|
||||
/// `kigi "do the thing"` does not re-submit on resume. Keeps everything else
|
||||
/// (e.g. `--no-leader`, `--model`, endpoint overrides) intact, including the
|
||||
/// value token that follows value-taking flags.
|
||||
///
|
||||
@@ -175,7 +175,7 @@ pub(crate) fn build_screen_mode_relaunch_args(
|
||||
continue;
|
||||
}
|
||||
|
||||
// Bare positional prompt (e.g. `grok "fix the bug"`). Must not re-fire
|
||||
// Bare positional prompt (e.g. `kigi "fix the bug"`). Must not re-fire
|
||||
// on resume. Clap positionals never start with `-`. Values for earlier
|
||||
// flags were already consumed above, so any remaining bare word here is
|
||||
// the prompt.
|
||||
@@ -210,7 +210,7 @@ pub(crate) fn screen_mode_relaunch_resume_hint(session_id: &str, want_minimal: b
|
||||
} else {
|
||||
"--fullscreen"
|
||||
};
|
||||
format!("{KIGI_SCREEN_MODE_ENV}={mode} grok {flag} --resume {session_id}")
|
||||
format!("{KIGI_SCREEN_MODE_ENV}={mode} kigi {flag} --resume {session_id}")
|
||||
}
|
||||
|
||||
/// Replace the current process with a relaunch into the requested screen mode.
|
||||
@@ -331,7 +331,7 @@ pub(crate) fn parse_screen_mode(value: Option<&str>) -> Option<super::ScreenMode
|
||||
///
|
||||
/// Reads **and removes** the variable so the override is truly one-shot: it
|
||||
/// must not linger in this process's environment where every spawned child
|
||||
/// (tool shells, workers, nested `grok` invocations) would inherit a forced
|
||||
/// (tool shells, workers, nested `kigi` invocations) would inherit a forced
|
||||
/// screen mode the user never asked for.
|
||||
///
|
||||
/// When set, the returned mode **wins** over CLI flags (`--minimal`,
|
||||
@@ -440,7 +440,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn adds_minimal_and_resume() {
|
||||
let out = build_screen_mode_relaunch_args(args(&["grok", "--no-leader"]), "abc", true);
|
||||
let out = build_screen_mode_relaunch_args(args(&["kigi", "--no-leader"]), "abc", true);
|
||||
assert_eq!(
|
||||
as_strs(&out),
|
||||
vec!["--no-leader", "--resume", "abc", "--minimal"]
|
||||
@@ -451,7 +451,7 @@ mod tests {
|
||||
/// resolution still works without the env override.
|
||||
#[test]
|
||||
fn adds_fullscreen_and_resume() {
|
||||
let out = build_screen_mode_relaunch_args(args(&["grok", "--no-leader"]), "abc", false);
|
||||
let out = build_screen_mode_relaunch_args(args(&["kigi", "--no-leader"]), "abc", false);
|
||||
assert_eq!(
|
||||
as_strs(&out),
|
||||
vec!["--no-leader", "--resume", "abc", "--fullscreen"]
|
||||
@@ -466,7 +466,7 @@ mod tests {
|
||||
fn strips_session_id_flag() {
|
||||
let out = build_screen_mode_relaunch_args(
|
||||
args(&[
|
||||
"grok",
|
||||
"kigi",
|
||||
"--session-id",
|
||||
"11111111-1111-1111-1111-111111111111",
|
||||
"--no-leader",
|
||||
@@ -480,7 +480,7 @@ mod tests {
|
||||
);
|
||||
|
||||
let out = build_screen_mode_relaunch_args(
|
||||
args(&["grok", "-s", "11111111-1111-1111-1111-111111111111"]),
|
||||
args(&["kigi", "-s", "11111111-1111-1111-1111-111111111111"]),
|
||||
"new",
|
||||
false,
|
||||
);
|
||||
@@ -494,7 +494,7 @@ mod tests {
|
||||
fn strips_worktree_and_restore_code() {
|
||||
let out = build_screen_mode_relaunch_args(
|
||||
args(&[
|
||||
"grok",
|
||||
"kigi",
|
||||
"-w",
|
||||
"feature-x",
|
||||
"--worktree-ref",
|
||||
@@ -518,7 +518,7 @@ mod tests {
|
||||
fn strips_eq_forms_of_one_shot_flags() {
|
||||
let out = build_screen_mode_relaunch_args(
|
||||
args(&[
|
||||
"grok",
|
||||
"kigi",
|
||||
"--session-id=u1",
|
||||
"--worktree=wt",
|
||||
"--worktree-ref=main",
|
||||
@@ -539,7 +539,7 @@ mod tests {
|
||||
#[test]
|
||||
fn strips_bare_worktree_without_eating_next_flag() {
|
||||
let out = build_screen_mode_relaunch_args(
|
||||
args(&["grok", "--worktree", "--no-leader"]),
|
||||
args(&["kigi", "--worktree", "--no-leader"]),
|
||||
"new",
|
||||
false,
|
||||
);
|
||||
@@ -552,7 +552,7 @@ mod tests {
|
||||
#[test]
|
||||
fn strips_prior_minimal_and_resume() {
|
||||
let out = build_screen_mode_relaunch_args(
|
||||
args(&["grok", "--minimal", "--resume", "old", "--no-leader"]),
|
||||
args(&["kigi", "--minimal", "--resume", "old", "--no-leader"]),
|
||||
"new",
|
||||
false,
|
||||
);
|
||||
@@ -570,7 +570,7 @@ mod tests {
|
||||
#[test]
|
||||
fn strips_prior_fullscreen_flag() {
|
||||
let out = build_screen_mode_relaunch_args(
|
||||
args(&["grok", "--fullscreen", "--resume", "old", "--no-leader"]),
|
||||
args(&["kigi", "--fullscreen", "--resume", "old", "--no-leader"]),
|
||||
"new",
|
||||
true,
|
||||
);
|
||||
@@ -584,7 +584,7 @@ mod tests {
|
||||
#[test]
|
||||
fn strips_short_resume_and_continue() {
|
||||
let out = build_screen_mode_relaunch_args(
|
||||
args(&["grok", "-r", "old", "-c", "--no-leader"]),
|
||||
args(&["kigi", "-r", "old", "-c", "--no-leader"]),
|
||||
"sid",
|
||||
true,
|
||||
);
|
||||
@@ -598,7 +598,7 @@ mod tests {
|
||||
#[test]
|
||||
fn strips_resume_equals_form() {
|
||||
let out = build_screen_mode_relaunch_args(
|
||||
args(&["grok", "--resume=old-id", "--no-leader"]),
|
||||
args(&["kigi", "--resume=old-id", "--no-leader"]),
|
||||
"sid",
|
||||
false,
|
||||
);
|
||||
@@ -611,7 +611,7 @@ mod tests {
|
||||
#[test]
|
||||
fn strips_positional_prompt() {
|
||||
let out = build_screen_mode_relaunch_args(
|
||||
args(&["grok", "--no-leader", "fix the bug"]),
|
||||
args(&["kigi", "--no-leader", "fix the bug"]),
|
||||
"sid",
|
||||
true,
|
||||
);
|
||||
@@ -624,11 +624,11 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn double_dash_and_following_positionals_dropped() {
|
||||
// `grok --no-leader -- "fix the bug"`: everything after `--` is the
|
||||
// `kigi --no-leader -- "fix the bug"`: everything after `--` is the
|
||||
// prompt. The separator itself must go too, or the appended
|
||||
// `--resume <id>` would be parsed as positional prompt words.
|
||||
let out = build_screen_mode_relaunch_args(
|
||||
args(&["grok", "--no-leader", "--", "fix the bug"]),
|
||||
args(&["kigi", "--no-leader", "--", "fix the bug"]),
|
||||
"sid",
|
||||
false,
|
||||
);
|
||||
@@ -644,9 +644,9 @@ mod tests {
|
||||
// as the bare positional prompt (regression: relaunch argv drops flag values).
|
||||
let out = build_screen_mode_relaunch_args(
|
||||
args(&[
|
||||
"grok",
|
||||
"kigi",
|
||||
"--model",
|
||||
"grok-4",
|
||||
"kigi-4",
|
||||
"--cwd",
|
||||
"/tmp/proj",
|
||||
"--leader-socket",
|
||||
@@ -663,7 +663,7 @@ mod tests {
|
||||
as_strs(&out),
|
||||
vec![
|
||||
"--model",
|
||||
"grok-4",
|
||||
"kigi-4",
|
||||
"--cwd",
|
||||
"/tmp/proj",
|
||||
"--leader-socket",
|
||||
@@ -682,7 +682,7 @@ mod tests {
|
||||
#[test]
|
||||
fn keeps_equals_form_and_short_model_flag() {
|
||||
let out = build_screen_mode_relaunch_args(
|
||||
args(&["grok", "-m", "grok-4", "--cwd=/tmp/proj", "--no-leader"]),
|
||||
args(&["kigi", "-m", "kigi-4", "--cwd=/tmp/proj", "--no-leader"]),
|
||||
"sid",
|
||||
false,
|
||||
);
|
||||
@@ -690,7 +690,7 @@ mod tests {
|
||||
as_strs(&out),
|
||||
vec![
|
||||
"-m",
|
||||
"grok-4",
|
||||
"kigi-4",
|
||||
"--cwd=/tmp/proj",
|
||||
"--no-leader",
|
||||
"--resume",
|
||||
@@ -705,7 +705,7 @@ mod tests {
|
||||
// `--no-leader` is boolean; the bare word after it is the prompt and
|
||||
// must be dropped, not attached as a spurious value.
|
||||
let out = build_screen_mode_relaunch_args(
|
||||
args(&["grok", "--no-leader", "fix the bug"]),
|
||||
args(&["kigi", "--no-leader", "fix the bug"]),
|
||||
"sid",
|
||||
false,
|
||||
);
|
||||
@@ -717,9 +717,9 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn resume_without_value_then_flag_is_not_eaten() {
|
||||
// `grok --resume --no-leader` (resume most-recent; next token is a flag).
|
||||
// `kigi --resume --no-leader` (resume most-recent; next token is a flag).
|
||||
let out = build_screen_mode_relaunch_args(
|
||||
args(&["grok", "--resume", "--no-leader"]),
|
||||
args(&["kigi", "--resume", "--no-leader"]),
|
||||
"sid",
|
||||
false,
|
||||
);
|
||||
@@ -833,11 +833,11 @@ mod tests {
|
||||
// explicit flag keeps the resume in the right mode if the env is dropped.
|
||||
assert_eq!(
|
||||
screen_mode_relaunch_resume_hint("abc-sid", false),
|
||||
"KIGI_SCREEN_MODE=fullscreen grok --fullscreen --resume abc-sid"
|
||||
"KIGI_SCREEN_MODE=fullscreen kigi --fullscreen --resume abc-sid"
|
||||
);
|
||||
assert_eq!(
|
||||
screen_mode_relaunch_resume_hint("abc-sid", true),
|
||||
"KIGI_SCREEN_MODE=minimal grok --minimal --resume abc-sid"
|
||||
"KIGI_SCREEN_MODE=minimal kigi --minimal --resume abc-sid"
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -26,7 +26,7 @@ pub enum DeferredSessionStartup {
|
||||
parent_cwd: Option<PathBuf>,
|
||||
new_session_id: Option<String>,
|
||||
},
|
||||
/// Fresh plain Grok session whose first prompt resumes a foreign tool session.
|
||||
/// Fresh plain Kigi session whose first prompt resumes a foreign tool session.
|
||||
ForeignResume {
|
||||
tool: kigi_workspace::foreign_sessions::ForeignSessionTool,
|
||||
native_id: String,
|
||||
@@ -53,7 +53,7 @@ impl DeferredStartupActions {
|
||||
std::mem::take(self)
|
||||
}
|
||||
}
|
||||
/// Build `x.ai/session/fork` params shared by TUI effects and headless.
|
||||
/// Build `kigi/session/fork` params shared by TUI effects and headless.
|
||||
///
|
||||
/// `new_cwd` is the write namespace for the child (parent session cwd when
|
||||
/// cross-cwd); preflight must use the same path via [`effective_fork_new_cwd`].
|
||||
@@ -114,7 +114,7 @@ pub fn parent_session_is_worktree(session_id: &str, cwd: &Path) -> bool {
|
||||
}
|
||||
false
|
||||
}
|
||||
/// Parse `newSessionId` from an `x.ai/session/fork` ACP response body.
|
||||
/// Parse `newSessionId` from an `kigi/session/fork` ACP response body.
|
||||
pub fn fork_response_new_session_id(resp_json: &str) -> Option<String> {
|
||||
let v: serde_json::Value = serde_json::from_str(resp_json).unwrap_or_default();
|
||||
if v.get("error").is_some_and(|e| !e.is_null()) {
|
||||
@@ -375,7 +375,7 @@ pub struct MaterializeCtx {
|
||||
pub has_worktree: bool,
|
||||
/// When true, attempt remote restore if the session is not on disk.
|
||||
pub allow_remote_restore: bool,
|
||||
/// Process-wide flag: resume targets are grok.com conversations, not
|
||||
/// Process-wide flag: resume targets are kimi.com conversations, not
|
||||
/// the local disk store. Always `false` without the optional feature;
|
||||
/// setting it anyway errors rather than silently falling back to disk.
|
||||
pub chat_mode: bool,
|
||||
@@ -404,12 +404,12 @@ async fn most_recent_session_id(cwd: &str) -> anyhow::Result<(String, Option<Str
|
||||
let first = summaries.first().ok_or_else(|| {
|
||||
anyhow::anyhow!(
|
||||
"No session found for current directory. \
|
||||
Use 'grok' to start a new session."
|
||||
Use 'kigi' to start a new session."
|
||||
)
|
||||
})?;
|
||||
Ok((first.info.id.to_string(), first.display_title_opt()))
|
||||
}
|
||||
/// `AuthManager` for direct grok.com calls made outside the agent (pre-ACP
|
||||
/// `AuthManager` for direct kimi.com calls made outside the agent (pre-ACP
|
||||
/// `--continue` conversation listing, the GCS restore effect). Wires the
|
||||
/// auth-provider refresher before the first `auth()`: without it, environments
|
||||
/// that mint credentials via `auth_provider_command` report `NoOauth`.
|
||||
@@ -689,14 +689,14 @@ mod tests {
|
||||
#[test]
|
||||
fn intent_default_is_new_auto() {
|
||||
assert_eq!(
|
||||
parse(&["grok"]).session_startup_intent().unwrap(),
|
||||
parse(&["kigi"]).session_startup_intent().unwrap(),
|
||||
SessionStartupIntent::NewAuto
|
||||
);
|
||||
}
|
||||
#[test]
|
||||
fn intent_resume_id() {
|
||||
assert_eq!(
|
||||
parse(&["grok", "--resume", "abc"])
|
||||
parse(&["kigi", "--resume", "abc"])
|
||||
.session_startup_intent()
|
||||
.unwrap(),
|
||||
SessionStartupIntent::Resume {
|
||||
@@ -708,7 +708,7 @@ mod tests {
|
||||
#[test]
|
||||
fn intent_resume_empty_is_most_recent() {
|
||||
assert_eq!(
|
||||
parse(&["grok", "--resume"])
|
||||
parse(&["kigi", "--resume"])
|
||||
.session_startup_intent()
|
||||
.unwrap(),
|
||||
SessionStartupIntent::Resume {
|
||||
@@ -720,7 +720,7 @@ mod tests {
|
||||
#[test]
|
||||
fn intent_continue() {
|
||||
assert_eq!(
|
||||
parse(&["grok", "-c"]).session_startup_intent().unwrap(),
|
||||
parse(&["kigi", "-c"]).session_startup_intent().unwrap(),
|
||||
SessionStartupIntent::Resume {
|
||||
session_id: None,
|
||||
most_recent_for_cwd: true,
|
||||
@@ -730,7 +730,7 @@ mod tests {
|
||||
#[test]
|
||||
fn intent_session_id_alone_is_new_with_id() {
|
||||
assert_eq!(
|
||||
parse(&["grok", "--session-id", "my-id"])
|
||||
parse(&["kigi", "--session-id", "my-id"])
|
||||
.session_startup_intent()
|
||||
.unwrap(),
|
||||
SessionStartupIntent::NewWithId {
|
||||
@@ -740,7 +740,7 @@ mod tests {
|
||||
}
|
||||
#[test]
|
||||
fn intent_session_id_with_resume_without_fork_errors() {
|
||||
let err = parse(&["grok", "-r", "a", "-s", "b"])
|
||||
let err = parse(&["kigi", "-r", "a", "-s", "b"])
|
||||
.session_startup_intent()
|
||||
.unwrap_err();
|
||||
assert_eq!(err, StartupFlagError::SessionIdRequiresFork);
|
||||
@@ -748,7 +748,7 @@ mod tests {
|
||||
#[test]
|
||||
fn intent_fork_with_resume() {
|
||||
assert_eq!(
|
||||
parse(&["grok", "-r", "old", "--fork-session"])
|
||||
parse(&["kigi", "-r", "old", "--fork-session"])
|
||||
.session_startup_intent()
|
||||
.unwrap(),
|
||||
SessionStartupIntent::ForkFrom {
|
||||
@@ -761,7 +761,7 @@ mod tests {
|
||||
#[test]
|
||||
fn intent_fork_with_resume_and_new_id() {
|
||||
assert_eq!(
|
||||
parse(&["grok", "-r", "old", "--fork-session", "-s", "new"])
|
||||
parse(&["kigi", "-r", "old", "--fork-session", "-s", "new"])
|
||||
.session_startup_intent()
|
||||
.unwrap(),
|
||||
SessionStartupIntent::ForkFrom {
|
||||
@@ -773,21 +773,21 @@ mod tests {
|
||||
}
|
||||
#[test]
|
||||
fn intent_fork_alone_errors() {
|
||||
let err = parse(&["grok", "--fork-session"])
|
||||
let err = parse(&["kigi", "--fork-session"])
|
||||
.session_startup_intent()
|
||||
.unwrap_err();
|
||||
assert_eq!(err, StartupFlagError::ForkRequiresResumeOrContinue);
|
||||
}
|
||||
#[test]
|
||||
fn intent_fork_with_worktree_errors() {
|
||||
let err = parse(&["grok", "-r", "a", "--fork-session", "-w"])
|
||||
let err = parse(&["kigi", "-r", "a", "--fork-session", "-w"])
|
||||
.session_startup_intent()
|
||||
.unwrap_err();
|
||||
assert_eq!(err, StartupFlagError::ForkWithWorktree);
|
||||
}
|
||||
#[test]
|
||||
fn intent_from_flags_matches_pager_args() {
|
||||
let args = parse(&["grok", "-r", "old", "--fork-session", "-s", "new"]);
|
||||
let args = parse(&["kigi", "-r", "old", "--fork-session", "-s", "new"]);
|
||||
let from_flags = session_startup_intent_from_flags(SessionStartupFlags {
|
||||
session_id: Some("new"),
|
||||
resume_session_id: Some("old"),
|
||||
@@ -888,7 +888,7 @@ mod tests {
|
||||
}
|
||||
#[test]
|
||||
fn materialize_ctx_chat_mode_from_args() {
|
||||
assert!(!MaterializeCtx::from_pager_args(&parse(&["grok"])).chat_mode);
|
||||
assert!(!MaterializeCtx::from_pager_args(&parse(&["kigi"])).chat_mode);
|
||||
}
|
||||
/// Explicit-id resume under `--chat` passes the id through untouched:
|
||||
/// no disk resolution, no GCS restore (the cwd does not even exist).
|
||||
|
||||
@@ -110,7 +110,7 @@ thread_local! {
|
||||
static REPLAY_KIGI_SHARE_DIR : std::cell::RefCell < Option < std::path::PathBuf >> = const
|
||||
{ std::cell::RefCell::new(None) };
|
||||
}
|
||||
/// Override grok home for disk-replay unit tests (thread-local; production never sets this).
|
||||
/// Override kigi home for disk-replay unit tests (thread-local; production never sets this).
|
||||
#[cfg(test)]
|
||||
pub(crate) fn set_replay_kigi_home_for_tests(home: Option<std::path::PathBuf>) {
|
||||
REPLAY_KIGI_SHARE_DIR.with(|h| *h.borrow_mut() = home);
|
||||
@@ -764,15 +764,15 @@ mod tests {
|
||||
#[test]
|
||||
fn subagent_meta_all_fields() {
|
||||
assert_eq!(
|
||||
format_subagent_meta(Some("researcher"), Some("analyst"), Some("grok-3")),
|
||||
" (researcher \u{00b7} analyst \u{00b7} grok-3)"
|
||||
format_subagent_meta(Some("researcher"), Some("analyst"), Some("kigi-3")),
|
||||
" (researcher \u{00b7} analyst \u{00b7} kigi-3)"
|
||||
);
|
||||
}
|
||||
#[test]
|
||||
fn subagent_meta_partial_skips_nones() {
|
||||
assert_eq!(
|
||||
format_subagent_meta(Some("researcher"), None, Some("grok-3")),
|
||||
" (researcher \u{00b7} grok-3)"
|
||||
format_subagent_meta(Some("researcher"), None, Some("kigi-3")),
|
||||
" (researcher \u{00b7} kigi-3)"
|
||||
);
|
||||
}
|
||||
#[test]
|
||||
@@ -813,8 +813,8 @@ mod tests {
|
||||
#[test]
|
||||
fn subagent_meta_collapses_duplicate_persona_role() {
|
||||
assert_eq!(
|
||||
format_subagent_meta(Some("reviewer"), Some("reviewer"), Some("grok-3")),
|
||||
" (reviewer \u{00b7} grok-3)"
|
||||
format_subagent_meta(Some("reviewer"), Some("reviewer"), Some("kigi-3")),
|
||||
" (reviewer \u{00b7} kigi-3)"
|
||||
);
|
||||
}
|
||||
#[test]
|
||||
@@ -841,8 +841,8 @@ mod tests {
|
||||
#[test]
|
||||
fn subagent_meta_drops_both_empty_persona_role() {
|
||||
assert_eq!(
|
||||
format_subagent_meta(Some(""), Some(" "), Some("grok-3")),
|
||||
" (grok-3)"
|
||||
format_subagent_meta(Some(""), Some(" "), Some("kigi-3")),
|
||||
" (kigi-3)"
|
||||
);
|
||||
}
|
||||
#[test]
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
//! Finalizing a turn from a terminal turn signal.
|
||||
//!
|
||||
//! The pager learns a turn reached its terminal outcome from two rails: the
|
||||
//! fire-and-forget `x.ai/session/prompt_complete` broadcast (the one-release
|
||||
//! fire-and-forget `kigi/session/prompt_complete` broadcast (the one-release
|
||||
//! compat path for not-yet-upgraded leaders) and the durable, persisted+replayed
|
||||
//! `XaiSessionUpdate::TurnCompleted`. Both converge on
|
||||
//! [`finalize_turn_from_terminal`] so the turn-finalize behavior lives in one
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
Based on Grok Build Open Source
|
||||
@@ -152,7 +152,7 @@ fn draw(f: &mut ratatui::Frame, app: &App) {
|
||||
)),
|
||||
Line::from(""),
|
||||
Line::from(
|
||||
"I'm Grok Build, an interactive CLI agent built to help with software engineering tasks.",
|
||||
"I'm Kigi, an interactive CLI agent built to help with software engineering tasks.",
|
||||
),
|
||||
Line::from(
|
||||
"This sample is here so you can drag over real text while watching raw mouse events.",
|
||||
|
||||
@@ -4,7 +4,7 @@ use std::time::Duration;
|
||||
use crossterm::ExecutableCommand;
|
||||
use crossterm::event::{self, Event, KeyCode, KeyEvent, KeyModifiers};
|
||||
use crossterm::terminal::{self, EnterAlternateScreen, LeaveAlternateScreen};
|
||||
use kigi_tools::implementations::grok_build::ask_user_question::{Question, QuestionOption};
|
||||
use kigi_tools::implementations::kigi::ask_user_question::{Question, QuestionOption};
|
||||
use kigi_tui::theme::Theme;
|
||||
use kigi_tui::views::prompt_widget::StashedPrompt;
|
||||
use kigi_tui::views::question_view::{
|
||||
|
||||
@@ -84,7 +84,7 @@ impl App {
|
||||
"I am thinking through how to answer your question before responding.",
|
||||
));
|
||||
scrollback.push_block(RenderBlock::agent_message(
|
||||
"I'm Grok Build, an interactive CLI agent built to help with software engineering tasks like coding, debugging, refactoring, and exploring codebases.",
|
||||
"I'm Kigi, an interactive CLI agent built to help with software engineering tasks like coding, debugging, refactoring, and exploring codebases.",
|
||||
));
|
||||
scrollback.push_block(RenderBlock::agent_message(
|
||||
"Try drag-selecting text. Double-click toggles fold by default; set Text selection → Word select for double-click word / triple-click line.",
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
pub const PAGER_CLIENT_TYPE: &str = "grok-pager";
|
||||
pub const PAGER_CLIENT_TYPE: &str = "kigi-pager";
|
||||
pub const HEADLESS_CLIENT_TYPE: &str = "kigi";
|
||||
|
||||
pub const PAGER_CLIENT_VERSION: &str = kigi_version::VERSION;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
//! `grok completions <shell>` — generate shell completion scripts.
|
||||
//! `kigi completions <shell>` — generate shell completion scripts.
|
||||
//!
|
||||
//! Used by the installers and npm postinstall; must stay side-effect free
|
||||
//! (no network, auth, tracing, or tokio).
|
||||
@@ -10,16 +10,16 @@ use crate::app::PagerArgs;
|
||||
|
||||
/// Generate and print the completion script for the given shell.
|
||||
pub fn run(shell: Shell) {
|
||||
// Ensure the script always uses the public "grok" name (matches historical
|
||||
// Ensure the script always uses the public "kigi" name (matches historical
|
||||
// behavior and what the installers + docs expect).
|
||||
let mut cmd = PagerArgs::command().name("grok");
|
||||
let mut cmd = PagerArgs::command().name("kigi");
|
||||
if shell != Shell::Zsh {
|
||||
generate(shell, &mut cmd, "grok", &mut std::io::stdout());
|
||||
generate(shell, &mut cmd, "kigi", &mut std::io::stdout());
|
||||
return;
|
||||
}
|
||||
// zsh needs post-processing (see fix_zsh_root_prompt_positional).
|
||||
let mut buf = Vec::new();
|
||||
generate(shell, &mut cmd, "grok", &mut buf);
|
||||
generate(shell, &mut cmd, "kigi", &mut buf);
|
||||
match String::from_utf8(buf) {
|
||||
Ok(script) => print!("{}", fix_zsh_root_prompt_positional(&script)),
|
||||
// clap_complete output is generated from Rust strings, so this arm is
|
||||
@@ -39,7 +39,7 @@ pub fn run(shell: Shell) {
|
||||
/// The generated root `_arguments` spec emits a `'::prompt …'` slot before
|
||||
/// the subcommand slot but dispatches subcommands with `case $line[2]`. zsh
|
||||
/// assigns the typed subcommand to the *prompt* slot (`$line[1]`), leaves
|
||||
/// `$line[2]` empty, and the dispatch falls through — so `grok worktree <TAB>`
|
||||
/// `$line[2]` empty, and the dispatch falls through — so `kigi worktree <TAB>`
|
||||
/// re-offers every top-level command. (`hide = true` on the positional does
|
||||
/// not change the generated script.)
|
||||
///
|
||||
@@ -64,8 +64,8 @@ fn fix_zsh_root_prompt_positional(script: &str) -> String {
|
||||
r#"words=($line[1] "${words[@]}")"#,
|
||||
),
|
||||
(
|
||||
r#"curcontext="${curcontext%:*:*}:grok-command-$line[2]:""#,
|
||||
r#"curcontext="${curcontext%:*:*}:grok-command-$line[1]:""#,
|
||||
r#"curcontext="${curcontext%:*:*}:kigi-command-$line[2]:""#,
|
||||
r#"curcontext="${curcontext%:*:*}:kigi-command-$line[1]:""#,
|
||||
),
|
||||
(r#"case $line[2] in"#, r#"case $line[1] in"#),
|
||||
] {
|
||||
@@ -80,15 +80,15 @@ mod tests {
|
||||
|
||||
/// Generate the zsh completion script exactly like `run` does.
|
||||
fn zsh_script() -> String {
|
||||
let mut cmd = PagerArgs::command().name("grok");
|
||||
let mut cmd = PagerArgs::command().name("kigi");
|
||||
let mut buf = Vec::new();
|
||||
generate(Shell::Zsh, &mut cmd, "grok", &mut buf);
|
||||
generate(Shell::Zsh, &mut cmd, "kigi", &mut buf);
|
||||
String::from_utf8(buf).expect("completion script is UTF-8")
|
||||
}
|
||||
|
||||
// The optional `[PROMPT]` positional (app/cli.rs) makes clap_complete emit
|
||||
// a `::prompt` slot before the subcommand slot and dispatch on `$line[2]`,
|
||||
// so `grok worktree <TAB>` re-offered every top-level command (upstream
|
||||
// so `kigi worktree <TAB>` re-offered every top-level command (upstream
|
||||
// clap-rs/clap#6282).
|
||||
#[test]
|
||||
fn zsh_completions_drop_prompt_slot_and_dispatch_on_line_1() {
|
||||
@@ -112,15 +112,15 @@ mod tests {
|
||||
"root dispatch must be shifted to $line[1]"
|
||||
);
|
||||
assert!(
|
||||
fixed.contains(r#"curcontext="${curcontext%:*:*}:grok-command-$line[1]:""#),
|
||||
fixed.contains(r#"curcontext="${curcontext%:*:*}:kigi-command-$line[1]:""#),
|
||||
"root dispatch context must use $line[1]"
|
||||
);
|
||||
// Subcommand dispatch blocks (already on $line[1]) must survive.
|
||||
assert!(
|
||||
fixed.contains("grok-worktree-command-$line[1]"),
|
||||
fixed.contains("kigi-worktree-command-$line[1]"),
|
||||
"nested subcommand dispatch must be untouched"
|
||||
);
|
||||
// The subcommand list itself must still be offered at the root.
|
||||
assert!(fixed.contains("_grok_commands"), "root command list intact");
|
||||
assert!(fixed.contains("_kigi_commands"), "root command list intact");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2060,7 +2060,7 @@ mod tests {
|
||||
#[test]
|
||||
fn notification_none_protocol_no_warnings() {
|
||||
let ctx = TerminalContext {
|
||||
brand: TerminalName::GrokDesktop,
|
||||
brand: TerminalName::KigiDesktop,
|
||||
..Default::default()
|
||||
};
|
||||
let query = FakeTmuxQuery::healthy_modern();
|
||||
@@ -2084,7 +2084,7 @@ mod tests {
|
||||
assert!(supports_focus_tracking(TerminalName::Terminator));
|
||||
assert!(supports_focus_tracking(TerminalName::WarpTerminal));
|
||||
assert!(supports_focus_tracking(TerminalName::VsCode));
|
||||
assert!(supports_focus_tracking(TerminalName::GrokDesktop));
|
||||
assert!(supports_focus_tracking(TerminalName::KigiDesktop));
|
||||
assert!(!supports_focus_tracking(TerminalName::AppleTerminal));
|
||||
assert!(!supports_focus_tracking(TerminalName::Unknown));
|
||||
assert!(!supports_focus_tracking(TerminalName::Otty));
|
||||
@@ -2133,7 +2133,7 @@ mod tests {
|
||||
line.starts_with(&format!(" themes {n}/{total}: ")),
|
||||
"level {level:?}: {line}"
|
||||
);
|
||||
assert!(line.contains("groknight") && line.contains("grokday"));
|
||||
assert!(line.contains("kiginight") && line.contains("kigiday"));
|
||||
assert!(!line.contains("tokyonight"));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@ use crate::terminal::{TerminalName, terminal_context};
|
||||
/// Fed from three places, all off the render path:
|
||||
/// - [`cwd_git_info_lazy`] — a lazy, throttled refresh when a view reads a cwd.
|
||||
/// - [`populate_from_cwd_async`] — an eager warm at startup / on a cwd change.
|
||||
/// - [`update_from_notification`] — the `x.ai/git_head_changed` ACP
|
||||
/// - [`update_from_notification`] — the `kigi/git_head_changed` ACP
|
||||
/// notification, so a branch switch inside an agent reflects immediately
|
||||
/// instead of waiting out [`CWD_GIT_REFRESH_TTL`].
|
||||
type CwdCacheEntry = (Option<CwdGitInfo>, Instant);
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
//! Headless single-turn mode (`grok -p "prompt"`).
|
||||
//! Headless single-turn mode (`kigi -p "prompt"`).
|
||||
//!
|
||||
//! Runs the agent in-process via
|
||||
//! `spawn_grok_shell`, sends the ACP lifecycle (init → auth → session → prompt),
|
||||
//! `spawn_kigi_shell`, sends the ACP lifecycle (init → auth → session → prompt),
|
||||
//! streams text to stdout, and exits cleanly via `CancellationToken`.
|
||||
|
||||
use std::collections::HashSet;
|
||||
@@ -24,7 +24,7 @@ use kigi_shell::sampling::types::{
|
||||
use kigi_shell::util::config as cli_config;
|
||||
|
||||
use crate::acp::model_state::{EffortTokenError, ModelState};
|
||||
use crate::acp::spawn::spawn_grok_shell;
|
||||
use crate::acp::spawn::spawn_kigi_shell;
|
||||
use crate::client_identity::{HEADLESS_CLIENT_TYPE, PAGER_CLIENT_VERSION};
|
||||
|
||||
// ── Types ────────────────────────────────────────────────────────────────
|
||||
@@ -618,7 +618,7 @@ async fn open_session(
|
||||
let mut m = acp::Meta::new();
|
||||
m.insert("noReplay".into(), serde_json::Value::Bool(true));
|
||||
if let Some(true) = restore_code {
|
||||
m.insert("x.ai/restore_code".into(), serde_json::Value::Bool(true));
|
||||
m.insert("kigi/restore_code".into(), serde_json::Value::Bool(true));
|
||||
}
|
||||
Some(m)
|
||||
}),
|
||||
@@ -694,7 +694,7 @@ async fn fork_then_open(
|
||||
let parent_is_worktree = parent_session_is_worktree(parent_id, &write_cwd);
|
||||
let payload = fork_session_params(parent_id, &write_cwd, new_id, 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(),
|
||||
@@ -796,7 +796,7 @@ async fn apply_headless_model_and_effort(
|
||||
.map_err(|e| {
|
||||
if let Some(name) = model_name {
|
||||
anyhow::anyhow!(
|
||||
"Couldn't set model '{}': {}. Run 'grok models' to see available models.",
|
||||
"Couldn't set model '{}': {}. Run 'kigi models' to see available models.",
|
||||
name,
|
||||
e
|
||||
)
|
||||
@@ -916,7 +916,7 @@ pub async fn run_single_turn(
|
||||
|
||||
let cancel = CancellationToken::new();
|
||||
let memory_config = agent_config.memory_config.clone();
|
||||
let spawned = match spawn_grok_shell(agent_config, &cancel, memory_config).await {
|
||||
let spawned = match spawn_kigi_shell(agent_config, &cancel, memory_config).await {
|
||||
Ok(s) => s,
|
||||
Err(e) => {
|
||||
let msg = format!("Couldn't start session: {e}");
|
||||
@@ -1121,9 +1121,9 @@ pub async fn run_single_turn(
|
||||
let mut ttf_logged = false;
|
||||
let mut prompt_fut = Box::pin(acp_send(request, &acp_tx));
|
||||
let mut prompt_result = None;
|
||||
// Pending background work: bash/monitor via x.ai/task_backgrounded +
|
||||
// Pending background work: bash/monitor via kigi/task_backgrounded +
|
||||
// task_completed; background subagents via SubagentSpawned + SubagentFinished
|
||||
// on x.ai/session_notification (prefixed `subagent:{id}` in pending_bg).
|
||||
// on kigi/session_notification (prefixed `subagent:{id}` in pending_bg).
|
||||
// Tracked regardless of wait_for_background so the exit reaper always
|
||||
// sees still-running work; the flag only gates waiting.
|
||||
// No idle/quiet polling and no wait for server-side auto-wake text — exit
|
||||
@@ -1336,13 +1336,13 @@ fn reap_request_for_key(
|
||||
) -> serde_json::Result<acp::ExtRequest> {
|
||||
let (method, params) = match key.strip_prefix("subagent:") {
|
||||
Some(id) => (
|
||||
"x.ai/subagent/cancel",
|
||||
"kigi/subagent/cancel",
|
||||
serde_json::value::to_raw_value(&CancelSubagentRequest {
|
||||
subagent_id: id.to_string(),
|
||||
})?,
|
||||
),
|
||||
None => (
|
||||
"x.ai/task/kill",
|
||||
"kigi/task/kill",
|
||||
serde_json::value::to_raw_value(&KillTaskRequest {
|
||||
session_id: session_id.0.to_string(),
|
||||
task_id: key.to_string(),
|
||||
@@ -1557,7 +1557,7 @@ fn handle_ext_notification(
|
||||
let method = notif.request.method.as_ref();
|
||||
|
||||
// Background task lifecycle uses dedicated methods (not session_notification).
|
||||
if method == "x.ai/task_backgrounded" {
|
||||
if method == "kigi/task_backgrounded" {
|
||||
#[derive(serde::Deserialize)]
|
||||
struct TaskBgEnvelope {
|
||||
update: TaskBgUpdate,
|
||||
@@ -1587,7 +1587,7 @@ fn handle_ext_notification(
|
||||
return ExtEvent::None;
|
||||
}
|
||||
|
||||
if method == "x.ai/task_completed" {
|
||||
if method == "kigi/task_completed" {
|
||||
#[derive(serde::Deserialize)]
|
||||
struct TaskDoneEnvelope {
|
||||
update: TaskDoneUpdate,
|
||||
@@ -1615,12 +1615,12 @@ fn handle_ext_notification(
|
||||
return ExtEvent::None;
|
||||
}
|
||||
|
||||
if method == "x.ai/monitor_event" {
|
||||
if method == "kigi/monitor_event" {
|
||||
return ExtEvent::MonitorEvent;
|
||||
}
|
||||
|
||||
match method {
|
||||
"x.ai/session_notification" | "x.ai/session/update" => {}
|
||||
"kigi/session_notification" | "kigi/session/update" => {}
|
||||
_ => return ExtEvent::None,
|
||||
}
|
||||
|
||||
@@ -1800,7 +1800,7 @@ mod tests {
|
||||
fn reap_request_for_task_kills_with_session_scope() {
|
||||
let session_id = acp::SessionId::new("sess-1");
|
||||
let request = super::reap_request_for_key("task-42", &session_id).unwrap();
|
||||
assert_eq!(request.method.as_ref(), "x.ai/task/kill");
|
||||
assert_eq!(request.method.as_ref(), "kigi/task/kill");
|
||||
let params: serde_json::Value = serde_json::from_str(request.params.get()).unwrap();
|
||||
assert_eq!(params["sessionId"], "sess-1");
|
||||
assert_eq!(params["taskId"], "task-42");
|
||||
@@ -1810,7 +1810,7 @@ mod tests {
|
||||
fn reap_request_for_subagent_cancels_with_stripped_id() {
|
||||
let session_id = acp::SessionId::new("sess-1");
|
||||
let request = super::reap_request_for_key("subagent:sub-7", &session_id).unwrap();
|
||||
assert_eq!(request.method.as_ref(), "x.ai/subagent/cancel");
|
||||
assert_eq!(request.method.as_ref(), "kigi/subagent/cancel");
|
||||
let params: serde_json::Value = serde_json::from_str(request.params.get()).unwrap();
|
||||
assert_eq!(params["subagentId"], "sub-7");
|
||||
}
|
||||
@@ -2009,10 +2009,10 @@ mod tests {
|
||||
#[test]
|
||||
fn headless_task_backgrounded_parses_task_id() {
|
||||
// `make_ext_notif` wraps the arg under `update`, so pass
|
||||
// the inner update object (matching the real `x.ai/task_backgrounded`
|
||||
// the inner update object (matching the real `kigi/task_backgrounded`
|
||||
// wire shape: `{ "update": { "sessionUpdate": ..., "task_id": ... } }`).
|
||||
let notif = make_ext_notif(
|
||||
"x.ai/task_backgrounded",
|
||||
"kigi/task_backgrounded",
|
||||
serde_json::json!({
|
||||
"sessionUpdate": "task_backgrounded",
|
||||
"task_id": "task-abc",
|
||||
@@ -2027,7 +2027,7 @@ mod tests {
|
||||
#[test]
|
||||
fn headless_task_backgrounded_with_monitor_description_is_monitor() {
|
||||
let notif = make_ext_notif(
|
||||
"x.ai/task_backgrounded",
|
||||
"kigi/task_backgrounded",
|
||||
serde_json::json!({
|
||||
"sessionUpdate": "task_backgrounded",
|
||||
"task_id": "mon-1",
|
||||
@@ -2048,7 +2048,7 @@ mod tests {
|
||||
// this test guards against a future `rename_all = "camelCase"` on
|
||||
// `TaskSnapshot` silently turning waiting into a no-op.
|
||||
let notif = make_ext_notif(
|
||||
"x.ai/task_completed",
|
||||
"kigi/task_completed",
|
||||
serde_json::json!({
|
||||
"sessionUpdate": "task_completed",
|
||||
"task_snapshot": { "task_id": "task-abc" }
|
||||
@@ -2063,7 +2063,7 @@ mod tests {
|
||||
#[test]
|
||||
fn headless_subagent_spawned_and_finished_parse() {
|
||||
let spawned = make_ext_notif(
|
||||
"x.ai/session_notification",
|
||||
"kigi/session_notification",
|
||||
serde_json::json!({
|
||||
"sessionUpdate": "subagent_spawned",
|
||||
"subagent_id": "sub-1",
|
||||
@@ -2078,7 +2078,7 @@ mod tests {
|
||||
ExtEvent::SubagentSpawned { subagent_id } if subagent_id == "sub-1"
|
||||
));
|
||||
let finished = make_ext_notif(
|
||||
"x.ai/session_notification",
|
||||
"kigi/session_notification",
|
||||
serde_json::json!({
|
||||
"sessionUpdate": "subagent_finished",
|
||||
"subagent_id": "sub-1",
|
||||
@@ -2107,7 +2107,7 @@ mod tests {
|
||||
let raw = serde_json::value::to_raw_value(&payload).unwrap();
|
||||
let (tx, _rx) = tokio::sync::oneshot::channel();
|
||||
let notif = kigi_acp_lib::AcpArgs {
|
||||
request: acp::ExtNotification::new("x.ai/other", raw.into()),
|
||||
request: acp::ExtNotification::new("kigi/other", raw.into()),
|
||||
response_tx: tx,
|
||||
}
|
||||
.boxed();
|
||||
|
||||
@@ -340,7 +340,7 @@ impl ScrollConfig {
|
||||
| TerminalName::Windsurf
|
||||
| TerminalName::Zed => 1,
|
||||
TerminalName::Kitty => 3,
|
||||
TerminalName::GrokDesktop
|
||||
TerminalName::KigiDesktop
|
||||
| TerminalName::Vte
|
||||
| TerminalName::Terminator
|
||||
| TerminalName::WindowsTerminal
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
//! kigi-tui — Grok Build TUI.
|
||||
//! kigi-tui — Kigi TUI.
|
||||
//!
|
||||
//! A clean-room implementation built on the v3 pager rendering engine.
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user