Removed root-and-branch for the zero-egress guarantee (the hub was xAI's
remote-workspace/cloud-sandbox service):
- Crates deleted: kigi-computer-hub-core, kigi-computer-hub-sdk,
kigi-computer-hub-mcp-adapter, kigi-workspace-client (hub-proxied
workspace RPC client), and kigi-tracing (its sole network path was the
OTLP gRPC exporter; zero consumers remained). kigi-tracing-macros
(purely local) stays.
- kigi-workspace: every hub surface deleted — hub server/channel/auth,
HITL-over-hub permissions, donation/metrics pumps, file upload RPCs,
hub tool-snapshot merge (resolve pipeline is MCP-only now),
WorkspaceOps::Proxy. Local worktrees, sessions, leader IPC, MCP, and
the ACP permission prompt path are untouched; LocalRegistry re-homed
into kigi-tool-runtime on the existing ToolDyn types so in-process
tool dispatch is unchanged.
- kigi-shell: leader workspace-exposure control surface (incl. the
wss://computer-hub... URL), [hub] config, ObservabilityBridge, hub
WebSocket proxy, dead OTLP config knobs. ClientMode::Headless (never
constructed) removed.
- kigi-tui/bin: hidden `kigi workspace` command removed (`kigi
worktree` stays).
- Renames: --xai-api-base-url → --api-base-url / KIGI_API_BASE_URL /
[endpoints] api_base_url (serde alias keeps old configs working; the
flag feeds BYOK/custom-endpoint routing, not main inference);
grok_version → kigi_version in inspect/models-cache/trace metadata
(old caches self-heal via version-mismatch refetch).
- Dependency tree: dropped fastrace*, opentelemetry-otlp/http/proto,
tokio-tungstenite from the workspace; fixed the 4 real useless_format
violations the fastrace lint allowance was masking and removed the
allowance.
- marketplaceAllowlist kept: it gates the LOCAL plugin-marketplace
feature, not an xAI service.
Known §9 leftover (deliberate, for the M3 sweep): the BYOK default base
URL string. Gates: workspace check/clippy 0/0, fmt, deny ok; suites
green (workspace 1042, shell 4918, tui 6634, tools 2608, tool-runtime
47, mcp 154).
The welcome and prompt model labels (and /effort's 'current' hint)
rendered the canonical internal level name, so K3 at its default effort
showed 'K3 (xhigh)' even though the server's vocabulary for that level is
'max' (live /models think_efforts: low/high/max).
New ModelState::reasoning_effort_display() resolves the current effort
through the model's own effort menu (option id whose value matches),
falling back to the canonical name only when the model has no entry for
the level. All three display sites route through it; test pins the K3
mapping (Xhigh → 'max', Low → 'low', no-menu-entry → canonical).
New `kigi import-kimi` (with --dry-run) plus a one-time welcome-screen
hint when ~/.kimi/config.toml is present and unimported.
- Sources: ~/.kimi/config.toml (default_model, [models.*], [providers.*])
and ~/.kimi/mcp.json ({"mcpServers": ...} — parsed by the existing
McpConfig machinery). Shapes ported from kimi-cli 1.49.0 config.py with
citations.
- Strictly read-only over ~/.kimi: plain reads only; the test suite pins
byte-identical contents AND unchanged mtimes across scan+apply. Keyring
credentials are never imported; KIMI_SHARE_DIR / any KIMI_* env var is
never consulted (official dir hardcoded to ~/.kimi).
- Mapping: MCP servers merge into kigi [mcp_servers.*] without clobbering
existing names; models on non-built-in providers become kigi [model.*]
custom entries (base_url + api_key + context_window); providers that
duplicate kigi's built-in Kimi/Moonshot platforms are skipped with a
note; default_model maps to the imported alias or the managed catalog
key and never overwrites an existing default.
- One-time marker ~/.kigi/kimi_import_done (claude-import convention);
the startup hint and re-runs no-op once set. api_key values flow only
into the user's own config.toml and are redacted in every summary.
Verified end-to-end with the real binary in a sandboxed home: dry-run,
apply, `kigi mcp list` shows both imported servers, second run no-ops,
~/.kimi mtimes unchanged. 8 unit tests + CLI parse test.
- `kigi acp`: top-level alias for the stdio ACP server (kimi-cli `acp`).
AgentArgs.mode is now optional — bare `kigi agent` and `kigi acp` both
default to stdio at dispatch; `kigi acp <mode>` is rejected.
- `kigi mcp auth <name>`: authorize an OAuth-enabled remote MCP server
(kimi-cli `mcp auth`). Reuses the doctor's interactive connection path:
starts the named server with OauthInteractivity::Interactive (browser
flow when required), completes the handshake, and reports the tool
count. Stdio servers and unknown names fail with actionable errors.
- `--mcp-config-file <PATH>` (repeatable, global): extra MCP config files
in the .mcp.json shape ({"mcpServers": {...}}), kimi-cli semantics.
Files are validated at parse time (fail fast on unreadable/invalid
JSON), carried across the TUI -> shell boundary via
KIGI_MCP_CONFIG_FILES, and merged at HIGHEST priority — an explicitly
passed file overrides every config scope. Covered by loader unit tests
and verified live: an injected server surfaces in the session's
x.ai/mcp/servers_updated notification via both the flag and the env.
The stale bare-agent-requires-mode CLI test is re-contracted to the new
default-to-stdio behavior.
web_search now speaks the Kimi search service (kimi-cli tools/web/search.py,
wire-verified against api.kimi.com):
- POST {coding_base}/search with {text_query, limit 1-20 (default 5),
enable_page_crawling, timeout_seconds: 30}, OAuth bearer +
X-Msh-Tool-Call-Id; results render in kimi-cli's Title/Date/URL/Summary
schema with result URLs as citations.
- The old implementation called the xAI Responses API with a search model;
that client is fully replaced and the entire model-based config surface
is excised root-and-branch: web_search_model config keys/env/CLI plumbing,
resolve_web_search_sampling_config, toolset web_search SamplerConfig,
RemoteSettings.web_search_model, default_web_search_model.
- Enablement is now purely structural: the service exists only on the Kimi
Code subscription channel, so OAuth sessions get Enabled and API-key-only
sessions get Disabled (tool absent) — per PRD F5.
web_fetch gains the Kimi fetch service as its primary path (kimi-cli
tools/web/fetch.py): POST {coding_base}/fetch with {url}, Accept:
text/markdown, OAuth bearer + X-Msh-Tool-Call-Id; the 200 body is the
extracted markdown (still overflow-budgeted). Any service failure falls
back to the existing local pipeline (SSRF guards, cache, extraction).
The tool gate defaults ON now (kimi-cli always offers FetchURL) and the
egress User-Agent no longer claims grok-agent/x.ai.
Verified end-to-end against the scripted mock service: a headless session
drove web_search (limit/crawling/call-id observed on the wire) then
web_fetch (Accept + call-id observed) to completion.
The headless not-signed-in message still pointed at `grok login` and
XAI_API_KEY; error copy across the shell (re-auth prompts, managed-config
rejection, mcp doctor, trace classifier) and kigi-bin did too. All now
reference `kigi login` / `kigi logout`, and the headless fallback points
at the Moonshot open-platform key (KIGI_MOONSHOT_API_KEY).
Root cause of today's repeated logouts: the keyring entry (service kigi /
oauth/kimi-code) is global per OS user, but the enable gate keyed off ENV
VARS while an AuthManager's identity is its constructor path. Integration
test binaries (compiled without cfg(test), no KIGI_SHARE_DIR in env)
constructed managers on tempdirs whose remove_scope() then deleted the
developer's REAL keychain credential — the unified log shows 13 such
wipes in one day, one per test run.
Structural fix: keyring participation is now a property of the manager's
own path. AuthManager captures keyring_path_scoped at construction
(path == default ~/.kigi/auth.json) and every keyring touch — the
constructor read, update()'s write, remove_scope()'s delete — requires it,
with the dynamic keyring_enabled() gate (env kill-switch, cfg(test) mock
toggle) layered on top. A tempdir-rooted manager can no longer read,
write, or delete the global entry no matter what process type it runs in.
Regression test tempdir_manager_never_touches_global_keyring pins the
incident: a foreign manager's logout must leave the (mock) keyring entry
intact. Keyring behavior tests keep constructor-read coverage via a
thread-local path-scope test seam.
The live /models wire (verified against api.kimi.com) marks every Kimi
Code model supports_thinking_type: "only" and gives K3 a think_efforts
block {support, valid_efforts: [low, high, max], default_effort: max} —
both of which the F4 sync discarded, hardcoding
supports_reasoning_effort: false. Every effort selection was therefore
rejected with 'current model does not support reasoning effort'.
- kigi-models: WireModel gains supports_thinking_type + WireThinkEfforts;
"only" forces the always_thinking capability.
- models_fetch: think_efforts maps into the catalog entry — wire tokens
stay the option ids/labels (max/Max), canonical values map via the
ReasoningEffort parser (max → Xhigh), default_effort marks the default.
This lights up the existing /model <model> [effort] two-phase completion
and the /effort menu with the server's own vocabulary.
- kimi_compat: the effort level rides the wire as thinking.effort
({"type": "enabled", "effort": "low"} is accepted live; invalid
levels are a 400). Only the canonical-vs-wire spelling divergence
(xhigh → max) is renamed; levels pass through verbatim so a contract
violation surfaces instead of being clamped away.
Live acceptance: kigi -m kimi-code/k3 --reasoning-effort max -p ... round
trips against api.kimi.com, and the refreshed models_cache.json carries
low/high/max with the max default.
Sampler / inference (PRD F3):
- kimi_compat.rs: single adaptation point for the Kimi chat/completions
dialect (thinking-field mapping, model_id stripping, empty-content
tool-call message fix, stream_options.include_usage), with kimi-cli
source citations
- Rate-limit handling reworked for Kimi/Moonshot semantics; UA kigi/{version}
- /models replaces the xAI models-v2 endpoint everywhere; idle model
refresh carries X-Msh-* device headers only (X-XAI-Token-Auth and
x-grok-client-mode/CLIENT_MODE_HEADER machinery deleted)
Cloud-surface excision (PRD §5, zero-egress):
- remote/ conversations lane, cli-chat-proxy-types crate, prod/ dir,
share command, credit bar: deleted (single local session lane;
paginate() replaces merge_and_paginate)
- Subscription/tier gate stack deleted end-to-end: AppView
gate/tier/team/ZDR fields, app/subscription.rs watch loop,
dispatch/billing.rs paywall + SuperGrok upsell, free-usage-exhausted
chain, tier-restricted commands, GateInfo, RemoteSettings gate fields,
SettingsUpdateNotification gate fields
- /privacy + coding-data-sharing setting deleted (backed by a dead xAI
RPC; Kigi is zero-egress — nothing to share or retain remotely)
Auth UX correctness (user-reported):
- Device-flow fixtures now mirror the live Kimi payload shape
(https://www.kimi.com/code/authorize_device?user_code=..., verified
against auth.kimi.com); the fabricated auth.kimi.com/device?code=...
URLs are gone
- open_browser_detached is a no-op under cfg(test): unit tests drove
wiremock fixture URLs into the real browser (root cause of the
"garbage mock link" ABCD-1234 tabs)
- Welcome/pager-minimal rebrand: Grok Build -> Kigi, grok.com ->
kimi.com, "Sign in to Grok" -> "Sign in to Kimi"
F2 — fixed three-platform registry in kigi-models: kimi-code
(subscription, OAuth bearer, base kigi_env::coding_api_base_url()),
moonshot-cn (https://api.moonshot.cn/v1), moonshot-ai
(https://api.moonshot.ai/v1) with kimi-k model-prefix filtering.
Moonshot API keys via KIGI_MOONSHOT_CN_API_KEY / KIGI_MOONSHOT_AI_API_KEY
(+ KIGI_MOONSHOT_API_KEY shared fallback) or ~/.kigi/config.toml;
values redacted from logs/display.
F4 — model catalog now syncs from GET {base}/models (Bearer auth,
wire shape per official kimi-cli: id/context_length/supports_reasoning/
supports_image_in/supports_video_in/display_name) with the official
capability-derivation rules (thinking / always_thinking-in-name /
kimi-k2 implicit set). Managed keys {platform_id}/{model_id}; default
model = first list entry; default thinking iff capabilities contain
thinking/always_thinking. Sync failure → last cache; no cache →
built-in fallback table seeded from ids sourced in official kimi-cli
(kimi-for-coding, kimi-k2-turbo-preview, kimi-k2-thinking-turbo).
401 during sync forces one token refresh and retries.
Model resolution priority preserved: CLI > env > config > server >
fallback. Grok model artifacts (grok-4*/grok-build catalog, tier
gating remnants) removed from non-test code.
All first-party endpoints re-verified live: device_authorization mints
real codes; /models on all three platforms answers with real API auth
errors when unauthenticated.
Gates: check/clippy --all-targets 0/0, fmt clean, deny ok,
kigi-shell lib 5136 green, kigi-tui lib 6819 green, kigi-models 8.