Hard fork of xai-org/grok-build (Apache-2.0) re-targeted as Kigi, an
unofficial Kimi Code CLI community build.
Rename & identity
- 72 xai-*/xai-grok-* crates -> kigi-* (explicit: xai-grok-pager-bin ->
kigi-bin [binary `kigi`], xai-grok-pager -> kigi-tui; rest mechanical);
ptyctl, ptyctl-cli, third_party/ unchanged; proto package
xai.grok.tools.v1 -> kigi.tools.v1
- Config home ~/.kigi (KIGI_SHARE_DIR override), env prefix GROK_* ->
KIGI_*, `kigi --version` carries the unofficial-community-build notice
- clap identity, help text, startup banner, prompt templates rebranded
(templates re-encrypted)
Deletions (PRD removal list #5/#6/#7/#9/#10)
- voice input (xai-grok-voice) and all TUI wiring
- telemetry: Mixpanel client, external OTel stream, Sentry, OTLP layers,
trace/GCS/S3 upload queues (kigi-file-utils halved), workspace upload
module & dc_log, heap-profile uploader, auth-diagnostics uploader,
session-analytics halves of feedback; local zero-egress observability
preserved in new kigi-log crate (unified log, --debug firehose,
subsystem file logs, opt-in instrumentation)
- announcements (crate, remote-settings fields, TUI surfaces)
- plugin marketplace (crate, sources/browse/CTA/extensions-modal tab);
direct plugin install/uninstall/update via kigi-agent git_install kept
- relay/gateway/assets endpoints and features (agent relay, headless
relay transport, gateway bridge, LeaderEnvUrls); leader IPC socket now
~/.kigi/leader.sock + KIGI_LEADER_SOCKET, no ws-url derivation
- functional types rehomed instead of deleted: PermissionMode ->
kigi-config-types, McpInitStrategy -> kigi-mcp, PrCreationSource ->
session signals, TerminalDiagnostics -> kigi-pager-render, agent_id ->
shell util
Endpoints
- kigi-env rewritten: single production KigiEndpoints {coding_api_base_url
https://api.kimi.com/coding/v1 (KIGI_CODE_BASE_URL), oauth_host
https://auth.kimi.com (KIGI_OAUTH_HOST), update_base_url (GitHub
Releases API), upgrade_page_url}; GrokBuildEnvironment enum deleted
Toolchain & workspace hygiene
- Rust 1.97.0 pinned; edition 2024; full cargo update; git2 hoisted to
workspace at 0.21 (Option->Result API migration), quick-xml 0.41
- Root Cargo.toml hand-maintained (PRD §8.1): version 0.1.0 inherited by
all members, members sorted, unused deps pruned
- cargo-deny advisories gate (deny.toml with documented transitive
exceptions); CI workflow (check/clippy/fmt/deny/test, macOS+Linux)
- cross-crate test seams re-gated behind `test-support` cargo feature;
insta snapshot baselines renamed to the kigi_tui prefix
- clippy --workspace --all-targets: zero warnings; fmt clean
Fixes surfaced by the port
- updater probe/installer divergence (bin/kigi vs bin/grok symlink set)
- idle model-metadata refresh dead under KIGI_CODE_BASE_URL override
(new is_effective_coding_endpoint_url, loopback+override aware)
- macOS symlinked-TMPDIR fixture canonicalization (foreign_sessions,
fast-worktree); RSS measurement tests serialized via serial_test
Docs & legal (Apache §4)
- NOTICE added (upstream attribution + change statement); THIRD-PARTY
notices sustained; kigi-tools ported-code notices extended; README,
CONTRIBUTING, SECURITY, AGENTS.md rewritten
Out of scope for M0 (tracked): Kimi auth/inference (M1), search/fetch,
command parity, config import (M2), Computer Hub excision & final
brand-token sweep (M2), distribution & self-update rewrite (M3).
209 lines
7.6 KiB
Rust
209 lines
7.6 KiB
Rust
//! Closed enumeration of every JSON-RPC method on the wire.
|
|
//!
|
|
//! Each variant is defined once in the [`define_methods!`] macro invocation
|
|
//! together with its wire string. The macro generates the enum, serde
|
|
//! renames, [`Method::as_wire_str`], and [`Method::from_wire_str`] from
|
|
//! that single source of truth.
|
|
|
|
use serde::{Deserialize, Serialize};
|
|
|
|
macro_rules! define_methods {
|
|
(
|
|
$(
|
|
$(#[$var_attr:meta])*
|
|
$variant:ident => $wire:literal
|
|
),* $(,)?
|
|
) => {
|
|
/// Every JSON-RPC method understood by the computer hub.
|
|
///
|
|
/// The variants are grouped by direction in source order; the enum is
|
|
/// flat — direction enforcement is the computer hub's job, not the
|
|
/// protocol crate's.
|
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
|
pub enum Method {
|
|
$(
|
|
$(#[$var_attr])*
|
|
#[serde(rename = $wire)]
|
|
$variant,
|
|
)*
|
|
}
|
|
|
|
impl Method {
|
|
/// Every `Method` variant, for exhaustive iteration in tests.
|
|
pub const ALL: &'static [Method] = &[$(Self::$variant,)*];
|
|
|
|
/// Wire string for this method. Equivalent to the serde
|
|
/// serialization but without a round-trip through `serde_json`.
|
|
pub const fn as_wire_str(self) -> &'static str {
|
|
match self {
|
|
$(Self::$variant => $wire,)*
|
|
}
|
|
}
|
|
|
|
/// Inverse of [`Self::as_wire_str`]. Returns `None` for
|
|
/// strings that don't match any known method.
|
|
pub fn from_wire_str(s: &str) -> Option<Self> {
|
|
match s {
|
|
$($wire => Some(Self::$variant),)*
|
|
_ => None,
|
|
}
|
|
}
|
|
}
|
|
};
|
|
}
|
|
|
|
/// Message prefix the hub uses when rejecting a request whose `method`
|
|
/// string does not parse into [`Method`] — the shape an OLD hub produces
|
|
/// for verbs it predates. Current clients answer hub skew from the
|
|
/// `hello_ack` `capabilities` advertisement instead of sniffing this
|
|
/// message, but the shape stays pinned here: terminal binaries built
|
|
/// while the SDK still keyed old-hub detection on this exact prefix
|
|
/// remain in the fleet. Do not change casually.
|
|
pub const UNKNOWN_METHOD_MSG_PREFIX: &str = "unknown method `";
|
|
|
|
define_methods! {
|
|
// harness → service
|
|
SessionOpen => "session_open",
|
|
SessionClose => "session_close",
|
|
SessionBindServer => "session_bind_server",
|
|
SessionUnbindServer => "session_unbind_server",
|
|
/// Attach this harness connection to an EXISTING session as an
|
|
/// observer. Answered hub-locally from the session→tool-server
|
|
/// routing established by the owner's `session_bind_server` (or the
|
|
/// server's re-`serve`); never forwarded to the tool server.
|
|
SessionAttachServer => "session_attach_server",
|
|
ToolsList => "tools.list",
|
|
ToolsSearch => "tools.search",
|
|
ToolCall => "tool.call",
|
|
/// Sugar for [`Method::Hook`] with [`crate::HookEvent::Cancel`].
|
|
/// SDKs translate this method to a hook frame before sending; there
|
|
/// is no separate `tool.cancel` wire frame and no `ToolCancelParams`
|
|
/// struct in [`crate::frames`].
|
|
ToolCancel => "tool.cancel",
|
|
ToolNotify => "tool.notify",
|
|
SystemNotify => "system.notify",
|
|
SubscribeNotifications => "subscribe_notifications",
|
|
UnsubscribeNotifications => "unsubscribe_notifications",
|
|
Hook => "hook",
|
|
Hello => "hello",
|
|
HelloAck => "hello_ack",
|
|
Ping => "ping",
|
|
Pong => "pong",
|
|
|
|
// tool_server → service
|
|
ToolCallProgress => "tool_call_progress",
|
|
ToolNotification => "tool.notification",
|
|
/// Reply to a request/response hook, correlated back to the harness by `hook_id`.
|
|
HookReply => "hook_reply",
|
|
/// Notification (no `id`, no response); rejects surface only in
|
|
/// hub metrics. Only hub-minted trace-ids are accepted.
|
|
TracesDonate => "traces.donate",
|
|
/// Notification (no `id`, no response); rejects surface only in hub
|
|
/// metrics. Donor service.name must be hub-allowlisted.
|
|
LogsDonate => "logs.donate",
|
|
/// Notification (no `id`, no response); rejects surface only in hub
|
|
/// metrics. Donor service.name must be hub-allowlisted. No envelope
|
|
/// `session_id` — metrics are process-aggregate.
|
|
MetricsDonate => "metrics.donate",
|
|
|
|
// service → tool_server
|
|
ToolCallRequest => "tool_call_request",
|
|
|
|
// service → harness
|
|
ToolsChanged => "tools_changed",
|
|
SubscribeAck => "subscribe_ack",
|
|
UnsubscribeAck => "unsubscribe_ack",
|
|
|
|
// harness → service (server discovery)
|
|
/// List available tool servers for the authenticated user.
|
|
ServersList => "servers.list",
|
|
|
|
// tool_server status lifecycle
|
|
ToolServerStatus => "tool_server.status",
|
|
ToolServerGetStatus => "tool_server.get_status",
|
|
ToolServerEvict => "tool_server.evict",
|
|
|
|
// ── Session lifecycle ───────────────────────────────────────────
|
|
|
|
/// Full tool snapshot for a session (server → hub). Idempotent:
|
|
/// re-sending replaces the tool set; the hub diffs and emits
|
|
/// `tools_changed`.
|
|
Serve => "serve",
|
|
/// Hub requests the server to start serving a session
|
|
/// (hub → server). The server responds with its tool snapshot.
|
|
SessionBind => "session.bind",
|
|
/// Hub tells the server to stop serving a session
|
|
/// (hub → server). Notification — no response expected.
|
|
SessionUnbind => "session.unbind",
|
|
}
|
|
|
|
impl std::fmt::Display for Method {
|
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
f.write_str(self.as_wire_str())
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
|
|
#[test]
|
|
fn round_trip_as_wire_str_from_wire_str() {
|
|
let all = [
|
|
Method::SessionOpen,
|
|
Method::SessionClose,
|
|
Method::SessionBindServer,
|
|
Method::SessionUnbindServer,
|
|
Method::SessionAttachServer,
|
|
Method::ToolsList,
|
|
Method::ToolsSearch,
|
|
Method::ToolCall,
|
|
Method::ToolCancel,
|
|
Method::ToolNotify,
|
|
Method::SystemNotify,
|
|
Method::SubscribeNotifications,
|
|
Method::UnsubscribeNotifications,
|
|
Method::Hook,
|
|
Method::Hello,
|
|
Method::HelloAck,
|
|
Method::Ping,
|
|
Method::Pong,
|
|
Method::ToolCallProgress,
|
|
Method::ToolNotification,
|
|
Method::HookReply,
|
|
Method::TracesDonate,
|
|
Method::LogsDonate,
|
|
Method::MetricsDonate,
|
|
Method::ToolCallRequest,
|
|
Method::ToolsChanged,
|
|
Method::SubscribeAck,
|
|
Method::UnsubscribeAck,
|
|
Method::ServersList,
|
|
Method::ToolServerStatus,
|
|
Method::ToolServerGetStatus,
|
|
Method::ToolServerEvict,
|
|
Method::Serve,
|
|
Method::SessionBind,
|
|
Method::SessionUnbind,
|
|
];
|
|
for m in all {
|
|
assert_eq!(Method::from_wire_str(m.as_wire_str()), Some(m));
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn from_wire_str_returns_none_for_unknown() {
|
|
assert_eq!(Method::from_wire_str("not_a_method"), None);
|
|
assert_eq!(Method::from_wire_str(""), None);
|
|
}
|
|
|
|
#[test]
|
|
fn serde_round_trip_matches_wire_str() {
|
|
let method = Method::ToolCall;
|
|
let json = serde_json::to_value(method).expect("serialize");
|
|
assert_eq!(json.as_str(), Some("tool.call"));
|
|
let back: Method = serde_json::from_value(json).expect("deserialize");
|
|
assert_eq!(back, method);
|
|
}
|
|
}
|