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

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

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

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

Gates: §9 grep 0; fmt clean; workspace check/clippy 0/0 (-D warnings);
FULL cargo test --workspace: 234 suites, 21,961 passed, 0 failed;
deny advisories ok.
This commit is contained in:
2026-07-18 02:48:46 -04:00
parent 86e3724310
commit 6f31415ed6
1056 changed files with 8410 additions and 18307 deletions
@@ -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(&notif).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(&notif, &mut app));
let agent = app.agents.get(&AgentId(0)).unwrap();
@@ -232,7 +232,7 @@
meta: None,
};
let raw = serde_json::value::to_raw_value(&notif).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(&notif, &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(&params).unwrap().into(),
);
let affected = handle_ext_notification(&notif, &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(&params).unwrap().into(),
);
let affected = handle_ext_notification(&notif, &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(&params).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(&notif, &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(&params).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(&params).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(&params).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(&params).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(&params).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(&params).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(&params).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(&notif).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(&notif).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(&notif).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(&notif).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(&notif).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(&notif).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(&notif).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(&notif).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(&notif, &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(&notif, &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(&notif, &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(&notif, &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(&notif, &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(&notif, &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(&notif, &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(&notif, &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(&notif, &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(&notif, &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(&params).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(&params).unwrap().into(),
);
handle_prompt_complete(&notif, &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(&notif, &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(&notif, &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);