Commit graph

521 commits

Author SHA1 Message Date
github-actions[bot]
5cc194956f
ci: release packages (#1785)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-07-17 20:45:40 +08:00
qer
f441193e1d
chore: remove kimi-desktop app and desktop release pipeline (#1849)
* chore: remove kimi-desktop app and desktop release pipeline

The desktop client has moved to a dedicated internal repository. Drop the
Electron shell app, the desktop-build reusable workflow and its release
job, and all workspace references (dev script, typecheck filter,
onlyBuiltDependencies, flake.nix entries, lockfile). The kimi-web desktop
detection code is intentionally kept intact.

* chore: fix flake.nix after kimi-desktop removal

Complete the workspaceNames cleanup missed in the previous commit and
refresh the pnpmDeps fixed-output hash for the regenerated lockfile.
2026-07-17 20:32:47 +08:00
qer
9e1248416f
fix(web): remember the thinking level per model (#1838)
* fix(web): remember the thinking level per model

Persist kimi-web.thinking as a JSON map of model id to level instead of a
single global value, and resolve the active level against the model's
catalog (stored pick when still declared, else the model default) at
loadModels, setModel, and on active-model changes via a watcher.

Fixes the empty, unresponsive thinking picker shown for a model that does
not declare a previously stored level (e.g. a max-only model with a stale
global 'low').

* fix(web): resolve a submitted prompt's thinking from its own model

submitPromptInternal and the steer path read the single active-session
rawState.thinking, so a queue drain for a background session submitted the
level of whichever session the user had switched to since enqueueing —
the same cross-model leak on the submit path. Thinking now joins model and
the per-session modes in being resolved from the prompt's own session
model (its stored pick when declared, else the catalog default), falling
back to the active value only when the model has left the catalog.

* fix(web): keep model switches from persisting derived thinking defaults

setModel routed the resolved level through applyThinkingLevel, which
writes per-model storage unconditionally — a switch to a model with no
saved pick stored the catalog default as if it were an explicit choice,
pinning the user to it across later default changes, and the rollback
path did the same write for a switch that never happened. Model switches
now update the in-memory level only; storage writes stay with
setThinking, the explicit picker path.

* fix(web): resolve thinking per target session on the BTW and skill paths

sendSideChatPromptOn combined the captured parent's model with the
active-session level, so a session switch during the startBtw await sent
the BTW first turn at the wrong model's effort — resolve it from the
parent's own model, falling back to the active value off-catalog, same
as the other submit paths.

activateSkill carries no thinking either, so the daemon ran skills at
the session profile effort, which can predate the per-model restore the
picker now shows. Persist the resolved level to the session profile
first, mirroring the new-session skill path; that path itself now
resolves against the new session's model instead of the raw active
value.

* fix(web): keep per-model thinking picks in memory as the runtime truth

The resolver re-read localStorage on every submission, letting storage —
not the displayed state — decide what the daemon receives: with storage
unavailable (policy/quota) an explicit pick reached the UI while every
submit path fell back to the catalog default, and a pick made in another
tab silently changed what this tab submits mid-session.

Per-model picks now live in an in-memory map hydrated from localStorage
at startup; explicit picks update it first and persist best-effort
(read-modify-write merge, so concurrent tabs' entries still survive).
localStorage is only hydration plus persistence — another tab's pick can
no longer alter this tab's runtime level.

* fix(web): carry the legacy global thinking pick forward as a fallback

Pre-map installs stored a single global level as a raw string; the map
parser dropped it, silently resetting the user's explicit preference to
the catalog default on upgrade. The legacy value is now carried as a
fallback for models without their own entry — validated against each
model's catalog at resolution, so effort models keep the user's pick
while a max-only model still falls through to its default and can never
be trapped by it.

* fix(web): keep the legacy thinking fallback across the first map rewrite

The first explicit pick after an upgrade rewrote the raw legacy value
into a map containing only that one model, so the next reload saw a
nonempty map and dropped the legacy fallback for every other model.
The migrated value now lives inside the map under a '*' key that no
real model id can collide with: per-model entries override it, and
rewrites persist it alongside them instead of deleting it.

* fix(web): persist only the changed thinking pick on write

Overlaying the whole in-memory map on write could revert a newer pick
made in another tab for a model this tab still held a stale copy of.
Write the changed entry alone (delta-style, like saveUnread), carrying
only the migrated legacy '*' fallback along so it survives the first
rewrite into map format.

* fix(web): abort skill activation when the thinking profile persist fails

persistSessionProfile surfaces failures itself and resolves, so awaiting
it never blocked a following activation: a failed /profile write still
launched the skill at the session's stale effort. It now resolves a
success flag; both activation paths (existing session and new-session
draft) gate on it and skip activating when the persist fails, without
reporting a second, synthetic error.

* refactor(web): persist the new-session skill profile's thinking once

startSessionAndActivateSkill persisted the resolved thinking and then
activateSkill persisted it again unconditionally — a redundant profile
update and status refresh whose transient failure would false-veto an
activation whose prerequisite profile was already applied. Thinking is
now written by activateSkill alone (the single, gated writer); the
draft patch carries only model, plan/swarm and permission.

* fix(web): throw an Error instance for the profile-persist sentinel

oxlint --type-aware (only-throw-error) rejects throwing a Symbol; the
identity-based sentinel works the same as a shared Error instance.

* fix(web): resolve an empty session model through the default before skills

session.model can be '' transiently (daemon profile echo), so
activateSkill fell back to the raw active-view level; in the new-session
flow a concurrent switch could persist another model's effort onto the
target session. Normalize '' through the configured default_model first,
same as the prompt/BTW/steer paths.
2026-07-17 19:56:26 +08:00
qer
18d3374137
fix(vscode): reliable cancel and preserved session model on attach (#1845) 2026-07-17 19:55:45 +08:00
qer
03021b6db7
fix(kimi-web): stop the prompt queue from ghost-sending stale attachments (#1833)
* fix(kimi-web): stop the prompt queue from ghost-sending stale attachments

Sending while a turn was running queues prompts locally. A failed flush
left entries stuck, and every later session open silently re-submitted
them with their old file attachments. Gate the drain on locally
witnessed turns, re-drive stuck entries FIFO from real events with a
failure budget, restore merged entries on steer failure, persist the
queue per session so a refresh loses nothing, and converge queues
across tabs via storage events.

* fix(kimi-web): merge cross-tab queue updates by entry id (review P1/P2)

Whole-record adoption could silently discard a prompt another tab
enqueued concurrently. Queue entries now carry a stable id and enqueue
timestamp; adoption union-merges snapshots by id, a shared TTL'd removal
set stops flushed/discarded entries from resurrecting (and being
flushed twice), an in-flight marker covers the submit window, and
manual reorders re-stamp timestamps so they survive merges. The flush
failure budget is also tracked per entry instead of per session, so
removing or reordering the head no longer hands its strikes to the
next entry.

* fix(kimi-web): single-flusher queue entries, merge order convergence, forget guard (review round 2)

Turn-end events reach every open tab and the server accepts concurrent
submissions as distinct prompts, so two tabs holding the same adopted
entry could both submit it. Entries now record their owner tab and only
the owner flushes (ownerless legacy entries flush anywhere); an idle
send adopts entries left behind by closed tabs so a stranded queue can
still drain. Cross-tab merges now keep the newer enqueuedAt copy per id
so a manual reorder converges instead of ping-ponging writes, and the
flush failure callback no longer resurrects a queue whose session was
forgotten while the submit was pending.

* refactor(kimi-web): drop the cross-tab queue persistence, keep the minimal fix

Cross-tab queue sync over localStorage is a distributed-systems problem
(claim/lease, conflict merge, ownership) that keeps generating review
findings far beyond this PR's scope. Remove the persistence/hydration/
adoption machinery wholesale; keep the bug fix proper: gated queue
drain, event-driven FIFO retry with a per-entry failure budget, steer
queue restore, and the forgotten-session flush guard. Durable queued
prompts will be designed together with the server-side prompt queue.

* chore: align the changeset wording with the reduced scope

* fix(kimi-web): never duplicate on ambiguous submit failures; advance after drop

Two review findings: (1) restoring merged queue entries after ANY steer
failure could re-submit prompts the daemon had already accepted when the
failure was a lost response — submits now report ok/rejected/uncertain
and restores + flush re-queues happen only on definitive daemon
rejections, while ambiguous failures drop the entry (the failure toast
still tells the user); (2) dropping an exhausted queue head no longer
strands the entries behind it — the new head is submitted immediately,
carrying its own failure budget.
2026-07-17 18:57:38 +08:00
qer
429521b669
fix(vscode): allow editor mentions for files outside the working directory (#1836)
* fix(vscode): allow editor mentions for files outside the working directory

* fix(vscode): quote editor mentions whose paths contain spaces
2026-07-17 18:02:50 +08:00
Kai
1b907b07cd
fix(tui): hide whitespace-only thinking from the transcript (#1829)
Models occasionally stream whitespace-only thinking (e.g. a single
space). It starts a thinking draft that renders as a bare bullet line,
both while streaming and when replaying session history. Skip
whitespace-only thinking deltas before they create a draft, and skip
whitespace-only think text at the component funnel so stored
whitespace think parts never render on replay. Stored thinking is
still replayed verbatim to the model.
2026-07-17 17:19:22 +08:00
liruifengv
bfecd0128f
feat: refresh model list for API-key providers at the managed endpoint (#1824)
* feat: refresh model list for API-key providers at the managed endpoint

* docs: simplify changeset wording

* docs: add kimi-code/k3 to the default config example

* fix: clear stale default model when a refresh drops its alias

* fix: clear refresh defaults via replace; set(undefined) cannot delete

* docs: drop the auto model refresh paragraph from providers page

* docs: simplify changeset wording
2026-07-17 17:17:58 +08:00
liruifengv
a5c568dc7a
feat(tui): add /copy slash command to copy the last assistant message (#1822)
* feat(tui): add /copy slash command to copy the last assistant message

The command copies the latest assistant reply (text parts only, skipping thinking, tool-call-only turns, error and internal messages) to the clipboard. Clipboard writes now also emit an OSC 52 sequence (tmux-aware) so copying keeps working over SSH and in containers where no native clipboard tool exists.

* fix(tui): gate /copy to idle and report OSC 52-only copies as unverified

During streaming the in-flight assistant text is not in getContext() history yet, so /copy would silently copy an older message; make the command idle-only like /export-md. copyTextToClipboard now returns how the text was delivered so /copy can say when only an unverified OSC 52 escape carried it.

* fix(tui): source /copy text from the visible transcript

After /compact the model context keeps only user messages plus a user-role summary, so scanning it found no assistant message even though the last reply is still on screen. Read the last assistant transcript entry instead — it matches what the user sees and survives compaction and resume.

* fix(tui): mark real model text in the transcript so /copy skips synthetic cards

Hook-result and goal-completion cards are also 'assistant' transcript entries appended after the real reply, so /copy could copy a hook card instead of the answer. Tag the single entry-creation site for genuine model text (both live and replay flow through it) and have /copy only accept tagged entries.
2026-07-17 16:39:19 +08:00
Haozhe
9b496946dc
feat(kimi-inspect): add kap-server web inspector with dev-only /api/v1/debug RPC surface (#1806)
* feat(kimi-inspect): add web inspector for kap-server /api/v2 surface

- new apps/kimi-inspect app: connect screen (server URL + optional bearer
  token, persisted in localStorage, deep-linkable via ?url=/?token=),
  workspace/session browser sidebar, per-session chat view, and live
  Service panels with data and trigger buttons for Session/Agent scopes
- built on @moonshot-ai/klient (HTTP for calls, /api/v2/ws for events);
  Vite dev server proxies /api to a running kap-server
- register the workspace in AGENTS.md project map and flake.nix
  workspacePaths/workspaceNames

* fix(kimi-inspect): align dependency versions with the workspace (sherif)

* feat: dev /api/v1/debug RPC surface and kimi-inspect channel rework

- kimi-inspect: replace @moonshot-ai/klient with an in-app old-klient-style
  channel layer (service-bound IChannel, HTTP ProxyChannel, shared /api/v2/ws
  socket with ref-counted event listens), typed by agent-core-v2 interfaces;
  /channels descriptors + serviceByName keep every wire protocol loaded 1:1
- kimi-inspect: local server auto-discovery (Vite middleware over the
  kap-server instance registry + home token), zero-config startup connect,
  and a header switcher for runtime server switching
- kap-server: wire the dormant --debug-endpoints flag to a new
  whitelist-free /api/v1/debug dispatcher (every scoped service callable),
  gated to loopback binds; repo dev scripts pass the flag
- kimi-inspect: probe the debug surface at connect, falling back to /api/v2
  on servers without it
- tests: channel + discovery unit tests in kimi-inspect; debug RPC and
  loopback-gating coverage in the kap-server rpc/debugNonloopback suites

* chore(changesets): ignore @moonshot-ai/kimi-inspect

The private dev app never ships, so it should never appear in a changeset.
Add it to the changeset config ignore list (next to vis*) and note the rule
in the gen-changesets skill.
2026-07-17 15:56:53 +08:00
qer
66cc279975
chore(vscode): release 0.6.2 (#1820) 2026-07-17 15:00:02 +08:00
Haozhe
56a321d4d1
fix(workspace): dedupe workspaces across Windows path spelling variants (#1809)
* fix(workspace): dedupe workspaces across Windows path spelling variants

The same directory reached the workspace registry as distinct strings on
Windows (drive-letter casing, typed vs on-disk casing, slash style), and
every identity check compared exact strings, so one folder could appear
as multiple workspaces with sessions split across hash-keyed buckets.

- add workspaceRootKey (slash-normalize + case-fold Windows-shaped
  paths) in agent-core, agent-core-v2, and the web app, and compare
  roots by identity key everywhere instead of exact strings
- registry createOrTouch folds alias spellings onto the existing entry
  instead of minting a new workspace id; session buckets reuse the
  registered id via a resolver in the v1 session store
- list endpoints expand alias buckets (resolveAliasIds /
  resolveAliasWorkDirs, including session-index-only spellings) so
  previously split workspaces list all sessions and counts under one
  merged group; session_index entries use the registry-resolved id

* fix(workspace): fold the runtime touch path and drive-root identity keys

Two gaps in the Windows path-spelling folding, both reachable in the
v1 session-create flow:

- touchWorkspaceRegistry minted the alias spelling's id outright; the
  freshly persisted alias entry then became the resolver's preferred id
  on the next create, splitting sessions into a duplicate bucket again.
  It now folds onto the identity-matching existing entry, mirroring the
  registry service.
- workspaceRootKey stripped trailing separators before testing the
  Windows shape, so a drive root (C:\) collapsed to C: and escaped the
  case-fold. The shape test now runs before the strip in all three
  copies (agent-core, agent-core-v2, web).

* fix(workspace): unfold symmetric operations that escaped the identity key

Two asymmetric spots left the folded comparison one-sided:

- the web app matched hidden roots by folded key but cleared them on
  re-add by exact string, so hiding C:\Foo and re-adding c:\foo kept
  the workspace hidden forever; clearing now folds too
- registry delete (both engines) removed and tombstoned only the exact
  id, so a legacy split sibling resurfaced as the directory's
  representative on the next list; delete now removes every registered
  spelling sharing the root's identity key and tombstones the full
  alias set (registered ids plus session-index spelling mints), so the
  session-index merge cannot resurrect the directory either
2026-07-17 14:56:06 +08:00
qer
1a5c0de19a
fix(vscode): stop mid-turn core errors from corrupting the active turn (#1807)
* fix(vscode): stop mid-turn core errors from corrupting the active turn

* fix(vscode): reject prompts during exclusive operations with a terminal error
2026-07-17 14:40:43 +08:00
liruifengv
cec15e2188
fix(tui): dismiss /btw panel before cancelling compaction on Esc and Ctrl+C (#1811) 2026-07-17 13:29:12 +08:00
Haozhe
319001ae5c
refactor: remove git detection from workspace wire and folder browse (#1787) 2026-07-16 21:05:19 +08:00
github-actions[bot]
36b05820cb
ci: release packages (#1767)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-07-16 18:54:29 +08:00
qer
3ace52b697
chore(vscode): release 0.6.1 (#1783)
* chore(vscode): release 0.6.1

* test(vscode): read the extension version from the manifest in version assertions

* test(vscode): declare version on the runtime rig type
2026-07-16 18:53:16 +08:00
qer
ba36c6a563
fix(vscode): make the gear sign-in actually log in (#1779)
* fix(vscode): count configured provider credentials as signed in and make the gear sign-in actually log in

* fix(vscode): keep the gear auth action scoped to the Kimi account session
2026-07-16 18:21:53 +08:00
Kai
d531398d01
fix: scope Anthropic effort fallback profile to non-Kimi providers (#1765)
* fix: scope Anthropic effort fallback profile to non-Kimi providers

Managed Kimi models routed through the Anthropic protocol (protocol =
"anthropic", no catalog-declared think_efforts) inherited the inferred
latest-Opus effort profile, so the UI showed reasoning effort choices the
server never declared. The fallback now applies only when the provider type
is known, non-kimi, and the effective wire protocol is Anthropic; Kimi
providers keep catalog-declared efforts only, and callers without provider
context fall back to name matching.

* fix: align v2 catalog with resolver for providerless Anthropic models

Flat models (inline base_url, no named provider) and providers without a
declared type now fall back to the model's own protocol when deciding the
Anthropic fallback effort profile, so the model catalog stays consistent
with runtime resolution.

* fix: keep flat Anthropic model effort metadata in TUI and ACP catalogs

Flat models without a named provider (inline base_url, protocol:
"anthropic") have no provider entry to look up; fall back to the
model's own protocol as the provider identity so the effort picker and
ACP catalog stay consistent with runtime resolution.

* style: move v2 anthropic fallback rationale into the modelAuth header

The v2 comment convention keeps comments in the top-of-file block only;
drop the inline explanations added beside functions and statements.
2026-07-16 17:30:20 +08:00
qer
d1ca65e1de
feat(vscode): migrate extension to Node SDK (#1769)
* feat(vscode): migrate extension to Node SDK

* fix(vscode): address CI failures

* fix(vis): handle token count records

* fix(vscode): keep chat toolbar and header readable at narrow widths

* fix(vscode): map yolo to core yolo permission and honor the global yolo setting

* docs(vscode): record Node SDK migration design

* docs(vscode): split breaking changes out of the 0.6.0 changelog

* fix(vscode): keep a resumed session's thinking effort instead of reapplying the default

* fix(vscode): announce session status when a view attaches so the display matches it

* fix(vscode): align webview thinking effort handling with the TUI
2026-07-16 17:27:21 +08:00
liruifengv
b5139757e2
fix: align context usage display with 1024-based units and ceiled percents (#1771)
* fix(web): align context usage display with 1024-based units and ring-only meter

- simplify the composer context meter to the ring only; the full
  used/max/pct numbers live in the tooltip
- format token counts with 1024-based k/M units via a shared formatTokens
  helper (256k context reads "256k", not "262k"), applied to the composer
  tooltip, status panel, mobile settings sheet, model picker, goal strip,
  and turn rendering
- ceil the usage percent so sub-0.5% usage still shows a sliver instead
  of an empty meter

* fix(tui): render context usage with 1024-based units and ceil percent

- formatTokenCount is now 1024-based ("256k", not "262.1k"); the footer,
  /status and /usage panels, subagent cards, and goal stats all share it,
  replacing five local 1000-based copies
- the footer and panel percents use an integer ceil (new usagePercent
  helper) so any non-zero usage shows at least 1% instead of "0.0%"

* fix(web): clamp the status panel context percent to [0,100]

ctxUsed can momentarily exceed ctxMax (estimates), which could flash a
"101%" readout — the composer and mobile sheet already clamp the same
ConversationStatus data, so apply the same clamp around the ceiled
percentage here.

* chore: merge the context usage changesets into one

* chore: reword the context usage changeset in English
2026-07-16 16:41:40 +08:00
Haozhe
78967e283d
refactor(model-catalog): drop WS catalog-changed event; refresh on picker open (#1772)
- kap-server: remove event.model_catalog.changed from the v1 WS union,
  broadcaster forwarding, and the zod event registry
- web: refresh all providers (POST /providers:refresh) before loading
  models when the model picker opens, replacing the event-driven refresh
- keep domain publishers, the protocol schema, and the web receiver for
  compatibility with older daemons
2026-07-16 16:34:32 +08:00
_Kerman
7042af3571
fix(web): keep the sidebar resize handle above the chat composer background (#1766) 2026-07-16 16:17:20 +08:00
liruifengv
81414b6ad5
feat: warn about context-cache loss when switching model or thinking effort (#1763)
* feat: warn about context-cache loss when switching model or thinking effort

* chore: polish the switch warning copy

* feat: wrap the switch warning instead of truncating it

* chore: bold /new in the switch warning

* Revert "chore: bold /new in the switch warning"

This reverts commit a438e87cda543202a6d4726c96cdabd73b8dcf6f.

* chore: align the changeset with the warning copy
2026-07-16 15:43:23 +08:00
github-actions[bot]
50b919b67e
ci: release packages (#1752)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-07-16 10:17:32 +08:00
Kai
1d7c205e83
fix: close two silent-exit vectors around unhandled rejections (#1758)
Some checks are pending
CI / test-pi-tui (push) Waiting to run
CI / test-windows (push) Waiting to run
CI / lint (push) Waiting to run
CI / build (push) Waiting to run
CI / test (1) (push) Waiting to run
CI / test (2) (push) Waiting to run
CI / test (3) (push) Waiting to run
CI / test (4) (push) Waiting to run
CI / test (5) (push) Waiting to run
Nix Build / Check flake.nix workspace sync (push) Waiting to run
Nix Build / nix build .#kimi-code (push) Blocked by required conditions
Release / Release (push) Waiting to run
Release / Deploy docs (push) Blocked by required conditions
CI / typecheck (push) Waiting to run
Release / Native release artifact (push) Blocked by required conditions
Release / Desktop release artifact (push) Blocked by required conditions
Release / Publish native release assets (push) Blocked by required conditions
* fix: record crash telemetry for unhandled promise rejections

The crash handler only listened on uncaughtExceptionMonitor, which never
fires for a rejection that has a listener — and the TUI always registers
one, converting rejections into a silent exit(1) with no telemetry at
all. Observe unhandledRejection directly so those crashes still leave a
trace. Since registering a real listener suppresses Node's default
crash-on-rejection, rethrow when we are the only listener (print /
server modes), deduped so the monitor does not double-report.

* fix: fall back to text paste when the clipboard image handler rejects

The Ctrl+V image-paste dispatch chained off the async handler without a
rejection branch, so any failure inside it became an unhandled
rejection — which the CLI's crash path turns into a silent exit(1).
Treat a rejection like "no image available" and paste as text.

* fix: dedupe all rethrown rejection reasons at the crash monitor

Non-Error rejection reasons (plain objects, strings, null) were recorded
by the rejection handler but not added to the dedupe set, so the
uncaughtExceptionMonitor pass after the rethrow reported a second crash
for the same failure; null/undefined reasons also crashed the monitor's
own error-type extraction. Use a Set so primitives dedupe by value, add
every rethrown reason, and classify monitor crashes null-safely.
2026-07-16 02:14:14 +08:00
Kai
918c1354d9
fix: align Anthropic-compatible model capabilities (#1746)
* fix: align Anthropic-compatible model capabilities

* fix: warn on Anthropic effort mismatches

* fix: harden Anthropic model resolution

* fix: align Anthropic replay and ACP thinking state

* fix: normalize Anthropic thinking stream payloads

* fix: backfill non-empty preserved thinking

* fix: resolve session thinking effort with provider context

* fix: honor adaptive thinking opt-out in effort resolution
2026-07-16 01:31:38 +08:00
Kai
f0c8a103c6
fix: preserve the crash error in diagnostic logs on unexpected exit (#1757)
The TUI crash path (uncaughtException / unhandledRejection) logged the
error into an asynchronously-drained sink and then called process.exit()
on the same tick, so the one line explaining the crash was never written
to disk. Flush the diagnostic logs synchronously before exiting.
2026-07-16 01:26:27 +08:00
_Kerman
df75a0f5c2
refactor(agent-core-v2): derive session busy from agent activity (#1751) 2026-07-15 23:33:58 +08:00
qer
e885aec7ff
feat(web): show detailed diagnostics for model request failures (#1756)
Surface the coded provider error the daemon already sends: a semantic
title per error code, the provider's raw message, and expandable
diagnostics (error code, HTTP status, request ID, SDK error name) with
copy support, instead of a bare text-only toast.
2026-07-15 23:07:38 +08:00
qer
1186686554
fix(web): dedupe background subagent rows in the agents dock (#1754)
* fix(web): dedupe background subagent rows in the agents dock

* fix(web): seed agent identity on late task registration and prefer REST output in task fold

* fix(web): backfill terminal output to folded background subagent rows

* fix(web): sync subagent phase when the REST fold makes a row terminal
2026-07-15 23:07:23 +08:00
Haozhe
0b790cdc05
feat(web): allow attaching any file type and fix CSP on non-loopback binds (#1731)
* feat(web): allow attaching any file type in chat

- Composer, paste, and drop no longer filter out non-media files;
  arbitrary files upload as generic icon chips and are submitted as
  file content parts
- kap-server materializes file parts into the session's attachments
  dir and replaces them with a path reference, so the model opens the
  file with the Read tool on demand instead of receiving inline bytes
- Images rejected by the provider format gate (SVG, AVIF, ...) are now
  persisted and referenced by path instead of being dropped with a
  notice; uploaded file names are sanitized before hitting disk

* fix(server): stop CSP from blocking web bootstrap script and fonts

- Move the anti-FOUC bootstrap from an inline <script> in index.html
  to /boot.js: CSP 'self' never covers inline scripts, while a classic
  same-origin script keeps the same render-blocking timing
- Allow data: in font-src — KaTeX and the Inter / JetBrains Mono
  Variable fonts ship @font-face data URIs in their distributed CSS
- Set explicit form-action, base-uri, and frame-ancestors, which do
  not fall back to default-src
- Add a regression test asserting the served index.html carries no
  inline scripts or inline event handlers

* fix(web): normalize empty attachment MIME so extensionless files submit

Files with an empty File.type (Makefile, LICENSE, other extensionless
or unknown types) stored mediaType: '' on the chip, and the submit
fallback used ?? which does not catch empty strings — the wire schema
requires a non-empty media_type, so the prompt was rejected. Normalize
to application/octet-stream at attachment creation, adopt the
server-recorded MIME after upload completes, and make both submit
mappings use || so reloaded chips with '' are covered too.

* feat(web): render all user-turn attachments as chips

* feat(web): attach files by dropping them anywhere in the window

* refactor(web): share one attachment chip between composer and chat bubble

* fix(web): neutral attachment chips, paperclip attach icon, and clickable file chips

* fix(web): drop the extension badge from attachment chips

* fix(web): use the tabler paperclip for the attach button

* fix(web): whitelist attachment previews, reject active document types

Clicking a file chip navigated a new tab to a blob: URL of the uploaded
bytes whenever the type looked browser-renderable. blob: inherits the
web origin, so a text/html or image/svg+xml attachment would execute
same-origin script with the daemon credential (localStorage) and a live
window.opener.

Preview is now restricted to inert types (pdf, non-SVG images, video,
audio, non-HTML text), the blob is re-wrapped with the whitelisted MIME
instead of trusting the recorded content-type, and window.opener is
severed. Non-whitelisted types no longer silently download: the chip
reports 'unsupported' and the pane shows a transient hint.

* fix(web): recover file attachment chips from the server notice

The kap-server prompt route replaces file parts with an "Attached file
…" text notice before enqueueing, so after any snapshot resync the
attachment chip degraded into raw notice text leaking the absolute
server path — unlike image/video uploads, which already recover their
chip from the <video|image path> tag. Parse the notice the same way:
the materialized basename carries the file id, so the chip becomes
clickable again (and editable back into the composer); inline-base64
notices (content-hash named, no file id) still collapse into a
non-clickable chip instead of raw text.

The notice wording is now a client/server contract — flagged on
buildAttachedFileNotice.

* fix(web): preserve UUID file ids when rebuilding attachment chips

* fix(web): skip unresendable file chips when loading attachments for edit

---------

Co-authored-by: qer <wbxl2000@outlook.com>
2026-07-15 21:54:48 +08:00
qer
b89d385fa5
fix(web): confirm dialogs respond to Enter and await async actions (#1744)
Some checks are pending
CI / test (3) (push) Waiting to run
CI / build (push) Waiting to run
CI / test (1) (push) Waiting to run
CI / test (2) (push) Waiting to run
CI / test (4) (push) Waiting to run
CI / test (5) (push) Waiting to run
CI / test-pi-tui (push) Waiting to run
CI / test-windows (push) Waiting to run
CI / lint (push) Waiting to run
CI / typecheck (push) Waiting to run
Nix Build / Check flake.nix workspace sync (push) Waiting to run
Nix Build / nix build .#kimi-code (push) Blocked by required conditions
Release / Publish native release assets (push) Blocked by required conditions
Release / Deploy docs (push) Blocked by required conditions
Release / Native release artifact (push) Blocked by required conditions
Release / Desktop release artifact (push) Blocked by required conditions
Release / Release (push) Waiting to run
* fix(web): confirm dialogs respond to Enter and await async actions

The confirm dialog's initial focus was resolved from the Button
component's $el, which is a text node in dev builds (the component has
a template-root comment, so it renders as a fragment). Focus fell back
to the header close button, so Enter cancelled instead of confirming.
Resolve the initial focus with a CSS selector on the confirm button
instead.

ConfirmOptions now accepts an async action: the dialog stays open with
a loading state (cancel/Esc/overlay suppressed) until the work settles.
The archive-session, remove-workspace, and delete-provider confirms
move from the menu components into App.vue so the dialog can await the
actual client call.

* fix(web): block superseding a confirm dialog while its action runs

A second confirm() during an in-flight action would replace the busy
dialog and inherit the global busy state, opening inert until the first
action settled. Resolve the new request unconfirmed instead.
2026-07-15 20:04:50 +08:00
github-actions[bot]
6a8e610ca3
ci: release packages (#1702)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-07-15 18:00:18 +08:00
qer
6eb8e13417
fix(kimi-web): improve mobile safe-area handling (#1459)
* fix(kimi-web): improve mobile safe-area handling

* fix(kimi-web): restore dock-height fallback where ChatDock is absent

* fix(kimi-web): pin the app shell to the visual viewport height

* fix(kimi-web): pin the app shell to the visual viewport

* chore: add changeset for mobile safe-area fixes
2026-07-15 14:50:01 +08:00
qer
b6ae0a1054
fix(web): surface session list load failures (#1641)
* fix(web): surface session list load failures

* fix(web): preserve partial session pages
2026-07-15 13:58:03 +08:00
qer
d8d4e8ceb5
fix(web): keep long streams responsive (#1643)
* fix(web): keep long streams responsive

* fix(web): drop queued events for archived sessions
2026-07-15 13:47:11 +08:00
qer
b24a347e20
fix(kap-server): carry the live subagent roster in the session snapshot (#1719)
* fix(kap-server): carry the live subagent roster in the session snapshot

* fix(kap-server): clear the subagent roster on the next main turn start

* fix(kap-server): exclude background subagents from the snapshot roster

* fix(kap-server): finalize live roster entries when the main turn aborts

* fix(kap-server): drop roster entries when foreground subagents detach

* fix(web): expand the swarm card by default while subagents are running

* docs(kap-server): qualify the roster-clearing durability claim
2026-07-15 12:40:30 +08:00
7Sageer
c291ee2ddb
fix(telemetry): deliver session_started in v2 headless mode (#1687)
Some checks are pending
CI / test (2) (push) Waiting to run
CI / test (3) (push) Waiting to run
CI / test (4) (push) Waiting to run
CI / test (5) (push) Waiting to run
CI / test-pi-tui (push) Waiting to run
CI / test-windows (push) Waiting to run
CI / lint (push) Waiting to run
CI / typecheck (push) Waiting to run
CI / build (push) Waiting to run
CI / test (1) (push) Waiting to run
Nix Build / Check flake.nix workspace sync (push) Waiting to run
Nix Build / nix build .#kimi-code (push) Blocked by required conditions
Release / Desktop release artifact (push) Blocked by required conditions
Release / Publish native release assets (push) Blocked by required conditions
Release / Release (push) Waiting to run
Release / Deploy docs (push) Blocked by required conditions
Release / Native release artifact (push) Blocked by required conditions
* fix(telemetry): deliver session_started in v2 headless mode

session_started/session_load_failed fire inside create()/resume(), but the CloudAppender was installed only after resolveNativeSession() returned, so they were dropped to the null appender.

- run-v2-print: install the appender before resolving the session; reconcile a resumed session's real model via setContext afterwards.
- sessionLifecycle: bind the session id in announceCreated before emitting so session_started carries session_id.
- CloudAppender.setContext: allow updating the model context.

* fix(agent-core-v2): bind session id on session_load_failed

The resume failure path emitted session_load_failed without binding the
session id first, so the event went out with session_id null even though
the service knows which session failed to load. Bind it via setContext
before track2, mirroring the announceCreated path for session_started,
and update the two tests that locked the empty context in as expected.
2026-07-15 11:52:01 +08:00
qer
de493aeec9
fix(web): use upward chevron for dock card expand buttons (#1715)
* fix(web): use upward chevron for plan card expand button

* fix(web): use upward chevron for question card expand button
2026-07-14 23:06:15 +08:00
qer
20b69724aa
fix(web): make code block copy work over plain HTTP (#1714) 2026-07-14 22:31:36 +08:00
Haozhe
78ca44b4d1
feat(kimi-code): keep print-mode goal runs alive until the goal settles (#1712)
* feat(kimi-code): keep print-mode goal runs alive until the goal settles

- applyPrintBackgroundPolicy waits for goal continuation turns via a goalActive hook before applying the exit/drain/steer mode, bounded by the wait ceiling
- createPrintTurnEndings skips the timeout when the remaining budget is not finite
- update the changeset to cover the goal-run lifecycle

* fix(kimi-code): wake the print goal wait periodically so settled goals exit promptly
2026-07-14 21:09:09 +08:00
_Kerman
26d499bca7
refactor(agent-core-v2): consolidate wire services (#1680) 2026-07-14 19:51:29 +08:00
qer
9eff230f97
fix(web): show errors for failed actions and add daemon request/operation logging (#1711)
Some checks are pending
CI / test (5) (push) Waiting to run
CI / test-pi-tui (push) Waiting to run
CI / test-windows (push) Waiting to run
CI / build (push) Waiting to run
CI / test (1) (push) Waiting to run
Nix Build / Check flake.nix workspace sync (push) Waiting to run
Nix Build / nix build .#kimi-code (push) Blocked by required conditions
Release / Release (push) Waiting to run
Release / Deploy docs (push) Blocked by required conditions
Release / Native release artifact (push) Blocked by required conditions
Release / Desktop release artifact (push) Blocked by required conditions
Release / Publish native release assets (push) Blocked by required conditions
CI / typecheck (push) Waiting to run
CI / lint (push) Waiting to run
CI / test (2) (push) Waiting to run
CI / test (3) (push) Waiting to run
CI / test (4) (push) Waiting to run
* fix(web): show errors for failed actions and add daemon request/operation logging

* fix(web): dedupe identical operation-failure toasts to avoid flooding

* Revert "fix(web): dedupe identical operation-failure toasts to avoid flooding"

This reverts commit 54fcfdcc97a034d7236d61cbb3664292a8966c64.
2026-07-14 19:17:28 +08:00
Haozhe
38a2363a00
fix: cross-version session compatibility and real message times in snapshot history (#1704)
* fix(server): report CLI version as server_version

- kap-server: accept opts.version in startServer, reported as
  server_version (/meta, OpenAPI, session exports, lock registry,
  default User-Agent); defaults to its own package version
- kimi-code: pass the CLI product version when starting the server
- add boot test covering /api/v1/meta, lock file, and User-Agent

* fix(agent-core-v2): make new sessions resumable by older v1 builds

- add wireRecord.seal() to write the metadata envelope at agent creation,
  so fresh logs satisfy v1 replay's first-record-must-be-metadata invariant
- seal the wire log before any op dispatch in AgentLifecycleService.create;
  no-op when the log already has records (resume / forked copies)
- seed empty agents/custom maps in session metadata so v1 Session.resume()
  can index agents['main'] on a v2-created state.json
- add seal unit tests and lifecycle sealing tests

* fix(kap-server): show real message send times in snapshot history

- read per-record times stamped on wire.jsonl via reduceContextTranscript
  and cache them alongside the transcript messages
- prefer each record's real dispatch time for created_at; records without a
  stamp fall back to session.createdAt + index, clamped so the page stays
  strictly increasing (mirrors MessageLegacyService.list)
- add tests for fallback, clamping, and the page-offset index mapping

* fix(agent-core-v2): backfill missing agents/custom maps in existing session metadata

- load() now heals pre-fix v2 state.json documents that never gained the
  agents/custom maps, persisting the backfill so one open on a new build
  leaves the session resumable by released v1 builds (Session.resume()
  indexes agents['main'] unconditionally)
- updatedAt is deliberately untouched so the format heal does not reorder
  session listings

* feat(agent-core-v2): make subagent timeout configurable, default 2h

- add [subagent] config section with timeout_ms and KIMI_SUBAGENT_TIMEOUT_MS env override
- resolve Agent and AgentSwarm per-run timeouts through resolveSubagentTimeoutMs
- update tool descriptions and tests to reflect the 2-hour default

* feat(agent-core-v2): align print-mode background policy with v1

- add printBackgroundMode/printMaxTurns to the task config section with resolvePrintBackgroundMode (keepAliveOnExit fallback)
- apply exit/drain/steer policy to kimi -p on the experimental engine, buffering turn.ended events and failing the run when a steered turn fails
- register the subagent config section from the package entry

* fix(agent-core-v2): strict equality in metadata heal, comments in file headers only

- replace == null with === undefined in the session-metadata heal to
  satisfy eqeqeq (oxlint --type-aware in CI)
- move the seal() rationale into the wireRecord contract header and the
  agents/custom invariant into the sessionMetadata header per the
  tree's header-only comment convention
2026-07-14 18:35:02 +08:00
github-actions[bot]
1d2249bfa4
ci: release packages (#1682)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-07-14 17:03:41 +08:00
qer
ab22a2adf0
fix(web): show just the thinking level name in the model pill (#1689)
Drop the "thinking:" / "思考:" prefix from the effort suffix and
capitalize the level via effortLabel, matching the segment labels.
2026-07-14 17:00:09 +08:00
Kai
d158e0a7ac
fix: resolve and synchronize thinking effort (#1625)
* fix: preserve provider thinking effort values

* fix: resolve Kimi thinking effort fallbacks

* fix: use resolved Kimi effort in v2 requests

* fix: align Kimi effort resolution paths

* fix: synchronize forced Kimi effort state

* fix: synchronize forced Kimi effort in v2 state

* fix: tolerate unresolved models in v2 status
2026-07-14 16:37:03 +08:00
liruifengv
268fd41734
docs(changelog): sync 0.24.0 from apps/kimi-code/CHANGELOG.md (#1683)
* docs(changelog): sync 0.24.0 from apps/kimi-code/CHANGELOG.md

* chore: update changelog

---------

Co-authored-by: qer <wbxl2000@outlook.com>
2026-07-14 16:23:07 +08:00
qer
0f64b4dcc4
fix(web): submit thinking level verbatim and drop the hardcoded default (#1673)
* fix(web): submit thinking level verbatim and drop the hardcoded default

Align kimi-web's thinking-level handling with the TUI:

- Submit the stored level as-is on every prompt path (prompt, steer,
  skill activation, BTW side chat) instead of coercing it onto the
  target model's declared efforts.
- No stored preference (undefined) instead of a hardcoded 'high'
  default: prompts omit the thinking override and the daemon resolves
  the config/model default, same as an unset [thinking] in the TUI.
- Model switcher pre-selects the target model's own default level when
  switching models; re-selecting the current model keeps the level.
- Display the effective level (stored value, else the model default)
  in the composer, mobile sheet, and /status panel.

* chore(web): remove the dead dev:stub script

The stub daemon (dev/stub-daemon.mjs) no longer exists, so the
dev:stub npm script and its docs references were dead weight.

* fix(web): pin the model default thinking level and persist picks globally

- With no stored preference, loadModels() pins the active model's
  catalog default_effort as a concrete in-memory value, so what the
  UI shows, what prompts submit, and what the session runs always
  agree. localStorage stays reserved for levels the user picked.
- setThinking and model switches now also write the daemon-wide
  [thinking] config (same mapping as the TUI's thinkingEffortToConfig),
  so sessions created by other clients inherit the pick.
2026-07-14 15:04:28 +08:00