§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:
@@ -759,7 +759,7 @@ mod tests {
|
||||
let output = ToolOutput::Todo(TodoWriteOutput::TodosUpdated(TodoWriteSuccess {
|
||||
summary_for_prompt: "tasks".to_string(),
|
||||
todos: vec![],
|
||||
state: kigi_tools::implementations::grok_build::todo::TodoState::default(),
|
||||
state: kigi_tools::implementations::kigi::todo::TodoState::default(),
|
||||
}));
|
||||
let update = acp_tool_update(&output, "call-1", None, None).unwrap();
|
||||
assert_eq!(update.fields.status, Some(acp::ToolCallStatus::Completed));
|
||||
@@ -768,7 +768,7 @@ mod tests {
|
||||
#[test]
|
||||
fn test_turn_end_plan_cleanup_preserves_semantics_and_priority() {
|
||||
use crate::tools::todo::plan_entry_from_todo_item;
|
||||
use kigi_tools::implementations::grok_build::todo::{TodoItem, TodoPriority, TodoStatus};
|
||||
use kigi_tools::implementations::kigi::todo::{TodoItem, TodoPriority, TodoStatus};
|
||||
|
||||
// Simulate a mixed todo list at turn end.
|
||||
let items = [
|
||||
@@ -836,13 +836,13 @@ mod tests {
|
||||
fn test_acp_plan_update_todo() {
|
||||
let output = ToolOutput::Todo(TodoWriteOutput::TodosUpdated(TodoWriteSuccess {
|
||||
summary_for_prompt: "tasks".to_string(),
|
||||
todos: vec![kigi_tools::implementations::grok_build::todo::TodoItem {
|
||||
todos: vec![kigi_tools::implementations::kigi::todo::TodoItem {
|
||||
content: "Task 1".to_string(),
|
||||
priority: kigi_tools::implementations::grok_build::todo::TodoPriority::Medium,
|
||||
status: kigi_tools::implementations::grok_build::todo::TodoStatus::Completed,
|
||||
priority: kigi_tools::implementations::kigi::todo::TodoPriority::Medium,
|
||||
status: kigi_tools::implementations::kigi::todo::TodoStatus::Completed,
|
||||
meta: None,
|
||||
}],
|
||||
state: kigi_tools::implementations::grok_build::todo::TodoState::default(),
|
||||
state: kigi_tools::implementations::kigi::todo::TodoState::default(),
|
||||
}));
|
||||
let plan = acp_plan_update(&output).unwrap();
|
||||
assert_eq!(plan.entries.len(), 1);
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
//! In-process SDK MCP servers over the ACP reverse channel (`x.ai/mcp/sdk_call`).
|
||||
//! In-process SDK MCP servers over the ACP reverse channel (`kigi/mcp/sdk_call`).
|
||||
//!
|
||||
//! The official `grok-agent-sdk` lets a host define in-process tools (`@tool` /
|
||||
//! The official `kigi-agent-sdk` lets a host define in-process tools (`@tool` /
|
||||
//! `create_sdk_mcp_server`). When `transport="acp"`, the SDK registers them in
|
||||
//! `session/new` `_meta["x.ai/mcp/servers"] = [{ "name", "serverId" }]` and the agent
|
||||
//! `session/new` `_meta["kigi/mcp/servers"] = [{ "name", "serverId" }]` and the agent
|
||||
//! invokes their tools by sending each MCP JSON-RPC message back to the client as a
|
||||
//! reverse `x.ai/mcp/sdk_call` request — handled here by [`GatewayAcpInvoker`].
|
||||
//! reverse `kigi/mcp/sdk_call` request — handled here by [`GatewayAcpInvoker`].
|
||||
//!
|
||||
//! NOTE: the *reverse* route (agent -> client, `x.ai/mcp/sdk_call`) invokes a tool that
|
||||
//! NOTE: the *reverse* route (agent -> client, `kigi/mcp/sdk_call`) invokes a tool that
|
||||
//! lives in the SDK's process. It is the zero-IPC mirror of the *forward* route (client
|
||||
//! -> agent, `x.ai/mcp/call` in `extensions::mcp`), which invokes a tool on a server the
|
||||
//! -> agent, `kigi/mcp/call` in `extensions::mcp`), which invokes a tool on a server the
|
||||
//! AGENT is connected to. They use distinct method strings and sit on opposite request
|
||||
//! handlers, so they never collide.
|
||||
|
||||
@@ -20,7 +20,7 @@ use kigi_mcp::acp_transport::AcpReverseInvoker;
|
||||
use kigi_mcp::servers::AcpServerEntry;
|
||||
use kigi_mcp::wire;
|
||||
|
||||
/// Parse `_meta["x.ai/mcp/servers"]` into [`AcpServerEntry`] registrations. Each entry
|
||||
/// Parse `_meta["kigi/mcp/servers"]` into [`AcpServerEntry`] registrations. Each entry
|
||||
/// is deserialized directly into the canonical type (so the `serverId` wire field is
|
||||
/// serde-checked, not hand-read); entries missing `name`/`serverId` are skipped with a
|
||||
/// warning. A name seen twice keeps the first (server names are the tool namespace, so a
|
||||
@@ -38,12 +38,12 @@ pub fn parse_acp_mcp_servers(meta: Option<&acp::Meta>) -> Vec<AcpServerEntry> {
|
||||
let server: AcpServerEntry = match serde_json::from_value(entry.clone()) {
|
||||
Ok(server) => server,
|
||||
Err(err) => {
|
||||
tracing::warn!(entry = %entry, %err, "ignoring malformed x.ai/mcp/servers entry");
|
||||
tracing::warn!(entry = %entry, %err, "ignoring malformed kigi/mcp/servers entry");
|
||||
continue;
|
||||
}
|
||||
};
|
||||
if !seen.insert(server.name.clone()) {
|
||||
tracing::warn!(name = %server.name, "ignoring duplicate x.ai/mcp/servers entry");
|
||||
tracing::warn!(name = %server.name, "ignoring duplicate kigi/mcp/servers entry");
|
||||
continue;
|
||||
}
|
||||
servers.push(server);
|
||||
@@ -53,7 +53,7 @@ pub fn parse_acp_mcp_servers(meta: Option<&acp::Meta>) -> Vec<AcpServerEntry> {
|
||||
|
||||
/// Reverse-RPC invoker for in-process SDK MCP servers.
|
||||
///
|
||||
/// Each [`invoke`](AcpReverseInvoker::invoke) sends one `x.ai/mcp/sdk_call` reverse request
|
||||
/// Each [`invoke`](AcpReverseInvoker::invoke) sends one `kigi/mcp/sdk_call` reverse request
|
||||
/// straight through the gateway. `AcpAgentGatewaySender::send` returns a `Send` future
|
||||
/// (unlike the `?Send` `acp::Client::ext_method` trait method), so the rmcp transport's
|
||||
/// `Send` invoker bound is satisfied with no relay task. Calls are independent and may
|
||||
@@ -68,7 +68,7 @@ impl GatewayAcpInvoker {
|
||||
}
|
||||
}
|
||||
|
||||
/// Reverse `x.ai/mcp/sdk_call` params. Declares the on-wire field names once (mirrors
|
||||
/// Reverse `kigi/mcp/sdk_call` params. Declares the on-wire field names once (mirrors
|
||||
/// the forward side's typed `McpCallRequest`) so the `serverId` literal isn't hand-spelled.
|
||||
#[derive(serde::Serialize)]
|
||||
struct SdkCallParams<'a> {
|
||||
@@ -111,7 +111,7 @@ mod tests {
|
||||
#[test]
|
||||
fn parses_valid_entries_and_skips_malformed() {
|
||||
let meta = serde_json::json!({
|
||||
"x.ai/mcp/servers": [
|
||||
"kigi/mcp/servers": [
|
||||
{ "name": "harness-tools", "serverId": "srv_0" },
|
||||
{ "name": "missing-id" },
|
||||
{ "serverId": "no_name" },
|
||||
@@ -126,7 +126,7 @@ mod tests {
|
||||
#[test]
|
||||
fn duplicate_names_keep_the_first() {
|
||||
let meta = serde_json::json!({
|
||||
"x.ai/mcp/servers": [
|
||||
"kigi/mcp/servers": [
|
||||
{ "name": "tools", "serverId": "srv_0" },
|
||||
{ "name": "tools", "serverId": "srv_1" },
|
||||
]
|
||||
|
||||
@@ -61,7 +61,7 @@ use kigi_sampler::SamplerConfig as SamplingConfig;
|
||||
use kigi_sampling_types::truncate_bytes;
|
||||
use kigi_tools::computer::local::LocalTerminalBackend;
|
||||
use kigi_tools::implementations::BashToolInput;
|
||||
use kigi_tools::implementations::grok_build::web_fetch::WebFetchConfig;
|
||||
use kigi_tools::implementations::kigi::web_fetch::WebFetchConfig;
|
||||
use kigi_tools::types::ToolInput;
|
||||
use kigi_tools::types::compat::CompatConfig;
|
||||
use kigi_tools::types::output::{
|
||||
@@ -178,7 +178,7 @@ mod spawn;
|
||||
use super::acp_types::*;
|
||||
pub use spawn::SessionThread;
|
||||
pub(crate) use spawn::*;
|
||||
/// Client-registered hook gates (the `x.ai/hooks/run` reverse request).
|
||||
/// Client-registered hook gates (the `kigi/hooks/run` reverse request).
|
||||
mod hooks;
|
||||
pub(crate) struct InputItem {
|
||||
pub(crate) prompt_id: String,
|
||||
@@ -461,7 +461,7 @@ pub(crate) struct SessionActor {
|
||||
/// Server-side doom-loop check policy, resolved once at spawn by
|
||||
/// `Config::resolve_doom_loop_recovery`; `None` = disabled.
|
||||
/// `reconstruct_full_config` threads it into the sampler config, and the
|
||||
/// sampler itself sends the matching `x-grok-doom-loop-check` header.
|
||||
/// sampler itself sends the matching `x-kigi-doom-loop-check` header.
|
||||
pub(crate) doom_loop_recovery: Option<kigi_sampling_types::DoomLoopRecoveryPolicy>,
|
||||
/// Telemetry-only per-turn doom-loop recovery tally (attempts, whether a
|
||||
/// budget-spent accept happened, tightest trigger label). Accumulated by
|
||||
@@ -524,10 +524,10 @@ pub(crate) struct SessionActor {
|
||||
/// Wrapped in `RefCell` for mid-session mutation (skill refresh, prompt regen).
|
||||
/// Safe: session actor is single-threaded (LocalSet), no concurrent access.
|
||||
pub(crate) agent: std::cell::RefCell<kigi_agent::Agent>,
|
||||
/// Dedup slot for `x.ai/git_head_changed`, shared with the fs-watch
|
||||
/// Dedup slot for `kigi/git_head_changed`, shared with the fs-watch
|
||||
/// `GitHead` consumer (see `git_head_dedup_key`).
|
||||
pub(crate) last_reported_branch: Arc<parking_lot::Mutex<Option<String>>>,
|
||||
/// Client opted into `x.ai/gitHeadChanged`. When false (headless/SDK),
|
||||
/// Client opted into `kigi/gitHeadChanged`. When false (headless/SDK),
|
||||
/// `maybe_notify_git_branch` no-ops — no git subprocess.
|
||||
git_head_enabled: bool,
|
||||
/// Shared models manager for etag-triggered refresh from response headers.
|
||||
@@ -615,7 +615,7 @@ pub(crate) struct SessionActor {
|
||||
pub(crate) goal_update_rx: std::cell::RefCell<
|
||||
Option<
|
||||
tokio::sync::mpsc::UnboundedReceiver<
|
||||
kigi_tools::implementations::grok_build::update_goal::UpdateGoalEnvelope,
|
||||
kigi_tools::implementations::kigi::update_goal::UpdateGoalEnvelope,
|
||||
>,
|
||||
>,
|
||||
>,
|
||||
@@ -624,7 +624,7 @@ pub(crate) struct SessionActor {
|
||||
/// empty ToolBridge. The `rx` half is owned by the drainer task (see
|
||||
/// `goal_update_rx`).
|
||||
pub(crate) goal_update_tx: tokio::sync::mpsc::UnboundedSender<
|
||||
kigi_tools::implementations::grok_build::update_goal::UpdateGoalEnvelope,
|
||||
kigi_tools::implementations::kigi::update_goal::UpdateGoalEnvelope,
|
||||
>,
|
||||
/// Resolved master kill-switch for the verification stage (the
|
||||
/// adversarial skeptic panel). `false` short-circuits
|
||||
@@ -690,7 +690,7 @@ pub(crate) struct SessionActor {
|
||||
/// time; only the input is parked here for the TurnEnd drain to
|
||||
/// run through the verification stage.
|
||||
pub(crate) pending_classifier_completions: parking_lot::Mutex<
|
||||
VecDeque<kigi_tools::implementations::grok_build::update_goal::UpdateGoalInput>,
|
||||
VecDeque<kigi_tools::implementations::kigi::update_goal::UpdateGoalInput>,
|
||||
>,
|
||||
/// Per-session re-entry guard for the verification stage. Set with
|
||||
/// `compare_exchange(false, true)` at fire-entry and cleared on
|
||||
@@ -742,7 +742,7 @@ pub(crate) struct SessionActor {
|
||||
/// Wrapped in `RefCell` for mid-session reload from `&self` methods.
|
||||
/// Safe: session actor is single-threaded (LocalSet), no concurrent access.
|
||||
pub(crate) hook_registry: std::cell::RefCell<Option<Arc<kigi_hooks::discovery::HookRegistry>>>,
|
||||
/// Client hooks from `session/new` `_meta["x.ai/hooks"]`; gated in
|
||||
/// Client hooks from `session/new` `_meta["kigi/hooks"]`; gated in
|
||||
/// [`crate::session::acp_session::hooks`]. `RefCell` so `load_session` reconnect can
|
||||
/// replace the set on the live actor (see `SessionCommand::SetClientHooks`).
|
||||
pub(crate) client_hooks: std::cell::RefCell<crate::extensions::hooks::ClientHooks>,
|
||||
@@ -981,7 +981,7 @@ impl SessionActor {
|
||||
memory_configured: self.memory.backend_params.is_some(),
|
||||
scheduler: tool_names
|
||||
.iter()
|
||||
.any(|n| n == kigi_tools::implementations::grok_build::SCHEDULER_CREATE_TOOL_NAME),
|
||||
.any(|n| n == kigi_tools::implementations::kigi::SCHEDULER_CREATE_TOOL_NAME),
|
||||
hooks: self.hook_registry.borrow().is_some(),
|
||||
plugins: self.plugin_registry.borrow().is_some(),
|
||||
goal,
|
||||
@@ -1039,7 +1039,7 @@ const PROMPT_CONTEXT_FILENAME: &str = "prompt_context.json";
|
||||
/// Persist the structured prompt context to `{session_dir}/prompt_context.json`.
|
||||
///
|
||||
/// This is best-effort: failures are logged but do not block session creation.
|
||||
/// The saved JSON enables deterministic re-rendering, `grok prompt --json`
|
||||
/// The saved JSON enables deterministic re-rendering, `kigi prompt --json`
|
||||
/// inspection, and post-hoc debugging of what went into a session's system prompt.
|
||||
fn save_prompt_context(session_info: &SessionInfo, prompt_context: &kigi_agent::PromptContext) {
|
||||
let dir = crate::session::persistence::session_dir(session_info);
|
||||
@@ -1232,7 +1232,7 @@ mod turn_completion_emit_tests;
|
||||
mod usage_categories_tests;
|
||||
#[cfg(test)]
|
||||
mod tool_meta_stamp_tests {
|
||||
//! Pin the `x.ai/tool` stamps on the harness emission paths: the early
|
||||
//! Pin the `kigi/tool` stamps on the harness emission paths: the early
|
||||
//! ToolCall registered by `prepare_tool_call` and the permission-request
|
||||
//! ToolCallUpdate (a dropped `stamp_tool_meta` call would regress silently).
|
||||
use super::replay_buffer_send_update_tests::make_replay_send_update_fixture;
|
||||
@@ -1252,7 +1252,7 @@ mod tool_meta_stamp_tests {
|
||||
},
|
||||
}
|
||||
}
|
||||
/// The `x.ai/tool` object from an event's `_meta`, if present.
|
||||
/// The `kigi/tool` object from an event's `_meta`, if present.
|
||||
fn tool_meta(meta: Option<&acp::Meta>) -> Option<&serde_json::Value> {
|
||||
meta.and_then(|m| m.get(TOOL_META_KEY))
|
||||
}
|
||||
@@ -1263,10 +1263,8 @@ mod tool_meta_stamp_tests {
|
||||
.run_until(async {
|
||||
let mut fixture = make_replay_send_update_fixture().await;
|
||||
fixture.actor.agent = std::cell::RefCell::new(
|
||||
test_agent_with_tools(vec![ToolConfig::from_id(
|
||||
"GrokBuild:read_file".to_string(),
|
||||
)])
|
||||
.await,
|
||||
test_agent_with_tools(vec![ToolConfig::from_id("Kigi:read_file".to_string())])
|
||||
.await,
|
||||
);
|
||||
let prepared = fixture
|
||||
.actor
|
||||
@@ -1289,13 +1287,13 @@ mod tool_meta_stamp_tests {
|
||||
}
|
||||
}
|
||||
let early = early.expect("early ToolCall emitted");
|
||||
let t = tool_meta(early.as_ref()).expect("early ToolCall carries x.ai/tool");
|
||||
let t = tool_meta(early.as_ref()).expect("early ToolCall carries kigi/tool");
|
||||
assert_eq!(t["name"], "read_file");
|
||||
assert_eq!(t["kind"], "read");
|
||||
assert_eq!(t["namespace"], "grok_build");
|
||||
assert_eq!(t["namespace"], "kigi");
|
||||
assert!(t.get("input").is_none(), "identity-only before parse");
|
||||
let refined = refined.expect("refinement ToolCallUpdate emitted");
|
||||
let t = tool_meta(refined.as_ref()).expect("refinement carries x.ai/tool");
|
||||
let t = tool_meta(refined.as_ref()).expect("refinement carries kigi/tool");
|
||||
assert_eq!(t["input"]["path"], "/tmp/stamp.txt");
|
||||
})
|
||||
.await;
|
||||
@@ -1307,10 +1305,8 @@ mod tool_meta_stamp_tests {
|
||||
.run_until(async {
|
||||
let mut fixture = make_replay_send_update_fixture().await;
|
||||
fixture.actor.agent = std::cell::RefCell::new(
|
||||
test_agent_with_tools(vec![ToolConfig::from_id(
|
||||
"GrokBuild:read_file".to_string(),
|
||||
)])
|
||||
.await,
|
||||
test_agent_with_tools(vec![ToolConfig::from_id("Kigi:read_file".to_string())])
|
||||
.await,
|
||||
);
|
||||
let (perm_tx, mut perm_rx) = mpsc::unbounded_channel();
|
||||
fixture.actor.permissions = PermissionHandle::Actor {
|
||||
@@ -1350,7 +1346,7 @@ mod tool_meta_stamp_tests {
|
||||
.take()
|
||||
.expect("permission request must have been issued");
|
||||
let t = tool_meta(update.meta.as_ref())
|
||||
.expect("permission-request ToolCallUpdate carries x.ai/tool");
|
||||
.expect("permission-request ToolCallUpdate carries kigi/tool");
|
||||
assert_eq!(t["name"], "read_file");
|
||||
assert_eq!(t["kind"], "read");
|
||||
assert_eq!(t["input"]["path"], "/tmp/stamp.txt");
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
//! Client-registered hooks for [`SessionActor`].
|
||||
//!
|
||||
//! Hooks registered at `session/new` (`_meta["x.ai/hooks"]`) come in two flavors,
|
||||
//! Hooks registered at `session/new` (`_meta["kigi/hooks"]`) come in two flavors,
|
||||
//! both matched by the agent ([`kigi_hooks::matcher::HookMatcher`], shared with
|
||||
//! file hooks):
|
||||
//! - **`PreToolUse` gate**: an awaited reverse *request* `x.ai/hooks/run`; a `deny`
|
||||
//! - **`PreToolUse` gate**: an awaited reverse *request* `kigi/hooks/run`; a `deny`
|
||||
//! blocks the tool.
|
||||
//! - **All other events**: fire-and-forget *notifications* `x.ai/hooks/event`,
|
||||
//! - **All other events**: fire-and-forget *notifications* `kigi/hooks/event`,
|
||||
//! observe-only (the callback's return is ignored). Sent per matching callback.
|
||||
|
||||
use std::sync::Arc;
|
||||
@@ -23,10 +23,10 @@ use crate::extensions::hooks::{
|
||||
};
|
||||
use crate::sampling::types::ToolCallResponse;
|
||||
|
||||
const HOOK_EVENT_METHOD: &str = "x.ai/hooks/event";
|
||||
const HOOK_RUN_METHOD: &str = "x.ai/hooks/run";
|
||||
const HOOK_EVENT_METHOD: &str = "kigi/hooks/event";
|
||||
const HOOK_RUN_METHOD: &str = "kigi/hooks/run";
|
||||
|
||||
/// Default per-callback bound for a client's `x.ai/hooks/run` reply; on timeout the gate
|
||||
/// Default per-callback bound for a client's `kigi/hooks/run` reply; on timeout the gate
|
||||
/// fails open (the tool proceeds).
|
||||
///
|
||||
/// Some external hosts default to 600s per hook; we default to 30s because our gate sits
|
||||
@@ -47,7 +47,7 @@ enum ClientHookGateOutcome {
|
||||
UnknownDecision,
|
||||
}
|
||||
|
||||
/// Outcome of the `x.ai/hooks/run` reverse request, before interpreting it as a
|
||||
/// Outcome of the `kigi/hooks/run` reverse request, before interpreting it as a
|
||||
/// decision. Separate so [`classify`] stays pure and unit-testable.
|
||||
enum ReverseOutcome {
|
||||
Responded(Arc<RawValue>),
|
||||
@@ -68,7 +68,7 @@ fn classify(outcome: ReverseOutcome) -> (ClientHookResponse, ClientHookGateOutco
|
||||
ClientHookDecision::Deny => ClientHookGateOutcome::Denied,
|
||||
ClientHookDecision::Other => {
|
||||
tracing::warn!(
|
||||
"x.ai/hooks/run returned an unknown decision value; failing open"
|
||||
"kigi/hooks/run returned an unknown decision value; failing open"
|
||||
);
|
||||
ClientHookGateOutcome::UnknownDecision
|
||||
}
|
||||
@@ -77,7 +77,7 @@ fn classify(outcome: ReverseOutcome) -> (ClientHookResponse, ClientHookGateOutco
|
||||
(resp, label)
|
||||
}
|
||||
Err(err) => {
|
||||
tracing::warn!(%err, "malformed x.ai/hooks/run response; failing open");
|
||||
tracing::warn!(%err, "malformed kigi/hooks/run response; failing open");
|
||||
(
|
||||
ClientHookResponse::default(),
|
||||
ClientHookGateOutcome::Malformed,
|
||||
@@ -86,14 +86,14 @@ fn classify(outcome: ReverseOutcome) -> (ClientHookResponse, ClientHookGateOutco
|
||||
}
|
||||
}
|
||||
ReverseOutcome::Transport(err) => {
|
||||
tracing::warn!(%err, "x.ai/hooks/run transport error (no client wired?); failing open");
|
||||
tracing::warn!(%err, "kigi/hooks/run transport error (no client wired?); failing open");
|
||||
(
|
||||
ClientHookResponse::default(),
|
||||
ClientHookGateOutcome::TransportError,
|
||||
)
|
||||
}
|
||||
ReverseOutcome::Timeout => {
|
||||
tracing::warn!("x.ai/hooks/run timed out; failing open");
|
||||
tracing::warn!("kigi/hooks/run timed out; failing open");
|
||||
(
|
||||
ClientHookResponse::default(),
|
||||
ClientHookGateOutcome::TimedOut,
|
||||
@@ -206,7 +206,7 @@ impl SessionActor {
|
||||
}
|
||||
|
||||
/// Run the client-registered `PreToolUse` hooks for `call`, firing
|
||||
/// `x.ai/hooks/run` once per matching callback with the shared `envelope` (the
|
||||
/// `kigi/hooks/run` once per matching callback with the shared `envelope` (the
|
||||
/// same payload file hooks and observe events receive).
|
||||
///
|
||||
/// Returns `Some(ToolLoop::HookDenied)` on the first deny, else `None`.
|
||||
@@ -285,7 +285,7 @@ impl SessionActor {
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
/// Issue one `x.ai/hooks/run` reverse request, bounded by a per-callback `timeout`.
|
||||
/// Issue one `kigi/hooks/run` reverse request, bounded by a per-callback `timeout`.
|
||||
async fn send_hook_run(
|
||||
&self,
|
||||
dispatch: &ClientHookDispatch<'_>,
|
||||
@@ -305,7 +305,7 @@ impl SessionActor {
|
||||
}
|
||||
|
||||
/// Fire observe-only client hooks for `envelope`'s event: send an
|
||||
/// `x.ai/hooks/event` notification to each matching registered callback.
|
||||
/// `kigi/hooks/event` notification to each matching registered callback.
|
||||
/// Fire-and-forget (no decision is consumed); independent of file hooks, so it
|
||||
/// runs even when no on-disk hook registry exists. No-op when nothing is registered.
|
||||
pub(super) fn notify_client_hooks(&self, envelope: &HookEventEnvelope) {
|
||||
|
||||
@@ -22,7 +22,7 @@ impl RoleCapability {
|
||||
/// `can_execute` for terminal/bash).
|
||||
fn is_satisfied(
|
||||
self,
|
||||
summary: &kigi_tools::implementations::grok_build::task::types::SubagentTypeSummary,
|
||||
summary: &kigi_tools::implementations::kigi::task::types::SubagentTypeSummary,
|
||||
) -> bool {
|
||||
match self {
|
||||
Self::Skeptic => summary.can_read && summary.can_search,
|
||||
@@ -42,7 +42,7 @@ pub(crate) struct PanelResolveCache {
|
||||
/// result for the role's `general-purpose` toolset on that harness).
|
||||
describe: std::collections::HashMap<
|
||||
String,
|
||||
kigi_tools::implementations::grok_build::task::types::SubagentDescribeOutcome,
|
||||
kigi_tools::implementations::kigi::task::types::SubagentDescribeOutcome,
|
||||
>,
|
||||
}
|
||||
|
||||
@@ -57,7 +57,7 @@ fn role_tool_names_from(
|
||||
cache: &PanelResolveCache,
|
||||
inherit: &crate::session::goal_role_tools::RoleToolNames,
|
||||
) -> crate::session::goal_role_tools::RoleToolNames {
|
||||
use kigi_tools::implementations::grok_build::task::types::SubagentDescribeOutcome;
|
||||
use kigi_tools::implementations::kigi::task::types::SubagentDescribeOutcome;
|
||||
// `override_.agent_type` is the committed harness; the cache is keyed on it.
|
||||
match override_.agent_type.as_deref() {
|
||||
Some(harness) => match cache.describe.get(harness) {
|
||||
@@ -115,9 +115,9 @@ impl SessionActor {
|
||||
&self,
|
||||
current_tokens: i64,
|
||||
purpose: DrainPurpose,
|
||||
extra: Vec<kigi_tools::implementations::grok_build::update_goal::UpdateGoalEnvelope>,
|
||||
extra: Vec<kigi_tools::implementations::kigi::update_goal::UpdateGoalEnvelope>,
|
||||
) {
|
||||
use kigi_tools::implementations::grok_build::update_goal::{RejectReason, UpdateGoalAck};
|
||||
use kigi_tools::implementations::kigi::update_goal::{RejectReason, UpdateGoalAck};
|
||||
// The `update_goal` tool and its `GoalUpdateHandle` are always
|
||||
// registered (see `spawn_session_actor`), so a model can call
|
||||
// `update_goal` in a session that never entered goal mode — e.g. any
|
||||
@@ -650,10 +650,10 @@ impl SessionActor {
|
||||
attempt: u32,
|
||||
outcome: crate::session::goal_classifier::GoalClassifierOutcome,
|
||||
notify: &crate::session::goal_orchestrator::GoalNotifySender,
|
||||
) -> kigi_tools::implementations::grok_build::update_goal::UpdateGoalAck {
|
||||
) -> kigi_tools::implementations::kigi::update_goal::UpdateGoalAck {
|
||||
use crate::session::goal_classifier::GoalClassifierOutcome;
|
||||
use crate::session::goal_tracker::GoalClassifierVerdict;
|
||||
use kigi_tools::implementations::grok_build::update_goal::UpdateGoalAck;
|
||||
use kigi_tools::implementations::kigi::update_goal::UpdateGoalAck;
|
||||
|
||||
let current_tokens = self.chat_state_handle.get_total_tokens().await as i64;
|
||||
let (tokens_used, finished_marginal) = self.goal_tokens(current_tokens);
|
||||
@@ -1299,7 +1299,7 @@ impl SessionActor {
|
||||
choice: &crate::agent::config::GoalRoleModelChoice,
|
||||
capability: RoleCapability,
|
||||
event_tx: &tokio::sync::mpsc::UnboundedSender<
|
||||
kigi_tools::implementations::grok_build::task::types::SubagentEvent,
|
||||
kigi_tools::implementations::kigi::task::types::SubagentEvent,
|
||||
>,
|
||||
) -> (
|
||||
crate::session::goal_planner::RoleSpawnOverride,
|
||||
@@ -1372,17 +1372,15 @@ impl SessionActor {
|
||||
pair: &crate::util::config::GoalRoleModel,
|
||||
capability: RoleCapability,
|
||||
event_tx: &tokio::sync::mpsc::UnboundedSender<
|
||||
kigi_tools::implementations::grok_build::task::types::SubagentEvent,
|
||||
kigi_tools::implementations::kigi::task::types::SubagentEvent,
|
||||
>,
|
||||
available_models: &indexmap::IndexMap<String, crate::agent::config::ModelEntry>,
|
||||
cache: &mut PanelResolveCache,
|
||||
) -> crate::session::goal_planner::RoleSpawnOverride {
|
||||
use crate::session::events::{Event, GoalRoleModelFailOpenReason as Reason};
|
||||
use crate::session::goal_planner::RoleSpawnOverride;
|
||||
use kigi_tools::implementations::grok_build::task::backend::{
|
||||
ChannelBackend, SubagentBackend,
|
||||
};
|
||||
use kigi_tools::implementations::grok_build::task::types::SubagentDescribeOutcome;
|
||||
use kigi_tools::implementations::kigi::task::backend::{ChannelBackend, SubagentBackend};
|
||||
use kigi_tools::implementations::kigi::task::types::SubagentDescribeOutcome;
|
||||
|
||||
let fail_open = |reason: Reason| {
|
||||
self.emit_event(Event::GoalRoleModelFailOpen {
|
||||
@@ -1408,7 +1406,7 @@ impl SessionActor {
|
||||
}
|
||||
// 2b. Reject a STRICT harness whose flavor isn't representable (e.g.
|
||||
// `codex`): it resolves, but `resolve_subagent_toolset` would
|
||||
// silently run grok-build flavor. Non-strict names (grok-build
|
||||
// silently run kigi flavor. Non-strict names (kigi
|
||||
// family) run the default flavor and pass; unresolvable names fall
|
||||
// through to the describe `Unknown` arm below.
|
||||
if kigi_agent::config::is_strict_harness_agent_type(&pair.agent_type)
|
||||
@@ -1511,9 +1509,9 @@ impl SessionActor {
|
||||
/// toolset; [`RoleToolNames::from_parent`] applies the literal fallback for
|
||||
/// any kind the bridge lacks, and resolves `{WRITE_TOOL}` from the parent
|
||||
/// `Edit` tool when the bridge has no `Write` (so the inherit / retry render
|
||||
/// agrees with `from_summary` — `search_replace` on the default grok-build
|
||||
/// agrees with `from_summary` — `search_replace` on the default kigi
|
||||
/// host, not the literal `write`). The `{TOOLSET_TOOLS}` block is empty on
|
||||
/// this path (no per-role toolset enumeration); on the default grok-build
|
||||
/// this path (no per-role toolset enumeration); on the default kigi
|
||||
/// host the bridge resolves the parent's real tool ids (`read_file`, …).
|
||||
pub(crate) async fn resolve_inherit_role_tool_names(
|
||||
&self,
|
||||
@@ -2340,7 +2338,7 @@ impl SessionActor {
|
||||
#[cfg(test)]
|
||||
mod role_capability_tests {
|
||||
use super::RoleCapability;
|
||||
use kigi_tools::implementations::grok_build::task::types::SubagentTypeSummary;
|
||||
use kigi_tools::implementations::kigi::task::types::SubagentTypeSummary;
|
||||
|
||||
fn summary(can_read: bool, can_search: bool, can_execute: bool) -> SubagentTypeSummary {
|
||||
SubagentTypeSummary {
|
||||
@@ -2371,7 +2369,7 @@ mod role_tool_names_tests {
|
||||
use super::{PanelResolveCache, role_tool_names_from};
|
||||
use crate::session::goal_planner::RoleSpawnOverride;
|
||||
use crate::session::goal_role_tools::RoleToolNames;
|
||||
use kigi_tools::implementations::grok_build::task::types::{
|
||||
use kigi_tools::implementations::kigi::task::types::{
|
||||
SubagentDescribeOutcome, SubagentTypeSummary,
|
||||
};
|
||||
use kigi_tools::types::tool::ToolKind;
|
||||
|
||||
@@ -15,10 +15,10 @@ impl DrainSource {
|
||||
pub(super) fn into_parts(
|
||||
self,
|
||||
) -> (
|
||||
kigi_tools::implementations::grok_build::update_goal::UpdateGoalInput,
|
||||
kigi_tools::implementations::kigi::update_goal::UpdateGoalInput,
|
||||
Option<
|
||||
tokio::sync::oneshot::Sender<
|
||||
kigi_tools::implementations::grok_build::update_goal::UpdateGoalAck,
|
||||
kigi_tools::implementations::kigi::update_goal::UpdateGoalAck,
|
||||
>,
|
||||
>,
|
||||
) {
|
||||
@@ -33,11 +33,9 @@ impl DrainSource {
|
||||
/// source). No-op for `Pending` source — its ack was already resolved.
|
||||
pub(super) fn try_send_ack(
|
||||
ack_tx: Option<
|
||||
tokio::sync::oneshot::Sender<
|
||||
kigi_tools::implementations::grok_build::update_goal::UpdateGoalAck,
|
||||
>,
|
||||
tokio::sync::oneshot::Sender<kigi_tools::implementations::kigi::update_goal::UpdateGoalAck>,
|
||||
>,
|
||||
ack: kigi_tools::implementations::grok_build::update_goal::UpdateGoalAck,
|
||||
ack: kigi_tools::implementations::kigi::update_goal::UpdateGoalAck,
|
||||
) {
|
||||
if let Some(tx) = ack_tx {
|
||||
send_ack(tx, ack);
|
||||
@@ -154,9 +152,9 @@ impl<F: FnOnce(&mut crate::session::goal_tracker::GoalTracker)> Drop for Tracker
|
||||
/// the receiver was dropped (benign — tool future aborted).
|
||||
pub(super) fn send_ack(
|
||||
ack_tx: tokio::sync::oneshot::Sender<
|
||||
kigi_tools::implementations::grok_build::update_goal::UpdateGoalAck,
|
||||
kigi_tools::implementations::kigi::update_goal::UpdateGoalAck,
|
||||
>,
|
||||
ack: kigi_tools::implementations::grok_build::update_goal::UpdateGoalAck,
|
||||
ack: kigi_tools::implementations::kigi::update_goal::UpdateGoalAck,
|
||||
) {
|
||||
if ack_tx.send(ack).is_err() {
|
||||
tracing::debug!("update_goal ack receiver dropped before harness could respond");
|
||||
@@ -702,14 +700,14 @@ mod fold_tokens_by_model_tests {
|
||||
#[test]
|
||||
fn mixed_models_sum_marginals_sorted_desc() {
|
||||
let records = vec![
|
||||
rec(Some("g1"), 0, 100, Some("grok-3")),
|
||||
rec(Some("g1"), 100, 500, Some("grok-4")), // marginal 400
|
||||
rec(Some("g1"), 0, 50, Some("grok-3")), // grok-3 total 150
|
||||
rec(Some("g1"), 0, 100, Some("kigi-3")),
|
||||
rec(Some("g1"), 100, 500, Some("kigi-4")), // marginal 400
|
||||
rec(Some("g1"), 0, 50, Some("kigi-3")), // kigi-3 total 150
|
||||
];
|
||||
let out = fold_tokens_by_model(&records, "g1", "cur");
|
||||
assert_eq!(
|
||||
out,
|
||||
vec![("grok-4".to_owned(), 400), ("grok-3".to_owned(), 150)]
|
||||
vec![("kigi-4".to_owned(), 400), ("kigi-3".to_owned(), 150)]
|
||||
);
|
||||
}
|
||||
|
||||
@@ -736,38 +734,38 @@ mod fold_tokens_by_model_tests {
|
||||
#[test]
|
||||
fn single_distinct_model_collapses_to_one_entry() {
|
||||
let records = vec![
|
||||
rec(Some("g1"), 0, 100, Some("grok-4")),
|
||||
rec(Some("g1"), 0, 200, None), // folds under current = grok-4
|
||||
rec(Some("g1"), 0, 100, Some("kigi-4")),
|
||||
rec(Some("g1"), 0, 200, None), // folds under current = kigi-4
|
||||
];
|
||||
let out = fold_tokens_by_model(&records, "g1", "grok-4");
|
||||
assert_eq!(out, vec![("grok-4".to_owned(), 300)]);
|
||||
let out = fold_tokens_by_model(&records, "g1", "kigi-4");
|
||||
assert_eq!(out, vec![("kigi-4".to_owned(), 300)]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn other_goal_records_excluded() {
|
||||
let records = vec![
|
||||
rec(Some("g1"), 0, 100, Some("grok-4")),
|
||||
rec(Some("g2"), 0, 999, Some("grok-4")),
|
||||
rec(None, 0, 999, Some("grok-4")),
|
||||
rec(Some("g1"), 0, 100, Some("kigi-4")),
|
||||
rec(Some("g2"), 0, 999, Some("kigi-4")),
|
||||
rec(None, 0, 999, Some("kigi-4")),
|
||||
];
|
||||
let out = fold_tokens_by_model(&records, "g1", "cur");
|
||||
assert_eq!(out, vec![("grok-4".to_owned(), 100)]);
|
||||
assert_eq!(out, vec![("kigi-4".to_owned(), 100)]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn last_below_anchor_does_not_underflow() {
|
||||
let records = vec![rec(Some("g1"), 500, 100, Some("grok-4"))];
|
||||
let records = vec![rec(Some("g1"), 500, 100, Some("kigi-4"))];
|
||||
// marginal saturates to 0 -> skipped as a zero-token entry.
|
||||
assert!(fold_tokens_by_model(&records, "g1", "cur").is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn captured_model_survives_mid_goal_current_model_switch() {
|
||||
// A record captured `grok-4` at spawn keeps it even though the
|
||||
// current model at aggregation time is `grok-3`.
|
||||
let records = vec![rec(Some("g1"), 0, 100, Some("grok-4"))];
|
||||
let out = fold_tokens_by_model(&records, "g1", "grok-3");
|
||||
assert_eq!(out, vec![("grok-4".to_owned(), 100)]);
|
||||
// A record captured `kigi-4` at spawn keeps it even though the
|
||||
// current model at aggregation time is `kigi-3`.
|
||||
let records = vec![rec(Some("g1"), 0, 100, Some("kigi-4"))];
|
||||
let out = fold_tokens_by_model(&records, "g1", "kigi-3");
|
||||
assert_eq!(out, vec![("kigi-4".to_owned(), 100)]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -788,11 +786,11 @@ mod fold_tokens_by_model_tests {
|
||||
// An empty id folds into the SAME bucket as records that
|
||||
// explicitly captured the current model id.
|
||||
let records = vec![
|
||||
rec(Some("g1"), 0, 100, Some("grok-4")),
|
||||
rec(Some("g1"), 0, 100, Some("kigi-4")),
|
||||
rec(Some("g1"), 0, 200, Some("")),
|
||||
];
|
||||
let out = fold_tokens_by_model(&records, "g1", "grok-4");
|
||||
assert_eq!(out, vec![("grok-4".to_owned(), 300)]);
|
||||
let out = fold_tokens_by_model(&records, "g1", "kigi-4");
|
||||
assert_eq!(out, vec![("kigi-4".to_owned(), 300)]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -822,7 +820,7 @@ pub(crate) fn planner_failure_pause_message() -> String {
|
||||
}
|
||||
|
||||
pub(crate) fn goal_slash_and_harness_available(goal_enabled: bool, tool_names: &[String]) -> bool {
|
||||
use kigi_tools::implementations::grok_build::UPDATE_GOAL_TOOL_NAME;
|
||||
use kigi_tools::implementations::kigi::UPDATE_GOAL_TOOL_NAME;
|
||||
goal_enabled && tool_names.iter().any(|n| n == UPDATE_GOAL_TOOL_NAME)
|
||||
}
|
||||
|
||||
@@ -1468,9 +1466,7 @@ impl SessionActor {
|
||||
self.agent
|
||||
.borrow()
|
||||
.tool_bridge()
|
||||
.update_resource(
|
||||
kigi_tools::implementations::grok_build::task::types::GoalLoopActive(active),
|
||||
)
|
||||
.update_resource(kigi_tools::implementations::kigi::task::types::GoalLoopActive(active))
|
||||
.await;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,7 +22,7 @@ pub(crate) type PendingInterjection =
|
||||
/// converted into standalone prompt turns (arrived while idle, or after the
|
||||
/// running turn's final drain). The prefix keeps the turn's user echo
|
||||
/// persist-only: every pane already rendered the text from the
|
||||
/// `x.ai/session/interjection` broadcast, so a live echo would duplicate it.
|
||||
/// `kigi/session/interjection` broadcast, so a live echo would duplicate it.
|
||||
pub(crate) const INTERJECT_FALLBACK_PROMPT_PREFIX: &str = "interject-fallback-";
|
||||
|
||||
impl SessionActor {
|
||||
@@ -164,7 +164,7 @@ impl SessionActor {
|
||||
self.notifications
|
||||
.gateway
|
||||
.forward_fire_and_forget(acp::ExtNotification::new(
|
||||
"x.ai/session/interjection",
|
||||
"kigi/session/interjection",
|
||||
params.into(),
|
||||
));
|
||||
}
|
||||
|
||||
@@ -323,7 +323,7 @@ impl SessionActor {
|
||||
/// the sampler call goes through `prepare_chat_completion().conversation_collect()`
|
||||
/// — a side-channel direct-HTTP call that does NOT publish events
|
||||
/// on the per-session shared sampler channel. The client never
|
||||
/// sees "Grok is thinking", streaming token chunks, or any other
|
||||
/// sees "Kigi is thinking", streaming token chunks, or any other
|
||||
/// session update from a classifier fire. Stalled verdicts only
|
||||
/// queue a `<system-reminder>` into chat state via
|
||||
/// `push_system_reminder`; no `InputItem` is pushed into
|
||||
@@ -549,18 +549,18 @@ impl SessionActor {
|
||||
model: Some(model_id.clone()),
|
||||
temperature: Some(0.0),
|
||||
max_output_tokens: Some(LAZINESS_MAX_OUTPUT_TOKENS),
|
||||
// Don't pass `reasoning_effort` — `grok-4.5` (and
|
||||
// other tool-flavoured Grok variants) reject the field at
|
||||
// Don't pass `reasoning_effort` — `kigi-4.5` (and
|
||||
// other tool-flavoured Kigi variants) reject the field at
|
||||
// the proxy with `400 Bad Request: Model does not support
|
||||
// parameter reasoningEffort`. Omitting it lets each model
|
||||
// apply its own default. The classifier task is one short
|
||||
// JSON object — even on reasoning-capable models the
|
||||
// default suffices; no need to force it off.
|
||||
reasoning_effort: None,
|
||||
x_grok_conv_id: Some(session_id_str.clone()),
|
||||
x_grok_req_id: Some(format!("{LAZINESS_REQ_ID_PREFIX}{}", uuid::Uuid::new_v4())),
|
||||
x_grok_session_id: Some(session_id_str),
|
||||
x_grok_agent_id: Some(crate::util::agent_id::agent_id()),
|
||||
x_kigi_conv_id: Some(session_id_str.clone()),
|
||||
x_kigi_req_id: Some(format!("{LAZINESS_REQ_ID_PREFIX}{}", uuid::Uuid::new_v4())),
|
||||
x_kigi_session_id: Some(session_id_str),
|
||||
x_kigi_agent_id: Some(crate::util::agent_id::agent_id()),
|
||||
..ConversationRequest::default()
|
||||
};
|
||||
|
||||
|
||||
@@ -110,7 +110,7 @@ impl LazinessAbortReason {
|
||||
/// Prompt-structure mitigations against motivated reasoning:
|
||||
/// "Do not roleplay", JSON-only, no chain-of-thought,
|
||||
/// no role context, transcript framed as third-party data.
|
||||
/// Prefix on `x_grok_req_id` for laziness-classifier sampler calls.
|
||||
/// Prefix on `x_kigi_req_id` for laziness-classifier sampler calls.
|
||||
/// Centralised here so the production producer
|
||||
/// (`maybe_fire_laziness_check`) AND the offline replay harness
|
||||
/// (`crate::trace_classifier::build_classifier_request`) share a
|
||||
|
||||
@@ -150,7 +150,7 @@ impl SessionActor {
|
||||
);
|
||||
}
|
||||
}
|
||||
/// Emit per-server `x.ai/mcp/tools_changed` notifications.
|
||||
/// Emit per-server `kigi/mcp/tools_changed` notifications.
|
||||
///
|
||||
/// Each emission carries the owning
|
||||
/// `sessionId` so the pager can route via `find_session_match`
|
||||
@@ -182,7 +182,7 @@ impl SessionActor {
|
||||
}
|
||||
}
|
||||
}
|
||||
/// Handle explicit auth trigger from the client (x.ai/mcp/auth_trigger).
|
||||
/// Handle explicit auth trigger from the client (kigi/mcp/auth_trigger).
|
||||
///
|
||||
/// Runs force_reauth (browser flow), then re-initializes the server
|
||||
/// and registers its tools.
|
||||
@@ -580,7 +580,7 @@ impl SessionActor {
|
||||
/// On success: the new `Arc<McpClient>` is in
|
||||
/// `mcp_state.owned_clients[server]` with `ClientState::Ready`,
|
||||
/// the dispatcher's `notify_tx` is wired to its
|
||||
/// `GrokClientHandler`, and the liveness watcher is armed —
|
||||
/// `KigiClientHandler`, and the liveness watcher is armed —
|
||||
/// matching the post-handshake state produced by
|
||||
/// [`Self::ensure_mcp_tools_initialized`] for a fresh server.
|
||||
///
|
||||
@@ -603,7 +603,7 @@ impl SessionActor {
|
||||
/// `Reason::Initialized` from the dispatcher's mapping, one
|
||||
/// `Reason::RestartSucceeded` from the restart task).
|
||||
///
|
||||
/// The `GrokClientHandler` constructed inside `try_handshake`
|
||||
/// The `KigiClientHandler` constructed inside `try_handshake`
|
||||
/// holds the SHARED `Arc<Mutex<Option<Sender>>>` slot
|
||||
/// (`SharedEventTx`), so wiring the sender AFTER the handshake
|
||||
/// still routes subsequent `tools/list_changed` /
|
||||
@@ -814,7 +814,7 @@ impl SessionActor {
|
||||
self.notifications
|
||||
.gateway
|
||||
.forward_fire_and_forget(acp::ExtNotification::new(
|
||||
"x.ai/mcp_initialized",
|
||||
"kigi/mcp_initialized",
|
||||
params.into(),
|
||||
));
|
||||
}
|
||||
@@ -884,7 +884,7 @@ impl SessionActor {
|
||||
self.notifications
|
||||
.gateway
|
||||
.forward_fire_and_forget(acp::ExtNotification::new(
|
||||
"x.ai/mcp_initialized",
|
||||
"kigi/mcp_initialized",
|
||||
params.into(),
|
||||
));
|
||||
}
|
||||
@@ -1432,7 +1432,7 @@ impl SessionActor {
|
||||
"elapsedMs" : elapsed.as_millis() as u64, }
|
||||
)) {
|
||||
gateway.forward_fire_and_forget(acp::ExtNotification::new(
|
||||
"x.ai/mcp_initialized",
|
||||
"kigi/mcp_initialized",
|
||||
params.into(),
|
||||
));
|
||||
}
|
||||
|
||||
@@ -314,10 +314,10 @@ impl SessionActor {
|
||||
ConversationItem::user(user_message),
|
||||
],
|
||||
model: Some(model),
|
||||
x_grok_conv_id: Some(session_id.clone()),
|
||||
x_grok_req_id: Some(format!("xai-dream-{}", uuid::Uuid::new_v4())),
|
||||
x_grok_session_id: Some(session_id),
|
||||
x_grok_agent_id: Some(crate::util::agent_id::agent_id()),
|
||||
x_kigi_conv_id: Some(session_id.clone()),
|
||||
x_kigi_req_id: Some(format!("xai-dream-{}", uuid::Uuid::new_v4())),
|
||||
x_kigi_session_id: Some(session_id),
|
||||
x_kigi_agent_id: Some(crate::util::agent_id::agent_id()),
|
||||
..Default::default()
|
||||
};
|
||||
let response = sampling_client
|
||||
@@ -413,10 +413,10 @@ impl SessionActor {
|
||||
let request = ConversationRequest {
|
||||
items,
|
||||
model: Some(model),
|
||||
x_grok_conv_id: Some(session_id.clone()),
|
||||
x_grok_req_id: Some(format!("xai-flush-{}", uuid::Uuid::new_v4())),
|
||||
x_grok_session_id: Some(session_id.clone()),
|
||||
x_grok_agent_id: Some(crate::util::agent_id::agent_id()),
|
||||
x_kigi_conv_id: Some(session_id.clone()),
|
||||
x_kigi_req_id: Some(format!("xai-flush-{}", uuid::Uuid::new_v4())),
|
||||
x_kigi_session_id: Some(session_id.clone()),
|
||||
x_kigi_agent_id: Some(crate::util::agent_id::agent_id()),
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
@@ -608,7 +608,7 @@ impl SessionActor {
|
||||
}
|
||||
|
||||
/// Rewrite a raw memory note into well-structured markdown via a one-shot
|
||||
/// LLM call using the `grok-build` model.
|
||||
/// LLM call using the `kigi` model.
|
||||
///
|
||||
/// Follows the same streaming pattern as [`handle_ai_suggest`]: prepares
|
||||
/// a sampling client, builds a system+user prompt, streams the response,
|
||||
|
||||
@@ -207,7 +207,7 @@ impl SessionActor {
|
||||
}
|
||||
bridge
|
||||
.update_resource(
|
||||
kigi_tools::implementations::grok_build::update_goal::GoalUpdateHandle(
|
||||
kigi_tools::implementations::kigi::update_goal::GoalUpdateHandle(
|
||||
self.goal_update_tx.clone(),
|
||||
),
|
||||
)
|
||||
|
||||
@@ -94,7 +94,7 @@ impl SessionActor {
|
||||
.borrow()
|
||||
.tool_bridge()
|
||||
.update_resource(
|
||||
kigi_tools::implementations::grok_build::task::types::CurrentPromptIdResource(
|
||||
kigi_tools::implementations::kigi::task::types::CurrentPromptIdResource(
|
||||
prompt_id.clone(),
|
||||
),
|
||||
)
|
||||
@@ -249,7 +249,7 @@ impl SessionActor {
|
||||
let Some(buffer) = &self.tool_context.monitor_event_buffer else {
|
||||
return;
|
||||
};
|
||||
for event in kigi_tools::implementations::grok_build::task::types::drain_owned(
|
||||
for event in kigi_tools::implementations::kigi::task::types::drain_owned(
|
||||
buffer,
|
||||
Some(self.session_info.id.0.as_ref()),
|
||||
) {
|
||||
@@ -308,7 +308,7 @@ impl SessionActor {
|
||||
notifications: Vec<PendingNotification>,
|
||||
task_output_tool_name: &str,
|
||||
) -> bool {
|
||||
use kigi_tools::implementations::grok_build::task::types::MonitorEventNotification;
|
||||
use kigi_tools::implementations::kigi::task::types::MonitorEventNotification;
|
||||
|
||||
// Collapse monitor entries: collect their text into events, remember
|
||||
// where the first one sat so the batch lands in arrival position.
|
||||
|
||||
@@ -277,7 +277,7 @@ pub(super) fn build_truncated_prompt_message(
|
||||
}
|
||||
/// Replace the file-referencing offload `notice` embedded in `message` with the
|
||||
/// no-file [`OFFLOAD_FAILED_NOTICE`]. Position-independent (the notice sits at the
|
||||
/// end for grok ordering), so a failed offload never
|
||||
/// end for kigi ordering), so a failed offload never
|
||||
/// leaves the model chasing a "read this file" pointer to a file that does not
|
||||
/// exist. Returns `message` unchanged if the notice is absent (defensive).
|
||||
pub(super) fn strip_offload_notice(message: &str, notice: &str) -> String {
|
||||
|
||||
@@ -319,7 +319,7 @@ impl SessionActor {
|
||||
entry_count = payload.entries.len(),
|
||||
entries = ?payload.entries.iter().map(|e| e.id.as_str()).collect::<Vec<_>>(),
|
||||
session = self.session_info.id.0.as_ref(),
|
||||
"broadcasting x.ai/queue/changed to subscribers",
|
||||
"broadcasting kigi/queue/changed to subscribers",
|
||||
);
|
||||
if let Ok(params) = serde_json::value::to_raw_value(&payload) {
|
||||
self.notifications
|
||||
@@ -426,7 +426,7 @@ impl SessionActor {
|
||||
/// stale `expected_version`, or is owned by another client, or
|
||||
/// - the row is not a plain prompt (it would reach the model as prompt text).
|
||||
///
|
||||
/// Always re-broadcasts `x.ai/queue/changed` so every client reconciles
|
||||
/// Always re-broadcasts `kigi/queue/changed` so every client reconciles
|
||||
/// (the row vanishes on success, is unchanged on a no-op).
|
||||
/// `new_text` (when `Some`) replaces the stored queue text in the
|
||||
/// interjection — the client edited the row before interjecting. It rides
|
||||
@@ -609,7 +609,7 @@ impl SessionActor {
|
||||
/// user has explicitly typed replacement text).
|
||||
/// 2. Update `queue_meta.text`, bump `queue_meta.version`, and record
|
||||
/// `last_editor` (the original `owner` attribution is preserved).
|
||||
/// 3. Re-broadcast `x.ai/queue/changed` so every subscriber renders the
|
||||
/// 3. Re-broadcast `kigi/queue/changed` so every subscriber renders the
|
||||
/// new text and version.
|
||||
///
|
||||
/// **No-op cases** (each is a benign discard with no rebroadcast — nothing
|
||||
|
||||
@@ -110,10 +110,10 @@ impl SessionActor {
|
||||
tools: tool_specs,
|
||||
model: Some(model.clone()),
|
||||
temperature: None,
|
||||
x_grok_conv_id: Some(btw_session_id.clone()),
|
||||
x_grok_req_id: Some(format!("xai-btw-{}", uuid::Uuid::new_v4())),
|
||||
x_grok_session_id: Some(parent_session_id.clone()),
|
||||
x_grok_agent_id: Some(crate::util::agent_id::agent_id()),
|
||||
x_kigi_conv_id: Some(btw_session_id.clone()),
|
||||
x_kigi_req_id: Some(format!("xai-btw-{}", uuid::Uuid::new_v4())),
|
||||
x_kigi_session_id: Some(parent_session_id.clone()),
|
||||
x_kigi_agent_id: Some(crate::util::agent_id::agent_id()),
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
@@ -238,8 +238,8 @@ impl SessionActor {
|
||||
// ~25–40 words, and `clean_recap_text` caps it at a generous
|
||||
// RECAP_MAX_CHARS safety net, so an explicit token cap isn't needed.
|
||||
let started_at = chrono::Utc::now().to_rfc3339();
|
||||
let x_grok_conv_id = format!("recap-{}", uuid::Uuid::new_v4());
|
||||
let x_grok_req_id = format!("xai-recap-{}", uuid::Uuid::new_v4());
|
||||
let x_kigi_conv_id = format!("recap-{}", uuid::Uuid::new_v4());
|
||||
let x_kigi_req_id = format!("xai-recap-{}", uuid::Uuid::new_v4());
|
||||
// Clone the exact request items for the on-disk artifact (recap never
|
||||
// mutates conversation state, so this file is the only durable record).
|
||||
let chat_history_for_artifact = items.clone();
|
||||
@@ -248,10 +248,10 @@ impl SessionActor {
|
||||
tools: vec![],
|
||||
model: Some(model.clone()),
|
||||
temperature: None,
|
||||
x_grok_conv_id: Some(x_grok_conv_id.clone()),
|
||||
x_grok_req_id: Some(x_grok_req_id.clone()),
|
||||
x_grok_session_id: Some(self.session_info.id.to_string()),
|
||||
x_grok_agent_id: Some(crate::util::agent_id::agent_id()),
|
||||
x_kigi_conv_id: Some(x_kigi_conv_id.clone()),
|
||||
x_kigi_req_id: Some(x_kigi_req_id.clone()),
|
||||
x_kigi_session_id: Some(self.session_info.id.to_string()),
|
||||
x_kigi_agent_id: Some(crate::util::agent_id::agent_id()),
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
@@ -265,8 +265,8 @@ impl SessionActor {
|
||||
auto,
|
||||
strip_reasoning,
|
||||
tag,
|
||||
&x_grok_req_id,
|
||||
&x_grok_conv_id,
|
||||
&x_kigi_req_id,
|
||||
&x_kigi_conv_id,
|
||||
started_at,
|
||||
None,
|
||||
None,
|
||||
@@ -291,8 +291,8 @@ impl SessionActor {
|
||||
auto,
|
||||
strip_reasoning,
|
||||
tag,
|
||||
&x_grok_req_id,
|
||||
&x_grok_conv_id,
|
||||
&x_kigi_req_id,
|
||||
&x_kigi_conv_id,
|
||||
started_at,
|
||||
None,
|
||||
Some(raw_response.as_str()).filter(|s| !s.is_empty()),
|
||||
@@ -307,7 +307,7 @@ impl SessionActor {
|
||||
}
|
||||
|
||||
// New prompt while generating: keep artifact, skip display, leave watermark.
|
||||
// Applies to manual `/recap` too: spinner-less clients (e.g. Grok
|
||||
// Applies to manual `/recap` too: spinner-less clients (e.g. Kigi
|
||||
// Desktop) would otherwise append the late recap mid-turn.
|
||||
if self.recap_was_cancelled(recap_epoch) {
|
||||
tracing::info!(
|
||||
@@ -322,8 +322,8 @@ impl SessionActor {
|
||||
auto,
|
||||
strip_reasoning,
|
||||
tag,
|
||||
&x_grok_req_id,
|
||||
&x_grok_conv_id,
|
||||
&x_kigi_req_id,
|
||||
&x_kigi_conv_id,
|
||||
started_at,
|
||||
Some(summary.as_str()),
|
||||
Some(raw_response.as_str()),
|
||||
@@ -346,8 +346,8 @@ impl SessionActor {
|
||||
auto,
|
||||
strip_reasoning,
|
||||
tag,
|
||||
&x_grok_req_id,
|
||||
&x_grok_conv_id,
|
||||
&x_kigi_req_id,
|
||||
&x_kigi_conv_id,
|
||||
started_at,
|
||||
Some(summary.as_str()),
|
||||
Some(raw_response.as_str()),
|
||||
@@ -365,8 +365,8 @@ impl SessionActor {
|
||||
auto,
|
||||
strip_reasoning,
|
||||
tag,
|
||||
&x_grok_req_id,
|
||||
&x_grok_conv_id,
|
||||
&x_kigi_req_id,
|
||||
&x_kigi_conv_id,
|
||||
started_at,
|
||||
Some(summary.as_str()),
|
||||
Some(raw_response.as_str()),
|
||||
@@ -433,8 +433,8 @@ impl SessionActor {
|
||||
auto: bool,
|
||||
strip_reasoning: bool,
|
||||
reminder_tag: &str,
|
||||
x_grok_req_id: &str,
|
||||
x_grok_conv_id: &str,
|
||||
x_kigi_req_id: &str,
|
||||
x_kigi_conv_id: &str,
|
||||
started_at: String,
|
||||
summary: Option<&str>,
|
||||
raw_response: Option<&str>,
|
||||
@@ -449,8 +449,8 @@ impl SessionActor {
|
||||
created_at: started_at,
|
||||
trigger: if auto { "auto" } else { "manual" }.to_owned(),
|
||||
model: model.to_owned(),
|
||||
x_grok_req_id: x_grok_req_id.to_owned(),
|
||||
x_grok_conv_id: x_grok_conv_id.to_owned(),
|
||||
x_kigi_req_id: x_kigi_req_id.to_owned(),
|
||||
x_kigi_conv_id: x_kigi_conv_id.to_owned(),
|
||||
strip_reasoning,
|
||||
reminder_tag: reminder_tag.to_owned(),
|
||||
chat_history,
|
||||
@@ -578,7 +578,7 @@ impl SessionActor {
|
||||
/// Temperature, max_output_tokens, and
|
||||
/// reasoning_effort are left unset — mirrors [`Self::handle_recap`]: the
|
||||
/// proxy may inject provider defaults, a small token cap silently empties
|
||||
/// a reasoning model's response, and some models (e.g. `grok-build`)
|
||||
/// a reasoning model's response, and some models (e.g. `kigi`)
|
||||
/// reject an explicit `reasoningEffort` with a 400. Output is filtered
|
||||
/// through [`prompt_suggest::sanitize_suggestion`]; any failure returns
|
||||
/// through [`prompt_suggest::sanitize_suggestion`]; any failure returns
|
||||
@@ -643,10 +643,10 @@ impl SessionActor {
|
||||
tools: vec![],
|
||||
model: Some(model),
|
||||
temperature: None,
|
||||
x_grok_conv_id: Some(format!("promptsuggest-{}", uuid::Uuid::new_v4())),
|
||||
x_grok_req_id: Some(format!("xai-promptsuggest-{}", uuid::Uuid::new_v4())),
|
||||
x_grok_session_id: Some(self.session_info.id.to_string()),
|
||||
x_grok_agent_id: Some(crate::util::agent_id::agent_id()),
|
||||
x_kigi_conv_id: Some(format!("promptsuggest-{}", uuid::Uuid::new_v4())),
|
||||
x_kigi_req_id: Some(format!("xai-promptsuggest-{}", uuid::Uuid::new_v4())),
|
||||
x_kigi_session_id: Some(self.session_info.id.to_string()),
|
||||
x_kigi_agent_id: Some(crate::util::agent_id::agent_id()),
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
|
||||
@@ -175,7 +175,7 @@ pub(crate) fn date_rollover_reminder(
|
||||
}
|
||||
/// Body of the one-shot interrupt `<system-reminder>` injected on the next real
|
||||
/// user turn after a mid-stream abort that left the model with no other signal.
|
||||
/// Wrapped in grok's `<system-reminder>` shape by [`SessionActor::push_system_reminder`].
|
||||
/// Wrapped in kigi's `<system-reminder>` shape by [`SessionActor::push_system_reminder`].
|
||||
/// See [`SessionActor::maybe_inject_interrupt_reminder`].
|
||||
pub(crate) const INTERRUPT_REMINDER: &str = "[Request interrupted by user]";
|
||||
/// TodoGate when enabled and the prompt carries `<task_completion_discipline>`
|
||||
@@ -317,7 +317,7 @@ impl SessionActor {
|
||||
let Some(tx) = &self.tool_context.subagent_event_tx else {
|
||||
return;
|
||||
};
|
||||
use kigi_tools::implementations::grok_build::task::types::{
|
||||
use kigi_tools::implementations::kigi::task::types::{
|
||||
SubagentCompletionsRequest, SubagentEvent,
|
||||
};
|
||||
let suppress_ids = self
|
||||
|
||||
@@ -525,7 +525,7 @@ impl SessionActor {
|
||||
.send(PersistenceMsg::MergeRewindPointsFrom { target_index });
|
||||
}
|
||||
|
||||
/// Out-of-band history repair (`x.ai/session/repair`) for a resident
|
||||
/// Out-of-band history repair (`kigi/session/repair`) for a resident
|
||||
/// session: run `kigi_chat_state::compaction_utils::repair_history` inside
|
||||
/// the chat-state actor, then flush persistence so `chat_history.jsonl`
|
||||
/// is rewritten on disk before the caller sees success.
|
||||
|
||||
@@ -386,7 +386,7 @@ pub(super) async fn run_session(
|
||||
acp::ContentBlock::Text(t) = b { Some(t.text.clone()) } else { None } })
|
||||
.collect::< Vec < _ >> ().join("\n"); let task_id = source.task_id()
|
||||
.to_owned(); const MAX_BUFFER_EVENTS : usize = 50; buffer
|
||||
.push_capped(kigi_tools::implementations::grok_build::task::types::MonitorEventNotification
|
||||
.push_capped(kigi_tools::implementations::kigi::task::types::MonitorEventNotification
|
||||
{ task_id : task_id.clone(), event_text, owner_session_id : Some(session
|
||||
.session_info.id.0.to_string(),), }, MAX_BUFFER_EVENTS,);
|
||||
tracing::debug!(task_id = % task_id,
|
||||
|
||||
@@ -33,7 +33,7 @@ struct SessionTokenAuthGate {
|
||||
is_session_based: bool,
|
||||
model_byok: crate::agent::auth_method::ModelByok,
|
||||
/// Whether the request targets a first-party host. Lets an `Unknown`
|
||||
/// BYOK status still refresh against cli-chat-proxy / `*.x.ai` without
|
||||
/// BYOK status still refresh against the first-party cli-chat-proxy hosts without
|
||||
/// risking a session-token leak to a third-party BYOK endpoint.
|
||||
endpoint_is_first_party: bool,
|
||||
}
|
||||
@@ -419,10 +419,10 @@ impl SessionActor {
|
||||
kigi_workspace::permission::classifier_output_json_schema(),
|
||||
),
|
||||
reasoning_effort: classifier_reasoning_effort,
|
||||
x_grok_conv_id: Some(session_id.clone()),
|
||||
x_grok_req_id: Some(format!("xai-perm-auto-{}", uuid::Uuid::new_v4())),
|
||||
x_grok_session_id: Some(session_id),
|
||||
x_grok_agent_id: Some(crate::util::agent_id::agent_id()),
|
||||
x_kigi_conv_id: Some(session_id.clone()),
|
||||
x_kigi_req_id: Some(format!("xai-perm-auto-{}", uuid::Uuid::new_v4())),
|
||||
x_kigi_session_id: Some(session_id),
|
||||
x_kigi_agent_id: Some(crate::util::agent_id::agent_id()),
|
||||
..ConversationRequest::default()
|
||||
};
|
||||
let fut = sampling_client.conversation_collect(request);
|
||||
|
||||
@@ -289,7 +289,7 @@ impl SessionActor {
|
||||
);
|
||||
}
|
||||
/// The activation reminder template for the active template (no
|
||||
/// first-entry/reentry distinction), or grok's reentry/full variant.
|
||||
/// first-entry/reentry distinction), or kigi's reentry/full variant.
|
||||
/// Shared by turn-start injection (`inject_plan_mode_reminders` case 1)
|
||||
/// and the mid-turn toggle (`activate_plan_mode_mid_turn`).
|
||||
fn plan_activation_template(&self, is_reentry: bool) -> &'static str {
|
||||
|
||||
@@ -146,8 +146,8 @@ pub(crate) async fn spawn_session_actor(
|
||||
inference_idle_timeout_secs: u64,
|
||||
max_retries: Option<u32>,
|
||||
web_search_config: kigi_tools::implementations::WebSearchConfig,
|
||||
web_fetch_config: kigi_tools::implementations::grok_build::web_fetch::WebFetchConfig,
|
||||
app_builder_deployer_config: kigi_tools::implementations::grok_build::deploy_app::AppBuilderDeployerConfig,
|
||||
web_fetch_config: kigi_tools::implementations::kigi::web_fetch::WebFetchConfig,
|
||||
app_builder_deployer_config: kigi_tools::implementations::kigi::deploy_app::AppBuilderDeployerConfig,
|
||||
write_file_enabled: bool,
|
||||
goal_enabled: bool,
|
||||
subagents_enabled: bool,
|
||||
@@ -180,7 +180,7 @@ pub(crate) async fn spawn_session_actor(
|
||||
std::sync::Arc<dyn kigi_tools::computer::types::TerminalBackend>,
|
||||
>,
|
||||
parent_scheduler_handle: Option<
|
||||
kigi_tools::implementations::grok_build::scheduler::types::SchedulerHandle,
|
||||
kigi_tools::implementations::kigi::scheduler::types::SchedulerHandle,
|
||||
>,
|
||||
max_turns: Option<usize>,
|
||||
forked_tool_override: Option<Vec<ToolSpec>>,
|
||||
@@ -224,7 +224,7 @@ pub(crate) async fn spawn_session_actor(
|
||||
"CLI --allow catch-all ignored: always-approve disabled by managed policy"
|
||||
);
|
||||
if startup_hints.non_interactive {
|
||||
eprintln!("grok: --allow catch-all ignored: {reason}");
|
||||
eprintln!("kigi: --allow catch-all ignored: {reason}");
|
||||
}
|
||||
}
|
||||
if !cli_permission_rules.is_empty() {
|
||||
@@ -531,7 +531,7 @@ pub(crate) async fn spawn_session_actor(
|
||||
};
|
||||
let reminder_policy = resolve_reminder_policy(remote_settings.as_ref(), todo_gate);
|
||||
let (user_question_tx, user_question_rx) = tokio::sync::mpsc::unbounded_channel::<
|
||||
kigi_tools::implementations::grok_build::ask_user_question::types::UserQuestionRequest,
|
||||
kigi_tools::implementations::kigi::ask_user_question::types::UserQuestionRequest,
|
||||
>();
|
||||
let attribution_callback_for_spec = auth_manager.as_ref().map(|am| {
|
||||
crate::auth::attribution::ShellAttribution::new_tool_callback(
|
||||
@@ -672,7 +672,7 @@ pub(crate) async fn spawn_session_actor(
|
||||
state.set_acp_servers(acp_mcp_servers, invoker);
|
||||
tracing::info!(
|
||||
session_id = % session_info.id.0, acp_mcp_servers = acp_server_count,
|
||||
"Registered in-process SDK MCP servers (x.ai/mcp/sdk_call)"
|
||||
"Registered in-process SDK MCP servers (kigi/mcp/sdk_call)"
|
||||
);
|
||||
}
|
||||
Arc::new(TokioMutex::new(state))
|
||||
@@ -763,7 +763,7 @@ pub(crate) async fn spawn_session_actor(
|
||||
let scheduler_handle_for_handle = {
|
||||
let toolset = agent.tool_bridge().toolset();
|
||||
let res = toolset.resources.lock().await;
|
||||
res.get::<kigi_tools::implementations::grok_build::scheduler::types::SchedulerHandle>()
|
||||
res.get::<kigi_tools::implementations::kigi::scheduler::types::SchedulerHandle>()
|
||||
.cloned()
|
||||
};
|
||||
if let Err(e) = workspace_ops.bind_local_session(
|
||||
@@ -831,13 +831,13 @@ pub(crate) async fn spawn_session_actor(
|
||||
"Creating feedback manager"
|
||||
);
|
||||
let feedback_client_type = match client_type {
|
||||
ClientType::GrokTUI => crate::session::feedback_types::ClientType::Tui,
|
||||
ClientType::GrokWeb => crate::session::feedback_types::ClientType::Web,
|
||||
ClientType::KigiTUI => crate::session::feedback_types::ClientType::Tui,
|
||||
ClientType::KigiWeb => crate::session::feedback_types::ClientType::Web,
|
||||
ClientType::Nebula => crate::session::feedback_types::ClientType::Nebula,
|
||||
ClientType::Extension => crate::session::feedback_types::ClientType::Extension,
|
||||
ClientType::Generic => crate::session::feedback_types::ClientType::Agent,
|
||||
ClientType::Desktop => crate::session::feedback_types::ClientType::Desktop,
|
||||
ClientType::GrokPager => crate::session::feedback_types::ClientType::Tui,
|
||||
ClientType::KigiPager => crate::session::feedback_types::ClientType::Tui,
|
||||
};
|
||||
let feedback_config = FeedbackManagerConfig {
|
||||
feedback_enabled: feedback_flags.enabled,
|
||||
@@ -930,7 +930,7 @@ pub(crate) async fn spawn_session_actor(
|
||||
.map(|e| e.to_string())
|
||||
.collect();
|
||||
let (goal_update_tx, goal_update_rx) = tokio::sync::mpsc::unbounded_channel::<
|
||||
kigi_tools::implementations::grok_build::update_goal::UpdateGoalEnvelope,
|
||||
kigi_tools::implementations::kigi::update_goal::UpdateGoalEnvelope,
|
||||
>();
|
||||
let mut effective_config = crate::config::load_effective_config()
|
||||
.ok()
|
||||
@@ -1224,7 +1224,7 @@ pub(crate) async fn spawn_session_actor(
|
||||
.borrow()
|
||||
.tool_bridge()
|
||||
.update_resource(
|
||||
kigi_tools::implementations::grok_build::update_goal::GoalUpdateHandle(
|
||||
kigi_tools::implementations::kigi::update_goal::GoalUpdateHandle(
|
||||
session.goal_update_tx.clone(),
|
||||
),
|
||||
)
|
||||
@@ -1288,7 +1288,7 @@ pub(crate) async fn spawn_session_actor(
|
||||
}
|
||||
{
|
||||
use agent_client_protocol::Client as _;
|
||||
use kigi_tools::implementations::grok_build::ask_user_question::{
|
||||
use kigi_tools::implementations::kigi::ask_user_question::{
|
||||
AskUserQuestionExtRequest, AskUserQuestionExtResponse, UserQuestionError,
|
||||
UserQuestionResponse,
|
||||
};
|
||||
@@ -1300,7 +1300,7 @@ pub(crate) async fn spawn_session_actor(
|
||||
let mut user_question_rx = user_question_rx;
|
||||
tokio::task::spawn_local(async move {
|
||||
while let Some(mut request) = user_question_rx.recv().await {
|
||||
use kigi_tools::implementations::grok_build::ask_user_question::AskUserQuestionMode;
|
||||
use kigi_tools::implementations::kigi::ask_user_question::AskUserQuestionMode;
|
||||
let mode = match *current_prompt_mode.lock() {
|
||||
PromptMode::Plan => AskUserQuestionMode::Plan,
|
||||
_ => AskUserQuestionMode::Default,
|
||||
@@ -1316,7 +1316,7 @@ pub(crate) async fn spawn_session_actor(
|
||||
"ask_user_question reverse-request must carry a non-empty sessionId (design §5.4)"
|
||||
);
|
||||
let ext_request = agent_client_protocol::ExtRequest::new(
|
||||
"x.ai/ask_user_question",
|
||||
"kigi/ask_user_question",
|
||||
serde_json::value::to_raw_value(&ext_req)
|
||||
.expect("AskUserQuestionExtRequest serialization should not fail")
|
||||
.into(),
|
||||
@@ -1521,8 +1521,8 @@ pub(crate) async fn spawn_session_on_thread(
|
||||
inference_idle_timeout_secs: u64,
|
||||
max_retries: Option<u32>,
|
||||
web_search_config: kigi_tools::implementations::WebSearchConfig,
|
||||
web_fetch_config: kigi_tools::implementations::grok_build::web_fetch::WebFetchConfig,
|
||||
app_builder_deployer_config: kigi_tools::implementations::grok_build::deploy_app::AppBuilderDeployerConfig,
|
||||
web_fetch_config: kigi_tools::implementations::kigi::web_fetch::WebFetchConfig,
|
||||
app_builder_deployer_config: kigi_tools::implementations::kigi::deploy_app::AppBuilderDeployerConfig,
|
||||
write_file_enabled: bool,
|
||||
goal_enabled: bool,
|
||||
subagents_enabled: bool,
|
||||
@@ -1556,7 +1556,7 @@ pub(crate) async fn spawn_session_on_thread(
|
||||
std::sync::Arc<dyn kigi_tools::computer::types::TerminalBackend>,
|
||||
>,
|
||||
parent_scheduler_handle: Option<
|
||||
kigi_tools::implementations::grok_build::scheduler::types::SchedulerHandle,
|
||||
kigi_tools::implementations::kigi::scheduler::types::SchedulerHandle,
|
||||
>,
|
||||
max_turns: Option<usize>,
|
||||
forked_tool_override: Option<Vec<ToolSpec>>,
|
||||
|
||||
@@ -179,7 +179,7 @@ impl SessionActor {
|
||||
|
||||
fn cancel_subagents_for_prompt_id(&self, parent_prompt_id: &str) {
|
||||
if let Some(event_tx) = self.tool_context.subagent_event_tx.clone() {
|
||||
use kigi_tools::implementations::grok_build::task::types::{
|
||||
use kigi_tools::implementations::kigi::task::types::{
|
||||
SubagentCancelRequest, SubagentCancelTarget, SubagentEvent,
|
||||
};
|
||||
let _ = event_tx.send(SubagentEvent::Cancel(SubagentCancelRequest {
|
||||
@@ -346,7 +346,7 @@ impl SessionActor {
|
||||
// * normal interactive cancel: remove ONLY the running turn,
|
||||
// PRESERVING every queued prompt so the `Cancel` handler's
|
||||
// follow-up `maybe_start_running_task` promotes the new front (the
|
||||
// user's next queued prompt) and rebroadcasts `x.ai/queue/changed`.
|
||||
// user's next queued prompt) and rebroadcasts `kigi/queue/changed`.
|
||||
// The cancelling client does not pull any prompt back into its
|
||||
// input — the server queue is the single source of truth for what
|
||||
// runs next. Previously every cancel did `std::mem::take`,
|
||||
@@ -418,7 +418,7 @@ impl SessionActor {
|
||||
.borrow()
|
||||
.tool_bridge()
|
||||
.update_resource(
|
||||
kigi_tools::implementations::grok_build::task::types::CurrentPromptIdResource(
|
||||
kigi_tools::implementations::kigi::task::types::CurrentPromptIdResource(
|
||||
String::new(),
|
||||
),
|
||||
)
|
||||
|
||||
@@ -135,7 +135,7 @@ pub(super) fn should_intercept_exit_plan_approval(
|
||||
pub(super) enum PlanEditGate {
|
||||
/// Execute normally (plan mode inactive, not an edit, or allowed target).
|
||||
Allow,
|
||||
/// Grok-toolset edit outside the plan file (plan-file-only rule).
|
||||
/// Kigi-toolset edit outside the plan file (plan-file-only rule).
|
||||
RejectNonPlanFile,
|
||||
}
|
||||
/// Gate edit-class tool calls while plan mode is active.
|
||||
@@ -149,7 +149,7 @@ pub(super) enum PlanEditGate {
|
||||
/// file is editable in plan mode (plan docs are written with these
|
||||
/// same tools); everything else is rejected. Pre-existing behavior.
|
||||
/// - **Compat-toolset `Delete`** is **not** on the markdown carve-out: it maps to
|
||||
/// `AccessKind::Edit` and is plan-file-only (same as grok edits). Deleting
|
||||
/// `AccessKind::Edit` and is plan-file-only (same as kigi edits). Deleting
|
||||
/// an arbitrary `.md` in plan mode must not pass.
|
||||
/// - **Every other edit tool** (`AccessKind::Edit`) is restricted to the plan
|
||||
/// file itself, via the same predicate that auto-approves plan-file edits
|
||||
@@ -192,7 +192,7 @@ pub(super) enum PlanApprovalOutcome {
|
||||
}
|
||||
impl PlanApprovalOutcome {
|
||||
fn from_response(
|
||||
resp: &kigi_tools::implementations::grok_build::exit_plan_mode::ExitPlanModeExtResponse,
|
||||
resp: &kigi_tools::implementations::kigi::exit_plan_mode::ExitPlanModeExtResponse,
|
||||
) -> Self {
|
||||
match resp.outcome.as_str() {
|
||||
"approved" => Self::Approved,
|
||||
@@ -255,7 +255,7 @@ fn resume_action_for(outcome: PlanApprovalOutcome, feedback: Option<String>) ->
|
||||
}
|
||||
}
|
||||
impl SessionActor {
|
||||
/// Merge the canonical `x.ai/tool` identity envelope into a tool-call
|
||||
/// Merge the canonical `kigi/tool` identity envelope into a tool-call
|
||||
/// event's `_meta`, resolving the tool from the live toolset by wire name.
|
||||
pub(super) fn stamp_tool_meta(
|
||||
&self,
|
||||
@@ -1203,7 +1203,7 @@ impl SessionActor {
|
||||
};
|
||||
Ok(Ok(prepared))
|
||||
}
|
||||
/// Issue the `x.ai/exit_plan_mode` reverse-request and await the user's
|
||||
/// Issue the `kigi/exit_plan_mode` reverse-request and await the user's
|
||||
/// decision. Shared by the mid-turn intercept and the resume
|
||||
/// re-park. Marks `awaiting_plan_approval` while the request is
|
||||
/// outstanding and clears it on every exit path via [`AwaitingApprovalGuard`].
|
||||
@@ -1212,11 +1212,11 @@ impl SessionActor {
|
||||
tool_call_id: &acp::ToolCallId,
|
||||
plan_content: Option<String>,
|
||||
) -> Result<
|
||||
kigi_tools::implementations::grok_build::exit_plan_mode::ExitPlanModeExtResponse,
|
||||
kigi_tools::implementations::kigi::exit_plan_mode::ExitPlanModeExtResponse,
|
||||
acp::Error,
|
||||
> {
|
||||
use agent_client_protocol::Client as _;
|
||||
use kigi_tools::implementations::grok_build::exit_plan_mode::{
|
||||
use kigi_tools::implementations::kigi::exit_plan_mode::{
|
||||
ExitPlanModeExtRequest, ExitPlanModeExtResponse,
|
||||
};
|
||||
let ext_req = ExitPlanModeExtRequest {
|
||||
@@ -1229,7 +1229,7 @@ impl SessionActor {
|
||||
"exit_plan_mode reverse-request must carry a non-empty sessionId (design §5.4)"
|
||||
);
|
||||
let ext_request = acp::ExtRequest::new(
|
||||
"x.ai/exit_plan_mode",
|
||||
"kigi/exit_plan_mode",
|
||||
serde_json::value::to_raw_value(&ext_req)
|
||||
.expect("ExitPlanModeExtRequest serialization should not fail")
|
||||
.into(),
|
||||
@@ -2540,7 +2540,7 @@ mod plan_mode_edit_gate_tests {
|
||||
plan_mode_edit_gate(tracker, input, &AccessKind::from(input))
|
||||
}
|
||||
fn search_replace(path: &str) -> ToolInput {
|
||||
use kigi_tools::implementations::grok_build::search_replace::SearchReplaceInput;
|
||||
use kigi_tools::implementations::kigi::search_replace::SearchReplaceInput;
|
||||
ToolInput::SearchReplace(SearchReplaceInput {
|
||||
file_path: path.into(),
|
||||
old_string: "a".into(),
|
||||
@@ -2555,10 +2555,10 @@ mod plan_mode_edit_gate_tests {
|
||||
content: "x".into(),
|
||||
})
|
||||
}
|
||||
/// Grok edit tools are plan-file-only while plan mode is active — the
|
||||
/// Kigi edit tools are plan-file-only while plan mode is active — the
|
||||
/// enforcement that makes plan mode read-only even under always-approve.
|
||||
#[test]
|
||||
fn grok_edits_outside_plan_file_rejected() {
|
||||
fn kigi_edits_outside_plan_file_rejected() {
|
||||
let t = active_tracker();
|
||||
assert_eq!(
|
||||
gate(&t, &search_replace("/tmp/src/main.rs")),
|
||||
@@ -2567,7 +2567,7 @@ mod plan_mode_edit_gate_tests {
|
||||
assert_eq!(
|
||||
gate(&t, &write("/tmp/README.md")),
|
||||
PlanEditGate::RejectNonPlanFile,
|
||||
"grok tools get no markdown exception — plan file only"
|
||||
"kigi tools get no markdown exception — plan file only"
|
||||
);
|
||||
}
|
||||
/// The carve-out and the permission bypass share `should_auto_approve_edit`,
|
||||
@@ -2644,7 +2644,7 @@ mod plan_approval_helper_tests {
|
||||
PlanApprovalOutcome, ResumeAction, ext_method_no_client, resume_action_for,
|
||||
revise_plan_message,
|
||||
};
|
||||
use kigi_tools::implementations::grok_build::exit_plan_mode::ExitPlanModeExtResponse;
|
||||
use kigi_tools::implementations::kigi::exit_plan_mode::ExitPlanModeExtResponse;
|
||||
fn resp(outcome: &str) -> ExitPlanModeExtResponse {
|
||||
ExitPlanModeExtResponse {
|
||||
outcome: outcome.into(),
|
||||
|
||||
@@ -41,11 +41,11 @@ fn str_arg<'a>(args: &'a serde_json::Value, keys: &[&str]) -> Option<&'a str> {
|
||||
/// serializing concurrent same-file edits inside `execute_tool_calls`.
|
||||
///
|
||||
/// Different toolsets advertise the path under different JSON keys:
|
||||
/// - `file_path` — grok_build (`search_replace`), opencode (`EditTool`,
|
||||
/// `WriteTool`, `ReadTool`), codex (`read_file`), grok_build_hashline
|
||||
/// - `file_path` — kigi (`search_replace`), opencode (`EditTool`,
|
||||
/// `WriteTool`, `ReadTool`), codex (`read_file`), kigi_hashline
|
||||
/// (`hashline_edit`)
|
||||
/// - `path` — alternate edit/read tools
|
||||
/// - `target_file` — grok_build (`read_file`, via `#[serde(rename)]`)
|
||||
/// - `target_file` — kigi (`read_file`, via `#[serde(rename)]`)
|
||||
///
|
||||
/// Returning the same string for two calls in a batch causes them to share a
|
||||
/// `tokio::sync::Mutex` and therefore run sequentially in model-emitted order.
|
||||
@@ -202,7 +202,7 @@ impl SessionActor {
|
||||
is_background: false,
|
||||
});
|
||||
// Bash mode has no model-issued wire name; resolve the toolset's
|
||||
// execute tool by kind so the x.ai/tool identity still stamps.
|
||||
// execute tool by kind so the kigi/tool identity still stamps.
|
||||
let bash_marker = serde_json::json!({"bash_mode": true}).as_object().cloned();
|
||||
let exec_wire = {
|
||||
let agent = self.agent.borrow();
|
||||
@@ -366,7 +366,7 @@ pub(crate) const MAX_ARGS_IN_ERROR: usize = 2_000;
|
||||
///
|
||||
/// 1. The normal error description (so the model knows *what* failed).
|
||||
/// 2. The **original arguments string** the model produced (capped at
|
||||
/// [`MAX_ARGS_IN_ERROR`] bytes). Without this, grok-shell would sanitize
|
||||
/// [`MAX_ARGS_IN_ERROR`] bytes). Without this, kigi-shell would sanitize
|
||||
/// the arguments to `"{}"` before forwarding them to the provider (to
|
||||
/// avoid 400 errors), so the model would only see an empty object and have
|
||||
/// to regenerate all its work from scratch.
|
||||
|
||||
@@ -60,9 +60,7 @@ impl UsageDrainOutcome {
|
||||
/// Same policy as freeze's terminal outcome: FG live → fail-closed;
|
||||
/// sticky and background → report only.
|
||||
pub(super) fn from_outstanding_reply(
|
||||
reply: Option<
|
||||
&kigi_tools::implementations::grok_build::task::types::SubagentOutstandingReply,
|
||||
>,
|
||||
reply: Option<&kigi_tools::implementations::kigi::task::types::SubagentOutstandingReply>,
|
||||
) -> Self {
|
||||
match reply {
|
||||
None => Self {
|
||||
@@ -1050,7 +1048,7 @@ impl SessionActor {
|
||||
let Some(tx) = &self.tool_context.subagent_event_tx else {
|
||||
return false;
|
||||
};
|
||||
use kigi_tools::implementations::grok_build::task::types::{
|
||||
use kigi_tools::implementations::kigi::task::types::{
|
||||
SubagentEvent, SubagentMarkUsageNotAppliedRequest,
|
||||
};
|
||||
let (respond_to, ack) = tokio::sync::oneshot::channel();
|
||||
@@ -1074,12 +1072,12 @@ impl SessionActor {
|
||||
/// `push_user_message`, NOT `inject_synthetic_user_message`: the latter
|
||||
/// persists a `UserMessageChunk` to `updates.jsonl`, which resume
|
||||
/// replays — the raw XML would render as a user prompt. Clients see
|
||||
/// monitor events only via the structured `x.ai/monitor_event` channel.
|
||||
/// monitor events only via the structured `kigi/monitor_event` channel.
|
||||
pub(crate) async fn inject_pending_monitor_events(&self) {
|
||||
let Some(buffer) = &self.tool_context.monitor_event_buffer else {
|
||||
return;
|
||||
};
|
||||
let mine = kigi_tools::implementations::grok_build::task::types::drain_owned(
|
||||
let mine = kigi_tools::implementations::kigi::task::types::drain_owned(
|
||||
buffer,
|
||||
Some(self.session_info.id.0.as_ref()),
|
||||
);
|
||||
@@ -1632,12 +1630,12 @@ impl SessionActor {
|
||||
)),
|
||||
);
|
||||
let mut request = request;
|
||||
request.x_grok_session_id = Some(self.session_info.id.to_string());
|
||||
request.x_grok_turn_idx =
|
||||
request.x_kigi_session_id = Some(self.session_info.id.to_string());
|
||||
request.x_kigi_turn_idx =
|
||||
Some(self.chat_state_handle.get_prompt_index().await.to_string());
|
||||
request.x_grok_agent_id = Some(crate::util::agent_id::agent_id());
|
||||
if request.x_grok_deployment_id.is_none() {
|
||||
request.x_grok_deployment_id = crate::managed_config::resolve_deployment_id(
|
||||
request.x_kigi_agent_id = Some(crate::util::agent_id::agent_id());
|
||||
if request.x_kigi_deployment_id.is_none() {
|
||||
request.x_kigi_deployment_id = crate::managed_config::resolve_deployment_id(
|
||||
crate::managed_config::resolve_deployment_key().as_deref(),
|
||||
);
|
||||
}
|
||||
@@ -2115,7 +2113,7 @@ mod user_echo_broadcast_tests {
|
||||
);
|
||||
}
|
||||
/// Interject-fallback turns are persist-only: every pane already rendered
|
||||
/// the text from the `x.ai/session/interjection` broadcast, so a live
|
||||
/// the text from the `kigi/session/interjection` broadcast, so a live
|
||||
/// echo would duplicate the block.
|
||||
#[test]
|
||||
fn interject_fallback_turn_is_persist_only() {
|
||||
|
||||
@@ -78,7 +78,7 @@ impl SessionActor {
|
||||
self.emit_transient_notification(notification);
|
||||
}
|
||||
|
||||
/// Emit `x.ai/git_head_changed` after an edit/shell command that may have
|
||||
/// Emit `kigi/git_head_changed` after an edit/shell command that may have
|
||||
/// moved HEAD (e.g. `git checkout`), so clients update their status bar
|
||||
/// immediately rather than waiting for the debounced fs-watch refresh.
|
||||
pub(super) async fn maybe_notify_git_branch(&self) {
|
||||
@@ -112,7 +112,7 @@ impl SessionActor {
|
||||
main_repo,
|
||||
};
|
||||
if let Ok(raw) = serde_json::value::to_raw_value(¶ms) {
|
||||
let notification = acp::ExtNotification::new("x.ai/git_head_changed", raw.into());
|
||||
let notification = acp::ExtNotification::new("kigi/git_head_changed", raw.into());
|
||||
self.notifications
|
||||
.gateway
|
||||
.forward_fire_and_forget(notification);
|
||||
@@ -123,12 +123,11 @@ impl SessionActor {
|
||||
pub(super) async fn outstanding_reply_for_prompt(
|
||||
&self,
|
||||
prompt_id: &str,
|
||||
) -> Option<kigi_tools::implementations::grok_build::task::types::SubagentOutstandingReply>
|
||||
{
|
||||
) -> Option<kigi_tools::implementations::kigi::task::types::SubagentOutstandingReply> {
|
||||
let Some(tx) = &self.tool_context.subagent_event_tx else {
|
||||
return Some(Default::default());
|
||||
};
|
||||
use kigi_tools::implementations::grok_build::task::types::{
|
||||
use kigi_tools::implementations::kigi::task::types::{
|
||||
SubagentEvent, SubagentOutstandingRequest,
|
||||
};
|
||||
let (respond_to, rx) = tokio::sync::oneshot::channel();
|
||||
@@ -147,9 +146,7 @@ impl SessionActor {
|
||||
/// Report-level incomplete (error-path attach, tests). Same OR as
|
||||
/// [`super::turn::UsageDrainOutcome::report_incomplete`].
|
||||
pub(super) fn usage_incomplete_from_reply(
|
||||
reply: Option<
|
||||
&kigi_tools::implementations::grok_build::task::types::SubagentOutstandingReply,
|
||||
>,
|
||||
reply: Option<&kigi_tools::implementations::kigi::task::types::SubagentOutstandingReply>,
|
||||
) -> bool {
|
||||
super::turn::UsageDrainOutcome::from_outstanding_reply(reply).report_incomplete()
|
||||
}
|
||||
@@ -158,7 +155,7 @@ impl SessionActor {
|
||||
let Some(tx) = &self.tool_context.subagent_event_tx else {
|
||||
return;
|
||||
};
|
||||
use kigi_tools::implementations::grok_build::task::types::{
|
||||
use kigi_tools::implementations::kigi::task::types::{
|
||||
SubagentClearUsageNotAppliedRequest, SubagentEvent,
|
||||
};
|
||||
let _ = tx.send(SubagentEvent::ClearUsageNotApplied(
|
||||
@@ -186,7 +183,7 @@ impl SessionActor {
|
||||
.borrow()
|
||||
.tool_bridge()
|
||||
.update_resource(
|
||||
kigi_tools::implementations::grok_build::task::types::CurrentPromptIdResource(
|
||||
kigi_tools::implementations::kigi::task::types::CurrentPromptIdResource(
|
||||
String::new(),
|
||||
),
|
||||
)
|
||||
@@ -257,7 +254,7 @@ impl SessionActor {
|
||||
|
||||
// Durable twin of the fire-and-forget `prompt_complete` (emitted from
|
||||
// `MvpAgent::prompt`): publish the turn's terminal on the persisted +
|
||||
// replayed `_x.ai/session/update` rail so a viewer that re-attaches
|
||||
// replayed `_kigi/session/update` rail so a viewer that re-attaches
|
||||
// mid-turn finalizes from replay instead of stranding on "Waiting…".
|
||||
// The caller flushed the replay buffer first, so this lands strictly
|
||||
// after the turn's last `session/update` delta. Emit ONLY for a
|
||||
|
||||
@@ -199,8 +199,8 @@ pub(crate) enum DrainPurpose {
|
||||
/// Origin of a drain entry. `Pending` entries had their acks resolved
|
||||
/// at defer time; `Channel` entries still carry a live oneshot.
|
||||
pub(crate) enum DrainSource {
|
||||
Pending(kigi_tools::implementations::grok_build::update_goal::UpdateGoalInput),
|
||||
Channel(kigi_tools::implementations::grok_build::update_goal::UpdateGoalEnvelope),
|
||||
Pending(kigi_tools::implementations::kigi::update_goal::UpdateGoalInput),
|
||||
Channel(kigi_tools::implementations::kigi::update_goal::UpdateGoalEnvelope),
|
||||
}
|
||||
|
||||
/// Reason a NotAchieved verdict was synthesized without invoking the
|
||||
|
||||
@@ -230,7 +230,7 @@ impl SessionActor {
|
||||
self.notifications
|
||||
.gateway
|
||||
.forward_fire_and_forget(acp::ExtNotification::new(
|
||||
"x.ai/session_notification",
|
||||
"kigi/session_notification",
|
||||
params.into(),
|
||||
));
|
||||
}
|
||||
@@ -734,7 +734,7 @@ impl SessionActor {
|
||||
.ok();
|
||||
if let Some(params) = params {
|
||||
let ext_notification =
|
||||
acp::ExtNotification::new("x.ai/session_notification", params.into());
|
||||
acp::ExtNotification::new("kigi/session_notification", params.into());
|
||||
self.notifications
|
||||
.gateway
|
||||
.forward_fire_and_forget(ext_notification);
|
||||
|
||||
+3
-3
@@ -355,7 +355,7 @@ async fn proactive_refresh_makes_per_turn_refresh_a_cache_hit() {
|
||||
fn model_not_found_error() -> kigi_sampler::SamplingErrorInfo {
|
||||
kigi_sampler::SamplingErrorInfo {
|
||||
kind: kigi_sampler::SamplingErrorKind::Api,
|
||||
message: "API error (status 404 Not Found): The model grok-build does not exist or your team does not have access".into(),
|
||||
message: "API error (status 404 Not Found): The model kigi does not exist or your team does not have access".into(),
|
||||
status_code: Some(404),
|
||||
is_retryable: false,
|
||||
retry_after_secs: None,
|
||||
@@ -379,7 +379,7 @@ fn model_not_found_error() -> kigi_sampler::SamplingErrorInfo {
|
||||
fn unauthorized_401_error() -> kigi_sampler::SamplingErrorInfo {
|
||||
kigi_sampler::SamplingErrorInfo {
|
||||
kind: kigi_sampler::SamplingErrorKind::Api,
|
||||
message: "Unauthorized (401) from https://cli-chat-proxy.kigi.com/v1/responses: {\"error\":\"Invalid or expired credentials (auth_kind=bearer, x_xai_token_auth=xai-grok-cli, upstream=Unauthenticated, reason=no auth context)\"}".into(),
|
||||
message: "Unauthorized (401) from https://cli-chat-proxy.kigi.com/v1/responses: {\"error\":\"Invalid or expired credentials (auth_kind=bearer, x_xai_token_auth=xai-kigi-cli, upstream=Unauthenticated, reason=no auth context)\"}".into(),
|
||||
status_code: Some(401),
|
||||
is_retryable: false,
|
||||
retry_after_secs: None,
|
||||
@@ -553,7 +553,7 @@ async fn sampler_401_login_method_with_stale_api_key_auth_type_still_recovers()
|
||||
let (_dir, am) = auth_manager_with_refresher(refresher);
|
||||
let (actor, _rx) = make_actor_with_method_and_credentials(
|
||||
Some(am),
|
||||
"grok.com",
|
||||
"kimi-code",
|
||||
kigi_chat_state::AuthType::ApiKey,
|
||||
"stale-session-jwt".to_string(),
|
||||
)
|
||||
|
||||
+1
-1
@@ -905,7 +905,7 @@ async fn reparented_record_is_noop_without_goal_harness() {
|
||||
/// subagent(s) completed" reminder.
|
||||
#[tokio::test(flavor = "current_thread")]
|
||||
async fn between_turn_drain_suppresses_auto_wake_delivered_subagents() {
|
||||
use kigi_tools::implementations::grok_build::task::types::{
|
||||
use kigi_tools::implementations::kigi::task::types::{
|
||||
SubagentCompletionSummary, SubagentEvent,
|
||||
};
|
||||
use kigi_tools::reminders::task_completion::AutoWakeDeliveredIds;
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
use kigi_tools::implementations::grok_build::task::types::SubagentCompletionSummary;
|
||||
use kigi_tools::implementations::kigi::task::types::SubagentCompletionSummary;
|
||||
use kigi_tools::reminders::task_completion::format_between_turn_completions;
|
||||
|
||||
fn summary(
|
||||
|
||||
+4
-4
@@ -778,7 +778,7 @@ async fn cancel_running_task_teardown_clears_running_and_pending_work() {
|
||||
agent
|
||||
.tool_bridge()
|
||||
.update_resource(
|
||||
kigi_tools::implementations::grok_build::task::types::CurrentPromptIdResource(
|
||||
kigi_tools::implementations::kigi::task::types::CurrentPromptIdResource(
|
||||
"running".to_string(),
|
||||
),
|
||||
)
|
||||
@@ -981,7 +981,7 @@ async fn cancel_running_task_teardown_clears_running_and_pending_work() {
|
||||
actor.cancel_running_task(true, true, false, None).await;
|
||||
let scoped_prompt_id = bridge
|
||||
.read_resource::<
|
||||
kigi_tools::implementations::grok_build::task::types::CurrentPromptIdResource,
|
||||
kigi_tools::implementations::kigi::task::types::CurrentPromptIdResource,
|
||||
>()
|
||||
.await;
|
||||
assert!(
|
||||
@@ -1013,7 +1013,7 @@ async fn cancel_running_task_teardown_clears_running_and_pending_work() {
|
||||
/// the running turn and removes ONLY the running prompt (the front of
|
||||
/// `pending_inputs`). Every queued prompt is PRESERVED so the `Cancel`
|
||||
/// handler's follow-up `maybe_start_running_task` promotes the new front (the
|
||||
/// user's next queued prompt) and rebroadcasts `x.ai/queue/changed`. The
|
||||
/// user's next queued prompt) and rebroadcasts `kigi/queue/changed`. The
|
||||
/// cancelling client never pulls a queued prompt back into its input — the
|
||||
/// server queue is the single source of truth for what runs next.
|
||||
///
|
||||
@@ -1756,7 +1756,7 @@ async fn cancel_propagates_to_sampler_handle_so_no_further_emission() {
|
||||
agent
|
||||
.tool_bridge()
|
||||
.update_resource(
|
||||
kigi_tools::implementations::grok_build::task::types::CurrentPromptIdResource(
|
||||
kigi_tools::implementations::kigi::task::types::CurrentPromptIdResource(
|
||||
"running".to_string(),
|
||||
),
|
||||
)
|
||||
|
||||
@@ -42,9 +42,9 @@ async fn client_hooks_fire_without_file_registry() {
|
||||
.try_recv()
|
||||
.expect("client hook must fire with no file registry");
|
||||
let kigi_acp_lib::AcpClientMessage::ExtNotification(args) = msg else {
|
||||
panic!("expected an x.ai/hooks/event ext notification");
|
||||
panic!("expected an kigi/hooks/event ext notification");
|
||||
};
|
||||
assert_eq!(args.request.method.as_ref(), "x.ai/hooks/event");
|
||||
assert_eq!(args.request.method.as_ref(), "kigi/hooks/event");
|
||||
let params: serde_json::Value =
|
||||
serde_json::from_str(args.request.params.get()).unwrap();
|
||||
assert_eq!(params["hookCallbackId"], "cb_0");
|
||||
@@ -54,7 +54,7 @@ async fn client_hooks_fire_without_file_registry() {
|
||||
}
|
||||
|
||||
/// The PreToolUse gate blocks a tool when a client hook returns `deny`: the reverse
|
||||
/// `x.ai/hooks/run` request is answered with a deny and `run_pre_tool_use_client_hook`
|
||||
/// `kigi/hooks/run` request is answered with a deny and `run_pre_tool_use_client_hook`
|
||||
/// returns `ToolLoop::HookDenied`. Complements the pure `classify` test by covering the
|
||||
/// gate wiring (the one new path that can block tool execution).
|
||||
#[tokio::test(flavor = "current_thread")]
|
||||
@@ -79,7 +79,7 @@ async fn pre_tool_use_client_deny_blocks_the_tool() {
|
||||
);
|
||||
*actor.client_hooks.borrow_mut() = client_hooks;
|
||||
|
||||
// Answer the x.ai/hooks/run reverse request with a deny; ack the UI
|
||||
// Answer the kigi/hooks/run reverse request with a deny; ack the UI
|
||||
// notifications `deny_tool` emits so it can't block the gate.
|
||||
tokio::task::spawn_local(async move {
|
||||
while let Some(msg) = gateway_rx.recv().await {
|
||||
@@ -431,7 +431,7 @@ async fn pre_tool_use_slow_callback_does_not_starve_a_deny() {
|
||||
/// dispatch error fires only PostToolUseFailure; a successful dispatch fires only
|
||||
/// PostToolUse. Guards the explicitly-hardened no-double-fire path (the PostToolUse
|
||||
/// success block routes through `dispatch_hook`, the same as the failure arm). Each
|
||||
/// post-tool event is observed as a fire-and-forget `x.ai/hooks/event` notification.
|
||||
/// post-tool event is observed as a fire-and-forget `kigi/hooks/event` notification.
|
||||
#[tokio::test(flavor = "current_thread")]
|
||||
async fn post_tool_use_and_failure_never_double_fire() {
|
||||
let local = tokio::task::LocalSet::new();
|
||||
@@ -443,7 +443,7 @@ async fn post_tool_use_and_failure_never_double_fire() {
|
||||
tokio::sync::mpsc::unbounded_channel::<PersistenceMsg>();
|
||||
let actor = create_test_actor(0, 256_000, 85, gateway_tx, persistence_tx).await;
|
||||
// The agent's tool bridge must know `todo_write` for it to parse + dispatch.
|
||||
*actor.agent.borrow_mut() = test_grok_build_agent_with_todo().await;
|
||||
*actor.agent.borrow_mut() = test_kigi_agent_with_todo().await;
|
||||
|
||||
let mut client_hooks = crate::extensions::hooks::ClientHooks::new();
|
||||
for event in [
|
||||
@@ -461,13 +461,13 @@ async fn post_tool_use_and_failure_never_double_fire() {
|
||||
}
|
||||
*actor.client_hooks.borrow_mut() = client_hooks;
|
||||
|
||||
// Collect the `hookEventName` of every `x.ai/hooks/event` notification queued.
|
||||
// Collect the `hookEventName` of every `kigi/hooks/event` notification queued.
|
||||
let drain =
|
||||
|rx: &mut tokio::sync::mpsc::UnboundedReceiver<kigi_acp_lib::AcpClientMessage>| {
|
||||
let mut events = Vec::new();
|
||||
while let Ok(msg) = rx.try_recv() {
|
||||
if let kigi_acp_lib::AcpClientMessage::ExtNotification(args) = msg
|
||||
&& args.request.method.as_ref() == "x.ai/hooks/event"
|
||||
&& args.request.method.as_ref() == "kigi/hooks/event"
|
||||
{
|
||||
let params: serde_json::Value =
|
||||
serde_json::from_str(args.request.params.get()).unwrap();
|
||||
@@ -544,7 +544,7 @@ async fn pre_tool_use_deny_feeds_reason_back_and_continues_turn() {
|
||||
let actor = create_test_actor(0, 256_000, 85, gateway_tx, persistence_tx).await;
|
||||
// The agent's tool bridge must know `todo_write` so it parses + reaches
|
||||
// the PreToolUse gate (rather than short-circuiting as an unknown tool).
|
||||
*actor.agent.borrow_mut() = test_grok_build_agent_with_todo().await;
|
||||
*actor.agent.borrow_mut() = test_kigi_agent_with_todo().await;
|
||||
|
||||
let mut client_hooks = crate::extensions::hooks::ClientHooks::new();
|
||||
client_hooks.insert(
|
||||
@@ -557,7 +557,7 @@ async fn pre_tool_use_deny_feeds_reason_back_and_continues_turn() {
|
||||
);
|
||||
*actor.client_hooks.borrow_mut() = client_hooks;
|
||||
|
||||
// Answer the reverse x.ai/hooks/run request with a deny carrying a reason;
|
||||
// Answer the reverse kigi/hooks/run request with a deny carrying a reason;
|
||||
// ack the UI notifications `deny_tool` emits so it can't block the gate.
|
||||
tokio::task::spawn_local(async move {
|
||||
while let Some(msg) = gateway_rx.recv().await {
|
||||
|
||||
+4
-4
@@ -18,7 +18,7 @@ async fn tool_bridge_routes_writes_through_injected_fs() {
|
||||
let config = ToolServerConfig {
|
||||
tools: vec![
|
||||
ToolConfig {
|
||||
id: "GrokBuild:read_file".into(),
|
||||
id: "Kigi:read_file".into(),
|
||||
params: None,
|
||||
name_override: None,
|
||||
params_name_overrides: None,
|
||||
@@ -27,7 +27,7 @@ async fn tool_bridge_routes_writes_through_injected_fs() {
|
||||
kind: None,
|
||||
},
|
||||
ToolConfig {
|
||||
id: "GrokBuild:search_replace".into(),
|
||||
id: "Kigi:search_replace".into(),
|
||||
params: Some(
|
||||
serde_json::from_value(serde_json::json!({
|
||||
"skip_read_before_edit": true
|
||||
@@ -47,13 +47,13 @@ async fn tool_bridge_routes_writes_through_injected_fs() {
|
||||
backend: terminal,
|
||||
fs,
|
||||
cwd: cwd.clone(),
|
||||
session_folder: std::env::temp_dir().join("grok-test-fs"),
|
||||
session_folder: std::env::temp_dir().join("kigi-test-fs"),
|
||||
session_env: std::sync::Arc::new(std::collections::HashMap::new()),
|
||||
notification_handle: ToolNotificationHandle::noop(),
|
||||
owner_session_id: None,
|
||||
parent_scheduler_handle: None,
|
||||
skills: vec![],
|
||||
state_path: std::env::temp_dir().join("grok-test-fs/tool_state.json"),
|
||||
state_path: std::env::temp_dir().join("kigi-test-fs/tool_state.json"),
|
||||
memory_backend: None,
|
||||
web_search_config: Default::default(),
|
||||
web_fetch_config: Default::default(),
|
||||
|
||||
+38
-39
@@ -459,8 +459,8 @@ async fn handle_turn_end_verified_complete_during_drain_skips_bail_nudge() {
|
||||
let (tx, rx) = tokio::sync::mpsc::unbounded_channel();
|
||||
*actor.goal_update_rx.borrow_mut() = Some(rx);
|
||||
tx.send(
|
||||
kigi_tools::implementations::grok_build::update_goal::envelope_for_test(
|
||||
kigi_tools::implementations::grok_build::update_goal::UpdateGoalInput {
|
||||
kigi_tools::implementations::kigi::update_goal::envelope_for_test(
|
||||
kigi_tools::implementations::kigi::update_goal::UpdateGoalInput {
|
||||
completed: Some(true),
|
||||
message: None,
|
||||
blocked_reason: None,
|
||||
@@ -1436,8 +1436,8 @@ async fn drain_goal_updates_blocked_reason_transitions_after_three_attempts() {
|
||||
let (tx, rx) = tokio::sync::mpsc::unbounded_channel();
|
||||
*actor.goal_update_rx.borrow_mut() = Some(rx);
|
||||
tx.send(
|
||||
kigi_tools::implementations::grok_build::update_goal::envelope_for_test(
|
||||
kigi_tools::implementations::grok_build::update_goal::UpdateGoalInput {
|
||||
kigi_tools::implementations::kigi::update_goal::envelope_for_test(
|
||||
kigi_tools::implementations::kigi::update_goal::UpdateGoalInput {
|
||||
completed: None,
|
||||
message: Some("longer body".into()),
|
||||
blocked_reason: Some("short label".into()),
|
||||
@@ -1479,8 +1479,8 @@ async fn drain_goal_updates_blocked_reason_rejected_below_threshold() {
|
||||
let (tx, rx) = tokio::sync::mpsc::unbounded_channel();
|
||||
*actor.goal_update_rx.borrow_mut() = Some(rx);
|
||||
tx.send(
|
||||
kigi_tools::implementations::grok_build::update_goal::envelope_for_test(
|
||||
kigi_tools::implementations::grok_build::update_goal::UpdateGoalInput {
|
||||
kigi_tools::implementations::kigi::update_goal::envelope_for_test(
|
||||
kigi_tools::implementations::kigi::update_goal::UpdateGoalInput {
|
||||
completed: None,
|
||||
message: None,
|
||||
blocked_reason: Some("only label".into()),
|
||||
@@ -1580,8 +1580,8 @@ async fn drain_goal_updates_blocked_reason_against_non_active_does_not_stash_pau
|
||||
let (tx, rx) = tokio::sync::mpsc::unbounded_channel();
|
||||
*actor.goal_update_rx.borrow_mut() = Some(rx);
|
||||
tx.send(
|
||||
kigi_tools::implementations::grok_build::update_goal::envelope_for_test(
|
||||
kigi_tools::implementations::grok_build::update_goal::UpdateGoalInput {
|
||||
kigi_tools::implementations::kigi::update_goal::envelope_for_test(
|
||||
kigi_tools::implementations::kigi::update_goal::UpdateGoalInput {
|
||||
completed: None,
|
||||
message: Some("body".into()),
|
||||
blocked_reason: Some("would-block".into()),
|
||||
@@ -1625,7 +1625,7 @@ async fn drain_goal_updates_completes_after_blocked_does_not_leak_pause_message(
|
||||
.store(2, Ordering::Relaxed);
|
||||
let (tx, rx) = tokio::sync::mpsc::unbounded_channel();
|
||||
*actor.goal_update_rx.borrow_mut() = Some(rx);
|
||||
tx.send(kigi_tools::implementations::grok_build::update_goal::envelope_for_test(kigi_tools::implementations::grok_build::update_goal::UpdateGoalInput {
|
||||
tx.send(kigi_tools::implementations::kigi::update_goal::envelope_for_test(kigi_tools::implementations::kigi::update_goal::UpdateGoalInput {
|
||||
completed: None,
|
||||
message: None,
|
||||
blocked_reason: Some("blk".into()),
|
||||
@@ -1641,7 +1641,7 @@ async fn drain_goal_updates_completes_after_blocked_does_not_leak_pause_message(
|
||||
// accepts complete() from any paused variant (including
|
||||
// Blocked), and the pause_message
|
||||
// is cleared during the transition.
|
||||
tx.send(kigi_tools::implementations::grok_build::update_goal::envelope_for_test(kigi_tools::implementations::grok_build::update_goal::UpdateGoalInput {
|
||||
tx.send(kigi_tools::implementations::kigi::update_goal::envelope_for_test(kigi_tools::implementations::kigi::update_goal::UpdateGoalInput {
|
||||
|
||||
completed: Some(true),
|
||||
message: None,
|
||||
@@ -1675,8 +1675,8 @@ async fn drain_goal_updates_skips_subsequent_completed_after_block() {
|
||||
let (tx, rx) = tokio::sync::mpsc::unbounded_channel();
|
||||
*actor.goal_update_rx.borrow_mut() = Some(rx);
|
||||
tx.send(
|
||||
kigi_tools::implementations::grok_build::update_goal::envelope_for_test(
|
||||
kigi_tools::implementations::grok_build::update_goal::UpdateGoalInput {
|
||||
kigi_tools::implementations::kigi::update_goal::envelope_for_test(
|
||||
kigi_tools::implementations::kigi::update_goal::UpdateGoalInput {
|
||||
completed: None,
|
||||
message: None,
|
||||
blocked_reason: Some("X".into()),
|
||||
@@ -1685,8 +1685,8 @@ async fn drain_goal_updates_skips_subsequent_completed_after_block() {
|
||||
)
|
||||
.unwrap();
|
||||
tx.send(
|
||||
kigi_tools::implementations::grok_build::update_goal::envelope_for_test(
|
||||
kigi_tools::implementations::grok_build::update_goal::UpdateGoalInput {
|
||||
kigi_tools::implementations::kigi::update_goal::envelope_for_test(
|
||||
kigi_tools::implementations::kigi::update_goal::UpdateGoalInput {
|
||||
completed: Some(true),
|
||||
message: None,
|
||||
blocked_reason: None,
|
||||
@@ -2259,8 +2259,8 @@ async fn drain_goal_updates_message_only_does_not_change_status() {
|
||||
let (tx, rx) = tokio::sync::mpsc::unbounded_channel();
|
||||
*actor.goal_update_rx.borrow_mut() = Some(rx);
|
||||
tx.send(
|
||||
kigi_tools::implementations::grok_build::update_goal::envelope_for_test(
|
||||
kigi_tools::implementations::grok_build::update_goal::UpdateGoalInput {
|
||||
kigi_tools::implementations::kigi::update_goal::envelope_for_test(
|
||||
kigi_tools::implementations::kigi::update_goal::UpdateGoalInput {
|
||||
completed: None,
|
||||
message: Some("Running tests...".into()),
|
||||
blocked_reason: None,
|
||||
@@ -2294,7 +2294,7 @@ async fn drain_goal_updates_message_only_does_not_change_status() {
|
||||
/// producing an ack"). The drain must instead reply with a clean ack.
|
||||
#[tokio::test(flavor = "current_thread")]
|
||||
async fn drain_goal_updates_harness_disabled_does_not_drop_ack() {
|
||||
use kigi_tools::implementations::grok_build::update_goal::{
|
||||
use kigi_tools::implementations::kigi::update_goal::{
|
||||
RejectReason, UpdateGoalAck, UpdateGoalInput,
|
||||
};
|
||||
let local = tokio::task::LocalSet::new();
|
||||
@@ -2374,8 +2374,8 @@ async fn drain_goal_updates_blocked_reason_takes_precedence_over_completed() {
|
||||
let (tx, rx) = tokio::sync::mpsc::unbounded_channel();
|
||||
*actor.goal_update_rx.borrow_mut() = Some(rx);
|
||||
tx.send(
|
||||
kigi_tools::implementations::grok_build::update_goal::envelope_for_test(
|
||||
kigi_tools::implementations::grok_build::update_goal::UpdateGoalInput {
|
||||
kigi_tools::implementations::kigi::update_goal::envelope_for_test(
|
||||
kigi_tools::implementations::kigi::update_goal::UpdateGoalInput {
|
||||
completed: Some(true),
|
||||
message: None,
|
||||
blocked_reason: Some("stuck".into()),
|
||||
@@ -2404,9 +2404,8 @@ async fn drain_goal_updates_blocked_reason_takes_precedence_over_completed() {
|
||||
// classifier sampler invoked); the full Achieved/NotAchieved/cap
|
||||
// E2E suite using `MockSpawner` lives separately.
|
||||
|
||||
fn make_completed_cmd() -> kigi_tools::implementations::grok_build::update_goal::UpdateGoalEnvelope
|
||||
{
|
||||
let input = kigi_tools::implementations::grok_build::update_goal::UpdateGoalInput {
|
||||
fn make_completed_cmd() -> kigi_tools::implementations::kigi::update_goal::UpdateGoalEnvelope {
|
||||
let input = kigi_tools::implementations::kigi::update_goal::UpdateGoalInput {
|
||||
completed: Some(true),
|
||||
message: None,
|
||||
blocked_reason: None,
|
||||
@@ -2966,8 +2965,8 @@ async fn drain_goal_updates_completed_resets_blocked_streak() {
|
||||
let (tx, rx) = tokio::sync::mpsc::unbounded_channel();
|
||||
*actor.goal_update_rx.borrow_mut() = Some(rx);
|
||||
tx.send(
|
||||
kigi_tools::implementations::grok_build::update_goal::envelope_for_test(
|
||||
kigi_tools::implementations::grok_build::update_goal::UpdateGoalInput {
|
||||
kigi_tools::implementations::kigi::update_goal::envelope_for_test(
|
||||
kigi_tools::implementations::kigi::update_goal::UpdateGoalInput {
|
||||
completed: Some(true),
|
||||
message: None,
|
||||
blocked_reason: None,
|
||||
@@ -3379,7 +3378,7 @@ async fn subagent_spawn_captures_effective_model_id() {
|
||||
.handle_xai_session_notification(spawn_notif_with_model(
|
||||
"a",
|
||||
None,
|
||||
Some("grok-4.5"),
|
||||
Some("kigi-4.5"),
|
||||
))
|
||||
.await;
|
||||
let model = actor
|
||||
@@ -3387,7 +3386,7 @@ async fn subagent_spawn_captures_effective_model_id() {
|
||||
.lock()
|
||||
.get("a")
|
||||
.and_then(|r| r.model.clone());
|
||||
assert_eq!(model.as_deref(), Some("grok-4.5"));
|
||||
assert_eq!(model.as_deref(), Some("kigi-4.5"));
|
||||
})
|
||||
.await;
|
||||
}
|
||||
@@ -3417,17 +3416,17 @@ async fn goal_tokens_by_model_breaks_down_active_goal_records() {
|
||||
local
|
||||
.run_until(async {
|
||||
let actor = make_test_actor_with_active_goal().await;
|
||||
insert_record_with_model(&actor, "a", Some("test-goal"), 0, 100, Some("grok-4"));
|
||||
insert_record_with_model(&actor, "b", Some("test-goal"), 0, 400, Some("grok-3"));
|
||||
insert_record_with_model(&actor, "a", Some("test-goal"), 0, 100, Some("kigi-4"));
|
||||
insert_record_with_model(&actor, "b", Some("test-goal"), 0, 400, Some("kigi-3"));
|
||||
// No captured model → folds under the supplied current model.
|
||||
insert_record_with_model(&actor, "c", Some("test-goal"), 0, 50, None);
|
||||
// A record from another goal must be excluded.
|
||||
insert_record_with_model(&actor, "d", Some("other-goal"), 0, 999, Some("grok-3"));
|
||||
insert_record_with_model(&actor, "d", Some("other-goal"), 0, 999, Some("kigi-3"));
|
||||
// A FINISHED record under the active goal must be excluded from the
|
||||
// LIVE active-window breakdown (the per-model analogue of the
|
||||
// finished/in-flight split in goal_tokens). If it leaked, grok-4
|
||||
// finished/in-flight split in goal_tokens). If it leaked, kigi-4
|
||||
// would be 800 and sort first.
|
||||
insert_record_with_model(&actor, "e", Some("test-goal"), 0, 700, Some("grok-4"));
|
||||
insert_record_with_model(&actor, "e", Some("test-goal"), 0, 700, Some("kigi-4"));
|
||||
actor
|
||||
.subagent_token_records
|
||||
.lock()
|
||||
@@ -3438,8 +3437,8 @@ async fn goal_tokens_by_model_breaks_down_active_goal_records() {
|
||||
assert_eq!(
|
||||
out,
|
||||
vec![
|
||||
("grok-3".to_owned(), 400),
|
||||
("grok-4".to_owned(), 100),
|
||||
("kigi-3".to_owned(), 400),
|
||||
("kigi-4".to_owned(), 100),
|
||||
("cur-model".to_owned(), 50),
|
||||
]
|
||||
);
|
||||
@@ -3453,7 +3452,7 @@ async fn goal_tokens_by_model_empty_without_active_goal() {
|
||||
local
|
||||
.run_until(async {
|
||||
let actor = make_test_actor_with_active_goal().await;
|
||||
insert_record_with_model(&actor, "a", Some("test-goal"), 0, 100, Some("grok-4"));
|
||||
insert_record_with_model(&actor, "a", Some("test-goal"), 0, 100, Some("kigi-4"));
|
||||
// Drop the orchestration: with no active goal the breakdown is
|
||||
// empty regardless of any lingering records.
|
||||
actor.goal_tracker.lock().clear();
|
||||
@@ -3757,8 +3756,8 @@ async fn blocked_streak_reaches_pause_across_successful_turns() {
|
||||
let (tx, rx) = tokio::sync::mpsc::unbounded_channel();
|
||||
*actor.goal_update_rx.borrow_mut() = Some(rx);
|
||||
let blocked = || {
|
||||
kigi_tools::implementations::grok_build::update_goal::envelope_for_test(
|
||||
kigi_tools::implementations::grok_build::update_goal::UpdateGoalInput {
|
||||
kigi_tools::implementations::kigi::update_goal::envelope_for_test(
|
||||
kigi_tools::implementations::kigi::update_goal::UpdateGoalInput {
|
||||
completed: None,
|
||||
message: None,
|
||||
blocked_reason: Some("cannot reach service".into()),
|
||||
@@ -3849,7 +3848,7 @@ async fn subagent_progress_advances_goal_tokens_live_without_double_count() {
|
||||
let kigi_acp_lib::AcpClientMessage::ExtNotification(args) = msg else {
|
||||
continue;
|
||||
};
|
||||
if args.request.method.as_ref() != "x.ai/session_notification" {
|
||||
if args.request.method.as_ref() != "kigi/session_notification" {
|
||||
continue;
|
||||
}
|
||||
let Ok(v) =
|
||||
@@ -4028,8 +4027,8 @@ async fn setup_goal_resets_streaks_from_previous_goal() {
|
||||
let (tx, rx) = tokio::sync::mpsc::unbounded_channel();
|
||||
*actor.goal_update_rx.borrow_mut() = Some(rx);
|
||||
tx.send(
|
||||
kigi_tools::implementations::grok_build::update_goal::envelope_for_test(
|
||||
kigi_tools::implementations::grok_build::update_goal::UpdateGoalInput {
|
||||
kigi_tools::implementations::kigi::update_goal::envelope_for_test(
|
||||
kigi_tools::implementations::kigi::update_goal::UpdateGoalInput {
|
||||
completed: None,
|
||||
message: None,
|
||||
blocked_reason: Some("blk".into()),
|
||||
|
||||
+39
-58
@@ -17,10 +17,10 @@
|
||||
use super::support::*;
|
||||
use super::*;
|
||||
use crate::session::PromptOrigin;
|
||||
use kigi_tools::implementations::grok_build::task::types::{
|
||||
use kigi_tools::implementations::kigi::task::types::{
|
||||
SubagentCancelOutcome, SubagentEvent, SubagentResult,
|
||||
};
|
||||
use kigi_tools::implementations::grok_build::update_goal::{RejectReason, UpdateGoalInput};
|
||||
use kigi_tools::implementations::kigi::update_goal::{RejectReason, UpdateGoalInput};
|
||||
use serial_test::serial;
|
||||
use std::collections::VecDeque;
|
||||
use std::sync::Arc as StdArc;
|
||||
@@ -190,9 +190,7 @@ struct MockCoordinator {
|
||||
/// configured role pair commits; tests override it to exercise the
|
||||
/// describe-driven fail-open branches.
|
||||
describe_outcome: StdArc<
|
||||
parking_lot::Mutex<
|
||||
kigi_tools::implementations::grok_build::task::types::SubagentDescribeOutcome,
|
||||
>,
|
||||
parking_lot::Mutex<kigi_tools::implementations::kigi::task::types::SubagentDescribeOutcome>,
|
||||
>,
|
||||
/// Per-describe `(subagent_type, harness_agent_type)` in call order.
|
||||
describe_calls: DescribeCallLog,
|
||||
@@ -200,8 +198,8 @@ struct MockCoordinator {
|
||||
/// A fully-capable describe summary (read + search + execute + edit + write)
|
||||
/// so any role's capability gate passes.
|
||||
fn capable_describe_outcome()
|
||||
-> kigi_tools::implementations::grok_build::task::types::SubagentDescribeOutcome {
|
||||
use kigi_tools::implementations::grok_build::task::types::{
|
||||
-> kigi_tools::implementations::kigi::task::types::SubagentDescribeOutcome {
|
||||
use kigi_tools::implementations::kigi::task::types::{
|
||||
SubagentDescribeOutcome, SubagentTypeSummary,
|
||||
};
|
||||
use kigi_tools::types::tool::ToolKind;
|
||||
@@ -403,7 +401,7 @@ fn seed_channel(actor: &SessionActor, cmds: Vec<UpdateGoalInput>) {
|
||||
let (tx, rx) = tokio::sync::mpsc::unbounded_channel();
|
||||
*actor.goal_update_rx.borrow_mut() = Some(rx);
|
||||
for cmd in cmds {
|
||||
tx.send(kigi_tools::implementations::grok_build::update_goal::envelope_for_test(cmd))
|
||||
tx.send(kigi_tools::implementations::kigi::update_goal::envelope_for_test(cmd))
|
||||
.unwrap();
|
||||
}
|
||||
drop(tx);
|
||||
@@ -415,9 +413,7 @@ fn seed_channel_with_acks(
|
||||
actor: &SessionActor,
|
||||
cmds: Vec<UpdateGoalInput>,
|
||||
) -> Vec<
|
||||
tokio::sync::oneshot::Receiver<
|
||||
kigi_tools::implementations::grok_build::update_goal::UpdateGoalAck,
|
||||
>,
|
||||
tokio::sync::oneshot::Receiver<kigi_tools::implementations::kigi::update_goal::UpdateGoalAck>,
|
||||
> {
|
||||
let (tx, rx) = tokio::sync::mpsc::unbounded_channel();
|
||||
*actor.goal_update_rx.borrow_mut() = Some(rx);
|
||||
@@ -712,7 +708,7 @@ async fn goal_classifier_stall_early_exit_pauses_with_no_progress() {
|
||||
VecDeque::from([Response::not_achieved(), Response::not_achieved()]),
|
||||
);
|
||||
let (actor, tmp) = make_actor(Some(coord.tx.clone()), true).await;
|
||||
use kigi_tools::implementations::grok_build::update_goal::UpdateGoalAck;
|
||||
use kigi_tools::implementations::kigi::update_goal::UpdateGoalAck;
|
||||
let mut last_ack = None;
|
||||
for _ in 0..2 {
|
||||
let mut rxs = seed_channel_with_acks(&actor, vec![make_completed()]);
|
||||
@@ -752,7 +748,7 @@ async fn goal_classifier_blocked_outcome_pauses_for_user_and_consolidates_queue(
|
||||
let local = tokio::task::LocalSet::new();
|
||||
local
|
||||
.run_until(async {
|
||||
use kigi_tools::implementations::grok_build::update_goal::UpdateGoalAck;
|
||||
use kigi_tools::implementations::kigi::update_goal::UpdateGoalAck;
|
||||
let coord = MockCoordinator::spawn(VecDeque::from([Response::blocked("unverifiable")]));
|
||||
let (actor, tmp) = make_actor(Some(coord.tx.clone()), true).await;
|
||||
let rxs = seed_channel_with_acks(&actor, vec![make_completed(), make_completed()]);
|
||||
@@ -817,7 +813,7 @@ async fn goal_classifier_cap_takes_precedence_over_stall() {
|
||||
let local = tokio::task::LocalSet::new();
|
||||
local
|
||||
.run_until(async {
|
||||
use kigi_tools::implementations::grok_build::update_goal::UpdateGoalAck;
|
||||
use kigi_tools::implementations::kigi::update_goal::UpdateGoalAck;
|
||||
let coord = MockCoordinator::spawn(VecDeque::from([
|
||||
Response::not_achieved(),
|
||||
Response::not_achieved(),
|
||||
@@ -878,7 +874,7 @@ async fn goal_classifier_stall_pause_consolidates_mid_drain_queue() {
|
||||
let local = tokio::task::LocalSet::new();
|
||||
local
|
||||
.run_until(async {
|
||||
use kigi_tools::implementations::grok_build::update_goal::UpdateGoalAck;
|
||||
use kigi_tools::implementations::kigi::update_goal::UpdateGoalAck;
|
||||
let coord = MockCoordinator::spawn(VecDeque::from([
|
||||
Response::not_achieved(),
|
||||
Response::not_achieved(),
|
||||
@@ -929,7 +925,7 @@ async fn goal_classifier_post_blocked_resume_does_not_immediately_restall() {
|
||||
let local = tokio::task::LocalSet::new();
|
||||
local
|
||||
.run_until(async {
|
||||
use kigi_tools::implementations::grok_build::update_goal::UpdateGoalAck;
|
||||
use kigi_tools::implementations::kigi::update_goal::UpdateGoalAck;
|
||||
let coord = MockCoordinator::spawn(VecDeque::from([
|
||||
Response::not_achieved_with("src/a.rs:1 missing coverage"),
|
||||
Response::blocked("contradiction"),
|
||||
@@ -2070,7 +2066,7 @@ async fn update_goal_tool_blocks_until_classifier_verdict_when_enabled() {
|
||||
let ack = ack_rx.await.expect("ack delivered");
|
||||
assert!(
|
||||
matches!(ack,
|
||||
kigi_tools::implementations::grok_build::update_goal::UpdateGoalAck::ClassifierAchieved
|
||||
kigi_tools::implementations::kigi::update_goal::UpdateGoalAck::ClassifierAchieved
|
||||
{ .. },),
|
||||
"classifier-enabled drain must deliver Achieved ack; got {ack:?}",
|
||||
);
|
||||
@@ -2089,7 +2085,7 @@ async fn update_goal_tool_returns_immediately_when_classifier_disabled() {
|
||||
let ack = ack_rx.await.expect("ack delivered");
|
||||
assert!(
|
||||
matches!(ack,
|
||||
kigi_tools::implementations::grok_build::update_goal::UpdateGoalAck::CompletedWithoutClassifier,)
|
||||
kigi_tools::implementations::kigi::update_goal::UpdateGoalAck::CompletedWithoutClassifier,)
|
||||
);
|
||||
})
|
||||
.await;
|
||||
@@ -2105,7 +2101,7 @@ async fn update_goal_tool_returns_error_when_classifier_in_flight_for_previous_c
|
||||
let ack_rx = rxs.pop().expect("one ack");
|
||||
actor.drain_goal_updates(0, DrainPurpose::TurnEnd).await;
|
||||
let ack = ack_rx.await.expect("ack delivered");
|
||||
use kigi_tools::implementations::grok_build::update_goal::UpdateGoalAck;
|
||||
use kigi_tools::implementations::kigi::update_goal::UpdateGoalAck;
|
||||
match ack {
|
||||
UpdateGoalAck::ClassifierConcurrentInFlight {
|
||||
attempt, max_runs, ..
|
||||
@@ -2127,7 +2123,7 @@ async fn update_goal_tool_returns_error_when_classifier_in_flight_for_previous_c
|
||||
}
|
||||
#[tokio::test(flavor = "current_thread")]
|
||||
async fn update_goal_tool_does_not_deadlock_on_mid_turn_completion() {
|
||||
use kigi_tools::implementations::grok_build::update_goal::UpdateGoalAck;
|
||||
use kigi_tools::implementations::kigi::update_goal::UpdateGoalAck;
|
||||
let local = tokio::task::LocalSet::new();
|
||||
local
|
||||
.run_until(async {
|
||||
@@ -2150,7 +2146,7 @@ async fn update_goal_tool_does_not_deadlock_on_mid_turn_completion() {
|
||||
}
|
||||
#[tokio::test(flavor = "current_thread")]
|
||||
async fn update_goal_tool_deferred_input_fires_classifier_at_turn_end() {
|
||||
use kigi_tools::implementations::grok_build::update_goal::UpdateGoalAck;
|
||||
use kigi_tools::implementations::kigi::update_goal::UpdateGoalAck;
|
||||
let local = tokio::task::LocalSet::new();
|
||||
local
|
||||
.run_until(async {
|
||||
@@ -2185,18 +2181,14 @@ async fn update_goal_tool_returns_immediately_for_blocked_reason() {
|
||||
local
|
||||
.run_until(async {
|
||||
let (actor, _tmp) = make_actor(None, true).await;
|
||||
let mut rxs = seed_channel_with_acks(
|
||||
&actor,
|
||||
vec![make_blocked("transient")],
|
||||
);
|
||||
let mut rxs = seed_channel_with_acks(&actor, vec![make_blocked("transient")]);
|
||||
let ack_rx = rxs.pop().expect("one ack");
|
||||
actor.drain_goal_updates(0, DrainPurpose::TurnEnd).await;
|
||||
let ack = ack_rx.await.expect("ack delivered");
|
||||
assert!(
|
||||
matches!(ack,
|
||||
kigi_tools::implementations::grok_build::update_goal::UpdateGoalAck::Accepted
|
||||
{ .. },)
|
||||
);
|
||||
assert!(matches!(
|
||||
ack,
|
||||
kigi_tools::implementations::kigi::update_goal::UpdateGoalAck::Accepted { .. },
|
||||
));
|
||||
})
|
||||
.await;
|
||||
}
|
||||
@@ -2291,7 +2283,7 @@ async fn goal_classifier_sequential_drain_four_completions_three_attempts_then_c
|
||||
let local = tokio::task::LocalSet::new();
|
||||
local
|
||||
.run_until(async {
|
||||
use kigi_tools::implementations::grok_build::update_goal::UpdateGoalAck;
|
||||
use kigi_tools::implementations::kigi::update_goal::UpdateGoalAck;
|
||||
let coordinator = MockCoordinator::spawn(three_distinct_not_achieved());
|
||||
let (actor, _tmp) = make_actor_with_cap(
|
||||
Some(coordinator.tx.clone()),
|
||||
@@ -2358,7 +2350,7 @@ async fn goal_classifier_sequential_drain_four_completions_three_attempts_then_c
|
||||
/// must ack as `ClassifierConcurrentInFlight` (NOT success).
|
||||
#[tokio::test(flavor = "current_thread")]
|
||||
async fn goal_classifier_concurrent_in_flight_short_circuits_second_completion() {
|
||||
use kigi_tools::implementations::grok_build::update_goal::UpdateGoalAck;
|
||||
use kigi_tools::implementations::kigi::update_goal::UpdateGoalAck;
|
||||
let local = tokio::task::LocalSet::new();
|
||||
local
|
||||
.run_until(async {
|
||||
@@ -2414,7 +2406,7 @@ async fn rejected_ack_post_cap_carries_correct_reason() {
|
||||
let ack_rx = rxs.pop().unwrap();
|
||||
actor.drain_goal_updates(0, DrainPurpose::TurnEnd).await;
|
||||
let ack = ack_rx.await.expect("ack delivered");
|
||||
use kigi_tools::implementations::grok_build::update_goal::UpdateGoalAck;
|
||||
use kigi_tools::implementations::kigi::update_goal::UpdateGoalAck;
|
||||
match ack {
|
||||
UpdateGoalAck::Rejected { reason, detail } => {
|
||||
assert_eq!(reason, RejectReason::PostCap);
|
||||
@@ -2437,7 +2429,7 @@ async fn pending_queue_overflow_acks_all_as_deferred_and_caps_at_pending_queue_c
|
||||
}
|
||||
let rxs = seed_channel_with_acks(&actor, inputs);
|
||||
actor.drain_goal_updates(0, DrainPurpose::MidTurn).await;
|
||||
use kigi_tools::implementations::grok_build::update_goal::UpdateGoalAck;
|
||||
use kigi_tools::implementations::kigi::update_goal::UpdateGoalAck;
|
||||
for rx in rxs {
|
||||
let ack = rx.await.expect("ack delivered");
|
||||
assert!(
|
||||
@@ -2462,9 +2454,7 @@ async fn pending_queue_overflow_acks_all_as_deferred_and_caps_at_pending_queue_c
|
||||
}
|
||||
#[test]
|
||||
fn render_ack_classifier_achieved_is_success() {
|
||||
use kigi_tools::implementations::grok_build::update_goal::{
|
||||
UpdateGoalAck, render_ack_into_output,
|
||||
};
|
||||
use kigi_tools::implementations::kigi::update_goal::{UpdateGoalAck, render_ack_into_output};
|
||||
let out = render_ack_into_output(UpdateGoalAck::ClassifierAchieved {
|
||||
details_path: "/tmp/details.md".to_string(),
|
||||
})
|
||||
@@ -2475,9 +2465,7 @@ fn render_ack_classifier_achieved_is_success() {
|
||||
}
|
||||
#[test]
|
||||
fn render_ack_classifier_fail_open_achieved_clarifies_no_verdict() {
|
||||
use kigi_tools::implementations::grok_build::update_goal::{
|
||||
UpdateGoalAck, render_ack_into_output,
|
||||
};
|
||||
use kigi_tools::implementations::kigi::update_goal::{UpdateGoalAck, render_ack_into_output};
|
||||
let out =
|
||||
render_ack_into_output(UpdateGoalAck::ClassifierFailOpenAchieved { reason: "timeout" })
|
||||
.expect("FailOpen must be Ok (treated as achieved)");
|
||||
@@ -2488,9 +2476,7 @@ fn render_ack_classifier_fail_open_achieved_clarifies_no_verdict() {
|
||||
}
|
||||
#[test]
|
||||
fn render_ack_not_achieved_is_tool_error_with_correct_code() {
|
||||
use kigi_tools::implementations::grok_build::update_goal::{
|
||||
UpdateGoalAck, render_ack_into_output,
|
||||
};
|
||||
use kigi_tools::implementations::kigi::update_goal::{UpdateGoalAck, render_ack_into_output};
|
||||
let err = render_ack_into_output(UpdateGoalAck::ClassifierNotAchieved {
|
||||
details_path: "/tmp/details.md".to_string(),
|
||||
attempt: 2,
|
||||
@@ -2501,9 +2487,7 @@ fn render_ack_not_achieved_is_tool_error_with_correct_code() {
|
||||
}
|
||||
#[test]
|
||||
fn render_ack_cap_reached_is_tool_error_with_cap_code() {
|
||||
use kigi_tools::implementations::grok_build::update_goal::{
|
||||
UpdateGoalAck, render_ack_into_output,
|
||||
};
|
||||
use kigi_tools::implementations::kigi::update_goal::{UpdateGoalAck, render_ack_into_output};
|
||||
let err = render_ack_into_output(UpdateGoalAck::ClassifierCapReached {
|
||||
details_path: "/tmp/details.md".to_string(),
|
||||
attempt: 3,
|
||||
@@ -2524,9 +2508,7 @@ fn tool_error_code(err: &kigi_tool_runtime::ToolError) -> &str {
|
||||
}
|
||||
#[test]
|
||||
fn render_ack_rejected_uses_reason_error_code() {
|
||||
use kigi_tools::implementations::grok_build::update_goal::{
|
||||
UpdateGoalAck, render_ack_into_output,
|
||||
};
|
||||
use kigi_tools::implementations::kigi::update_goal::{UpdateGoalAck, render_ack_into_output};
|
||||
for (reason, want_code) in all_reject_reasons() {
|
||||
let err = render_ack_into_output(UpdateGoalAck::Rejected {
|
||||
reason: *reason,
|
||||
@@ -2613,7 +2595,7 @@ fn reject_reasons_complete_matrix() {
|
||||
}
|
||||
use crate::session::acp_session::GoalRoleModelConfig;
|
||||
use crate::session::acp_session::goal::{PanelResolveCache, RoleCapability};
|
||||
use kigi_tools::implementations::grok_build::task::types::SubagentDescribeOutcome;
|
||||
use kigi_tools::implementations::kigi::task::types::SubagentDescribeOutcome;
|
||||
fn role_pair(model: &str, agent_type: &str) -> crate::util::config::GoalRoleModel {
|
||||
crate::util::config::GoalRoleModel {
|
||||
model: model.to_string(),
|
||||
@@ -2824,11 +2806,10 @@ async fn resolve_role_override_toolset_incapable_fails_open() {
|
||||
let local = tokio::task::LocalSet::new();
|
||||
local
|
||||
.run_until(async {
|
||||
let mut summary =
|
||||
kigi_tools::implementations::grok_build::task::types::SubagentTypeSummary {
|
||||
can_read: true,
|
||||
..Default::default()
|
||||
};
|
||||
let mut summary = kigi_tools::implementations::kigi::task::types::SubagentTypeSummary {
|
||||
can_read: true,
|
||||
..Default::default()
|
||||
};
|
||||
summary
|
||||
.tool_names
|
||||
.insert(kigi_tools::types::tool::ToolKind::Read, "read_file".into());
|
||||
@@ -2871,7 +2852,7 @@ async fn resolve_role_override_all_pass_commits_and_emits_resolved() {
|
||||
}
|
||||
/// A strict-but-unrepresentable harness (`codex`) fails open with the distinct
|
||||
/// `harness_flavor_unsupported` reason. Honored:
|
||||
/// `grok-build-plan` (non-strict), and `opencode` (non-strict + unrepresentable
|
||||
/// `kigi-plan` (non-strict), and `opencode` (non-strict + unrepresentable
|
||||
/// — proving the gate keys on `is_strict`, not representability alone).
|
||||
#[tokio::test(flavor = "current_thread")]
|
||||
async fn resolve_role_override_harness_flavor_representability_gate() {
|
||||
@@ -2889,7 +2870,7 @@ async fn resolve_role_override_harness_flavor_representability_gate() {
|
||||
let evs = lines_with_type(&log, "goal_role_model_fail_open");
|
||||
assert_eq!(evs.len(), 1, "{log}");
|
||||
assert_eq!(evs[0]["reason"], "harness_flavor_unsupported");
|
||||
let honored = ["grok-build-plan", "opencode"];
|
||||
let honored = ["kigi-plan", "opencode"];
|
||||
for harness in honored {
|
||||
let (ov, log) = run_resolve(
|
||||
&role_pair("good-model", harness),
|
||||
@@ -2983,7 +2964,7 @@ async fn single_role_override_explicit_builds_tool_names_from_summary() {
|
||||
let local = tokio::task::LocalSet::new();
|
||||
local
|
||||
.run_until(async {
|
||||
use kigi_tools::implementations::grok_build::task::types::{
|
||||
use kigi_tools::implementations::kigi::task::types::{
|
||||
SubagentDescribeOutcome, SubagentTypeSummary,
|
||||
};
|
||||
use kigi_tools::types::tool::ToolKind;
|
||||
|
||||
+1
-1
@@ -5,7 +5,7 @@
|
||||
|
||||
use super::support::*;
|
||||
use super::*;
|
||||
use kigi_tools::implementations::grok_build::task::types::{SubagentEvent, SubagentResult};
|
||||
use kigi_tools::implementations::kigi::task::types::{SubagentEvent, SubagentResult};
|
||||
use serial_test::serial;
|
||||
use std::sync::Arc as StdArc;
|
||||
use std::sync::atomic::{AtomicUsize, Ordering as SeqOrd};
|
||||
|
||||
+28
-28
@@ -129,7 +129,7 @@ async fn setup_goal_includes_simplified_prompt() {
|
||||
}
|
||||
#[tokio::test(flavor = "current_thread")]
|
||||
async fn goal_enabled_without_update_goal_disables_harness_continuation_and_todo_gate() {
|
||||
use kigi_tools::implementations::grok_build::UPDATE_GOAL_TOOL_NAME;
|
||||
use kigi_tools::implementations::kigi::UPDATE_GOAL_TOOL_NAME;
|
||||
let local = tokio::task::LocalSet::new();
|
||||
local
|
||||
.run_until(async {
|
||||
@@ -828,7 +828,7 @@ fn render_goal_rules_places_discipline_after_block_recap() {
|
||||
&block_recap,
|
||||
"",
|
||||
None,
|
||||
"/tmp/grok-goal-x/implementer",
|
||||
"/tmp/kigi-goal-x/implementer",
|
||||
true,
|
||||
);
|
||||
let recap_idx = body
|
||||
@@ -851,7 +851,7 @@ fn render_goal_rules_discipline_before_tracking_when_block_recap_empty() {
|
||||
"",
|
||||
"",
|
||||
None,
|
||||
"/tmp/grok-goal-x/implementer",
|
||||
"/tmp/kigi-goal-x/implementer",
|
||||
true,
|
||||
);
|
||||
assert_goal_discipline_in_reminder(&body, "render_goal_rules_empty_recap");
|
||||
@@ -865,7 +865,7 @@ fn render_goal_rules_substitutes_custom_todo_tool_through_full_composition() {
|
||||
"",
|
||||
"",
|
||||
None,
|
||||
"/tmp/grok-goal-x/implementer",
|
||||
"/tmp/kigi-goal-x/implementer",
|
||||
true,
|
||||
);
|
||||
assert_eq!(
|
||||
@@ -908,7 +908,7 @@ fn render_goal_rules_substitutes_all_placeholders_in_slim_template() {
|
||||
"<block>recap</block>\n",
|
||||
"<goal-state>state</goal-state>\n\n",
|
||||
None,
|
||||
"/tmp/grok-goal-x/implementer",
|
||||
"/tmp/kigi-goal-x/implementer",
|
||||
true,
|
||||
);
|
||||
assert!(body.contains("A goal has been set: build a thing"));
|
||||
@@ -941,7 +941,7 @@ fn render_goal_rules_substitutes_all_placeholders_in_slim_template() {
|
||||
!body.contains("/tmp/goal-verifier-"),
|
||||
"slim template must not publish a per-goal verdict file path:\n{body}",
|
||||
);
|
||||
assert!(body.contains("/tmp/grok-goal-x/implementer"));
|
||||
assert!(body.contains("/tmp/kigi-goal-x/implementer"));
|
||||
assert!(body.contains("`{SCRATCH}` placeholder resolves to"));
|
||||
for placeholder in [
|
||||
"{OBJECTIVE}",
|
||||
@@ -978,7 +978,7 @@ fn render_goal_rules_plan_aware_block_when_plan_present() {
|
||||
"",
|
||||
"",
|
||||
Some(plan_path),
|
||||
"/tmp/grok-goal-x/implementer",
|
||||
"/tmp/kigi-goal-x/implementer",
|
||||
true,
|
||||
);
|
||||
assert_eq!(
|
||||
@@ -1042,7 +1042,7 @@ fn render_goal_rules_no_plan_block_when_plan_absent() {
|
||||
"",
|
||||
"",
|
||||
None,
|
||||
"/tmp/grok-goal-x/implementer",
|
||||
"/tmp/kigi-goal-x/implementer",
|
||||
true,
|
||||
);
|
||||
assert!(
|
||||
@@ -1088,7 +1088,7 @@ fn render_goal_rules_scratch_status_reflects_readiness() {
|
||||
"",
|
||||
"",
|
||||
None,
|
||||
"/tmp/grok-goal-x/implementer",
|
||||
"/tmp/kigi-goal-x/implementer",
|
||||
true,
|
||||
);
|
||||
assert!(
|
||||
@@ -1109,7 +1109,7 @@ fn render_goal_rules_scratch_status_reflects_readiness() {
|
||||
"",
|
||||
"",
|
||||
None,
|
||||
"/tmp/grok-goal-x/implementer",
|
||||
"/tmp/kigi-goal-x/implementer",
|
||||
false,
|
||||
);
|
||||
assert!(
|
||||
@@ -1294,7 +1294,7 @@ fn continuation_directive_renders_strategist_note_only_when_present() {
|
||||
"next",
|
||||
"todo_write",
|
||||
"update_goal",
|
||||
"/tmp/grok-goal-x/implementer",
|
||||
"/tmp/kigi-goal-x/implementer",
|
||||
true,
|
||||
);
|
||||
let note_idx = with_note
|
||||
@@ -1319,7 +1319,7 @@ fn continuation_directive_renders_strategist_note_only_when_present() {
|
||||
"next",
|
||||
"todo_write",
|
||||
"update_goal",
|
||||
"/tmp/grok-goal-x/implementer",
|
||||
"/tmp/kigi-goal-x/implementer",
|
||||
true,
|
||||
);
|
||||
assert!(
|
||||
@@ -1349,7 +1349,7 @@ fn strategist_note_neutralises_placeholder_injection() {
|
||||
"next",
|
||||
"todo_write",
|
||||
"update_goal",
|
||||
"/tmp/grok-goal-x/implementer",
|
||||
"/tmp/kigi-goal-x/implementer",
|
||||
true,
|
||||
);
|
||||
assert!(
|
||||
@@ -1362,7 +1362,7 @@ fn strategist_note_neutralises_placeholder_injection() {
|
||||
"the `{{goal_tool}}` token inside the note must neither survive intact nor expand:\n{directive}",
|
||||
);
|
||||
assert!(
|
||||
!directive.contains("write to /tmp/grok-goal-x/implementer;"),
|
||||
!directive.contains("write to /tmp/kigi-goal-x/implementer;"),
|
||||
"the `{{scratch_dir}}` token inside the note must NOT expand:\n{directive}",
|
||||
);
|
||||
}
|
||||
@@ -1431,7 +1431,7 @@ fn render_goal_continuation_directive_substitutes_all_placeholders() {
|
||||
"Wire the stop detector into handle_turn_end.",
|
||||
"todo_write",
|
||||
"update_goal",
|
||||
"/tmp/grok-goal-x/implementer",
|
||||
"/tmp/kigi-goal-x/implementer",
|
||||
true,
|
||||
);
|
||||
assert!(body.contains("Objective: ship the directive nudge"));
|
||||
@@ -1471,7 +1471,7 @@ fn render_goal_continuation_directive_substitutes_all_placeholders() {
|
||||
);
|
||||
assert!(!body.contains("Per <task_completion_discipline>"));
|
||||
assert!(
|
||||
body.contains("/tmp/grok-goal-x/implementer"),
|
||||
body.contains("/tmp/kigi-goal-x/implementer"),
|
||||
"continuation directive must advertise the scratch dir:\n{body}",
|
||||
);
|
||||
assert!(
|
||||
@@ -1525,7 +1525,7 @@ fn render_goal_continuation_directive_scratch_status_reflects_readiness() {
|
||||
"next",
|
||||
"todo_write",
|
||||
"update_goal",
|
||||
"/tmp/grok-goal-x/implementer",
|
||||
"/tmp/kigi-goal-x/implementer",
|
||||
true,
|
||||
);
|
||||
assert!(
|
||||
@@ -1552,7 +1552,7 @@ fn render_goal_continuation_directive_scratch_status_reflects_readiness() {
|
||||
"next",
|
||||
"todo_write",
|
||||
"update_goal",
|
||||
"/tmp/grok-goal-x/implementer",
|
||||
"/tmp/kigi-goal-x/implementer",
|
||||
false,
|
||||
);
|
||||
assert!(
|
||||
@@ -1585,7 +1585,7 @@ fn render_goal_continuation_directive_bail_preface_toggles_cleanly() {
|
||||
"next",
|
||||
"todo_write",
|
||||
"update_goal",
|
||||
"/tmp/grok-goal-x/implementer",
|
||||
"/tmp/kigi-goal-x/implementer",
|
||||
true,
|
||||
);
|
||||
assert!(
|
||||
@@ -1605,7 +1605,7 @@ fn render_goal_continuation_directive_bail_preface_toggles_cleanly() {
|
||||
"next",
|
||||
"todo_write",
|
||||
"update_goal",
|
||||
"/tmp/grok-goal-x/implementer",
|
||||
"/tmp/kigi-goal-x/implementer",
|
||||
true,
|
||||
);
|
||||
assert!(
|
||||
@@ -1644,7 +1644,7 @@ fn render_goal_continuation_directive_section_order_is_pinned() {
|
||||
"wire it",
|
||||
"todo_write",
|
||||
"update_goal",
|
||||
"/tmp/grok-goal-x/implementer",
|
||||
"/tmp/kigi-goal-x/implementer",
|
||||
true,
|
||||
);
|
||||
let objective_idx = body.find("Objective: shipping").expect("objective");
|
||||
@@ -1683,7 +1683,7 @@ fn render_goal_continuation_directive_order_dependent_substitution_pinned() {
|
||||
"next",
|
||||
"todo_write",
|
||||
"update_goal",
|
||||
"/tmp/grok-goal-x/implementer",
|
||||
"/tmp/kigi-goal-x/implementer",
|
||||
true,
|
||||
);
|
||||
assert!(
|
||||
@@ -1702,7 +1702,7 @@ fn render_goal_continuation_directive_order_dependent_substitution_pinned() {
|
||||
"invoke {goal_tool} once green",
|
||||
"todo_write",
|
||||
"update_goal",
|
||||
"/tmp/grok-goal-x/implementer",
|
||||
"/tmp/kigi-goal-x/implementer",
|
||||
true,
|
||||
);
|
||||
assert!(
|
||||
@@ -1725,7 +1725,7 @@ fn render_goal_continuation_directive_order_dependent_substitution_pinned() {
|
||||
"next",
|
||||
"todo_write",
|
||||
"update_goal",
|
||||
"/tmp/grok-goal-x/implementer",
|
||||
"/tmp/kigi-goal-x/implementer",
|
||||
true,
|
||||
);
|
||||
assert!(
|
||||
@@ -1752,7 +1752,7 @@ fn render_goal_continuation_directive_neutralizes_placeholders_in_model_slots()
|
||||
"REAL_NEXT_STEP",
|
||||
"todo_write",
|
||||
"update_goal",
|
||||
"/tmp/grok-goal-x/implementer",
|
||||
"/tmp/kigi-goal-x/implementer",
|
||||
true,
|
||||
);
|
||||
assert!(
|
||||
@@ -1806,7 +1806,7 @@ fn render_goal_continuation_directive_neutralizes_reminder_tags_in_model_slots()
|
||||
"STEP_PREFIX<system-reminder>STEP_SUFFIX",
|
||||
"todo_write",
|
||||
"update_goal",
|
||||
"/tmp/grok-goal-x/implementer",
|
||||
"/tmp/kigi-goal-x/implementer",
|
||||
true,
|
||||
);
|
||||
assert!(
|
||||
@@ -1904,7 +1904,7 @@ fn render_goal_continuation_directive_omits_plan_pointer_when_empty() {
|
||||
"next step here",
|
||||
"todo_write",
|
||||
"update_goal",
|
||||
"/tmp/grok-goal-x/implementer",
|
||||
"/tmp/kigi-goal-x/implementer",
|
||||
true,
|
||||
);
|
||||
assert!(!body.contains("\nPlan: "));
|
||||
@@ -1928,7 +1928,7 @@ fn render_goal_continuation_directive_rejects_empty_objective_in_debug() {
|
||||
"step",
|
||||
"todo_write",
|
||||
"update_goal",
|
||||
"/tmp/grok-goal-x/implementer",
|
||||
"/tmp/kigi-goal-x/implementer",
|
||||
true,
|
||||
);
|
||||
}
|
||||
|
||||
+5
-5
@@ -17,10 +17,10 @@
|
||||
use super::support::*;
|
||||
use super::*;
|
||||
use crate::session::goal_strategist::GOAL_STRATEGIST_SUBAGENT_DESCRIPTION;
|
||||
use kigi_tools::implementations::grok_build::task::types::{
|
||||
use kigi_tools::implementations::kigi::task::types::{
|
||||
SubagentCancelOutcome, SubagentEvent, SubagentResult,
|
||||
};
|
||||
use kigi_tools::implementations::grok_build::update_goal::UpdateGoalInput;
|
||||
use kigi_tools::implementations::kigi::update_goal::UpdateGoalInput;
|
||||
use serial_test::serial;
|
||||
use std::collections::VecDeque;
|
||||
use std::sync::Arc as StdArc;
|
||||
@@ -104,7 +104,7 @@ fn spawn_coordinator(
|
||||
|
||||
async fn answer_strategist(
|
||||
behaviour: StrategistBehaviour,
|
||||
req: Box<kigi_tools::implementations::grok_build::task::types::SubagentRequest>,
|
||||
req: Box<kigi_tools::implementations::kigi::task::types::SubagentRequest>,
|
||||
) {
|
||||
match behaviour {
|
||||
StrategistBehaviour::WriteNoteThenDone => {
|
||||
@@ -139,7 +139,7 @@ async fn answer_strategist(
|
||||
async fn answer_skeptic(
|
||||
verdict: SkepticVerdict,
|
||||
spawn_idx: usize,
|
||||
req: Box<kigi_tools::implementations::grok_build::task::types::SubagentRequest>,
|
||||
req: Box<kigi_tools::implementations::kigi::task::types::SubagentRequest>,
|
||||
) {
|
||||
if let Some(p) = parse_details_path(&req.prompt) {
|
||||
let _ = tokio::fs::write(&p, b"# mock skeptic details\n").await;
|
||||
@@ -243,7 +243,7 @@ fn seed_channel(actor: &SessionActor, cmds: Vec<UpdateGoalInput>) {
|
||||
let (tx, rx) = tokio::sync::mpsc::unbounded_channel();
|
||||
*actor.goal_update_rx.borrow_mut() = Some(rx);
|
||||
for cmd in cmds {
|
||||
tx.send(kigi_tools::implementations::grok_build::update_goal::envelope_for_test(cmd))
|
||||
tx.send(kigi_tools::implementations::kigi::update_goal::envelope_for_test(cmd))
|
||||
.unwrap();
|
||||
}
|
||||
drop(tx);
|
||||
|
||||
+5
-5
@@ -17,10 +17,10 @@
|
||||
use super::support::*;
|
||||
use super::*;
|
||||
use crate::session::goal_summarizer::GOAL_SUMMARIZER_SUBAGENT_DESCRIPTION;
|
||||
use kigi_tools::implementations::grok_build::task::types::{
|
||||
use kigi_tools::implementations::kigi::task::types::{
|
||||
SubagentCancelOutcome, SubagentEvent, SubagentResult,
|
||||
};
|
||||
use kigi_tools::implementations::grok_build::update_goal::UpdateGoalInput;
|
||||
use kigi_tools::implementations::kigi::update_goal::UpdateGoalInput;
|
||||
use serial_test::serial;
|
||||
use std::collections::VecDeque;
|
||||
use std::sync::Arc as StdArc;
|
||||
@@ -98,7 +98,7 @@ fn spawn_coordinator(
|
||||
|
||||
async fn answer_summarizer(
|
||||
behaviour: SummarizerBehaviour,
|
||||
req: Box<kigi_tools::implementations::grok_build::task::types::SubagentRequest>,
|
||||
req: Box<kigi_tools::implementations::kigi::task::types::SubagentRequest>,
|
||||
) {
|
||||
match behaviour {
|
||||
SummarizerBehaviour::ReturnSummary => {
|
||||
@@ -127,7 +127,7 @@ async fn answer_summarizer(
|
||||
async fn answer_skeptic(
|
||||
verdict: SkepticVerdict,
|
||||
spawn_idx: usize,
|
||||
req: Box<kigi_tools::implementations::grok_build::task::types::SubagentRequest>,
|
||||
req: Box<kigi_tools::implementations::kigi::task::types::SubagentRequest>,
|
||||
) {
|
||||
if let Some(p) =
|
||||
crate::session::goal_classifier::parse_skeptic_details_path_from_prompt(&req.prompt)
|
||||
@@ -268,7 +268,7 @@ fn seed_channel(actor: &SessionActor, cmds: Vec<UpdateGoalInput>) {
|
||||
let (tx, rx) = tokio::sync::mpsc::unbounded_channel();
|
||||
*actor.goal_update_rx.borrow_mut() = Some(rx);
|
||||
for cmd in cmds {
|
||||
tx.send(kigi_tools::implementations::grok_build::update_goal::envelope_for_test(cmd))
|
||||
tx.send(kigi_tools::implementations::kigi::update_goal::envelope_for_test(cmd))
|
||||
.unwrap();
|
||||
}
|
||||
drop(tx);
|
||||
|
||||
@@ -199,7 +199,7 @@ mod interjection_broadcast_tests {
|
||||
/// Multi-client fix: a mid-turn interjection must be broadcast to every
|
||||
/// attached client (not just the originator) so all panes viewing the same
|
||||
/// session render it. This locks the wire contract the pager's
|
||||
/// `handle_interjection` depends on: method `x.ai/session/interjection`
|
||||
/// `handle_interjection` depends on: method `kigi/session/interjection`
|
||||
/// carrying `sessionId` + `text`.
|
||||
#[tokio::test]
|
||||
async fn broadcast_interjection_emits_sessionid_and_text() {
|
||||
@@ -217,14 +217,14 @@ mod interjection_broadcast_tests {
|
||||
let mut payload = None;
|
||||
while let Ok(msg) = gateway_rx.try_recv() {
|
||||
if let kigi_acp_lib::AcpClientMessage::ExtNotification(args) = msg
|
||||
&& args.request.method.as_ref() == "x.ai/session/interjection"
|
||||
&& args.request.method.as_ref() == "kigi/session/interjection"
|
||||
{
|
||||
payload =
|
||||
serde_json::from_str::<serde_json::Value>(args.request.params.get())
|
||||
.ok();
|
||||
}
|
||||
}
|
||||
let payload = payload.expect("an x.ai/session/interjection broadcast");
|
||||
let payload = payload.expect("an kigi/session/interjection broadcast");
|
||||
assert_eq!(
|
||||
payload.get("sessionId").and_then(|v| v.as_str()),
|
||||
Some("test-actor"),
|
||||
|
||||
+1
-1
@@ -622,7 +622,7 @@ fn sample_line() -> LazinessDebugLogLine {
|
||||
LazinessDebugLogLine {
|
||||
timestamp: "2026-05-21T22:14:01.123Z".to_string(),
|
||||
session_id: "019e4c65-434b-7d62-9d4b-8137d1d413e4".to_string(),
|
||||
model_id: "grok-4.5".to_string(),
|
||||
model_id: "kigi-4.5".to_string(),
|
||||
items_sent: 28,
|
||||
todo_snapshot: vec![DebugTodoSnapshot {
|
||||
id: "turn-finish-test-1".to_string(),
|
||||
|
||||
@@ -180,7 +180,7 @@ async fn incremental_dispatch_surfaces_fast_tool_before_slow_sibling() {
|
||||
/// `execute_tool_calls` Phase 2. The original implementation hardcoded
|
||||
/// `parsed_args.get("file_path")`, which silently bypassed serialization
|
||||
/// for any toolset whose edit input declared the path under a different
|
||||
/// JSON key. The compat toolset input types use `path`, and grok_build's
|
||||
/// JSON key. The compat toolset input types use `path`, and kigi's
|
||||
/// `read_file` uses `target_file`, so all of
|
||||
/// those calls fell through to fully concurrent dispatch and could lose
|
||||
/// edits via TOCTOU on the same workspace file.
|
||||
@@ -188,8 +188,8 @@ async fn incremental_dispatch_surfaces_fast_tool_before_slow_sibling() {
|
||||
/// These tests pin the JSON-key contract so the bucket key keeps tracking
|
||||
/// every toolset's actual schema.
|
||||
#[test]
|
||||
fn lock_path_for_args_matches_grok_build_file_path() {
|
||||
// grok_build search_replace / opencode EditTool / WriteTool / etc.
|
||||
fn lock_path_for_args_matches_kigi_file_path() {
|
||||
// kigi search_replace / opencode EditTool / WriteTool / etc.
|
||||
let args = serde_json::json!({
|
||||
"file_path": "/repo/src/main.rs",
|
||||
"old_string": "foo",
|
||||
@@ -210,8 +210,8 @@ fn lock_path_for_args_matches_path_arg() {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn lock_path_for_args_matches_grok_build_target_file() {
|
||||
// grok_build read_file uses #[serde(rename = "target_file")].
|
||||
fn lock_path_for_args_matches_kigi_target_file() {
|
||||
// kigi read_file uses #[serde(rename = "target_file")].
|
||||
let args = serde_json::json!({
|
||||
"target_file": "/repo/src/main.rs",
|
||||
});
|
||||
@@ -274,14 +274,14 @@ fn lock_path_for_args_buckets_parallel_compat_strreplace_to_same_lock() {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn lock_path_for_args_buckets_grok_build_and_compat_to_same_lock_for_same_file() {
|
||||
// A mixed batch (e.g. grok_build search_replace + StrReplace
|
||||
fn lock_path_for_args_buckets_kigi_and_compat_to_same_lock_for_same_file() {
|
||||
// A mixed batch (e.g. kigi search_replace + StrReplace
|
||||
// in the same turn — possible if the harness ever exposes both, or
|
||||
// during toolset migration) must still serialize on the shared file
|
||||
// path. file_path takes precedence over path when both are present,
|
||||
// but neither tool emits both keys today, so this asserts the
|
||||
// cross-toolset key normalization works in practice.
|
||||
let grok = serde_json::json!({
|
||||
let kigi = serde_json::json!({
|
||||
"file_path": "/repo/src/main.rs",
|
||||
"old_string": "a",
|
||||
"new_string": "b",
|
||||
@@ -291,7 +291,7 @@ fn lock_path_for_args_buckets_grok_build_and_compat_to_same_lock_for_same_file()
|
||||
"old_string": "c",
|
||||
"new_string": "d",
|
||||
});
|
||||
assert_eq!(lock_path_for_args(&grok), lock_path_for_args(&compat));
|
||||
assert_eq!(lock_path_for_args(&kigi), lock_path_for_args(&compat));
|
||||
}
|
||||
|
||||
/// Regression: skill-discovery reminders must land after all tool results, not mid-batch.
|
||||
|
||||
+2
-2
@@ -1,7 +1,7 @@
|
||||
//! Resume re-park of the `exit_plan_mode` approval + the mid-turn
|
||||
//! disconnect handling.
|
||||
//!
|
||||
//! On resume the shell re-issues the `x.ai/exit_plan_mode` reverse-request when
|
||||
//! On resume the shell re-issues the `kigi/exit_plan_mode` reverse-request when
|
||||
//! `awaiting_plan_approval` was persisted, recreating a real live waiter so the
|
||||
//! pager's existing approve/revise/abandon path works unchanged. These tests
|
||||
//! pin the reverse-request shape, the awaiting-bit lifecycle, and the mid-turn
|
||||
@@ -93,7 +93,7 @@ async fn request_plan_approval_issues_reverse_request_and_clears_flag() {
|
||||
);
|
||||
|
||||
let (method, session_id) = responder.await.unwrap();
|
||||
assert_eq!(method.as_deref(), Some("x.ai/exit_plan_mode"));
|
||||
assert_eq!(method.as_deref(), Some("kigi/exit_plan_mode"));
|
||||
assert_eq!(
|
||||
session_id.as_deref(),
|
||||
Some("test-actor"),
|
||||
|
||||
+7
-7
@@ -6,12 +6,12 @@
|
||||
//! BEFORE the permission layer can auto-approve.
|
||||
use super::support::*;
|
||||
use super::*;
|
||||
/// Build an actor whose toolset parses grok `search_replace` plus the plan
|
||||
/// Build an actor whose toolset parses kigi `search_replace` plus the plan
|
||||
/// tools (so `${{ tools.by_kind.exit_plan }}` resolves in the rejection
|
||||
/// message), with a gateway drain answering session notifications.
|
||||
async fn build_gate_actor() -> SessionActor {
|
||||
use kigi_tools::implementations::grok_build::enter_plan_mode::EnterPlanModeTool;
|
||||
use kigi_tools::implementations::grok_build::exit_plan_mode::ExitPlanModeTool;
|
||||
use kigi_tools::implementations::kigi::enter_plan_mode::EnterPlanModeTool;
|
||||
use kigi_tools::implementations::kigi::exit_plan_mode::ExitPlanModeTool;
|
||||
use kigi_tools::registry::types::ToolConfig;
|
||||
let (gateway_tx, mut gateway_rx) =
|
||||
tokio::sync::mpsc::unbounded_channel::<kigi_acp_lib::AcpClientMessage>();
|
||||
@@ -19,8 +19,8 @@ async fn build_gate_actor() -> SessionActor {
|
||||
tokio::sync::mpsc::unbounded_channel::<PersistenceMsg>();
|
||||
let actor = create_test_actor(0, 256_000, 85, gateway_tx, persistence_tx).await;
|
||||
*actor.agent.borrow_mut() = test_agent_with_tools(vec![
|
||||
ToolConfig::from_id("GrokBuild:read_file"),
|
||||
ToolConfig::from_id("GrokBuild:search_replace"),
|
||||
ToolConfig::from_id("Kigi:read_file"),
|
||||
ToolConfig::from_id("Kigi:search_replace"),
|
||||
ToolConfig::for_tool::<EnterPlanModeTool>(),
|
||||
ToolConfig::for_tool::<ExitPlanModeTool>(),
|
||||
])
|
||||
@@ -77,10 +77,10 @@ async fn tool_result_text(actor: &SessionActor, call_id: &str) -> String {
|
||||
.unwrap_or_else(|| panic!("no tool_result for {call_id} in {conv:?}"))
|
||||
}
|
||||
/// The headline: plan mode Active + allow-all permissions (the always-approve
|
||||
/// worst case) still rejects a grok edit outside the plan file, without ever
|
||||
/// worst case) still rejects a kigi edit outside the plan file, without ever
|
||||
/// reaching the permission layer, and steers the model to `exit_plan_mode`.
|
||||
#[tokio::test(flavor = "current_thread")]
|
||||
async fn plan_mode_rejects_grok_edit_outside_plan_file_despite_allow_all_permissions() {
|
||||
async fn plan_mode_rejects_kigi_edit_outside_plan_file_despite_allow_all_permissions() {
|
||||
let local = tokio::task::LocalSet::new();
|
||||
local
|
||||
.run_until(async {
|
||||
|
||||
+6
-6
@@ -60,7 +60,7 @@ fn test_system_prompt_write_and_read() {
|
||||
|
||||
#[test]
|
||||
fn test_system_prompt_is_plain_text_not_json() {
|
||||
let prompt = "You are a Grok Build subagent.";
|
||||
let prompt = "You are a Kigi subagent.";
|
||||
// system_prompt.txt is raw text, NOT JSON-encoded.
|
||||
assert!(!prompt.starts_with('"'), "must not be JSON-quoted");
|
||||
assert!(!prompt.starts_with('{'), "must not be JSON object");
|
||||
@@ -107,7 +107,7 @@ fn test_system_prompt_matches_chat_history_system_message() {
|
||||
let session_dir = tmp.path().join("session-consistency");
|
||||
std::fs::create_dir_all(&session_dir).unwrap();
|
||||
|
||||
let system_prompt = "You are a Grok Build subagent.\n\n<tool_calling>\n...";
|
||||
let system_prompt = "You are a Kigi subagent.\n\n<tool_calling>\n...";
|
||||
|
||||
// Write system_prompt.txt (same string used for chat_history).
|
||||
std::fs::write(session_dir.join(SYSTEM_PROMPT_FILENAME), system_prompt).unwrap();
|
||||
@@ -164,7 +164,7 @@ fn test_load_system_prompt_returns_content_when_present() {
|
||||
let session_dir = tmp.path().join("session-load-test");
|
||||
std::fs::create_dir_all(&session_dir).unwrap();
|
||||
|
||||
let prompt = "You are a Grok Build subagent.";
|
||||
let prompt = "You are a Kigi subagent.";
|
||||
std::fs::write(session_dir.join(SYSTEM_PROMPT_FILENAME), prompt).unwrap();
|
||||
|
||||
let loaded = load_system_prompt_from_dir(&session_dir);
|
||||
@@ -243,7 +243,7 @@ const HEAD_TOKEN: &str = "HEADSTART_TOKEN_aaa";
|
||||
const TAIL_TOKEN: &str = "TAILEND_TOKEN_zzz";
|
||||
|
||||
fn fake_prompt_path() -> std::path::PathBuf {
|
||||
std::path::PathBuf::from("/tmp/grok-test-home/sessions/cwd/sid/prompts/prompt_0.txt")
|
||||
std::path::PathBuf::from("/tmp/kigi-test-home/sessions/cwd/sid/prompts/prompt_0.txt")
|
||||
}
|
||||
|
||||
/// `truncate_bytes_suffix` keeps a char-boundary-safe suffix (multibyte-safe).
|
||||
@@ -343,7 +343,7 @@ fn build_truncated_preserves_small_query_truncates_context() {
|
||||
assert!(message.contains(&query), "small query preserved intact");
|
||||
assert!(
|
||||
message.starts_with(&query),
|
||||
"grok ordering: query block first"
|
||||
"kigi ordering: query block first"
|
||||
);
|
||||
assert!(message.contains("CTXHEAD_TOKEN"), "context head preserved");
|
||||
assert!(!message.contains(&context), "oversized context truncated");
|
||||
@@ -381,7 +381,7 @@ fn build_truncated_both_oversized_keeps_bounded_heads() {
|
||||
assert!(!message.contains(&context), "full context not inlined");
|
||||
assert!(
|
||||
message.starts_with("QHEAD_TOKEN"),
|
||||
"grok ordering: query first"
|
||||
"kigi ordering: query first"
|
||||
);
|
||||
assert!(
|
||||
message.len() <= TRUNCATED_PROMPT_PREFIX_SIZE,
|
||||
|
||||
@@ -415,9 +415,9 @@ async fn manual_recap_generation_failure_persists_request_artifact() {
|
||||
"artifact must include the recap request items"
|
||||
);
|
||||
assert!(
|
||||
artifact.x_grok_req_id.starts_with("xai-recap-"),
|
||||
artifact.x_kigi_req_id.starts_with("xai-recap-"),
|
||||
"req id: {}",
|
||||
artifact.x_grok_req_id
|
||||
artifact.x_kigi_req_id
|
||||
);
|
||||
saw_recap_request = true;
|
||||
}
|
||||
@@ -529,7 +529,7 @@ async fn manual_recap_over_budget_trims_persisted_request_and_is_display_only()
|
||||
/// Over-budget recap serializes to a well-formed Anthropic Messages payload:
|
||||
/// system preserved, reasoning stripped, no dangling `tool_use`/`tool_result`, no
|
||||
/// `tool_result` before the appended instruction. (Messages is the strictest
|
||||
/// shape, so it also covers the laxer grok ChatCompletions/Responses shapes.)
|
||||
/// shape, so it also covers the laxer kigi ChatCompletions/Responses shapes.)
|
||||
#[test]
|
||||
fn over_budget_recap_serializes_to_well_formed_messages_request() {
|
||||
use crate::session::helpers::session_recap;
|
||||
@@ -568,7 +568,7 @@ fn over_budget_recap_serializes_to_well_formed_messages_request() {
|
||||
ConversationItem::tool_result("c2", "z".repeat(40_000)), // trailing run
|
||||
];
|
||||
|
||||
// grok backend => strip_reasoning=false; the over-budget branch strips anyway.
|
||||
// kigi backend => strip_reasoning=false; the over-budget branch strips anyway.
|
||||
let items = session_recap::budget_recap_items(conv, "system-reminder", false, 8_000);
|
||||
let req = ConversationRequest::from_items(items);
|
||||
let msg = kigi_sampling_types::build_messages_request(&req);
|
||||
|
||||
@@ -115,7 +115,7 @@ fn remote_settings_preserves_false_and_zero_todo_gate_fields() {
|
||||
assert_eq!(settings.todo_gate_max_fires_per_prompt, Some(0));
|
||||
}
|
||||
fn def_with_template(tpl: TemplateOverride) -> AgentDefinition {
|
||||
let mut def = AgentDefinition::default_grok_build();
|
||||
let mut def = AgentDefinition::default_kigi();
|
||||
def.system_prompt = tpl;
|
||||
def
|
||||
}
|
||||
@@ -127,7 +127,7 @@ fn policy_with_gate(enabled: bool) -> ReminderPolicy {
|
||||
use crate::session::goal_tracker::GoalStatus;
|
||||
#[test]
|
||||
fn goal_slash_and_harness_available_predicate_matrix() {
|
||||
use kigi_tools::implementations::grok_build::UPDATE_GOAL_TOOL_NAME;
|
||||
use kigi_tools::implementations::kigi::UPDATE_GOAL_TOOL_NAME;
|
||||
let other = vec!["todo_write".to_string()];
|
||||
let with_update = vec![UPDATE_GOAL_TOOL_NAME.to_string()];
|
||||
for (goal_enabled, tool_names, expect) in [
|
||||
|
||||
+1
-1
@@ -1193,7 +1193,7 @@ async fn reasoning_only_doomloop_turn_captures_every_generation_as_segments() {
|
||||
completion_tokens: Some(0),
|
||||
reasoning_tokens: Some(4096),
|
||||
prompt_tokens: Some(128),
|
||||
model: "grok-test".to_string(),
|
||||
model: "kigi-test".to_string(),
|
||||
first_choice_seen: true,
|
||||
}),
|
||||
doom_loop_triggers: None,
|
||||
|
||||
+2
-2
@@ -2,10 +2,10 @@
|
||||
//! (permission / `ask_user_question` / plan-approval) must carry a
|
||||
//! non-empty `sessionId`, otherwise Tier-2 routing silently drops it
|
||||
//! (`server.rs`). The invariant holds today; these tests pin it.
|
||||
use kigi_tools::implementations::grok_build::ask_user_question::{
|
||||
use kigi_tools::implementations::kigi::ask_user_question::{
|
||||
AskUserQuestionExtRequest, AskUserQuestionMode,
|
||||
};
|
||||
use kigi_tools::implementations::grok_build::exit_plan_mode::ExitPlanModeExtRequest;
|
||||
use kigi_tools::implementations::kigi::exit_plan_mode::ExitPlanModeExtRequest;
|
||||
|
||||
#[test]
|
||||
fn ask_user_question_request_carries_session_id() {
|
||||
|
||||
+6
-8
@@ -51,7 +51,7 @@ fn skips_synthetic_reminder_at_index_one() {
|
||||
/// Drives the real `handle_rebuild_agent_for_definition` path.
|
||||
#[tokio::test(flavor = "current_thread")]
|
||||
async fn rebuild_reinjects_goal_update_handle() {
|
||||
use kigi_tools::implementations::grok_build::update_goal::{
|
||||
use kigi_tools::implementations::kigi::update_goal::{
|
||||
GoalUpdateHandle, UpdateGoalInput, envelope_for_test,
|
||||
};
|
||||
let local = tokio::task::LocalSet::new();
|
||||
@@ -61,9 +61,7 @@ async fn rebuild_reinjects_goal_update_handle() {
|
||||
let (persist_tx, _persist_rx) = tokio::sync::mpsc::unbounded_channel();
|
||||
let actor = create_test_actor(0, 256_000, 85, gw_tx, persist_tx).await;
|
||||
actor
|
||||
.handle_rebuild_agent_for_definition(
|
||||
kigi_agent::AgentDefinition::default_grok_build(),
|
||||
)
|
||||
.handle_rebuild_agent_for_definition(kigi_agent::AgentDefinition::default_kigi())
|
||||
.await
|
||||
.expect("zero-turn rebuild should succeed");
|
||||
let bridge = actor.agent.borrow().tool_bridge().clone();
|
||||
@@ -99,7 +97,7 @@ async fn rebuild_reinjects_goal_update_handle() {
|
||||
.await;
|
||||
}
|
||||
/// The seeded skill used by the rebuild skill-reminder tests. A non-plugin
|
||||
/// Local skill is always listable, so it renders into the grok markdown skill
|
||||
/// Local skill is always listable, so it renders into the kigi markdown skill
|
||||
/// catalog when the pending baseline is drained for a different agent.
|
||||
fn regression_skill() -> kigi_tools::implementations::skills::types::SkillInfo {
|
||||
kigi_tools::implementations::skills::types::SkillInfo {
|
||||
@@ -149,9 +147,9 @@ fn stale_source_reminder() -> ConversationItem {
|
||||
- stale-source-skill: from the source session.\n</system-reminder>",
|
||||
)
|
||||
}
|
||||
/// Regression: a zero-turn agent rebuild INTO a grok/Default agent
|
||||
/// Regression: a zero-turn agent rebuild INTO a kigi/Default agent
|
||||
/// must re-inject the baseline skill `<system-reminder>`. `initialize()`
|
||||
/// is otherwise the only place skills are surfaced for the grok agent, so
|
||||
/// is otherwise the only place skills are surfaced for the kigi agent, so
|
||||
/// before the fix a switch into such an agent — whose rebuilt bridge holds
|
||||
/// a pending `BaselineChange` — dropped the skill listing for a no-tool first
|
||||
/// turn. Drives the real `inject_baseline_skill_reminder` seam that
|
||||
@@ -187,7 +185,7 @@ async fn rebuild_reinjects_baseline_skill_reminder_for_non_cursor() {
|
||||
let text = reminder.text_content();
|
||||
assert!(
|
||||
text.contains("The following skills are available for use:"),
|
||||
"reminder must carry the grok skill catalog header:\n{text}",
|
||||
"reminder must carry the kigi skill catalog header:\n{text}",
|
||||
);
|
||||
assert!(
|
||||
text.contains("regression-baseline-skill"),
|
||||
|
||||
+10
-11
@@ -88,7 +88,7 @@ async fn subagent_usage_fold_attribution_gate() {
|
||||
#[test]
|
||||
fn usage_drain_outcome_policy_matches_freeze_and_cancel() {
|
||||
use super::turn::UsageDrainOutcome;
|
||||
use kigi_tools::implementations::grok_build::task::types::SubagentOutstandingReply;
|
||||
use kigi_tools::implementations::kigi::task::types::SubagentOutstandingReply;
|
||||
|
||||
let none = UsageDrainOutcome::from_outstanding_reply(None);
|
||||
assert!(none.fail_closed);
|
||||
@@ -341,11 +341,10 @@ async fn snapshot_ors_ledger_incomplete_even_when_reply_complete() {
|
||||
/// Scripted coordinator stub: answers each `Outstanding` query with the next
|
||||
/// queued reply, repeating the last one; other events are ignored.
|
||||
fn scripted_outstanding_responder(
|
||||
replies: Vec<kigi_tools::implementations::grok_build::task::types::SubagentOutstandingReply>,
|
||||
) -> tokio::sync::mpsc::UnboundedSender<
|
||||
kigi_tools::implementations::grok_build::task::types::SubagentEvent,
|
||||
> {
|
||||
use kigi_tools::implementations::grok_build::task::types::SubagentEvent;
|
||||
replies: Vec<kigi_tools::implementations::kigi::task::types::SubagentOutstandingReply>,
|
||||
) -> tokio::sync::mpsc::UnboundedSender<kigi_tools::implementations::kigi::task::types::SubagentEvent>
|
||||
{
|
||||
use kigi_tools::implementations::kigi::task::types::SubagentEvent;
|
||||
let (tx, mut rx) = tokio::sync::mpsc::unbounded_channel::<SubagentEvent>();
|
||||
tokio::task::spawn_local(async move {
|
||||
let mut queue = replies.into_iter();
|
||||
@@ -365,7 +364,7 @@ fn scripted_outstanding_responder(
|
||||
/// ledgers are marked incomplete.
|
||||
#[tokio::test(flavor = "current_thread")]
|
||||
async fn freeze_timeout_marks_report_and_both_ledgers() {
|
||||
use kigi_tools::implementations::grok_build::task::types::SubagentOutstandingReply;
|
||||
use kigi_tools::implementations::kigi::task::types::SubagentOutstandingReply;
|
||||
tokio::task::LocalSet::new()
|
||||
.run_until(async {
|
||||
let mut actor = make_actor().await;
|
||||
@@ -404,7 +403,7 @@ async fn freeze_timeout_marks_report_and_both_ledgers() {
|
||||
/// because its fold still lands on the session ledger at completion.
|
||||
#[tokio::test(flavor = "current_thread")]
|
||||
async fn freeze_background_only_flags_report_not_ledgers() {
|
||||
use kigi_tools::implementations::grok_build::task::types::SubagentOutstandingReply;
|
||||
use kigi_tools::implementations::kigi::task::types::SubagentOutstandingReply;
|
||||
tokio::task::LocalSet::new()
|
||||
.run_until(async {
|
||||
let mut actor = make_actor().await;
|
||||
@@ -446,7 +445,7 @@ async fn freeze_background_only_flags_report_not_ledgers() {
|
||||
#[tokio::test(flavor = "current_thread")]
|
||||
async fn finalize_background_only_flags_report_not_ledgers() {
|
||||
use super::turn::UsageDrainOutcome;
|
||||
use kigi_tools::implementations::grok_build::task::types::SubagentOutstandingReply;
|
||||
use kigi_tools::implementations::kigi::task::types::SubagentOutstandingReply;
|
||||
|
||||
tokio::task::LocalSet::new()
|
||||
.run_until(async {
|
||||
@@ -583,7 +582,7 @@ async fn apply_miss_matching_pin_stains_prompt_and_session() {
|
||||
/// Sticky (session-only) is report-only on freeze: session ledger stays complete.
|
||||
#[tokio::test(flavor = "current_thread")]
|
||||
async fn freeze_sticky_only_flags_report_not_ledgers() {
|
||||
use kigi_tools::implementations::grok_build::task::types::SubagentOutstandingReply;
|
||||
use kigi_tools::implementations::kigi::task::types::SubagentOutstandingReply;
|
||||
tokio::task::LocalSet::new()
|
||||
.run_until(async {
|
||||
let mut actor = make_actor().await;
|
||||
@@ -637,7 +636,7 @@ async fn freeze_sticky_only_flags_report_not_ledgers() {
|
||||
/// A fold landing mid-drain completes cleanly: no incomplete flag anywhere.
|
||||
#[tokio::test(flavor = "current_thread")]
|
||||
async fn freeze_completes_when_fold_lands_mid_drain() {
|
||||
use kigi_tools::implementations::grok_build::task::types::SubagentOutstandingReply;
|
||||
use kigi_tools::implementations::kigi::task::types::SubagentOutstandingReply;
|
||||
tokio::task::LocalSet::new()
|
||||
.run_until(async {
|
||||
let mut actor = make_actor().await;
|
||||
|
||||
@@ -21,16 +21,16 @@ pub(crate) async fn test_agent_default() -> kigi_agent::Agent {
|
||||
/// resolve to their builtins when a turn is driven through `handle_prompt`.
|
||||
#[cfg(test)]
|
||||
pub(crate) async fn test_agent_with_goal_tool() -> kigi_agent::Agent {
|
||||
use kigi_tools::implementations::grok_build::update_goal::UpdateGoalTool;
|
||||
use kigi_tools::implementations::kigi::update_goal::UpdateGoalTool;
|
||||
use kigi_tools::registry::types::ToolConfig;
|
||||
test_agent_with_tools(vec![ToolConfig::for_tool::<UpdateGoalTool>()]).await
|
||||
}
|
||||
/// Grok-build agent with the real `TodoWriteTool` (id `todo_write`, kind
|
||||
/// Kigi-build agent with the real `TodoWriteTool` (id `todo_write`, kind
|
||||
/// `Plan`) registered, so `tool_for_kind(ToolKind::Plan)` resolves through the
|
||||
/// live toolset instead of the literal fallback.
|
||||
#[cfg(test)]
|
||||
pub(crate) async fn test_grok_build_agent_with_todo() -> kigi_agent::Agent {
|
||||
use kigi_tools::implementations::grok_build::todo::TodoWriteTool;
|
||||
pub(crate) async fn test_kigi_agent_with_todo() -> kigi_agent::Agent {
|
||||
use kigi_tools::implementations::kigi::todo::TodoWriteTool;
|
||||
use kigi_tools::registry::types::ToolConfig;
|
||||
test_agent_with_tools(vec![ToolConfig::for_tool::<TodoWriteTool>()]).await
|
||||
}
|
||||
@@ -39,8 +39,8 @@ pub(crate) async fn test_grok_build_agent_with_todo() -> kigi_agent::Agent {
|
||||
/// `exit_plan_mode` only finalizes when `enter_plan_mode` is also present.
|
||||
#[cfg(test)]
|
||||
pub(crate) async fn test_agent_with_plan_tools() -> kigi_agent::Agent {
|
||||
use kigi_tools::implementations::grok_build::enter_plan_mode::EnterPlanModeTool;
|
||||
use kigi_tools::implementations::grok_build::exit_plan_mode::ExitPlanModeTool;
|
||||
use kigi_tools::implementations::kigi::enter_plan_mode::EnterPlanModeTool;
|
||||
use kigi_tools::implementations::kigi::exit_plan_mode::ExitPlanModeTool;
|
||||
use kigi_tools::registry::types::ToolConfig;
|
||||
test_agent_with_tools(vec![
|
||||
ToolConfig::for_tool::<EnterPlanModeTool>(),
|
||||
@@ -57,7 +57,7 @@ pub(crate) async fn test_agent_with_tools(
|
||||
tools,
|
||||
behavior_preset: None,
|
||||
},
|
||||
kigi_agent::AgentDefinition::default_grok_build(),
|
||||
kigi_agent::AgentDefinition::default_kigi(),
|
||||
std::sync::Arc::new(kigi_tools::computer::local::LocalTerminalBackend::new()),
|
||||
)
|
||||
.await
|
||||
@@ -78,7 +78,7 @@ async fn test_agent_from_config(
|
||||
backend,
|
||||
fs,
|
||||
cwd: std::path::PathBuf::from("/tmp"),
|
||||
session_folder: std::env::temp_dir().join("grok-test"),
|
||||
session_folder: std::env::temp_dir().join("kigi-test"),
|
||||
session_env: std::sync::Arc::new(std::collections::HashMap::new()),
|
||||
notification_handle: ToolNotificationHandle::noop(),
|
||||
owner_session_id: None,
|
||||
|
||||
+1
-1
@@ -388,7 +388,7 @@ async fn send_now_cancel_stamps_cancel_trigger_on_turn_end() {
|
||||
let mut wire_meta = None;
|
||||
while let Ok(msg) = gateway_rx.try_recv() {
|
||||
if let kigi_acp_lib::AcpClientMessage::ExtNotification(args) = msg
|
||||
&& args.request.method.as_ref() == "x.ai/session_notification"
|
||||
&& args.request.method.as_ref() == "kigi/session_notification"
|
||||
&& let Ok(v) =
|
||||
serde_json::from_str::<serde_json::Value>(args.request.params.get())
|
||||
&& v["update"]["sessionUpdate"] == "turn_completed"
|
||||
|
||||
@@ -9,7 +9,7 @@ async fn web_search_errors_when_disabled() {
|
||||
let builder = crate::tools::bridge::ToolBridge::get_builder();
|
||||
let config = ToolServerConfig {
|
||||
tools: vec![ToolConfig {
|
||||
id: "GrokBuild:web_search".into(),
|
||||
id: "Kigi:web_search".into(),
|
||||
params: None,
|
||||
name_override: None,
|
||||
params_name_overrides: None,
|
||||
@@ -26,13 +26,13 @@ async fn web_search_errors_when_disabled() {
|
||||
backend: terminal,
|
||||
fs,
|
||||
cwd: std::env::temp_dir(),
|
||||
session_folder: std::env::temp_dir().join("grok-web-search-disabled"),
|
||||
session_folder: std::env::temp_dir().join("kigi-web-search-disabled"),
|
||||
session_env: std::sync::Arc::new(std::collections::HashMap::new()),
|
||||
notification_handle: ToolNotificationHandle::noop(),
|
||||
owner_session_id: None,
|
||||
parent_scheduler_handle: None,
|
||||
skills: vec![],
|
||||
state_path: std::env::temp_dir().join("grok-web-search-disabled/state.json"),
|
||||
state_path: std::env::temp_dir().join("kigi-web-search-disabled/state.json"),
|
||||
memory_backend: None,
|
||||
web_search_config: kigi_tools::implementations::web_search::WebSearchConfig::Disabled,
|
||||
web_fetch_config: Default::default(),
|
||||
|
||||
@@ -460,7 +460,7 @@ pub struct ContextInfo {
|
||||
/// at the time this snapshot was captured. Comes from the 6-tier resolution
|
||||
/// (env > user per-model > user global > GB per-model > GB global > 85).
|
||||
/// Used by the TUI `/context` view so the displayed “Auto-compact at X%”
|
||||
/// always matches the actual trigger (e.g. 65 for grok-build in remote settings).
|
||||
/// always matches the actual trigger (e.g. 65 for kigi in remote settings).
|
||||
#[serde(default = "default_auto_compact_threshold")]
|
||||
pub auto_compact_threshold_percent: u8,
|
||||
/// Itemized usage rows (skills listing, MCP server listing). Empty on
|
||||
@@ -495,7 +495,7 @@ fn default_auto_compact_threshold() -> u8 {
|
||||
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct SessionInfoData {
|
||||
/// Agent definition name for this session (e.g. `grok-build`).
|
||||
/// Agent definition name for this session (e.g. `kigi`).
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub agent_name: Option<String>,
|
||||
pub model: Option<String>,
|
||||
@@ -554,7 +554,7 @@ pub fn model_display_name(
|
||||
model.to_string()
|
||||
}
|
||||
|
||||
/// Full wire response for `x.ai/session/info`.
|
||||
/// Full wire response for `kigi/session/info`.
|
||||
///
|
||||
/// Wraps `SessionInfoData` with session-level fields (`session_id`, `cwd`)
|
||||
/// that come from the agent layer rather than the session actor.
|
||||
@@ -659,7 +659,7 @@ mod tests {
|
||||
);
|
||||
assert_eq!(input.session_id, "sess-1");
|
||||
|
||||
let submission = input.to_submission(Some("grok-3".into()), None, None, Some(5));
|
||||
let submission = input.to_submission(Some("kigi-3".into()), None, None, Some(5));
|
||||
assert_eq!(
|
||||
submission.client_type,
|
||||
crate::session::feedback_types::ClientType::Desktop
|
||||
|
||||
@@ -44,12 +44,12 @@ use kigi_agent::prompt::context::PromptAudience;
|
||||
use kigi_agent::prompt::skills::SkillsConfig;
|
||||
use kigi_agent::{Agent, AgentBuilder, CompactionPolicy, ReminderPolicy};
|
||||
use kigi_tools::computer::types::{AsyncFileSystem, TerminalBackend};
|
||||
use kigi_tools::implementations::grok_build::ask_user_question::types::UserQuestionRequest;
|
||||
use kigi_tools::implementations::grok_build::deploy_app::AppBuilderDeployerConfig;
|
||||
use kigi_tools::implementations::grok_build::task::types::{
|
||||
use kigi_tools::implementations::kigi::ask_user_question::types::UserQuestionRequest;
|
||||
use kigi_tools::implementations::kigi::deploy_app::AppBuilderDeployerConfig;
|
||||
use kigi_tools::implementations::kigi::task::types::{
|
||||
MonitorEventBuffer, SubagentEvent, TaskModelValidator,
|
||||
};
|
||||
use kigi_tools::implementations::grok_build::web_fetch::WebFetchConfig;
|
||||
use kigi_tools::implementations::kigi::web_fetch::WebFetchConfig;
|
||||
use kigi_tools::implementations::lsp::LspBackend;
|
||||
use kigi_tools::implementations::web_search::WebSearchConfig;
|
||||
use kigi_tools::notification::ToolNotificationHandle;
|
||||
@@ -125,7 +125,7 @@ pub(crate) struct AgentRebuildSpec {
|
||||
pub system_prompt_label: String,
|
||||
pub owner_session_id: Option<String>,
|
||||
pub parent_scheduler_handle:
|
||||
Option<kigi_tools::implementations::grok_build::scheduler::types::SchedulerHandle>,
|
||||
Option<kigi_tools::implementations::kigi::scheduler::types::SchedulerHandle>,
|
||||
}
|
||||
impl AgentRebuildSpec {
|
||||
/// Build a fresh [`Agent`] from this spec and an [`AgentDefinition`].
|
||||
@@ -305,10 +305,10 @@ impl AgentRebuildSpec {
|
||||
}))
|
||||
.await;
|
||||
if let Some(event_tx) = subagent_event_tx.clone() {
|
||||
use kigi_tools::implementations::grok_build::task::backend::{
|
||||
use kigi_tools::implementations::kigi::task::backend::{
|
||||
ChannelBackend, SubagentBackendResource,
|
||||
};
|
||||
use kigi_tools::implementations::grok_build::task::types::{
|
||||
use kigi_tools::implementations::kigi::task::types::{
|
||||
SessionIdResource, SubagentDepthCounter, SubagentEventSender,
|
||||
};
|
||||
let backend = SubagentBackendResource(Arc::new(ChannelBackend::new(event_tx.clone())));
|
||||
@@ -342,7 +342,7 @@ impl AgentRebuildSpec {
|
||||
))
|
||||
.await;
|
||||
{
|
||||
use kigi_tools::implementations::grok_build::ask_user_question::UserQuestionSender;
|
||||
use kigi_tools::implementations::kigi::ask_user_question::UserQuestionSender;
|
||||
agent
|
||||
.tool_bridge()
|
||||
.update_resource(UserQuestionSender(user_question_tx.clone()))
|
||||
@@ -424,13 +424,13 @@ mod tests {
|
||||
let toolset = agent.tool_bridge().toolset();
|
||||
let task_name = toolset
|
||||
.tool_name_for_kind(kigi_tools::types::tool::ToolKind::Task)
|
||||
.expect("GrokBuild Task tool should be present");
|
||||
.expect("Kigi Task tool should be present");
|
||||
toolset
|
||||
.tool_definitions()
|
||||
.into_iter()
|
||||
.find(|definition| definition.function.name == task_name)
|
||||
.and_then(|definition| definition.function.description)
|
||||
.expect("GrokBuild Task description should be present")
|
||||
.expect("Kigi Task description should be present")
|
||||
}
|
||||
#[tokio::test(flavor = "current_thread")]
|
||||
async fn rebuild_projects_fresh_public_model_keys_into_task_description() {
|
||||
@@ -453,7 +453,7 @@ mod tests {
|
||||
models_manager
|
||||
.insert_test_entry("private-unselectable-model", unselectable);
|
||||
let first = spec
|
||||
.build_agent(AgentDefinition::default_grok_build())
|
||||
.build_agent(AgentDefinition::default_kigi())
|
||||
.await
|
||||
.expect("first agent build should succeed");
|
||||
let first_description = task_description(&first);
|
||||
@@ -478,7 +478,7 @@ mod tests {
|
||||
.insert_test_entry("beta-public", model_entry("internal-beta"));
|
||||
assert!(validator.error_for("beta-public").is_none());
|
||||
let rebuilt = spec
|
||||
.build_agent(AgentDefinition::default_grok_build())
|
||||
.build_agent(AgentDefinition::default_kigi())
|
||||
.await
|
||||
.expect("rebuilt agent should succeed");
|
||||
let rebuilt_description = task_description(&rebuilt);
|
||||
|
||||
@@ -186,10 +186,10 @@ pub enum SessionCommand {
|
||||
/// and does NOT update `primaryModelId` in signals — the resolved model
|
||||
/// is already tracked via inference responses), this command also calls
|
||||
/// `set_primary_model()` so that signals report the override model
|
||||
/// rather than the agent-level default (e.g. `grok-4.5`).
|
||||
/// rather than the agent-level default (e.g. `kigi-4.5`).
|
||||
///
|
||||
/// Keeps the existing base_url, api_key, and other config — only changes
|
||||
/// the `model` field sent in the `x-grok-model-override` header and merges
|
||||
/// the `model` field sent in the `x-kigi-model-override` header and merges
|
||||
/// any additional headers (e.g. `x-openrouter-api-key` for BYOK).
|
||||
///
|
||||
/// Used to set model IDs (e.g. opaque third-party routing names) that are
|
||||
@@ -256,7 +256,7 @@ pub enum SessionCommand {
|
||||
request: RewindRequest,
|
||||
respond_to: oneshot::Sender<anyhow::Result<RewindResponse>>,
|
||||
},
|
||||
/// Out-of-band history repair (`x.ai/session/repair`): fix tool-pairing
|
||||
/// Out-of-band history repair (`kigi/session/repair`): fix tool-pairing
|
||||
/// violations (orphaned/displaced `ToolResult`s, duplicates, unanswered
|
||||
/// calls) that would otherwise 400 on every request. `dry_run` only
|
||||
/// reports. Refused while a turn is in flight.
|
||||
@@ -329,7 +329,7 @@ pub enum SessionCommand {
|
||||
respond_to: oneshot::Sender<()>,
|
||||
},
|
||||
/// Update MCP servers for an existing session (used during reconnect or
|
||||
/// mid-session via the `x.ai/session/update_mcp_servers` extension method).
|
||||
/// mid-session via the `kigi/session/update_mcp_servers` extension method).
|
||||
/// This replaces the current MCP server configuration and triggers re-initialization.
|
||||
///
|
||||
/// The caller is notified via `respond_to` once MCP re-initialization
|
||||
@@ -453,7 +453,7 @@ pub enum SessionCommand {
|
||||
action: kigi_hooks_plugins_types::PluginsAction,
|
||||
respond_to: oneshot::Sender<kigi_hooks_plugins_types::ActionOutcome>,
|
||||
},
|
||||
/// This session's plugin registry, as served by `x.ai/plugins/list`.
|
||||
/// This session's plugin registry, as served by `kigi/plugins/list`.
|
||||
PluginsList {
|
||||
respond_to: oneshot::Sender<Option<std::sync::Arc<kigi_agent::plugins::PluginRegistry>>>,
|
||||
},
|
||||
@@ -515,7 +515,7 @@ pub enum SessionCommand {
|
||||
},
|
||||
/// Replace the text of a queued (not-yet-running) prompt in place
|
||||
/// (server-side LWW). Last write wins via the actor's
|
||||
/// serialized mailbox; the rebroadcast of `x.ai/queue/changed` is the
|
||||
/// serialized mailbox; the rebroadcast of `kigi/queue/changed` is the
|
||||
/// truth signal for every attached client. The original `owner`
|
||||
/// attribution is preserved; `editor` is recorded as the most recent
|
||||
/// editor (for future "alice edited this" UX). A missing id, or an id
|
||||
@@ -533,7 +533,7 @@ pub enum SessionCommand {
|
||||
/// like [`RemoveQueuedPrompt`]. A benign no-op (the prompt stays queued and
|
||||
/// runs normally) when no turn is running, the id names the running turn, is
|
||||
/// stale/already-drained, or `owner` doesn't match. The rebroadcast of
|
||||
/// `x.ai/queue/changed` is the truth signal for every attached client.
|
||||
/// `kigi/queue/changed` is the truth signal for every attached client.
|
||||
InterjectQueuedPrompt {
|
||||
id: String,
|
||||
expected_version: u64,
|
||||
@@ -630,7 +630,7 @@ pub enum SessionCommand {
|
||||
///
|
||||
/// Fired by the client after a turn completes. The session builds a
|
||||
/// compact text-only transcript of the recent conversation, makes one
|
||||
/// tool-free model call (default `grok-build-0.1` when available via
|
||||
/// tool-free model call (default `kigi-0.1` when available via
|
||||
/// `model_override`, else the session model), sanitizes the output, and
|
||||
/// returns the predicted prompt via `respond_to`. Best-effort: any
|
||||
/// failure returns `None`.
|
||||
@@ -640,7 +640,7 @@ pub enum SessionCommand {
|
||||
},
|
||||
/// Rewrite a raw memory note into well-structured markdown via a one-shot
|
||||
/// LLM call. The session uses `prepare_chat_completion()` with
|
||||
/// `grok-build` model, low temperature, and capped output tokens.
|
||||
/// `kigi` model, low temperature, and capped output tokens.
|
||||
RewriteMemoryNote {
|
||||
raw_text: String,
|
||||
context_summary: String,
|
||||
@@ -653,7 +653,7 @@ pub enum SessionCommand {
|
||||
Interject {
|
||||
text: String,
|
||||
/// Client-minted id echoed back on the broadcast
|
||||
/// `x.ai/session/interjection` so the originating pager can dedup its
|
||||
/// `kigi/session/interjection` so the originating pager can dedup its
|
||||
/// optimistic local block. `None` from older clients.
|
||||
id: Option<String>,
|
||||
/// Pasted images riding along with the interjection. Empty from
|
||||
|
||||
@@ -1243,7 +1243,7 @@ impl SessionActor {
|
||||
self.tool_context.subagent_event_tx
|
||||
{
|
||||
let (tx, rx) = tokio::sync::oneshot::channel();
|
||||
use kigi_tools::implementations::grok_build::task::types::{
|
||||
use kigi_tools::implementations::kigi::task::types::{
|
||||
SubagentEvent, SubagentListActiveRequest,
|
||||
};
|
||||
let _ =
|
||||
@@ -2830,11 +2830,11 @@ mod inline_auto_compact_flow_tests {
|
||||
SuppressReason::CreditBlock
|
||||
);
|
||||
assert_eq!(
|
||||
classify("API error (status 402 Payment Required): Grok Build usage balance exhausted"),
|
||||
classify("API error (status 402 Payment Required): Kigi usage balance exhausted"),
|
||||
SuppressReason::CreditBlock
|
||||
);
|
||||
assert_eq!(
|
||||
classify("Grok Build usage limit reached"),
|
||||
classify("Kigi usage limit reached"),
|
||||
SuppressReason::CreditBlock
|
||||
);
|
||||
assert_eq!(
|
||||
|
||||
@@ -163,7 +163,7 @@ mod prefire_state_tests {
|
||||
note1: "NOTE1".to_string(),
|
||||
prefix_len: 3,
|
||||
fingerprint: 42,
|
||||
model_slug: "grok".to_string(),
|
||||
model_slug: "kigi".to_string(),
|
||||
pass1_latency_ms: 5,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -569,7 +569,7 @@ pub const GOAL_ROLE_MODEL_FAIL_OPEN_SPAWN_FAILED: &str = "spawn_failed";
|
||||
|
||||
/// Fail-open — the configured `agent_type` resolves as a STRICT harness whose
|
||||
/// subagent flavor `resolve_subagent_toolset` can't represent (e.g. `codex`):
|
||||
/// committing it would silently run grok-build flavor. Distinct from
|
||||
/// committing it would silently run kigi flavor. Distinct from
|
||||
/// `toolset_unknown` (a name that doesn't resolve at all).
|
||||
pub const GOAL_ROLE_MODEL_FAIL_OPEN_HARNESS_FLAVOR_UNSUPPORTED: &str = "harness_flavor_unsupported";
|
||||
|
||||
|
||||
@@ -24,7 +24,7 @@ struct XaiJsonRpcNotification<'a> {
|
||||
}
|
||||
|
||||
const ACP_SESSION_UPDATE_METHOD: &str = "session/update";
|
||||
const XAI_SESSION_UPDATE_METHOD: &str = "_x.ai/session/update";
|
||||
const XAI_SESSION_UPDATE_METHOD: &str = "_kigi/session/update";
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct ExportedMessage {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
//! Feedback request heuristics for Grok Code sessions.
|
||||
//! Feedback request heuristics for Kigi Code sessions.
|
||||
//!
|
||||
//! This module implements the feedback request decision logic based on session signals.
|
||||
//! It uses tiered probability sampling to request feedback at appropriate moments
|
||||
@@ -245,7 +245,7 @@ impl FeedbackHeuristics {
|
||||
tier1_feedback_mode: FeedbackMode::Thumbs,
|
||||
tier1_dismissible: true,
|
||||
tier1_prompt:
|
||||
"You've been using Grok Code productively! Would you mind sharing quick feedback?"
|
||||
"You've been using Kigi Code productively! Would you mind sharing quick feedback?"
|
||||
.to_string(),
|
||||
|
||||
// Tier 2: Complex session with friction
|
||||
@@ -452,7 +452,7 @@ impl FeedbackHeuristics {
|
||||
tier1_feedback_mode: FeedbackMode::Thumbs,
|
||||
tier1_dismissible: true,
|
||||
tier1_prompt:
|
||||
"You've been using Grok Code productively! Would you mind sharing quick feedback?"
|
||||
"You've been using Kigi Code productively! Would you mind sharing quick feedback?"
|
||||
.to_string(),
|
||||
tier2_enabled: true,
|
||||
tier2_sample_rate: 0.0002,
|
||||
@@ -774,7 +774,7 @@ impl FeedbackRequest {
|
||||
Some(p) if !p.is_empty() => p.to_string(),
|
||||
_ => match tier {
|
||||
FeedbackTier::Tier1 => {
|
||||
"You've been using Grok Code productively! Would you mind sharing quick feedback?".to_string()
|
||||
"You've been using Kigi Code productively! Would you mind sharing quick feedback?".to_string()
|
||||
}
|
||||
FeedbackTier::Tier2 => {
|
||||
"You've worked through a complex session. Your feedback would help us improve.".to_string()
|
||||
|
||||
@@ -374,7 +374,7 @@ impl FeedbackManager {
|
||||
/// heuristics, sampling, cooldown, and enabled checks.
|
||||
///
|
||||
/// Engineers developing clients can call this via the
|
||||
/// `x.ai/debug/trigger_feedback` ACP extension method to exercise
|
||||
/// `kigi/debug/trigger_feedback` ACP extension method to exercise
|
||||
/// the full feedback notification ↔ response flow without needing a
|
||||
/// real session that meets tier criteria.
|
||||
#[tracing::instrument(name = "feedback.force_feedback_request", skip_all, fields(
|
||||
|
||||
@@ -185,7 +185,7 @@ mod tests {
|
||||
source_cwd: "/old/project".to_string(),
|
||||
new_cwd: "/new/project".to_string(),
|
||||
new_session_id: Some("custom-session-id".to_string()),
|
||||
new_model_id: Some("grok-3".to_string()),
|
||||
new_model_id: Some("kigi-3".to_string()),
|
||||
target_prompt_index: None,
|
||||
..Default::default()
|
||||
};
|
||||
@@ -200,7 +200,7 @@ mod tests {
|
||||
deserialized.new_session_id,
|
||||
Some("custom-session-id".to_string())
|
||||
);
|
||||
assert_eq!(deserialized.new_model_id, Some("grok-3".to_string()));
|
||||
assert_eq!(deserialized.new_model_id, Some("kigi-3".to_string()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -223,7 +223,7 @@ mod tests {
|
||||
plan_state_copied: true,
|
||||
new_cwd: "/new/project".to_string(),
|
||||
parent_session_id: "abc123".to_string(),
|
||||
new_model_id: Some("grok-3".to_string()),
|
||||
new_model_id: Some("kigi-3".to_string()),
|
||||
};
|
||||
|
||||
let json = serde_json::to_string(&response).unwrap();
|
||||
@@ -235,7 +235,7 @@ mod tests {
|
||||
assert!(deserialized.plan_state_copied);
|
||||
assert_eq!(deserialized.new_cwd, "/new/project");
|
||||
assert_eq!(deserialized.parent_session_id, "abc123");
|
||||
assert_eq!(deserialized.new_model_id, Some("grok-3".to_string()));
|
||||
assert_eq!(deserialized.new_model_id, Some("kigi-3".to_string()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
@@ -70,7 +70,7 @@ pub(crate) fn forward_to_hunk_tracker(
|
||||
}
|
||||
}
|
||||
|
||||
/// Dedup key for `x.ai/git_head_changed`, shared by the watcher's `GitHead`
|
||||
/// Dedup key for `kigi/git_head_changed`, shared by the watcher's `GitHead`
|
||||
/// consumer and the post-edit `maybe_notify_git_branch` path so both compute
|
||||
/// the same identity (branch | is_worktree | main_repo).
|
||||
pub(crate) fn git_head_dedup_key(
|
||||
@@ -264,7 +264,7 @@ pub(crate) struct CapabilityInputs {
|
||||
pub client_notify: bool,
|
||||
pub hunk_tracking: bool,
|
||||
pub code_nav: bool,
|
||||
/// `x.ai/gitHeadChanged`; opt-in (absent => off).
|
||||
/// `kigi/gitHeadChanged`; opt-in (absent => off).
|
||||
pub git_head_changed: Option<bool>,
|
||||
}
|
||||
|
||||
@@ -319,7 +319,7 @@ impl ClientNotify {
|
||||
|
||||
match self.mode {
|
||||
ClientFsMode::Events => {
|
||||
// Present-tense strings are the `x.ai/fs_notify` wire protocol;
|
||||
// Present-tense strings are the `kigi/fs_notify` wire protocol;
|
||||
// do not sync to internal variant names.
|
||||
let kind_str = match kind {
|
||||
FsEventKind::Created => "Create",
|
||||
@@ -339,7 +339,7 @@ impl ClientNotify {
|
||||
if let Ok(raw) = to_raw_value(¶ms) {
|
||||
self.gateway
|
||||
.forward_fire_and_forget(acp::ExtNotification::new(
|
||||
"x.ai/fs_notify",
|
||||
"kigi/fs_notify",
|
||||
raw.into(),
|
||||
));
|
||||
}
|
||||
@@ -365,7 +365,7 @@ impl ClientNotify {
|
||||
if let Ok(raw) = to_raw_value(¶ms) {
|
||||
self.gateway
|
||||
.forward_fire_and_forget(acp::ExtNotification::new(
|
||||
"x.ai/fs/index/delta",
|
||||
"kigi/fs/index/delta",
|
||||
raw.into(),
|
||||
));
|
||||
}
|
||||
@@ -425,7 +425,7 @@ impl ClientNotify {
|
||||
if let Ok(raw) = serde_json::value::to_raw_value(¶ms) {
|
||||
self.gateway
|
||||
.forward_fire_and_forget(acp::ExtNotification::new(
|
||||
"x.ai/fs/index",
|
||||
"kigi/fs/index",
|
||||
raw.into(),
|
||||
));
|
||||
}
|
||||
@@ -526,7 +526,7 @@ impl GitHead {
|
||||
if let Ok(raw) = serde_json::value::to_raw_value(¶ms) {
|
||||
self.gateway
|
||||
.forward_fire_and_forget(acp::ExtNotification::new(
|
||||
"x.ai/git_head_changed",
|
||||
"kigi/git_head_changed",
|
||||
raw.into(),
|
||||
));
|
||||
}
|
||||
|
||||
@@ -444,7 +444,7 @@ pub(crate) async fn capture_git_baseline(workspace_root: &Path) -> Option<String
|
||||
/// never sees the spawn live — it is direct (no `task` tool call).
|
||||
pub(crate) struct ChannelSpawner {
|
||||
pub(crate) event_tx: tokio::sync::mpsc::UnboundedSender<
|
||||
kigi_tools::implementations::grok_build::task::types::SubagentEvent,
|
||||
kigi_tools::implementations::kigi::task::types::SubagentEvent,
|
||||
>,
|
||||
pub(crate) parent_session_id: String,
|
||||
pub(crate) parent_prompt_id: Option<String>,
|
||||
@@ -503,7 +503,7 @@ impl ChannelSpawner {
|
||||
harness_agent_type: Option<String>,
|
||||
resume_from: Option<&str>,
|
||||
) -> Result<String, SpawnError> {
|
||||
use kigi_tools::implementations::grok_build::task::types::{
|
||||
use kigi_tools::implementations::kigi::task::types::{
|
||||
SubagentEvent, SubagentRequest, SubagentRuntimeOverrides,
|
||||
};
|
||||
let (result_tx, result_rx) = tokio::sync::oneshot::channel();
|
||||
@@ -2355,7 +2355,7 @@ mod tests {
|
||||
|
||||
#[tokio::test]
|
||||
async fn channel_spawner_request_is_harness_internal() {
|
||||
use kigi_tools::implementations::grok_build::task::types::{SubagentEvent, SubagentResult};
|
||||
use kigi_tools::implementations::kigi::task::types::{SubagentEvent, SubagentResult};
|
||||
|
||||
let (tx, mut rx) = tokio::sync::mpsc::unbounded_channel();
|
||||
let spawner = ChannelSpawner {
|
||||
@@ -2402,7 +2402,7 @@ mod tests {
|
||||
/// SAME model — i.e. skeptic-0 keeps `pool[0]` on the cold fallback.
|
||||
#[tokio::test]
|
||||
async fn channel_spawner_applies_per_index_model_to_request() {
|
||||
use kigi_tools::implementations::grok_build::task::types::{SubagentEvent, SubagentResult};
|
||||
use kigi_tools::implementations::kigi::task::types::{SubagentEvent, SubagentResult};
|
||||
|
||||
let (tx, mut rx) = tokio::sync::mpsc::unbounded_channel();
|
||||
let spawner = ChannelSpawner {
|
||||
@@ -2470,7 +2470,7 @@ mod tests {
|
||||
/// `None` — the historic default-spawn behavior.
|
||||
#[tokio::test]
|
||||
async fn channel_spawner_inherit_index_leaves_model_none() {
|
||||
use kigi_tools::implementations::grok_build::task::types::{SubagentEvent, SubagentResult};
|
||||
use kigi_tools::implementations::kigi::task::types::{SubagentEvent, SubagentResult};
|
||||
let (tx, mut rx) = tokio::sync::mpsc::unbounded_channel();
|
||||
let spawner = ChannelSpawner {
|
||||
event_tx: tx,
|
||||
@@ -2514,7 +2514,7 @@ mod tests {
|
||||
let mac_like = Path::new("/var/folders/zz/T");
|
||||
assert!(
|
||||
validate_details_path_in_root(
|
||||
Path::new("/var/folders/zz/T/grok-goal-abc/goal-classifier-abc-1.md"),
|
||||
Path::new("/var/folders/zz/T/kigi-goal-abc/goal-classifier-abc-1.md"),
|
||||
mac_like,
|
||||
)
|
||||
.is_ok(),
|
||||
@@ -2527,7 +2527,7 @@ mod tests {
|
||||
);
|
||||
assert!(
|
||||
validate_details_path_in_root(
|
||||
Path::new("/tmp/grok-goal-abc/goal-classifier-abc-1.md"),
|
||||
Path::new("/tmp/kigi-goal-abc/goal-classifier-abc-1.md"),
|
||||
Path::new("/tmp"),
|
||||
)
|
||||
.is_ok(),
|
||||
@@ -3478,10 +3478,10 @@ mod tests {
|
||||
]);
|
||||
assert_eq!(a, b, "scratch-path churn must not break the fingerprint");
|
||||
let c = gap_fingerprint(&[
|
||||
"no captured output in /var/folders/x1/T/grok-goal-1/out.log for criterion 2",
|
||||
"no captured output in /var/folders/x1/T/kigi-goal-1/out.log for criterion 2",
|
||||
]);
|
||||
let d = gap_fingerprint(&[
|
||||
"no captured output in /var/folders/x1/T/grok-goal-2/out.log for criterion 2",
|
||||
"no captured output in /var/folders/x1/T/kigi-goal-2/out.log for criterion 2",
|
||||
]);
|
||||
assert_eq!(c, d);
|
||||
// Genuinely different gaps still differ.
|
||||
@@ -3908,7 +3908,7 @@ mod tests {
|
||||
/// renders leave no tool placeholder unresolved.
|
||||
#[test]
|
||||
fn verifier_template_renders_per_agent_type_and_falls_back() {
|
||||
use kigi_tools::implementations::grok_build::task::types::SubagentTypeSummary;
|
||||
use kigi_tools::implementations::kigi::task::types::SubagentTypeSummary;
|
||||
let mut tool_names = std::collections::HashMap::new();
|
||||
tool_names.insert(
|
||||
kigi_tools::types::tool::ToolKind::Read,
|
||||
@@ -3941,14 +3941,14 @@ mod tests {
|
||||
);
|
||||
assert_no_tool_placeholders(&cursor);
|
||||
|
||||
// grok-build explicit render: no leftover placeholder either.
|
||||
let grok = RoleToolNames::from_summary(&summary_with(&[
|
||||
// kigi explicit render: no leftover placeholder either.
|
||||
let kigi = RoleToolNames::from_summary(&summary_with(&[
|
||||
(kigi_tools::types::tool::ToolKind::Read, "read_file"),
|
||||
(kigi_tools::types::tool::ToolKind::ListDir, "list_dir"),
|
||||
(kigi_tools::types::tool::ToolKind::Search, "grep"),
|
||||
]))
|
||||
.apply(GOAL_VERIFIER_PROMPT_TEMPLATE);
|
||||
assert_no_tool_placeholders(&grok);
|
||||
assert_no_tool_placeholders(&kigi);
|
||||
|
||||
// Fallback path (e.g. `describe_subagent_type` ⇒ `Unavailable`): the
|
||||
// parent-toolset defaults render and no placeholder survives.
|
||||
@@ -4123,8 +4123,8 @@ mod tests {
|
||||
"/tmp/goal-verifier-details-x-1-0.md",
|
||||
"/tmp/goal-verdict-x-1-0.json",
|
||||
kind_lens(Some(GoalKind::CodeChange)),
|
||||
"/tmp/grok-goal-x/skeptic-0",
|
||||
"/tmp/grok-goal-x/implementer",
|
||||
"/tmp/kigi-goal-x/skeptic-0",
|
||||
"/tmp/kigi-goal-x/implementer",
|
||||
None,
|
||||
&RoleToolNames::inherit_defaults(),
|
||||
true,
|
||||
@@ -4136,8 +4136,8 @@ mod tests {
|
||||
);
|
||||
// The skeptic's own scratch dir AND the implementer-scratch
|
||||
// awareness line are both present, with no dangling placeholder.
|
||||
assert!(body.contains("/tmp/grok-goal-x/skeptic-0"));
|
||||
assert!(body.contains("/tmp/grok-goal-x/implementer"));
|
||||
assert!(body.contains("/tmp/kigi-goal-x/skeptic-0"));
|
||||
assert!(body.contains("/tmp/kigi-goal-x/implementer"));
|
||||
assert!(
|
||||
!body.contains("{SKEPTIC_SCRATCH}") && !body.contains("{IMPLEMENTER_SCRATCH}"),
|
||||
"scratch placeholders must be substituted:\n{body}"
|
||||
@@ -4154,8 +4154,8 @@ mod tests {
|
||||
"/tmp/goal-verifier-details-x-1-0.md",
|
||||
"/tmp/goal-verdict-x-1-0.json",
|
||||
kind_lens(None),
|
||||
"/tmp/grok-goal-x/skeptic-1",
|
||||
"/tmp/grok-goal-x/implementer",
|
||||
"/tmp/kigi-goal-x/skeptic-1",
|
||||
"/tmp/kigi-goal-x/implementer",
|
||||
None,
|
||||
&RoleToolNames::inherit_defaults(),
|
||||
true,
|
||||
@@ -4181,8 +4181,8 @@ mod tests {
|
||||
"/tmp/goal-verifier-details-x-1-0.md",
|
||||
"/tmp/goal-verdict-x-1-0.json",
|
||||
kind_lens(Some(GoalKind::CodeChange)),
|
||||
"/tmp/grok-goal-x/skeptic-0",
|
||||
"/tmp/grok-goal-x/implementer",
|
||||
"/tmp/kigi-goal-x/skeptic-0",
|
||||
"/tmp/kigi-goal-x/implementer",
|
||||
None,
|
||||
&RoleToolNames::inherit_defaults(),
|
||||
scratch_ready,
|
||||
@@ -4232,8 +4232,8 @@ mod tests {
|
||||
"/tmp/goal-verifier-details-x-2-1.md",
|
||||
"/tmp/goal-verdict-x-2-1.json",
|
||||
kind_lens(Some(GoalKind::CodeChange)),
|
||||
"/tmp/grok-goal-x/skeptic-1",
|
||||
"/tmp/grok-goal-x/implementer",
|
||||
"/tmp/kigi-goal-x/skeptic-1",
|
||||
"/tmp/kigi-goal-x/implementer",
|
||||
prior,
|
||||
&RoleToolNames::inherit_defaults(),
|
||||
true,
|
||||
@@ -4266,8 +4266,8 @@ mod tests {
|
||||
"/tmp/goal-classifier-x-2-skeptic-0.md",
|
||||
"/tmp/goal-verdict-x-2-0.json",
|
||||
kind_lens(Some(GoalKind::CodeChange)),
|
||||
"/tmp/grok-goal-x/skeptic-0",
|
||||
"/tmp/grok-goal-x/implementer",
|
||||
"/tmp/kigi-goal-x/skeptic-0",
|
||||
"/tmp/kigi-goal-x/implementer",
|
||||
None,
|
||||
&RoleToolNames::inherit_defaults(),
|
||||
true,
|
||||
@@ -4289,8 +4289,8 @@ mod tests {
|
||||
assert!(body.contains("/tmp/goal-verdict-x-2-0.json"));
|
||||
assert!(body.contains("/tmp/goal-classifier-x-2-skeptic-0.md"));
|
||||
// Scratch dirs: own + implementer-awareness, both substituted.
|
||||
assert!(body.contains("/tmp/grok-goal-x/skeptic-0"));
|
||||
assert!(body.contains("/tmp/grok-goal-x/implementer"));
|
||||
assert!(body.contains("/tmp/kigi-goal-x/skeptic-0"));
|
||||
assert!(body.contains("/tmp/kigi-goal-x/implementer"));
|
||||
assert!(
|
||||
!body.contains("{KIND_LENS}")
|
||||
&& !body.contains("{DETAILS_FILE}")
|
||||
@@ -4649,11 +4649,11 @@ mod tests {
|
||||
workspace_root,
|
||||
verifier_id,
|
||||
attempt,
|
||||
model_id: "grok-test",
|
||||
model_id: "kigi-test",
|
||||
goal_created_at: 0,
|
||||
plan_file: None,
|
||||
plan_baseline_file: None,
|
||||
implementer_scratch_dir: Path::new("/tmp/grok-goal-test/implementer"),
|
||||
implementer_scratch_dir: Path::new("/tmp/kigi-goal-test/implementer"),
|
||||
scratch_dir_ready: true,
|
||||
skeptic_count,
|
||||
max_runs: GOAL_CLASSIFIER_MAX_RUNS_DEFAULT,
|
||||
@@ -4747,7 +4747,7 @@ mod tests {
|
||||
// and this skeptic's own dir (derived from verifier_id) are both
|
||||
// present; neither placeholder leaks.
|
||||
assert!(
|
||||
p.contains("/tmp/grok-goal-test/implementer"),
|
||||
p.contains("/tmp/kigi-goal-test/implementer"),
|
||||
"implementer scratch dir missing in prompt",
|
||||
);
|
||||
assert!(
|
||||
@@ -5676,7 +5676,7 @@ mod tests {
|
||||
/// `runtime_overrides.model`.
|
||||
#[tokio::test]
|
||||
async fn cold_fallback_after_resume_failure_carries_pool0_model_on_request() {
|
||||
use kigi_tools::implementations::grok_build::task::types::{SubagentEvent, SubagentResult};
|
||||
use kigi_tools::implementations::kigi::task::types::{SubagentEvent, SubagentResult};
|
||||
use std::sync::Mutex as StdMutex;
|
||||
|
||||
// (model, resume_from) per spawn, in spawn order.
|
||||
@@ -6068,7 +6068,7 @@ mod tests {
|
||||
|
||||
#[tokio::test]
|
||||
async fn channel_spawner_blocks_until_subagent_result() {
|
||||
use kigi_tools::implementations::grok_build::task::types::{SubagentEvent, SubagentResult};
|
||||
use kigi_tools::implementations::kigi::task::types::{SubagentEvent, SubagentResult};
|
||||
|
||||
let (event_tx, mut event_rx) = tokio::sync::mpsc::unbounded_channel();
|
||||
let release = Arc::new(Notify::new());
|
||||
@@ -6328,7 +6328,7 @@ mod tests {
|
||||
verifier_id: &vid,
|
||||
attempt: 1,
|
||||
kind_lens: "",
|
||||
implementer_scratch: "/tmp/grok-goal-test/implementer",
|
||||
implementer_scratch: "/tmp/kigi-goal-test/implementer",
|
||||
scratch_dir_ready: true,
|
||||
prior_gaps: None,
|
||||
};
|
||||
|
||||
@@ -1421,7 +1421,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn evidence_packet_plan_path_with_spaces_and_unicode_round_trips() {
|
||||
let plan = "/tmp/grok sessions/✓ goal/plan.md";
|
||||
let plan = "/tmp/kigi sessions/✓ goal/plan.md";
|
||||
let packet = build_classifier_evidence_packet(
|
||||
"obj",
|
||||
ChangesRef::Unavailable,
|
||||
|
||||
@@ -104,7 +104,7 @@ impl GoalNotifySender {
|
||||
}
|
||||
if let Some(raw) = raw {
|
||||
let ext = agent_client_protocol::ExtNotification::new(
|
||||
"x.ai/session_notification",
|
||||
"kigi/session_notification",
|
||||
raw.into(),
|
||||
);
|
||||
self.gateway.forward_fire_and_forget(ext);
|
||||
@@ -432,7 +432,7 @@ mod tests {
|
||||
// — the wire field stays empty so the doc contract holds and we
|
||||
// don't ship a 1-element vec the pager would collapse anyway.
|
||||
let mut o = make_base_orchestration();
|
||||
o.live_tokens_by_model = vec![("grok-4".into(), 5_000)];
|
||||
o.live_tokens_by_model = vec![("kigi-4".into(), 5_000)];
|
||||
match build_goal_updated(&o, 0, 0) {
|
||||
XaiSessionUpdate::GoalUpdated {
|
||||
live_tokens_by_model,
|
||||
@@ -445,14 +445,14 @@ mod tests {
|
||||
}
|
||||
|
||||
// ≥2 distinct models are transmitted verbatim.
|
||||
o.live_tokens_by_model = vec![("grok-4".into(), 5_000), ("grok-3".into(), 3_000)];
|
||||
o.live_tokens_by_model = vec![("kigi-4".into(), 5_000), ("kigi-3".into(), 3_000)];
|
||||
match build_goal_updated(&o, 0, 0) {
|
||||
XaiSessionUpdate::GoalUpdated {
|
||||
live_tokens_by_model,
|
||||
..
|
||||
} => assert_eq!(
|
||||
live_tokens_by_model,
|
||||
vec![("grok-4".to_owned(), 5_000), ("grok-3".to_owned(), 3_000)]
|
||||
vec![("kigi-4".to_owned(), 5_000), ("kigi-3".to_owned(), 3_000)]
|
||||
),
|
||||
_ => panic!("expected GoalUpdated"),
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@ use std::sync::Arc;
|
||||
/// and the parent-side `describe_subagent_type` probe so the gated/probed
|
||||
/// toolset matches the spawned one.
|
||||
///
|
||||
/// [`SubagentRuntimeOverrides::harness_agent_type`]: kigi_tools::implementations::grok_build::task::types::SubagentRuntimeOverrides::harness_agent_type
|
||||
/// [`SubagentRuntimeOverrides::harness_agent_type`]: kigi_tools::implementations::kigi::task::types::SubagentRuntimeOverrides::harness_agent_type
|
||||
pub(crate) const GOAL_ROLE_SUBAGENT_TYPE: &str = "general-purpose";
|
||||
|
||||
/// Resolved per-role spawn override.
|
||||
@@ -36,7 +36,7 @@ pub(crate) const GOAL_ROLE_SUBAGENT_TYPE: &str = "general-purpose";
|
||||
pub(crate) struct RoleSpawnOverride {
|
||||
/// Resolved, post-auth, post-fail-open model id, or `None` to inherit.
|
||||
pub model: Option<String>,
|
||||
/// Resolved harness `agent_type` (e.g. `"grok-build-plan"`)
|
||||
/// Resolved harness `agent_type` (e.g. `"kigi-plan"`)
|
||||
/// whose `AgentDefinition` decides the spawned subagent's harness flavor
|
||||
/// (system prompt + toolset), applied REGARDLESS of the
|
||||
/// parent agent. `None` ⇒ inherit the session harness. NOT a subagent type —
|
||||
@@ -254,7 +254,7 @@ pub(crate) fn parse_terminal_response(text: &str) -> bool {
|
||||
|
||||
pub(crate) struct ChannelSpawner {
|
||||
pub(crate) event_tx: tokio::sync::mpsc::UnboundedSender<
|
||||
kigi_tools::implementations::grok_build::task::types::SubagentEvent,
|
||||
kigi_tools::implementations::kigi::task::types::SubagentEvent,
|
||||
>,
|
||||
pub(crate) parent_session_id: String,
|
||||
pub(crate) parent_prompt_id: Option<String>,
|
||||
@@ -299,7 +299,7 @@ impl ChannelSpawner {
|
||||
model: Option<String>,
|
||||
harness_agent_type: Option<String>,
|
||||
) -> Result<String, SpawnError> {
|
||||
use kigi_tools::implementations::grok_build::task::types::{
|
||||
use kigi_tools::implementations::kigi::task::types::{
|
||||
SubagentEvent, SubagentRequest, SubagentRuntimeOverrides,
|
||||
};
|
||||
let (result_tx, result_rx) = tokio::sync::oneshot::channel();
|
||||
@@ -499,7 +499,7 @@ mod tests {
|
||||
#[test]
|
||||
fn planner_template_default_render_preserves_wording_and_has_no_placeholders() {
|
||||
// Default/inherit render: placeholders resolve to the literal parent
|
||||
// (grok-build) tool names; guards against accidental wording drift.
|
||||
// (kigi) tool names; guards against accidental wording drift.
|
||||
let rendered = RoleToolNames::inherit_defaults().apply(GOAL_PLANNER_PROMPT_TEMPLATE);
|
||||
assert!(
|
||||
rendered.contains("with your\n`read_file`/`grep`/`list_dir` tools to clarify scope"),
|
||||
@@ -597,7 +597,7 @@ mod tests {
|
||||
|
||||
#[tokio::test]
|
||||
async fn channel_spawner_request_is_harness_internal() {
|
||||
use kigi_tools::implementations::grok_build::task::types::{SubagentEvent, SubagentResult};
|
||||
use kigi_tools::implementations::kigi::task::types::{SubagentEvent, SubagentResult};
|
||||
|
||||
let (tx, mut rx) = tokio::sync::mpsc::unbounded_channel();
|
||||
let spawner = ChannelSpawner {
|
||||
@@ -740,7 +740,7 @@ mod tests {
|
||||
context: "",
|
||||
plan_file: &plan_file,
|
||||
attempt: 1,
|
||||
model_id: "grok-test",
|
||||
model_id: "kigi-test",
|
||||
tool_names: &RoleToolNames::inherit_defaults(),
|
||||
inherit_tool_names: &RoleToolNames::inherit_defaults(),
|
||||
},
|
||||
@@ -769,7 +769,7 @@ mod tests {
|
||||
context: "",
|
||||
plan_file: &plan_file,
|
||||
attempt: 1,
|
||||
model_id: "grok-test",
|
||||
model_id: "kigi-test",
|
||||
tool_names: &RoleToolNames::inherit_defaults(),
|
||||
inherit_tool_names: &RoleToolNames::inherit_defaults(),
|
||||
},
|
||||
@@ -804,7 +804,7 @@ mod tests {
|
||||
context: "",
|
||||
plan_file: &plan_file,
|
||||
attempt: 1,
|
||||
model_id: "grok-test",
|
||||
model_id: "kigi-test",
|
||||
tool_names: &RoleToolNames::inherit_defaults(),
|
||||
inherit_tool_names: &RoleToolNames::inherit_defaults(),
|
||||
},
|
||||
@@ -846,7 +846,7 @@ mod tests {
|
||||
context: "",
|
||||
plan_file: &plan_file,
|
||||
attempt: 1,
|
||||
model_id: "grok-test",
|
||||
model_id: "kigi-test",
|
||||
tool_names: &RoleToolNames::inherit_defaults(),
|
||||
inherit_tool_names: &RoleToolNames::inherit_defaults(),
|
||||
},
|
||||
@@ -888,7 +888,7 @@ mod tests {
|
||||
context: "",
|
||||
plan_file: &plan_file,
|
||||
attempt: 1,
|
||||
model_id: "grok-test",
|
||||
model_id: "kigi-test",
|
||||
tool_names: &RoleToolNames::inherit_defaults(),
|
||||
inherit_tool_names: &RoleToolNames::inherit_defaults(),
|
||||
},
|
||||
@@ -927,7 +927,7 @@ mod tests {
|
||||
context: "",
|
||||
plan_file: &plan_file,
|
||||
attempt: 1,
|
||||
model_id: "grok-test",
|
||||
model_id: "kigi-test",
|
||||
tool_names: &RoleToolNames::inherit_defaults(),
|
||||
inherit_tool_names: &RoleToolNames::inherit_defaults(),
|
||||
},
|
||||
@@ -953,7 +953,7 @@ mod tests {
|
||||
context: "",
|
||||
plan_file: &plan_file,
|
||||
attempt: 1,
|
||||
model_id: "grok-test",
|
||||
model_id: "kigi-test",
|
||||
tool_names: &RoleToolNames::inherit_defaults(),
|
||||
inherit_tool_names: &RoleToolNames::inherit_defaults(),
|
||||
},
|
||||
@@ -990,7 +990,7 @@ mod tests {
|
||||
context: "prior conversation\n",
|
||||
plan_file: &plan_file,
|
||||
attempt: 1,
|
||||
model_id: "grok-test",
|
||||
model_id: "kigi-test",
|
||||
tool_names: &RoleToolNames::inherit_defaults(),
|
||||
inherit_tool_names: &RoleToolNames::inherit_defaults(),
|
||||
},
|
||||
@@ -1197,7 +1197,7 @@ mod tests {
|
||||
/// request's `harness_agent_type`, not the subagent_type.
|
||||
#[tokio::test]
|
||||
async fn channel_spawner_threads_harness_override_to_request() {
|
||||
use kigi_tools::implementations::grok_build::task::types::{SubagentEvent, SubagentResult};
|
||||
use kigi_tools::implementations::kigi::task::types::{SubagentEvent, SubagentResult};
|
||||
let (tx, mut rx) = tokio::sync::mpsc::unbounded_channel();
|
||||
let spawner = ChannelSpawner {
|
||||
event_tx: tx,
|
||||
@@ -1494,7 +1494,7 @@ mod tests {
|
||||
/// `ChannelSpawner` whose explicit spawn fails still returns `Planned`.
|
||||
#[tokio::test]
|
||||
async fn planner_retries_to_inherit_instead_of_failing_closed() {
|
||||
use kigi_tools::implementations::grok_build::task::types::{SubagentEvent, SubagentResult};
|
||||
use kigi_tools::implementations::kigi::task::types::{SubagentEvent, SubagentResult};
|
||||
let plan_file = tmp_plan_file("retry-failopen");
|
||||
let plan_for_coord = plan_file.clone();
|
||||
let (tx, mut rx) = tokio::sync::mpsc::unbounded_channel();
|
||||
@@ -1540,7 +1540,7 @@ mod tests {
|
||||
context: "",
|
||||
plan_file: &plan_file,
|
||||
attempt: 1,
|
||||
model_id: "grok-test",
|
||||
model_id: "kigi-test",
|
||||
tool_names: &RoleToolNames::inherit_defaults(),
|
||||
inherit_tool_names: &RoleToolNames::inherit_defaults(),
|
||||
},
|
||||
@@ -1561,7 +1561,7 @@ mod tests {
|
||||
/// fail-CLOSED cancellation semantics.
|
||||
#[tokio::test]
|
||||
async fn planner_cancellation_pauses_as_aborted_without_retry() {
|
||||
use kigi_tools::implementations::grok_build::task::types::{SubagentEvent, SubagentResult};
|
||||
use kigi_tools::implementations::kigi::task::types::{SubagentEvent, SubagentResult};
|
||||
use std::sync::atomic::{AtomicUsize, Ordering};
|
||||
let plan_file = tmp_plan_file("cancel-aborted");
|
||||
let spawns = Arc::new(AtomicUsize::new(0));
|
||||
@@ -1601,7 +1601,7 @@ mod tests {
|
||||
context: "",
|
||||
plan_file: &plan_file,
|
||||
attempt: 1,
|
||||
model_id: "grok-test",
|
||||
model_id: "kigi-test",
|
||||
tool_names: &RoleToolNames::inherit_defaults(),
|
||||
inherit_tool_names: &RoleToolNames::inherit_defaults(),
|
||||
},
|
||||
|
||||
@@ -40,7 +40,7 @@ pub(crate) struct RoleToolNames {
|
||||
/// `{SEARCH_TOOL}` — `ToolKind::Search` (grep maps here).
|
||||
pub search: String,
|
||||
/// `{WRITE_TOOL}` — `ToolKind::Write`, falling back to `ToolKind::Edit`
|
||||
/// (the default grok-build host's `search_replace` mutator) when `Write`
|
||||
/// (the default kigi host's `search_replace` mutator) when `Write`
|
||||
/// is absent from the describe summary.
|
||||
pub write: String,
|
||||
/// `{EXECUTE_TOOL}` — `ToolKind::Execute` (terminal/bash maps here).
|
||||
@@ -99,7 +99,7 @@ impl RoleToolNames {
|
||||
/// `{WRITE_TOOL}` falls back to the parent `Edit` tool name when the bridge
|
||||
/// has no `Write` — mirroring [`Self::from_summary`], so the inherit / retry
|
||||
/// render names the same mutator the subagent actually exposes (e.g.
|
||||
/// `search_replace` on the default grok-build host) instead of the literal
|
||||
/// `search_replace` on the default kigi host) instead of the literal
|
||||
/// `write` default. No `{TOOLSET_TOOLS}` enumeration on the inherit path.
|
||||
pub(crate) fn from_parent(
|
||||
read: Option<String>,
|
||||
@@ -127,14 +127,14 @@ impl RoleToolNames {
|
||||
/// summary (the `name_override`-aware client name per kind), and
|
||||
/// `{TOOLSET_TOOLS}` enumerates the toolset.
|
||||
pub(crate) fn from_summary(
|
||||
summary: &kigi_tools::implementations::grok_build::task::types::SubagentTypeSummary,
|
||||
summary: &kigi_tools::implementations::kigi::task::types::SubagentTypeSummary,
|
||||
) -> Self {
|
||||
let get = |kind: ToolKind| summary.tool_names.get(&kind).cloned();
|
||||
Self::from_parts(
|
||||
get(ToolKind::Read),
|
||||
get(ToolKind::ListDir),
|
||||
get(ToolKind::Search),
|
||||
// The default grok-build host's pre-spawn describe probe exposes only
|
||||
// The default kigi host's pre-spawn describe probe exposes only
|
||||
// `Edit` (`search_replace`) as the file mutator — the injection-only
|
||||
// `write`/`Write` tool is absent there. Without this fallback
|
||||
// `{WRITE_TOOL}` would render the literal `write` default instead of
|
||||
@@ -264,7 +264,7 @@ fn enumerate_toolset_tools(tool_names: &std::collections::HashMap<ToolKind, Stri
|
||||
#[cfg(test)]
|
||||
pub(crate) mod tests {
|
||||
use super::*;
|
||||
use kigi_tools::implementations::grok_build::task::types::SubagentTypeSummary;
|
||||
use kigi_tools::implementations::kigi::task::types::SubagentTypeSummary;
|
||||
|
||||
/// Build a `SubagentTypeSummary` from `(ToolKind, name)` pairs for the
|
||||
/// per-agent_type rendering tests. Shared with the planner / classifier /
|
||||
@@ -312,8 +312,8 @@ pub(crate) mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn from_summary_uses_grok_build_names() {
|
||||
// A grok-build toolset: client names match the literal defaults.
|
||||
fn from_summary_uses_kigi_names() {
|
||||
// A kigi toolset: client names match the literal defaults.
|
||||
let tn = RoleToolNames::from_summary(&summary_with(&[
|
||||
(ToolKind::Read, "read_file"),
|
||||
(ToolKind::ListDir, "list_dir"),
|
||||
@@ -379,7 +379,7 @@ pub(crate) mod tests {
|
||||
fn web_tools_fall_back_when_absent_from_the_toolset() {
|
||||
// A summary / parent bridge without WebSearch/WebFetch ⇒ both resolve to
|
||||
// the stock client names, so the planner prompt still names a real tool
|
||||
// on the default grok-build host (the stock `web_search`/`web_fetch`).
|
||||
// on the default kigi host (the stock `web_search`/`web_fetch`).
|
||||
let summary = RoleToolNames::from_summary(&summary_with(&[(ToolKind::Read, "rd")]));
|
||||
assert_eq!(summary.web_search, "web_search");
|
||||
assert_eq!(summary.web_fetch, "web_fetch");
|
||||
@@ -389,8 +389,8 @@ pub(crate) mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn from_summary_write_falls_back_to_edit_on_default_grok_build_host() {
|
||||
// Default grok-build host: the pre-spawn describe probe exposes only
|
||||
fn from_summary_write_falls_back_to_edit_on_default_kigi_host() {
|
||||
// Default kigi host: the pre-spawn describe probe exposes only
|
||||
// `Edit` (`search_replace`); `Write` is injection-only and absent. The
|
||||
// planner gate accepts this toolset, so `{WRITE_TOOL}` must name the
|
||||
// real mutator (`search_replace`), not the literal `write` default.
|
||||
@@ -415,7 +415,7 @@ pub(crate) mod tests {
|
||||
|
||||
#[test]
|
||||
fn from_parent_write_falls_back_to_edit_when_bridge_has_no_write() {
|
||||
// Default grok-build parent bridge: no `Write`, only `Edit`
|
||||
// Default kigi parent bridge: no `Write`, only `Edit`
|
||||
// (`search_replace`). The inherit / fail-open render must name the
|
||||
// real mutator, not the literal `write` default — matching
|
||||
// `from_summary`'s primary render.
|
||||
@@ -510,10 +510,10 @@ pub(crate) mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parent_and_summary_renders_agree_on_default_grok_build_mutator() {
|
||||
fn parent_and_summary_renders_agree_on_default_kigi_mutator() {
|
||||
// The explicit-pair `primary` (from_summary) and inherit/fail-open
|
||||
// `fallback` (from_parent) renders must name the SAME mutator on the
|
||||
// default grok-build host (Edit-only toolset), so a fail-open retry
|
||||
// default kigi host (Edit-only toolset), so a fail-open retry
|
||||
// can never disagree with the first attempt's `{WRITE_TOOL}`.
|
||||
let primary = RoleToolNames::from_summary(&summary_with(&[
|
||||
(ToolKind::Read, "read_file"),
|
||||
|
||||
@@ -107,7 +107,7 @@ pub(crate) fn strategist_should_fire(consecutive: u32, last_fired: u32, every: u
|
||||
|
||||
pub(crate) struct ChannelSpawner {
|
||||
pub(crate) event_tx: tokio::sync::mpsc::UnboundedSender<
|
||||
kigi_tools::implementations::grok_build::task::types::SubagentEvent,
|
||||
kigi_tools::implementations::kigi::task::types::SubagentEvent,
|
||||
>,
|
||||
pub(crate) parent_session_id: String,
|
||||
pub(crate) parent_prompt_id: Option<String>,
|
||||
@@ -152,7 +152,7 @@ impl ChannelSpawner {
|
||||
model: Option<String>,
|
||||
harness_agent_type: Option<String>,
|
||||
) -> Result<String, SpawnError> {
|
||||
use kigi_tools::implementations::grok_build::task::types::{
|
||||
use kigi_tools::implementations::kigi::task::types::{
|
||||
SubagentEvent, SubagentRequest, SubagentRuntimeOverrides,
|
||||
};
|
||||
let (result_tx, result_rx) = tokio::sync::oneshot::channel();
|
||||
@@ -541,7 +541,7 @@ mod tests {
|
||||
|
||||
#[tokio::test]
|
||||
async fn channel_spawner_request_is_harness_internal() {
|
||||
use kigi_tools::implementations::grok_build::task::types::{SubagentEvent, SubagentResult};
|
||||
use kigi_tools::implementations::kigi::task::types::{SubagentEvent, SubagentResult};
|
||||
|
||||
let (tx, mut rx) = tokio::sync::mpsc::unbounded_channel();
|
||||
let spawner = ChannelSpawner {
|
||||
@@ -580,7 +580,7 @@ mod tests {
|
||||
/// request's `harness_agent_type`, not the subagent_type.
|
||||
#[tokio::test]
|
||||
async fn channel_spawner_threads_harness_override_to_request() {
|
||||
use kigi_tools::implementations::grok_build::task::types::{SubagentEvent, SubagentResult};
|
||||
use kigi_tools::implementations::kigi::task::types::{SubagentEvent, SubagentResult};
|
||||
let (tx, mut rx) = tokio::sync::mpsc::unbounded_channel();
|
||||
let spawner = ChannelSpawner {
|
||||
event_tx: tx,
|
||||
@@ -809,7 +809,7 @@ mod tests {
|
||||
attempt: 3,
|
||||
consecutive_failures: 5,
|
||||
every: 2,
|
||||
model_id: "grok-test",
|
||||
model_id: "kigi-test",
|
||||
tool_names: default_tool_names(),
|
||||
inherit_tool_names: default_tool_names(),
|
||||
}
|
||||
@@ -1210,7 +1210,7 @@ mod tests {
|
||||
use crate::session::goal_role_tools::tests::assert_no_tool_placeholders;
|
||||
|
||||
/// Default/inherit render: the tool placeholders resolve to the literal
|
||||
/// parent (grok-build) names, with no placeholder left behind. Guards
|
||||
/// parent (kigi) names, with no placeholder left behind. Guards
|
||||
/// against accidental wording drift in the strategist template.
|
||||
#[test]
|
||||
fn strategist_template_default_render_preserves_wording() {
|
||||
@@ -1229,7 +1229,7 @@ mod tests {
|
||||
/// explicit `from_summary` path leaves no tool placeholder unresolved.
|
||||
#[test]
|
||||
fn strategist_template_renders_per_agent_type_names() {
|
||||
use kigi_tools::implementations::grok_build::task::types::SubagentTypeSummary;
|
||||
use kigi_tools::implementations::kigi::task::types::SubagentTypeSummary;
|
||||
let mut tool_names = std::collections::HashMap::new();
|
||||
tool_names.insert(
|
||||
kigi_tools::types::tool::ToolKind::Read,
|
||||
|
||||
@@ -82,7 +82,7 @@ pub(crate) trait GoalSummarizerSpawner: Send + Sync {
|
||||
|
||||
pub(crate) struct ChannelSpawner {
|
||||
pub(crate) event_tx: tokio::sync::mpsc::UnboundedSender<
|
||||
kigi_tools::implementations::grok_build::task::types::SubagentEvent,
|
||||
kigi_tools::implementations::kigi::task::types::SubagentEvent,
|
||||
>,
|
||||
pub(crate) parent_session_id: String,
|
||||
pub(crate) parent_prompt_id: Option<String>,
|
||||
@@ -129,7 +129,7 @@ impl ChannelSpawner {
|
||||
harness_agent_type: Option<String>,
|
||||
) -> Result<String, SpawnError> {
|
||||
use kigi_tool_types::SubagentCapabilityMode;
|
||||
use kigi_tools::implementations::grok_build::task::types::{
|
||||
use kigi_tools::implementations::kigi::task::types::{
|
||||
SubagentEvent, SubagentRequest, SubagentRuntimeOverrides,
|
||||
};
|
||||
let (result_tx, result_rx) = tokio::sync::oneshot::channel();
|
||||
@@ -342,7 +342,7 @@ mod tests {
|
||||
.replace("{DETAILS_FILE}", &details_str)
|
||||
.replace("{SESSION_TRACES_DIR}", &traces_dir_str);
|
||||
let rendered = RoleToolNames::inherit_defaults().apply(&with_paths);
|
||||
// §7 tool placeholders resolve to the default grok-build names.
|
||||
// §7 tool placeholders resolve to the default kigi names.
|
||||
assert!(rendered.contains("read_file"));
|
||||
assert!(rendered.contains("grep"));
|
||||
assert!(rendered.contains("list_dir"));
|
||||
@@ -422,7 +422,7 @@ mod tests {
|
||||
details_file: None,
|
||||
session_traces_dir: plan.parent().unwrap(),
|
||||
attempt: 2,
|
||||
model_id: "grok-test",
|
||||
model_id: "kigi-test",
|
||||
tool_names,
|
||||
}
|
||||
}
|
||||
@@ -604,7 +604,7 @@ mod tests {
|
||||
#[tokio::test]
|
||||
async fn channel_spawner_request_is_harness_internal_and_read_only() {
|
||||
use kigi_tool_types::SubagentCapabilityMode;
|
||||
use kigi_tools::implementations::grok_build::task::types::{SubagentEvent, SubagentResult};
|
||||
use kigi_tools::implementations::kigi::task::types::{SubagentEvent, SubagentResult};
|
||||
|
||||
let (tx, mut rx) = tokio::sync::mpsc::unbounded_channel();
|
||||
let spawner = ChannelSpawner {
|
||||
|
||||
@@ -252,7 +252,7 @@ impl GoalHistoryEntry {
|
||||
// GoalOrchestration (full persisted state)
|
||||
|
||||
/// Generate a short opaque identifier used to scope the per-goal
|
||||
/// scratch root (`<temp_dir>/grok-goal-<id>`) and the verifier
|
||||
/// scratch root (`<temp_dir>/kigi-goal-<id>`) and the verifier
|
||||
/// verdict/details files inside it.
|
||||
///
|
||||
/// The id is a 12-char prefix of a UUIDv4 simple form — ~48 bits of
|
||||
@@ -267,13 +267,13 @@ pub(crate) fn generate_verifier_id() -> String {
|
||||
s
|
||||
}
|
||||
|
||||
/// Private per-goal scratch root: `<temp_dir>/grok-goal-<verifier_id>`.
|
||||
/// Private per-goal scratch root: `<temp_dir>/kigi-goal-<verifier_id>`.
|
||||
///
|
||||
/// Rooted at [`std::env::temp_dir`] (respects `TMPDIR`) and namespaced by the
|
||||
/// goal's `verifier_id`, so concurrent goals never collide and cleanup of one
|
||||
/// never touches another. Removed wholesale on every terminal goal transition.
|
||||
pub(crate) fn goal_scratch_root(verifier_id: &str) -> PathBuf {
|
||||
std::env::temp_dir().join(format!("grok-goal-{verifier_id}"))
|
||||
std::env::temp_dir().join(format!("kigi-goal-{verifier_id}"))
|
||||
}
|
||||
|
||||
/// Create (or verify) the goal's scratch root, locked to the owner
|
||||
@@ -1646,7 +1646,7 @@ mod tests {
|
||||
/// fresh values on every call. The fixed length is part of the
|
||||
/// public contract — verifier file paths embed it verbatim, and
|
||||
/// drift here would silently invalidate the documented
|
||||
/// `grok-goal-<12 hex chars>` scratch-root format (and the
|
||||
/// `kigi-goal-<12 hex chars>` scratch-root format (and the
|
||||
/// 12-hex restore validation in `from_snapshot`).
|
||||
#[test]
|
||||
fn generate_verifier_id_is_short_hex_and_unique() {
|
||||
@@ -2539,7 +2539,7 @@ mod tests {
|
||||
activate_tracker(&mut t);
|
||||
t.update_live_progress(
|
||||
100,
|
||||
vec![("grok-4".to_owned(), 60), ("grok-3".to_owned(), 40)],
|
||||
vec![("kigi-4".to_owned(), 60), ("kigi-3".to_owned(), 40)],
|
||||
200_000,
|
||||
50,
|
||||
3,
|
||||
@@ -3081,11 +3081,11 @@ mod tests {
|
||||
let mut o = make_base_orchestration();
|
||||
o.skeptic_model_assignment = vec![
|
||||
crate::util::config::GoalRoleModel {
|
||||
model: "grok-4".to_string(),
|
||||
model: "kigi-4".to_string(),
|
||||
agent_type: "general-purpose".to_string(),
|
||||
},
|
||||
crate::util::config::GoalRoleModel {
|
||||
model: "grok-4.5".to_string(),
|
||||
model: "kigi-4.5".to_string(),
|
||||
agent_type: "cursor".to_string(),
|
||||
},
|
||||
];
|
||||
@@ -3132,7 +3132,7 @@ mod tests {
|
||||
activate_tracker(&mut t);
|
||||
t.snapshot_mut().unwrap().skeptic_model_assignment =
|
||||
vec![crate::util::config::GoalRoleModel {
|
||||
model: "grok-4".to_string(),
|
||||
model: "kigi-4".to_string(),
|
||||
agent_type: "general-purpose".to_string(),
|
||||
}];
|
||||
let applied = match ending {
|
||||
@@ -3170,22 +3170,22 @@ mod tests {
|
||||
}
|
||||
|
||||
/// The scratch path helpers derive the pinned layout from
|
||||
/// `temp_dir()` + `verifier_id`: a `grok-goal-<vid>` root with an
|
||||
/// `temp_dir()` + `verifier_id`: a `kigi-goal-<vid>` root with an
|
||||
/// `implementer/` subdir and per-index `skeptic-<idx>/` subdirs.
|
||||
#[test]
|
||||
fn scratch_path_helpers_derive_pinned_layout() {
|
||||
let root = goal_scratch_root("vid123");
|
||||
assert_eq!(root, std::env::temp_dir().join("grok-goal-vid123"));
|
||||
assert_eq!(root, std::env::temp_dir().join("kigi-goal-vid123"));
|
||||
assert_eq!(
|
||||
implementer_scratch_dir("vid123"),
|
||||
std::env::temp_dir()
|
||||
.join("grok-goal-vid123")
|
||||
.join("kigi-goal-vid123")
|
||||
.join("implementer"),
|
||||
);
|
||||
assert_eq!(
|
||||
skeptic_scratch_dir("vid123", 2),
|
||||
std::env::temp_dir()
|
||||
.join("grok-goal-vid123")
|
||||
.join("kigi-goal-vid123")
|
||||
.join("skeptic-2"),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ use std::collections::{HashMap, HashSet};
|
||||
use tokio::sync::{mpsc, oneshot};
|
||||
/// Coarse lifecycle state of a session as known to the leader/agent.
|
||||
///
|
||||
/// A grok session has no
|
||||
/// A kigi session has no
|
||||
/// terminal status field on its own — it is a resumable log on disk — so
|
||||
/// "liveness" is *residency + turn-state*, not a pid. The agent's join-handle
|
||||
/// supervisor tracks this per session so a panicked actor can be reaped
|
||||
@@ -103,7 +103,7 @@ pub struct SessionHandle {
|
||||
/// client behaviors like yolo broadcasts.
|
||||
pub origin_client: Option<crate::http::OriginClientInfo>,
|
||||
/// Whether the client that created this session advertised
|
||||
/// `x.ai/codeNavigation.enabled`. Stored per-session so that in leader
|
||||
/// `kigi/codeNavigation.enabled`. Stored per-session so that in leader
|
||||
/// mode a later `initialize()` from a different client cannot retroactively
|
||||
/// change code-nav eligibility for already-running sessions.
|
||||
pub code_nav_enabled: bool,
|
||||
@@ -112,13 +112,13 @@ pub struct SessionHandle {
|
||||
/// env gate). Stored per-session so subagents inherit it at spawn.
|
||||
pub ask_user_question_enabled: bool,
|
||||
/// Plan mode tracker — shared with the session actor via Arc.
|
||||
/// Exposed so the `x.ai/toggle_plan_mode` handler can toggle plan mode
|
||||
/// Exposed so the `kigi/toggle_plan_mode` handler can toggle plan mode
|
||||
/// without going through the session command channel.
|
||||
pub plan_mode: std::sync::Arc<parking_lot::Mutex<crate::session::plan_mode::PlanModeTracker>>,
|
||||
/// Debug flag: when set to `true`, the next turn unconditionally triggers
|
||||
/// auto-compaction regardless of context window usage. Consumed (reset to
|
||||
/// `false`) atomically on use via `compare_exchange`.
|
||||
/// Set via `x.ai/debug/arm_auto_compact`.
|
||||
/// Set via `kigi/debug/arm_auto_compact`.
|
||||
pub force_compact: std::sync::Arc<std::sync::atomic::AtomicBool>,
|
||||
pub permission_handle: kigi_workspace::permission::PermissionHandle,
|
||||
/// The parent SessionActor's live `Auth401AttributionCallback`
|
||||
@@ -147,7 +147,7 @@ pub struct SessionHandle {
|
||||
/// Scheduler handle for this session. Subagents inherit the parent's
|
||||
/// handle so scheduled tasks survive the subagent's exit.
|
||||
pub scheduler_handle:
|
||||
Option<kigi_tools::implementations::grok_build::scheduler::types::SchedulerHandle>,
|
||||
Option<kigi_tools::implementations::kigi::scheduler::types::SchedulerHandle>,
|
||||
}
|
||||
impl SessionHandle {
|
||||
/// Last assistant `model_id` / `model_fingerprint` in conversation (global, not turn-scoped).
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
//!
|
||||
//! The three **common** active-agent sections (background tasks, TODO list,
|
||||
//! running subagents) are formatted by
|
||||
//! [`kigi_compaction::reminder`] so grok-chat and grok-build stay in lockstep.
|
||||
//! [`kigi_compaction::reminder`] so kigi-chat and kigi stay in lockstep.
|
||||
//! Harness-only sections (edited files, AGENTS.md, skills, MCP, memory) stay here.
|
||||
|
||||
use std::path::PathBuf;
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
//! grok-build's L5 wiring onto the shared full-replace engine
|
||||
//! kigi's L5 wiring onto the shared full-replace engine
|
||||
//! (`kigi_compaction::code_compaction`).
|
||||
//!
|
||||
//! The shared engine drives the sample → retry → degenerate/failure
|
||||
//! classification loop via [`sample_full_replace_summary`](kigi_compaction::sample_full_replace_summary);
|
||||
//! this module adapts grok-build's transport and telemetry to its two seams:
|
||||
//! this module adapts kigi's transport and telemetry to its two seams:
|
||||
//!
|
||||
//! - [`ShellCompactionSampler`] wraps
|
||||
//! [`generate_session_compact`](crate::session::helpers::session_compact::generate_session_compact)
|
||||
@@ -42,7 +42,7 @@ use crate::session::helpers::session_compact::{
|
||||
};
|
||||
|
||||
/// Wraps `generate_session_compact` as the shared engine's
|
||||
/// [`CompactionSampler`] for grok-build's full-replace pass.
|
||||
/// [`CompactionSampler`] for kigi's full-replace pass.
|
||||
///
|
||||
/// Holds the per-call request context the seam does not carry (tools, client,
|
||||
/// session, config) and stashes the last successful [`CompactOutput`] so the
|
||||
@@ -51,8 +51,8 @@ use crate::session::helpers::session_compact::{
|
||||
///
|
||||
/// The summarization prompt is selected here by `use_short_prompt` (the
|
||||
/// short-prompt harness uses the short self-summarization prompt; everyone
|
||||
/// else the structured grok-build prompt), so the shared `CompactionPrompt`
|
||||
/// the engine passes is ignored — the engine builds the grok-build prompt,
|
||||
/// else the structured kigi prompt), so the shared `CompactionPrompt`
|
||||
/// the engine passes is ignored — the engine builds the kigi prompt,
|
||||
/// which equals what `build_compaction_chat_history(.., false)` appends, and
|
||||
/// the short-prompt harness needs its own variant the engine can't produce.
|
||||
pub(crate) struct ShellCompactionSampler {
|
||||
@@ -118,7 +118,7 @@ impl CompactionSampler for ShellCompactionSampler {
|
||||
_timeout: Duration,
|
||||
) -> Result<LlmCompactionOutput, CompactionSampleError> {
|
||||
// Append the harness-selected summarization prompt as the final user
|
||||
// message (compat short vs structured grok-build), ignoring the shared
|
||||
// message (compat short vs structured kigi), ignoring the shared
|
||||
// engine's `_prompt` (see the struct doc).
|
||||
let chat_history = build_compaction_chat_history(
|
||||
turns.to_vec(),
|
||||
@@ -151,7 +151,7 @@ impl CompactionSampler for ShellCompactionSampler {
|
||||
}
|
||||
}
|
||||
|
||||
/// Map grok-build's [`CompactFailure`] onto the shared engine's
|
||||
/// Map kigi's [`CompactFailure`] onto the shared engine's
|
||||
/// [`CompactionSampleError`] so the shared retry loop classifies it the same
|
||||
/// way the in-shell loop did:
|
||||
///
|
||||
@@ -207,7 +207,7 @@ struct ObserverState {
|
||||
last_error_msg: Option<String>,
|
||||
}
|
||||
|
||||
/// [`FullReplaceObserver`] that reproduces grok-build's per-attempt telemetry:
|
||||
/// [`FullReplaceObserver`] that reproduces kigi's per-attempt telemetry:
|
||||
/// `CompactionAttempt` rows, rejection counters, the `CompactionRetryDegraded`
|
||||
/// event, and the warn/error tracing — without the shared engine depending on
|
||||
/// a telemetry backend.
|
||||
|
||||
@@ -328,7 +328,7 @@ mod tests {
|
||||
.as_deref(),
|
||||
Some(default_suggest_model())
|
||||
);
|
||||
// OAuth catalogs exclude grok-build-0.1 → skip the request entirely,
|
||||
// OAuth catalogs exclude kigi-0.1 → skip the request entirely,
|
||||
// never a doomed call (and never the session model).
|
||||
assert_eq!(
|
||||
effective_suggest_model(&Pin::Unpinned, None, |_| false),
|
||||
|
||||
@@ -61,7 +61,10 @@ pub fn find_latest_compaction_checkpoint(
|
||||
continue;
|
||||
};
|
||||
|
||||
if env.method != Some("_x.ai/session/update") {
|
||||
if !env
|
||||
.method
|
||||
.is_some_and(crate::session::storage::is_ext_session_update_method)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@ use reqwest::StatusCode;
|
||||
/// `<summary_request>` only -- the surrounding `<user_query>` is implicit
|
||||
/// because we push this as a `ConversationItem::user`.
|
||||
///
|
||||
/// All other agents (grok-build, etc.) continue to use the detailed
|
||||
/// All other agents (kigi, etc.) continue to use the detailed
|
||||
/// structured prompt built inline in `generate_session_compact`.
|
||||
pub(crate) const SELF_SUMMARIZATION_PROMPT: &str = r#"<summary_request>
|
||||
Please summarize the conversation so far. This summary (everything after your
|
||||
@@ -376,10 +376,10 @@ pub(crate) async fn generate_session_compact(
|
||||
.with_tool_choice(ToolChoice::none());
|
||||
}
|
||||
let sid = session_id.to_string();
|
||||
message.x_grok_conv_id = Some(sid.clone());
|
||||
message.x_grok_req_id = Some(format!("xai-compact-{}", uuid::Uuid::new_v4()));
|
||||
message.x_grok_session_id = Some(sid);
|
||||
message.x_grok_agent_id = Some(crate::util::agent_id::agent_id());
|
||||
message.x_kigi_conv_id = Some(sid.clone());
|
||||
message.x_kigi_req_id = Some(format!("xai-compact-{}", uuid::Uuid::new_v4()));
|
||||
message.x_kigi_session_id = Some(sid);
|
||||
message.x_kigi_agent_id = Some(crate::util::agent_id::agent_id());
|
||||
tracing::info!(
|
||||
compact_model = % sampling_config.model, num_messages = num_messages,
|
||||
"Sending compact request (streaming)"
|
||||
@@ -471,10 +471,10 @@ pub(crate) async fn generate_session_compact(
|
||||
hosted_tools,
|
||||
model: Some(sampling_config.model.to_owned()),
|
||||
temperature: Some(1.0),
|
||||
x_grok_conv_id: Some(session_id.to_string()),
|
||||
x_grok_req_id: Some(format!("xai-compact-{}", uuid::Uuid::new_v4())),
|
||||
x_grok_session_id: Some(session_id.to_string()),
|
||||
x_grok_agent_id: Some(crate::util::agent_id::agent_id()),
|
||||
x_kigi_conv_id: Some(session_id.to_string()),
|
||||
x_kigi_req_id: Some(format!("xai-compact-{}", uuid::Uuid::new_v4())),
|
||||
x_kigi_session_id: Some(session_id.to_string()),
|
||||
x_kigi_agent_id: Some(crate::util::agent_id::agent_id()),
|
||||
..Default::default()
|
||||
};
|
||||
let stream_result = client.conversation_stream_responses(request).await;
|
||||
@@ -593,10 +593,10 @@ pub(crate) async fn generate_session_compact(
|
||||
hosted_tools,
|
||||
model: Some(sampling_config.model.to_owned()),
|
||||
temperature: Some(1.0),
|
||||
x_grok_conv_id: Some(session_id.to_string()),
|
||||
x_grok_req_id: Some(format!("xai-compact-{}", uuid::Uuid::new_v4())),
|
||||
x_grok_session_id: Some(session_id.to_string()),
|
||||
x_grok_agent_id: Some(crate::util::agent_id::agent_id()),
|
||||
x_kigi_conv_id: Some(session_id.to_string()),
|
||||
x_kigi_req_id: Some(format!("xai-compact-{}", uuid::Uuid::new_v4())),
|
||||
x_kigi_session_id: Some(session_id.to_string()),
|
||||
x_kigi_agent_id: Some(crate::util::agent_id::agent_id()),
|
||||
..Default::default()
|
||||
};
|
||||
let stream_result = client.conversation_stream_messages(request).await;
|
||||
@@ -1127,13 +1127,13 @@ mod compacted_history_shape_tests {
|
||||
);
|
||||
assert_eq!(compacted.len(), 5);
|
||||
}
|
||||
/// Regression guard: grok-build must DROP the working
|
||||
/// tail post-compaction. A prior change routed grok-build to keep `recent_messages`,
|
||||
/// Regression guard: kigi must DROP the working
|
||||
/// tail post-compaction. A prior change routed kigi to keep `recent_messages`,
|
||||
/// which survive only as `Tool call omitted...` stubs (dead tokens). Mirrors
|
||||
/// `summary_before_recent_compaction_with_no_user_query_yields_three_messages` for grok-build
|
||||
/// `summary_before_recent_compaction_with_no_user_query_yields_three_messages` for kigi
|
||||
/// (`summary_before_recent = false`).
|
||||
#[tokio::test]
|
||||
async fn grok_build_compaction_drops_working_tail_regression_206460() {
|
||||
async fn kigi_compaction_drops_working_tail_regression_206460() {
|
||||
let conversation = vec![
|
||||
ConversationItem::system("You are a helpful assistant."),
|
||||
ConversationItem::user(
|
||||
@@ -1163,7 +1163,7 @@ mod compacted_history_shape_tests {
|
||||
let dropped = full.for_compaction();
|
||||
assert!(
|
||||
dropped.recent_messages.is_empty(),
|
||||
"grok-build must drop recent_messages post-compaction",
|
||||
"kigi must drop recent_messages post-compaction",
|
||||
);
|
||||
let compacted = build_compacted_history(
|
||||
"You are a helpful assistant.",
|
||||
@@ -1177,7 +1177,7 @@ mod compacted_history_shape_tests {
|
||||
.iter()
|
||||
.any(|i| matches!(i, ConversationItem::ToolResult(_))
|
||||
|| i.text_content() == "Tool call omitted..."),
|
||||
"no tail (ToolResult or stub) may leak into the grok-build compacted history",
|
||||
"no tail (ToolResult or stub) may leak into the kigi compacted history",
|
||||
);
|
||||
}
|
||||
/// Verify that the auto-continue prompt (sent after compaction) is also
|
||||
|
||||
@@ -62,7 +62,7 @@ pub(crate) fn recap_instruction(tag: &str) -> String {
|
||||
/// 1. Optionally strips reasoning/thinking blocks (`strip_reasoning`). This is
|
||||
/// only needed on the Anthropic Messages backend, which rejects thinking
|
||||
/// blocks sent without a top-level `thinking` config. Every other backend
|
||||
/// (grok/SGLang via ChatCompletions/Responses) keeps reasoning VERBATIM so
|
||||
/// (kigi/SGLang via ChatCompletions/Responses) keeps reasoning VERBATIM so
|
||||
/// the conversation prefix is byte-identical to the last turn and the
|
||||
/// provider's prefix KV cache stays warm — which is the whole reason we
|
||||
/// append the instruction after the prefix. Mirrors compaction's
|
||||
@@ -89,7 +89,7 @@ pub(crate) fn build_recap_items(
|
||||
}
|
||||
|
||||
/// Cap on the effective context window for recap budgeting: the verified
|
||||
/// `max_prompt_length` for current `grok-build` / `grok-4.5` product backends
|
||||
/// `max_prompt_length` for current `kigi` / `kigi-4.5` product backends
|
||||
/// (`500000`). Applied via `min(window, CAP)`, so a smaller real window still
|
||||
/// wins (e.g. a 256k legacy model or a debug override).
|
||||
const RECAP_CONTEXT_WINDOW_CAP: u64 = 500_000;
|
||||
@@ -110,10 +110,10 @@ const RECAP_BUDGET_HEADROOM_TOKENS: u64 = 4_000;
|
||||
/// `ic_400_prompt_too_long` on long sessions. Not an absolute guarantee — a
|
||||
/// degenerate tiny window, an oversized retained `System` prefix, or estimator
|
||||
/// optimism can still exceed the real limit (the 85% + headroom + 500k cap make
|
||||
/// that unlikely for normal grok-build sessions).
|
||||
/// that unlikely for normal kigi sessions).
|
||||
///
|
||||
/// * Fast path — if the whole snapshot already fits, returns
|
||||
/// `build_recap_items(...)` verbatim (keeps the grok prefix KV cache warm;
|
||||
/// `build_recap_items(...)` verbatim (keeps the kigi prefix KV cache warm;
|
||||
/// honors the caller's `strip_reasoning`).
|
||||
/// * Over budget — strip reasoning (the prefix cache is lost once we trim),
|
||||
/// normalize the trailing boundary ([`pop_trailing_tool_run`]),
|
||||
@@ -136,7 +136,7 @@ pub(crate) fn budget_recap_items(
|
||||
let snapshot_budget = prompt_budget.saturating_sub(estimate_item_tokens(&instruction));
|
||||
|
||||
// Un-stripped estimate is a safe upper bound (stripping only shrinks); the
|
||||
// verbatim path keeps the grok prefix cache warm.
|
||||
// verbatim path keeps the kigi prefix cache warm.
|
||||
let pre_tokens = estimate_conversation_tokens(&conversation);
|
||||
if pre_tokens <= snapshot_budget {
|
||||
return build_recap_items(conversation, tag, strip_reasoning);
|
||||
@@ -690,13 +690,13 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn budget_over_budget_strips_reasoning_even_on_grok() {
|
||||
fn budget_over_budget_strips_reasoning_even_on_kigi() {
|
||||
let conv = vec![
|
||||
mk_reasoning("r1"),
|
||||
ConversationItem::assistant("did stuff"),
|
||||
ConversationItem::user("z".repeat(40_000)),
|
||||
];
|
||||
// grok backend => strip_reasoning=false, but the over-budget branch must
|
||||
// kigi backend => strip_reasoning=false, but the over-budget branch must
|
||||
// strip reasoning anyway (the prefix cache is already lost once trimmed).
|
||||
let out = budget_recap_items(conv, "system-reminder", false, 8_000);
|
||||
assert!(
|
||||
@@ -707,19 +707,19 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn budget_fast_path_keeps_reasoning_on_grok() {
|
||||
fn budget_fast_path_keeps_reasoning_on_kigi() {
|
||||
let conv = vec![
|
||||
mk_reasoning("r1"),
|
||||
ConversationItem::assistant("did stuff"),
|
||||
ConversationItem::user("small"),
|
||||
];
|
||||
// Fits under a large window on grok (strip_reasoning=false) => verbatim,
|
||||
// Fits under a large window on kigi (strip_reasoning=false) => verbatim,
|
||||
// reasoning kept so the prefix KV cache stays warm.
|
||||
let out = budget_recap_items(conv, "system-reminder", false, 256_000);
|
||||
assert!(
|
||||
out.iter()
|
||||
.any(|i| matches!(i, ConversationItem::Reasoning(_))),
|
||||
"fits path on grok must keep reasoning verbatim"
|
||||
"fits path on kigi must keep reasoning verbatim"
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
//!
|
||||
//! That harness uses a separate vision endpoint to describe images
|
||||
//! rather than passing them inline. When a user message contains image
|
||||
//! content blocks, the session calls a vision-capable Grok model
|
||||
//! content blocks, the session calls a vision-capable Kigi model
|
||||
//! (defaults to the agent's current model unless explicitly overridden)
|
||||
//! to produce text descriptions that are injected into the turn. Per-image
|
||||
//! requests are deduplicated via [`ImageDescribeCache`] (same bytes +
|
||||
|
||||
@@ -912,11 +912,11 @@ mod tests {
|
||||
#[test]
|
||||
fn re_encode_fallback_notice_picks_tag_per_harness() {
|
||||
let notes = vec!["Image 1 could not be re-encoded under the cap.".to_string()];
|
||||
let grok = render_re_encode_fallback_notice(¬es, false);
|
||||
assert!(grok.contains("<system-reminder>"));
|
||||
assert!(grok.contains("</system-reminder>"));
|
||||
assert!(!grok.contains("<system_reminder>"));
|
||||
assert!(grok.contains("<image_re_encode_fallback>"));
|
||||
let kigi = render_re_encode_fallback_notice(¬es, false);
|
||||
assert!(kigi.contains("<system-reminder>"));
|
||||
assert!(kigi.contains("</system-reminder>"));
|
||||
assert!(!kigi.contains("<system_reminder>"));
|
||||
assert!(kigi.contains("<image_re_encode_fallback>"));
|
||||
}
|
||||
#[test]
|
||||
fn display_format() {
|
||||
@@ -1316,12 +1316,12 @@ mod tests {
|
||||
#[test]
|
||||
fn image_dropped_notice_picks_tag_per_harness() {
|
||||
let notes = vec!["Image 5 was dropped before send: corrupt".to_string()];
|
||||
let grok = render_image_dropped_notice(¬es, false);
|
||||
assert!(grok.contains("<system-reminder>"));
|
||||
assert!(grok.contains("</system-reminder>"));
|
||||
assert!(!grok.contains("<system_reminder>"));
|
||||
assert!(grok.contains("<image_dropped_notice>"));
|
||||
assert!(grok.contains("Image 5"));
|
||||
let kigi = render_image_dropped_notice(¬es, false);
|
||||
assert!(kigi.contains("<system-reminder>"));
|
||||
assert!(kigi.contains("</system-reminder>"));
|
||||
assert!(!kigi.contains("<system_reminder>"));
|
||||
assert!(kigi.contains("<image_dropped_notice>"));
|
||||
assert!(kigi.contains("Image 5"));
|
||||
assert_eq!(render_image_dropped_notice(&[], false), "");
|
||||
}
|
||||
/// Large flat-color images compress better as PNG than JPEG; the
|
||||
|
||||
@@ -506,7 +506,7 @@ mod tests {
|
||||
let tagged = apply_mcp_server_policy(
|
||||
vec![
|
||||
acp::McpServer::Http(
|
||||
acp::McpServerHttp::new("grok_com_slack", "https://mcp.slack.com/sse")
|
||||
acp::McpServerHttp::new("kigi_com_slack", "https://mcp.slack.com/sse")
|
||||
.headers(vec![]),
|
||||
),
|
||||
// Substring-only match must not be denied.
|
||||
@@ -521,7 +521,7 @@ mod tests {
|
||||
|
||||
let slack = tagged
|
||||
.iter()
|
||||
.find(|s| mcp_server_name(&s.server) == "grok_com_slack")
|
||||
.find(|s| mcp_server_name(&s.server) == "kigi_com_slack")
|
||||
.expect("managed server present in policy output");
|
||||
assert!(
|
||||
matches!(
|
||||
@@ -762,7 +762,7 @@ enabled = false
|
||||
root: plugin_root.clone(),
|
||||
canonical_root: plugin_root.clone(),
|
||||
scope: PluginScope::User,
|
||||
origin: kigi_agent::plugins::PluginOrigin::UserGrok,
|
||||
origin: kigi_agent::plugins::PluginOrigin::UserKigi,
|
||||
trusted: true,
|
||||
skill_dirs: vec![],
|
||||
command_dirs: vec![],
|
||||
@@ -830,7 +830,7 @@ enabled = false
|
||||
root: plugin_root.clone(),
|
||||
canonical_root: plugin_root.clone(),
|
||||
scope: PluginScope::User,
|
||||
origin: kigi_agent::plugins::PluginOrigin::UserGrok,
|
||||
origin: kigi_agent::plugins::PluginOrigin::UserKigi,
|
||||
trusted: true,
|
||||
skill_dirs: vec![],
|
||||
command_dirs: vec![],
|
||||
@@ -904,7 +904,7 @@ enabled = false
|
||||
root: plugin_root.clone(),
|
||||
canonical_root: plugin_root.clone(),
|
||||
scope: PluginScope::User,
|
||||
origin: kigi_agent::plugins::PluginOrigin::UserGrok,
|
||||
origin: kigi_agent::plugins::PluginOrigin::UserKigi,
|
||||
trusted: true,
|
||||
skill_dirs: vec![],
|
||||
command_dirs: vec![],
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
//! Receives [`kigi_mcp::servers::McpClientEvent`]s emitted by:
|
||||
//! - per-client transport-liveness watchers
|
||||
//! ([`kigi_mcp::liveness`]),
|
||||
//! - the [`kigi_mcp::servers::GrokClientHandler`] (server-pushed
|
||||
//! - the [`kigi_mcp::servers::KigiClientHandler`] (server-pushed
|
||||
//! `tools/list_changed` and `resources/list_changed`),
|
||||
//! - the `ensure_initialized` success/failure path,
|
||||
//! - the session/managed-config diff path.
|
||||
@@ -16,7 +16,7 @@
|
||||
//!
|
||||
//! Each surviving entry is emitted as an ACP
|
||||
//! [`agent_client_protocol::ExtNotification`] with method
|
||||
//! `x.ai/mcp/server_status` and the payload schema defined by
|
||||
//! `kigi/mcp/server_status` and the payload schema defined by
|
||||
//! [`McpServerStatusPayload`].
|
||||
//!
|
||||
//! ## Doc-comment ↔ implementation contract
|
||||
@@ -55,7 +55,7 @@ use crate::extensions::mcp::McpServerSource;
|
||||
pub const COALESCE_WINDOW: Duration = Duration::from_millis(50);
|
||||
|
||||
/// Method name for the ACP push.
|
||||
pub const SERVER_STATUS_METHOD: &str = "x.ai/mcp/server_status";
|
||||
pub const SERVER_STATUS_METHOD: &str = "kigi/mcp/server_status";
|
||||
|
||||
/// JSON payload pushed over ACP. Fields written in camelCase per ACP
|
||||
/// convention.
|
||||
@@ -438,7 +438,7 @@ pub fn build_payload(
|
||||
/// Per-flush side effects:
|
||||
/// - update `shutting_down` for `TransportClosed` /
|
||||
/// `ConfigRemoved` keys,
|
||||
/// - emit one ACP `x.ai/mcp/server_status` push per surviving
|
||||
/// - emit one ACP `kigi/mcp/server_status` push per surviving
|
||||
/// buffer entry, via the provided gateway.
|
||||
///
|
||||
/// `gateway` is a [`kigi_acp_lib::AcpAgentGatewaySender`] (forwarded
|
||||
@@ -638,7 +638,7 @@ pub async fn drop_dead_clients(
|
||||
/// gated on client identity (see [`collect_close_candidates`]).
|
||||
/// Stale `TransportClosed` keys are stripped from the window so they
|
||||
/// push no status, emit no disconnect span, and schedule no restart.
|
||||
/// 3. `flush_window` — emit ACP `x.ai/mcp/server_status` per
|
||||
/// 3. `flush_window` — emit ACP `kigi/mcp/server_status` per
|
||||
/// surviving entry.
|
||||
/// 4. `maybe_schedule_restart` — for every
|
||||
/// `TransportClosed` / `HandshakeFailed` key, the
|
||||
|
||||
@@ -213,7 +213,7 @@ pub trait RestartActions {
|
||||
/// bubbles up.
|
||||
async fn respawn_stdio(&self, server: &str) -> Result<(), String>;
|
||||
|
||||
/// Push an already-built `x.ai/mcp/server_status` payload to the
|
||||
/// Push an already-built `kigi/mcp/server_status` payload to the
|
||||
/// pager. The production impl wraps the dispatcher's gateway
|
||||
/// sender via [`forward_status`].
|
||||
fn push_status(&self, payload: &McpServerStatusPayload);
|
||||
@@ -617,7 +617,7 @@ fn push(
|
||||
}
|
||||
|
||||
/// Serialize a [`McpServerStatusPayload`] and send it to the gateway as an
|
||||
/// ACP `x.ai/mcp/server_status` notification. Failures are logged and
|
||||
/// ACP `kigi/mcp/server_status` notification. Failures are logged and
|
||||
/// dropped — restart-task pushes must not block the session actor.
|
||||
///
|
||||
/// Public so production impls and tests can wrap a gateway sender
|
||||
@@ -1506,7 +1506,7 @@ mod tests {
|
||||
fn forward_status_uses_dispatcher_method() {
|
||||
assert_eq!(
|
||||
crate::session::mcp_dispatcher::SERVER_STATUS_METHOD,
|
||||
"x.ai/mcp/server_status",
|
||||
"kigi/mcp/server_status",
|
||||
"wire method name pinned",
|
||||
);
|
||||
// The `forward_status` function uses
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
//! Merged session listing — combines local and remote session data.
|
||||
//!
|
||||
//! Used by both the ACP `x.ai/session/list` handler and the `grok sessions`
|
||||
//! Used by both the ACP `kigi/session/list` handler and the `kigi sessions`
|
||||
//! CLI command. Deduplicates by session ID (remote wins), filters local
|
||||
//! results by query, and sorts by the same key the picker UI displays
|
||||
//! (`last_active_at` falling back to `updated_at`) descending.
|
||||
|
||||
@@ -36,15 +36,15 @@ pub type PendingInteractions = Arc<Mutex<HashMap<String, PendingKind>>>;
|
||||
pub enum PendingKind {
|
||||
/// `request_permission` for a tool action.
|
||||
Permission,
|
||||
/// `x.ai/ask_user_question`.
|
||||
/// `kigi/ask_user_question`.
|
||||
Question,
|
||||
/// `x.ai/exit_plan_mode` plan approval.
|
||||
/// `kigi/exit_plan_mode` plan approval.
|
||||
PlanApproval,
|
||||
}
|
||||
|
||||
/// Whether a blocking plan-approval reverse-request is parked in `pending`.
|
||||
///
|
||||
/// The resume re-park issues `x.ai/exit_plan_mode` from a detached task
|
||||
/// The resume re-park issues `kigi/exit_plan_mode` from a detached task
|
||||
/// with no running turn, making it the one parked interaction that also carries a
|
||||
/// persisted gate (`awaiting_plan_approval`). `session_has_live_work` consults
|
||||
/// this to keep such a session resident until the decision is answered or a real
|
||||
@@ -70,7 +70,7 @@ fn broadcast(gateway: &GatewaySender, session_id: &acp::SessionId, update: XaiSe
|
||||
};
|
||||
if let Ok(params) = serde_json::value::to_raw_value(¬ification) {
|
||||
gateway.forward_fire_and_forget(acp::ExtNotification::new(
|
||||
"x.ai/session_notification",
|
||||
"kigi/session_notification",
|
||||
params.into(),
|
||||
));
|
||||
}
|
||||
|
||||
@@ -136,8 +136,8 @@ mod feedback_tests {
|
||||
Some("could be better".into())
|
||||
},
|
||||
feedback_categories: vec![],
|
||||
model_id: Some("grok-3-fast".into()),
|
||||
resolved_model_id: Some("grok-4.5".into()),
|
||||
model_id: Some("kigi-3-fast".into()),
|
||||
resolved_model_id: Some("kigi-4.5".into()),
|
||||
model_fingerprint: None,
|
||||
context_type: None,
|
||||
request_id: None,
|
||||
@@ -299,7 +299,7 @@ pub enum PersistenceMsg {
|
||||
ReplaceChatHistory(Vec<ConversationItem>),
|
||||
CurrentModel {
|
||||
model_id: acp::ModelId,
|
||||
/// The active agent definition name (e.g. `"grok-build"`).
|
||||
/// The active agent definition name (e.g. `"kigi"`).
|
||||
/// Persisted in `summary.agent_name` so session resume doesn't depend
|
||||
/// on the mutable model catalog.
|
||||
agent_name: Option<String>,
|
||||
@@ -411,7 +411,7 @@ fn session_exists_for_cwd_in_root(session_id: &str, cwd: &str, sessions_root: &P
|
||||
///
|
||||
/// When a remote session is restored, a new local child is created with
|
||||
/// `summary.parent_session_id == remote_session_id`. On a second
|
||||
/// `grok -r <remote_id>` in the same cwd, this function returns the already-restored
|
||||
/// `kigi -r <remote_id>` in the same cwd, this function returns the already-restored
|
||||
/// child so no duplicate restore is performed.
|
||||
///
|
||||
/// If multiple children match (e.g., from pre-fix duplicate restores), the
|
||||
@@ -521,7 +521,7 @@ fn find_local_child_for_remote_in_root(
|
||||
}
|
||||
|
||||
// Collect all matching children. Multiple can exist when a user ran
|
||||
// `grok -r <remote_id>` before this fix was deployed.
|
||||
// `kigi -r <remote_id>` before this fix was deployed.
|
||||
// Tuple: (updated_at, dir_mtime_nanos, session_id) — all sorted descending.
|
||||
let mut candidates: Vec<(String, u128, String)> = Vec::new();
|
||||
|
||||
@@ -632,7 +632,7 @@ pub fn session_exists_by_id(session_id: &str) -> bool {
|
||||
}
|
||||
|
||||
/// Inner implementation of `session_exists_by_id` that accepts a custom root.
|
||||
/// Separated so tests can use a tempdir without touching the real grok home.
|
||||
/// Separated so tests can use a tempdir without touching the real kigi home.
|
||||
fn session_exists_in_root(session_id: &str, sessions_root: &Path) -> bool {
|
||||
if !sessions_root.exists() {
|
||||
return false;
|
||||
@@ -1908,7 +1908,7 @@ pub(crate) fn io_error_to_acp(e: &io::Error) -> acp::Error {
|
||||
}
|
||||
|
||||
/// Best-effort worktree liveness touch: stamp `last_accessed_at` on the
|
||||
/// worktree containing this session's cwd so `grok worktree gc` expires by
|
||||
/// worktree containing this session's cwd so `kigi worktree gc` expires by
|
||||
/// last use, not creation time. Lives here — not in a `StorageAdapter` —
|
||||
/// so every session create/load path shares it regardless of backend.
|
||||
fn spawn_worktree_touch(info: &Info) -> tokio::task::JoinHandle<()> {
|
||||
@@ -2489,13 +2489,7 @@ mod agent_name_persistence_tests {
|
||||
|
||||
#[test]
|
||||
fn summary_round_trips_various_agent_names() {
|
||||
for name in [
|
||||
"cursor",
|
||||
"grok-build",
|
||||
"grok-build-plan",
|
||||
"codex",
|
||||
"browser-use",
|
||||
] {
|
||||
for name in ["cursor", "kigi", "kigi-plan", "codex", "browser-use"] {
|
||||
let mut summary = Summary::new(
|
||||
&Info {
|
||||
id: acp::SessionId::new("test"),
|
||||
@@ -2645,7 +2639,7 @@ mod session_exists_tests {
|
||||
|
||||
#[test]
|
||||
fn returns_false_when_root_does_not_exist() {
|
||||
let root = std::path::PathBuf::from("/nonexistent/grok/sessions");
|
||||
let root = std::path::PathBuf::from("/nonexistent/kigi/sessions");
|
||||
assert!(!session_exists_in_root("any-id", &root));
|
||||
}
|
||||
|
||||
@@ -2722,7 +2716,7 @@ mod find_summary_by_session_id_tests {
|
||||
"created_at": "2026-01-01T00:00:00Z",
|
||||
"updated_at": "2026-01-01T00:00:00Z",
|
||||
"num_messages": 0,
|
||||
"current_model_id": "grok-3",
|
||||
"current_model_id": "kigi-3",
|
||||
"head_commit": head_commit,
|
||||
"head_branch": head_branch
|
||||
})
|
||||
@@ -2802,7 +2796,7 @@ mod resumed_sandbox_profile_tests {
|
||||
"created_at": "2026-01-01T00:00:00Z",
|
||||
"updated_at": updated_at,
|
||||
"num_messages": 0,
|
||||
"current_model_id": "grok-3",
|
||||
"current_model_id": "kigi-3",
|
||||
});
|
||||
if let Some(la) = last_active_at {
|
||||
summary["last_active_at"] = serde_json::Value::String(la.to_string());
|
||||
@@ -2873,7 +2867,7 @@ mod resumed_sandbox_profile_tests {
|
||||
"created_at": "2026-01-01T00:00:00Z",
|
||||
"updated_at": "2026-01-01T00:00:00Z",
|
||||
"num_messages": 0,
|
||||
"current_model_id": "grok-3",
|
||||
"current_model_id": "kigi-3",
|
||||
"parent_session_id": "remote-xyz",
|
||||
"sandbox_profile": "workspace",
|
||||
});
|
||||
@@ -3206,7 +3200,7 @@ mod find_local_child_tests {
|
||||
assert!(found.is_none());
|
||||
}
|
||||
|
||||
/// Regression: a second `grok -r <remote_id>` must return the existing child
|
||||
/// Regression: a second `kigi -r <remote_id>` must return the existing child
|
||||
/// without creating a new restore, not return `None`.
|
||||
#[test]
|
||||
fn repeated_resume_returns_existing_child() {
|
||||
@@ -3305,7 +3299,7 @@ mod resolve_local_session_tests {
|
||||
|
||||
// resolve_local_session delegates to the same _in_root helpers tested above,
|
||||
// so we test the composition logic via the public function indirectly by
|
||||
// setting up the on-disk structures under a fake grok home.
|
||||
// setting up the on-disk structures under a fake kigi home.
|
||||
// For unit isolation, we test the equivalent logic via the inner helpers.
|
||||
|
||||
fn setup_session(root: &std::path::Path, cwd: &str, session_id: &str) {
|
||||
|
||||
@@ -644,7 +644,7 @@ mod tests {
|
||||
use kigi_tools::types::template_renderer::TemplateRenderer;
|
||||
use kigi_tools::types::tool::ToolKind;
|
||||
use std::collections::HashMap;
|
||||
/// Build a test TemplateRenderer with standard Grok Build tool mappings.
|
||||
/// Build a test TemplateRenderer with standard Kigi tool mappings.
|
||||
fn test_renderer() -> TemplateRenderer {
|
||||
let tools: HashMap<ToolKind, String> = [
|
||||
(ToolKind::Edit, "search_replace".to_owned()),
|
||||
|
||||
@@ -5,13 +5,13 @@ use serde::Deserialize;
|
||||
use std::path::PathBuf;
|
||||
/// Parsed prompt with context and query kept separate.
|
||||
///
|
||||
/// Some templates put `<user_query>` last (context first); Grok puts it first.
|
||||
/// Some templates put `<user_query>` last (context first); Kigi puts it first.
|
||||
/// Keeping them separate lets the caller truncate context without
|
||||
/// searching for the query boundary in a flat string.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ParsedPrompt {
|
||||
/// Context blocks: `<attached_files>` payloads and resource-link sections.
|
||||
/// Grok mode may include editor open/focus metadata; the compat mode does not.
|
||||
/// Kigi mode may include editor open/focus metadata; the compat mode does not.
|
||||
/// Empty string when there is no context.
|
||||
pub context: String,
|
||||
/// The user's query, already wrapped in `<user_query>` tags
|
||||
@@ -45,7 +45,7 @@ impl ParsedPrompt {
|
||||
/// Assemble context, query, and skill information into the final message string.
|
||||
///
|
||||
/// Layout:
|
||||
/// - **Grok mode:** `<user_query>` + `<skill_information>` + context
|
||||
/// - **Kigi mode:** `<user_query>` + `<skill_information>` + context
|
||||
/// - **Query-last mode:** context + `<user_query>` + `<skill_information>`
|
||||
///
|
||||
/// The `<skill_information>` block always follows `<user_query>` immediately
|
||||
@@ -75,7 +75,7 @@ impl ParsedPrompt {
|
||||
/// - `<attached_files>` (bare), resource links, then `<user_query>` last
|
||||
/// - File references use `<code_selection>` tags
|
||||
///
|
||||
/// When `is_cursor` is false, produces original Grok-format output:
|
||||
/// When `is_cursor` is false, produces original Kigi-format output:
|
||||
/// - `<user_query>` first, then `<system-reminder>` wrapped `<attached_files>` and resource links
|
||||
/// - File references use `<file_contents>` tags
|
||||
pub async fn parse_prompt(
|
||||
@@ -203,7 +203,7 @@ Below are some potentially helpful/relevant pieces of information for figuring o
|
||||
if !context.is_empty() {
|
||||
context.push_str("\n\n");
|
||||
}
|
||||
context.push_str(&render_resource_links_grok(resource_links));
|
||||
context.push_str(&render_resource_links_kigi(resource_links));
|
||||
}
|
||||
(context, query)
|
||||
}
|
||||
@@ -285,9 +285,9 @@ fn render_regular_links(links: &[&acp::ResourceLink]) -> String {
|
||||
}
|
||||
s.trim_end_matches('\n').to_string()
|
||||
}
|
||||
/// Grok-format resource links: `<focused_files>` / `<open_files>` with
|
||||
/// Kigi-format resource links: `<focused_files>` / `<open_files>` with
|
||||
/// metadata inside a `<system-reminder>` wrapper.
|
||||
fn render_resource_links_grok(resource_links: &[acp::ResourceLink]) -> String {
|
||||
fn render_resource_links_kigi(resource_links: &[acp::ResourceLink]) -> String {
|
||||
let mut regular_links = Vec::new();
|
||||
let mut focused_files = Vec::new();
|
||||
let mut open_files = Vec::new();
|
||||
@@ -363,8 +363,8 @@ mod tests {
|
||||
format!("{query}\n\n{context}")
|
||||
}
|
||||
}
|
||||
/// Shorthand: render + assemble for grok mode.
|
||||
fn render_grok(
|
||||
/// Shorthand: render + assemble for kigi mode.
|
||||
fn render_kigi(
|
||||
message: &str,
|
||||
embedded: Vec<String>,
|
||||
file_refs: Vec<String>,
|
||||
@@ -475,13 +475,13 @@ mod tests {
|
||||
assert!(parse_editor_meta(&link).is_none());
|
||||
}
|
||||
#[test]
|
||||
fn test_grok_render_plain_message() {
|
||||
let result = render_grok("hello", vec![], vec![], &[], false);
|
||||
fn test_kigi_render_plain_message() {
|
||||
let result = render_kigi("hello", vec![], vec![], &[], false);
|
||||
assert_eq!(result, "<user_query>\nhello\n</user_query>");
|
||||
}
|
||||
#[test]
|
||||
fn test_grok_render_with_attachments_uses_system_reminder_wrapper() {
|
||||
let result = render_grok(
|
||||
fn test_kigi_render_with_attachments_uses_system_reminder_wrapper() {
|
||||
let result = render_kigi(
|
||||
"check this",
|
||||
vec!["embedded content".into()],
|
||||
vec![],
|
||||
@@ -496,25 +496,25 @@ mod tests {
|
||||
assert!(result.contains("embedded content"));
|
||||
assert!(
|
||||
result.starts_with("<user_query>"),
|
||||
"Grok should start with <user_query>, got: {result}"
|
||||
"Kigi should start with <user_query>, got: {result}"
|
||||
);
|
||||
}
|
||||
#[test]
|
||||
fn test_grok_render_user_query_first() {
|
||||
fn test_kigi_render_user_query_first() {
|
||||
let link = acp::ResourceLink::new("doc.md", "file:///doc.md")
|
||||
.title(Some("My Doc".into()))
|
||||
.size(Some(1024));
|
||||
let result = render_grok("hello", vec![], vec![], &[link], false);
|
||||
let result = render_kigi("hello", vec![], vec![], &[link], false);
|
||||
let uq_pos = result.find("<user_query>").unwrap();
|
||||
let rr_pos = result.find("Referenced resources:").unwrap();
|
||||
assert!(
|
||||
uq_pos < rr_pos,
|
||||
"Grok: <user_query> ({uq_pos}) should come before resource links ({rr_pos})\ngot: {result}"
|
||||
"Kigi: <user_query> ({uq_pos}) should come before resource links ({rr_pos})\ngot: {result}"
|
||||
);
|
||||
assert!(result.contains("<system-reminder>"));
|
||||
}
|
||||
#[test]
|
||||
fn test_grok_render_resource_links_use_focused_files_format() {
|
||||
fn test_kigi_render_resource_links_use_focused_files_format() {
|
||||
let links = vec![
|
||||
acp::ResourceLink::new("main.rs", "file:///project/src/main.rs").meta(
|
||||
serde_json::json!({ "source" : "editor", "fileState" : "focused",
|
||||
@@ -528,7 +528,7 @@ mod tests {
|
||||
.cloned(),
|
||||
),
|
||||
];
|
||||
let result = render_grok("hello", vec![], vec![], &links, false);
|
||||
let result = render_kigi("hello", vec![], vec![], &links, false);
|
||||
assert!(result.contains("<system-reminder>"), "got: {result}");
|
||||
assert!(result.contains("<focused_files>"), "got: {result}");
|
||||
assert!(result.contains("<open_files>"), "got: {result}");
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
pub use kigi_prompt_queue::{QueueChanged, QueueEntryMeta, QueueEntryWire};
|
||||
|
||||
// Outbound method for broadcast_queue_changed. An ACP routing concern, not a queue concern.
|
||||
pub const QUEUE_CHANGED_METHOD: &str = "x.ai/queue/changed";
|
||||
pub const QUEUE_CHANGED_METHOD: &str = "kigi/queue/changed";
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
@@ -20,7 +20,7 @@ mod tests {
|
||||
entries: vec![QueueEntryWire {
|
||||
id: "p1".to_string(),
|
||||
version: 0,
|
||||
owner: Some("grok-tui".to_string()),
|
||||
owner: Some("kigi-tui".to_string()),
|
||||
last_editor: None,
|
||||
kind: "prompt".to_string(),
|
||||
text: "hello".to_string(),
|
||||
@@ -56,14 +56,14 @@ mod tests {
|
||||
let entry = QueueEntryWire {
|
||||
id: "p1".to_string(),
|
||||
version: 3,
|
||||
owner: Some("grok-tui".to_string()),
|
||||
last_editor: Some("grok-vscode".to_string()),
|
||||
owner: Some("kigi-tui".to_string()),
|
||||
last_editor: Some("kigi-vscode".to_string()),
|
||||
kind: "prompt".to_string(),
|
||||
text: "hello".to_string(),
|
||||
position: 0,
|
||||
};
|
||||
let json = serde_json::to_value(&entry).unwrap();
|
||||
assert_eq!(json["lastEditor"], "grok-vscode");
|
||||
assert_eq!(json["lastEditor"], "kigi-vscode");
|
||||
let round: QueueEntryWire = serde_json::from_value(json).unwrap();
|
||||
assert_eq!(round, entry);
|
||||
}
|
||||
|
||||
@@ -280,7 +280,7 @@ pub struct SessionSignals {
|
||||
/// Number of edit-and-retry actions (user rewinds and submits a different prompt)
|
||||
pub edit_and_retry_count: u32,
|
||||
|
||||
// === Bash tool patterns (grok_build) ===
|
||||
// === Bash tool patterns (kigi) ===
|
||||
/// Number of times the bash tool was used for a bare `echo "<msg>"` (or close
|
||||
/// variant). Tracked for usage statistics.
|
||||
#[serde(default)]
|
||||
@@ -1928,20 +1928,20 @@ mod tests {
|
||||
let actor_handle = tokio::spawn(actor.run());
|
||||
|
||||
// Set primary model
|
||||
handle.set_primary_model("grok-3");
|
||||
handle.set_primary_model("kigi-3");
|
||||
|
||||
let snapshot = handle.snapshot().await.unwrap();
|
||||
assert_eq!(snapshot.primary_model_id, Some("grok-3".to_string()));
|
||||
assert_eq!(snapshot.models_used, vec!["grok-3".to_string()]);
|
||||
assert_eq!(snapshot.primary_model_id, Some("kigi-3".to_string()));
|
||||
assert_eq!(snapshot.models_used, vec!["kigi-3".to_string()]);
|
||||
|
||||
// Record additional model usage
|
||||
handle.record_model_usage("grok-4");
|
||||
handle.record_model_usage("grok-3"); // Duplicate
|
||||
handle.record_model_usage("kigi-4");
|
||||
handle.record_model_usage("kigi-3"); // Duplicate
|
||||
|
||||
let snapshot = handle.snapshot().await.unwrap();
|
||||
assert_eq!(snapshot.models_used.len(), 2);
|
||||
assert!(snapshot.models_used.contains(&"grok-3".to_string()));
|
||||
assert!(snapshot.models_used.contains(&"grok-4".to_string()));
|
||||
assert!(snapshot.models_used.contains(&"kigi-3".to_string()));
|
||||
assert!(snapshot.models_used.contains(&"kigi-4".to_string()));
|
||||
|
||||
handle.shutdown();
|
||||
actor_handle.await.unwrap();
|
||||
@@ -2501,7 +2501,7 @@ mod tests {
|
||||
"bash".to_string(),
|
||||
"search_replace".to_string(),
|
||||
],
|
||||
vec!["grok-3".to_string(), "grok-4".to_string()],
|
||||
vec!["kigi-3".to_string(), "kigi-4".to_string()],
|
||||
);
|
||||
|
||||
let snapshot = handle.snapshot().await.unwrap();
|
||||
@@ -2520,21 +2520,21 @@ mod tests {
|
||||
|
||||
// Model tracking (newly restored)
|
||||
assert_eq!(snapshot.models_used.len(), 2);
|
||||
assert!(snapshot.models_used.contains(&"grok-3".to_string()));
|
||||
assert!(snapshot.models_used.contains(&"grok-4".to_string()));
|
||||
assert!(snapshot.models_used.contains(&"kigi-3".to_string()));
|
||||
assert!(snapshot.models_used.contains(&"kigi-4".to_string()));
|
||||
|
||||
// After seeding, new tool calls should accumulate correctly
|
||||
handle.record_tool_call("bash"); // existing tool
|
||||
handle.record_tool_call("grep"); // new tool
|
||||
handle.record_model_usage("grok-3"); // existing model
|
||||
handle.record_model_usage("grok-4.5"); // new model
|
||||
handle.record_model_usage("kigi-3"); // existing model
|
||||
handle.record_model_usage("kigi-4.5"); // new model
|
||||
|
||||
let snapshot = handle.snapshot().await.unwrap();
|
||||
assert_eq!(snapshot.tool_call_count, 14); // 12 + 2
|
||||
assert_eq!(snapshot.tools_used.len(), 4); // bash not duplicated, grep added
|
||||
assert!(snapshot.tools_used.contains(&"grep".to_string()));
|
||||
assert_eq!(snapshot.models_used.len(), 3); // grok-3 not duplicated, grok-5 added
|
||||
assert!(snapshot.models_used.contains(&"grok-4.5".to_string()));
|
||||
assert_eq!(snapshot.models_used.len(), 3); // kigi-3 not duplicated, kigi-5 added
|
||||
assert!(snapshot.models_used.contains(&"kigi-4.5".to_string()));
|
||||
|
||||
handle.shutdown();
|
||||
actor_handle.await.unwrap();
|
||||
@@ -2553,7 +2553,7 @@ mod tests {
|
||||
handle1.record_tool_failure("bash");
|
||||
handle1.record_error();
|
||||
handle1.record_assistant_message();
|
||||
handle1.record_model_usage("grok-3");
|
||||
handle1.record_model_usage("kigi-3");
|
||||
|
||||
// Record inference metrics with ITL intervals for turn 1
|
||||
handle1.record_inference_metrics(InferenceLatencyStats {
|
||||
@@ -2572,7 +2572,7 @@ mod tests {
|
||||
handle1.record_tool_call("search_replace");
|
||||
handle1.record_cancellation();
|
||||
handle1.record_assistant_message();
|
||||
handle1.record_model_usage("grok-4");
|
||||
handle1.record_model_usage("kigi-4");
|
||||
|
||||
handle1.increment_turn(); // turn 3
|
||||
handle1.record_tool_call("bash");
|
||||
@@ -2639,14 +2639,14 @@ mod tests {
|
||||
assert!(restored.tools_used.contains(&"read_file".to_string()));
|
||||
assert!(restored.tools_used.contains(&"search_replace".to_string()));
|
||||
assert_eq!(restored.models_used.len(), 2);
|
||||
assert!(restored.models_used.contains(&"grok-3".to_string()));
|
||||
assert!(restored.models_used.contains(&"grok-4".to_string()));
|
||||
assert!(restored.models_used.contains(&"kigi-3".to_string()));
|
||||
assert!(restored.models_used.contains(&"kigi-4".to_string()));
|
||||
assert_eq!(restored.latency_sample_count, 2);
|
||||
assert_eq!(restored.avg_time_to_first_token_ms, 150);
|
||||
assert_eq!(restored.avg_response_time_ms, 1500);
|
||||
assert_eq!(restored.min_time_to_first_token_ms, 100);
|
||||
assert_eq!(restored.max_time_to_first_token_ms, 200);
|
||||
// ITL stats must survive the restore (regression test for grok-critique bug)
|
||||
// ITL stats must survive the restore (regression test for kigi-critique bug)
|
||||
assert_eq!(
|
||||
restored.itl_p50_ms, snapshot.itl_p50_ms,
|
||||
"itl_p50_ms should survive restore"
|
||||
@@ -2669,7 +2669,7 @@ mod tests {
|
||||
assert_eq!(restored.itl_sample_count, 1);
|
||||
|
||||
// Phase 2b: Take a turn-end snapshot *without* recording new ITL data.
|
||||
// This is the exact scenario the grok-critique bug describes: the
|
||||
// This is the exact scenario the kigi-critique bug describes: the
|
||||
// TakeTurnEndSnapshot handler calls update_session_itl_percentiles()
|
||||
// which must NOT wipe persisted ITL p50/p99 when itl_digest is None.
|
||||
handle2.increment_turn(); // turn 4 (no ITL data recorded this turn)
|
||||
@@ -2694,7 +2694,7 @@ mod tests {
|
||||
handle2.increment_turn(); // turn 5
|
||||
handle2.record_tool_call("grep"); // new tool
|
||||
handle2.record_tool_call("bash"); // existing tool (should dedup)
|
||||
handle2.record_model_usage("grok-3"); // existing model (should dedup)
|
||||
handle2.record_model_usage("kigi-3"); // existing model (should dedup)
|
||||
handle2.record_error();
|
||||
handle2.record_assistant_message();
|
||||
|
||||
@@ -2709,7 +2709,7 @@ mod tests {
|
||||
assert_eq!(after_turn.error_count, 3); // 2 + 1
|
||||
assert_eq!(after_turn.tools_used.len(), 4); // bash not duplicated, grep added
|
||||
assert!(after_turn.tools_used.contains(&"grep".to_string()));
|
||||
assert_eq!(after_turn.models_used.len(), 2); // grok-3 not duplicated
|
||||
assert_eq!(after_turn.models_used.len(), 2); // kigi-3 not duplicated
|
||||
// Latency: (100+200+300)/3 = 200
|
||||
assert_eq!(after_turn.latency_sample_count, 3);
|
||||
assert_eq!(after_turn.avg_time_to_first_token_ms, 200);
|
||||
|
||||
@@ -534,7 +534,7 @@ pub(crate) fn builtin_commands(availability: CommandAvailability) -> Vec<acp::Av
|
||||
.collect()
|
||||
}
|
||||
|
||||
// ── x.ai/commands/list ext method ────────────────────────────────
|
||||
// ── kigi/commands/list ext method ────────────────────────────────
|
||||
|
||||
#[derive(serde::Deserialize)]
|
||||
pub(crate) struct ListCommandsRequest {
|
||||
@@ -731,7 +731,7 @@ impl BuiltinAction {
|
||||
/// How to rewrite the user's prompt when a slash command resolves to a skill.
|
||||
///
|
||||
/// - `RewriteToRun` (default): replace `/foo args` with `"run /foo args"`,
|
||||
/// matching today's Grok Build flow that calls our dedicated `skill` tool.
|
||||
/// matching today's Kigi flow that calls our dedicated `skill` tool.
|
||||
/// - `Passthrough`: leave the prompt verbatim. Some templates use this —
|
||||
/// the model is trained to spot a leading `/<name>`, look it up in the
|
||||
/// `<agent_skills>` listing, and call the Read tool on `fullPath`.
|
||||
@@ -1081,7 +1081,7 @@ fn parse_slash_prefix(prompt_blocks: &[acp::ContentBlock]) -> Option<(&str, &str
|
||||
/// default: the model derives the cadence from the request and asks when none
|
||||
/// is given.
|
||||
fn build_loop_prompt_blocks(args: &str) -> Vec<acp::ContentBlock> {
|
||||
use kigi_tools::implementations::grok_build::{loop_schedule_instruction, loop_usage_message};
|
||||
use kigi_tools::implementations::kigi::{loop_schedule_instruction, loop_usage_message};
|
||||
|
||||
let text = if args.trim().is_empty() {
|
||||
loop_usage_message().to_string()
|
||||
@@ -1685,9 +1685,7 @@ mod tests {
|
||||
#[test]
|
||||
fn loop_prompt_matches_pager_wording() {
|
||||
// The shell and pager must stay textually identical so they don't drift.
|
||||
use kigi_tools::implementations::grok_build::{
|
||||
loop_schedule_instruction, loop_usage_message,
|
||||
};
|
||||
use kigi_tools::implementations::kigi::{loop_schedule_instruction, loop_usage_message};
|
||||
assert_eq!(loop_text(""), loop_usage_message());
|
||||
assert_eq!(
|
||||
loop_text("2h run tests"),
|
||||
|
||||
@@ -616,7 +616,7 @@ fn transform_session_id_in_update(
|
||||
/// 2. Truncates at the last complete turn boundary. A complete turn runs
|
||||
/// `User → Assistant → (matching ToolResults)`, possibly across multiple
|
||||
/// Assistant/ToolResult cycles, with `Reasoning` siblings interleaved
|
||||
/// throughout (real grok-build turns emit `[reasoning, assistant, tool
|
||||
/// throughout (real kigi turns emit `[reasoning, assistant, tool
|
||||
/// results, reasoning, assistant, ...]`). The scan treats everything
|
||||
/// except `Assistant` as transparent and only advances the boundary when an
|
||||
/// Assistant closes every tool call it made, so it survives reasoning
|
||||
|
||||
@@ -121,7 +121,7 @@ async fn test_jsonl_round_trip() {
|
||||
.unwrap();
|
||||
let plan_state = create_test_plan_state();
|
||||
adapter.write_plan_state(&info, &plan_state).await.unwrap();
|
||||
let new_model = acp::ModelId::new("grok-4.3");
|
||||
let new_model = acp::ModelId::new("kigi-4.3");
|
||||
adapter.update_current_model(&info, &new_model).await.unwrap();
|
||||
let loaded = adapter.load_session(&info).await.unwrap();
|
||||
assert_eq!(loaded.summary.info.id, info.id);
|
||||
@@ -456,12 +456,12 @@ async fn test_subagent_notifications_round_trip() {
|
||||
.len()
|
||||
);
|
||||
let spawned_json: serde_json::Value = serde_json::from_str(lines[0]).unwrap();
|
||||
assert_eq!(spawned_json["method"], "_x.ai/session/update");
|
||||
assert_eq!(spawned_json["method"], "_kigi/session/update");
|
||||
let spawned_update = &spawned_json["params"]["update"];
|
||||
assert_eq!(spawned_update["sessionUpdate"], "subagent_spawned");
|
||||
assert_eq!(spawned_update["subagent_id"], "child-001");
|
||||
let finished_json: serde_json::Value = serde_json::from_str(lines[1]).unwrap();
|
||||
assert_eq!(finished_json["method"], "_x.ai/session/update");
|
||||
assert_eq!(finished_json["method"], "_kigi/session/update");
|
||||
let finished_update = &finished_json["params"]["update"];
|
||||
assert_eq!(finished_update["sessionUpdate"], "subagent_finished");
|
||||
assert_eq!(finished_update["tool_calls"], 5);
|
||||
@@ -742,13 +742,13 @@ async fn test_copy_session_data_with_model_override() {
|
||||
};
|
||||
let options = CopySessionOptions {
|
||||
parent_session_id: Some("source-model-test".to_string()),
|
||||
new_model_id: Some("grok-3".to_string()),
|
||||
new_model_id: Some("kigi-3".to_string()),
|
||||
target_prompt_index: None,
|
||||
..Default::default()
|
||||
};
|
||||
adapter.copy_session_data(&source_info, &target_info, options).await.unwrap();
|
||||
let loaded = adapter.load_session(&target_info).await.unwrap();
|
||||
assert_eq!(loaded.summary.current_model_id.0.as_ref(), "grok-3");
|
||||
assert_eq!(loaded.summary.current_model_id.0.as_ref(), "kigi-3");
|
||||
assert_eq!(loaded.summary.parent_session_id, Some("source-model-test".to_string()));
|
||||
}
|
||||
#[tokio::test]
|
||||
@@ -1108,8 +1108,8 @@ async fn test_append_feedback_creates_file_and_persists() {
|
||||
rating_value: Some(1),
|
||||
feedback_text: None,
|
||||
feedback_categories: vec![],
|
||||
model_id: Some("grok-3-fast".into()),
|
||||
resolved_model_id: Some("grok-4.5".into()),
|
||||
model_id: Some("kigi-3-fast".into()),
|
||||
resolved_model_id: Some("kigi-4.5".into()),
|
||||
model_fingerprint: None,
|
||||
context_type: None,
|
||||
request_id: None,
|
||||
@@ -1163,7 +1163,7 @@ async fn test_copy_session_data_copies_tool_state() {
|
||||
.await
|
||||
.unwrap();
|
||||
let tool_state_json = serde_json::json!(
|
||||
{ "state" : { "grok_build.TodoState" : { "todos" : [] } } }
|
||||
{ "state" : { "kigi.TodoState" : { "todos" : [] } } }
|
||||
);
|
||||
let source_dir = adapter.session_dir(&source_info);
|
||||
std::fs::write(
|
||||
@@ -2250,7 +2250,7 @@ fn load_lines(lines: &[&str]) -> Vec<ConversationItem> {
|
||||
}
|
||||
/// Real-shape legacy fixture from a web-search session.
|
||||
/// The assistant carries `reasoning: { text, encrypted, id }` inline —
|
||||
/// the legacy grok-build / Opus / chat-completions shape.
|
||||
/// the legacy kigi / Opus / chat-completions shape.
|
||||
/// BackendToolCall sits as its own sibling line (it was already a
|
||||
/// sibling variant in the legacy shape).
|
||||
#[test]
|
||||
@@ -2260,7 +2260,7 @@ fn read_chat_history_upgrades_legacy_singular_reasoning_to_sibling() {
|
||||
r#"{"type":"system","content":"You are helpful."}"#,
|
||||
r#"{"type":"user","content":[{"type":"text","text":"cats and dogs"}]}"#,
|
||||
r#"{"type":"backend_tool_call","kind":{"tool_type":"web_search","id":"ws_legacy_1","status":"completed","action":{"type":"search","query":"cats and dogs","sources":[]}}}"#,
|
||||
r#"{"type":"assistant","content":"results...","reasoning":{"text":"the results are about cats","encrypted":"enc-blob","id":"rs_legacy"},"model_id":"grok-build"}"#,
|
||||
r#"{"type":"assistant","content":"results...","reasoning":{"text":"the results are about cats","encrypted":"enc-blob","id":"rs_legacy"},"model_id":"kigi"}"#,
|
||||
],
|
||||
);
|
||||
assert_eq!(
|
||||
@@ -2336,11 +2336,11 @@ fn read_chat_history_handles_hybrid_legacy_and_post_pr_lines() {
|
||||
r#"{"type":"system","content":"sys"}"#,
|
||||
r#"{"type":"user","content":[{"type":"text","text":"q1"}]}"#,
|
||||
r#"{"type":"backend_tool_call","kind":{"tool_type":"web_search","id":"ws_legacy_1","status":"completed","action":{"type":"search","query":"q1","sources":[]}}}"#,
|
||||
r#"{"type":"assistant","content":"a1","reasoning":{"text":"legacy thinking","encrypted":"enc","id":"rs_legacy"},"model_id":"grok-build"}"#,
|
||||
r#"{"type":"assistant","content":"a1","reasoning":{"text":"legacy thinking","encrypted":"enc","id":"rs_legacy"},"model_id":"kigi"}"#,
|
||||
r#"{"type":"user","content":[{"type":"text","text":"q2"}]}"#,
|
||||
r#"{"type":"reasoning","id":"rs_postpr","summary":[{"type":"summary_text","text":"new thinking"}]}"#,
|
||||
r#"{"type":"backend_tool_call","kind":{"tool_type":"web_search","id":"ws_postpr","status":"completed","action":{"type":"search","query":"q2","sources":[]}}}"#,
|
||||
r#"{"type":"assistant","content":"a2","model_id":"grok-build"}"#,
|
||||
r#"{"type":"assistant","content":"a2","model_id":"kigi"}"#,
|
||||
],
|
||||
);
|
||||
let kinds: Vec<&'static str> = items
|
||||
@@ -2381,7 +2381,7 @@ fn read_chat_history_handles_hybrid_legacy_and_post_pr_lines() {
|
||||
};
|
||||
assert_eq!(legacy_assistant.content.as_ref(), "a1");
|
||||
assert_eq!(
|
||||
legacy_assistant.model_id.as_deref(), Some("grok-build"),
|
||||
legacy_assistant.model_id.as_deref(), Some("kigi"),
|
||||
"model_id preserved across the upgrade"
|
||||
);
|
||||
let ConversationItem::Reasoning(reconstructed) = &items[3] else {
|
||||
@@ -2402,7 +2402,7 @@ fn read_chat_history_is_idempotent_on_post_pr_sessions() {
|
||||
r#"{"type":"system","content":"sys"}"#,
|
||||
r#"{"type":"user","content":[{"type":"text","text":"q"}]}"#,
|
||||
r#"{"type":"reasoning","id":"rs_x","summary":[{"type":"summary_text","text":"thought"}]}"#,
|
||||
r#"{"type":"assistant","content":"a","model_id":"grok-build"}"#,
|
||||
r#"{"type":"assistant","content":"a","model_id":"kigi"}"#,
|
||||
],
|
||||
);
|
||||
let kinds: Vec<&'static str> = items
|
||||
@@ -2516,7 +2516,7 @@ fn read_chat_history_skips_merged_line_from_interrupted_append() {
|
||||
let good_1 = r#"{"type":"user","content":[{"type":"text","text":"kept"}]}"#;
|
||||
let partial = r#"{"type":"assistant","content":"cut mid-wri"#;
|
||||
let merged_onto = r#"{"type":"user","content":[{"type":"text","text":"lost"}]}"#;
|
||||
let good_2 = r#"{"type":"assistant","content":"after","model_id":"grok-build"}"#;
|
||||
let good_2 = r#"{"type":"assistant","content":"after","model_id":"kigi"}"#;
|
||||
let raw = format!("{good_1}\n{partial}{merged_onto}\n{good_2}\n");
|
||||
let temp_dir = TempDir::new().unwrap();
|
||||
let (_, _, items) = load_raw_chat(&temp_dir, raw.as_bytes());
|
||||
|
||||
@@ -92,8 +92,22 @@ impl Iterator for UpdatesIterator {
|
||||
/// Method name for standard ACP session/update notifications.
|
||||
const ACP_SESSION_UPDATE_METHOD: &str = "session/update";
|
||||
|
||||
/// Method name for xAI extension session/update notifications.
|
||||
pub(crate) const XAI_SESSION_UPDATE_METHOD: &str = "_x.ai/session/update";
|
||||
/// Method name for extension session/update notifications.
|
||||
pub(crate) const XAI_SESSION_UPDATE_METHOD: &str = "_kigi/session/update";
|
||||
|
||||
/// Pre-rebrand spelling of [`XAI_SESSION_UPDATE_METHOD`], as written into
|
||||
/// `updates.jsonl` by builds that predate the `kigi/` extension-method
|
||||
/// rename. READ-SIDE ALIAS ONLY: parsing accepts both spellings so existing
|
||||
/// session files keep loading; the write side always emits
|
||||
/// [`XAI_SESSION_UPDATE_METHOD`].
|
||||
pub(crate) const LEGACY_XAI_SESSION_UPDATE_METHOD: &str = "_x.ai/session/update";
|
||||
|
||||
/// Whether `method` names the extension session-update rail, accepting the
|
||||
/// current spelling and the legacy pre-rebrand spelling (persisted session
|
||||
/// files written by older builds).
|
||||
pub(crate) fn is_ext_session_update_method(method: &str) -> bool {
|
||||
method == XAI_SESSION_UPDATE_METHOD || method == LEGACY_XAI_SESSION_UPDATE_METHOD
|
||||
}
|
||||
|
||||
/// A unified session update that can be either an ACP notification or an xAI extension notification.
|
||||
/// This allows storing all session updates in chronological order.
|
||||
@@ -153,7 +167,7 @@ pub(crate) struct SessionUpdateEnvelope {
|
||||
#[serde(default)]
|
||||
pub timestamp: u64,
|
||||
/// The method name identifying the update type.
|
||||
/// Either "session/update" for ACP or "_x.ai/session/update" for xAI extensions.
|
||||
/// Either "session/update" for ACP or "_kigi/session/update" for xAI extensions.
|
||||
pub method: String,
|
||||
/// The actual notification payload.
|
||||
pub params: serde_json::Value,
|
||||
@@ -183,7 +197,7 @@ impl SessionUpdateEnvelope {
|
||||
|
||||
/// Convert this envelope back into a SessionUpdate.
|
||||
pub(crate) fn into_update(self) -> Result<SessionUpdate, serde_json::Error> {
|
||||
if self.method == XAI_SESSION_UPDATE_METHOD {
|
||||
if is_ext_session_update_method(&self.method) {
|
||||
let notification: SessionNotification = serde_json::from_value(self.params)?;
|
||||
Ok(SessionUpdate::Xai(Box::new(notification)))
|
||||
} else {
|
||||
@@ -224,7 +238,7 @@ impl SessionUpdateEnvelope {
|
||||
// Try to parse as envelope first (has "method" + "params")
|
||||
if let Ok(envelope) = serde_json::from_str::<BorrowedEnvelope<'_>>(line) {
|
||||
let raw_params = envelope.params.get();
|
||||
return if envelope.method == Some(XAI_SESSION_UPDATE_METHOD) {
|
||||
return if envelope.method.is_some_and(is_ext_session_update_method) {
|
||||
let notification: SessionNotification = serde_json::from_str(raw_params)?;
|
||||
Ok(SessionUpdate::Xai(Box::new(notification)))
|
||||
} else {
|
||||
@@ -774,7 +788,7 @@ pub(crate) fn filter_rewind_lines<'a>(lines: Vec<&'a str>) -> Vec<&'a str> {
|
||||
for line in &lines {
|
||||
let (raw_params, is_xai) = if let Ok(env) = serde_json::from_str::<RawLinePeek<'_>>(line) {
|
||||
let raw = env.params.map(|p| p.get()).unwrap_or(line);
|
||||
let xai = env.method == Some(XAI_SESSION_UPDATE_METHOD);
|
||||
let xai = env.method.is_some_and(is_ext_session_update_method);
|
||||
(raw, xai)
|
||||
} else {
|
||||
(*line, false)
|
||||
@@ -923,7 +937,7 @@ pub fn load_updates_for_replay(
|
||||
load_updates_for_replay_from_dir(&session_dir)
|
||||
}
|
||||
|
||||
/// Like [`load_updates_for_replay`], but resolves the session under a specific grok home.
|
||||
/// Like [`load_updates_for_replay`], but resolves the session under a specific kigi home.
|
||||
pub fn load_updates_for_replay_at(
|
||||
session_id: &str,
|
||||
kigi_home: &std::path::Path,
|
||||
@@ -1666,7 +1680,7 @@ pub(crate) fn parse_prompt_extract_event(line: &str) -> PromptExtractEvent {
|
||||
// Step 1: try to extract the envelope (method + raw params).
|
||||
let (raw_params, is_xai) = if let Ok(env) = serde_json::from_str::<RawLinePeek<'_>>(line) {
|
||||
let raw = env.params.map(|p| p.get()).unwrap_or(line);
|
||||
let xai = env.method == Some(XAI_SESSION_UPDATE_METHOD);
|
||||
let xai = env.method.is_some_and(is_ext_session_update_method);
|
||||
(raw, xai)
|
||||
} else {
|
||||
// Not a valid envelope → try legacy format: the line IS the params.
|
||||
@@ -1739,7 +1753,7 @@ mod tests {
|
||||
/// Wrap a xAI notification as the envelope stored in updates.jsonl.
|
||||
fn xai_envelope(session_update_json: &str) -> String {
|
||||
format!(
|
||||
r#"{{"timestamp":1,"method":"_x.ai/session/update","params":{{"sessionId":"s","update":{session_update_json}}}}}"#
|
||||
r#"{{"timestamp":1,"method":"_kigi/session/update","params":{{"sessionId":"s","update":{session_update_json}}}}}"#
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2479,7 +2493,7 @@ mod tests {
|
||||
r#"{"eventId":"ev1"}"#,
|
||||
);
|
||||
// xAI-style line persisted by an older binary: no _meta at all.
|
||||
let old_xai = r#"{"timestamp":2,"method":"_x.ai/session/update","params":{"sessionId":"s","update":{"sessionUpdate":"hook_annotation","message":"trailing"}}}"#;
|
||||
let old_xai = r#"{"timestamp":2,"method":"_kigi/session/update","params":{"sessionId":"s","update":{"sessionUpdate":"hook_annotation","message":"trailing"}}}"#;
|
||||
let raw = format!("{a1}\n{old_xai}\n");
|
||||
|
||||
let prepared = prepare_replay_lines(&raw, Some("ev1"));
|
||||
@@ -2490,7 +2504,7 @@ mod tests {
|
||||
assert_eq!(prepared.lines.len(), 2, "full history is replayed");
|
||||
|
||||
// Same history with the trailing line stamped resolves incrementally.
|
||||
let new_xai = r#"{"timestamp":2,"method":"_x.ai/session/update","params":{"sessionId":"s","update":{"sessionUpdate":"hook_annotation","message":"trailing"},"_meta":{"eventId":"ev2"}}}"#;
|
||||
let new_xai = r#"{"timestamp":2,"method":"_kigi/session/update","params":{"sessionId":"s","update":{"sessionUpdate":"hook_annotation","message":"trailing"},"_meta":{"eventId":"ev2"}}}"#;
|
||||
let raw = format!("{a1}\n{new_xai}\n");
|
||||
let prepared = prepare_replay_lines(&raw, Some("ev1"));
|
||||
assert!(!prepared.mark_replay);
|
||||
@@ -2961,12 +2975,12 @@ mod tests {
|
||||
fn prepare_replay_reports_spawn_without_finish() {
|
||||
let spawn = |id: &str, child: &str| {
|
||||
format!(
|
||||
r#"{{"method":"_x.ai/session/update","params":{{"sessionId":"s","update":{{"sessionUpdate":"subagent_spawned","subagent_id":"{id}","parent_session_id":"s","child_session_id":"{child}","subagent_type":"general-purpose","description":"task"}},"_meta":{{"eventId":"s-1"}}}}}}"#
|
||||
r#"{{"method":"_kigi/session/update","params":{{"sessionId":"s","update":{{"sessionUpdate":"subagent_spawned","subagent_id":"{id}","parent_session_id":"s","child_session_id":"{child}","subagent_type":"general-purpose","description":"task"}},"_meta":{{"eventId":"s-1"}}}}}}"#
|
||||
)
|
||||
};
|
||||
let finish = |id: &str| {
|
||||
format!(
|
||||
r#"{{"method":"_x.ai/session/update","params":{{"sessionId":"s","update":{{"sessionUpdate":"subagent_finished","subagent_id":"{id}","child_session_id":"c{id}","status":"completed","tool_calls":0,"turns":0,"duration_ms":0}},"_meta":{{"eventId":"s-2"}}}}}}"#
|
||||
r#"{{"method":"_kigi/session/update","params":{{"sessionId":"s","update":{{"sessionUpdate":"subagent_finished","subagent_id":"{id}","child_session_id":"c{id}","status":"completed","tool_calls":0,"turns":0,"duration_ms":0}},"_meta":{{"eventId":"s-2"}}}}}}"#
|
||||
)
|
||||
};
|
||||
// `a` spawns and finishes (paired); `b` only spawns (orphan).
|
||||
@@ -3139,4 +3153,38 @@ mod tests {
|
||||
SessionUpdate::Acp(_) => panic!("expected Xai variant"),
|
||||
}
|
||||
}
|
||||
|
||||
/// Session files written before the `kigi/` extension-method rename carry
|
||||
/// the legacy `_…/session/update` method name. The read-side alias must
|
||||
/// keep those lines loading as extension updates; the write side always
|
||||
/// emits the current [`XAI_SESSION_UPDATE_METHOD`].
|
||||
#[test]
|
||||
fn from_str_accepts_legacy_pre_rebrand_method_name() {
|
||||
let line = format!(
|
||||
r#"{{"timestamp":1,"method":"{LEGACY_XAI_SESSION_UPDATE_METHOD}","params":{{"sessionId":"s","update":{{"sessionUpdate":"memory_flush_started"}}}}}}"#
|
||||
);
|
||||
let update = SessionUpdateEnvelope::from_str(&line).unwrap();
|
||||
match &update {
|
||||
SessionUpdate::Xai(notif) => {
|
||||
assert_eq!(
|
||||
notif.update,
|
||||
crate::extensions::notification::SessionUpdate::MemoryFlushStarted
|
||||
);
|
||||
}
|
||||
SessionUpdate::Acp(_) => panic!("expected Xai variant for legacy method name"),
|
||||
}
|
||||
|
||||
// Both spellings classify as the extension rail; an unrelated method
|
||||
// does not.
|
||||
assert!(is_ext_session_update_method(XAI_SESSION_UPDATE_METHOD));
|
||||
assert!(is_ext_session_update_method(
|
||||
LEGACY_XAI_SESSION_UPDATE_METHOD
|
||||
));
|
||||
assert!(!is_ext_session_update_method("session/update"));
|
||||
|
||||
// Write side: envelopes produced today carry the current name only.
|
||||
let reserialized = serde_json::to_string(&update).unwrap();
|
||||
assert!(reserialized.contains(XAI_SESSION_UPDATE_METHOD));
|
||||
assert!(!reserialized.contains(LEGACY_XAI_SESSION_UPDATE_METHOD));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
//! The index is bootstrapped (all sessions indexed) on first search.
|
||||
//! After that, individual sessions are re-indexed on save/title update
|
||||
//! via `notify_session_updated()`. Because the SQLite DB is shared with
|
||||
//! other concurrently running grok processes (which may wipe or downgrade
|
||||
//! other concurrently running kigi processes (which may wipe or downgrade
|
||||
//! it — older binaries drop-and-restamp the schema on open), every
|
||||
//! subsequent search re-verifies the on-disk completed-bootstrap marker
|
||||
//! and re-runs the full bootstrap when it is missing.
|
||||
@@ -27,7 +27,7 @@ use super::search_fts::{SessionDoc, SessionSearchIndex, SessionSearchRow};
|
||||
use super::search_remote_sync;
|
||||
use super::{
|
||||
ContentPeek, PromptExtractEvent, RawLinePeek, RawParamsPeek, StorageAdapter,
|
||||
XAI_SESSION_UPDATE_METHOD, collect_prompts_from_events,
|
||||
collect_prompts_from_events, is_ext_session_update_method,
|
||||
};
|
||||
use crate::session::info::Info;
|
||||
use crate::session::persistence::Summary;
|
||||
@@ -133,7 +133,7 @@ struct SearchManagerState {
|
||||
///
|
||||
/// Requires an active tokio runtime on first access (spawns tasks).
|
||||
///
|
||||
/// TODO: When multiple grok processes run concurrently, they each have
|
||||
/// TODO: When multiple kigi processes run concurrently, they each have
|
||||
/// their own `SearchIndexManager` writing to the same SQLite database.
|
||||
/// WAL mode prevents corruption, but redundant work is done. Consider
|
||||
/// adding reindex claim coordination (like the memory system's
|
||||
@@ -921,7 +921,7 @@ fn collect_all_indexable_content_single_pass(updates_path: &Path) -> io::Result<
|
||||
let (raw_params, is_xai) = if let Ok(env) = serde_json::from_str::<RawLinePeek<'_>>(trimmed)
|
||||
{
|
||||
let raw = env.params.map(|p| p.get()).unwrap_or(trimmed);
|
||||
let xai = env.method == Some(XAI_SESSION_UPDATE_METHOD);
|
||||
let xai = env.method.is_some_and(is_ext_session_update_method);
|
||||
(raw, xai)
|
||||
} else {
|
||||
(trimmed, false)
|
||||
@@ -938,7 +938,7 @@ fn collect_all_indexable_content_single_pass(updates_path: &Path) -> io::Result<
|
||||
// Content events (user messages, assistant responses, tool calls,
|
||||
// thoughts) come from the standard ACP protocol ("session/update").
|
||||
// Control events (rewind markers) come from xAI extensions
|
||||
// ("_x.ai/session/update"). Dispatch on source first, then tag.
|
||||
// ("_kigi/session/update"). Dispatch on source first, then tag.
|
||||
if !is_xai {
|
||||
// ── ACP content events ──────────────────────────────────
|
||||
match tag {
|
||||
@@ -1176,7 +1176,7 @@ fn collect_delta_content(updates_path: &Path, offset: u64) -> io::Result<DeltaRe
|
||||
let (raw_params, is_xai) = if let Ok(env) = serde_json::from_str::<RawLinePeek<'_>>(trimmed)
|
||||
{
|
||||
let raw = env.params.map(|p| p.get()).unwrap_or(trimmed);
|
||||
let xai = env.method == Some(XAI_SESSION_UPDATE_METHOD);
|
||||
let xai = env.method.is_some_and(is_ext_session_update_method);
|
||||
(raw, xai)
|
||||
} else {
|
||||
(trimmed, false)
|
||||
@@ -1389,7 +1389,7 @@ mod tests {
|
||||
|
||||
fn xai_update(session_update_json: &str) -> String {
|
||||
format!(
|
||||
r#"{{"timestamp":1,"method":"_x.ai/session/update","params":{{"sessionId":"s","update":{session_update_json}}}}}"#
|
||||
r#"{{"timestamp":1,"method":"_kigi/session/update","params":{{"sessionId":"s","update":{session_update_json}}}}}"#
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -111,7 +111,7 @@ impl SessionSearchIndex {
|
||||
.unwrap_or(None);
|
||||
|
||||
// One-way ratchet: drop only on UPGRADE (stored < current). Multiple
|
||||
// grok generations share this DB (stable vs alpha); an equality check
|
||||
// kigi generations share this DB (stable vs alpha); an equality check
|
||||
// made each binary wipe the other's index in a ping-pong that left
|
||||
// search empty mid-rebootstrap. A newer index is safe to read: bumps
|
||||
// regenerate content only (table schema is column-identical), and the
|
||||
@@ -709,7 +709,7 @@ mod tests {
|
||||
index
|
||||
.upsert_doc(&test_doc("s1", "Rust debugging", "borrow checker"))
|
||||
.unwrap();
|
||||
// Simulate an index owned by a newer grok generation that has
|
||||
// Simulate an index owned by a newer kigi generation that has
|
||||
// completed a bootstrap.
|
||||
index
|
||||
.set_meta("session_search_schema_version", "5")
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user