onPanesScroll swallowed any scroll event within 100ms of a programmatic scroll
to dodge the smooth-scroll echo. But the streaming follow re-pins every ~120ms
during the thinking phase, so a user's upward scroll almost always landed inside
a fresh window and was ignored — following never flipped off and the view
snapped back to the bottom. The window is only needed for the async smooth path
(the pill); the synchronous streaming scroll updates lastScrollTop inline and
its echo event is a no-op. Scope the suppression window to smooth scrolls only.
setModel optimistically shows the chosen model, but on failure it only pushed a
warning and left the picker on the new value — so a switch that never reached
the daemon (socket dropped / daemon unreachable) looked like it succeeded.
Capture the previous model and restore it when updateSession throws, keeping the
best-effort status refresh from rolling back a switch that did land.
A volatile assistant/thinking delta whose pre-append offset is 0 while the
projector still believes it is mid-stream means the daemon began a fresh
assistant stream (new turn / retry) whose durable turn.started raced past the
client's cursor on reconnect and was never re-delivered. Every such delta then
had offset < turnTextLen and hit the silent skip path (which, unlike gap, never
triggers a resync), so streaming stayed dead until a full page reload.
Reset the per-turn stream counter when a delta arrives at offset 0 mid-stream,
and clear both counters on turn.ended, so a missed turn boundary can no longer
wedge delta alignment. Adds reconnect-streaming regression tests.
- unconditionally expose /openapi.json and /asyncapi.json\n- add --swagger flag to kimi server run\n- mount Swagger UI at /documentation only when --swagger is passed\n- generate AsyncAPI document from ws-control schemas\n- update tests and docs
The onboarding composer (workspace draft) has no backend session yet, so
setModel() hit the early return and the pick was silently dropped. The
pick is now remembered as draftModel: the status line reflects it
immediately, and the first prompt passes it to createSession so the new
session starts on the chosen model.
Also keep the user's model when the daemon echoes model as '' — both in
/status refreshes and in snapshot syncs racing an optimistic setModel —
and guard the createSession echo the same way.
A user turn whose origin metadata marks it as a skill_activation
(trigger user-slash) no longer dumps the raw XML block into the chat:
the turn shows only the user-provided args plus an "activated skill"
label with the skill name.
Enable with ?debug=1 or localStorage kimi-web.debug=1. A ring buffer
(1000 entries, redacted secrets, truncated payloads) records every REST
call (method/path/requestId/status/envelope code/duration) and WS frame
(lifecycle, outbound control frames, inbound events with session/seq/
offset) as a side channel — request ordering and error handling are
unchanged. The panel offers a filterable timeline, per-entry JSON with
copy, JSONL export, and per-session/event-type aggregation.
- add GET /sessions/{session_id}/skills and POST .../{skill_name}:activate routes\n- add skill protocol schemas (SkillDescriptor, list/activate DTOs) and error codes\n- add ISkillService/SkillService to list and activate skills via core RPC\n- map skill errors to protocol envelopes (40415, 40912)\n- add end-to-end tests covering list, activate, unknown session/skill, and unsupported action
* feat(skill): qualify sub-skill names with parent prefix
- qualify sub-skill names with parent prefix and set isSubSkill metadata\n- hide sub-skills from the model skill listing
- expose sub-skills as dotted slash commands in TUI
- update slash command docs for English and Chinese
- align built-in sub-skill local names with their directories
- add wire.jsonl transcript reader (readWireRecords, readWireTranscript, reduceWireRecords)\n- rewrite MessageService to use wire log instead of live context history\n- add createdAtMsOverride to toProtocolMessage for wire-derived timestamps\n- add transcript cache with LRU eviction and mtime-based invalidation\n- append unflushed live tail when memory is ahead of the wire file\n- degrade to live context view on wire read/parse failures\n- add comprehensive tests for compaction, undo, clear, blob rehydration
The pnpm patch for list-nested code blocks hanging in production builds
is reverted in favor of an upstream fix; the verified recipe is archived
in reports/markstream-nested-codeblock-fix.md.
Upstream report: https://github.com/Simon-He95/markstream-vue/issues/498
Map the /models three-state supports_thinking_type field ('only' /
'no' / 'both', taking precedence over the legacy supports_reasoning
boolean) onto the existing always_thinking capability:
- oauth: parse the field in both /models parsers; 'only' emits
always_thinking alongside thinking, 'no' suppresses thinking even
when supports_reasoning is set, absent falls back to the legacy
boolean. Default thinking selection is forced on for 'only' (and
off for 'no') models during login and provider refresh
- TUI: render the thinking control with a fixed On/Off layout — locked
models show a greyed-out "Off (Unsupported)" segment, and
non-thinking models mirror the style with "On (Unsupported)"
- agent-core: clamp thinkingLevel at the getter so a stale
thinking-off config can never reach the request builder, status
events, or subagent inheritance
- acp-adapter: derive alwaysThinking from capabilities, collapse the
thinking select to a single locked "on" entry, and ignore off
requests for locked models while re-emitting the snapshot
* chore: ignore .worktrees directory
* feat: add tips banner below welcome panel on startup
- Fetch active/fallback tips from the configured CDN with a 3s timeout.
- Filter tips by semver, client version, and date range.
- Render the banner directly below the welcome panel on startup/resume.
- Support tag, multi-line text, subtext, automatic wrapping, and narrow-terminal safety.
* refactor(tui): use theme color methods in banner component
Replace raw chalk calls with currentTheme helpers: tag uses
boldFg('primary'), main text uses boldFg('textStrong'), and subtext
uses fg('textDim') without stacking the dim modifier on the already
dim shade. Strengthen tests to assert the exact themed ANSI output.
- Open the active server URL when `kimi server run` finds an existing local server\n- Distinguish background vs foreground server mode in conflict message\n- Show mode-appropriate stop command (`kimi server stop` or `pkill`)\n- Always include host in lock file contents\n- Add tests for already-running server handling
- add host field to LockContents and acquireLock options\n- record bind host in lock file during server startup\n- remove JSDoc block comments across service manager modules\n- update start.test.ts to assert host is persisted in lock
- collect and embed server web assets into the native SEA binary\n- add web-assets manifest and collection scripts for native builds\n- make swagger/swagger-ui registration conditional in server start\n- enrich lifecycle commands with service URL, running state, log path, and notes\n- add --no-open flag to control auto-opening web UI after install\n- introduce ServiceUnavailableError and ProgramServiceManager\n- update service managers (launchd, systemd, schtasks) with new status fields
- add terminal protocol schemas (REST and WebSocket controls)\n- add TerminalService with node-pty backend and frame buffering\n- add REST routes for terminal CRUD operations\n- add WebSocket terminal attach, input, resize, and close handlers\n- add e2e and unit tests for terminal lifecycle and I/O
- switch createServerLogger from pino transport target to in-process pretty stream\n- update changeset to cover @moonshot-ai/server\n- add test asserting pretty logger does not use ThreadStream\n- rename cli server test description
add pino-pretty to apps/kimi-code dependencies to fix server startup when pretty logging is enabled\nadd regression test verifying the dependency is declared\ninclude changeset for patch release
Add a third UI theme "kimi" implementing the official Kimi design
language (Quiet Utility) with token-exact values from kimi-design-skill
tokens.json v0.2.0:
- Interaction accent = kimiDark (black in light / white in dark);
kimiBlue stays reserved for brand/data-viz (::selection only)
- Gray user bubbles (bubbleGrayPc), flat tool cards and dialogs; shadows
only on the composer (shadow.inputDefault) and floating menus
(shadow.small); dark elevated surfaces use background.tertiary
- PingFang SC for UI, Geist Mono for code (bundled JetBrains fallback)
- Modern's de-terminalization rules are shared via
:is(html[data-theme="modern"], html[data-theme="kimi"]) — identical
specificity, so Modern renders exactly as before; kimi token blocks
sit after the data-accent rules and pin the accent (the accent picker
is hidden while kimi is active)
Theme pickers (sidebar popover, mobile sheet) and onboarding now offer
Modern/Kimi only. Terminal remains the CSS baseline and a persisted
'terminal' choice still loads; it's just no longer offered in the UI.
Also: file-preview pane default width now follows half the window.
- update existing changeset names and contents from daemon to server\n- rename changeset files to replace daemon with server\n- update services AGENTS.md and DI README references
- rename workspace package `@moonshot-ai/daemon` to `@moonshot-ai/server`\n- rename `@moonshot-ai/daemon-e2e` to `@moonshot-ai/server-e2e`\n- replace `kimi daemon` and `kimi web` with `kimi server run`\n- keep `kimi web` as alias for `kimi server run --open`\n- add `kimi server install/uninstall/start/stop/restart/status`\n for launchd (darwin), systemd (linux), and schtasks (win32)\n- update dev scripts, kimi-web stub, documentation, and service naming checks\n\nBREAKING CHANGE: the `kimi daemon` command and `@moonshot-ai/daemon`\npackage are removed; use `kimi server` and `@moonshot-ai/server`.