pi-provider-kimi-code 0.6.9 → 0.6.11

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/README.md CHANGED
@@ -3,16 +3,10 @@
3
3
  [![npm](https://img.shields.io/npm/v/pi-provider-kimi-code)](https://www.npmjs.com/package/pi-provider-kimi-code)
4
4
  [![license](https://img.shields.io/npm/l/pi-provider-kimi-code)](./LICENSE)
5
5
 
6
- > **Kimi Code harness extension for Pi.** Pi v0.82.0 ships a built-in `kimi-coding` provider that handles the LLM layer — K3, adaptive thinking, and a generated model catalog — plus native OAuth sign-in via `/login`. This extension brings the rest of the Kimi Code surface into Pi: shared login state with the official `kimi-code` CLI, Kimi Files API uploads, and Kimi-native tools such as `moonshot_search`, `moonshot_fetch`, and `kimi_datasource`.
7
-
8
- Pi's built-in `kimi-coding` provider handles the LLM. This extension covers the rest: shared login state with the official CLI, file uploads through Kimi's Files API, and Kimi-native tools. It also keeps a legacy provider registration so existing model sessions still load. New users should use Pi's built-in model provider.
9
-
10
- > **Live model catalog (legacy).** For backward compatibility, the extension still reads Kimi's live `/v1/models` catalog when it has credentials. New Pi users should rely on the built-in provider's generated catalog instead.
6
+ > **Kimi Code extension for Pi — use K3, K2.7 Code, and HighSpeed models with your Kimi Code Plan.** This extension brings the full Kimi Code surface into Pi: shared OAuth login state with the official `kimi-code` CLI, Kimi Files API uploads, and Kimi-native tools such as `moonshot_search`, `moonshot_fetch`, and `kimi_datasource`. (Pi v0.82.0+ ships a built-in `kimi-coding` provider for the LLM layer; this extension covers everything around it.)
11
7
 
12
8
  ## Why this exists
13
9
 
14
- Pi is a small harness you adapt to your own workflow. Kimi Code is Moonshot's official coding agent. Pi now ships a built-in `kimi-coding` provider that covers the LLM, including OAuth login. This extension is for the parts of Kimi Code that Pi's built-in provider does not cover: the account session, file uploads, and Kimi's own tools/datasource APIs.
15
-
16
10
  Use the built-in provider if you only want to chat with Kimi models. Install this extension when you also want the full Kimi Code surface inside Pi:
17
11
 
18
12
  - **Share a session with the official `kimi-code` CLI.** Reuse an existing `kimi-code` login, or sync Pi's OAuth login back to `kimi-code` so both CLIs stay authenticated.
@@ -90,11 +84,11 @@ KIMI_API_KEY=sk-... pi
90
84
 
91
85
  When this extension does register models, the official catalog determines what it publishes. The following IDs are the fallback set when catalog discovery is unavailable:
92
86
 
93
- | Pi model ID | Default name | Availability and context |
94
- | --------------------------------------- | ------------------------ | ------------------------ |
95
- | `kimi-coding/kimi-for-coding` | Kimi K2.7 Code | Official catalog |
96
- | `kimi-coding/kimi-for-coding-highspeed` | Kimi K2.7 Code HighSpeed | Official catalog |
97
- | `kimi-coding/k3` | Kimi K3 | Official catalog |
87
+ | Pi model ID | Default name | Source |
88
+ | --------------------------------------- | -------------------------- | ---------------- |
89
+ | `kimi-coding/kimi-for-coding` | Kimi for Coding | Official catalog |
90
+ | `kimi-coding/kimi-for-coding-highspeed` | Kimi for Coding High Speed | Official catalog |
91
+ | `kimi-coding/k3` | Kimi K3 | Official catalog |
98
92
 
99
93
  Kimi's official catalog at `https://api.kimi.com/coding/v1/models` is authoritative for model availability and context windows. When discovery is unavailable, the extension uses the fallback models and defaults below.
100
94
 
@@ -119,7 +113,7 @@ Inside Pi, run:
119
113
  /kimi-settings
120
114
  ```
121
115
 
122
- That command shows the current server-side model name (e.g. "K2.7 Code High Speed"), your Kimi quota and Extra Usage balance, and lets you edit the home or project config. Changes apply to the active session tool set.
116
+ That command shows your signed-in account (via Kimi's `/me` endpoint), the current server-side model name (e.g. "K2.7 Code High Speed"), your Kimi quota and Extra Usage balance, and lets you edit the home or project config. Changes apply to the active session tool set.
123
117
 
124
118
  Configurable settings include protocol mode, upload threshold, and per-tool enable/collapse.
125
119
 
@@ -170,6 +164,11 @@ Supported datasource names:
170
164
  | `arxiv` | Academic paper search and metadata |
171
165
  | `scholar` | Academic literature search |
172
166
  | `yuandian_law` | Chinese legal database and case law |
167
+ | `wind` | A-share intraday series, funds, bonds |
168
+ | `imf` | IMF FX, CPI, and GDP forecasts |
169
+ | `gildata` | Natural-language stock/fund screening |
170
+ | `sec_edgar` | US SEC filings (10-K/10-Q, 13F, 8-K) |
171
+ | `sp_data` | S&P fundamentals and transcripts |
173
172
 
174
173
  ### Common notes
175
174
 
package/index.ts CHANGED
@@ -77,7 +77,11 @@ import {
77
77
  parseByteSizeInput,
78
78
  } from "./src/settings-ui.ts";
79
79
  import { setStoreResolvedKimiConfig, streamSimpleKimi } from "./src/stream.ts";
80
- import { fetchKimiUsageSnapshot, getKimiUsageToken } from "./src/usage.ts";
80
+ import {
81
+ fetchKimiUsageSnapshot,
82
+ fetchKimiUserInfoSnapshot,
83
+ getKimiUsageToken,
84
+ } from "./src/usage.ts";
81
85
  import { buildMoonshotFetchTool, buildMoonshotSearchTool } from "./src/tools/moonshot.ts";
82
86
  import { buildKimiDatasourceTool } from "./src/tools/datasource.ts";
83
87
  interface KimiRuntimeState {
@@ -186,8 +190,9 @@ async function openSettingsMenu(
186
190
  state: KimiRuntimeState,
187
191
  ): Promise<void> {
188
192
  const modelDiscoveryToken = getKimiUsageToken();
189
- const [usageSnapshot, initialModelsRefreshed] = await Promise.all([
193
+ const [usageSnapshot, userInfoSnapshot, initialModelsRefreshed] = await Promise.all([
190
194
  fetchKimiUsageSnapshot(),
195
+ fetchKimiUserInfoSnapshot(),
191
196
  refreshModelExtras(state),
192
197
  ]);
193
198
  let modelsRefreshed = initialModelsRefreshed;
@@ -197,6 +202,7 @@ async function openSettingsMenu(
197
202
  }
198
203
  if (modelsRefreshed) await registerKimiProvider(pi, state);
199
204
  const usage = usageSnapshot.summary;
205
+ const account = userInfoSnapshot.summary;
200
206
 
201
207
  const projectTrusted = await isKimiProjectConfigApproved(ctx, ctx.cwd);
202
208
  const homeDraft = loadHomeKimiCodeConfig(os.homedir());
@@ -373,6 +379,9 @@ async function openSettingsMenu(
373
379
  width,
374
380
  ),
375
381
  "",
382
+ truncateToWidth(theme.fg("accent", theme.bold("Kimi account")), width),
383
+ truncateToWidth(` ${account}`, width),
384
+ "",
376
385
  truncateToWidth(theme.fg("accent", theme.bold("Kimi usage")), width),
377
386
  ...usageLines,
378
387
  "",
@@ -433,10 +442,20 @@ function buildKimiCatalogModels(state: KimiRuntimeState) {
433
442
  }
434
443
 
435
444
  // Node reports a missing subpath export (pi-ai <=0.79) and a missing package
436
- // with these codes. Both mean "this pi-ai has no compat layer", which is an
445
+ // with these codes. All mean "this pi-ai has no compat layer", which is an
437
446
  // expected configuration rather than a failure worth reporting: the fallback
438
447
  // simply does not exist there.
439
- const MODULE_UNAVAILABLE_CODES = new Set(["ERR_MODULE_NOT_FOUND", "ERR_PACKAGE_PATH_NOT_EXPORTED"]);
448
+ //
449
+ // MODULE_NOT_FOUND (the CJS code, without the ERR_ prefix) covers pi's jiti
450
+ // extension loader on old pi versions: it aliases the bare "@earendil-works/pi-ai"
451
+ // specifier straight to dist/index.js, so the "/compat" subpath degrades into
452
+ // the bogus file path "dist/index.js/compat" and fails legacy CJS resolution
453
+ // instead of exports-map resolution.
454
+ const MODULE_UNAVAILABLE_CODES = new Set([
455
+ "ERR_MODULE_NOT_FOUND",
456
+ "ERR_PACKAGE_PATH_NOT_EXPORTED",
457
+ "MODULE_NOT_FOUND",
458
+ ]);
440
459
 
441
460
  // Every api id this extension can hand out, not only the one for the currently
442
461
  // configured protocol: sessions persist the api id that was current when the
@@ -590,7 +609,7 @@ async function registerKimiProvider(pi: ExtensionAPI, state: KimiRuntimeState):
590
609
 
591
610
  // What pi throws from any `pi` method once the extension runtime behind it has
592
611
  // been retired, which is what a /reload does. Same wording on every version
593
- // this extension supports (0.78.1 through 0.82.0); if it ever changes, the
612
+ // this extension supports (0.78.1 through 0.83.0); if it ever changes, the
594
613
  // worst case is that a reload race gets logged instead of ignored.
595
614
  const STALE_EXTENSION_RUNTIME_PREFIX = "This extension ctx is stale";
596
615
 
package/package.json CHANGED
@@ -1,17 +1,21 @@
1
1
  {
2
2
  "name": "pi-provider-kimi-code",
3
- "version": "0.6.9",
4
- "description": "Kimi Code harness extension for Pi: shared login state, file uploads, and Kimi-native tools",
3
+ "version": "0.6.11",
4
+ "description": "Kimi Code models (K3, K2.7, HighSpeed) in Pi: OAuth shared login with kimi-code CLI, file uploads, and Kimi-native tools",
5
5
  "keywords": [
6
6
  "ai-provider",
7
7
  "coding-agent",
8
+ "highspeed",
9
+ "k3",
8
10
  "kimi",
9
11
  "kimi-code",
12
+ "kimi-code-plan",
10
13
  "kimi-for-coding",
11
14
  "kimi-k2",
12
15
  "kimi-k2.5",
13
16
  "kimi-k2.6",
14
17
  "kimi-k2.7",
18
+ "kimi-k3",
15
19
  "llm",
16
20
  "llm-provider",
17
21
  "moonshot",
@@ -53,9 +57,9 @@
53
57
  "proper-lockfile": "^4.1.2"
54
58
  },
55
59
  "devDependencies": {
56
- "@earendil-works/pi-ai": "^0.82.0",
57
- "@earendil-works/pi-coding-agent": "^0.82.0",
58
- "@earendil-works/pi-tui": "^0.82.0",
60
+ "@earendil-works/pi-ai": "^0.83.0",
61
+ "@earendil-works/pi-coding-agent": "^0.83.0",
62
+ "@earendil-works/pi-tui": "^0.83.0",
59
63
  "@j178/prek": "^0.4.4",
60
64
  "@types/node": "^24.0.0",
61
65
  "@types/proper-lockfile": "^4.1.4",
package/src/constants.ts CHANGED
@@ -41,14 +41,10 @@ export function getDefaultBaseUrl(protocol: KimiWireProtocol): string {
41
41
 
42
42
  export const DEFAULT_BASE_URL = getDefaultBaseUrl(ENV_KIMI_CODE_PROTOCOL);
43
43
 
44
- export const PROVIDER_VERSION = "0.6.9";
45
-
46
- // Upstream kimi-code CLI version — used in User-Agent and X-Msh-Version
47
- // headers to match the official client's identity. Update this when
48
- // syncing with https://github.com/MoonshotAI/kimi-code.
49
- export const KIMI_UPSTREAM_VERSION = "0.29.1";
50
- export const KIMI_CODE_USER_AGENT = `kimi-code-cli/${KIMI_UPSTREAM_VERSION}`;
51
- export const KIMI_PLATFORM = "kimi_code_cli";
44
+ export const PROVIDER_VERSION = "0.6.11";
45
+
46
+ export const KIMI_CODE_USER_AGENT = `pi-provider-kimi-code/${PROVIDER_VERSION}`;
47
+ export const KIMI_PLATFORM = "pi";
52
48
 
53
49
  export function getKimiCodeHome(): string {
54
50
  const value = process.env.KIMI_CODE_HOME?.trim();
package/src/device.ts CHANGED
@@ -8,12 +8,12 @@ import { readFile } from "node:fs/promises";
8
8
  import os from "node:os";
9
9
  import { dirname } from "node:path";
10
10
 
11
- import {
12
- DEVICE_ID_PATH,
13
- KIMI_CODE_USER_AGENT,
14
- KIMI_UPSTREAM_VERSION,
15
- KIMI_PLATFORM,
16
- } from "./constants.ts";
11
+ // Namespace import (not a named import of VERSION) so a missing export on old
12
+ // pi versions degrades to undefined rather than a module-link error that kills
13
+ // the whole extension load.
14
+ import * as piAgent from "@earendil-works/pi-coding-agent";
15
+
16
+ import { DEVICE_ID_PATH, KIMI_CODE_USER_AGENT, KIMI_PLATFORM } from "./constants.ts";
17
17
 
18
18
  function createDeviceId(): string {
19
19
  return randomBytes(16).toString("hex");
@@ -165,6 +165,13 @@ export function getOsVersion(): string {
165
165
  return os.release();
166
166
  }
167
167
 
168
+ // The running pi TUI's version, read off pi-coding-agent's VERSION export (it
169
+ // reads its own package.json at module load).
170
+ export function getPiVersion(): string {
171
+ const version = (piAgent as { VERSION?: unknown }).VERSION;
172
+ return typeof version === "string" && version ? version : "unknown";
173
+ }
174
+
168
175
  export function asciiHeaderValue(value: string, fallback = "unknown"): string {
169
176
  const trimmed = value.trim();
170
177
  /* oxlint-disable-next-line no-control-regex */
@@ -214,7 +221,7 @@ export function getCommonHeaders(): Record<string, string> {
214
221
  const headers = {
215
222
  "User-Agent": KIMI_CODE_USER_AGENT,
216
223
  "X-Msh-Platform": KIMI_PLATFORM,
217
- "X-Msh-Version": KIMI_UPSTREAM_VERSION,
224
+ "X-Msh-Version": getPiVersion(),
218
225
  "X-Msh-Device-Name": os.hostname(),
219
226
  "X-Msh-Device-Model": getDeviceModel(),
220
227
  "X-Msh-Os-Version": getOsVersion(),
@@ -41,6 +41,27 @@ const DATASOURCE_DEFINITIONS = [
41
41
  name: "yuandian_law",
42
42
  description: "Chinese laws, regulations, judicial cases; labor contracts and legal clauses.",
43
43
  },
44
+ {
45
+ name: "wind",
46
+ description:
47
+ "A-share intraday minute series, funds, bonds; map PE/PB/ROE-style field names via wind_search_fields first.",
48
+ },
49
+ {
50
+ name: "imf",
51
+ description: "IMF macro data: FX rates, CPI, GDP forecasts, balance of payments.",
52
+ },
53
+ {
54
+ name: "gildata",
55
+ description: "Natural-language stock/fund screening.",
56
+ },
57
+ {
58
+ name: "sec_edgar",
59
+ description: "US SEC filings: 10-K/10-Q, S-1, Form 4, 13F, 8-K.",
60
+ },
61
+ {
62
+ name: "sp_data",
63
+ description: "S&P fundamentals: consensus estimates, valuation ratios, transcripts.",
64
+ },
44
65
  ] as const;
45
66
 
46
67
  const DATASOURCE_NAMES = DATASOURCE_DEFINITIONS.map((source) => source.name);
@@ -50,7 +71,7 @@ export const kimiDatasourceSchema = Type.Object({
50
71
  DATASOURCE_NAMES.map((name) => Type.Literal(name)),
51
72
  {
52
73
  description:
53
- "Source: stock_finance_data stocks/reports; yahoo_finance quotes/options; world_bank_open_data GDP/population; tianyancha Chinese company registry/legal rep; arxiv preprints; scholar citations; yuandian_law Chinese laws/cases/contracts.",
74
+ "Source: stock_finance_data stocks/reports; yahoo_finance quotes/options; world_bank_open_data GDP/population; imf FX/CPI/GDP forecasts; tianyancha Chinese company registry/legal rep; arxiv preprints; scholar citations; yuandian_law Chinese laws/cases/contracts; wind A-share intraday minute series/funds/bonds (map field names via wind_search_fields first); gildata natural-language stock/fund screening; sec_edgar US filings (10-K/10-Q, S-1, Form 4, 13F, 8-K); sp_data S&P fundamentals (estimates, valuation, transcripts).",
54
75
  },
55
76
  ),
56
77
  api_name: Type.Optional(
package/src/usage.ts CHANGED
@@ -217,6 +217,8 @@ function formatWindowLabel(value: unknown, fallbackLabel: string): string {
217
217
  : unit.includes("MINUTE")
218
218
  ? duration
219
219
  : undefined;
220
+ if (unit.includes("WEEK")) return `Current ${duration}w window`;
221
+ if (unit.includes("DAY")) return `Current ${duration}d window`;
220
222
  if (!minutes) return fallbackLabel;
221
223
  if (minutes % 60 === 0) return `Current ${minutes / 60}h window`;
222
224
  return `Current ${minutes}m window`;
@@ -320,35 +322,123 @@ function fetchKimiUsage(token: string, signal: AbortSignal): Promise<Response> {
320
322
  });
321
323
  }
322
324
 
323
- export async function fetchKimiUsageSnapshot(
324
- options: { timeoutMs?: number; token?: string; refreshOnUnauthorized?: boolean } = {},
325
+ interface KimiSnapshotOptions {
326
+ timeoutMs?: number;
327
+ token?: string;
328
+ refreshOnUnauthorized?: boolean;
329
+ }
330
+
331
+ interface KimiSnapshotEndpoint {
332
+ prefix: string;
333
+ request: (token: string, signal: AbortSignal) => Promise<Response>;
334
+ summarize: (payload: unknown) => string;
335
+ }
336
+
337
+ async function fetchKimiSnapshot(
338
+ endpoint: KimiSnapshotEndpoint,
339
+ options: KimiSnapshotOptions = {},
325
340
  ): Promise<KimiUsageSnapshot> {
326
341
  const token = options.token ?? getKimiUsageToken();
327
342
  if (!token) {
328
- return { summary: "Usage: missing credentials. Run /login kimi-coding." };
343
+ return { summary: `${endpoint.prefix}: missing credentials. Run /login kimi-coding.` };
329
344
  }
330
345
 
331
346
  const controller = new AbortController();
332
347
  const timeout = setTimeout(() => controller.abort(), options.timeoutMs ?? 15_000);
333
348
  try {
334
- let response = await fetchKimiUsage(token, controller.signal);
349
+ let response = await endpoint.request(token, controller.signal);
335
350
  if (response.status === 401 && options.refreshOnUnauthorized !== false) {
336
351
  const refreshed = await refreshKimiAuthToken(token, { signal: controller.signal });
337
- if (refreshed) response = await fetchKimiUsage(refreshed, controller.signal);
352
+ if (refreshed) response = await endpoint.request(refreshed, controller.signal);
338
353
  }
339
354
  if (!response.ok) {
340
- return { summary: `Usage: fetch failed (${response.status})` };
355
+ return { summary: `${endpoint.prefix}: fetch failed (${response.status})` };
341
356
  }
342
357
  const payload = (await response.json()) as unknown;
343
- return { summary: parseUsageSummary(payload) };
358
+ return { summary: endpoint.summarize(payload) };
344
359
  } catch (error) {
345
360
  const message = error instanceof Error ? error.message : String(error);
346
- return { summary: `Usage: fetch failed (${message})` };
361
+ return { summary: `${endpoint.prefix}: fetch failed (${message})` };
347
362
  } finally {
348
363
  clearTimeout(timeout);
349
364
  }
350
365
  }
351
366
 
367
+ export async function fetchKimiUsageSnapshot(
368
+ options: KimiSnapshotOptions = {},
369
+ ): Promise<KimiUsageSnapshot> {
370
+ return fetchKimiSnapshot(
371
+ { prefix: "Usage", request: fetchKimiUsage, summarize: parseUsageSummary },
372
+ options,
373
+ );
374
+ }
375
+
352
376
  export async function fetchKimiUsageSummary(): Promise<string> {
353
377
  return (await fetchKimiUsageSnapshot()).summary;
354
378
  }
379
+
380
+ export interface KimiUserInfo {
381
+ userId: string;
382
+ nickname: string;
383
+ userLevel: number;
384
+ userLevelName: string;
385
+ email?: string;
386
+ }
387
+
388
+ export function parseKimiUserInfo(payload: unknown): KimiUserInfo | null {
389
+ if (typeof payload !== "object" || payload === null || Array.isArray(payload)) return null;
390
+ const record = payload as Record<string, unknown>;
391
+ const userId = toStringValue(record.user_id);
392
+ if (!userId) return null;
393
+ const email = toStringValue(record.email);
394
+ return {
395
+ userId,
396
+ nickname: toStringValue(record.nickname) ?? "",
397
+ userLevel: toNumber(record.user_level) ?? 0,
398
+ userLevelName: toStringValue(record.user_level_name) ?? "",
399
+ ...(email ? { email } : {}),
400
+ };
401
+ }
402
+
403
+ export function formatKimiUserInfo(info: KimiUserInfo): string {
404
+ const parts = [info.nickname || info.userId];
405
+ if (info.userLevelName) {
406
+ parts.push(
407
+ info.userLevel > 0 ? `${info.userLevelName} (Lv ${info.userLevel})` : info.userLevelName,
408
+ );
409
+ }
410
+ if (info.email) parts.push(info.email);
411
+ return parts.join(" · ");
412
+ }
413
+
414
+ export function buildKimiUserInfoUrl(baseUrl = getBaseUrl("openai")): string {
415
+ const normalized = baseUrl.replace(/\/+$/, "");
416
+ return normalized.endsWith("/v1") ? `${normalized}/me` : `${normalized}/v1/me`;
417
+ }
418
+
419
+ function fetchKimiUserInfo(token: string, signal: AbortSignal): Promise<Response> {
420
+ return fetch(buildKimiUserInfoUrl(), {
421
+ method: "GET",
422
+ headers: {
423
+ ...getKimiProviderHeaders(),
424
+ Authorization: `Bearer ${token}`,
425
+ },
426
+ signal,
427
+ });
428
+ }
429
+
430
+ export async function fetchKimiUserInfoSnapshot(
431
+ options: KimiSnapshotOptions = {},
432
+ ): Promise<KimiUsageSnapshot> {
433
+ return fetchKimiSnapshot(
434
+ {
435
+ prefix: "Account",
436
+ request: fetchKimiUserInfo,
437
+ summarize: (payload) => {
438
+ const info = parseKimiUserInfo(payload);
439
+ return info ? formatKimiUserInfo(info) : "Account: malformed response";
440
+ },
441
+ },
442
+ options,
443
+ );
444
+ }