Commit graph

555 commits

Author SHA1 Message Date
wenhua020201-arch
ef61f4369b
docs: document plugin slash commands (#1253) 2026-07-01 13:49:43 +08:00
qer
170b29aa90
feat(vis): support importing debug zips via drag and drop (#1251)
Some checks are pending
CI / build (push) Waiting to run
CI / test (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 / 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
* feat(vis): support importing debug zips via drag and drop

Add a window-level drop target so a /export-debug-zip bundle can be
imported by dragging it anywhere into the vis UI, alongside the existing
file-picker button. A full-screen overlay gives feedback during the drag
and while the upload is in flight, and non-zip files are rejected with a
hint.

* fix(vis): gate drop handler to file drags

Match the other drag handlers by checking dataTransfer.types before
calling preventDefault, so non-file drops (selected text or a URL into
the search input) keep their native behavior instead of being swallowed
by the window-level listener.
2026-07-01 12:02:08 +08:00
liruifengv
c2fd9f0494
docs(changelog): sync 0.21.0 from apps/kimi-code/CHANGELOG.md (#1250) 2026-07-01 11:26:44 +08:00
github-actions[bot]
f2c7ec75d3
ci: release packages (#1224)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-07-01 10:51:54 +08:00
qer
4d936d98b8
fix(kimi-desktop): revert sidebar header padding to 80px (#1242)
Some checks are pending
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 (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
100px left too much empty space next to the traffic lights; 80px clears
them without the extra gap.
2026-07-01 02:25:41 +08:00
Simon He
7f05f589e7
feat(web): add Mermaid diagram rendering and off-thread KaTeX/Mermaid workers (#1226)
* feat(web): add Mermaid diagram rendering and off-thread KaTeX/Mermaid workers

Enable Mermaid diagram support in the web chat via markstream-vue's enableMermaid(). Set up Web Workers for both KaTeX rendering and Mermaid parsing using markstream-vue's pre-built workers (katexRenderer.worker, mermaidParser.worker), keeping heavy computation off the main thread during live streaming.

* fix(web): skip Mermaid SVG subtrees in file link and markdown link rewriters

processFileLinks() uses a TreeWalker that scans all text nodes in mdRef.
Mermaid diagrams render as inline SVG, and diagram labels containing
file-path-like strings (e.g. src/App.vue) would be replaced with HTML
<button> elements inside SVG <text> nodes, corrupting the rendered diagram.

processMarkdownLinks() similarly queries a[href] inside SVGs; while
isLocalLink() mostly filters these out, explicitly skipping SVG subtrees
is safer.

Add svg to the closest() exclusion in processFileLinks(), and skip
links inside svg in processMarkdownLinks().

* fix(web): satisfy worker import lint

* chore: align mermaid dependency versions

* chore: change mermaid workers changeset to patch

---------

Co-authored-by: qer <wbxl2000@outlook.com>
2026-07-01 02:17:34 +08:00
Kai
8ac337a2b2
fix(agent-core): harden strict-provider wire compliance so malformed history can't brick a session (#1241)
* feat(agent-core): rework compaction to keep only user prompts and summary

* refactor(agent-core): rewrite compaction summary as first-person handoff

Rework the full-compaction summary to read as the agent's own continuing
notes instead of a third-party report:

- compaction-instruction.md: free-form first-person continuation that
  preserves exact commands, paths and outcomes, states the precise next
  action, and flags claimed-but-unverified work rather than trusting it.
- compaction-summary-prefix.md: skeptical "your own working notes"
  framing; drop the collaborative third-party prefix.
- system.md: add compaction-awareness guidance so the model continues
  naturally from a summary and re-checks any reported "done".
- Rename the compaction helpers module to handoff.ts.

Update tests and regenerate snapshots for the new prompt text, and fill
in contextSummary in the restored-compaction replay expectations.

* fix(agent-core): count image/audio/video parts in token estimation

estimateTokensForContentPart returned 0 for image_url/audio_url/video_url,
so auto-compaction triggers, the overflow-shrink budget, the kept-user
budget, and the reported context size all went blind to media — a
media-heavy session could overflow the model window while the estimate
reported a near-empty context. Media parts now carry a fixed estimate
(MEDIA_TOKEN_ESTIMATE), and the content-part switch is exhaustive so a new
ContentPart kind must declare its estimate rather than silently count as
zero.

* feat(agent-core): re-surface active background tasks after compaction

Folding the live context to [recent user prompts, summary] drops the
messages that started background tasks and their status updates, so the
model could forget a task is still running and spawn a duplicate.
injectAfterCompaction now appends a system-reminder listing active
background tasks (with guidance to use TaskOutput/TaskList/TaskStop
instead of re-spawning). It runs only post-compaction and carries an
injection origin, so the next compaction drops and rebuilds it rather
than stacking copies; the all-user-role post-compaction shape is
preserved (no tool-pairing reintroduced).

* test(agent-core): add compaction scenario guards and risk probes

Adds compaction-scenarios.test.ts driving the real Agent/ContextMemory/
FullCompaction machinery:

- A guard test locking in that repeated compaction folds the prior summary
  into the new one instead of stacking two summaries.
- Seven `it.fails` probes that executably reproduce known, currently-accepted
  edge-case defects so the suite stays green while documenting each one
  precisely; any of them will flip red (forcing removal of `.fails`) the day
  the behavior is fixed. They cover: assistant/tool appended during an
  in-flight summarizer call being dropped; unbounded shrink on empty
  summaries; the fixed 20k kept-user budget overflowing a small model window;
  a tool result orphaned when compaction starts mid-exchange; legacy
  compaction records dropping their verbatim tail on replay; micro-compaction
  clearing recent tool results in an overflow-shrunk suffix; and media being
  discarded when the oldest kept user message is truncated.

* fix(agent-core): repair tool_use/tool_result adjacency in projected context

A tool call and its result can end up non-adjacent in history — a
background-task notification or flushed steer lands between them, or an
interrupted/nested step delays the result — which strict providers reject
with HTTP 400. The projector now moves each tool_use's result up to
immediately follow it (projection-time only; the stored history is
untouched), and full compaction projects its summarizer input with a
synthetic result for any still-open call so the summary request stays
well-formed. Micro-compaction only surfaced this latent ordering by busting
the prompt cache, so it now defaults off.

Includes projector adjacency regression tests, a context-level integration
test, and a compaction synthesize-missing guard; the prior "keeps an
unresolved tool exchange out of the compaction prompt" test is updated to
the now-well-formed (synthetic-result) behavior.

* fix(agent-core): preserve the verbatim tail when restoring legacy compactions

A pre-rework `context.apply_compaction` record used
`[summary, ...history.slice(compactedCount)]` semantics and kept a verbatim
recent tail, but it has no `keptUserMessageCount`. The reworked applyCompaction
re-folded such records into the all-user shape, dropping the recent
assistant/tool tail — so resuming a session compacted by an older version
silently lost its most recent context.

On restore of such a record (gated on records.restoring, no keptUserMessageCount,
and compactedCount < history length) reproduce the old shape instead. The
forward/live path is unchanged; the projector's tool-adjacency repair keeps the
restored tail well-formed, and compaction only runs at clean step boundaries so
the tail has no open exchange. The legacy-tail probe now passes as a regression
guard via the real restore path.

* fix(agent-core): align legacy compaction foldedLength with live restore

The transcript reducer re-derived foldedLength for pre-rework
context.apply_compaction records (no keptUserMessageCount) using the new
kept-user+summary rule, but ContextMemory's restore now reproduces the legacy
[summary, ...history.slice(compactedCount)] shape for those records. The two
diverged for legacy sessions, so MessageService's foldedLength-vs-live-history
comparison could mis-handle GET /messages (miss or misorder recent output).

The reducer now mirrors the live legacy fold: when compactedCount is below the
pre-compaction length it computes 1 + (length - compactedCount); otherwise it
falls back to the kept-user derivation. The MessageService transcript test's
fixture is corrected to a new-format record, matching its all-user live mock.

* fix(kosong): merge a follow-up user turn into the preceding tool_results

The Anthropic message merge keyed on isToolResultOnly(last) ===
isToolResultOnly(converted), which left a tool_result-only user turn
followed by a plain-text user turn unmerged. After tool-exchange repair
this shape (assistant tool_use -> tool_result -> injected notification)
produces two adjacent user messages, which strict Anthropic-compatible
backends reject with HTTP 400.

Switch to the asymmetric predicate isToolResultOnly(last) ||
!isToolResultOnly(converted): a tool-result-only running message absorbs
whatever user turn follows (parallel tool_results or a trailing text),
yielding a valid [tool_result, ..., text] message; a plain-text running
message still only absorbs plain text. [tool_result, text] is valid for
both native Anthropic (which concatenates anyway) and strict backends.

* test(agent-core): pin micro-compaction flag in the shrunk-suffix probe

The 'does not clear recent tool results when projecting a shrunk suffix'
probe is an it.fails that only documents a real defect while
micro-compaction is active. It inherited the ambient
KIMI_CODE_EXPERIMENTAL master switch, so its pass/fail flipped with the
runner: green locally (master switch on) but a hard failure in CI, where
the flag defaults off and MicroCompaction.compact() is a no-op that
leaves the tool result intact.

Enable KIMI_CODE_EXPERIMENTAL_MICRO_COMPACTION explicitly for this probe
so it deterministically exercises the micro-compaction path regardless of
the environment.

* fix(agent-core): harden full compaction against in-flight races, unbounded shrink, and media loss

Three compaction-path fixes surfaced by review, each flipping its
documenting it.fails probe to a passing it:

- Append race (CMP-02): after the summarizer returns, the post-summary
  history check only compared the compacted prefix. A live step appending
  to the tail while a manual/SDK compaction was in flight slipped through —
  an appended assistant/tool turn is neither summarized (the summary covers
  only the snapshot) nor kept (the rebuild keeps user input), so it
  vanished. Now cancel when the appended tail contains a non-user message;
  an appended user message is still kept (rebuild picks it up), preserving
  the existing 'keeps messages appended while compacting an unchanged
  prefix' behavior.

- Unbounded empty/truncated shrink: an empty or truncated summary dropped
  the oldest message and reset retryCount, so a model that kept returning
  empty could issue ~one request per history entry. Bound the shrink
  attempts by MAX_COMPACTION_RETRY_ATTEMPTS, mirroring the overflow-shrink
  counter.

- Media dropped on truncation (CMP-07): truncating the oldest kept user
  message replaced its whole content with one text block, discarding any
  image/audio/video. Keep the non-text parts and spend the remaining budget
  (maxTokens minus their cost) on truncated text.

* fix(vis): mirror legacy compaction tail in the model-mode projector

For a pre-rework context.apply_compaction record (no keptUserMessageCount),
agent-core's ContextMemory restore and the transcript reducer keep the old
[summary, ...history.slice(compactedCount)] tail — a verbatim recent tail
including assistant/tool. The vis model-mode projector always applied the
new kept-user selection, so opening an older compacted session in model
mode hid the assistant/tool tail the resumed agent still holds (and
surfaced a pre-compaction user message the agent dropped).

Branch on a missing keptUserMessageCount with compactedCount < history
length and reproduce the legacy shape, matching the agent-core restore.

* fix(agent-core): cancel compaction on any droppable user-role tail

The in-flight append guard cancelled only when the tail grew with a
non-user role. A user-role message that compaction would still drop — a
background-task notification, hook/cron reminder, or shell-command output —
slipped through: appended after the summary snapshot (so absent from the
summary) and dropped by the all-user rebuild (which keeps only real user
input), vanishing silently.

Key the guard on the same predicate applyCompaction uses (!isRealUserInput)
so it cancels whenever the appended tail holds anything compaction would
drop. A real user message is still kept, so a live user turn racing a
manual/SDK compaction continues to complete.

* fix(agent-core): exclude pre-clear prompts from legacy folded length

The transcript reducer's legacy fallback (records predating
keptUserMessageCount, compacted with no verbatim tail) re-derived the
kept-user count from the whole transcript, including messages before the
last context.clear. Live ContextMemory rebuilds _history from post-clear
messages only, so counting pre-clear prompts overstated foldedLength;
MessageService then saw context.history.length <= foldedLength and skipped
appending unflushed live tail messages, dropping recent output from the
messages endpoint for old sessions compacted after a clear.

Derive only from entries at or after clearFloor to match the live context.

* fix(agent-core): drop media when truncating the oldest kept prompt

Revert the media-preserving truncation: keeping non-text parts on the
truncated boundary message overshot the kept-user budget when the media
alone exceeded it, and reordered interleaved text/media parts. Both codex
(no media-aware truncation) and Claude Code (strips media at compaction)
decline to preserve media on a truncated message, since media cannot be
partially truncated and keeping it whole breaks the budget.

truncateUserMessage now keeps only the truncated text. Recent messages
that fit the budget are still kept verbatim with their media; only the
oldest, partially-overflowing boundary message loses its attachments.

* fix(agent-core): make manual compaction and turns mutually exclusive

A manual/SDK compaction could start while a turn was streaming, or a new
turn could launch while a compaction was in flight. Either way the turn
mutates the shared context (streaming content into an existing assistant
message, or appending new messages) during the summarizer await, and that
output is neither summarized nor preserved by the all-user rebuild —
silent loss that object-identity checks can't detect (the streamed message
is mutated in place).

Guard both directions so the agent does one of {turn, compaction} at a
time: begin() refuses a manual compaction while a turn is active, and
launch() refuses a new turn while a compaction is in progress. Auto
compaction is exempt — it runs from within the turn at a step boundary,
which blocks the turn for its duration.

* chore(changeset): consolidate compaction changesets into one

* chore(agent-core): drop external-product references from compaction comments

* test(agent-core): add Anthropic wire-compliance smoke tests for compaction

Drive real compaction output and the compaction summarizer projection
through the real Anthropic provider conversion and assert the wire request
is well-formed: strict user/assistant alternation and every tool_use
answered by an adjacent tool_result. Locks in the cross-layer guarantee
(projector merge + Anthropic consecutive-user merge + adjacency repair +
synthesizeMissing) that compacted sessions stay valid for strict
Anthropic-compatible backends.

* fix(agent-core): defer and replay inputs during manual compaction instead of rejecting

Manual/SDK compaction runs outside a turn, so the earlier guard rejected
prompts/steers that arrived while it held the context. That broke three
things: a REST/web prompt got stuck 'running' (no terminal turn event), a
background-task/cron steer was silently lost (null was read as 'buffered'
but nothing was), and a follow-up prompt could land in the window after
isCompacting cleared but before reminders were reinjected.

Reuse the existing defer-and-replay model instead of rejecting:

- steer() and launch() buffer into steerBuffer while a compaction is in
  progress (returning null = buffered), mirroring how an active turn defers
  input.
- FullCompaction.compactionWorker keeps isCompacting true through
  refreshSystemPrompt + injectAfterCompaction (moving markCompleted and the
  completed event after reinjection), then replays the buffer via
  TurnFlow.onCompactionFinished — on success, on an A1 prefix/tail cancel,
  and on failure/abort.
- onCompactionFinished flushes into an active turn if one exists, else
  launches a fresh turn from the deferred input.

No PromptService change: a deferred prompt's eventual turn.started lets it
associate the pending prompt and clear it on turn.ended.

* feat(kosong): detect tool_use/tool_result adjacency errors

Add isToolExchangeAdjacencyError to classify the strict-provider 400 raised
when an assistant tool_use is not correctly paired with its tool_result
(missing, stray, or non-adjacent), excluding context-overflow 400s. Lets the
agent loop recognize the error and resend a wire-compliant request instead of
leaving the session stuck.

* fix(agent-core): close mid-history orphan tool calls and resend wire-compliant after a strict 400

Strict providers (Anthropic) reject a request whose assistant tool_use is not
answered by an adjacent tool_result, and the same malformed history is re-sent
every turn, permanently bricking the session.

- Projector now closes a mid-history tool call whose result is missing entirely
  (a later turn proves it is not in-flight) with a synthetic result; the
  trailing in-flight call is still left untouched.
- Add a strict projection (synthesize every open call, drop stray results) and,
  on a tool_use/tool_result adjacency 400, resend the request once with it.
- Report every projection repair (reorder / synthesize / drop) via log and
  telemetry, deduped by signature, so a silently-mangled history leaves a trace.
  Trailing-tail synthesis (expected under compaction) is not flagged.

* fix(kosong): merge consecutive user turns for strict providers

Gemini/Vertex require strictly alternating user/model turns and reject
consecutive user turns with HTTP 400. They arise after compaction (kept
prompts + user-role summary + injected reminders) and when a turn is
steered in right after a tool result. Anthropic already merged them
inline; the Google converter did not, so post-compaction requests failed.

Extract the asymmetric merge into a shared mergeConsecutiveUserMessages
helper applied at each strict provider's conversion boundary: refactor
Anthropic to use it (behavior unchanged) and apply it at the Google
converter's exit. A conformance suite drives every strict provider with
the post-compaction shape and a steer-after-tool-result shape, asserting
no consecutive same-role turns reach the wire, so a new strict provider
cannot silently omit the merge.

The provider-agnostic projector stays structure-preserving: lenient
providers (OpenAI/Kimi) keep distinct turns for clearer message
boundaries; only strict providers normalize, where the requirement lives.

* feat(kosong): recognize the broader structural request-rejection family

Add isRecoverableRequestStructureError, covering the strict-provider 400s that
stem from a malformed message array re-sent every turn: tool_use/tool_result
pairing, empty/whitespace-only text blocks, a non-user first message, and
non-alternating roles. Context-overflow 400s are excluded (handled by
compaction). Lets the loop trigger one strict, wire-compliant resend for the
whole family rather than only tool-pairing errors.

* fix(agent-core): sanitize whitespace and strict-resend structural 400s, with diagnostics

- Drop empty AND whitespace-only text blocks in projection (Anthropic rejects
  whitespace-only with "text content blocks must contain non-whitespace text",
  which otherwise sticks a session); treat whitespace-only tool output as empty.
- Broaden the post-400 strict resend to the whole structural family and add two
  strict-only passes to the strict projection: drop leading non-user messages
  (first message must be user) and merge consecutive assistant turns.
- Log + telemetry for every wire repair the projector applies (reorder,
  synthesize, drop orphan, drop leading, merge assistants, drop whitespace),
  deduped by signature; log the strict resend outcome (recovered or still
  rejected) so a stuck session always leaves a trace.

* fix(agent-core): normalize empty-equivalent tool result arrays to the empty placeholder

A tool result whose ContentPart[] output has no sendable content (an empty array,
or only empty/whitespace-only text blocks) was returned verbatim, so projection
stripped the blank blocks, left the tool message empty, and threw on every send —
bricking the session locally. String outputs were already normalized; do the same
for arrays. A non-text part or any non-whitespace text still keeps the real
output.

* chore(changeset): simplify the wire-compliance changeset
2026-07-01 02:16:19 +08:00
qer
882cf355a9
fix(web): persist workspace rename and hide provider manager (#1234)
* fix(web): persist workspace rename via the daemon update API

* fix(web): hide the unshipped provider manager

* fix(web): fall back to a local override for derived workspace renames

* fix(web): preserve workspace name override across registration
2026-07-01 02:03:19 +08:00
Kai
86e0c9201e
feat(agent-core): rework compaction to keep only user prompts and summary (#1214)
* feat(agent-core): rework compaction to keep only user prompts and summary

* refactor(agent-core): rewrite compaction summary as first-person handoff

Rework the full-compaction summary to read as the agent's own continuing
notes instead of a third-party report:

- compaction-instruction.md: free-form first-person continuation that
  preserves exact commands, paths and outcomes, states the precise next
  action, and flags claimed-but-unverified work rather than trusting it.
- compaction-summary-prefix.md: skeptical "your own working notes"
  framing; drop the collaborative third-party prefix.
- system.md: add compaction-awareness guidance so the model continues
  naturally from a summary and re-checks any reported "done".
- Rename the compaction helpers module to handoff.ts.

Update tests and regenerate snapshots for the new prompt text, and fill
in contextSummary in the restored-compaction replay expectations.

* fix(agent-core): count image/audio/video parts in token estimation

estimateTokensForContentPart returned 0 for image_url/audio_url/video_url,
so auto-compaction triggers, the overflow-shrink budget, the kept-user
budget, and the reported context size all went blind to media — a
media-heavy session could overflow the model window while the estimate
reported a near-empty context. Media parts now carry a fixed estimate
(MEDIA_TOKEN_ESTIMATE), and the content-part switch is exhaustive so a new
ContentPart kind must declare its estimate rather than silently count as
zero.

* feat(agent-core): re-surface active background tasks after compaction

Folding the live context to [recent user prompts, summary] drops the
messages that started background tasks and their status updates, so the
model could forget a task is still running and spawn a duplicate.
injectAfterCompaction now appends a system-reminder listing active
background tasks (with guidance to use TaskOutput/TaskList/TaskStop
instead of re-spawning). It runs only post-compaction and carries an
injection origin, so the next compaction drops and rebuilds it rather
than stacking copies; the all-user-role post-compaction shape is
preserved (no tool-pairing reintroduced).

* test(agent-core): add compaction scenario guards and risk probes

Adds compaction-scenarios.test.ts driving the real Agent/ContextMemory/
FullCompaction machinery:

- A guard test locking in that repeated compaction folds the prior summary
  into the new one instead of stacking two summaries.
- Seven `it.fails` probes that executably reproduce known, currently-accepted
  edge-case defects so the suite stays green while documenting each one
  precisely; any of them will flip red (forcing removal of `.fails`) the day
  the behavior is fixed. They cover: assistant/tool appended during an
  in-flight summarizer call being dropped; unbounded shrink on empty
  summaries; the fixed 20k kept-user budget overflowing a small model window;
  a tool result orphaned when compaction starts mid-exchange; legacy
  compaction records dropping their verbatim tail on replay; micro-compaction
  clearing recent tool results in an overflow-shrunk suffix; and media being
  discarded when the oldest kept user message is truncated.

* fix(agent-core): repair tool_use/tool_result adjacency in projected context

A tool call and its result can end up non-adjacent in history — a
background-task notification or flushed steer lands between them, or an
interrupted/nested step delays the result — which strict providers reject
with HTTP 400. The projector now moves each tool_use's result up to
immediately follow it (projection-time only; the stored history is
untouched), and full compaction projects its summarizer input with a
synthetic result for any still-open call so the summary request stays
well-formed. Micro-compaction only surfaced this latent ordering by busting
the prompt cache, so it now defaults off.

Includes projector adjacency regression tests, a context-level integration
test, and a compaction synthesize-missing guard; the prior "keeps an
unresolved tool exchange out of the compaction prompt" test is updated to
the now-well-formed (synthetic-result) behavior.

* fix(agent-core): preserve the verbatim tail when restoring legacy compactions

A pre-rework `context.apply_compaction` record used
`[summary, ...history.slice(compactedCount)]` semantics and kept a verbatim
recent tail, but it has no `keptUserMessageCount`. The reworked applyCompaction
re-folded such records into the all-user shape, dropping the recent
assistant/tool tail — so resuming a session compacted by an older version
silently lost its most recent context.

On restore of such a record (gated on records.restoring, no keptUserMessageCount,
and compactedCount < history length) reproduce the old shape instead. The
forward/live path is unchanged; the projector's tool-adjacency repair keeps the
restored tail well-formed, and compaction only runs at clean step boundaries so
the tail has no open exchange. The legacy-tail probe now passes as a regression
guard via the real restore path.

* fix(agent-core): align legacy compaction foldedLength with live restore

The transcript reducer re-derived foldedLength for pre-rework
context.apply_compaction records (no keptUserMessageCount) using the new
kept-user+summary rule, but ContextMemory's restore now reproduces the legacy
[summary, ...history.slice(compactedCount)] shape for those records. The two
diverged for legacy sessions, so MessageService's foldedLength-vs-live-history
comparison could mis-handle GET /messages (miss or misorder recent output).

The reducer now mirrors the live legacy fold: when compactedCount is below the
pre-compaction length it computes 1 + (length - compactedCount); otherwise it
falls back to the kept-user derivation. The MessageService transcript test's
fixture is corrected to a new-format record, matching its all-user live mock.

* fix(kosong): merge a follow-up user turn into the preceding tool_results

The Anthropic message merge keyed on isToolResultOnly(last) ===
isToolResultOnly(converted), which left a tool_result-only user turn
followed by a plain-text user turn unmerged. After tool-exchange repair
this shape (assistant tool_use -> tool_result -> injected notification)
produces two adjacent user messages, which strict Anthropic-compatible
backends reject with HTTP 400.

Switch to the asymmetric predicate isToolResultOnly(last) ||
!isToolResultOnly(converted): a tool-result-only running message absorbs
whatever user turn follows (parallel tool_results or a trailing text),
yielding a valid [tool_result, ..., text] message; a plain-text running
message still only absorbs plain text. [tool_result, text] is valid for
both native Anthropic (which concatenates anyway) and strict backends.

* test(agent-core): pin micro-compaction flag in the shrunk-suffix probe

The 'does not clear recent tool results when projecting a shrunk suffix'
probe is an it.fails that only documents a real defect while
micro-compaction is active. It inherited the ambient
KIMI_CODE_EXPERIMENTAL master switch, so its pass/fail flipped with the
runner: green locally (master switch on) but a hard failure in CI, where
the flag defaults off and MicroCompaction.compact() is a no-op that
leaves the tool result intact.

Enable KIMI_CODE_EXPERIMENTAL_MICRO_COMPACTION explicitly for this probe
so it deterministically exercises the micro-compaction path regardless of
the environment.

* fix(agent-core): harden full compaction against in-flight races, unbounded shrink, and media loss

Three compaction-path fixes surfaced by review, each flipping its
documenting it.fails probe to a passing it:

- Append race (CMP-02): after the summarizer returns, the post-summary
  history check only compared the compacted prefix. A live step appending
  to the tail while a manual/SDK compaction was in flight slipped through —
  an appended assistant/tool turn is neither summarized (the summary covers
  only the snapshot) nor kept (the rebuild keeps user input), so it
  vanished. Now cancel when the appended tail contains a non-user message;
  an appended user message is still kept (rebuild picks it up), preserving
  the existing 'keeps messages appended while compacting an unchanged
  prefix' behavior.

- Unbounded empty/truncated shrink: an empty or truncated summary dropped
  the oldest message and reset retryCount, so a model that kept returning
  empty could issue ~one request per history entry. Bound the shrink
  attempts by MAX_COMPACTION_RETRY_ATTEMPTS, mirroring the overflow-shrink
  counter.

- Media dropped on truncation (CMP-07): truncating the oldest kept user
  message replaced its whole content with one text block, discarding any
  image/audio/video. Keep the non-text parts and spend the remaining budget
  (maxTokens minus their cost) on truncated text.

* fix(vis): mirror legacy compaction tail in the model-mode projector

For a pre-rework context.apply_compaction record (no keptUserMessageCount),
agent-core's ContextMemory restore and the transcript reducer keep the old
[summary, ...history.slice(compactedCount)] tail — a verbatim recent tail
including assistant/tool. The vis model-mode projector always applied the
new kept-user selection, so opening an older compacted session in model
mode hid the assistant/tool tail the resumed agent still holds (and
surfaced a pre-compaction user message the agent dropped).

Branch on a missing keptUserMessageCount with compactedCount < history
length and reproduce the legacy shape, matching the agent-core restore.

* fix(agent-core): cancel compaction on any droppable user-role tail

The in-flight append guard cancelled only when the tail grew with a
non-user role. A user-role message that compaction would still drop — a
background-task notification, hook/cron reminder, or shell-command output —
slipped through: appended after the summary snapshot (so absent from the
summary) and dropped by the all-user rebuild (which keeps only real user
input), vanishing silently.

Key the guard on the same predicate applyCompaction uses (!isRealUserInput)
so it cancels whenever the appended tail holds anything compaction would
drop. A real user message is still kept, so a live user turn racing a
manual/SDK compaction continues to complete.

* fix(agent-core): exclude pre-clear prompts from legacy folded length

The transcript reducer's legacy fallback (records predating
keptUserMessageCount, compacted with no verbatim tail) re-derived the
kept-user count from the whole transcript, including messages before the
last context.clear. Live ContextMemory rebuilds _history from post-clear
messages only, so counting pre-clear prompts overstated foldedLength;
MessageService then saw context.history.length <= foldedLength and skipped
appending unflushed live tail messages, dropping recent output from the
messages endpoint for old sessions compacted after a clear.

Derive only from entries at or after clearFloor to match the live context.

* fix(agent-core): drop media when truncating the oldest kept prompt

Revert the media-preserving truncation: keeping non-text parts on the
truncated boundary message overshot the kept-user budget when the media
alone exceeded it, and reordered interleaved text/media parts. Both codex
(no media-aware truncation) and Claude Code (strips media at compaction)
decline to preserve media on a truncated message, since media cannot be
partially truncated and keeping it whole breaks the budget.

truncateUserMessage now keeps only the truncated text. Recent messages
that fit the budget are still kept verbatim with their media; only the
oldest, partially-overflowing boundary message loses its attachments.

* fix(agent-core): make manual compaction and turns mutually exclusive

A manual/SDK compaction could start while a turn was streaming, or a new
turn could launch while a compaction was in flight. Either way the turn
mutates the shared context (streaming content into an existing assistant
message, or appending new messages) during the summarizer await, and that
output is neither summarized nor preserved by the all-user rebuild —
silent loss that object-identity checks can't detect (the streamed message
is mutated in place).

Guard both directions so the agent does one of {turn, compaction} at a
time: begin() refuses a manual compaction while a turn is active, and
launch() refuses a new turn while a compaction is in progress. Auto
compaction is exempt — it runs from within the turn at a step boundary,
which blocks the turn for its duration.

* chore(changeset): consolidate compaction changesets into one

* chore(agent-core): drop external-product references from compaction comments

* test(agent-core): add Anthropic wire-compliance smoke tests for compaction

Drive real compaction output and the compaction summarizer projection
through the real Anthropic provider conversion and assert the wire request
is well-formed: strict user/assistant alternation and every tool_use
answered by an adjacent tool_result. Locks in the cross-layer guarantee
(projector merge + Anthropic consecutive-user merge + adjacency repair +
synthesizeMissing) that compacted sessions stay valid for strict
Anthropic-compatible backends.

* fix(agent-core): defer and replay inputs during manual compaction instead of rejecting

Manual/SDK compaction runs outside a turn, so the earlier guard rejected
prompts/steers that arrived while it held the context. That broke three
things: a REST/web prompt got stuck 'running' (no terminal turn event), a
background-task/cron steer was silently lost (null was read as 'buffered'
but nothing was), and a follow-up prompt could land in the window after
isCompacting cleared but before reminders were reinjected.

Reuse the existing defer-and-replay model instead of rejecting:

- steer() and launch() buffer into steerBuffer while a compaction is in
  progress (returning null = buffered), mirroring how an active turn defers
  input.
- FullCompaction.compactionWorker keeps isCompacting true through
  refreshSystemPrompt + injectAfterCompaction (moving markCompleted and the
  completed event after reinjection), then replays the buffer via
  TurnFlow.onCompactionFinished — on success, on an A1 prefix/tail cancel,
  and on failure/abort.
- onCompactionFinished flushes into an active turn if one exists, else
  launches a fresh turn from the deferred input.

No PromptService change: a deferred prompt's eventual turn.started lets it
associate the pending prompt and clear it on turn.ended.

* fix(kosong): merge consecutive user turns for strict providers

Gemini/Vertex require strictly alternating user/model turns and reject
consecutive user turns with HTTP 400. They arise after compaction (kept
prompts + user-role summary + injected reminders) and when a turn is
steered in right after a tool result. Anthropic already merged them
inline; the Google converter did not, so post-compaction requests failed.

Extract the asymmetric merge into a shared mergeConsecutiveUserMessages
helper applied at each strict provider's conversion boundary: refactor
Anthropic to use it (behavior unchanged) and apply it at the Google
converter's exit. A conformance suite drives every strict provider with
the post-compaction shape and a steer-after-tool-result shape, asserting
no consecutive same-role turns reach the wire, so a new strict provider
cannot silently omit the merge.

The provider-agnostic projector stays structure-preserving: lenient
providers (OpenAI/Kimi) keep distinct turns for clearer message
boundaries; only strict providers normalize, where the requirement lives.
2026-07-01 01:17:30 +08:00
qer
f12c83cfb9
fix(kimi-desktop): clear traffic lights and make the top draggable (#1237)
- Increase the sidebar header's left padding (100px) on macOS so the floating
  traffic lights no longer overlap the Kimi Code brand.
- Make the conversation header a window-drag region on macOS (interactive
  controls opt out with no-drag), so the whole top of the window can be dragged
  without adding a separate titlebar strip.
2026-06-30 23:28:34 +08:00
qer
bfe8e6ace3
fix(web): surface error when adding a workspace by path fails (#1236)
* fix(web): surface error when adding a workspace by path fails

Previously, addWorkspaceByPath swallowed any error from the daemon and created a local-only workspace that could not host sessions and vanished on reload, so an invalid absolute path appeared to succeed but never took effect. Now the error is reported to the user and no fake workspace is added.

* fix(web): preserve pending prompt when adding workspace fails

addWorkspaceByPath now returns a boolean success signal. handleAddWorkspace only clears pendingWorkspaceSubmit and sends the prompt on success, so a daemon-rejected path no longer drops the user's pending submission; they can retry with a valid path.

* fix(web): keep workspace picker open when add fails

Move closing the add-workspace dialog until after a successful add. On a daemon-rejected path the picker now stays open with the user's input intact, so they can correct the path and retry; the pending submission is consumed by a successful retry or dropped only when the user explicitly closes the picker.

* fix(web): show add-workspace errors inline in the picker

When the daemon rejects a path, the global warning toast rendered behind the picker's backdrop (z-index 60 vs 200) and could auto-dismiss unseen. Surface the failure as an inline error inside the dialog instead, so it is visible and persists until the user retries or closes. addWorkspaceByPath now returns a boolean and leaves surfacing to the caller; the picker shows a localized inline message.
2026-06-30 22:40:53 +08:00
liruifengv
108299be3c
refactor!: overhaul thinking config and effort resolution (#1132)
* feat: support multi-level thinking effort switching

- kimi provider: emit thinking.effort in the new wire format; keep reasoning_effort mirrored during the transition
- model catalog: thread support_efforts / default_effort from oauth through to /models
- config schema: add supportEfforts / defaultEffort on model aliases
- TUI: multi-segment thinking control in /model, new /effort command, footer effort display
- switch status uses displayName and distinguishes model vs effort-only changes

* docs: add thinking effort design plans

- thinking-effort-switching.md: implemented multi-level effort switching
- thinking-model-overhaul.md: follow-up refactor plan for the thinking state model

* docs: collapse thinking overhaul plan into a single PR

* refactor!: overhaul thinking config and effort resolution

Replace default_thinking and thinking.mode with a single [thinking] enabled/effort table. ThinkingEffort is now an open string ('off' | 'on' | model-declared effort); effort levels come from each model's support_efforts instead of a fixed enum.

Centralize default and always_thinking clamp logic in resolveThinkingEffort/defaultThinkingEffortFor, and honor an explicitly configured effort when an always_thinking model is forced back on.

TUI keeps a single thinkingEffort field instead of the boolean + level pair; 'on' is normalized to the model default at the UI boundary.

BREAKING CHANGE: default_thinking and thinking.mode are removed from config; migrate to [thinking] enabled/effort.

* refactor: rename residual thinking level wording to effort

Rename comments, error messages, parameter names, the SetThinkingPayload wire field (level -> effort), and TUI local variables so the thinking effort naming is consistent throughout. No behavior change.

* refactor: rename remaining camelCase thinking level identifiers to effort

Rename liveLevel/prevLevel/levelChanged/commitLevel/effectiveLevel to liveEffort/prevEffort/effortChanged/commitEffort/effectiveEffort in the TUI model picker and config commands.

* refactor: eliminate remaining thinking level wording in comments and tests

Rename levelLabel -> effortLabel, EffortSelectorOptions.levels -> efforts, and 'effort level(s)' / 'default level' / 'requested level' wording in comments, error messages, slash-command description, and test titles to effort. Also restore the withThinking(effort) parameter rename in the Kimi provider that was accidentally reverted.

* fix: address codex review feedback on thinking effort handling

- OpenAI thinkingEffortToReasoningEffort and Anthropic clampEffort now normalize 'on' / unrecognized efforts instead of throwing, so boolean non-Kimi models no longer crash on session start.

- ACP resolveCurrentThinkingEnabled treats a non-empty thinking.effort as enabled, matching agent-core's resolveThinkingEffort.

- REST promptThinkingSchema accepts any non-empty effort string so model-declared efforts are not rejected at the API boundary.

* test: align kimi e2e expectations with supportEfforts-gated reasoning_effort

The kimi provider now sends reasoning_effort only when the model declares support_efforts; boolean models (no support_efforts) send only thinking.type. Update the kimi e2e tests to drop the stale reasoning_effort expectation for the boolean test model.

* test: cover [thinking] effort parsing in config.test

Add effort = "high" to the documented [thinking] table in the config parse test and assert config.thinking.effort is resolved, so the new [thinking] effort field has direct parse coverage.

* docs: add thinking test coverage gap analysis

Capture the explore agent's test coverage review for the thinking overhaul PR, including P1/P2 gaps and the two open design questions, for follow-up test additions.

* feat(oauth): parse nested think_efforts from /models response

The /models endpoint now returns effort levels under a nested think_efforts object ({ support, valid_efforts, default_effort }). Parse it preferentially in both managed-kimi-code and open-platform model parsing, falling back to the legacy flat support_efforts / default_effort fields for older servers.

* refactor(oauth): only read nested think_efforts; gate on support=true

Drop the legacy flat support_efforts / default_effort fallback. The think_efforts object is now the single source, and its support flag gates the whole object — when support is not true, valid_efforts and default_effort are ignored entirely.

* chore: remove unused parseStringArray import in open-platform

* docs: finalize thinking effort release notes

Downgrade the changeset to minor with an English summary, drop the version-specific 'added in 1.0.0' info block, and present the deprecated config fields as a table (field / deprecated in 0.21.0 / description).

* refactor: drop temporary refresh toggles and kimi reasoning_effort mirror

Remove the always-true REFRESH_MODELS_ON_PICKER_OPEN / REFRESH_PROVIDER_MODELS_ON_STARTUP toggles and their stale re-enable TODOs, and stop sending reasoning_effort from the kimi provider (thinking.effort is the only wire field now).

* fix(tui): avoid persisting "on" as thinking effort

* fix: preserve persisted thinking effort across login and provider setup

* fix(tui): show actual thinking effort in /status and footer

* test(tui): align message-flow expectations with effort persistence and /status display

* fix(vis): rename thinkingLevel to thinkingEffort in config.update analysis
2026-06-30 22:34:13 +08:00
qer
e207f52f55
ci(kimi-desktop): strip Developer ID prefix from CSC_NAME (#1235)
* ci(kimi-desktop): strip Developer ID prefix from CSC_NAME

electron-builder rejects the 'Developer ID Application: ' prefix in CSC_NAME;
the keychain setup exports the full certificate name, so strip the prefix
before passing it to electron-builder.

* ci(kimi-desktop): add homepage and maintainer for linux .deb

electron-builder's .deb target requires a project homepage and a package
maintainer; set both so the Linux build succeeds.
2026-06-30 22:19:35 +08:00
Kai
020992c286
fix(cli): force-exit headless runs so a lingering handle can't wedge kimi -p (#1233)
* fix(cli): force-exit headless runs so a lingering handle can't wedge kimi -p

Print mode (`kimi -p`) never calls process.exit(); it relies on the Node
event loop draining once the run completes. A stray ref'd handle left over
from the run — a lingering socket, an un-cleared timer, or a child whose
pipes stay open — keeps the loop alive, so a finished run hangs until an
external timeout kills it instead of exiting.

- Arm an unref'd force-exit fallback after a headless run completes. A
  healthy run drains and exits before it fires (behaviour unchanged); it
  only force-exits a run whose loop is already wedged.
- Bound prompt cleanup with a timeout so a wedged shutdown step (a
  SessionEnd hook, MCP shutdown, or a connection blackholed by a
  restrictive firewall) can't keep a completed run alive forever.

Add unit tests for the force-exit guard and for cleanup staying bounded
when harness.close() hangs.

* refactor(cli): move headless force-exit to the entrypoint, keep handleMainCommand pure

handleMainCommand is a reusable, exported, unit-tested handler — scheduling a
deferred process.exit inside it could terminate a test runner or an embedding
host once the spied exit was restored. Make it pure: it now returns a
MainCommandOutcome, and the process entrypoint (the program action, which
already owns the error-path process.exit) arms the unref'd force-exit fallback
only for a completed headless run.

* fix(cli): drain stdio before the headless force-exit to avoid truncating output

The unref'd force-exit fallback fired after a fixed grace even when the event
loop was alive only because buffered stdout/stderr had not finished flushing to
a slow or piped consumer (the prompt writers ignore write() backpressure). That
could truncate the assistant output or resume hint of an otherwise-successful
`kimi -p` run — a regression versus the previous natural-drain exit.

finalizeHeadlessRun now flushes stdio first (bounded by HEADLESS_STDIO_DRAIN_TIMEOUT_MS)
and only then arms the force-exit. Draining first also means a leaked handle is
the only thing that can still hold the loop, so the backstop stays precise and
bounded.

* fix(cli): only swallow cleanup rejections abandoned by the timeout

The cleanup timeout guard caught every rejection, so a cleanup step that failed
fast (e.g. restoring a resumed session's permission, or harness.close hitting a
persistence error) was silently swallowed: runPrompt resolved as success and the
session could be left in `auto` with no error surfaced.

A timeout should bound how long we wait, not change the outcome. raceWithTimeout
now propagates a rejection that settles before the timeout, and only swallows the
abandoned promise's late rejection once the timeout has won the race.
2026-06-30 22:13:17 +08:00
qer
210cedb3bf
chore: add KCD client for test (#1230)
* feat(kimi-desktop): add Electron desktop client wrapping kimi-web

New apps/kimi-desktop — a thin Electron shell + process manager around
the existing web UI. It reuses kimi-code's shared daemon: it runs the
bundled SEA's `server run` (the same ensureDaemon reuse-or-spawn flow as
`kimi web`), reads ~/.kimi-code/server/lock for the real origin, and
loads the SEA-served kimi-web same-origin. The daemon is left running on
quit so the CLI / browser / TUI keep sharing it.

- main process: ensure-server (run SEA, read lock, confirm healthz),
  sea-path (dev vs packaged), window + native menu + window-state +
  loading/error screens
- packaging: electron-builder config; before-pack stages the
  matching-platform SEA into <resources>/bin/<target>
- CI: desktop-build workflow builds unsigned mac/win/linux installers,
  each runner building its own SEA
- workspace wiring: register in flake.nix, allow electron postinstall
  (onlyBuiltDependencies), root dev:desktop + typecheck entries

v1 is unsigned, default icon, no auto-update.

* feat(kimi-desktop): sign + notarize macOS builds

Unsigned macOS builds are blocked by Gatekeeper ("app is damaged") once
transferred to another Mac. Add Developer ID signing + Apple notarization,
mirroring the TUI native build:

- build/entitlements.mac.plist: hardened-runtime entitlements (allow-jit,
  disable-library-validation for koffi/clipboard, etc.) applied to the app
  and — via entitlementsInherit — the nested SEA backend
- electron-builder.config.cjs (replaces .yml): hardenedRuntime + entitlements;
  signing and notarization are env-driven (CSC_* + KIMI_DESKTOP_NOTARIZE +
  APPLE_API_* ), so the same config builds unsigned locally or signed+notarized
- desktop-build CI: sign-macos input reuses the existing macos-keychain-setup
  action + APPLE_* secrets, notarizes via the notary API key
- README: document signing, the Developer-ID requirement, and the
  "don't rename the .app" gotcha

Verified locally that electron-builder signs both the app and the nested SEA
with hardened runtime + the entitlements, and the signed app still launches and
serves the web UI. Notarization itself needs a Developer ID cert (CI / a machine
that has one).

* feat(kimi-desktop): rename product to Kimi Code Desktop

productName / window title / menu label / error-screen text all use
"Kimi Code Desktop" so the bundle name matches its executable (a
mismatch from manual renaming is itself reported as "damaged").

* ci(kimi-desktop): build and attach desktop installers in the release pipeline

Make desktop-build.yml reusable (workflow_call) and invoke it from the
release workflow, mirroring the native-build pipeline, so each release
also attaches signed+notarized macOS, Windows and Linux desktop
installers to the GitHub Release.

* feat(kimi-desktop): brand the desktop as an internal testing build

- Add an inline 'internal testing build' tag next to the Kimi Code brand
  in the sidebar header, shown only inside the desktop app.
- Use a hidden native title bar on macOS with the traffic lights folded
  into the sidebar header, and pin the window title to the product name.
- Ship the Kimi app icon for macOS and Linux builds.

Desktop detection is runtime (a query hint from the Electron shell,
persisted in sessionStorage) so the branding appears even when the
window is served by an already-running shared daemon.

* docs(kimi-desktop): update v1 scope now that the app icon ships

* feat(kimi-desktop): add the Kimi app icon for Windows builds

* chore(nix): update pnpmDeps hash after lockfile refresh

* ci(kimi-desktop): build desktop on release but do not attach to GitHub Release

The desktop build is an internal-testing artifact (branded as such), so
keep it as a CI artifact for internal download instead of publishing it
to the public GitHub Release.

* chore(kimi-desktop): mark installers as internal pre-release builds

Rename the packaged artifacts to KCD-Internal-<version>-<arch>.<ext> and
bump the version to the 0.1.1-internal.0 pre-release, so a leaked or
forwarded installer file is not mistaken for an official public release.

* feat(kimi-desktop): strengthen the internal-build tag wording

Change the sidebar tag to 'Internal testing · do not distribute' /
'内部测试 · 禁止外传' so the no-distribution intent is explicit.

* feat(kimi-desktop): tweak internal-build tag to '仅供内部测试'

* fix(kimi-desktop): pass the server token to the web UI on launch

Read the daemon's persistent bearer token from <KIMI_CODE_HOME>/server.token
and carry it in the URL fragment (#token=), matching how 'kimi web' opens
the Web UI. Without this, a fresh launch (no saved credential) boots the
web UI without a token, hits 401, and falls into the manual token dialog
even though the desktop started the daemon itself.

Addresses review feedback on the desktop URL.
2026-06-30 21:46:51 +08:00
Haozhe
ceb27f5e44
feat(server): add GUI store API mirroring localStorage (#1231)
Some checks are pending
CI / build (push) Waiting to run
CI / test (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 / Deploy docs (push) Blocked by required conditions
Release / Native release artifact (push) Blocked by required conditions
Release / Publish native release assets (push) Blocked by required conditions
Release / Release (push) Waiting to run
* feat(server): add GUI store API mirroring localStorage

- add /api/v1/gui/store/* endpoints (getItem/setItem/removeItem/clear/length) mirroring the browser localStorage interface
- add IGuiStoreService persisting opaque string values to ~/.kimi-code/gui.toml via smol-toml with atomic writes and an in-process write lock
- wire protocol schema, service, routes, and DI registration; add e2e tests and update the API surface snapshot

* chore: add changeset for gui store api

* fix(server): harden GUI store key handling and file permissions

- use a null-prototype record and an own-property check so keys that exist on Object.prototype (toString, constructor, __proto__) behave like ordinary keys
- write gui.toml with 0600 permissions so unsent drafts and input history stay private to the owning user
2026-06-30 21:04:59 +08:00
qer
aa6b0d065e
feat(web): always show the usage-data opt-out toggle in settings (#1232)
The telemetry toggle was hidden unless the config explicitly set a value, and its on/off mapping treated the default (unset) state as off even though telemetry is enabled by default. Show it always, treat unset/true as on, and rename it with a description and a restart note.
2026-06-30 20:49:35 +08:00
qer
5cb80ce879
feat: support plugin slash commands (#1204)
* feat(agent-core): support plugin slash commands

* feat(node-sdk): expose listPluginCommands

* feat(kimi-code): register and dispatch plugin slash commands

* chore: add changeset for plugin commands

* feat(agent-core): activate plugin commands server-side

* feat(node-sdk): add activatePluginCommand

* feat(kimi-code): render plugin command activations compactly

* feat(agent-core): recurse plugin command directories and preserve namespace

* fix(kimi-code): parse nested plugin command names

* fix(agent-core): update prompt metadata for plugin command turns

* fix(kimi-code): replay plugin command turns as command cards

* fix: treat plugin-command origins as real user prompts in undo

* fix(kimi-code): guard model-empty and clear plugin command render ids

* fix: propagate plugin_command to web and vis turn projectors

* fix(kimi-code): refresh plugin commands through auth flow

* fix(kimi-web): render plugin command cards in chat pane

* fix(kimi-web): render plugin command card in desktop chat view

* fix(kimi-code): treat slash-activation cards as transcript turn boundaries

* fix(kimi-code): count slash-activation entries when trimming transcript turns

* fix(kimi-code): preserve plugin command args in undo selector
2026-06-30 19:38:01 +08:00
Kai
42e37eb898
feat(timing): split TTFT into api-server and client portions (#1228)
* feat(timing): split TTFT into api-server and client portions

Time-to-first-token previously lumped in-process request building
(message serialization, param assembly) together with network + server
latency, making it impossible to tell whether a slow turn was the client
or the API server.

Add an `onRequestSent` hook to kosong's GenerateOptions, fired by every
provider immediately before it dispatches the network call. The window
from request start to dispatch is attributed to the client; the window
from dispatch to the first streamed token is attributed to the API
server. The split flows through the step.end / turn.step.completed
events (and therefore wire.jsonl) and is surfaced in three places:

- KIMI_CODE_DEBUG=1: `TTFT: 2.5s (api 2.4s + client 100ms)`
- session log: new `llm response` line with the timing breakdown
- vis: firstToken/api + firstToken/client rows and timeline label

The split is omitted (total only) when a provider does not report the
boundary, preserving backward compatibility.

* feat(timing): split the decode window into server vs client time

Time-to-first-token now reports a client/server split, but the slow part
of a long turn is the decode window (inter-token streaming), which was
still a single opaque number. Profiling long sessions showed decode
throughput halving over a session's lifetime independent of context
size, which the synchronous per-chunk stream pipeline can cause: kosong
awaits the host callback for every streamed part, so a loaded main
thread throttles how fast tokens are pulled off the wire.

Account for this directly in the stream loop: the time awaiting the next
part (server + network) versus the time spent processing each part
in-process (deep copy, host callback, part merge). The split is reported
through onStreamEnd and flows through the step.end / turn.step.completed
events (and wire.jsonl) into the same three surfaces as the TTFT split:

- KIMI_CODE_DEBUG=1: `TPS: 40.0 tok/s (200 tokens in 5.0s; server 4.6s + client 400ms)`
- session log: serverDecodeMs / clientConsumeMs on the `llm response` line
- vis: streamDuration/server + streamDuration/client rows and timeline label

A large, growing client share confirms host-side throttling; a dominant
server share points at the server/connection. The per-chunk accounting is
wrapped in try/finally so it stays correct across `continue` and aborts,
and is omitted when the stream reports nothing.
2026-06-30 19:15:02 +08:00
liruifengv
659062d11c
fix(tui): enable file path completion for / in shell mode (#1225)
Typing `/\' in shell mode (`!\') now triggers file path completion instead of the slash command menu, for both a bare leading `/\' and inline paths like `ls /\'. Hidden entries are skipped to match `/add-dir\', and accepting a completion no longer produces a double leading slash.
2026-06-30 17:54:24 +08:00
qer
a3f9cec8a9
fix(web): deduplicate workspaces shown in the sidebar (#1221)
Collapse registered workspaces that share a root in the daemon registry (preferring the canonical id) and in the web sidebar merge, so the same folder no longer renders as two identical, synchronously-selected entries.
2026-06-30 17:25:24 +08:00
liruifengv
ec51324230
feat(tui): open undo selector on double-Esc (#1220)
* feat(tui): open undo selector on double-Esc

Pressing Esc twice while idle now opens the undo selector, equivalent to running /undo with no arguments. Esc during streaming, compaction, or with a popup open keeps its cancel/close behavior and does not arm the double-press.

* fix(tui): disarm double-Esc undo on any intervening key

A pending double-Esc was only cleared by text changes, so a sequence like Esc, Ctrl-C, Esc within the window still opened the undo selector. Fire an onNonEscapeInput hook for every non-Escape key and clear the pending state there, so the shortcut only triggers for two consecutive Escape presses.
2026-06-30 15:42:27 +08:00
liruifengv
80e6888e34
fix(tui): open @ file mentions inside slash command arguments (#1223)
Typing @ in the middle of a slash command argument (for example `/goal Fix the @checkout docs`) was swallowed by the slash-argument completion guard before the @ mention branch ran, so the file list never opened. Run the @ mention branch ahead of the slash guards so file mentions take priority; plain slash-argument editing is still suppressed as before.
2026-06-30 15:35:19 +08:00
liruifengv
7f61488a88
docs(changelog): sync 0.20.3 from apps/kimi-code/CHANGELOG.md (#1216) 2026-06-30 14:50:31 +08:00
Kai
525fb146d6
feat(vis): full session debugging — tasks/cron, execution timeline, retries & tool progress (#1210)
* feat(vis): surface background tasks and cron jobs

The visualizer read every wire/state/blob artifact a session persists but
ignored the two on-demand families agent-core also writes under the session
directory: background tasks (tasks/<id>.json + output.log) and cron jobs
(cron/<id>.json). Neither is reconstructable from the wire, so there was no
way to inspect what a session spawned in the background or scheduled.

Server:
- task-store / cron-store read-only readers mirroring agent-core's on-disk
  layout, id-validation guard, and legacy snake_case task normalization
- GET /:id/tasks, /:id/tasks/:taskId/output (byte-window paged via an exact
  nextOffset cursor), and /:id/cron routes
- re-export the public background-task types from agent-core; mirror the
  non-exported CronTask shape with a fixture-backed drift test

Web:
- Tasks tab: process/agent/question kinds with status, timing, kind-specific
  fields, raw JSON, and a progressively paged output.log viewer
- Cron tab: expression, prompt, recurring/one-shot, created/last-fired
- count badges on both tabs

Tests: +20 (lib + route), all 113 vis-server tests green; web typecheck and
build clean.

* feat(agent-core): persist step retries and tool progress summary

Two transient signals were only ever emitted as live-only loop events, so
nothing survived in the agent record for post-hoc analysis:

- step retries: chatWithRetry gains an onRetry callback; turn-step collects
  the recovered attempts and attaches them to step.end as an optional
  `retries` array (previously only the live `step.retrying` event).
- tool progress: tool-call distills a tool's sparse status/percent updates
  into a bounded `progress` summary (updateCount / lastStatus / maxPercent)
  on tool.result. Streamed stdout/stderr is excluded — it would bloat the
  wire and is already reflected in the result output.

Both are additive optional fields, so the wire protocol version is unchanged
and existing records keep loading. New public types: LoopStepRetryRecord,
LoopToolProgressSummary.

* feat(vis): add execution-analysis timeline and surface retries/progress

Turn the debugger from a flat record viewer into an analysis tool.

New Timeline tab: folds the wire into turns → steps → tool calls (client-side,
no extra round-trip) and derives the metrics the raw list hides — per-turn /
per-step / per-tool duration, per-turn token cost, a context-window fill
sparkline with cache-hit rate, a tool usage table, idle-gap detection, and a
config-change timeline.

Inline elsewhere:
- Wire rows show tool.call → tool.result elapsed time; tool.result detail
  shows truncation, output size, retries, and the progress summary.
- Issues drawer gains tool-error, truncation, filtered, max_tokens, and
  retried categories.
- Tasks tab links agent-kind tasks to the subagent's wire.

Wires up vitest for the web package and adds analysis/issues unit tests.

* feat(vis): import debug zips with a logs view and imported-session filtering

A `/export-debug-zip` bundle is just `manifest.json` plus a flattened session
directory, which vis already knows how to read. Importing one therefore lights
up every existing tab for a session that lives on someone else's machine.

Server:
- zip-import: yauzl extraction with zip-slip path guards and entry-count /
  uncompressed-size caps for untrusted uploads.
- import-store: extract a bundle into <home>/imported/<imp_…>/, validate it
  has a main wire, and record an import-meta.json sidecar.
- session-store resolves imp_-prefixed ids against imported/, so wire /
  context / tasks / cron / blobs / logs all work on imported sessions; agent
  homedirs are re-derived locally (the bundle holds foreign absolute paths).
- POST /api/imports (raw zip body) and GET /api/sessions/:id/logs (structured
  log lines — also available for local sessions).

Web:
- session rail: import button + all/local/imported filter + imported badge.
- new Logs tab: virtualized, level filter, search, session/global toggle.
- manifest card atop the State tab for imported sessions.

SessionSummary/SessionDetail gain `imported` + `importMeta`. Tests cover
extraction, the zip-slip guard, list merge, reading an imported wire through
the existing route, and log parsing.

* fix(vis): read tasks/cron from agent homedirs and stop persisting tool status text

Addresses review feedback on the debug-tooling changes:

- Background tasks and cron jobs are persisted under each agent's homedir
  (<session>/agents/<id>/tasks and /cron), not the session root. The Tasks and
  Cron tabs read the session root, so they showed nothing for normal sessions.
  Both routes now aggregate across detail.agents homedirs; task entries carry
  the owning agentId. The route-test fixtures were writing to the wrong
  (session-root) location too — corrected to the real agents/main layout so
  they actually exercise the path.

- tool.result progress no longer keeps free-form status text, only updateCount
  and maxPercent. A tool's status string can contain sensitive data (e.g. an
  MCP OAuth authorization URL) that must not leak into persisted wire files or
  exported debug bundles.

* fix(vis): stop the main content area from overflowing horizontally

The <main> flex child lacked min-w-0, so it defaulted to min-width:auto and
refused to shrink below its content's intrinsic width. Tabs that lay out in
normal flow with flex-wrap rows (the Timeline tab) then got unbounded width,
never wrapped, and blew the layout out to thousands of pixels wide. Adding
min-w-0 lets the column shrink to the available width so its content wraps,
truncates, or scrolls within its own container.

* fix(vis): resolve local global log path and imported-state agent fallback

- Logs tab: for non-imported sessions the shared global log lives at
  <KIMI_CODE_HOME>/logs/kimi-code.log, not under the session dir (that path is
  only used inside exported bundles). The route now reads the home path for
  local sessions, so the global-log toggle works for them.
- Imported detail: a bundle's state.json is best-effort and may omit the
  agents map. When the inventory is empty, fall back to discovering agents
  from disk so routes that require an agent (wire/context) still resolve main.

* fix(vis): harden imported manifest and task parsing against corrupt input

An imported debug zip is untrusted, so a syntactically valid but type-corrupt
file could crash whole views:

- manifest.json: a non-string field (e.g. workspaceDir: 123) flowed into
  SessionSummary.workDir, where the session rail calls .split('/') and crashed
  the entire list. readManifest/readImportMeta now sanitize declared string
  fields, keeping only strings.
- task JSON: a record that passed the shape guard but held a non-string legacy
  field (e.g. stop_reason: 5) threw in normalization, failing GET /tasks with a
  500 and hiding all of a session's tasks. optionalNonEmptyString now tolerates
  non-strings, and listBackgroundTasks skips any record that still fails to
  normalize — honouring the reader's documented silently-skips contract.

* fix(vis): discover rotated logs; keep tool progress on thrown failures

- Logs tab: the diagnostic log can rotate (kimi-code.log.1, .2, …) and an
  exported bundle may contain only the archives. The route now discovers the
  active file plus its rotated siblings and concatenates them oldest-first, so
  a rotated-away log still surfaces (covered by node-sdk's rotated-export case).
- agent-core: a tool that reported sparse progress and then threw lost its
  progress summary, because the catch path built the error tool.result without
  it. Thread progressSummary through that path too, matching the success and
  malformed-return paths.

* fix(vis): skip type-corrupt agent entries in imported state

readImportedDetail's empty-inventory fallback never ran when a bundle's
state.json had a non-empty but type-corrupt agents map (e.g.
`{ "agents": { "main": null } }`): inventoryAgents dereferenced the null entry
and threw, so readSessionDetail returned 500 instead of recovering main from
the on-disk agents/main/wire.jsonl. inventoryAgents now skips non-object
entries, letting the disk-discovery fallback take over.

* fix(vis): reset timeline agent on session change; preserve context on zero-usage steps

- Timeline tab kept the previously-selected agent id across session navigation,
  so a subagent selection would 404 against the next session. Reset it to main
  on sessionId change, mirroring WireTab/ContextTab.
- A zero-usage step.end (e.g. a content-filtered response) reset the
  context-window fill to 0, pushing a false drop into the Timeline chart and the
  Context tab. agent-core's ContextMemory keeps the prior count in that case;
  the analysis lib and the context projector now do the same.

* revert: drop agent-core retries/tool-progress persistence

These were the only changes in this branch that touched agent-core. They
persisted two previously live-only signals (step retries, tool progress) to
the wire purely so the visualizer could display them — marginal features that
did not justify modifying the core loop or extending the wire surface.

Reverts the agent-core loop/type/export changes (restored to main, keeping
#1209) and its changeset, and removes the vis-side rendering and types that
consumed step.end.retries / tool.result.progress. The rest of vis is unchanged
and reads only data agent-core already persists.
2026-06-30 13:40:37 +08:00
github-actions[bot]
b41f108584
ci: release packages (#1197)
Some checks are pending
CI / build (push) Waiting to run
CI / test (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 / Native release artifact (push) Blocked by required conditions
Release / Release (push) Waiting to run
Release / Deploy docs (push) Blocked by required conditions
Release / Publish native release assets (push) Blocked by required conditions
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-06-30 12:37:57 +08:00
Haozhe
14d9e98903
feat(server): auto-refresh provider model catalog and push change events (#1207)
* feat(server): auto-refresh provider models and push change events

- add scheduled provider-model refresh in the daemon (configurable
  interval + refresh-on-start) plus manual endpoints:
  POST /providers:refresh and POST /providers/{id}:refresh
- publish global event.model_catalog.changed when a refresh changes
  the catalog so connected clients can resync
- extract the refresh orchestrator into @moonshot-ai/kimi-code-oauth so
  the CLI and server share managed/open-platform/custom-registry logic
- wire the web daemon client to the new refresh endpoints

* chore: add changeset for provider model auto-refresh

* fix(web): reload model and provider caches on catalog change events

When the daemon's scheduled refresh changes the catalog, the pushed
event.model_catalog.changed only advanced the websocket sequence, leaving
the web composer's model/provider refs stale until an unrelated reload.
Reload both caches when the event arrives.

* test(sdk): cover event.model_catalog.changed in event exhaustiveness
2026-06-30 12:29:10 +08:00
qer
636ccc40f1
fix(web): fix mobile Safari composer toolbar overlap and focus zoom (#1212)
* fix(web): keep composer visible above the mobile Safari toolbar and keyboard

* fix(web): scope the mobile Safari composer fix to the toolbar case

* fix(web): prevent page zoom when focusing the mobile composer
2026-06-30 12:15:14 +08:00
Kai
063538744f
refactor(agent-core): align malformed tool args with schema validation (#1209)
Some checks are pending
CI / test-windows (push) Waiting to run
CI / lint (push) Waiting to run
CI / build (push) Waiting to run
CI / test (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 / Native release artifact (push) Blocked by required conditions
Release / Release (push) Waiting to run
Release / Deploy docs (push) Blocked by required conditions
Release / Publish native release assets (push) Blocked by required conditions
* feat(agent-core): repair malformed tool args JSON

Attempt jsonrepair when tool call arguments fail JSON.parse, then continue schema validation. Return malformed JSON errors with a concise expected schema hint so the model can retry with corrected arguments.

* chore(nix): update pnpm deps hash

Update the fixed-output pnpmDeps hash after adding jsonrepair so the Nix build can fetch dependencies.

* refactor(agent-core): drop tool args JSON repair

Stop repairing malformed tool call arguments. Fall back to an empty object on JSON parse failure and let schema validation produce the retry error, while preserving valid arguments for unknown tools in the transcript.

* chore: add changeset for tool args validation
2026-06-29 23:24:03 +08:00
7Sageer
53e79e4bae
chore(telemetry): add ripgrep fallback telemetry (#1203)
Some checks are pending
CI / lint (push) Waiting to run
CI / build (push) Waiting to run
CI / test (push) Waiting to run
CI / test-windows (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 / Native release artifact (push) Blocked by required conditions
Release / Release (push) Waiting to run
Release / Deploy docs (push) Blocked by required conditions
Release / Publish native release assets (push) Blocked by required conditions
2026-06-29 18:30:09 +08:00
7Sageer
c82dcf9cd8
refactor(agent-core): use ripgrep for Glob tool (#1068)
* refactor(agent-core): use ripgrep for Glob tool

Glob now shares Grep's ripgrep subprocess plumbing: it respects .gitignore by default, supports brace patterns natively, adds an include_ignored option, and returns only files.

* fix(glob): address review findings on ripgrep migration

- Run rg with cwd pinned to the search root so glob patterns containing
  a slash (e.g. src/**/*.ts) match under an absolute search root.
- Keep include_dirs as a deprecated, ignored parameter so older calls
  are not rejected by parameter validation.
- Surface stdout truncation and drop half-written trailing paths when
  the rg output buffer is capped.
- Document that a bare pattern (e.g. *.ts) matches recursively, and sync
  user docs, the explore profile prompt, and the TUI summary to the new
  files-only / gitignore behavior.
- Add real-ripgrep integration tests covering sort order, recursion,
  brace patterns, and the absolute-search-root case.

* fix(glob): keep partial results on traversal errors

---------

Co-authored-by: hynor <hynor@users.noreply.github.com>
Co-authored-by: Kai <me@kaiyi.cool>
2026-06-29 17:40:58 +08:00
7Sageer
10ffb7d9f9
chore(telemetry): normalize telemetry property keys to snake_case (#1196)
- Rename camelCase telemetry keys to snake_case on compaction_finished, compaction_failed, micro_compaction_finished, and the tool error event (tokens_before, tokens_after, compacted_count, retry_count, thinking_level, error_type, input_tokens/output_tokens, and the micro compaction config/effect keys).

- Emit a fixed client-attribution key set (client_id/name/version/ui_mode, null when absent) from both session_started producers (core-impl and kimi-harness) so they share a stable schema.

- Drop the duplicate current/latest keys on update_prompted and the redundant ui_mode on server_started.

- Additive fields: login.method=oauth and question_answered.answered.

Telemetry-only change; no changeset.
2026-06-29 17:40:50 +08:00
Haozhe
3e98e709b3
docs(changelog): sync 0.20.2 from apps/kimi-code/CHANGELOG.md (#1198) 2026-06-29 16:51:23 +08:00
liruifengv
0df1812502
fix: render provider HTML error messages instead of blank lines (#1191)
When a provider returns an HTML error page (e.g. nginx 413 Request Entity Too Large), the error message carried CRLF line endings and raw HTML. The trailing carriage returns made the TUI render the error line as blank. Extract the page title for the wire message and strip carriage returns before rendering.
2026-06-29 15:49:48 +08:00
github-actions[bot]
52e2719d17
ci: release packages (#1154)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-06-29 14:49:34 +08:00
Haozhe
c8d1d76de5
test(agent-core): align turn telemetry expectations with provider_type (#1194)
Update the turn_started/turn_ended and api_error telemetry assertions to
expect provider_type, matching the telemetry property rename in #1184.
2026-06-29 14:44:26 +08:00
Kai
821847cb4b
feat(managed-kimi-code): route anthropic protocol via beta api (#1186)
* feat(managed-kimi-code): route anthropic protocol via beta api

- kosong: add betaApi option to use client.beta.messages.create
- agent-core: thread alias betaApi into the anthropic provider config
- oauth: route managed models on the anthropic protocol through the beta Messages API

* feat(providers): add KIMI_CODE_CUSTOM_HEADERS support

- Add KIMI_CODE_CUSTOM_HEADERS env var for custom outbound LLM headers
- Send User-Agent to non-Kimi providers
- Forward Kimi identity headers to model catalog fetches
- Support defaultHeaders in Google GenAI provider

* feat(agent-core): add protocol attrs to turn and api error telemetry

- Add type/protocol/alias to api_error for per-protocol error attribution

- Add turn_ended event with reason/duration/mode/type/protocol

- Add type/protocol to turn_interrupted

* chore(oauth): remove hardcoded internal dev endpoint from shared OAuth base URLs

---------

Co-authored-by: haozhe.yang <yanghaozhe@moonshot.ai>
2026-06-29 14:24:01 +08:00
liruifengv
04b3492e74
fix(tui): keep working tips out of the agent swarm progress line (#1189)
* fix(tui): keep working tips out of the agent swarm progress line

The activity loader is shared between the activity pane and the agent swarm progress status line. Tips were written into the loader, so they leaked into the swarm progress line and got squeezed against the bar. Keep the inline spinner text used by the swarm progress line free of tips, while the loader's own row in the activity pane still shows them.

* test(tui): stop moon loader timers in tests

MoonLoader starts a real setInterval in its constructor. Stop every loader created in the tests via afterEach so no live timer leaks past the file.
2026-06-29 14:21:16 +08:00
Haozhe
c99bd1c10e
chore(changeset): downgrade web-completion-sound changeset to patch (#1190) 2026-06-29 14:03:29 +08:00
liruifengv
db5fbc53c0
fix(tui): stop full-screen redraw on input and slash panel toggle (#1188)
Remove the global clear-on-shrink setting so typing in the input box and opening or closing the slash panel no longer trigger a full-screen redraw.
2026-06-29 13:51:31 +08:00
liruifengv
97f9263c6f
fix(tui): clear debug timing status on undo (#1187) 2026-06-29 13:31:08 +08:00
7Sageer
49e93893a6
refactor: standardize telemetry property names (#1184)
Align outlier telemetry events with the conventions already used by
tool_call, api_error, permission_approval_result, and the plan events:

- duration / latency_ms / duration_s -> duration_ms
- success boolean -> outcome enum ('success' | 'error')
- bare type -> provider_type

The exit event switches from seconds to milliseconds to match the
duration_ms convention, so its numeric scale changes accordingly.
2026-06-29 12:34:38 +08:00
qer
1dab2c2268
feat(web): preserve side panel and scope input history per session (#1181)
Some checks are pending
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 (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 / Publish native release assets (push) Blocked by required conditions
* feat(web): preserve open side panel across session switches

* feat(web): scope composer input history to current session

* fix(web): suppress side panel open animation on session switch

* fix(web): preserve per-session scroll position on session switch

* chore: add changeset for per-session scroll position

* fix(web): preserve follow-bottom state per session

* fix(web): scope composer attachments to their session

* fix(web): restore saved scroll position on session switch
2026-06-29 01:52:08 +08:00
qer
fc3d69dbdc
feat(web): add completion sound and question notifications (#1179)
* feat(web): play a sound when a turn completes

Synthesize a short chime when a session finishes a turn. Opt-in via Settings -> Notifications (off by default); the audio context is unlocked on the first user gesture so it also plays while the tab is backgrounded.

* feat(web): notify and play a sound when a question needs an answer

Reuse the existing notification/sound toggles so they also fire when the agent asks a question (the awaiting-answer state). Generalize the Settings labels to cover both cases.

* fix(web): don't queue the chime on a suspended audio context

A suspended AudioContext has a frozen clock, so tones scheduled on it would play stale when the context later resumes (e.g. on the next click). Only schedule the chime when the context is actually running; if it is still suspended, try to unlock it for next time and skip this one.

* fix(web): gate question notifications behind explicit opt-in

Question notifications surface question text, so they must not fire for users who only opted into turn-completion alerts (which default on). Split question notifications into their own persisted preference that defaults off, with a separate Settings toggle. Completion notifications keep their existing default-on behavior.

* fix(web): show the question text in question notifications

Lead with the actionable question text in the desktop notification body, keeping the short header as context (e.g. 'Storage: Which database?'). Previously the header alone was shown, so users had to open the tab to learn what was being asked.
2026-06-28 21:02:00 +08:00
qer
c63edd5bf6
refactor(web): unify new-session creation behind the first message (#1167)
Some checks are pending
CI / test-windows (push) Waiting to run
CI / lint (push) Waiting to run
CI / build (push) Waiting to run
CI / test (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 / Release (push) Waiting to run
Release / Deploy docs (push) Blocked by required conditions
Release / Native release artifact (push) Blocked by required conditions
Release / Publish native release assets (push) Blocked by required conditions
Remove the NewSessionDialog path so every new-session entry in the web UI enters the onboarding composer, creating the session only when the first message is sent. This removes the last web flow that produced an empty session.
2026-06-28 17:01:26 +08:00
qer
dfcfdfd9dd
feat(web): hide empty sessions from the session list (#1166)
* chore(web): remove the /sessions slash command

* feat(web): hide empty sessions from the session list

Add an optional exclude_empty parameter to the session list API; the web client passes it so unused "New Session" entries are hidden by default, with pagination and has_more computed on the filtered set.

* fix(protocol): add exclude_empty to the session list query schema

Keep the shared protocol schema in sync with the server route so clients using the protocol type see the new parameter.

* fix(protocol): keep exclude_empty off the child session list schema

listSessionChildrenQuerySchema aliased the main list schema, so it inherited exclude_empty even though the /sessions/{id}/children route does not filter by it. Split it so generated clients are not misled.
2026-06-28 13:38:46 +08:00
Haozhe
cf558cd742
feat(managed-kimi-code): support Anthropic-compatible protocol (#1170)
* fix(agent-core): recover from context overflow 413

- track provider-observed effective context limit after overflow
- compact with the reduced limit before retrying the turn
- treat large plain 413 responses as recoverable context overflow
- add CLI patch changeset

* feat(managed-kimi-code): support Anthropic-compatible protocol

- switch managed provider to anthropic when models declare anthropic protocol
- add base64 video content blocks to the kosong anthropic provider
- downgrade unsupported media parts to text placeholders by capability
- pass prompt cache key as Anthropic metadata.user_id for session affinity

* feat(agent-core): add protocol/type to request and video upload telemetry

- turn_started now carries `type` (configured provider wire type) and
  `protocol` (effective transport, i.e. alias.protocol ?? provider.type)
- new video_upload event reports mime type, size, latency and
  success/failure, plus type/protocol/model context
- ResolvedRuntimeProvider gains `type` and `protocol` fields
2026-06-28 13:04:03 +08:00
qer
f3b15322da
fix(web): replace the web composer attach plus icon with an image icon (#1165)
Some checks are pending
CI / lint (push) Waiting to run
CI / build (push) Waiting to run
CI / test (push) Waiting to run
CI / test-windows (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 / Release (push) Waiting to run
Release / Deploy docs (push) Blocked by required conditions
Release / Native release artifact (push) Blocked by required conditions
Release / Publish native release assets (push) Blocked by required conditions
2026-06-28 02:18:11 +08:00
qer
ff6e8bbd7c
fix(web): clear composer draft synchronously on send (#1163)
When the first message of an empty session is submitted, the optimistic
user turn unmounts the empty-session composer before the post-flush text
watcher can persist the cleared draft. The docked composer then mounts
and reloads the stale text from localStorage.

Clear the persisted draft synchronously in the submit / steer / slash
command paths instead of relying on the text watcher, so the next mount
always starts empty.
2026-06-28 01:55:04 +08:00
qer
b0708464f4
feat(kimi-web): rework ask-user-question card as step-by-step wizard (#1162)
* chore(web): show five sessions per workspace

* feat(kimi-web): rework ask-user-question card as step-by-step wizard
2026-06-28 01:24:38 +08:00