f825132983ffd5741a7433de883d6718d54df8c1
18
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
f825132983 |
Add Cerebras platform + generalize StrictOpenAi dialect (provider 10)
The 13th registry row: id "cerebras", CEREBRAS_API_KEY > auth.json "cerebras" scope, https://api.cerebras.ai/v1 with KIGI_CEREBRAS_BASE_URL override, Bearer, ChatCompletions, enrichment-backed metadata (models_dev_id cerebras). Cerebras' catalog is all chat LLMs (no embedding/tts pollution) and its /models is minimal (ids only), so restrict_to_enriched=FALSE: keep every live model, enrich the known ones (context + effort menus low/medium/high), unknown ones keep the default context. The e2e pins this enrich-without- restrict path (new — prior enrichment providers all used restrict=true). Review caught a likely-DOA defect: Cerebras uses strict additionalProperties:false validation (confirmed 400-rejecting store, maxTokens, thinking, nested reasoning_content), and stream_options is not in its schema — so Passthrough (which keeps the stream_options.include_usage kigi injects on every streaming request) would very likely 400 all streaming. Generalized ChatCompat::Mistral -> ChatCompat::StrictOpenAi (serde alias "mistral" keeps pre-rename persisted sessions loading), which strips stream_options + private fields for any strict OpenAI-compat validator; both Mistral and Cerebras now map to it. Future strict-validator candidates (NVIDIA/Azure/Xiaomi/OpenCode) noted for the same check. reasoning_effort (incl. "none") passes through; /v1/models requires auth so key validation works; console cloud.cerebras.ai. |
||
|
|
0a147bd8a5 |
Add Together AI platform + bare-array listing tolerance (provider 9)
The 12th registry row: id "together", TOGETHER_API_KEY > auth.json "together" scope, https://api.together.xyz/v1 with KIGI_TOGETHER_BASE_URL override, Bearer, ChatCompletions + Passthrough, enrichment-backed metadata (models_dev_id togetherai) with the tool-calling listing restriction (Together's listing mixes chat/embedding/rerank/image types). Together's GET /v1/models returns a BARE JSON ARRAY, not the OpenAI {object:list,data:[]} envelope. New shared parser parse_openai_listing tolerates both shapes (sniffs the top-level [ vs { for accurate diagnostics); every OpenAI-listing provider now routes through it, with byte-equivalent envelope behavior (verified: Groq/Google/OpenRouter unchanged) and non-silent errors. Together's org/Model ids match the togetherai snapshot keys exactly (verified), so restrict_to_enriched keeps the ~27 tool-calling models without the id-shape trap. Review: ship-ready, shared-parser change proven strictly-additive and safe, registry/e2e/dialect all correct. Backlog logged: kigi ignores the wire per-model field, so a live Together chat model absent from models.dev is dropped until indexed — a future wire- filter would unlock the fresh full catalog. |
||
|
|
e7dbb98207 |
Add OpenRouter platform: wire-served metadata (provider 8)
The 11th registry row and the first THIRD-PARTY wire_serves_metadata=true provider: id "openrouter", OPENROUTER_API_KEY > auth.json "openrouter" scope, https://openrouter.ai/api/v1 (note /api/v1) with KIGI_OPENROUTER_BASE_URL override, Bearer, OpenAI listing + ChatCompletions + Passthrough. OpenRouter's public /models serves context_length for every model (verified live: 340/340), so it needs NO enrichment: models_dev_id=None, wire_serves_metadata=true, restrict_to_enriched=false. An OpenRouter-only user makes zero models.dev calls; context comes straight from the listing. Slashed ids (anthropic/claude-opus-4.8) round-trip through the managed key via the first-slash split; the native id rides the wire. The e2e pins all of this with the models.dev refresh disabled. Review-confirmed defect fixed (and independently re-verified with live curls): OpenRouter's /models is PUBLIC — GET /models returns 200 for ANY key — so login key-validation would false-accept a bad key, deferring the failure to the first chat 401. New spec field key_validation_path lets a public-listing platform validate against an auth-requiring endpoint; OpenRouter uses /key (401s for bad keys). Reusable for Vercel (also public). Tests pin the /key validation and no regression to the default /models path. Gate caught a fixture regression: the kimi_import test used openrouter.ai to represent a CUSTOM provider, which now correctly dedupes to the builtin OpenRouter — moved the fixture to a reserved llm.example.test host that no future platform can shadow. |
||
|
|
7a2cd8a726 |
Add Google Gemini platform via OpenAI-compat endpoint (provider 7)
The 10th registry row: id "google", GEMINI_API_KEY > auth.json "google" scope, https://generativelanguage.googleapis.com/v1beta/openai (Gemini's OpenAI-compatibility shim) with KIGI_GOOGLE_BASE_URL override, Bearer, OpenAI listing + ChatCompletions + Passthrough, enrichment-backed metadata (models_dev_id google), tool-calling listing restriction. Review caught a ship-blocking defect: Gemini's compat /models returns -PREFIXED ids (confirmed via Google's own cookbook), but the models.dev snapshot keys and the chat endpoint use the BARE id. Without normalization the enrichment lookup misses and restrict_to_enriched silently empties the Gemini catalog (login works, zero models selectable). A doc WebFetch had hidden this — the compat docs show bare INPUT ids to retrieve/chat but never print the list OUTPUT. Fix: new spec field strip_listing_id_prefix, applied in the fetch before filter/enrich/keying. Google sets Some("models/") (defensive: a no-op if an id is already bare, so correct regardless of the live shape); all other rows None. The e2e now feeds the REAL prefixed listing ids and asserts they survive as the bare managed key google/gemini-2.5-pro with the bare id on the wire (chat rejects the prefix); a registry test pins the config. Note: models.dev models Gemini reasoning as budget_tokens (not effort-type), so no auto effort-menu — models still fully work; reasoning is dynamic. Gemini compat applies default safety filters (no BLOCK_NONE). |
||
|
|
e373ff8b29 |
Add Fireworks AI platform (provider 6)
The 9th registry row, pure Groq pattern: id "fireworks", FIREWORKS_API_KEY > auth.json "fireworks" scope, https://api.fireworks.ai/inference/v1 (note /inference/v1) with KIGI_FIREWORKS_BASE_URL override, OpenAI listing + ChatCompletions + Passthrough dialect, enrichment-backed metadata (models_dev_id fireworks-ai) with the tool-calling listing restriction. Fireworks native ids are deeply slashed (accounts/fireworks/models/glm-5p2); the e2e pins the full round-trip: the managed key (fireworks/accounts/fireworks/models/glm-5p2) parses back on the first slash, and — the 404-risk property — the NATIVE id rides the inference wire (entry.model and the resolved SamplerConfig.model) while the fireworks/ prefix stays internal routing only. Review (models.dev provider.toml + Fireworks docs): row facts confirmed, registry integrity at 9, counts complete, e2e strong on all axes, zero defects. One tradeoff logged as debt: restrict_to_enriched drops fine-tuned/account-scoped deployed models (a headline Fireworks feature) that can never be in models.dev. |
||
|
|
9953a26b8d |
Add Mistral platform + Mistral dialect + array-content handling (provider 5)
The 8th registry row: id "mistral", MISTRAL_API_KEY > auth.json "mistral" scope, https://api.mistral.ai/v1 with KIGI_MISTRAL_BASE_URL override, enrichment-backed metadata with the tool-calling listing restriction (embed/moderation/OCR noise). Mistral is NOT a pure-pattern provider — an adversarial review found two doc-confirmed blockers that no test exercises (no e2e covers a chat POST), so the gate-green registry row alone would have shipped it DOA. A research workflow pinned the exact wire shapes against the mistralai/client-python SDK source (adversarially verified), then both were fixed: 1. stream_options 422: Mistral's strict Pydantic validator rejects the stream_options.include_usage field kigi injects on every streaming request (the SDK's request model has no such field). New ChatCompat::Mistral dialect strips it (plus the kigi-private message fields, like Passthrough). Streaming usage falls back to token estimation. 2. Reasoning content arrays: Mistral reasoning models return content as Union[str, List[ContentChunk]] on both streaming and non-streaming, which the flat Option<String> path could not decode -> aborted turn. A UNIVERSAL lenient deserializer (#[serde(from = "Raw..")] on ChatResponseMessage + ChatChunkDelta) accepts string-or-array, routing {type:text} chunks to the answer and the nested text of {type:thinking} chunks to reasoning_content, tolerant of the OPEN chunk union (unknown types ignored, never fatal). String content stays byte-identical for every other provider (kimi/deepseek/groq/BYOK). Review refuted all seven attack lines (no regression, no crash, exhaustive) and flagged one coverage gap, now closed: a stream-consumer integration test drives a full thinking -> transition -> answer chunk sequence and proves it yields the same reasoning-sibling + assistant-answer result as the reasoning_content string path. Also folds a verified quirk matrix for all 23 remaining API providers into providers-plan.md, tiered by real difficulty (self-enriching OpenRouter/ Vercel; bare-array Together listing; Messages-dialect MiniMax reusing the Anthropic machinery; non-Bearer Azure/Bedrock; router wildcards; the OAuth block). |
||
|
|
49e6414c29 |
Add Groq platform (provider 4)
The 7th registry row and the first pure-pattern cycle: id "groq", GROQ_API_KEY > auth.json "groq" scope, https://api.groq.com/openai/v1 with KIGI_GROQ_BASE_URL override, OpenAI listing + ChatCompletions + Passthrough dialect (Groq accepts the OpenAI-style reasoning_effort scalar verbatim), enrichment-backed metadata with the tool-calling restriction (the listing carries whisper/tts/guard noise — 8 of 15 enrichment entries are non-chat). Review verdict: faithful pattern repeat, zero blocking findings — row facts verified against live Groq docs, registry integrity at 7, e2e proven strong on both axes (restriction + dialect mapping). Added the recommended pin: managed keys split on the FIRST slash, so Groq's provider-native slashed ids (openai/gpt-oss-120b and 10 more) round-trip as groq/openai/gpt-oss-120b → (Groq, openai/gpt-oss-120b). Also fixes a pre-existing test race this cycle surfaced: the enterprise- endpoints test asserted moonshot's fixed base while non-serial, racing serial tests that legitimately point KIGI_MOONSHOT_CN_BASE_URL at wiremock; now serial + env-unset like its documented siblings. Two display-only advisories logged as tracked debt (Groq's delta.reasoning field invisible in the TUI; platform-generic rate-limit copy). |
||
|
|
7efb4b07cc |
Add DeepSeek platform + ChatCompletions dialect system (provider 3)
The 6th registry row: id "deepseek", DEEPSEEK_API_KEY > auth.json "deepseek" scope, base https://api.deepseek.com (chat rides {base}/chat/completions per official docs) with KIGI_DEEPSEEK_BASE_URL override, enrichment-backed metadata (1M context, 384k output cap, high/max effort menu). Structural fix the cycle exposed: kigi's Kimi-specific body adaptation ran UNCONDITIONALLY on every ChatCompletions request. New ChatCompat dialect, declared per platform row and threaded through SamplerConfig, ClientDefaults, and the session-persisted SamplingConfig (serde-default Kimi keeps restored pre-field sessions and BYOK endpoints byte-identical; production persist seams copy it; subagents inherit it): - Kimi: full legacy pipeline (dispatch ≡ legacy pinned) - DeepSeek: thinking:{type, reasoning_effort} per api-docs.deepseek.com (server maps low/medium→high, xhigh→max itself; none disables; absent leaves the server default) - Passthrough: OpenAI-style reasoning_effort scalar untouched (unblocks Groq and the rest of the OpenAI-compatible list) Review-confirmed release blocker fixed: kigi replays Kimi's reasoning_content (and its private model_id) on input assistant messages — Kimi consumes these, but DeepSeek documents input reasoning_content as prefix-mode-only (historically a 400) and other providers don't know either field. The DeepSeek and Passthrough arms now strip both; Kimi's own pipeline is untouched. Pinned on both message shapes. |
||
|
|
b86722f508 |
Add Anthropic platform: wire-served metadata via listing dialect (provider 2)
The 5th registry row: id "anthropic", ANTHROPIC_API_KEY > auth.json "anthropic" scope, api.anthropic.com/v1 with KIGI_ANTHROPIC_BASE_URL override, Messages dialect. Two new spec dimensions most future rows reuse: ListingDialect (Anthropic's /v1/models wants x-api-key + anthropic-version headers, ?limit=1000, and its own response shape) and PlatformKeyHeader (Bearer vs x-api-key across listing/validation/ inference, with auth_scheme stamped onto entries). The 2026 Anthropic listing serves real metadata: the adapter maps max_input_tokens, per-level effort capabilities (low..max as the menu, xhigh/max distinct), thinking/image flags — and enrichment fills only genuine wire gaps (e2e pins wire-1M beating enrichment, and a zero context filled to 200k). Two review-confirmed defects fixed red-green: - Output caps were dropped at three layers, so every sub-128K-output model (64k Haiku, legacy models) would 400 on EVERY request against the sampler's 128K max_tokens default. Wire max_tokens and enrichment limit.output now flow to entry.max_completion_tokens. - An explicit wire effort-decline was indistinguishable from wire silence, letting enrichment inject effort menus pre-4.6 models reject (adaptive thinking 400). The adapter now emits a decline sentinel (support:false) that enrichment respects — proven end to end. Also: the Messages client now sends anthropic-version (previously never sent — real api.anthropic.com rejects such requests; pinned across all three scheme/backend quadrants), key validation builds per-key-header requests, missing listing data fails fast, empty-id ghosts drop with a warning, kimi-import recognizes api.anthropic.com as built-in automatically. |
||
|
|
23e94939c0 |
Add OpenAI platform: live model fetching with enrichment (provider 1)
The 4th registry row: id "openai", OPENAI_API_KEY env > auth.json "openai" scope (login picker/paste/validation all registry-generic — zero TUI changes needed, pinned by the picker test), base https://api.openai.com/v1 with KIGI_OPENAI_BASE_URL override, Responses dialect via the new PlatformWireApi spec field, enrichment-backed metadata (wire_serves_metadata=false). OpenAI's GET /v1/models returns bare ids and is polluted with tts/whisper/embeddings entries: the listing is restricted to enrichment-known TOOL-CALLING models (review caught that membership alone admitted models.dev-known embeddings models, which would 400 on every agentic request; dropped ids are debug-logged for launch-day diagnosability). Context windows, effort menus, display names, and thinking capability come from the enrichment pipeline — wiremock e2e pins the full contract: polluted live listing + models.dev → one Responses-backed chat model with a 400k documented context window. Responses max-effort wiring (closes the P0c-1 debt): canonical effort rides a CreateResponseWrapper sidecar and patch_reasoning_effort writes it onto the serialized body at both send sites (all seven levels pinned, xhigh/max distinct, summary preserved); normalize_effort_echo drops echoes async-openai's typed enum cannot represent at both the non-stream and SSE parse seams; the dead typed to_responses_api converter is deleted. Kimi/moonshot stay byte-identical (ChatCompletions untouched, wire_api maps to the same default; kimi wire tests green). kimi-import now recognizes ANY registry platform host as built-in (was hardcoded moonshot), covering openai and future rows. |
||
|
|
c5ddaec71e |
Add per-provider auth.json keys; make auth methods registry-generic (P0b)
Platform API keys now live in auth.json under the platform-id scope (the per-provider auth.json key contract), resolved env > auth.json > legacy [platforms.*] config.toml (read-only fallback). The TUI login picker, paste box, auth-method advertising, and authenticate handler are all registry-generic: a new PlatformSpec row appears in the login UI and authenticates with zero UI changes. Spec rows gained vendor/console_host/ login_label display fields (moonshot strings byte-identical, pinned by tests). Adversarial review caught that auth.json keys were validated at login but never stamped onto catalog entries (completions would 401; restart lost eager auth). Fixed red-green: resolve_model_list/resolve_model_catalog now take a resolved PlatformApiKeys snapshot consumed by the credential- stamping layer (auth.json beats stale config.toml, matching the login validator), with production callers resolving fresh per catalog build. Also from review: the new auth.json writer takes the manager's cross- process flock (bounded retry — an unlocked RMW racing a token refresh could revert a rotated refresh token); the oauth-401 wiremock test is hermetic (KIGI_SHARE_DIR tempdir; it could read a dev's real auth.json and hit live moonshot); cli_models resolves real keys; auth.json is read once per registry sweep; caller-less lock_config_writes deleted; catalog resolvers tightened to pub(crate); stale config.toml doc comments and the no-credentials error copy updated. |
||
|
|
10d0c0816f |
Excise the Changelog feature
Kigi never publishes CDN changelogs, so the entire inherited feature
was dead weight: the welcome-menu Changelog row, the hero-box info
slot (bullets + clickable CTA), /release-notes with its /changelog
alias, and the ChangelogManager CDN-fetch/disk-cache pipeline
(Effect::FetchChangelog, TaskResult::ChangelogFetched, startup and
post-login fetch kickoffs, AppView cache fields, mouse hover/click
handling). Welcome menu is now [Import] / New worktree / Resume
session / Quit; the hero box keeps title + version + subtitle and its
layout math simplifies to 3 + menu rows (verified equivalent by the
surviving boundary tests). Action::ShowReleaseNotes and the DocViewer
modal stay — /docs uses them. builtin.rs keeps deleting stale
CHANGELOG.{json,md} caches written by kigi ≤ 0.1.0.
kigi-shell-base drops its reqwest 'blocking' feature (only the deleted
module used it). -1206 lines net.
Gates: fmt clean, workspace check/clippy --all-targets 0/0, kigi-tui
lib 6609 / shell-base 56 / shell util:: 258 all passing, welcome pty
e2e (3 tests incl. braille logo) passing.
|
||
|
|
3952c28f16 |
Login picker: offer all three platforms like the official CLI
The unauthenticated welcome screen previously offered only 'Login with
Kimi Code'. It now lists every interactive platform the shell
advertises, matching the official kimi-cli picker:
Kimi Code (OAuth)
Moonshot Open Platform (API key · moonshot.cn)
Moonshot Open Platform (API key · moonshot.ai)
Quit
- Shell: new ACP auth methods moonshot-cn / moonshot-ai (advertised
after kimi-code; the BYOK first-position invariant holds).
authenticate(moonshot-*) reloads keys from env>config, fails with an
actionable message when none is configured, validates the key against
GET {base}/models (401 → 'invalid API key' naming the console), then
swaps the fresh config in, triggers the model sync, and reports
auth_mode api_key so the pager treats it like other API-key logins.
Never session-based; keys never logged.
- Config: save_platform_api_key persists [platforms.<id>].api_key via an
atomic mode-preserving write under the config lock; refuses OAuth
platforms, blank keys, and unparseable files.
- TUI: the Pending welcome renders the picker rows from the advertised
methods (arrows/Enter/mouse; 'l' keeps selecting the OAuth row).
Choosing a Moonshot row opens a masked paste box ('Paste your Moonshot
API key (from platform.moonshot.cn)'); Esc returns to the picker,
Enter persists the key and authenticates in one sequential effect;
failures return to the picker with the error line, success lands on
the normal welcome. Startup eager-auth is unchanged: a key already in
the environment authenticates exactly as before, and single-method
shells keep the historical auto-device-flow.
Gates: workspace check/clippy 0/0; shell 4870 + tui 6620 lib tests
green; headless probe advertises [kimi-code, moonshot-cn, moonshot-ai]
for a fresh user and xai.api_key-first with a key configured.
|
||
|
|
6f31415ed6 |
§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. |
||
|
|
5919526e91 |
Show reasoning effort in the model's own vocabulary (K3: max, not xhigh)
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). |
||
|
|
ea0ce9d15f |
F3: Kimi inference pipeline + full grok cloud-surface excision
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"
|
||
|
|
021b82443d |
M1/F1: Kimi Code OAuth device-code flow
Replace the xAI OAuth stack with the Kimi device authorization grant:
- kimi_oauth.rs wire layer (device_authorization + token poll + refresh
against kigi_env::oauth_host(); client_id per PRD; retryable statuses
429/5xx with backoff; expired_token restarts authorization)
- X-Msh-Device-{Name,Model,Id} headers; device_id minted uuid4-hex at
~/.kigi/device_id (0600)
- Storage: system keyring service `kigi`, entry `oauth/kimi-code`
(macOS/Windows native backends), atomic-file fallback under ~/.kigi;
official client's keyring/~/.kimi never touched
- Refresh manager: 60s tick, threshold max(300, expires_in*0.5),
401-tombstone keyed by rejected refresh token with 300s cooldown and
rotation auto-clear, cross-process lock with sibling-adoption
triple-check, sleep/wake forced refresh
- Deleted xAI machinery: enterprise OIDC (PKCE/JWKS/teams), devbox login,
external auth provider, JWT tier gating + subscription paywall stack,
X-XAI-Token-Auth marker headers, ZDR gates, /user enrichment
- kigi login / TUI /login both drive the device flow; login-host display
now derives from kigi_env::oauth_host()
- 264 auth unit/wiremock tests; live contract probe of
auth.kimi.com/api/oauth/device_authorization matches the wire shapes
Gates: check/clippy --all-targets clean, fmt, deny ok, kigi-shell lib
5131 tests green.
|
||
|
|
d6c20fc13f |
M0: compilable skeleton — Kigi 0.1.0 fork surgery
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).
|