jeo-code 0.12.3 → 0.12.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -9,6 +9,30 @@ The README mirrors the latest 5 entries — regenerate with `bun run changelog:s
9
9
 
10
10
  ## [Unreleased]
11
11
 
12
+ ## [0.12.5] - 2026-10-17
13
+ _Antigravity and Kimi provider support in interactive setup, Kimi live model cache rehydration, immediate discovery caching on provider probing._
14
+
15
+ ### Added
16
+ - **Interactive Setup Provider Options** (`src/commands/setup.ts`, `src/commands/setup-helpers.ts`) — added `antigravity` (Google Cloud Code Assist OAuth) and `kimi` (Moonshot API key / Kimi Code OAuth) to interactive setup provider selection, enabling browser OAuth, bearer token paste, or API key configuration with default models (`antigravity/gemini-3-flash`, `kimi-k2.5`). Updated `buildEnabledProviders` to recognize configured `kimi` credentials.
17
+ - **Kimi Live Model Cache Rehydration** (`src/ai/model-cache.ts`) — cache hits for Kimi OAuth (`provider === 'kimi' && source === 'oauth'`) now rehydrate `recordLiveKimiCodeModels(entry.models)` alongside OpenAI Codex models.
18
+ - **Immediate Model Cache on Provider Probe** (`src/commands/provider.ts`) — successful `jeo provider test <id>` and `jeo provider add` probing now immediately writes discovered custom/compatible models to the disk cache (`writeModelCache([result])`).
19
+
20
+ ### Fixed
21
+ - **Model Cache Key Scoping** (`src/ai/model-cache.ts`) — fixed `keyOf` helper scoping and included `kimi` in source distinction so OAuth and API key catalogs do not clobber each other.
22
+ - **OAuth Token Preservation in Setup** (`src/commands/setup.ts`) — fixed `delete next.oauth` condition to check `Object.keys(next.oauth).length === 0` so non-Anthropic/OpenAI/Gemini OAuth credentials (such as `antigravity` or `kimi`) are not inadvertently dropped on save.
23
+ - **Auth Import Model Reporting** (`src/commands/auth.ts`) — added `antigravity` reporting to `reportLiveModels` following Gemini OAuth import.
24
+
25
+ ## [0.12.4] - 2026-10-17
26
+ _Refresh Antigravity provider (`antigravity/*`) account catalogs with `antigravity/hub/2.23.1` so entitled Gemini 4 Argon and Gemini 3.8 Flash tiers appear directly in `/model provider antigravity`._
27
+
28
+ ### Fixed
29
+ - **Antigravity provider client identity & Argon catalog visibility** (`src/ai/providers/antigravity.ts`, `src/ai/model-discovery.ts`) — updated `getAntigravityUserAgent()` from `antigravity/1.104.0` to `antigravity/hub/2.23.1` (`jeopi` v16.6.3 parity) so `daily-cloudcode-pa.googleapis.com/v1internal:fetchAvailableModels` returns entitled `gemini-4-argon-{low,medium,high}` and `gemini-3.8-flash-*` tiers. Preserved named non-recommended chat models (`displayName`) alongside `agentModelSorts`/`tieredModelIds`/`defaultAgentModelId` while excluding explicit non-chat (`tabModelIds`, `imageGenerationModelIds`, `audioTranscriptionModelIds`, `deprecatedModelIds`), internal, and unnamed rows.
30
+ - **Client-UA-scoped Antigravity model cache invalidation** (`src/ai/model-cache.ts`) — included `getAntigravityUserAgent()` in `antigravityAccountScope` so client User-Agent updates immediately invalidate stale cached model lists without waiting for the 6-hour TTL.
31
+ - **In-name `-medium` thinking depth recognition** (`src/ai/providers/gemini.ts`, `src/ai/model-catalog.ts`) — `geminiNamedEffort` and `inferCatalogMetadata` now recognize `-medium` model suffixes (`gemini-4-argon-medium`, `gemini-3.8-flash-medium`) so unset `reasoningEffort` defaults to `medium` thinking depth.
32
+
33
+ ### Verified
34
+ - Focused Antigravity/Gemini/discovery/catalog regression suites (`175 pass, 0 fail`), `bun run typecheck`, `bun run build`, `bun run pack:check`, and live `antigravity/gemini-4-argon-{low,medium,high}` discovery + `antigravity/gemini-4-argon-medium` inference (`HTTP 200`).
35
+
12
36
  ## [0.12.3] - 2026-10-17
13
37
  _Anthropic static model catalog trimmed to claude-5.x with live API discovery, and `/route off` strictly fixes the active model with no failover._
14
38
 
package/README.ja.md CHANGED
@@ -416,10 +416,10 @@ CI は `.github/workflows/npm-publish.yml` で公開します — GitHub リリ
416
416
 
417
417
  <!-- CHANGELOG:START (auto-generated from CHANGELOG.md — run `bun run changelog:sync`) -->
418
418
  - **[Unreleased]**
419
+ - **[0.12.5]** (2026-10-17) — Antigravity and Kimi provider support in interactive setup, Kimi live model cache rehydration, immediate discovery caching on provider probing.
420
+ - **[0.12.4]** (2026-10-17) — Refresh Antigravity provider (`antigravity/*`) account catalogs with `antigravity/hub/2.23.1` so entitled Gemini 4 Argon and Gemini 3.8 Flash tiers appear directly in `/model provider antigravity`.
419
421
  - **[0.12.3]** (2026-10-17) — Anthropic static model catalog trimmed to claude-5.x with live API discovery, and `/route off` strictly fixes the active model with no failover.
420
422
  - **[0.12.2]** (2026-10-16) — Select Argon through the official agy full agent, with its own live catalog and session, separately from jeo's Cloud Code Assist backend.
421
- - **[0.12.1]** (2026-10-16) — Logged-in OpenAI subscription models were invisible on proxy-configured machines.
422
- - **[0.12.0]** (2026-10-16) — Karpathy behavioral guardrails become always-on discipline, and skill routing gains an ECC single-harness inventory plus a Jev-style advisory router.
423
423
 
424
424
  See [CHANGELOG.md](CHANGELOG.md) for the full history.
425
425
  <!-- CHANGELOG:END -->
package/README.ko.md CHANGED
@@ -417,10 +417,10 @@ CI는 `.github/workflows/npm-publish.yml`로 배포합니다 — GitHub 릴리
417
417
 
418
418
  <!-- CHANGELOG:START (auto-generated from CHANGELOG.md — run `bun run changelog:sync`) -->
419
419
  - **[Unreleased]**
420
+ - **[0.12.5]** (2026-10-17) — Antigravity and Kimi provider support in interactive setup, Kimi live model cache rehydration, immediate discovery caching on provider probing.
421
+ - **[0.12.4]** (2026-10-17) — Refresh Antigravity provider (`antigravity/*`) account catalogs with `antigravity/hub/2.23.1` so entitled Gemini 4 Argon and Gemini 3.8 Flash tiers appear directly in `/model provider antigravity`.
420
422
  - **[0.12.3]** (2026-10-17) — Anthropic static model catalog trimmed to claude-5.x with live API discovery, and `/route off` strictly fixes the active model with no failover.
421
423
  - **[0.12.2]** (2026-10-16) — Select Argon through the official agy full agent, with its own live catalog and session, separately from jeo's Cloud Code Assist backend.
422
- - **[0.12.1]** (2026-10-16) — Logged-in OpenAI subscription models were invisible on proxy-configured machines.
423
- - **[0.12.0]** (2026-10-16) — Karpathy behavioral guardrails become always-on discipline, and skill routing gains an ECC single-harness inventory plus a Jev-style advisory router.
424
424
 
425
425
  See [CHANGELOG.md](CHANGELOG.md) for the full history.
426
426
  <!-- CHANGELOG:END -->
package/README.md CHANGED
@@ -409,10 +409,10 @@ Huge thanks to [gajae-code](https://github.com/Yeachan-Heo/gajae-code) for the i
409
409
 
410
410
  <!-- CHANGELOG:START (auto-generated from CHANGELOG.md — run `bun run changelog:sync`) -->
411
411
  - **[Unreleased]**
412
+ - **[0.12.5]** (2026-10-17) — Antigravity and Kimi provider support in interactive setup, Kimi live model cache rehydration, immediate discovery caching on provider probing.
413
+ - **[0.12.4]** (2026-10-17) — Refresh Antigravity provider (`antigravity/*`) account catalogs with `antigravity/hub/2.23.1` so entitled Gemini 4 Argon and Gemini 3.8 Flash tiers appear directly in `/model provider antigravity`.
412
414
  - **[0.12.3]** (2026-10-17) — Anthropic static model catalog trimmed to claude-5.x with live API discovery, and `/route off` strictly fixes the active model with no failover.
413
415
  - **[0.12.2]** (2026-10-16) — Select Argon through the official agy full agent, with its own live catalog and session, separately from jeo's Cloud Code Assist backend.
414
- - **[0.12.1]** (2026-10-16) — Logged-in OpenAI subscription models were invisible on proxy-configured machines.
415
- - **[0.12.0]** (2026-10-16) — Karpathy behavioral guardrails become always-on discipline, and skill routing gains an ECC single-harness inventory plus a Jev-style advisory router.
416
416
 
417
417
  See [CHANGELOG.md](CHANGELOG.md) for the full history.
418
418
  <!-- CHANGELOG:END -->
package/README.zh.md CHANGED
@@ -414,10 +414,10 @@ CI 通过 `.github/workflows/npm-publish.yml` 发布 — GitHub 发布 release
414
414
 
415
415
  <!-- CHANGELOG:START (auto-generated from CHANGELOG.md — run `bun run changelog:sync`) -->
416
416
  - **[Unreleased]**
417
+ - **[0.12.5]** (2026-10-17) — Antigravity and Kimi provider support in interactive setup, Kimi live model cache rehydration, immediate discovery caching on provider probing.
418
+ - **[0.12.4]** (2026-10-17) — Refresh Antigravity provider (`antigravity/*`) account catalogs with `antigravity/hub/2.23.1` so entitled Gemini 4 Argon and Gemini 3.8 Flash tiers appear directly in `/model provider antigravity`.
417
419
  - **[0.12.3]** (2026-10-17) — Anthropic static model catalog trimmed to claude-5.x with live API discovery, and `/route off` strictly fixes the active model with no failover.
418
420
  - **[0.12.2]** (2026-10-16) — Select Argon through the official agy full agent, with its own live catalog and session, separately from jeo's Cloud Code Assist backend.
419
- - **[0.12.1]** (2026-10-16) — Logged-in OpenAI subscription models were invisible on proxy-configured machines.
420
- - **[0.12.0]** (2026-10-16) — Karpathy behavioral guardrails become always-on discipline, and skill routing gains an ECC single-harness inventory plus a Jev-style advisory router.
421
421
 
422
422
  See [CHANGELOG.md](CHANGELOG.md) for the full history.
423
423
  <!-- CHANGELOG:END -->
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "jeo-code",
3
- "version": "0.12.3",
3
+ "version": "0.12.5",
4
4
  "description": "Clean, highly optimized AI coding agent using spec-first loop",
5
5
  "type": "module",
6
6
  "main": "src/cli.ts",
@@ -19,9 +19,10 @@ import { mkdir, readFile, writeFile } from "node:fs/promises";
19
19
  import * as os from "node:os";
20
20
  import * as path from "node:path";
21
21
  import { jeoEnv } from "../util/env";
22
- import { recordLiveCodexModels, recordLiveProviderModels, setAntigravityAccountScope, setOpenAIOauthAccountScope } from "./model-catalog";
22
+ import { recordLiveCodexModels, recordLiveKimiCodeModels, recordLiveProviderModels, setAntigravityAccountScope, setOpenAIOauthAccountScope } from "./model-catalog";
23
23
  import type { Credential } from "../auth";
24
24
  import { getStoredOAuth, snapshotProvider } from "../auth/storage";
25
+ import { getAntigravityUserAgent } from "./providers/antigravity";
25
26
  import { extractChatgptAccountId } from "./providers/openai-responses";
26
27
  import type { ProviderName } from "./types";
27
28
 
@@ -87,7 +88,7 @@ export async function antigravityAccountScope(credential?: Credential): Promise<
87
88
  || process.env.GOOGLE_CLOUD_PROJECT?.trim()
88
89
  || process.env.GOOGLE_CLOUD_PROJECT_ID?.trim()
89
90
  || "";
90
- return createHash("sha256").update(`${providerSlot}\u0000${identity}\u0000${projectId}`, "utf8").digest("hex");
91
+ return createHash("sha256").update(`${providerSlot}\u0000${identity}\u0000${projectId}\u0000${getAntigravityUserAgent()}`, "utf8").digest("hex");
91
92
  } catch {
92
93
  return undefined;
93
94
  }
@@ -157,7 +158,7 @@ export function mergeCacheEntries(
157
158
  const isScopedOAuth = (provider: ProviderName, source: ModelCacheSource): boolean =>
158
159
  (provider === "openai" || provider === "antigravity") && source === "oauth";
159
160
  const keyOf = (provider: ProviderName, baseUrl?: string, accountId?: string, source?: ModelCacheSource) =>
160
- `${provider}\u0000${baseUrl?.replace(/\/+$/, "") ?? ""}\u0000${isScopedOAuth(provider, source ?? "none") ? accountId ?? "" : ""}\u0000${provider === "openai" || provider === "antigravity" ? source ?? "none" : ""}`;
161
+ `${provider}\u0000${baseUrl?.replace(/\/+$/, "") ?? ""}\u0000${isScopedOAuth(provider, source ?? "none") ? accountId ?? "" : ""}\u0000${provider === "openai" || provider === "antigravity" || provider === "kimi" ? source ?? "none" : ""}`;
161
162
  for (const entry of previous) {
162
163
  const scoped = isScopedOAuth(entry.provider, entry.source);
163
164
  if (entry.provider === "agy") continue;
@@ -239,6 +240,9 @@ export function applyCachedModels(cache: ModelCacheFile | null, openAIOauthAccou
239
240
  if (isOpenAIOAuth && entry.accountId && !entry.baseUrl) {
240
241
  recordLiveCodexModels(entry.models, entry.accountId);
241
242
  }
243
+ if (entry.provider === "kimi" && entry.source === "oauth") {
244
+ recordLiveKimiCodeModels(entry.models);
245
+ }
242
246
  applied += entry.models.length;
243
247
  }
244
248
  return applied;
@@ -501,6 +501,30 @@ export const KIMI_CODE_MODELS: readonly string[] = [
501
501
  "kimi-k2",
502
502
  ];
503
503
 
504
+ const liveKimiCodeModels = new Set<string>();
505
+
506
+ /** Record ids confirmed by the Kimi Code OAuth `/v1/models` endpoint so brand-new
507
+ * subscription models work immediately without waiting for a static catalog release. */
508
+ export function recordLiveKimiCodeModels(ids: readonly string[]): void {
509
+ for (const id of ids) {
510
+ const trimmed = id.trim();
511
+ if (!trimmed) continue;
512
+ const wire = trimmed.startsWith("kimi/") ? trimmed.slice(5) : trimmed;
513
+ if (wire) liveKimiCodeModels.add(wire);
514
+ }
515
+ }
516
+
517
+ /** Whether `model` is served by the Kimi Code OAuth subscription endpoint. */
518
+ export function isKimiCodeModel(model: string): boolean {
519
+ const wire = model.startsWith("kimi/") ? model.slice(5) : model;
520
+ return KIMI_CODE_MODELS.includes(wire) || liveKimiCodeModels.has(wire);
521
+ }
522
+
523
+ /** Test-only reset for live Kimi Code observations. */
524
+ export function resetLiveKimiCodeModels(): void {
525
+ liveKimiCodeModels.clear();
526
+ }
527
+
504
528
  /** Recency compare for `releaseDate` ("YYYY-MM"): `>0` when `a` is newer; a
505
529
  * missing date sorts oldest, so an unconfirmed date never outranks a
506
530
  * verified-newer model. */
@@ -618,7 +642,7 @@ export function inferCatalogMetadata(modelId: string): CatalogModel | undefined
618
642
  providerModel: id,
619
643
  contextTokens: 1_000_000,
620
644
  maxOutputTokens: 65_536,
621
- thinking: !reasons ? [] : big3 || id.includes("thinking") || id.includes("-high") || id.includes("-low") ? FULL : STD,
645
+ thinking: !reasons ? [] : big3 || id.includes("thinking") || id.includes("-high") || id.includes("-medium") || id.includes("-low") ? FULL : STD,
622
646
  images: true,
623
647
  company: antigravity ? "Google Antigravity" : "Google",
624
648
  };
@@ -639,6 +663,20 @@ export function inferCatalogMetadata(modelId: string): CatalogModel | undefined
639
663
  company: "xAI",
640
664
  };
641
665
  }
666
+ // Kimi / Moonshot
667
+ const kimi = id.match(/^kimi-(k\d+|latest|for-coding)/);
668
+ if (kimi) {
669
+ return {
670
+ canonical: raw,
671
+ provider: "kimi",
672
+ providerModel: id,
673
+ contextTokens: 256_000,
674
+ maxOutputTokens: 16_384,
675
+ thinking: FULL,
676
+ images: false,
677
+ company: "Moonshot",
678
+ };
679
+ }
642
680
 
643
681
  return undefined;
644
682
  }
@@ -16,6 +16,7 @@ import {
16
16
  catalogByProvider,
17
17
  KIMI_CODE_MODELS,
18
18
  recordLiveCodexModels,
19
+ recordLiveKimiCodeModels,
19
20
  recordLiveProviderModels,
20
21
  setOpenAIOauthAccountScope,
21
22
  setAntigravityAccountScope,
@@ -26,7 +27,7 @@ import { openaiCompatDef } from "./providers/openai-compatible-catalog";
26
27
  import { antigravityAccountScope } from "./model-cache";
27
28
  import { getAntigravityUserAgent, resolveAntigravityProjectId } from "./providers/antigravity";
28
29
  import { discoverGoogleProjectId, ANTIGRAVITY_DISCOVERY_METADATA } from "../auth/flows/google-project";
29
-
30
+ import { getKimiCommonHeaders } from "../auth/flows/kimi";
30
31
  export interface ProviderModelsResult {
31
32
  provider: ProviderName;
32
33
  /** Discovered model ids (provider-qualified where the router expects it). */
@@ -185,6 +186,15 @@ export function discoveryRequest(
185
186
  return { url: "https://api.x.ai/v1/models", headers: token ? { Authorization: `Bearer ${token}` } : {} };
186
187
  }
187
188
  case "kimi": {
189
+ if (cred?.kind === "oauth") {
190
+ return {
191
+ url: "https://api.kimi.com/coding/v1/models",
192
+ headers: {
193
+ ...getKimiCommonHeaders(),
194
+ Authorization: `Bearer ${cred.token}`,
195
+ },
196
+ };
197
+ }
188
198
  const token = cred?.kind === "api_key" ? cred.token : "";
189
199
  return { url: "https://api.moonshot.ai/v1/models", headers: token ? { Authorization: `Bearer ${token}` } : {} };
190
200
  }
@@ -212,7 +222,7 @@ function isGeminiChatModel(id: string): boolean {
212
222
  }
213
223
 
214
224
  type CodexModelRow = { slug?: string; id?: string; visibility?: string; supported_in_api?: boolean; priority?: number };
215
- type AntigravityModelRow = { slug?: string; id?: string; name?: string; isInternal?: boolean; model?: string };
225
+ type AntigravityModelRow = { slug?: string; id?: string; name?: string; displayName?: string; isInternal?: boolean; model?: string };
216
226
 
217
227
  function normalizeAntigravityModelId(id: string): string {
218
228
  return id.replace(/^models\//, "").replace(/^antigravity\//, "");
@@ -250,8 +260,9 @@ export function parseModelsBody(provider: ProviderName, body: unknown, opts: { o
250
260
  // (MODEL_PLACEHOLDER_*) and must never be surfaced. The response's OWN
251
261
  // metadata decides what to show — no hard-coded model lists:
252
262
  // Agent sorts and tiered model ids are complementary chat offerings, not
253
- // interchangeable allowlists. Include both plus the declared default;
254
- // only older responses without these fields need non-chat role filtering.
263
+ // interchangeable allowlists. Include both plus the declared default and
264
+ // named non-recommended chat models; exclude explicit non-chat roles and
265
+ // unnamed unreferenced rows.
255
266
  const payload = body as {
256
267
  models?: Record<string, AntigravityModelRow> | AntigravityModelRow[];
257
268
  agentModelSorts?: { groups?: { modelIds?: string[] }[] }[];
@@ -280,13 +291,16 @@ export function parseModelsBody(provider: ProviderName, body: unknown, opts: { o
280
291
  if (typeof payload.defaultAgentModelId === "string") agentIds.add(normalizeAntigravityModelId(payload.defaultAgentModelId));
281
292
  const hasChatModelMetadata = Array.isArray(payload.agentModelSorts)
282
293
  || payload.tieredModelIds !== undefined || payload.defaultAgentModelId !== undefined;
283
- const nonChat = new Set([
294
+ const explicitNonChat = new Set([
284
295
  ...roleIds(payload.tabModelIds),
285
296
  ...roleIds(payload.imageGenerationModelIds),
286
297
  ...roleIds(payload.audioTranscriptionModelIds),
298
+ ...deprecated,
299
+ ]);
300
+ const nonChat = new Set([
301
+ ...explicitNonChat,
287
302
  ...roleIds(payload.commitMessageModelIds),
288
303
  ...roleIds(payload.mqueryModelIds),
289
- ...deprecated,
290
304
  ]);
291
305
  const rawModels = payload.models;
292
306
  const entries = Array.isArray(rawModels)
@@ -297,7 +311,10 @@ export function parseModelsBody(provider: ProviderName, body: unknown, opts: { o
297
311
  .filter(([id, model]) =>
298
312
  id.length > 0 &&
299
313
  model?.isInternal !== true &&
300
- (hasChatModelMetadata ? agentIds.has(id) : !nonChat.has(id)))
314
+ !explicitNonChat.has(id) &&
315
+ (hasChatModelMetadata
316
+ ? agentIds.size > 0 && (agentIds.has(id) || (!nonChat.has(id) && typeof model?.displayName === "string" && model.displayName.trim().length > 0))
317
+ : !nonChat.has(id)))
301
318
  .map(([id]) => `antigravity/${id}`);
302
319
  }
303
320
  if (provider === "gemini") {
@@ -578,6 +595,7 @@ export async function listProviderModels(
578
595
  ...(source === "oauth" && scope.accountId ? { accountId: scope.accountId } : {}),
579
596
  });
580
597
  if (provider === "openai" && source === "oauth" && !baseUrl) recordLiveCodexModels(allModels, oauthAccountId);
598
+ if (provider === "kimi" && source === "oauth") recordLiveKimiCodeModels(allModels);
581
599
  return {
582
600
  provider,
583
601
  models,
@@ -652,6 +670,12 @@ export async function discoverModels(
652
670
  return [primary, await listProviderModels(p, { ...discoveryOpts, baseUrl: undefined, preferOAuth: true, codexCatalog: true })];
653
671
  }
654
672
  }
673
+ if (p === "kimi" && primary.source !== "oauth") {
674
+ const credential = await resolveCredential("kimi");
675
+ if (credential.kind === "oauth") {
676
+ return [primary, await listProviderModels(p, { ...discoveryOpts, preferOAuth: true })];
677
+ }
678
+ }
655
679
  return [primary];
656
680
  }),
657
681
  );
@@ -6,7 +6,7 @@ import "./register-providers"; // side-effect: registers built-in adapters into
6
6
  import type { CallOptions, Message, ProviderAdapter, ProviderName } from "./types";
7
7
  import { expandAlias, resolveModelId, effectiveAliasesFor, BUILTIN_ALIASES } from "./model-registry";
8
8
  import { findCatalogEntry, type ModelCatalogEntry } from "./model-catalog-compat";
9
- import { toProviderModel, CODEX_MODELS, KIMI_CODE_MODELS, findCatalogModel, isCodexModel, findLiveCatalogModel } from "./model-catalog";
9
+ import { toProviderModel, CODEX_MODELS, KIMI_CODE_MODELS, findCatalogModel, isCodexModel, isKimiCodeModel, findLiveCatalogModel } from "./model-catalog";
10
10
  import { xaiCredential } from "./providers/xai";
11
11
  import { allCompatNames, isOpenAICompatProvider } from "./providers/openai-compatible-catalog";
12
12
  import { withRetry, defaultRetryable, withConnectionContext, isConnectionError, ConnectionContextError, type RetryOptions } from "../util/retry";
@@ -329,8 +329,7 @@ export function oauthServesModel(provider: AuthProvider, model: string): boolean
329
329
  return isCodexModel(model);
330
330
  }
331
331
  if (provider === "kimi") {
332
- const wire = model.startsWith("kimi/") ? model.slice(5) : model;
333
- return KIMI_CODE_MODELS.includes(wire);
332
+ return isKimiCodeModel(model);
334
333
  }
335
334
  if (isOAuthProvider(provider) && OAUTH_FLOW_REGISTRY[provider].verifiedEndToEnd === false) return false;
336
335
  return true;
@@ -28,10 +28,10 @@ function antigravityClaudeThinkingBudget(effort: CallOptions["reasoningEffort"])
28
28
  const ENDPOINTS = [ANTIGRAVITY_DAILY_ENDPOINT, ANTIGRAVITY_SANDBOX_ENDPOINT] as const;
29
29
 
30
30
  export function getAntigravityUserAgent(): string {
31
- const version = process.env.PI_AI_ANTIGRAVITY_VERSION || "1.104.0";
31
+ const version = process.env.PI_AI_ANTIGRAVITY_VERSION || "2.23.1";
32
32
  const os = process.platform === "win32" ? "windows" : process.platform;
33
33
  const arch = process.arch === "x64" ? "amd64" : process.arch === "ia32" ? "386" : process.arch;
34
- return `antigravity/${version} ${os}/${arch}`;
34
+ return `antigravity/hub/${version} ${os}/${arch}`;
35
35
  }
36
36
 
37
37
  function antigravityModelId(model: string): string {
@@ -48,6 +48,7 @@ export function geminiThinkingBudget(model: string, effort?: CallOptions["reason
48
48
  * the documented thought-token burn protection). Expects a lowercased id. */
49
49
  function geminiNamedEffort(m: string): CallOptions["reasoningEffort"] | undefined {
50
50
  return m.includes("-high") ? "high"
51
+ : m.includes("-medium") ? "medium"
51
52
  : m.includes("-low") ? "low"
52
53
  : m.includes("thinking") ? "medium"
53
54
  : undefined;
@@ -152,7 +152,7 @@ async function runAuthLogin(rest: string[]): Promise<void> {
152
152
  * already succeeded and is not rolled back by this check. Prints only model
153
153
  * ids/count — no tokens/secrets.
154
154
  */
155
- const LIVE_MODEL_REPORT_PROVIDERS = new Set<AuthProvider>(["openai", "anthropic", "antigravity"]);
155
+ const LIVE_MODEL_REPORT_PROVIDERS = new Set<AuthProvider>(["openai", "anthropic", "antigravity", "kimi"]);
156
156
 
157
157
  async function reportLiveModels(provider: AuthProvider): Promise<void> {
158
158
  try {
@@ -249,6 +249,7 @@ async function runAuthImport(rest: string[]): Promise<void> {
249
249
  }
250
250
  const expiryMsg = expires ? fmtExpiry(expires) : "";
251
251
  console.log(`Expiry status:${expiryMsg || " valid"}`);
252
+ await reportLiveModels("antigravity");
252
253
  } catch (err: any) {
253
254
  console.log(`[FAILED] ${err.message}`);
254
255
  process.exitCode = 1;
@@ -335,8 +336,16 @@ async function selectProvider(
335
336
  console.log(" 1) anthropic (real PKCE OAuth, verified end-to-end)");
336
337
  console.log(" 2) openai (real PKCE OAuth, Codex backend)");
337
338
  console.log(" 3) gemini (real Google OAuth, Cloud Code Assist)");
338
- const ans = (await rl.question("Choose [1-3]: ")).trim();
339
- const map: Record<string, AuthProvider> = { "1": "anthropic", "2": "openai", "3": "gemini" };
339
+ console.log(" 4) antigravity (Google Cloud Code Assist OAuth)");
340
+ console.log(" 5) kimi (Kimi Code OAuth device authorization)");
341
+ const ans = (await rl.question("Choose [1-5]: ")).trim();
342
+ const map: Record<string, AuthProvider> = {
343
+ "1": "anthropic",
344
+ "2": "openai",
345
+ "3": "gemini",
346
+ "4": "antigravity",
347
+ "5": "kimi",
348
+ };
340
349
  const p = map[ans];
341
350
  if (!p) {
342
351
  console.log("Invalid choice.");
@@ -31,6 +31,7 @@ import {
31
31
  normalizeProviderId,
32
32
  } from "../ai/providers/custom-providers";
33
33
  import { listProviderModels } from "../ai/model-discovery";
34
+ import { writeModelCache } from "../ai/model-cache";
34
35
  import { describeProvider } from "../ai/provider-status";
35
36
 
36
37
  const USAGE = [
@@ -186,6 +187,7 @@ async function probe(id: string, opts: { failOnError: boolean }): Promise<void>
186
187
  if (shown.length) console.log(` ${shown.join(", ")}${result.models.length > shown.length ? ", …" : ""}`);
187
188
  const first = result.models[0];
188
189
  console.log(` status : OK — use it with 'jeo launch --model ${first ? qualify(first) : `${id}/<model>`}'`);
190
+ await writeModelCache([result]);
189
191
  return;
190
192
  }
191
193
 
@@ -76,6 +76,7 @@ export function buildEnabledProviders(config: Config): string[] {
76
76
  if (cfg.providers?.anthropic || cfg.oauth?.anthropic) enabled.push("anthropic");
77
77
  if (cfg.providers?.openai || cfg.oauth?.openai) enabled.push("openai");
78
78
  if (cfg.providers?.gemini || cfg.oauth?.gemini || cfg.providers?.antigravity || cfg.oauth?.antigravity) enabled.push("google-oauth");
79
+ if (cfg.providers?.kimi || cfg.oauth?.kimi) enabled.push("kimi");
79
80
  if (cfg.ollamaBaseUrl) enabled.push(`ollama(${cfg.ollamaBaseUrl})`);
80
81
  if (cfg.openaiBaseUrl) enabled.push(`openai-compatible(${cfg.openaiBaseUrl})`);
81
82
  return enabled;
@@ -24,12 +24,14 @@ function reportModelChoice(r: { warning?: string; suggestions: string[] }): void
24
24
  if (r.suggestions.length) console.log(` Did you mean: ${r.suggestions.join(", ")}?`);
25
25
  }
26
26
 
27
- type ProviderChoice = "anthropic" | "openai" | "gemini" | "ollama" | "lmstudio" | "openai-compatible";
27
+ type ProviderChoice = "anthropic" | "openai" | "gemini" | "antigravity" | "kimi" | "ollama" | "lmstudio" | "openai-compatible";
28
28
 
29
29
  const DEFAULT_MODELS: Record<ProviderChoice, string> = {
30
30
  anthropic: "claude-sonnet-4-6",
31
31
  openai: "gpt-4o",
32
32
  gemini: "gemini-2.0-flash",
33
+ antigravity: "antigravity/gemini-3-flash",
34
+ kimi: "kimi-k2.5",
33
35
  ollama: "ollama/llama3.1:8b",
34
36
  lmstudio: "openai/local-model",
35
37
  "openai-compatible": "openai/local-model",
@@ -87,16 +89,19 @@ export async function runSetupCommand(): Promise<void> {
87
89
  console.log(" 1) anthropic — Claude (API key or OAuth bearer)");
88
90
  console.log(" 2) openai — GPT (API key or OAuth bearer)");
89
91
  console.log(" 3) gemini — Google AI Studio (API key or OAuth bearer)");
90
- console.log(" 4) ollama — local Ollama (no auth)");
91
- console.log(" 5) lmstudio — local LM Studio (OpenAI-compatible)");
92
- console.log(" 6) openai-compatible — custom OpenAI-compatible (vLLM, llama-cpp-server, ...)");
93
- console.log(" 7) skip — keep existing values, just change defaults\n");
94
-
95
- const sel = (await rl.question("Configure which provider? [1-7]: ")).trim();
92
+ console.log(" 4) antigravity — Google Cloud Code Assist (OAuth)");
93
+ console.log(" 5) kimi — Kimi / Moonshot (API key or Kimi Code OAuth)");
94
+ console.log(" 6) ollama — local Ollama (no auth)");
95
+ console.log(" 7) lmstudio — local LM Studio (OpenAI-compatible)");
96
+ console.log(" 8) openai-compatible — custom OpenAI-compatible (vLLM, llama-cpp-server, ...)");
97
+ console.log(" 9) skip — keep existing values, just change defaults\n");
98
+
99
+ const sel = (await rl.question("Configure which provider? [1-9]: ")).trim();
96
100
  const map: Record<string, ProviderChoice | "skip"> = {
97
101
  "1": "anthropic", "2": "openai", "3": "gemini",
98
- "4": "ollama", "5": "lmstudio", "6": "openai-compatible",
99
- "7": "skip",
102
+ "4": "antigravity", "5": "kimi",
103
+ "6": "ollama", "7": "lmstudio", "8": "openai-compatible",
104
+ "9": "skip",
100
105
  };
101
106
  const choice = map[sel] ?? "skip";
102
107
 
@@ -108,7 +113,7 @@ export async function runSetupCommand(): Promise<void> {
108
113
  next.providers = next.providers || {};
109
114
  next.oauth = next.oauth || {};
110
115
 
111
- if (choice === "anthropic" || choice === "openai" || choice === "gemini") {
116
+ if (choice === "anthropic" || choice === "openai" || choice === "gemini" || choice === "antigravity" || choice === "kimi") {
112
117
  const authMode = (
113
118
  await rl.question("Auth mode: (b)rowser OAuth login, (t)oken paste, or api (k)ey? [b]: ")
114
119
  ).trim().toLowerCase();
@@ -223,7 +228,7 @@ export async function runSetupCommand(): Promise<void> {
223
228
  rl.close();
224
229
 
225
230
  // Drop empty oauth/providers to keep config tidy.
226
- if (next.oauth && !next.oauth.anthropic && !next.oauth.openai && !next.oauth.gemini) delete next.oauth;
231
+ if (next.oauth && Object.keys(next.oauth).length === 0) delete next.oauth;
227
232
 
228
233
  await saveGlobalConfig(next);
229
234
  console.log("\n[SUCCESS] Configuration saved to ~/.jeo/config.json");