Commit Graph
24 Commits
Author SHA1 Message Date
雷电芽衣 791f953c07 feat(login): custom provider rows, URL / name / key steps, and custom:<name> authenticate
The picker lists Custom provider (OpenAI compatible) and (Anthropic compatible).
The TUI asks for a base URL, a name (default from the host), and a key, saves the
definition and the key, and the agent validates the key against GET {base}/models.
PTY e2e covers both wires against the mock inference server.
2026-09-29 16:10:41 -04:00
雷电芽衣 d1c29a576c fix(compaction): failures carry the real error, with endpoint credentials redacted
The pager rendered "Compaction failed." with the error dropped, and the
shell's unsuppressed auto-compaction failure sent an empty error. Both
now carry the decoded first line of the failure (retry guidance plus the
detail), decoded from the ACP data rather than its JSON-escaped Display,
propagated unchanged through the compact extension, capped for a banner.

Endpoint credentials never reach the banner: the sampler redacts the
parsed endpoint (userinfo, sensitive query values, fragment via the
repository sanitizer) in every 401 message and the appended request URL,
and the shell redacts every URL in the error text before line selection,
including URLs whose password holds whitespace or that follow a
multi-byte delimiter.

Ported from upstream 1.0.13 (grok-build dispatch/prompt.rs,
session/compaction.rs), extended with the redaction.

Claude-Session: https://claude.ai/code/session_01RUqV8HVGXcAETyYbZ2tPQ4
2026-09-13 12:12:00 -04:00
雷电芽衣 584d0fb77e fix(tui): /compact <instructions> reach the compaction request
The queued /compact command drained into Effect::Compact with only the
session id, so the instructions typed after the command never reached the
shell's compaction prompt (which already accepts userContext). The effect
carries the parsed instructions, trimmed so an edited queue row with
leading whitespace still counts.

Ported from upstream 1.0.22 (grok-build dispatch/queue.rs).

Claude-Session: https://claude.ai/code/session_01RUqV8HVGXcAETyYbZ2tPQ4
2026-09-13 09:23:39 -04:00
雷电芽衣 4512df79e6 fix(tui): terminal escapes ride the writer queue and teardown is bounded
A terminal that stopped draining the pty parked the writer thread inside
its tty write while it held the stderr lock; the next inline stderr write
from the event loop (mouse toggle, notification, kitty clear) deadlocked
the loop, and /quit hung on the unbounded writer join and the teardown
writes.

EscapeWriter queues out-of-band escapes on the frame writer: the mouse
capture toggle, notifications (title, progress, protocol), the auth
cursor, gboom keyboard flags, the wrap clipboard request, and the kitty
clears (queued per agent view and merged into the frame's post-flush,
nested subagent views included). The $EDITOR/$PAGER handoff aborts when
the writer does not drain. WriterThread::join_within and the teardown
run under a 2 s grace; after a stall fd 2 is silenced, the exit hints,
relaunch and update prints are skipped, and the panic hook keeps only its
log report.

Ported from upstream 1.0.19 (grok-build EscapeWriter, WriterJoin,
run_bounded_teardown), extended to the remaining inline writes.

Claude-Session: https://claude.ai/code/session_01RUqV8HVGXcAETyYbZ2tPQ4
2026-09-13 09:08:09 -04:00
雷电芽衣 b957e12370 fix(tui): repaint the header context window on a /model switch
After `/model`, the header kept the previous model's window ("15K /
1.0M" on a 262K model) until the next turn or a restart. The pager
caches the window inside its context snapshot (streaming `totalTokens`
updates refresh it from the catalog) and nothing refreshed the cache
when the session's model changed. Comparing cached numbers against
catalog windows cannot tell a catalog-derived total from a shell
effective limit (KIGI_DEBUG_CONTEXT_WINDOW, header upgrades), so the
shell, which installs the effective window, now reports it:

- kigi-shell: `handle_set_session_model` returns the installed window;
  `model_switch::apply` stamps it as `totalContextTokens` in the
  `session/set_model` response meta and carries it on the
  `ModelChanged` broadcast (`context_window`, serde-default: old pagers
  ignore it, old leaders omit it). The response meta previously
  serialized the actor's inner `Result` and reported success on an
  actor failure; that error now propagates.
- kigi-tui: `Effect::SwitchModel` reads the meta; `SwitchModelComplete`
  carries `Ok(Option<u64>)`; the completion handler and the follower
  `ModelChanged` arm call `AgentView::apply_context_window`, which
  re-derives the snapshot (pct, free tokens) and reports whether it
  changed so a follower redraws on a window-only change.
- Tests: the bare `/model <name>` dispatch path asserts the snapshot and
  the drawn header; follower with/without the field; same-model new
  window redraw; actor returns the window; serde round trip.

Claude-Session: https://claude.ai/code/session_01RUqV8HVGXcAETyYbZ2tPQ4
2026-09-13 00:15:14 -04:00
ZacharyZhang-NY f656627c10 fix(tui): start the session a sessionless model switch defers into
Launching kigi in a non-project directory (~/Downloads, ~, /tmp — anywhere
`is_project_dir` rejects) leaves the agent view session-less behind the
project-picker question, which only a PLAIN prompt can open. Slash
commands still execute there, so `/model <name> <effort>` stashed its
switch in `deferred_model_switch` — a stash that assumes a create is in
flight — and with none pending it dangled forever with zero feedback:
the picker rendered, the user chose model and effort, and nothing
changed. Not a 0.1.10 regression: 0.1.9 pty-reproduces identically; the
report correlated with the upgrade only because every earlier launch
happened to be from a project directory.

Apply the QueueCommand precedent ("queued slash work bypasses the
picker, so create the deferred session or it never drains") to both
deferral sites — Action::SwitchModel in the router and
set_default_model's no-session branch — via
skip_picker_and_create_session, whose in-flight guard already makes it a
no-op while a create is pending, so the racing-create case is unchanged.
SessionCreated then applies the stash through the existing
apply_deferred_model_switch path; pty-verified end-to-end from
~/Downloads (session created, model changed, effort applied).
2026-07-27 15:55:57 -04:00
ZacharyZhang-NY a02b555e66 docs(comments): rewrite comments across all crates to the guidelines
Sweep every first-party crate source (1956 .rs files) to the project comment
guidelines: delete redundant restatements, decorative banners, change
narration, and end-of-line comments; keep and tighten the crucial ones
(invariants, bug rationale, SAFETY blocks, ported-source attribution).

No functional code changed. Every edit is proven comment-only against the
prior tree by a comment-stripping lexer (string/char/raw-string aware) plus a
separate doctest-fence check. Where removing a comment made rustfmt or clippy
want to re-lay-out adjacent code, the minimal triggering comment is restored so
code tokens stay byte-identical.

Gates green: cargo fmt --all --check (0 diffs), cargo check and cargo clippy
--workspace --all-targets (0 warnings).

Adds scripts/check_codegen_comment_guidelines.py — the enforcement gate for
these guidelines (flags banners, end-of-line comments, change narration, and
commented-out code).
2026-07-23 16:55:39 -04:00
ZacharyZhang-NY f403c38a94 fix(tui): a default-model persist failure no longer reverts the live model
Persist-failure ≠ switch-failure. The default_model rollback arm re-ran
set_default_model_inner(prev) AND issued a reverse SwitchModel whenever
the config.toml write failed — the only mechanism in the codebase that
deliberately re-shows the ORIGINAL model after a successful pick. On
Windows, where AV/indexer file locks routinely failed that write (until
27d009c), every /model selection appeared to not take.

The session switch succeeds independently and reports its own failures
via handle_switch_model_complete; a disk-persist failure now keeps the
live model, logs, and surfaces only the 'Could not save' toast — the
same policy PersistPreferredModel already ships ('still active for this
session').

Verified: kigi-tui 6874 tests green, clippy clean.
2026-07-22 19:55:02 -04:00
ZacharyZhang-NY 66d5194ee3 feat(tui): /model + Enter opens the model picker instead of erroring
A required-args slash command submitted bare (/model, or /m) ran straight
into its usage error. The completeness contract already existed —
is_command_complete's documented 'Blocks' row — but had zero non-test
callers; both dispatch paths ran the command unconditionally.

Both the session prompt path and the dashboard dispatch box now consult
is_command_complete before running: an incomplete required-args command
re-opens the input as '/{command} ' with the cursor in the args phase, so
the existing suggestion dropdown lists the choices (for /model: the
catalog — by construction the connected providers' models, after
1bb10ef). Generalizes to every future required-args command; no new UI.

Verified: kigi-tui 6872 tests green, clippy clean.
2026-07-22 17:28:58 -04:00
ZacharyZhang-NY fc7c2a1b9b feat(login): /login opens the provider picker with green connected badges
/login previously fired the resolved method's flow immediately — there was
no way to see providers or their status. It now lands on the provider
picker; already-connected providers show a green 'connected' badge in the
key column.

Shell: initialize() probes stored credentials once (primary session flag,
auth.json oauth/<provider> scopes, resolved platform keys env>auth.json>
config) and stamps _meta.connected on each advertised method
(connected_method_ids + stamp_connected_meta, pure and unit-tested).
Display state only — never an authorization input.

TUI:
- PendingMenuItem::connected() reads the badge from method meta; the
  picker renders it green (accent_success), replacing the shortcut hint.
- New Action::OpenLoginPicker: /login shows the picker; mid-session it
  stashes the view like dispatch_login, and starts no flow by itself.
- Mid-session picker: last row reads 'Cancel' and dispatches CancelLogin
  (clicking it must not exit the app); Esc also returns to the session.
- After a successful login, the just-authenticated method is stamped
  connected in the TUI's advertised-methods copy (auth_in_flight_method →
  AuthComplete), so a later /login shows the badge without re-initialize.

Verified: kigi-shell 5256 + kigi-tui 6870 tests green, clippy clean.
2026-07-22 16:14:42 -04:00
ZacharyZhang-NY 1e57cd9225 fix(tui): route each OAuth picker row to its own provider, not kimi.com
Root cause: PendingMenuItem::Login carried only a label, so every provider
row collapsed to the id-less Action::Login, and dispatch_login resolved
the FIRST advertised interactive method — the Kimi device flow. Selecting
Grok/Claude/Copilot/Codex all opened kimi.com. (The shell side was already
correct: authenticate() dispatches each method id to its own OAuth flow.)

- PendingMenuItem::Login now carries the advertised method id (None only
  on the no-interactive-method fallback row).
- New Action::LoginWith(AuthMethodId); the picker dispatches it with the
  row's own id. Action::Login keeps its meaning (resolved/default method)
  for /login, auto-login, and re-auth.
- dispatch_login_with resolves the id against the advertised methods and
  FAILS CLOSED on an unknown id — no silent first-method fallback — then
  adopts the method's label and start mode.

Tests: picker rows pinned to their method ids; LoginWith(claude-pro-max)
must authenticate with claude-pro-max even when kimi-code was previously
resolved; unknown ids surface an error and start nothing.

Verified: kigi-tui 6867 tests green, clippy clean.
2026-07-22 15:51:46 -04:00
ZacharyZhang-NY c2d1067f0d fix(tui): login picker scrolls in a capped viewport; the moon never clips
Root cause (issues: unscrollable list + half-blocked moon): the stacked
welcome layout gave the menu one Length row per item. With ~30 advertised
providers the vertical layout overflowed and the constraint solver
squeezed every Length — clipping the logo — while rows past the fold were
silently unreachable.

- WelcomeLayout::compute_inner caps the menu to the rows genuinely left
  over after logo/prompt/version, so the chrome never gets squeezed.
- render_menu is now a minimal-scroll viewport: it scrolls only when the
  selection exits it (stable under hover — no re-centering feedback
  loop), draws the picker-style scrollbar, and returns index-aligned hit
  rects (zero Rect for off-screen rows) plus the offset, which AppView
  feeds back next frame.
- Mouse wheel moves the login-picker selection (clamped, not wrapping).

Test: pending_menu_scrolls_and_never_clips_the_moon renders the full
method list at 40 rows and asserts the whole moon (10 braille rows), the
version badge, and that selecting Quit scrolls it into view.

Verified: kigi-tui 6864 tests green, clippy clean.
2026-07-22 15:31:18 -04:00
ZacharyZhang-NY 83e6935189 Split canonical ReasoningEffort::Max out of Xhigh (providers P0c-1)
OpenAI (Responses) and Anthropic (Messages) treat xhigh and max as
DISTINCT effort levels in 2026, and the Kimi K3 wire's top tier is max —
the old parse alias (max→Xhigh) conflated them. Canonical Max now exists:
parse/as_str/serde split, Messages mapping sends xhigh and max as their
own tokens (was Xhigh→"max"), and the K3 menu token max carries
canonical Max end to end.

Kimi wire is byte-identical in all four flows (menu pick, restored
legacy xhigh session, --reasoning-effort flag, /effort command) —
adversarially traced and pinned: kimi_compat's string-level xhigh→max
rename covers legacy tokens, max passes through verbatim.

From the review:
- Rollback safety: persisted reasoning_effort (session summaries, chat
  history) deserializes leniently — unknown future tokens degrade to
  None with a warning instead of hiding sessions or failing resume.
- Restore migration: a pre-split xhigh override onto a model whose menu
  offers max but not xhigh (K3) migrates once, healing display/active-row
  drift and re-persisting the live vocabulary.
- /effort max now rejects (with the offered list) on models whose menu
  lacks a max row instead of silently applying xhigh; deliberate, tested.
- The interim Responses-backend Max→xhigh downgrade (async-openai has no
  Max variant through 0.41) warns loudly; real max wiring lands with the
  OpenAI provider cycle via post-serialize body patch.
- Two rusted ignored-e2e wire pins asserted the pre-adapt reasoning_effort
  key (deleted by the body adapter since ea0ce9d); they now pin the real
  thinking.effort=max shape.
2026-07-21 02:36:37 -04:00
ZacharyZhang-NY 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.
2026-07-21 01:18:46 -04:00
ZacharyZhang-NY 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.
2026-07-18 12:31:52 -04:00
ZacharyZhang-NY 0dc98a34a6 /feedback opens the Kigi GitHub issues page
Feedback about an unofficial community build belongs on its own issue
tracker, not Moonshot's feedback endpoint — and this mirrors the
official kimi-cli, whose /feedback opens its repo's issues page
(ISSUE_URL in ui/shell/slash.py). /feedback now returns
Action::OpenUrl(https://github.com/ZacharyZhang-NY/Kigi-CLI/issues),
the same battle-tested browser path /docs web uses.

The now-dead TUI text-feedback pipeline is excised: PromptInputMode::
Feedback (~ prefix composer mode), Action::{EnterFeedbackMode,
SendFeedback}, Effect::SendFeedback (the kigi/feedback ACP POST),
TaskResult::{FeedbackComplete,FeedbackFailed}, and their dispatchers.
The shell-side kigi/feedback ACP extension stays: it is protocol
surface for editor embeddings, OAuth-gated, and shared with kigi/btw.

Gates: workspace check/clippy --all-targets 0/0, fmt, kigi-tui lib
6621 passed / 0 failed.
2026-07-18 11:51:13 -04:00
ZacharyZhang-NY 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.
2026-07-18 04:02:39 -04:00
ZacharyZhang-NY 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.
2026-07-18 02:48:46 -04:00
ZacharyZhang-NY 5e4e24db99 M2 audit: excise managed connectors and xAI media-gen tools
Managed connectors (grok.com MCP admin) removed root-and-branch:
- The managed-MCP fetch/injection pipeline is gone, including the whole
  kigi-shell-session-support crate (managed-config fetch client, gateway
  tool catalog + dispatch, header injection, refresh task), reactive
  managed re-auth, mcp_doctor's grok.com-source discovery, and the
  [managed_mcps] config surface.
- TUI: the 'Managed by grok.com' section, connectors URL/deep-link,
  Action::OpenManagedConnectors, and session_team_id are gone. Local MCP
  management (list/toggle/add/remove/auth/tools) is fully intact.
- Kept as LOCAL policy: managed-settings.json MCP allow/deny enforcement,
  the multi-source local MCP merge, folder-trust gating. PluginOrigin
  Project/User labels kept (they tag locally discovered plugin dirs).

imagine/media-gen tools (xAI image/video generation) removed:
- image_gen, image_edit, video_gen, image_to_video, reference_to_video
  implementations, registrations, ToolKind/ToolInput/Output variants
  (serde-safe), config plumbing end to end, ZDR video machinery,
  /imagine + /imagine-video commands and guidance text, the bundled
  imagine skill (added to legacy cleanup so user installs delete it),
  and the media-gen render path.
- Kept: image INPUT (paste/attach, [Image #N] meta, pdf/image fetch,
  clipboard wrap), generic media-ref rendering, and the generic tool
  401-retry machinery (tests renamed, assertions unweakened).
- deploy_app stays: it is a permanently-disabled local stub deploying
  nowhere.

121 files changed, 8 deleted. Gates: workspace check/clippy 0/0, fmt,
deny ok; suites green (tools 2554, shell 4862, tui 6608, workspace
1042). Remaining grok.com strings live only in the auth-method ids and
changelog archives (§9/M3 sweep).
2026-07-17 23:45:05 -04:00
ZacharyZhang-NY 74b210535e F5: web search/fetch on the Kimi services (kimi-cli parity)
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.
2026-07-17 19:33:19 -04:00
ZacharyZhang-NY 5406fb7d68 Rebrand remaining user-visible Grok strings to Kigi/Kimi
The welcome hero subtitle still read "Thanks for trying Grok Build";
theme display names, the NO_COLOR/COLORTERM diagnostics notes, the
permission notification title, the self-update restart hints, and the
clipboard-wrap protocol marker all still said Grok. Canonical theme keys
(groknight/grokday) are unchanged — only display strings move.
2026-07-17 18:22:00 -04:00
ZacharyZhang-NY 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"
2026-07-17 16:05:51 -04:00
ZacharyZhang-NY 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.
2026-07-17 07:37:29 -04:00
ZacharyZhang-NY 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).
2026-07-17 05:31:01 -04:00