Commit graph

441 commits

Author SHA1 Message Date
_Kerman
d69ed4b331 fix(agent-core-v2): apply KIMI_CODE_CUSTOM_HEADERS and host identity headers
Port v1's provider-manager outbound header logic to agent-core-v2 so
`KIMI_CODE_CUSTOM_HEADERS` and host identity headers are applied to
outbound LLM requests, closing the migration gap from #1186:

- env `KIMI_CODE_CUSTOM_HEADERS` is the lowest-precedence header layer;
- host identity headers (User-Agent + X-Msh-*) are sent for Kimi
  providers, only the User-Agent for every other provider — a Kimi
  provider routed through the Anthropic protocol still gets the full
  set, matching v1;
- provider `customHeaders` always win on conflict.

Host headers are seeded by the CLI via `createKimiDefaultHeaders` and a
new `IHostRequestHeaders` App-scope token (defaulting to empty), so the
model resolver can layer them without the host threading them through
every call site.
2026-07-09 14:34:24 +08:00
liruifengv
79a4125b41 feat(kimi-web): add status-aware browser notifications (#1479)
* feat(kimi-web): add approval notification storage key and i18n copy

* feat(kimi-web): add approval notification helpers and tests

* feat(kimi-web): wire approval notifications and guard completion alerts

* fix(kimi-web): extract shouldNotifyCompletion helper and add tests

* feat(kimi-web): add approval notification settings toggle

* chore(kimi-web): add changeset and tidy notification module comment

- Align approval notification tag with spec (kimi-approval-${approvalId})

- Update module header to describe all three notification kinds

* fix(kimi-web): make notifications fire reliably

- Key completion notification tags by turn (sid + promptId) and question
  tags by request id, so a stale notification left in the notification
  center no longer swallows every follow-up alert in the same session
- Suppress notifications only while the window is actually focused, not
  merely visible (document.hasFocus() on top of visibilityState)
- Play the attention sound when a tool needs approval, matching the
  completion and question sounds

* chore(kimi-web): simplify changeset
2026-07-09 13:59:37 +08:00
liruifengv
4d60160e55 docs(changelog): sync 0.23.3 and shorten OAuth error entry (#1509) 2026-07-09 13:59:37 +08:00
github-actions[bot]
ebb3c8fada ci: release packages (#1507)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-07-09 13:59:37 +08:00
haozhe.yang
536c4c0d44 feat(v2): auto-mint session ids and harden print-mode background drain
- make CreateSessionOptions.sessionId optional; SessionLifecycleService.create and fork now mint `session_<lowercase-uuid>` via a shared createSessionId helper, so edge layers stop minting their own ids (drop randomUUID in the v2 harness, ulid in kap-server)
- rework V2Session.waitForBackgroundTasksOnPrint to re-enumerate each round, suppress terminal notifications while waiting, and bound the drain by [task].print_wait_ceiling_s (default 1h) instead of a hardcoded 30s cap, so kimi -p can run long tasks to completion without being steered into a new turn
- add v2-session unit tests; seed session/agent/bootstrap context in the tool-dedupe harness for the real executor
2026-07-09 13:10:49 +08:00
7Sageer
193d4418a5 Revert "fix: make release-e2e scenarios pass under agent-core-v2"
This reverts commit ec9dae72ab.
2026-07-09 11:57:05 +08:00
7Sageer
ec9dae72ab fix: make release-e2e scenarios pass under agent-core-v2
Three independent fixes for release-e2e failures that only appeared with the experimental v2 engine (KIMI_CODE_EXPERIMENTAL_FLAG):

- agent-core-v2: register the KIMI_MODEL env overlay statically so it takes effect even when ModelService is not instantiated (the DI layer does not auto-instantiate Eager services). Fixes wire-llm-request-trace.

- cli: omit the leading system.version meta line in stream-json prompt mode so the role sequence stays clean. Fixes stream-json-cron.

- agent-core-v2: honor --skills-dir via a new explicit skill source seeded from the host. Fixes interactive-skills-dir.

Cherry-picked from 2a7232737 (v2-migration), excluding the node-sdk V2Host change (not applicable on this branch).
2026-07-09 11:40:43 +08:00
liruifengv
44e922b1ca
fix(agent-core-v2): port v1 parity fixes for hooks, anthropic, thinking config and add-dir (#1504)
* fix(agent-core-v2): hide console window when running hooks on Windows

Port the v1 hooks runner fix: extract buildHookSpawnOptions and pass
windowsHide:true so hook child processes no longer flash a console
window on Windows, mirroring the node-local process host defaults.
Includes the same regression tests as v1.

* fix(agent-core-v2): port anthropic max_tokens ceiling and override fixes

Port two v1 kosong fixes to the v2 anthropic provider:

- Fall back to the nearest lower catalogued minor when resolving the
  Claude output ceiling, and catalogue Opus 4.8's documented 128k cap,
  so an uncatalogued minor no longer drops to the family baseline.
- Treat an explicit defaultMaxTokens as the final max_tokens value
  instead of clamping it to the built-in ceiling.

Mirrors the v1 regression tests in a new anthropic-max-tokens test file.

* refactor(agent-core-v2): converge thinking config to enabled/effort

Port the v1 thinking-config overhaul (#1132's config side) to v2:

- ThinkingConfigSchema becomes { enabled, effort, keep }; the mode enum,
  the separate defaultThinking section, and the KIMI_MODEL_THINKING_MODE /
  KIMI_MODEL_DEFAULT_THINKING env bindings are removed.
- The effort resolver drops the mode/defaultThinking branches and no
  longer normalizes a requested 'on' to a concrete effort in core; 'on'
  is taken verbatim and normalization stays at the UI boundary.
- OAuth login/refresh and catalog refresh now persist the thinking.enabled
  value computed by the shared oauth apply/restore logic instead of
  dropping it and writing the removed default_thinking key, so
  [thinking] enabled = false actually disables thinking and the login
  default survives on disk.

Mirrors the v1 resolver regression tests and adds a persistence
regression for the refresh path.

* docs(agent-core-v2): fix stale loop-event comments after wire parity

The v1.4 wire-parity alignment switched the v2 live loop to stream turns
as context.append_loop_event records, but three comments still described
the old world (restore-only Op, "v2 never emits loop events"). Update
them to match the actual write path: non-loop appends use append_message,
the loop persists loop events byte-compatible with v1, and the fold runs
both at live dispatch time and on replay.

* fix(agent-core-v2): load workspace additional dirs on session create and resume

The /add-dir command persisted remembered dirs to .kimi-code/local.toml,
but session materialization never read them back and offered no caller
additionalDirs entry point — a remembered dir silently stopped applying
to new, resumed, and forked sessions.

Mirror v1's createSession/resumeSession: merge the project-local
local.toml dirs with caller-supplied additionalDirs (relative paths
resolve against workDir) and seed the session workspace context in
materializeSession, so create/resume/fork all pick them up. A broken
local.toml fails the create loudly with CONFIG_INVALID, same as v1.
Tests mirror v1's runtime coverage for the load/merge/dedupe/resume/fork
scenarios.

* fix(kimi-code): forward create-session additional dirs from the v2 harness

The in-process v2 print-mode harness dropped the SDK CreateSessionOptions
additionalDirs when calling ISessionLifecycleService.create, so --add-dir
never reached the v2 resolver. Pass it through.
2026-07-08 21:57:05 +08:00
github-actions[bot]
8e00de7bfe ci: release packages (#1468)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-07-08 17:31:36 +08:00
qer
0a8fcead30 feat(web): prefix skill slash commands with skill: to distinguish them from built-in commands (#1492) 2026-07-08 17:31:36 +08:00
qer
e9369ce93d fix(web): composer model switch also updates global default model (#1491)
* fix(web): composer model switch also updates global default model

The composer model switcher still switches the active session's model via
POST /sessions/{id}/profile (awaited, so the model pill reflects the result),
and additionally fires POST /api/v1/config with { default_model } as a
fire-and-forget side effect so new sessions inherit the chosen default. The
config request is skipped when the model already matches the current default.

* fix(web): route ModelPicker overlay selection through the default-model update

The overlay opened from the composer's "More models" row (and /model) is a
continuation of the same switch flow, so its selection now also bumps the
global default model instead of only switching the active session.

* fix(web): only persist the default model after a confirmed session switch

setModel now returns whether the switch was accepted (true for the draft
path), so the composer flow no longer writes a stale or invalid model alias
into the global config when the session-level switch failed and rolled back.
2026-07-08 15:45:24 +08:00
liruifengv
bec7319000 feat(web): redesign cron reminder as a message bubble (#1480)
* feat(web): redesign cron reminder as a message bubble

Restyle the cron trigger notice as a right-aligned user-style message bubble that shows the scheduled prompt in full (wrapping across lines), with a small meta row beneath it for the schedule, status, job id and run time. Extract a shared MessageTime component used by both user messages and the cron reminder so the timestamp format and click-to-expand behavior stay consistent, and give the CronCreate/CronList/CronDelete tools distinct calendar icons.

* refactor(web): render cron reminders only as standalone turns

Remove the embedded cron block path from the web transcript projector so cron reminder fires always render through the standalone right-aligned bubble path.

* chore(web): simplify cron redesign changeset
2026-07-08 15:45:24 +08:00
qer
27b0237d80 feat(web): support Enter key to confirm archive and other dialogs (#1490) 2026-07-08 15:45:24 +08:00
qer
ca09e31cb7 fix(kimi-code): exit 1 when a headless (-p) turn fails (#1483)
Headless (`kimi -p`) failures could exit with code 0 when the event loop
drained during the shutdown cleanup (e.g. telemetry's unref'd retry backoff
when the network is blocked), because the rejection never reached the
process.exit(1) call. Set the failure exit code before any await in both
the run-prompt catch and the main catch, and keep the cleanup timeout ref'd
so the loop stays alive long enough for the rejection to propagate.
2026-07-08 15:45:24 +08:00
Kai
536757b8f3 fix(agent-core): report EXIF-rotated image dimensions and raise edge cap to 3000px (#1460)
* 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.
2026-07-08 15:45:23 +08:00
haozhe.yang
2ebaf7221e feat(cli): emit version first in experimental v2 prompt mode
- write `kimi version <version>` to stderr first in text mode
- emit a `system.version` meta line first in stream-json mode
- gate behind isKimiV2Enabled() so the default v1 path is unchanged
2026-07-08 15:45:23 +08:00
haozhe.yang
f054fe9a21 feat(kap-server): log envelope code instead of HTTP status
- add custom onSend/onResponse access logging that records the envelope code
- disable Fastify built-in request logging (every response is HTTP 200)
- drop pino-pretty; logger always emits newline-delimited JSON
- rename dev:server-v2 script to dev:kap-server
2026-07-08 10:47:32 +08:00
haozhe.yang
79ffb7b347 chore: fix lint issues across packages
- remove unused imports and variables
- add void to floating promises
- drop redundant return-await
- bind unbound method references
- replace == null with strict null/undefined checks
- add explicit sort comparators and Array.from helpers
- rename background_tasks capability to tasks
- migrate event sink to event bus in tests
2026-07-07 23:37:34 +08:00
haozhe.yang
5b57a3297a feat(cli): run kimi -p on agent-core-v2 behind experimental flag
- add PromptHarness/PromptSession interfaces so the print driver is
  decoupled from the concrete SDK harness/session
- add an in-process agent-core-v2 harness (bootstrap + session +
  prompt/goal/permission adapters) under cli/v2/
- adapt v2 IEventBus events to the v1 Event union, with unit tests
- select the engine in createPromptHarness via isKimiV2Enabled()
  (KIMI_CODE_EXPERIMENTAL_FLAG), lazy-importing v2 off the default path
2026-07-07 23:37:03 +08:00
haozhe.yang
554d998b16 Merge remote-tracking branch 'origin/main' into kimi-code-v2 2026-07-07 21:08:35 +08:00
qer
11c6a37ce0
fix(web): dismiss stale connection error toast after ws reconnect (#1474) 2026-07-07 20:47:56 +08:00
haozhe.yang
304883ab81 refactor(kap-server): rename server-v2 package to kap-server
- rename packages/server-v2 to packages/kap-server and update every
  @moonshot-ai/server-v2 reference across apps, packages, flake.nix,
  and build scripts
- remove the experimental `kimi v2` CLI prefix and its tests from the
  apps/kimi-code main entry
- add legacy BackgroundTask protocol types and the `blocked` turn
  interrupt reason for cross-engine compatibility
2026-07-07 16:56:19 +08:00
liruifengv
ee385456d0
refactor(web): migrate icons to unplugin-icons (#1467)
* refactor(web): migrate icons to unplugin-icons

Replace the hand-written gen-icon-data.mjs + @iconify/utils runtime
rendering with unplugin-icons build-time imports. The public API
(<Icon name>, iconSvg, IconName, SIZE_PX, NAME_TO_REMIX) is unchanged;
127+ call sites are untouched.

- add unplugin-icons@^23.0.0 (devDep) + Vite Icons() plugin (compiler: vue3)
- rewrite src/lib/icons.ts: static ~icons/ri/* imports (component + ?raw)
  for 56 distinct Remix icons across 59 IconName entries
- Icon.vue renders <component :is> with unknown-name fallback
- append ICON_GROUPS export for DesignSystemView catalog
- DesignSystemView: v-for catalog, remove legacy-script references
- delete gen-icon-data.mjs, gen-icon-catalog.mjs, icon-data.ts,
  gen:icons script
- remove @iconify/vue and @iconify/utils from dependencies; move
  @iconify-json/ri to devDependencies
- drop Icon.vue from check-style ICON_EXEMPT (no hand-written <svg>)

* refactor(web): drop unused NAME_TO_REMIX icon mapping

NAME_TO_REMIX was a Record<IconName, string> table introduced to map
internal icon names to their ri: ids. After the unplugin-icons migration
it has no production consumers — only icons.test.ts imported it (for two
drift tests) and DesignSystemView mentioned it in descriptive copy. The
ICONS table already conveys the same ri: id via each entry's paired
component + ?raw imports (e.g. RiFolderOpenLine / RawFolderOpenLine).

- remove NAME_TO_REMIX const from src/lib/icons.ts (-63 lines)
- remove NAME_TO_REMIX import + describe block from icons.test.ts
- update DesignSystemView §02 copy: describe the import-pair idiom and
  stop claiming ICON_GROUPS is sourced from NAME_TO_REMIX

* chore: add changeset for web icon migration

* chore(nix): bump pnpmDeps hash for unplugin-icons

Adding unplugin-icons changed pnpm-lock.yaml, so the fixed-output
pnpmDeps derivation hash is stale. Update to the hash reported by the
Nix Build CI run.
2026-07-07 16:44:56 +08:00
github-actions[bot]
08b3c3fc53
ci: release packages (#1444)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-07-07 15:36:15 +08:00
Luyu Cheng
e9ef9399d0
fix(agent-core): harden goal-mode budget and outcome flow (#1456) 2026-07-07 15:23:57 +08:00
qer
16dc940834
fix(web): recover from stale background websocket (#1451) 2026-07-07 14:48:22 +08:00
qer
809a88cb34
fix(web): run new-session slash actions (skills, /goal, /btw) from the empty composer (#1445)
* fix(web): activate slash skills on the new-session screen

The composer now lists workspace skills before a session exists, but
activating one from the empty-session composer silently did nothing:
activateSkill() short-circuits when there is no active session id, so
the command was cleared with no turn started.

Mirror the first-prompt path: when a slash skill is activated with no
active session (but a workspace is active), create the session first and
activate on the new session id. Extract the shared session-creation
block out of startSessionAndSendPrompt into createDraftSession and add a
startSessionAndActivateSkill counterpart; activateSkill now also accepts
an explicit session id so a concurrent session switch can't redirect it.

Unrecognized '/cmd' text still falls through to a plain-text message as
before.

* fix(web): persist draft modes when a new session is opened via skill

The skill-activate request carries only `args`, so for a skill launched
from the new-session composer createDraftSession's local plan/swarm maps
never reached the daemon, and the first skill turn ran at default modes
while the UI showed them enabled.

Persist the draft plan/swarm modes to the new session's profile inside
createDraftSession (by the new session id), and teach persistSessionProfile
to take an explicit session id so a concurrent session switch during the
snapshot load can't write the patch to the wrong session. Plain prompts
already send planMode/swarmMode on the prompt request itself, so this is
redundant but harmless on the first-prompt path. Goal mode is a one-shot
flag consumed per send, not a profile field, so there is nothing to
persist for it.

Add coverage that the profile is persisted before activation when draft
plan/swarm modes are enabled.

* fix(web): await draft-mode profile POST before activating a skill

The previous fix wrote the draft plan/swarm modes to the new session's
profile but fire-and-forget: persistSessionProfile returned immediately
after starting POST /profile, so startSessionAndActivateSkill sent
:activate without waiting. Since skill activation carries only args, the
daemon could process :activate (and start the turn) before
applyAgentState from /profile finished, running the first skill turn at
default modes while the UI showed them enabled.

Make persistSessionProfile return the update promise and await it inside
createDraftSession, so any origin that follows (the skill activation, or
a plain prompt) only starts after the profile is applied. Existing
callers still fire-and-forget via `void persistSessionProfile(...)`.

Test coverage now blocks activation behind a deferred profile POST and
asserts it is issued only after the profile resolves.

* refactor(web): persist draft modes on the skill path only

Move the awaited plan/swarm profile write out of createDraftSession and
into startSessionAndActivateSkill. The create path now only builds the
session and mirrors draft modes into the per-session maps, matching the
old first-prompt behavior: plain prompts already send planMode/swarmMode
on the prompt request itself, so a /profile write there was redundant.

The profile write stays on the one path that needs it: skill activation
carries only args, so the draft modes must be stored on the new session
and applied before :activate is sent.

* fix(web): persist draft permission and thinking for new-session skill

/auto, /yolo, and /thinking on the new-session composer only update
rawState (there is no session to persist to yet). A plain first prompt
still honors them because submitPromptInternal sends permissionMode and
thinking on the request, but skill activation carries only args, so the
first skill turn was running at daemon defaults.

Include permissionMode and thinking in the awaited profile patch alongside
planMode/swarmMode before activating, so the skill turn matches the
controls the UI shows.

* fix(web): start /goal from the new-session composer

createGoal() short-circuited when there was no active session, so
`/goal <objective>` from the empty-session composer silently cleared and
ran nothing — the same bug class as the slash-skill activation fixed
earlier in this PR.

Mirror startSessionAndSendPrompt: when no session exists but a workspace
is active, create one first then target it with the goal profile update
and the objective prompt. Send via submitPromptInternal with the explicit
sid so a concurrent session switch during creation can't redirect it.
Plain prompts already carry their own permissionMode / thinking / plan /
swarm, so no profile fallback is needed here.

* fix(web): use active-workspace fallback for empty-composer /goal

On a fresh-booted empty workspace, load() never writes
rawState.activeWorkspaceId (no most-recent session to anchor it). The
UI still has a usable workspace via the client-wide activeWorkspaceId
computed, which falls back to the first sidebar-visible workspace — but
createGoal read the raw value directly and silently no-op'd when it was
null.

Normal first prompts and skill activations didn't hit this because
App.vue passes the computed activeWorkspaceId in. Make createGoal use
the same fallback so a first-session `/goal <objective>` works in empty
workspaces too.

* fix(web): start /btw from the new-session composer

openSideChat() reads rawState.activeSessionId directly, so `/btw
[<question>]` from the empty-session composer silently no-oped — it
still set detailTarget to 'btw', leaving the side chat panel open but
empty.

Add startSessionAndOpenSideChat(workspaceId, prompt?) that creates the
parent session first, then calls a new sideChat.openSideChatOn(sid,
prompt) which targets the explicit parent session id (race-safe against
a concurrent session switch, like the skill activation case). Route
through it from the empty-composer branch in openSideChatTab.

Side-chat prompts now also carry model / thinking / permissionMode /
plan / swarm (via a shared sendSideChatPromptOn), so a BTW first turn
matches the UI even when the parent /profile is still in flight. Unlike
skill activation, this means the BTW path needs no profile fallback.

Tests: startSessionAndOpenSideChat creates a session then opens BTW on
the new id, works without an initial question, and is a no-op for an
unknown workspace; sendSideChatPromptOn carries the runtime controls
on the submitted prompt.

* fix(web): preserve send queue when creating goals

Switching createGoal from sendPrompt to submitPromptInternal avoided the
activeSessionId race during the empty-composer create window, but it also
bypassed sendPrompt's queue guard: when a goal is created against an
already-active session that is running another turn, the prompt posted
immediately instead of being locally queued.

Restore the guard for the overwhelmingly common case (the goal still
targets the active session): route through sendPrompt when activeSessionId
still matches the resolved sid, which enqueues when the session is
running or a prompt is already in flight. Only fall back to the explicit-
session submitPromptInternal(sid) when activeSessionId moved during the
create window, so a concurrent session switch can't redirect the goal
prompt. The newly-created session is idle+not-in-flight in that branch,
so the explicit submit does not race another turn.

Add a regression test: createGoal against a running existing session
enqueues instead of submits.

* fix(web): coerce thinking for skill/BTW and handle goal creation failures

Three follow-ups from review:

- createGoal now wraps createDraftSession in a try/catch: App.vue invokes
  it fire-and-forget, so a rejection from session creation previously
  leaked as an unhandled rejection with no operation failure surfaced.
  Mirrors the skill / BTW / first-prompt paths that already wrap it.

- startSessionAndActivateSkill coerces the draft thinking level against
  the new session's model before persisting the profile (via
  coercePromptThinking), matching what the first-prompt path submits.
  A value carried over from another/default model (e.g. 'max' from an
  effort model) would otherwise be persisted verbatim and the first
  skill turn would run at a level the UI wouldn't send for this model.

- sendSideChatPromptOn coerces thinking against the parent session's
  model the same way normal prompts do (coerceThinkingForModel against
  the model catalog). Model catalog threaded in via a new 'models' dep
  on useSideChat. Same reasoning: stale rawState.thinking must not be
  submitted raw into a BTW first turn.

* fix(web): clear staged goal mode when submitting explicit /goal

When the empty composer has goal mode staged (e.g. the user runs bare
`/goal`, then `/goal <objective>`) and an explicit objective is then
submitted, createDraftSession copies draftModes.goalMode into
goalModeBySession[sid]. createGoal then updateSession(goalObjective) for
the explicit goal, and sendPrompt(trimmed) re-enters submitPromptInternal
which sees goalModeBySession[sid] still set and POSTs a second
goalObjective. The daemon rejects that as an existing goal, the catch in
submitPromptInternal rolls back the optimistic message, and the user's
objective prompt never lands.

Clear the staged goalModeBySession[sid] flag right after the explicit
update, since `/goal <objective>` has exactly the same effect as the
flag's consumption.
2026-07-07 14:18:40 +08:00
7Sageer
ebe16e39ff fix(server-v2): treat already-authenticated OAuth start as success
When the Kimi OAuth toolkit short-circuits via its `ensureFresh` fast path (the cached refresh token is still usable), `toolkit.login` resolves without firing `onDeviceCode`. The v2 `OAuthService.startLogin` previously rejected in this case, so `POST /api/v1/oauth/login` returned a 500 that the web client swallowed and mislabeled as "current daemon does not support login". CLI-authenticated users could not (re)enter the v2 daemon through the web UI.

Treat that path as already authenticated: provision the managed provider (idempotent), refresh OAuth provider models so `defaultModel` is seeded (`/auth` then reports ready, matching v1 parity), and return a new `status: 'authenticated'` `OAuthFlowStart` variant so the web LoginDialog can skip the device-code UI entirely instead of falling into its catch-all "unsupported daemon" state.

The protocol `OAuthFlowStart` schema is now a discriminated union on `status`; kimi-web's wire / composable / LoginDialog types are updated to handle the new `authenticated` branch.

Tests: updated the v2 OAuthService test that previously asserted the buggy rejection to instead assert the fast path returns authenticated and provisions the provider.
2026-07-07 14:16:48 +08:00
Kai
65d30177ad
feat(agent-core): record llm request trace in wire.jsonl (#1448)
* feat(agent-core): record llm request trace in wire.jsonl

Add three observability record types so every request sent to the model
can be reconstructed from the wire log at the logical-request level:

- llm.tools_snapshot: content-addressed snapshot of the top-level tools
  table as sent (post deferred-strip), written once per unique table
- llm.request: one record per outbound request (retries, strict resends,
  and compaction rounds included) carrying the effective request params
  and hash links to the system prompt and tools snapshot
- mcp.tools_discovered: the server's verbatim tools/list result plus the
  agent's gating (allow-list, collisions), deduplicated by content hash

Observability records never feed state rebuild; replay only restores the
write-dedup cursors. The records/types.ts contract now documents the two
record classes explicitly (persisted is not the same as replayed).

Recording happens at the single Agent.generate choke point. The
LLMRequestLogFields side channel gains kind/projection/maxTokens/
droppedCount, chatWithRetry preserves caller-set fields, and compaction
tags its requests. The vis wire view renders the new record kinds.

* fix(agent-core): record the provider-clamped completion cap in the request trace

The llm.request trace recorded the client-requested budget cap, but
chat-completions providers tighten the actual wire value inside
withMaxCompletionTokens (remaining-context sizing, transport ceilings,
model-default resolution) — with the default budget the clamp is active
on nearly every non-empty-context request, so the recorded value did not
match what was sent.

Providers now expose the effective cap they computed as a readonly
maxCompletionTokens field on the clone, and the recorder reads it from
the effective provider at the Agent.generate choke point. This replaces
the side-channel recomputation, which is removed along with the
appliedCompletionBudgetCap helper.

* fix(agent-core): park pre-replay MCP discovery records and hash the collision outcome

Two wire-hygiene fixes for the mcp.tools_discovered trace:

Parking: the real Session ordering connects MCP servers concurrently with
agent construction, so ToolManager can observe a connected server before
agent.resume() has replayed the wire. Recording at that point bypassed
the restored dedup cursor (duplicating a 1-50KB record on every resume)
and appended a stray metadata record ahead of replay. AgentRecords now
exposes a one-shot opened latch — set when replay completes (after the
migration rewrite flushes) or when the first live record is logged — and
ToolManager parks discoveries until then, re-running the dedup check at
drain time. A frozen range-limited replay never opens; those agents are
transient previews.

Collision hashing: the dedup hash now covers the collision outcome, not
just the raw list and allow-list. Collisions depend on which other
servers hold a sanitized qualified name at registration time, so a
server can re-register with identical tools but a flipped outcome; that
gating change must produce a new record instead of being suppressed.

* fix(agent-core): skip the request trace for pre-flight-aborted calls

Mirror kosong generate()'s pre-flight abort check at the Agent.generate
choke point: a call whose signal is already aborted never reaches the
wire (generate throws before dispatching), so it must not leave an
llm.request/llm.tools_snapshot trace or a diagnostic log line claiming a
request was sent. Recording stays before dispatch for every call that
passes the gate, preserving the crash-safety of the trace.

* chore(agent-core): remove a leftover adaptive-thinking override hook

The adaptiveThinkingOverride option was a temporary local hook explicitly
marked for removal before commit. Nothing passes it, so resolution falls
back to the alias-level adaptiveThinking value in all cases; drop the
option and the dead indirection.

* fix(kosong): derive the exposed completion cap from generation kwargs

maxCompletionTokens was a field stored only by withMaxCompletionTokens,
so caps that reach the wire through other paths were invisible to the
request trace: with completion budgeting disabled via env, Anthropic
still sends the constructor-resolved max_tokens (required by the
Messages API), and constructor-level kwargs like OpenAILegacyOptions
maxTokens were likewise unreported.

Replace the stored field with a getter derived from each provider's
generation kwargs — the single source the request body reads — covering
constructor defaults, direct withGenerationKwargs configuration, and
budget application in one place. Kimi mirrors its request-time legacy
max_tokens alias normalization; openai-legacy reuses the same
normalizeGenerationKwargs the request path uses.

* feat(agent-core): add thinkingKeep passthrough for Kimi providers and update tests
2026-07-07 14:09:19 +08:00
qer
9b18dc46cf
fix(kimi-web): shrink collapsed thinking block on mobile (#1461) 2026-07-07 14:04:00 +08:00
liruifengv
8d58d6ad4c
chore(dev): open monorepo root when running make dev (#1458)
* chore(dev): open monorepo root when running make dev

The dev runner spawned the tsx child process with cwd pinned to
apps/kimi-code, so the TUI always opened apps/kimi-code as its working
directory. Switch the child cwd to the monorepo root and pass the
tsconfig, raw-text loader, and entry point as absolute paths so their
resolution does not depend on cwd.

This only affects the local dev runner; the published CLI is unchanged.

* fix(dev): pass --import preload as a file URL

Node resolves `--import` preloads as ESM specifiers. On Windows, the
absolute path produced by path.resolve() looks like a `c:` URL scheme
and crashes the dev CLI with ERR_UNSUPPORTED_ESM_URL_SCHEME before
src/main.ts loads. Convert the preload to a file:// URL via
pathToFileURL so it resolves on every platform.

Addresses codex review on PR #1458.
2026-07-07 13:37:40 +08:00
haozhe.yang
b37345e5d5 refactor(agent-core-v2): remove domain barrels via debarrel tool
- add scripts/debarrel.mjs (ts-morph) to rewrite #/<dir> imports to
  precise leaf specifiers and regenerate src/index.ts
- rewrite src and test consumers across the package to import from leaf
  files instead of domain barrels
- delete src/app/event/index.ts barrel and load its leaves from the
  package entry
- extract applyPromptMetadataUpdate so /api/v1 prompt submission derives
  the easy session title, matching /api/v2
- rename the v2 prefix to beta in the CLI prefix tests
2026-07-07 13:33:18 +08:00
liruifengv
260a80793a
fix(cli): respect --skills-dir in interactive mode (#1457)
The interactive shell entry point dropped opts.skillsDirs when building the harness, so --skills-dir only took effect in prompt mode. Forward it so the flag works in the TUI as documented.
2026-07-07 13:02:57 +08:00
liruifengv
7a65e0d1c0
feat(tui): update permission mode copy and reorder /auto /yolo (#1450) 2026-07-07 12:26:25 +08:00
Kai
743f66e547
refactor: move tool-result metadata into a structured note side channel (#1437)
* fix: stop rendering <system> notes from tool results in the terminal and web UIs

Tool results carry <system> blocks as side-channel notes for the model (ReadMediaFile summaries, Read status, MCP image captions, error/empty sentinels). Keep them in history for the model, but strip them at every core-to-UI boundary so they no longer render as plain text. vis is intentionally left untouched to preserve the model's-eye view for debugging.

* fix: keep error/empty status text visible when stripping tool-result <system> tags

Unwrap the tool error/empty sentinels (<system>ERROR: ...</system>, <system>Tool output is empty.</system>) instead of deleting them: keep the human-readable text and drop only the tags. Otherwise a failed or empty tool result rendered as a blank output, indistinguishable from a rendering bug. The model still reads the wrapped form in history.

* refactor: move tool-result metadata into a structured note side channel

Tool-produced model-facing metadata (ReadMediaFile summaries, Read status
lines, MCP image-compression captions) was baked into tool output as
<system> text, so every UI had to strip it back out and three copies of
the model-view normalization had silently drifted apart.

- ExecutableToolResult gains `note`: content rendered to the model but
  never to UIs; records and history now store the raw output plus the
  structured isError/note fields
- the model view is rendered exactly once at the LLM projection boundary
  by renderToolResultForModel; the transcript and vis hand-copies are
  deleted (vis now calls the same function for its model view, fixing
  their drifted empty-output checks)
- ReadMediaFile / Read / MCP captions write `note`; tool outputs stay
  pure data, and text-only results keep a single text part (note joined
  with a newline) so provider tool content stays a plain string
- all UI-side <system> stripping is removed; failed tools show their own
  error text with the structured isError flag
- wire protocol 1.4 -> 1.5 migrates existing records' tool-produced
  <system> blocks into `note` on resume

* fix: provider-neutral wording, no wire migration, direct optional fields

- "The attached image was downsampled" replaces directional wording that
  depended on provider serialization order (inline media vs flatten-and-
  re-attach)
- drop the 1.4 -> 1.5 wire migration: legacy records replay verbatim, so
  the model view of old sessions stays byte-identical to what the model
  originally saw and UIs show the legacy <system> text as-is; this also
  removes the risk of the migration misclassifying user data that quotes
  tool metadata, and the additive note field needs no version bump
- pass optional result fields as undefined instead of conditional spreads
  (repo convention)

* fix: enforce the note contract at the trust boundary; narrow the TUI system-tag guard

- normalizeToolResult now keeps a note only when it is a non-empty string:
  tools and finalize hooks are arbitrary JS, and a malformed note (null,
  number, object) would previously persist into the record and crash every
  subsequent LLM projection of the session. Everything downstream now
  trusts note to be string | undefined.
- the TUI tool body suppression matches the full <system-reminder> tag
  instead of any <system prefix: reminder piggy-backing stays hidden,
  while real output that merely starts with a literal <system> tag (file
  contents, MCP text) stays visible, covered through the real
  ToolCallComponent path.

* fix: return MCP compression captions as data instead of extracting them from text

compressImageContentParts now returns { parts, captions } — captions come
back from the compressor as structured data and are never inserted into
the parts, so the MCP pipeline no longer pattern-matches text to move
them into the note side channel. Tool output that merely quotes a
caption (a doc, a log, a test fixture) stays verbatim in the output.
Also corrects the stale claim that prompt ingestion uses this helper
(it compresses per image while constructing the part).

* docs: correct the image-compression re-export comment; export CompressedContentParts

The package-root comment still described compressImageContentParts as the
input-stage helper every ingestion site calls; prompt ingestion compresses
per image with compressBase64ForModel / compressImageForModel, and the MCP
pipeline is the walker's only caller. Also export the walker's
CompressedContentParts return type so public-API consumers can name it.

* feat: wrap tool status sentinels in <system> so the model can tell harness verdicts from tool output

The error/empty status text is model-only after the note refactor (UIs
render the raw output and style failures via the structured isError
flag), so the earlier plain-text wording served no remaining audience.
Wrapping the statuses in <system> gives every piece of system-generated
text inside a tool result the same marker:

- failed calls get '<system>ERROR: Tool execution failed.</system>'
  unconditionally — the ERROR:-prefix guard is removed, so the harness
  verdict can no longer be confused with tool output that happens to
  start with error-like text
- empty outputs render as '<system>Tool output is empty.</system>'; the
  plain placeholder the loop layer bakes into records is still
  recognized and upgraded at projection time

* style: collapse an internal helper docstring per the services subtree convention
2026-07-07 11:40:27 +08:00
haozhe.yang
b0fafa5f5c Merge remote-tracking branch 'origin/main' into HEAD
# Conflicts:
#	apps/kimi-web/src/api/daemon/agentEventProjector.ts
2026-07-06 23:47:51 +08:00
github-actions[bot]
379bc57ef0
ci: release packages (#1378)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-07-06 23:18:32 +08:00
haozhe.yang
959717e922 feat(v2): add kimi v2 prefix and fold v1 loop events on replay
- handle `kimi v2 <subcommand>` in main.ts, gated on KIMI_CODE_EXPERIMENTAL_FLAG via shared isKimiV2Enabled
- route `kimi server run` to server-v2 through the same switch; drop server-v2 standalone main.ts/dev script
- fold v1 context.append_loop_event records into messages at replay and snapshot so v1 sessions restore on agent-core-v2
- report backend: 'v2' in meta; serve session status via SessionLegacyService.status
- drop unused service options (blob/fullCompaction/goal/permissionGate); permissionGate reads agentId from scope context
2026-07-06 22:57:49 +08:00
qer
903e8ed93a
fix(web): clarify archive session confirmation copy (#1428)
* fix(web): clarify archive session confirmation copy

* fix(web): drop delete mention from archive confirmation copy

* feat(web): add archived-sessions restore entry to mobile settings

Mobile used a separate settings bottom-sheet with no archived-sessions
restore surface, so the archive confirmation pointing users to Settings
to restore was untrue on mobile. Add an Archived sessions sub-view to
the mobile settings sheet with search, sort and restore, mirroring the
desktop Settings tab.

* fix(web): refresh mobile archived list and use Input primitive

Refresh the archived-sessions list each time the mobile sub-view opens so
sessions archived from the mobile switcher show up without remounting the
sheet, and replace the hand-rolled search input plus inline svg with the
shared Input primitive.

* fix(web): use Button primitive for mobile archived restore

Render the mobile Archived sessions restore action with the shared Button
primitive instead of a hand-rolled button and scoped CSS, matching the
desktop archived list and the app's design-system rule.
2026-07-06 22:48:01 +08:00
liruifengv
2374bc41c3
feat(kimi-web): render cron fire notices in the web chat (#1426)
* feat(kimi-web): render cron fire notices as in-transcript cards

Show scheduled-reminder fires as distinct notice cards in the web chat,
both live and after a reload, instead of hiding them. Each card carries
a humanized schedule, a dimmed job id, and a collapsible prompt body.

* fix(kimi-web): isolate cron notice prompt id and route prefixed events

- Give synthesized cron messages a fresh promptId so a fire mid-turn
  cannot be reconciled into the optimistic user echo and hide it.
- Add cron.fired to KNOWN_AGENT_CORE_TYPES so event.-prefixed frames
  reach the projector and render live too.

* fix(kimi-web): truncate long one-line cron prompts when collapsed

Slice the first line to the collapse limit with an ellipsis when a
single-line prompt exceeds it, so the collapsed state actually truncates
and the expand toggle is not a no-op for common one-line reminders.

* fix(kimi-web): keep cron notices from reconciling into optimistic user echoes

Skip optimistic-echo reconciliation for user messages whose origin is
cron_job or cron_missed: their prompt text can coincide with a still-
optimistic user message, and the loose content match would otherwise
replace the user's turn with the cron notice instead of appending.

* fix(kimi-web): keep in-turn cron injections from breaking tool results

A cron injection steered into an active turn lands inside that turn's
message sequence, between a tool use and its result. Treating it as a
hard user-turn boundary flushed the pending assistant group, so the next
tool result had no group to fold into and the tool rendered without
output. Embed such in-turn cron notices as a block inside the assistant
group instead, and only render a cron at a turn boundary as its own turn.

* fix(kimi-web): only embed cron notices while a tool is in flight

Embedding whenever a group was pending was too broad: on REST snapshots
without prompt ids the whole transcript shares one group, so an idle cron
fire merged into the previous assistant answer and its own reply kept
going in that group. Embed only while the group has a running tool (a
cron sandwiched between a tool use and its result); flush to its own
turn otherwise.

* fix(kimi-web): omit synthetic prompt id on cron notices

The synthesized cron message carried a cron_pr_ promptId that the web
client caches into promptIdBySession for Stop/abort. Because it is not a
real daemon prompt id, it clobbered the active promptId, so Stop first
aborted a nonexistent prompt and only recovered via the error fallback.
Omit the promptId; the reducer already skips optimistic-echo
reconciliation for cron-origin messages, so it is not needed for de-dup.
2026-07-06 22:25:19 +08:00
liruifengv
ac5b5e4cbf
fix(kimi-web): keep tool components from jumping on expand or collapse (#1433)
* fix(kimi-web): keep tool components from jumping on expand or collapse

Also show the scroll-to-bottom button whenever scrolled up, and render a
fallback icon for tools without a dedicated glyph.

* fix(kimi-web): preserve bottom follow during content-only resizes

Late-loading media can grow after scrollKey has run; keep chasing the
bottom on content growth, and only suppress follow during the pinned
expand/collapse window.
2026-07-06 22:19:02 +08:00
qer
d86fa38e11
fix(kimi-web): disable text hyphenation and code ligatures (#1438)
Set hyphens: none (with the -webkit- prefix) on body so chat and markdown text never gain a hyphen glyph at a line break; components still control where lines wrap via word-break/overflow-wrap.

Disable the ligatures that coding fonts enable by default (liga/calt/ss01) on native code elements so code renders literally, e.g. != stays as two characters.
2026-07-06 22:12:43 +08:00
qer
0824e7b668
chore: ignore and remove throwaway scratch files (#1439)
Agent working notes (HANDOVER/handoff) and one-off UI prototype HTML files were committed by mistake. Remove the already-tracked ones, add .gitignore patterns for these classes of files and a .tmp/ scratch dir, and document the rule plus a pre-commit self-check in AGENTS.md so future mistakes are caught mechanically.

No publishable package is affected, so this PR needs no changeset.
2026-07-06 22:12:14 +08:00
qer
c5e3e80041
feat(web): render AgentSwarm as an inline tool card (#1425)
* feat(web): render AgentSwarm as an inline tool card

Replace the bottom SwarmCard footer and the messagesToTurns live-skip
with one dedicated inline tool card for AgentSwarm. The card shows a
phase overview plus a per-subagent accordion: live progress while it
runs, parsed aggregated result once it completes (and after a refresh
that has already dropped the live tasks).

Refresh and resync keep member identity metadata (swarmIndex,
parentToolCallId, subagentType, runInBackground) stable across skeleton
task replacement in the reducer, and the .content-wrap flex layout is
hardened against the overflow compression that previously displaced the
footer.

* fix(web): handle swarm review feedback

- SwarmTool: when AgentSwarm fails before producing a structured
  agent_swarm_result (e.g. argument validation), render the raw tool
  output instead of the "waiting for subagents" placeholder so the
  failure cause is visible.
- resolveSwarmMembers: source live members from the AppTask store keyed
  by parentToolCallId rather than buildSwarmGroups, which filters out
  single-member groups. A resume-only AgentSwarm now streams its live
  progress before the final result arrives. The badge counter still
  relies on buildSwarmGroups's filter.

* fix(web): carry streamed subagent text into swarm rows

Swarm subagents that stream normal assistant output accumulate it on
AppTask.text (text-kind taskProgress), not outputLines. The new live
member map was dropping `text`, so a still-composing subagent rendered
an empty / stale row until the structured result arrived.

- Add `text` to SwarmMember and thread it through buildSwarmGroups and
  swarmMembersByToolCall.
- SwarmTool: prefer member.text for both the row activity preview and
  the expanded body; fall back to outputLines / summary.
- Tests cover text propagation through both helpers.

* fix(web): merge swarm result rows and fall back to raw output

Address the two latest swarm review comments:

- Rows: when a parsed agent_swarm_result coexists with live AppTasks
  (which the detail panel also depends on), the inline card previously
  only rendered the live members. Interrupted swarms can carry
  state="not_started" / outcome="aborted" result entries for items that
  never spawned a task; those rows were dropped until a refresh cleared
  the live tasks. Extract the row model into buildSwarmCardRows and
  merge result-only aborted/not-started rows with the live member rows.
- Fallback: when the tool is no longer running but produced no
  structured result (argument validation, parser miss, or legacy
  legacy transcript), render the raw tool output instead of
  "Waiting for subagents…" so the final text / failure cause is
  visible to the user.

* fix(web): parse swarm result subagent bodies defensively

Producer writes subagent body unescaped, so a subagent that analyzes or
emits an AgentSwarm snippet can include a literal "</subagent>" inside
its body. The non-greedy regex treated that as the row close and truncated
the body in the result-only path (post-refresh where the AppTask store is
gone).

Rewrite the parser to scan opening tags, then resolve each row's body as
everything up to the last "</subagent>" before the next row's opening tag
(or document end), preserving embedded close-tag strings. Add tests for a
literal "</subagent>" within a single body and across sibling rows.

* fix(web): only count top-level subagent result tags

A subagent body that contains a literal `<subagent ...>` tag — for
example emitting an AgentSwarm/XML snippet — was being pre-collected as
another result row, splitting the real body and producing duplicate /
bogus subagents after refresh where the AppTask store is gone.

Rewrite parseSubagents with a depth-tracking tokenizer: scan every
`<subagent ...>` / `</subagent>` token in order, push a real row frame
only at the outermost level, and treat openings / closings while nested
inside another body as body text. Drop the now-inaccurate "literal
</subagent> without matching open" regression tests; replace with tests
that verify a balanced nested snippet stays inside the parent body and
does not register as a separate row.
2026-07-06 22:05:05 +08:00
qer
a5fbcb75b4
fix(kimi-web): keep composer toolbar usable on narrow windows (#1436)
The desktop composer toolbar renders every control on one row and relies on
overflow:hidden to fit, so between the mobile breakpoint and a wide window it
clipped its own content. Shed secondary ink below 980px (the context readout
moves into the ring tooltip, the model name truncates earlier, permission is
capped) and keep the context ring visible on phones too, instead of sending it
to the settings sheet.
2026-07-06 21:54:38 +08:00
7Sageer
10922fc70f
feat(telemetry): add system metrics collection (#1435)
* feat(telemetry): add system metrics collection

Add periodic CPU and memory telemetry sampling with warmup capture, lifecycle cleanup, and tests.

* fix(telemetry): attach prompt session to system metrics
2026-07-06 21:54:12 +08:00
haozhe.yang
590eca5ccd feat(server-v2): wire server-v2 into CLI and close v1 parity gaps
- cli: opt into server-v2 via KIMI_CODE_EXPERIMENTAL_FLAG, lazy-loaded behind a RoutedServer adapter so the v1 module graph stays untouched by default
- build: add hashImportsPlugin (rolldown/tsdown) to resolve #/ subpath imports of inlined agent-core-v2 when bundling server-v2
- server-v2: add --dangerous-bypass-auth flag disabling bearer auth on every route, surfaced on /meta via dangerous_bypass_auth
- server-v2: serve bundled web UI at GET / + SPA fallback when webAssetsDir is set (auth-exempt, mirroring v1)
- server-v2: snapshot auto reader reads state.json + wire.jsonl from disk with a hard timeout; legacy keeps the live assembly as escape hatch
- server-v2: POST /sessions/{id}/profile now updates title, metadata, and agent_config and broadcasts session.meta.updated on title change
- server-v2: session list/children project live ISessionActivity status and filter by status; add archived_only query and workspace_id validation
- server-v2: close cwd gap G3 — persist cwd on session metadata and surface it on the index so unregistered workspaces keep their cwd
- server-v2: GET /warnings surfaces agents-md-oversized; add GET /workspaces/{id}/skills session-less scan
- server-v2: broadcaster reads the cold watermark from the disk journal and broadcasts title updates to every connection
- agent-core-v2: fix plan-file path resolution in AgentPlanService
- server: rename IFileService to IFileStore
2026-07-06 21:53:33 +08:00
qer
578f7d334c
fix(web): reconcile session from snapshot on reopen (#1409)
* fix(web): reconcile session from snapshot on reopen

* fix(web): discard stale snapshot when a newer prompt races reopen

* fix(web): harden reopen snapshot against first-open and optimistic-send races

* fix(web): keep evicted reopens subscribed when a snapshot races

* fix(web): let resync snapshots bypass the reopen staleness guard

* fix(web): force-apply the snapshot after an undo

* fix(web): gate session reopen on durable seq instead of updatedAt

* refactor(web): always rebuild reopened sessions from a snapshot

* fix(web): sharpen reopen snapshot discard and skip rebuilds mid-stream

* refactor(web): unconditionally apply session snapshots, drop the staleness guard

* fix(web): preserve loaded older messages when reopen snapshots apply
2026-07-06 21:30:25 +08:00
qer
4aacddc432
fix(kimi-web): clarify desktop notification title and icon (#1434) 2026-07-06 21:18:45 +08:00
haozhe.yang
a32aa95c6f Merge remote-tracking branch 'origin/main' into HEAD 2026-07-06 20:22:27 +08:00