* fix(agent-core): report EXIF-rotated image dimensions and raise edge cap to 3000px
Image compression now reports original dimensions in the decoded
(EXIF-rotated) space, matching the coordinate system of the sent image
and of ReadMediaFile region readback; previously portrait JPEGs
(orientation 5-8) got swapped width/height in captions. The longest-edge
downscale cap rises from 2000px to 3000px, and the default jimp resize
path is documented as the anti-aliased area-average one so it is not
accidentally switched to a point-sampled interpolation mode.
* test: shrink oversized image fixtures to fit CI timeouts
The 3600x3600 fixtures introduced for the 3000px edge cap nearly doubled
the pixel area jimp has to decode and deflate, pushing the slowest
compression tests past the 5s vitest timeout on CI runners. 3600x1800
keeps every fixture over the cap while restoring roughly the workload of
the old 2600x2600 fixtures that CI handled comfortably.
* test: pin anti-aliased downscale quality with executable guards
A 1px checkerboard probe pins the compressor to full-coverage averaging
at integer and fractional ratios, with jimp's point-sampled BILINEAR
mode kept as the executable aliasing counter-example (it collapses the
50%-gray pattern to solid black at 4:1). Also guards the other classic
downscale bugs: transparent-pixel color bleed, mean-brightness drift,
iterative recompression degradation, and zero-size collapse on extreme
aspect ratios.
* fix(agent-core): report decoded EXIF-rotated dimensions in ReadMediaFile notes
The media note derived its original-dimensions line from the header
sniff, which reports pre-rotation values for EXIF orientation 5-8
JPEGs. The sent image and region readback both live in the decoded
(rotated) space, so portrait photos got axis-swapped coordinate
guidance. Once a decode has happened — compression or crop — its
dimensions now overwrite the sniffed ones.
* fix(agent-core): improve handling of EXIF orientation in image dimensions and metadata
* fix(agent-core): sniff EXIF orientation and step budget fallback through 2000px
Two follow-ups to the EXIF and 3000px-cap changes:
sniffImageDimensions now reads the JPEG EXIF Orientation tag (pure
header parse, both byte orders) and reports display-space dimensions
for orientations 5-8. Passthrough images — never decoded — previously
kept the pre-rotation header size in compression results and media
read notes, disagreeing with the decoded space that region readback
uses.
encodeWithinBudget steps the over-budget fallback through 2000px
before the 1000px last resort. Raising the cap to 3000px had left a
regression window: an image whose 2000px encode fits the byte budget
was sent at 1000px where the old 2000px cap used to send it at
2000px.
* fix(kimi-code): record pasted image dimensions in display space
The TUI paste path recorded attachment and original dimensions from its
raw header parser, which ignores EXIF orientation. For a portrait JPEG
the submit-time caption then contradicted the sent image's aspect and
region readback coordinates were axis-swapped. Dimensions now come from
the compression result, which reports display space on both the
compressed and passthrough paths; parseImageMeta remains only the
format/mime gate.
* feat(agent-core): add image compression and crop telemetry
Every image ingestion path now reports an image_compress event —
outcome (compressed / passthrough fast, guard, unsupported, unhelpful,
error), input/output formats, byte and pixel sizes, EXIF transposition,
and duration — and region readback reports an image_crop event with a
failure classification and the region's share of the original area.
Wiring is per call site via a new CompressImageOptions.telemetry
option, so the outcome split and timing are measured inside the
compressor while each caller only names its source: ReadMediaFile
(tool construction, like GrepTool), MCP tool results (McpOutputOptions),
server prompt ingestion (ICoreProcessService now exposes the host
telemetry client), ACP prompts (session track adapter), and TUI paste
(host.track adapter). Properties are numeric/enum only — never paths
or content — and a throwing client can never affect the compression
result.
* fix(agent-core): run the full JPEG quality ladder at fallback sizes
The fallback rescales encoded only at quality 20, so a JPEG whose
ladder failed at the fitted size collapsed straight to the lowest
quality even when the smaller size left budget headroom for a higher
rung (the realistic window is the 1000px step, where the 4x pixel
drop pays for q80/q60). Each fallback edge now walks the same
q80-to-q20 ladder as the fitted size.
* test: shrink heavy JPEG fixtures and add explicit timeouts
The fallback-ladder test runs ~11 pure-JS JPEG encodes and the EXIF
paste test decodes, rotates, and re-encodes a 6.5MP frame; both sat at
the edge of the 5s vitest timeout on CI runners. Narrower fixtures cut
the pixel area (the ladder test keeps its width above 2000px so the
full fallback chain still runs) and explicit 15s timeouts absorb runner
variance.
* fix(server): scope prompt image compression telemetry to the session
The prompt-ingestion image_compress events were emitted with the bare
host telemetry client, while every agent-side source inherits a
session-scoped client — so prompt_inline/prompt_file events could not
be correlated with their session. The route now wraps the client with
withTelemetryContext({ sessionId }) like rpc/core-impl does for
session telemetry.
* chore(changeset): consolidate image compression changesets
One entry covering the cap raise and the EXIF dimension fix, listed
for both the CLI and the SDK so the SDK changelog's compression
description (previously pinned at 2000px) stays accurate.
|
||
|---|---|---|
| .. | ||
| config.json | ||
| fix-hooks-windows-console-popup.md | ||
| fix-web-ws-reconnect-toast.md | ||
| forbid-model-goal-pauses.md | ||
| image-compression-improvements.md | ||
| migrate-web-icons-to-unplugin-icons.md | ||
| README.md | ||
Changesets
This repository uses changesets to manage npm package versions and releases.
Package Publishing Strategy
This repository uses an independent, manually-selected publishing strategy. When generating a changeset, only select the publishable packages that this change actually affects. The repository's .changeset/config.json already filters out internal workspace packages via ignore, so only the publishable packages listed below should appear in the pnpm changeset prompt.
Current publishable packages:
| Package | Directory | Description |
|---|---|---|
@moonshot-ai/kimi-code |
apps/kimi-code |
CLI / TUI application — provides the kimi command after install |
@moonshot-ai/kimi-code-sdk |
packages/node-sdk |
Public TypeScript SDK |
All other workspace packages are private internal packages, are not published to npm, and are excluded via ignore in .changeset/config.json:
@moonshot-ai/acp-adapter@moonshot-ai/agent-core@moonshot-ai/kaos@moonshot-ai/kimi-code-oauth@moonshot-ai/kimi-telemetry@moonshot-ai/kimi-web@moonshot-ai/kosong@moonshot-ai/migration-legacy@moonshot-ai/protocol@moonshot-ai/server@moonshot-ai/server-e2e@moonshot-ai/vis@moonshot-ai/vis-server@moonshot-ai/vis-web
Version impact from internal dependencies must be judged manually. The published artifacts for CLI and SDK bundle internal workspace packages into the artifact itself; runtime dependencies of published packages must not include any @moonshot-ai/* internal workspace packages.
The repository's .changeset/config.json sets updateInternalDependencies: "patch". Because internal packages are not published, you still need to manually select all affected publishable packages in the changeset — do not rely solely on automatic dependency bumps to express user-visible changes.
Example scenarios:
| Change | Changeset selection |
|---|---|
Only modifies TUI behavior in @moonshot-ai/kimi-code |
Add patch / minor / major to @moonshot-ai/kimi-code |
| Only modifies internal packages, no user-visible change in SDK / CLI | Usually no changeset needed |
| Internal package fix changes the CLI user experience | Add a changeset to @moonshot-ai/kimi-code describing the user-visible fix |
| Internal package adds a new capability exposed by the SDK | Add a changeset to @moonshot-ai/kimi-code-sdk |
| SDK behavior change affects CLI user experience | Add changesets to both @moonshot-ai/kimi-code-sdk and @moonshot-ai/kimi-code |
| Provider abstraction change affects SDK / CLI | Add changesets to the affected @moonshot-ai/kimi-code-sdk and/or @moonshot-ai/kimi-code |
| Test-only, internal refactor, docs, or private debug tooling changes | Usually no changeset needed |
Bundled official plugin change under plugins/ (e.g. kimi-datasource) |
No changeset — the plugin is versioned via its own kimi.plugin.json / plugins/marketplace.json and shipped through the marketplace CDN, not the npm package |
Prerequisite: NPM Trusted Publishing (OIDC)
This repository uses npm's Trusted Publishing (OIDC-based) for publishing — no NPM_TOKEN is required.
Configuration steps
- Open each publishable package's page on the npm website, e.g.
https://www.npmjs.com/package/@moonshot-ai/kimi-code. - Go to Settings -> Publishing access.
- Find Automate publishing with GitHub Actions or Add trusted publisher.
- Click Add a new trusted publisher.
Fill in the following:
| Field | Value |
|---|---|
| GitHub Organization | MoonshotAI |
| GitHub Repository | kimi-code |
| GitHub Workflow | release.yml |
| Environment | leave empty |
Each publishable package needs its Trusted Publisher configured once. The current GitHub Actions workflow lives at .github/workflows/release.yml and already has id-token: write configured.
Development Workflow
1. Implement the feature or fix
Complete code, tests, and documentation changes as usual. A changeset is required when the change affects user-visible behavior, public API, dependency ranges, or release artifacts of a publishable package.
2. Generate a changeset
From the repository root:
pnpm changeset
Follow the prompts to choose:
- Which publishable packages this change affects;
- The version bump level:
patch: bug fixes, small changes, follow-up dependency updates;minor: backward-compatible new features;major: breaking changes;
- A user-facing description of the change.
The command creates a .changeset/*.md file that must be committed alongside the code.
3. Commit the changeset
git add .changeset/
git commit -m "chore: add changeset for package release"
git push
Commit messages must follow Conventional Commit style. Do not include any author/agent identity in the commit message.
4. CI generates the release PR
Once the changeset file is merged into main, .github/workflows/release.yml uses changesets/action@v1 to create or update a release PR.
The release PR runs:
pnpm changeset version: bumps publishable package versions and updates changelogs;- Deletes the consumed
.changeset/*.mdfiles; - Uses the title
[CI]: Release packages.
5. Merge the release PR
Once the release PR is merged into main, the same workflow runs:
pnpm install --frozen-lockfilepnpm buildpnpm changeset publish
The packages are then published via npm Trusted Publishing, and a GitHub Release is created.
Manual Publishing (Not Recommended)
Only publish manually when CI is unavailable. Before publishing manually, make sure you are logged into npm locally and using the Node.js and pnpm versions required by the repository.
pnpm run version
pnpm run publish
The underlying changesets commands are:
pnpm changeset version
pnpm changeset publish
The root-level pnpm run publish first runs typecheck, lint, sherif, test, build, and package lint, then runs changeset publish.
Notes
- Every PR that affects publishable-package behavior or public API should include a corresponding changeset.
- Changes under
plugins/(the bundled official plugins such askimi-datasource) do not need a changeset: each plugin carries its own version inkimi.plugin.jsonandplugins/marketplace.jsonand is distributed via the marketplace CDN, separately from the@moonshot-ai/kimi-codenpm package. - Changeset files must be committed to the repository — release PRs are only triggered after they're merged.
- Release PRs require human review and merge; they will not publish automatically.
- Do not add release changesets for private internal packages; only select
@moonshot-ai/kimi-codeand@moonshot-ai/kimi-code-sdk. - If a change in an underlying internal package alters user-visible behavior or public API of a publishable package, add a changeset to the affected publishable package. For example, when a bug fixed in
@moonshot-ai/agent-coreresolves an issue CLI users encounter, add a changeset to@moonshot-ai/kimi-codedescribing the user-visible fix. @moonshot-ai/kimi-codeis the official CLI package name; after a global install it provides thekimicommand.- Make sure each publishable package on npm has a Trusted Publisher configured.