fix(kimi-web): migrate to www.kimi.com Connect-RPC API (kimi.moonshot.cn retired) - #5858
Merged
diegosouzapw merged 6 commits intoJul 2, 2026
Conversation
Contributor
|
Warning You have reached your daily quota limit. Please wait up to 24 hours and I will start processing your requests again! |
…auth JWT The legacy `kimi.moonshot.cn` consumer chat domain was retired in favor of the international `www.kimi.com`. As part of that move the session probe shifted from a cookie-based scheme on `.moonshot.cn` to a JWT bearer scheme: the SPA stores the access JWT in the `kimi-auth` cookie and sends it as `Authorization: Bearer <jwt>` on every API request. The new profile endpoint is `GET /api/user` and returns the user object at the top level (`{ id, name, email, region, ... }`). This commit adds a specialty validator (`validateKimiWebProvider`) that extracts `kimi-auth` from whatever the user pasted (bare JWT, full Cookie header, or `Authorization: Bearer ...` form) and probes `/api/user` to confirm the session. Mirrors the qwen-web validator pattern. The dispatcher in `src/lib/providers/validation.ts` is updated to route `kimi-web` through the new validator instead of falling through to the generic OpenAI-compatible model-list probe (which was hitting a redirect on the retired `.cn` host).
… API The legacy `kimi.moonshot.cn/api/chat` REST endpoint is gone: the domain 307-redirects every non-CN visitor to `https://www.kimi.com/`, which serves a completely different chat API based on Connect-RPC over HTTP. New wire format (verified end-to-end against a live Plus-tier session): - Endpoint: `POST /apiv2/kimi.gateway.chat.v1.ChatService/Chat` - Auth: `Authorization: Bearer <jwt>` + `Cookie: kimi-auth=<jwt>` - Body: Connect streaming envelope ΓÇö 5-byte header (1 byte flags + 4 bytes big-endian length) wrapping the JSON request payload. Without the envelope the upstream returns `invalid_argument` for every request regardless of credentials. - Response: a stream of Connect-framed JSON events with one of: op=set, mask=block.text ΓåÆ first answer chunk op=append, mask=block.text.content ΓåÆ answer delta op=set, mask=block.think ΓåÆ first reasoning chunk op=append, mask=block.think.content ΓåÆ reasoning delta plus heartbeats and metadata events that are suppressed. The new executor: - Parses the pasted Cookie header to extract `kimi-auth` (or accepts a bare JWT / `Authorization: Bearer` form). - Frames the request body and parses the Connect event stream. - Translates `block.think.content` deltas to OpenAI `reasoning_content` and `block.text.content` deltas to OpenAI `content` (SSE for streaming, buffered JSON for non-streaming). - Sends only the `kimi-auth` cookie back rather than replaying the whole pasted Cookie header (avoids leaking analytics cookies like _ga, _gcl_au, __cf_bm, HM*, etc.). The `x-msh-*` / `x-traffic-id` / `x-msh-shield-data` headers the SPA sends are NOT required ΓÇö verified by stripping them one at a time against a live session; the upstream returns the same response either way. Registry, metadata, and docs are updated to point at the new domain and document the new auth hint. The i18n description strings (en, zh-CN, ru) are updated; other locales were already in the `__MISSING__` fallback state and will pick up the English source via the existing pipeline.
Replaces the old `kimi.moonshot.cn` SSE-passthrough test (which no longer reflects the wire format) with tests that pin: - the executor now targets `https://www.kimi.com/...ChatService/Chat` and explicitly NOT `moonshot.cn` - a missing/empty credential is rejected with 400 before fetch fires - `extractKimiJwt` accepts bare JWT, `kimi-auth=<jwt>` inside a full Cookie header, `Cookie:` / `Authorization: Bearer` prefixed forms, and rejects inputs with no JWT Updates `web-cookie-providers-new.test.ts` so the existing kimi-web execution tests feed a fake `kimi-auth` credential (the executor now requires one before fetching) and assert against the international URL.
…guard, dedupe parser) Six follow-ups from code review on PR diegosouzapw#5858: 1. i18n regression (I1). The original commit updated only en/zh-CN/ru with the new "Moonshot AI consumer chat via www.kimi.com" description; the PR claimed the other 39 locales would fall back via the runtime pipeline, but `deepMergeFallback` only uses EN when the locale value is `undefined`. The 39 locales carried a literal `__MISSING__:Chinese market AI chat via kimi.moonshot.cn` string that would have rendered verbatim. Now the same 14 locales that previously had a `__MISSING__:` placeholder are updated to the new English source under `__MISSING__:` so the runtime coverage counter still flags them as missing translations while showing the correct text via the `__MISSING__:` fallback consumers. 2. Decoder tests (I3). The Connect frame decoder is the riskiest piece of the migration and was unverified. New `executor-kimi-web-decoder` test suite pins: - frameConnectMessage / decodeConnectFrame round-trip - partial-frame buffering (need-more-bytes path) - two-frame splitting (consumed offset semantics) - oversized-frame rejection (consumed=-1 sentinel) - sign-correction for lengths with bit 31 set - non-JSON payload handling - extractDelta across set/append x text/think, plus suppression of heartbeats and unrelated masks - isEndOfStream on assistant COMPLETED vs other roles/statuses - foldMessages with system/user/assistant/tool/function scenarios, including the documented single-turn limitation 3. DoS guard on Connect frame length (I4). Added MAX_FRAME_LEN (8 MiB) ceiling with a ponytail: comment naming the limit and upgrade path. decodeConnectFrame returns consumed=-1 when a header claims more, and both caller paths (streaming + non-streaming) treat -1 as stream-fatal ΓÇö streaming errors the controller, non-streaming stops accumulating and returns what it has. 4. reasoning_effort short-circuit (I5). A user sending `model: "kimi-k2.6"` + `reasoning_effort: "none"` previously still got thinking because the model-id regex beat the explicit override. Now reasoning_effort: "none" wins unconditionally. 5. foldMessages JSDoc limitation (I6). Documented that tool/function messages are dropped and that assistant tool_calls/image parts are stringified ΓÇö agentic flows should use the `kimi-coding` provider. 6. Dedupe JWT parser (M1+M2). extractKimiJwt moved to `src/lib/providers/webCookieAuth.ts` (alongside extractQwenToken), imported by both the executor and the validator. Eliminates the duplication that had drifted: the validator was missing the `bearer <jwt>` fallback the executor had, so a `Bearer eyJ...` paste would have executed but failed validation.
janeza2
force-pushed
the
fix/kimi-web-migrate-to-international
branch
from
July 1, 2026 22:17
5a00303 to
37e4681
Compare
…eb-migrate-to-international Regenerated docs/reference/PROVIDER_REFERENCE.md after resolving the merge conflict (scripts/docs/gen-provider-reference.ts). Co-authored-by: diegosouzapw <diegosouza.pw@gmail.com>
The unit test suite imports extractKimiJwt via mod.extractKimiJwt (destructured from the executor module), but the executor only imported it from webCookieAuth without re-exporting it, leaving 5 test failures (TypeError: extractKimiJwt is not a function). Co-authored-by: diegosouzapw <diegosouza.pw@gmail.com>
Owner
|
Thanks @janeza2! Best PR of this review round — the Connect-RPC decoder is defensive (sign-extension, partial frames, 8MiB cap) and superbly tested. We resolved the release-branch conflicts, regenerated PROVIDER_REFERENCE, and added a one-line |
Merged
3 of 4 tasks
This was referenced Jul 2, 2026
Merged
tkgo11
pushed a commit
to tkgo11/OmniRoute
that referenced
this pull request
Sep 23, 2026
….cn retired) (diegosouzapw#5858) Integrated into release/v3.8.43
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
kimi-webis completely broken: the legacykimi.moonshot.cnconsumer chat domain was retired and now 307-redirects every non-CN visitor tohttps://www.kimi.com/, which speaks a completely different API. This PR repoints the provider at the international domain and reimplements the executor against the new wire format.The earlier issue #5813 sketched the migration; this PR is the working implementation, validated end-to-end against a live Plus-tier overseas session.
New wire format (verified live)
kimi.moonshot.cn)www.kimi.com)POST /api/chatPOST /apiv2/kimi.gateway.chat.v1.ChatService/Chatapplication/connect+json)kimi_tokenAuthorization: Bearer …+Cookie: kimi-auth=<jwt>invalid_argumentfor every request.op+maskResponse event schema
The executor translates
block.think.contentdeltas to OpenAIreasoning_contentandblock.text.contentdeltas to OpenAIcontent(SSE for streaming, buffered JSON for non-streaming).Auth handling
Users paste the full Cookie header from www.kimi.com. The executor's
extractKimiJwtaccepts:kimi-auth=<jwt>inside a full Cookie headerCookie:orAuthorization: Bearerprefixed formsOnly
kimi-authis replayed back to the upstream — analytics cookies (_ga,_gcl_au,__cf_bm, HM*, …) are dropped.Headers — what is and isn't required
Tested against a live session by stripping headers one at a time:
Authorization: Bearer <jwt>Cookie: kimi-auth=<jwt>connect-protocol-version: 1Content-Type: application/connect+jsonUnsupportedMediaType)invalid_argument)x-msh-platform/x-msh-versionx-msh-device-id/x-msh-session-id/x-traffic-idx-msh-shield-datax-language/r-timezoneThe 4 "test matrix" runs (full headers / no
bx-ua/ nox-msh-*at all / minimal set) all produced the same Connect-framed event stream with model output (1+1=2).Commits
validateKimiWebProviderprobingGET /api/user; registered in the dispatcher sokimi-webno longer falls through to the generic OpenAI-compatible probe that hit the retired.cnhost.KimiWebExecutorfor Connect-RPC; updatesbaseUrl, website/authHint, i18n strings (en, zh-CN, ru),tokenExtractionConfig, provider reference. Other locales were already in__MISSING__fallback state and pick up the English source via the existing pipeline.www.kimi.comand explicitly!moonshot.cn), the missing-JWT 400 short-circuit, and theextractKimiJwtparser across all accepted input forms. Updates the existing web-cookie-sweep tests to feed akimi-authcredential and assert the international URL.Test plan
npm run typecheck:core— passes (only pre-existing unrelatedappConfig.tserrors)ReadLints— clean on all changed files (the two pre-existingnode:test/duckduckgo-webtype errors inweb-cookie-providers-new.test.ts/webSessionCredentials.tsexist onupstream/mainalready)www.kimi.com— chat completion returns the correct Connect-framed stream with reasoning + answer (1+1=2)x-msh-*headers — confirmed not requiredtest-unit— needs upstream CI to run (local checkout lacks thecross-env/tsxruntime used bynpm run test:unit)Notes for reviewers
{ kimi-default, kimi-k2.6, kimi-128k }. Thescenariovalue is pinned toSCENARIO_K2D5(the only one the SPA sends today); if Kimi ships a new scenario we'll need to revisit.kimi-authJWT is now treated as a long-lived bearer token. Expiry handling relies on the existing web-cookie refresh path — if the SPA rotates the JWT on the next login, the user re-pastes a fresh Cookie header (same UX as qwen-web).