Skip to content

Port upstream 0.69.0: Kimi stale CLI credential guidance (stacked on #610) - #691

Closed
Finesssee wants to merge 4 commits into
codex/integrate-reviewed-ports-20260923from
port/micro-0.69.0-kimi-stale-cli-guidance
Closed

Finesssee wants to merge 4 commits into
codex/integrate-reviewed-ports-20260923from
port/micro-0.69.0-kimi-stale-cli-guidance

Conversation

@Finesssee

@Finesssee Finesssee commented Sep 29, 2026 •

Copy link
Copy Markdown
Collaborator

Summary

In Auto mode, a Kimi Code CLI credential that is stale (expired or inside the 60 s safety margin) or rejected by the Code API (401) no longer disappears silently. Web auth still runs next. If web auth has no token to try (no manual token, and the Kimi Desktop session and browser import yield none, or Cookie Source is Off or Manual without a token), the fetch fails with:

Kimi Code CLI credential is invalid or expired. Run kimi to renew it, or add a Kimi Code API key in Settings > Providers > Kimi (KIMI_CODE_API_KEY). CodexBar does not refresh CLI-owned credentials.

This follows upstream's rule of reporting the last strategy that was available:

  • A web token that was sent and rejected keeps the web error.
  • Another CLI failure (timeout, HTTP 403, 5xx) is reported as is when web auth has no token.
  • A Code API 403 now reads "Kimi Code API returned status 403 Forbidden (permission or quota denied)" instead of a sign-in problem. This applies in every mode that uses the Code API (API key, CLI credential, OAuth).
  • A credential file with an empty access token but a refresh token counts as a stale sign-in, not as no sign-in.

Unchanged:

  • CLI credential files stay read-only (never refreshed, rewritten or given a device_id).
  • The message never contains token values.
  • A working CLI credential or web session still succeeds.

Implementation:

  • kimi/auto.rs (new): fetch_cli_then_web runs the CLI credential, then web auth. It picks the reported failure from WebFetchFailure::had_token. Both fetches are closures, so the order is unit-tested without network or settings.
  • kimi/web.rs
    • fetch_web_session returns WebFetchFailure { error, had_token }.
    • The manual, desktop and browser token chain is a generic fetch_with_web_tokens with an injected fetch. Its behavior is unchanged, except that the HTTP client is built at the first token.
    • fetch_via_web keeps its signature.
  • kimi/code_api.rs
    • kimi_code_cli_credential returns KimiCliCredential { Unavailable, Stale, Fresh(token) }. A refresh-only file is Stale.
    • code_api_status_error maps 401 and 403 the way upstream codeAPIError(statusCode:) does.
    • kimi_cli_credential_error() holds the guidance text.
  • kimi/mod.rs: the Auto path after the API-key attempt calls auto::fetch_cli_then_web.

Upstream reference

Ported / Deferred

Ported:

  • The unified guidance text.
  • Stale and rejected CLI credential handling with web fallback.
  • The last-available-error rule for the CLI and web strategies.
  • hasKimiCodeCredential (access or refresh token).
  • The Code API 401 and 403 mapping.
  • The read-only credential guarantee, and staleness at 14 minutes for a 15-minute token (60 s margin).
  • No token values in the message.
  • Tests mirroring KimiCLICredentialLifecycleTests: stale or rejected CLI falls back to web auth, CLI-only Auto explains renewal and the API key setting, the next read recovers after the CLI replaces its credential, and the 839/840/900 s boundary with file bytes unchanged.
  • KimiAPIErrorTests: guidance contents.

Deferred or different (not changed here):

  • Auto mode does not report an API-key failure when the CLI and web also fail. Upstream reports it as the last available error when the later strategies are unavailable. This is pre-existing.
  • The port falls back to web after any CLI failure. Upstream stops on invalidRequest (400) and parse failures. This is pre-existing.
  • There is no environment web token; upstream's web strategy also counts one.
  • No device_id is written for CLI requests (upstream mints one when it sends a request). The CLI home stays read-only.
  • The guidance is ProviderError::Other, so the provider state is Unknown rather than Needs authentication.
  • A non-string refresh_token fails the credential decode. Upstream reads it as "". The field type is pre-existing.
  • There is no fetch-plan attempts list (kimi.api, kimi.cli, kimi.web) to assert; kimi/auto.rs tests cover the same order and outcomes.
  • Upstream docs/kimi.md has no local counterpart; a CHANGELOG entry is added instead.

Validation

Lane A review at 73307e9 (toolchain 1.98.0, lane-a worktree, build gate):

  • cargo +1.98.0 fmt --all --check: clean
  • cargo +1.98.0 clippy --workspace --all-targets -- -D warnings: pass
  • cargo +1.98.0 test -p codexbar --lib kimi: 86 passed, 0 failed
  • cargo +1.98.0 test -p codexbar: 2254 passed, 0 failed, 1 ignored
  • cargo +1.98.0 test -p codexbar-desktop-tauri: 477 passed, 1 failed. The failure is bootstrap_payload_exposes_every_provider_variant, the known catalog-size drift (Isolate bootstrap payload test from real settings #684, fixed by Make the bootstrap catalog test hermetic (#684) #711); it is also on the base.
  • Mutation checks: the guidance shown whatever the web token, every CLI failure turned into guidance, a refresh-only credential treated as absent, the 403 arm removed, 403 mapped to AuthRequired, and had_token forced to false. Each fails at least one new test.

Review comment: #691 (comment)

Affected areas

  • Rust backend (rust/src/providers/kimi/: auto.rs (new), code_api.rs, mod.rs, web.rs)
  • Tauri shell
  • Frontend
  • Settings schema / bridge types
  • Changelog

No changed file crosses 1000 lines (mod.rs 883, code_api.rs 753, web.rs 722, auto.rs 262).

UI proof

Not applicable (backend only: error text and fetch order; no UI surface changed).

@coderabbitai

coderabbitai Bot commented Sep 29, 2026 •

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Advanced

Run ID: 548cb79f-458c-4907-8eee-d309b33df652

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
  • Autopilot · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Autopilot is currently an internal CodeRabbit preview.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@Finesssee

Copy link
Copy Markdown
Collaborator Author

Reviewed by Codex gpt-6-luna (xhigh); verified and validated by Claude

Thermo-nuclear review

Scope: kimi/code_api.rs, kimi/mod.rs, kimi/web.rs, CHANGELOG. Spec: 0.69.0 kimi-stale-cli-guidance (upstream steipete#4086 / release note steipete#4063).

Behavior check against spec: the Auto-mode web fallback is retained; the guidance message replaces the web error only when the CLI credential is stale or rejected (AuthRequired) and the web failure means "no web session" (AuthRequired, NoCookies, or Cookie Source off/manual without a token); transport, parse and HTTP errors still surface as-is; the credential file is only read (test asserts the bytes are unchanged); the message contains no token values. Provider logic stays in rust/src/providers/kimi/; no new dependencies; no locale, bridge or frontend surface touched; no file crosses 1000 lines (mod.rs 909, code_api.rs 849).

Findings:

# Severity Location Finding Fixed
1 Low rust/src/providers/kimi/code_api.rs (KimiCliCredential) The new enum derived Debug while Fresh(String) holds the CLI access token, so any {:?} of the state would log a secret (AGENTS.md: never log tokens). Yes. Replaced the derive with a manual Debug that prints Fresh([REDACTED]).

No other valid findings.

Validation (pinned Rust 1.98.0, E-cores): cargo fmt --all -- --check clean; clippy -D warnings clean on rust and apps/desktop-tauri/src-tauri; cargo test kimi 65 passed, 0 failed. Frontend and UI unchanged, so no vitest or CUA run was needed.

@Finesssee

Copy link
Copy Markdown
Collaborator Author

Fixes landed at current head 7d0742f

Finding 1 (redact the CLI token in KimiCliCredential Debug) is fixed in rust/src/providers/kimi/code_api.rs. Re-validated on Rust 1.98.0: fmt clean, clippy -D warnings clean on both crates, cargo test kimi 65 passed. Rust crate only; no frontend, bridge or UI change.

@Finesssee

Copy link
Copy Markdown
Collaborator Author

Lane A review: fixes at 73307e9

Reviewed the whole branch against its base (codex/integrate-reviewed-ports-20260923 at 15f1091) and against upstream v0.69.0 (steipete#4086, tag-pinned KimiAPIError.swift, KimiUsageFetcher.swift, KimiProviderDescriptor.swift, ProviderFetchPlan.swift, KimiCLICredentialLifecycleTests, KimiAPIErrorTests).

Upstream reports the error of the last strategy that was available. The web strategy is available only when it has a token to send (a manual override, an environment token, or a desktop or browser token while import is allowed). So the CLI guidance appears only when web auth had nothing to try.

Defects found at 7d0742f

  • A rejected web token was reported as CLI guidance. When the CLI credential was unusable, is_session_unavailable turned a web AuthRequired into "run kimi" even after a manual, desktop or browser token had been sent and rejected. Upstream keeps the web invalidToken error in that case.
  • A signed-in CLI with an empty access token was treated as signed out. A credential file with only a refresh token returned Unavailable, so no guidance was shown. Upstream hasKimiCodeCredential counts either token, and the fetch then throws expiredCodeCredential.
  • A Code API 403 was reported as a sign-in problem. fetch_via_code_api mapped 401 and 403 to AuthRequired, so a permission or quota denial on a fresh CLI token showed "run kimi to renew it". It also showed as Needs authentication in API-key and OAuth modes. Upstream codeAPIError(statusCode:) maps 401 to invalidAPIKey (invalidCodeCredential for the CLI) and 403 to apiError("HTTP 403 (permission or quota denied)").
  • Other CLI failures were hidden when web auth had no token. A CLI timeout or 403 was replaced by the web "no session" or "cookie source is Off" error. Upstream returns the CLI error as the last available error.

What changed (merge of the base at 15f1091, then 73307e9)

  • New kimi/auto.rs (262 lines). fetch_cli_then_web runs the Auto order after the API key: the CLI credential, then web auth.
    • Both fetches are passed in as closures, so the order is unit-tested without network or settings.
    • The CLI failure is reported only when web auth had no token. A CLI 401 becomes the renewal guidance (upstream normalizedCodeAPIError); any other CLI failure keeps its error.
  • kimi/web.rs
    • fetch_web_session returns WebFetchFailure { error, had_token }.
    • The token chain moved into fetch_with_web_tokens with an injected fetch. It behaves as before: manual token authoritative, desktop then browser, browser cookies read only after a desktop rejection, no duplicate token. The one difference is that the HTTP client is built at the first token instead of up front.
    • fetch_via_web keeps its signature. is_session_unavailable is removed.
  • kimi/code_api.rs
    • New code_api_status_error: 401 gives AuthRequired; 403 gives "Kimi Code API returned status 403 Forbidden (permission or quota denied)"; other statuses are unchanged.
    • kimi_code_cli_credential returns Stale for an empty access token next to a non-empty refresh token. The refresh token is read only for that check and never used to refresh.
    • The test helper writes upstream's credential shape (expires_in, scope, token_type).
  • kimi/mod.rs: the Auto path calls auto::fetch_cli_then_web. The API-key attempt is unchanged.
  • CHANGELOG: the Fixed line now covers the web-token rule and the 403 text.

Tests (all new)

  • auto.rs, following KimiCLICredentialLifecycleTests:
    • A stale or rejected CLI credential falls back to web auth (25%). Only the rejected case sends api-bad.
    • CLI-only Auto explains renewal and the API key setting, with no token value in the message.
    • A fresh CLI credential is used before web auth.
    • Also: a rejected web token keeps the web error; a CLI timeout or 403 is reported when web has no token; an unavailable CLI credential reports the web error.
  • web.rs, six token-chain tests: no token is reported as untried; a rejected manual token is authoritative; a rejected desktop session falls through to the browser; a healthy desktop session never reads browser cookies; a duplicate browser token is not resent; a non-auth error stops the chain.
  • code_api.rs:
    • The 401, 403, 400 and 500 mapping.
    • A refresh-only credential is Stale; empty or missing tokens are Unavailable.
    • The next read recovers after the CLI replaces its credential.
    • No device_id file and no X-Msh-Device-Id header for a CLI token.

Mutation checks (reverted afterwards). I applied six mutations together:

  • the guidance shown whatever the web token
  • every CLI failure turned into guidance
  • a refresh-only credential treated as absent
  • the 403 arm removed
  • 403 mapped to AuthRequired
  • had_token forced to false

Six new tests failed. Each mutation targets at least one of them (both 403 mutations target the exact-message status test).

Not changed here (pre-existing or outside steipete#4086; for follow-up)

  • An API-key failure is not reported when the CLI and web also fail. Upstream would report it as the last available error when the later strategies are unavailable.
  • The port always falls back to web after any CLI failure. Upstream stops on invalidRequest (400) and on parse failures.
  • No environment web token. Upstream's web strategy also counts one.
  • No device_id is written for CLI requests (upstream mints one when it sends a request). The CLI home stays read-only.
  • The guidance is ProviderError::Other, so the provider state is Unknown rather than Needs authentication. This is unchanged from the branch.
  • There is no attempts list to assert (kimi.api, kimi.cli, kimi.web); auto.rs covers the same order.
  • Upstream docs/kimi.md has no local counterpart.
  • A non-string refresh_token fails the whole credential decode. Upstream reads it as "". The field type is pre-existing.

Commands and results (toolchain 1.98.0, run in the lane-a worktree through the build gate)

  • cargo +1.98.0 fmt --all --check: clean
  • cargo +1.98.0 clippy --workspace --all-targets -- -D warnings: pass
  • cargo +1.98.0 test -p codexbar --lib kimi: 86 passed, 0 failed
  • cargo +1.98.0 test -p codexbar: 2254 passed, 0 failed, 1 ignored
  • cargo +1.98.0 test -p codexbar-desktop-tauri: 477 passed, 1 failed. The failure is bootstrap_payload_exposes_every_provider_variant, the known catalog-size drift (Isolate bootstrap payload test from real settings #684, fixed by Make the bootstrap catalog test hermetic (#684) #711). It is also on the base, and this PR does not touch the provider catalog.

No frontend, locale or bridge change, and no new dependencies. Changed files stay under 1000 lines (mod.rs 883, code_api.rs 753, web.rs 722, auto.rs 262). Pushed as a fast-forward (7d0742f..73307e9). This is backend only, so no UI proof is needed.

@Finesssee

Copy link
Copy Markdown
Collaborator Author

Shipped in v0.70.0: this PR's head is included in main via #735 (merge commit 9d0a37a). Closing as integrated.

@Finesssee Finesssee closed this Oct 3, 2026
junglesub-bot Bot pushed a commit to junglesub/Win-CodexBar that referenced this pull request Oct 4, 2026
… guidance (stacked on nesszer#610)

# Conflicts:
#	CHANGELOG.md
junglesub-bot Bot pushed a commit to junglesub/Win-CodexBar that referenced this pull request Oct 4, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant