Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
0ec5bd6
chore(kimi-code): upgrade pi-tui to 0.78.1 and adapt native helpers
liruifengv Jun 29, 2026
42a5aff
chore: add changeset for pi-tui upgrade
liruifengv Jun 29, 2026
63e7b98
chore: vendor @earendil-works/pi-tui 0.80.2
liruifengv Jun 29, 2026
86540ca
feat(kimi-code): integrate vendored @moonshot-ai/pi-tui
liruifengv Jun 29, 2026
e12015f
fix(pi-tui): export package.json for native asset resolution
liruifengv Jul 1, 2026
186d6c0
chore(kimi-code): sync pi-tui native prebuilds at build time
liruifengv Jul 1, 2026
59e63d1
fix(pi-tui): avoid destructive full redraw during streaming
liruifengv Jul 1, 2026
2066cf6
Merge branch 'main' into chore/upgrade-pi-tui-0.78.1
liruifengv Jul 1, 2026
21db282
fix(kimi-code): update pi-tui imports in files merged from main
liruifengv Jul 1, 2026
5e07d0c
chore(nix): update pnpmDeps hash after lockfile regen
liruifengv Jul 1, 2026
a0686ad
fix(pi-tui): clamp above-viewport diff even when content shrinks
liruifengv Jul 1, 2026
ec1befc
fix(kimi-code): keep activity placeholder to avoid streaming shrink
liruifengv Jul 1, 2026
04680ee
chore: refine streaming scroll changeset wording
liruifengv Jul 1, 2026
e9e7848
chore: remove obsolete pi-tui native helpers changeset
liruifengv Jul 1, 2026
26e5084
chore: add pi-tui changesets and document the pi-tui changelog rule
liruifengv Jul 1, 2026
62d4842
Merge remote-tracking branch 'origin/main' into chore/upgrade-pi-tui-…
liruifengv Jul 1, 2026
713427d
chore(nix): update pnpmDeps hash after merging main
liruifengv Jul 1, 2026
a210b7e
fix(changeset): drop private kimi-code-docs from google-genai changeset
liruifengv Jul 1, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 38 additions & 0 deletions .agents/skills/gen-changesets/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ description: Use when generating changesets in the kimi-code repository, includi

All other `@moonshot-ai/*` packages are treated as internal packages, including `@moonshot-ai/kimi-code-sdk`, `agent-core`, `kosong`, `kaos`, `kimi-code-oauth`, `kimi-telemetry`, and `migration-legacy`.

`@moonshot-ai/pi-tui` is a special internal package: it is a private fork (`private: true`) that is never published, but it keeps its own changelog through changesets. It is an exception to Core Rule 4 — see the dedicated section below.

## Core Rules

1. **Inspect the actual changes first.** Use `git status` / `git diff --name-only` to identify which packages were actually changed.
Expand Down Expand Up @@ -176,6 +178,41 @@ Add the server-hosted web UI, including chat layout and session list behaviors.
Add the server REST and WebSocket APIs that power the web UI.
```

## `@moonshot-ai/pi-tui` changes

`@moonshot-ai/pi-tui` is a vendored fork that lives in `packages/pi-tui`. It is `private: true` and is never published, but it is **not** ignored by changesets: changesets versions it and writes `packages/pi-tui/CHANGELOG.md` so the fork keeps its own history. Because it is bundled into the CLI like other internal packages, it is an exception to Core Rule 4 — do **not** list `@moonshot-ai/kimi-code` for a change that only touches pi-tui.

- Changes that only affect pi-tui (build, package, strict-mode cleanup, renderer fixes): list `@moonshot-ai/pi-tui` only. No CLI changeset.
- If the same change is also user-visible in the CLI (for example a terminal rendering fix that CLI users can see), add a **separate** changeset that lists `@moonshot-ai/kimi-code` with CLI-focused wording, in addition to the pi-tui changeset. Do not mix both packages in one frontmatter — the two changelogs need different wording.

pi-tui-only change:

```markdown
---
"@moonshot-ai/pi-tui": patch
---

Export the package manifest so the bundled binary can locate its native assets.
```

pi-tui change that is also visible in the CLI (two separate changesets):

```markdown
---
"@moonshot-ai/pi-tui": patch
---

Clamp the differential render to the visible viewport so scrolling up during streaming no longer jumps to the top.
```

```markdown
---
"@moonshot-ai/kimi-code": patch
---

Fix the transcript jumping to the top when scrolling up through history during streaming output.
```

## Red Flags

- You are about to write `major` without asking the user.
Expand All @@ -188,3 +225,4 @@ Add the server REST and WebSocket APIs that power the web UI.
- The wording claims more than the diff actually did.
- The CLI wording mentions internal package names, class names, or PR numbers.
- The entry includes real internal identifiers instead of neutral placeholders.
- A change that only touches `@moonshot-ai/pi-tui` lists `@moonshot-ai/kimi-code` instead of `@moonshot-ai/pi-tui`, or mixes both packages in one frontmatter.
5 changes: 5 additions & 0 deletions .changeset/fix-streaming-scroll-jump.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@moonshot-ai/kimi-code": patch
---

Fix the transcript jumping to the top when scrolling up through history during streaming output.
1 change: 0 additions & 1 deletion .changeset/google-genai-base-url.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
---
"@moonshot-ai/kosong": patch
"@moonshot-ai/agent-core": patch
"kimi-code-docs": patch
---

Honor `base_url` for the `google-genai` and `vertexai` providers. A configured base URL was previously ignored and requests always went to `generativelanguage.googleapis.com`; it is now forwarded to the Google GenAI SDK (with `GOOGLE_GEMINI_BASE_URL` / `GOOGLE_VERTEX_BASE_URL` env fallbacks), so Gemini-compatible proxies and gateways can be used. Give the host root only — the SDK appends the API version segment itself.
5 changes: 5 additions & 0 deletions .changeset/pi-tui-export-manifest.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@moonshot-ai/pi-tui": patch
---

Export the package manifest so the bundled binary can locate its native assets.
5 changes: 5 additions & 0 deletions .changeset/pi-tui-fork-integration.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@moonshot-ai/pi-tui": patch
---

Integrate the fork into the monorepo and load it directly from source.
5 changes: 5 additions & 0 deletions .changeset/pi-tui-viewport-clamp.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@moonshot-ai/pi-tui": patch
---

Clamp the differential render to the visible viewport so scrolling up during streaming no longer jumps to the top.
1 change: 1 addition & 0 deletions .oxlintrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,7 @@
"node_modules/",
"apps/*/scripts/",
"docs/smoke-archive/",
"packages/pi-tui/",
"*.generated.ts"
]
}
3 changes: 3 additions & 0 deletions apps/kimi-code/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,6 @@ agents/
# next to it keeps `#/generated/vis-web-asset` type-resolvable on a fresh
# clone (before any build has produced the `.ts`).
src/generated/vis-web-asset.ts

# Copied from packages/pi-tui/native at build time by scripts/copy-native-assets.mjs
native/
6 changes: 3 additions & 3 deletions apps/kimi-code/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
"files": [
"dist",
"dist-web",
"native",
"scripts/postinstall.mjs",
"scripts/postinstall",
"README.md"
Expand All @@ -48,7 +49,7 @@
"provenance": true
},
"scripts": {
"build": "pnpm -C ../kimi-web run build && tsdown && node scripts/copy-web-assets.mjs",
"build": "pnpm -C ../kimi-web run build && tsdown && node scripts/copy-native-assets.mjs && node scripts/copy-web-assets.mjs",
"prebuild": "node scripts/build-vis-asset.mjs",
"catalog:update": "node scripts/update-catalog.mjs --out dist/built-in-catalog.json",
"smoke": "node scripts/smoke.mjs",
Expand All @@ -75,17 +76,16 @@
},
"optionalDependencies": {
"@mariozechner/clipboard": "^0.3.9",
"koffi": "^2.16.0",
"node-pty": "^1.1.0"
},
"devDependencies": {
"@earendil-works/pi-tui": "^0.74.0",
"@moonshot-ai/acp-adapter": "workspace:^",
"@moonshot-ai/kimi-code-oauth": "workspace:^",
"@moonshot-ai/kimi-code-sdk": "workspace:^",
"@moonshot-ai/kimi-telemetry": "workspace:^",
"@moonshot-ai/kimi-web": "workspace:^",
"@moonshot-ai/migration-legacy": "workspace:^",
"@moonshot-ai/pi-tui": "workspace:^",
"@moonshot-ai/server": "workspace:^",
"@moonshot-ai/vis-server": "workspace:^",
"@moonshot-ai/vis-web": "workspace:*",
Expand Down
38 changes: 38 additions & 0 deletions apps/kimi-code/scripts/copy-native-assets.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import { cp, mkdir, rm, stat } from 'node:fs/promises';
import { dirname, resolve } from 'node:path';
import { fileURLToPath } from 'node:url';

const appRoot = resolve(dirname(fileURLToPath(import.meta.url)), '..');
const repoRoot = resolve(appRoot, '../..');
const source = resolve(repoRoot, 'packages/pi-tui/native');
const target = resolve(appRoot, 'native');

// pi-tui ships platform-specific native helpers only for darwin/win32;
// Linux has no native helper, so there is nothing to copy for it.
const PLATFORMS = ['darwin', 'win32'];

async function assertPrebuilds(platform) {
const dir = resolve(source, platform, 'prebuilds');
try {
const info = await stat(dir);
if (!info.isDirectory()) {
throw new Error('not a directory');
}
} catch {
throw new Error(
`pi-tui native prebuilds were not found at ${dir}. Build or restore packages/pi-tui first.`,
);
}
return dir;
}

await rm(target, { recursive: true, force: true });
await mkdir(target, { recursive: true });

for (const platform of PLATFORMS) {
const srcPrebuilds = await assertPrebuilds(platform);
const dstPrebuilds = resolve(target, platform, 'prebuilds');
await cp(srcPrebuilds, dstPrebuilds, { recursive: true });
}

console.log(`Copied pi-tui native prebuilds to ${target}`);
16 changes: 9 additions & 7 deletions apps/kimi-code/scripts/native/assets.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,7 @@ export const NATIVE_TARGETS = Object.freeze(
SUPPORTED_TARGETS.map((t) => {
const deps = resolveTargetDeps(t);
const clipboardTarget = deps.find((d) => d.id === 'clipboard-target')?.resolvedName;
const koffiNativeFile = deps.find((d) => d.id === 'koffi')?.nativeFileRelatives?.[0];
const koffiTriplet = koffiNativeFile?.match(/koffi\/([^/]+)\/koffi\.node$/)?.[1] ?? null;
return [t, { clipboardPackage: clipboardTarget, koffiTriplet }];
return [t, { clipboardPackage: clipboardTarget }];
}),
),
);
Expand Down Expand Up @@ -161,16 +159,19 @@ async function collectPackageFiles({
packageName,
packageRoot,
includeNativeFiles,
includeEntryJs = true,
nativeFileRelatives = [],
}) {
const packageJsonPath = join(packageRoot, 'package.json');
const packageJson = await readJson(packageJsonPath);
const selected = new Set([packageJsonPath]);

const entry = resolvePackageEntry(packageRoot, packageJson);
if (entry !== null) {
selected.add(entry);
await addRuntimeDependencyFiles(packageRoot, entry, selected);
if (includeEntryJs) {
const entry = resolvePackageEntry(packageRoot, packageJson);
if (entry !== null) {
selected.add(entry);
await addRuntimeDependencyFiles(packageRoot, entry, selected);
}
}

for (const nativeFileRelative of nativeFileRelatives) {
Expand Down Expand Up @@ -250,6 +251,7 @@ export async function collectNativeAssets({ appRoot, target }) {
packageName: dep.resolvedName,
packageRoot,
includeNativeFiles: dep.collect === 'native-files',
includeEntryJs: dep.collect !== 'native-file-only',
nativeFileRelatives: dep.nativeFileRelatives,
});
const result = await packageManifestEntries({
Expand Down
2 changes: 1 addition & 1 deletion apps/kimi-code/scripts/native/check-bundle.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ const optionalRuntimeRequires = new Set([
'utf-8-validate',
]);
const optionalRelativeRuntimeRequires = new Set(['./crypto/build/Release/sshcrypto.node']);
const handledNativeRuntimeRequires = new Set(['koffi']);
const handledNativeRuntimeRequires = new Set();

function isAllowedSpecifier(specifier) {
if (builtins.has(specifier) || specifier.startsWith('node:')) return true;
Expand Down
41 changes: 21 additions & 20 deletions apps/kimi-code/scripts/native/native-deps.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -27,13 +27,16 @@ const clipboardSubpackageByTarget = Object.freeze({
'win32-x64': '@mariozechner/clipboard-win32-x64-msvc',
});

const koffiTripletByTarget = Object.freeze({
'darwin-arm64': 'darwin_arm64',
'darwin-x64': 'darwin_x64',
'linux-arm64': 'linux_arm64',
'linux-x64': 'linux_x64',
'win32-arm64': 'win32_arm64',
'win32-x64': 'win32_x64',
// pi-tui ships platform-specific native helpers (no Linux build):
// - darwin: Shift-modifier detection for Terminal.app Shift+Enter
// - win32: enable ENABLE_VIRTUAL_TERMINAL_INPUT so Shift+Tab is distinguishable
const piTuiNativeFileByTarget = Object.freeze({
'darwin-arm64': ['native/darwin/prebuilds/darwin-arm64/darwin-modifiers.node'],
'darwin-x64': ['native/darwin/prebuilds/darwin-x64/darwin-modifiers.node'],
'linux-arm64': [],
'linux-x64': [],
'win32-arm64': ['native/win32/prebuilds/win32-arm64/win32-console-mode.node'],
'win32-x64': ['native/win32/prebuilds/win32-x64/win32-console-mode.node'],
});

export function isSupportedTarget(target) {
Expand All @@ -45,13 +48,15 @@ export function isSupportedTarget(target) {
* @property {string} id — stable internal id used for parent refs
* @property {(target: string) => string} name
* — npm package name (may depend on target)
* @property {'js-only'|'native-files'|'js-and-native-file'|'virtual'} collect
* @property {'js-only'|'native-files'|'js-and-native-file'|'native-file-only'|'virtual'} collect
* @property {string|null} parent
* — id of another registered dep this nests under (for pnpm),
* or null for top-level (resolvable from app root)
* @property {(target: string) => string[]} [nativeFileRelatives]
* — explicit list of .node files relative to package root
* (used by 'js-and-native-file'; native-files mode auto-scans *.node)
* (used by 'js-and-native-file' and 'native-file-only';
* native-files mode auto-scans *.node). 'native-file-only' collects
* package.json + these .node files but skips the package entry JS.
*/

/** @type {readonly NativeDepDescriptor[]} */
Expand All @@ -70,18 +75,14 @@ export const nativeDeps = Object.freeze([
},
{
id: 'pi-tui',
name: () => '@earendil-works/pi-tui',
// pi-tui is bundled into main.cjs at build time — we don't collect it as
// a native dep, only register it so koffi can declare it as parent.
collect: 'virtual',
name: () => '@moonshot-ai/pi-tui',
// pi-tui's JS is bundled into main.cjs, so only the platform-specific
// native helper (.node under native/) ships alongside the binary — its
// dist/ JS is intentionally NOT collected (it stays in the bundle). This
// keeps the SEA native-asset payload small. Linux has no native helper.
collect: 'native-file-only',
parent: null,
},
{
id: 'koffi',
name: () => 'koffi',
collect: 'js-and-native-file',
parent: 'pi-tui',
nativeFileRelatives: (target) => [`build/koffi/${koffiTripletByTarget[target]}/koffi.node`],
nativeFileRelatives: (target) => piTuiNativeFileByTarget[target] ?? [],
},
]);

Expand Down
2 changes: 1 addition & 1 deletion apps/kimi-code/src/migration/migration-screen.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
* This file implements the ask, progress, and result phases. `beginMigration`
* drives the real runMigration flow (injectable for tests).
*/
import { Container, matchesKey, Key, truncateToWidth, type Focusable } from '@earendil-works/pi-tui';
import { Container, matchesKey, Key, truncateToWidth, type Focusable } from '@moonshot-ai/pi-tui';
import chalk from 'chalk';

import type { ColorPalette } from '#/tui/theme/colors';
Expand Down
34 changes: 25 additions & 9 deletions apps/kimi-code/src/native/module-hook.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import { existsSync } from 'node:fs';
import { createRequire } from 'node:module';
import { join } from 'node:path';

import { loadNativePackage } from './native-require';
import { getNativePackageRoot } from './native-assets';

type ModuleLoad = (request: string, parent: unknown, isMain: boolean) => unknown;

Expand All @@ -10,7 +12,16 @@ interface ModuleWithLoad {

const nodeRequire = createRequire(import.meta.url);
let installed = false;
let loadingNativePackage = false;

// pi-tui loads its platform-specific native helpers via an absolute-path
// require() computed from import.meta.url / process.execPath
// (see pi-tui dist/terminal.js and dist/native-modifiers.js). In a SEA binary
// those .node files live in the native-asset cache, so redirect any absolute
// require of a pi-tui native helper to the cached copy.
//
// Path shape: native/<darwin|win32>/prebuilds/<arch>/<file>.node — note the
// two path segments after "prebuilds", so ".+" (not "[^/]+") is required.
const PI_TUI_NATIVE_PATTERN = /native[\\/](?:win32|darwin)[\\/]prebuilds[\\/].+\.node$/;

export function installNativeModuleHook(): void {
if (installed) return;
Expand All @@ -26,13 +37,18 @@ export function installNativeModuleHook(): void {
parent: unknown,
isMain: boolean,
): unknown {
if (request === 'koffi' && !loadingNativePackage) {
loadingNativePackage = true;
try {
const pkg = loadNativePackage<unknown>('koffi');
if (pkg !== null) return pkg;
} finally {
loadingNativePackage = false;
if (
typeof request === 'string' &&
PI_TUI_NATIVE_PATTERN.test(request) &&
!existsSync(request)
) {
const pkgRoot = getNativePackageRoot('@moonshot-ai/pi-tui');
if (pkgRoot !== null) {
const match = request.match(PI_TUI_NATIVE_PATTERN);
if (match !== null) {
const redirected = join(pkgRoot, match[0]);
return originalLoad.call(this, redirected, parent, isMain);
}
}
}
return originalLoad.call(this, request, parent, isMain);
Expand Down
Loading
Loading