Commit graph

718 commits

Author SHA1 Message Date
github-actions[bot]
efacf0452d
ci: release packages (#1946)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-07-20 22:57:20 +08:00
tt-a1i
c5b6103bb9
fix(acp): allow configured provider auth (#934)
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
Release / Release (push) Waiting to run
Release / Deploy docs (push) Blocked by required conditions
Release / Native release artifact (push) Blocked by required conditions
CI / lint (push) Waiting to run
CI / build (push) Waiting to run
CI / test (1) (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
2026-07-20 22:05:17 +08:00
Haozhe
ad8cc85251
feat(cli): foreground-only web servers, deprecated kimi server kill (#1967)
- the /web slash command now always starts a new server instead of offering to reuse a running one
- remove the kimi web kill and kimi web ps subcommands; foreground servers stop with Ctrl+C
- keep kimi server kill as a deprecated fallback that only stops servers started by a version before 0.28.0 (recorded in the legacy server lock)
2026-07-20 21:50:09 +08:00
Haozhe
f6f4192957
fix(agent-core-v2): broadcast permission mode switches to live subagents (#1948)
* fix(agent-core-v2): broadcast permission mode switches to live subagents

- add IAgentLifecycleService.broadcastPermissionMode to fan a mode out to
  every live agent in the session registry
- fan out setPermission RPC calls addressed to the main agent so
  already-running subagents observe the new mode
- route the legacy agent_config.permission_mode patch through the same
  broadcast instead of only touching the main agent

* style(agent-core-v2): remove non-header comments
2026-07-20 15:55:27 +08:00
Haozhe
5ae60fa673
feat(transcript): add unified transcript layer, drop the /api/v2 RPC surface (#1888)
* feat(transcript): add unified transcript layer and v1 surface

- add packages/transcript: agent-granular L1 store, idempotent L2 ops,
  off/turn/block/delta L3 granularity, L4 view registry, and turn-cursor
  pagination; sole owner of all transcript wire types
- kap-server: engine-event-driven TranscriptService with history backfill,
  GET /sessions/{id}/transcript, and transcript.ops WS deltas with
  per-connection granularity control
- kimi-inspect: render ChatView from the transcript surface (REST pages +
  delta-only WS) instead of context memory
- sync flake.nix workspace lists and document packages/transcript and
  packages/server-e2e in AGENTS.md

* fix(transcript): project live prompts and anchor backfilled items

- agent-core-v2: carry the extracted prompt text on turn.started so the
  transcript projector can render the user input at turn open (the context
  append with the same text is not a bus event and lands later)
- kap-server: keep the prompt through turn.ended; anchor backfilled
  markers/taskrefs to their following snapshot turn so replaying history
  after live turns arrived keeps the historical order
- transcript: add an optional beforeTurn placement anchor to
  marker.upsert / taskref.upsert; anchored inserts land before the first
  turn at or past the anchor instead of appending blindly

Addresses review feedback on #1888.

* fix(transcript): adopt backfilled stream frames and group goal turns

- kap-server: on mid-stream attach, adopt the backfill-seeded stream frame
  (id + offset) instead of opening an empty one whose upsert clobbered the
  seeded text and whose offset-0 appends could not land
- transcript: group a turn-opening system_trigger (goal continuation) into
  its own turn so cold rebuilds keep the turn boundary and stay
  ordinal-aligned with the engine's live turn numbering

Addresses review feedback on #1888.

* fix(transcript): drop stale live stores on close and heal after turns

- drop a session's live transcript store when the session closes or archives
  (lifecycle events plus a re-check on the cached-entry path) so reads fall
  back to the cold rebuild instead of serving a stale store
- re-read an ended turn from the persisted wire records (debounced per
  agent) and merge it back live-first: headers keep live state/timestamps
  while origin/prompt recover from disk, and truncated text/thinking frames
  from a mid-turn attach are restored only when the persisted text is
  longer — a fresh live frame or a lagging flush is never reverted

Addresses review feedback on #1888.

* fix(transcript): adopt seeded tool frames and route subagent questions

- kap-server: adopt the backfill-seeded tool frame when a tool.result
  arrives for a call that started before the projector attached, so the
  output lands instead of being dropped (the producer-store lookups now
  ride one projector options object)
- agent-core-v2: record the owning agent on question interactions
  (ISessionQuestionService.request gains an agentId option, passed by
  AskUserQuestionTool from its agent scope) so a subagent's questions
  route to its own transcript and WS events instead of 'main'

Addresses review feedback on #1888.

* fix(transcript): adopt parent frames, namespace live markers, redact resets

- kap-server: fall back to store adoption when subagent.spawned links a
  parent tool call that started before the projector attached, so the
  agentRefs link is not lost on mid-bind attaches
- kap-server: id live markers in their own namespace (live-mN) — the cold
  rebuild numbers its markers m1... too, and a colliding id made the
  store's upsert replace the historical marker instead of appending
- transcript/kap-server: redact transcript.reset snapshots to the
  subscriber's grade (below 'block' the step/frame detail is stripped), so
  a 'turn'-grade subscription no longer receives full content on resets

Addresses review feedback on #1888.

* fix(transcript): fold blocked turns into failed, drop inline v2 comment

- kap-server: map turn.ended reason 'blocked' to the 'failed' transcript
  state, matching the engine's TurnEndReason wire contract and the v1
  mapTurnReason folding (it was presented as a user cancellation)
- agent-core-v2: move the question agentId rationale into the ask-user
  file header — package rules keep comments in the top-of-file block only

Addresses review feedback on #1888.

* fix(transcript): keep roster descriptors and resolved-event agents

- kap-server: keep the metadata-seeded roster descriptor (parentAgentId /
  label) when an on-demand history backfill lands its roster entry, instead
  of downgrading it to { agentId, type }
- kap-server: remember each interaction's owning agent and stamp it on the
  resolved question/approval v1 events — they were hard-coded to 'main', so
  an agent-filtered subscriber saw a subagent's question open but never
  close

Addresses review feedback on #1888.

* fix(transcript): defer pending seeding, skip ghost roster entries

- kap-server: announce interactions pending at bind time only after the
  initial backfill (new TranscriptBinding.seedPendingInteractions), and
  adopt the seeded tool frame on the request/resolve paths, so a
  pre-existing approval lands next to its backfilled tool call and keeps
  the approvalId back-link
- kap-server: skip the roster entry when a probed agent id has neither a
  roster presence nor persisted content (agent_id=nope no longer conjures
  a ghost subagent)
- agent-core-v2: fold the turnEvents import rationale into the loop file
  header (package rule: comments live in the top-of-file block only)

Addresses review feedback on #1888.

* fix(transcript): reject hostile agent ids, honor the agent allowlist

- transcript/kap-server: validate agent ids as plain names (no separators,
  no dot segments) at the REST query layer and again before the id is
  joined into the wire-records path — an authenticated client could
  otherwise read a wire.jsonl outside the agents directory
- kap-server: compose the legacy v1 agent allowlist with transcript
  grades on every fan-out path (initial resets, per-ops fan-out,
  roster-driven resets), so a filtered connection no longer receives
  other agents' transcript frames

Addresses review feedback on #1888.

* fix(transcript): settle foreground shell tasks on shell.completed

- agent-core-v2: emit a transient shell.completed event when a foreground
  `!` command settles (detached runs keep reporting through the task
  lifecycle) — the generic task.terminated never fires for foreground
  tasks, so their transcript cards were stuck at 'running'
- kap-server: map shell.completed to the terminal transcript task state
  (completed/failed) and classify the event as volatile on both v1
  durability gates, like its shell.* siblings

Addresses review feedback on #1888.

* fix(transcript): replay early resolves, reset on a widened filter

- kap-server: register bind-time pending interactions without frames so a
  resolve arriving before the post-backfill seed still routes, then replay
  it at seed time — request and resolve land together with the approvalId
  back-link, instead of the interaction vanishing entirely
- kap-server: treat an agent newly admitted by a broadened legacy agent
  filter as owed a transcript.reset even when its grade transition is a
  no-op (delta → delta) — its ops were suppressed so far, so it has no
  baseline otherwise

Addresses review feedback on #1888.

* fix(transcript): keep materialized transcripts on agent disposal

- kap-server: only the projector dies with the agent scope — the
  materialized transcript and roster entry now survive disposal (the
  roster mirrors session metadata, which keeps completed agents).
  Dropping them lost already-served history for good: the backfill cache
  dedupes per agent, so the next read rebuilt an empty shell instead of
  replaying the persisted records

Addresses review feedback on #1888.

* fix(transcript): anchor refreshes by oldest turn, group slash turns

- kimi-inspect: re-cover the previously loaded window after a refresh by
  paging until the previous OLDEST turn is loaded again (extracted as
  recoverLoadedWindow) — a count-based stop silently dropped the window's
  head once new turns shifted the server window
- transcript: group user-slash skill/plugin activations into their own
  turn (marker included), mirroring the engine's isRealUserPrompt — their
  assistant output no longer folds into the previous turn on cold rebuilds;
  other triggers stay marker-only

Addresses review feedback on #1888.

* fix(transcript): compare all tool fields, overlay in-flight backfills

- transcript: include toolCallId/name/view/input in the tool frame
  equality check — an upsert correcting only those was dropped as a no-op,
  leaving stale tool metadata on clients
- kap-server: overlay the loop's active turn as 'running' after a backfill
  (cold grouping marks every rebuilt turn completed, so a live turn showed
  as finished until it ended); snapshot data supplies origin/prompt, and a
  projector-owned running header is never downgraded

Addresses review feedback on #1888.

* fix(transcript): gate ops before the seed, re-assert running headers

- kap-server: gate the transcript ops fan-out (and roster-driven resets) on
  a per-connection seeded flag set only after the baseline reset has
  landed — a subscriber joining mid-stream no longer receives deltas
  against an empty baseline
- kap-server: always re-assert the loop's active turn as 'running' after
  the snapshot ops in a backfill (skipping the overlay when a live running
  header existed let the snapshot's cold 'completed' header downgrade it);
  live header fields win over the snapshot's

Addresses review feedback on #1888.

* docs(agent-core-v2): fold turnEvents notes into the file header

Move the turn.started prompt rationale from field/function TSDoc into the
module header — package rules keep comments in the top-of-file block only.

Addresses review feedback on #1888.

* fix(transcript): emit shell failure output, dispose per-agent listeners

- agent-core-v2: emit the synthesized failure text as a final shell.output
  chunk before shell.completed (it was never streamed, so failed
  foreground commands showed empty output in transcript tasks until a
  full rebuild)
- kap-server: track each agent's bus subscription per agent and dispose it
  in onDidDispose — the listener captures the projector, so a disposed
  agent no longer keeps projecting late events into the store

Addresses review feedback on #1888.

* fix(transcript): route shell events by task id, tidy question docs

- agent-core-v2: keep the commandId → foreground-task-id mapping and carry
  taskId on shell.output / shell.completed, so consumers attaching
  mid-command (having missed shell.started) can still route output and the
  terminal state
- kap-server: fall back to the event's taskId in the shell output/completed
  projectors and seed the shell task before the first chunk, so output is
  preserved and the terminal upsert cannot clobber it with an empty tail
- agent-core-v2: fold the question agentId note into the question.ts file
  header (package rule: comments live in the top-of-file block only)

Addresses review feedback on #1888.

* fix(transcript): tighten agent id validation, match kinds in heals

- transcript: constrain agent ids to a filename-safe shape
  ([A-Za-z0-9._-], <=128 chars) — NUL-containing or overlong ids made the
  wire-records read throw unhandled errors (500) instead of failing
  validation
- kap-server: require the live frame's kind to match before the post-turn
  heal's length shortcut — a kind-mismatched frame (the projector guessed
  the stream kind wrong mid-turn) is now replaced by the persisted one
  instead of being skipped

Addresses review feedback on #1888.

* fix(transcript): heal missed tool results, seed pendings per agent

- kap-server: re-emit tool frames in the post-turn heal when the live step
  lacks the frame or the live frame lacks the outcome the persisted one
  carries (a tool.result dropped in the attach race is otherwise
  unrecoverable); live-only extras (display / agentRefs / approvalId) are
  preserved, and frames with a live outcome stay untouched
- kap-server: scope seedPendingInteractions by agent — the initial seed
  after backfillMain covers main-owned pendings, and each subagent's
  pendings seed after its own on-demand backfill, so placement and the
  approvalId back-link find the persisted tool frames

Addresses review feedback on #1888.

* fix(protocol): register shell.completed on the v1 event surface

- packages/protocol: add ShellCompletedEvent (plus optional taskId on
  shell.output / shell.completed and prompt on turn.started) to the event
  interfaces, zod schemas, the agent event union, and the volatile list —
  schema-validating consumers previously rejected the forwarded
  shell.completed frames outright
- kap-server: mirror the same fields in the v1 events-zod module

Addresses review feedback on #1888.

* test(node-sdk): cover shell.completed in the exhaustive event switch

The SDK's session-event type test asserts exhaustiveness with assertNever;
register the new event there (CI typecheck caught it).

Addresses review feedback on #1888.

* fix(transcript): source cold-session rosters from session metadata

- kap-server: add TranscriptService.readColdRoster (persisted state.json →
  descriptors, mapped like the live seeding) and use it for the cold
  transcript path — the requested agent id is only appended when it has
  content (or is main), so an empty probe (agent_id=nope) no longer
  fabricates a ghost roster entry, matching the live path

Addresses review feedback on #1888.

* fix(transcript): emit taskrefs when seeding missed shell commands

- kap-server: the mid-command-attach seeding in onShellOutput now emits
  the matching taskref.upsert (exactly like onShellStarted), and
  onShellCompleted emits one when the whole command was missed — the task
  no longer exists only in the global map with no timeline item to render

Addresses review feedback on #1888.

* fix(transcript): defer unseeded live pendings, keep cold tools running

- kap-server: pendings created before their owning agent's seed has run
  now defer into the same unseeded queue as bind-time ones (tracked per
  agent) — announcing them during the backfill window misplaced them into
  a synthetic step with no later repair
- transcript: cold grouping initializes tool frames as 'running' and lets
  the tool-message branch transition them to done/error — an approval-
  gated or still-executing tool no longer shows as completed on rebuilds

Addresses review feedback on #1888.

* fix(transcript): seed live-created agents, merge backfills live-first

- kap-server: agents created after binding are marked seeded immediately —
  their projector covers every event from creation on, so their pendings
  announce without waiting for an explicit history read (which previously
  left live subagent approvals/questions stuck in the unseeded queue)
- kap-server: the initial backfill merges turns live-first via
  healTurnOps (snapshotToOps gains a turn-mapper parameter) — live frame
  fields landed during the disk read (display/approvalId, longer text)
  are no longer replaced by the staler persisted version

Addresses review feedback on #1888.

* fix(transcript): count pages in turn segments, not head units

- transcript: the leading non-turn unit no longer consumes a turn slot —
  pages are counted in turn segments and the head unit rides only with the
  page reaching the first turn. A timeline with a head marker and exactly
  pageSize turns used to drop the marker from the newest page and
  hallucinate an older marker-only page (has_more: true with no older
  turns)

Addresses review feedback on #1888.

* fix(transcript): send baseline resets after cursor replay

- kap-server: broadcaster.subscribe gains deferTranscriptReset (recording
  prev grades/filter per target) plus flushTranscriptSeed; the v1
  connection defers the transcript baseline on cursor-carrying
  (re)subscribes and flushes it after replay — a reconnecting client no
  longer sees the reset's current seq ahead of the replayed lower-seq
  backlog

Addresses review feedback on #1888.

* fix(transcript): gate the ops fan-out only when a reset is coming

- kap-server: willSendTranscriptReset decides upfront whether any reset
  will be sent (grade upgrade or widened legacy filter); a same-grades
  resubscribe no longer un-seeds the target, so ops emitted mid-resubscribe
  keep flowing instead of being silently dropped by the fan-out gate

Addresses review feedback on #1888.

* fix(transcript): seed subscribers even when no reset is owed

- kap-server: a no-reset subscription (e.g. a client subscribing to a
  fresh session with an empty roster) now still marks the target seeded
  after subscribeTranscript completes — roster resets and ops would
  otherwise stay gated forever once agents appear

Addresses review feedback on #1888.

* fix(transcript): guard mismatched appends, expose prompt via klient

- transcript: appendAtOffset now treats an overlapping chunk whose head
  does not match the local tail as a gap (diverged stream) instead of
  silently rewriting from the offset and dropping local content
- klient: add the optional prompt field to the turn.started event schema
  so SDK listeners receive it instead of zod stripping it

Addresses review feedback on #1888.

* fix(transcript): open turns for subagent run prompts in cold grouping

- transcript: add the subagent system trigger to the turn-opening set —
  a subagent's run prompt (persisted as system_trigger/'subagent') always
  launches a new engine turn, so resumed subagent histories no longer fold
  the response into the previous turn or lose the prompt

Addresses review feedback on #1888.

* fix(transcript): guard bus subscriptions independently of projectors

- kap-server: subscribeAgent now guards on a dedicated subscribedAgents
  set instead of projector existence — a projector seeded before its
  agent's handle exists (e.g. during an on-demand backfill) no longer
  blocks the bus subscription, so the agent's live events keep flowing

Addresses review feedback on #1888.

* fix(kimi-inspect): reconcile the transcript on every socket open

- apps/kimi-inspect: TranscriptWs now reports onReconnected on the FIRST
  successful open too, not only on re-established ones — ops emitted
  between the REST page load and the subscription (a delayed or failed
  first connection) were previously lost onto a stale store; the consumer's
  refresh guard drops the no-op call while the initial load is in flight

Addresses review feedback on #1888.

* fix(transcript): derive the active step, dedupe tool error rendering

- kap-server: the projector gains a stepOrdinal lookup backed by the
  engine's activity view (resolved lazily through the agent lifecycle), so
  deltas after a late attach at step >= 2 land in the real active step
  instead of a synthesized t<N>.1
- apps/kimi-inspect: render a tool frame's error only when it differs from
  its output — onToolResult sets both to the same string for failed tools,
  which drew the failure twice in red

Addresses review feedback on #1888.

* fix(kimi-inspect): reconcile the transcript on the subscribe ack

- apps/kimi-inspect: TranscriptWs now fires onReconnected when the
  subscribe ack for its client_hello arrives instead of at socket open —
  the server attaches the transcript stream only after processing
  client_hello, so a refresh fired at open could finish before the
  subscription was active and still miss the ops in between

Addresses review feedback on #1888.

* fix(kimi-inspect): coalesce concurrent transcript refreshes instead of dropping

A subscribe ack landing while the initial REST load was still in flight
hit the `if (refreshing) return` guard, so ops emitted between the REST
page snapshot and the WS subscribe were neither in the page nor
delivered over the socket. Replace the drop guard with a coalesced
runner: at most one refresh in flight, and triggers during a run are
collapsed into exactly one follow-up run after it settles.

* fix(kap-server): force the transcript baseline after cursor-based replay

A cursor re-subscribe at unchanged grades deferred its baseline and then
compared against the previous grades on flush, so no reset was sent —
while volatile ops fanned out during the deferral had been dropped,
leaving the client with a permanent gap. flushTranscriptSeed now always
seeds a full baseline (previous grades no longer tracked in the deferred
record), and a regression test covers the same-grade cursor resubscribe.

Also drop the inline comments added to shellCommandService.ts — the
agent-core-v2 convention keeps commentary in the top-of-file block; the
context moved there.

* fix(kap-server): harden transcript seeding against stale and wildcard subs

Two subscribeTranscript gaps found in review:

- Re-read the target's subscription after the history awaits: subscribe
  work runs asynchronously, so an overlapping downgrade/unsubscribe used
  to be answered with resets computed from the stale spec. The reset
  loop now uses the latest grades/filter from state.targets and bails
  when the target is gone or no longer graded.
- Backfill roster agents admitted via the wildcard grade, not just
  explicitly named ones: a historical subagent seeded into the roster
  from session metadata had no materialized AgentTranscript, so
  wildcard subscribers silently never received its baseline reset.

Adds regression tests for the wildcard backfill, the mid-seed
downgrade, and the mid-seed unsubscribe (all three fail without the
fix); makeCore now accepts persisted agent metadata for roster seeding.

* fix(transcript): let meta.merge clear mode badges on mode exit

`agent.status.updated` with `planMode: false` / `swarmMode: false` was
dropped by the transcript projector because `meta.merge` could only set
mode badges, never clear them — clients kept rendering an exited mode
until the next full reset. The merge wire shape now accepts `null` per
mode key (set = object, clear = null, absent = keep): the reducer
deletes the key and normalizes an empty `modes` away, the zod schema
validates the nullable form, and the projector emits the clearing op
for exit events.

* fix(agent-core-v2): keep system-turn steering text out of turn prompts

`turn.started.prompt` was populated from the turn input for every
origin, so system-triggered turns (goal continuation, subagent run,
cron) exposed their internal steering text to live transcript
consumers; the cold rebuild mirrored the same leak when grouping
persisted history. The loop now populates the prompt only for
displayable user origins (user input, or a user-slash skill/plugin
activation) via the new isDisplayablePromptOrigin gate, and the cold
grouping opens hidden-origin turns promptless. Turns still open
normally — only the prompt text is withheld.

* fix(kap-server): reattach the transcript fan-out after a session reload

When the engine session closed or archived, TranscriptService dropped
the live store together with its ops listener set, but the
broadcaster's SessionState kept its transcriptStream — so
ensureTranscriptStream returned early for a later subscribe on the
resumed session, delivering a fresh reset but never the live
transcript.ops. The stream is now pinned to its TranscriptStore
instance and the fan-out re-registers whenever a rebuilt store shows
up. Adds a regression test that drops the service entry mid-stream and
asserts ops keep flowing after resubscribe (fails without the fix).

* fix(transcript): map legacy background_task origins in cold rebuilds

Legacy/v1 sessions persist background-task notifications with
origin.kind === 'background_task' (the live mapper already handles that
spelling), but the cold grouping only mapped 'task' — after a restart
those turns fell through to { kind: 'other' } and lost their taskId, so
the transcript could no longer associate the notification turn with its
background task. Both spellings now share the task-origin branch.

* fix(kap-server): project no-taskId shell failures into the transcript

A foreground `!` command that failed before onForegroundTaskStart ran
(Bash validation/spawn/registerTask errors) published shell.output /
shell.completed with taskId undefined, and the projector's guard
dropped them — the live transcript lost the stderr and the terminal
state of a command that did run. Shell events now resolve their task as
the id learned at shell.started, else the event's own taskId, else a
synthetic per-command id (shell-<commandId>), so early failures land
like any other shell task.

* refactor: drop the /api/v2 RPC surface and the klient http transport

- kap-server: remove the /api/v2 REST routes and /api/v2/ws socket (registerRpcRoutes renamed to serviceDispatcherRoutes; transport/ws/{eventMap,registerWs,wsClient,wsConnection,wsProtocol} deleted). /api/v1/debug/* is now the only RPC surface — a reflection dispatcher over the entire scoped DI registry with no whitelist — and /api/v1/ws the only WS endpoint
- klient: drop the http transport (transports/http/*, transports/ws/wsSocket.ts) and the kap-server devDependency; transports reduce to the ipc|memory subpath entries, and the dual/v2 e2e suites go with them
- kimi-inspect: target /api/v1/debug only with no fallback, replace the Service-event push channel (wsChannel/wsSocket) with on-demand fetch plus 15 s polling, and show a blocking "Debug surface unavailable" screen on connection failure
- transcript: add global attachment/interaction/todo entities (model, ops, wire schema) and project them from engine events in kap-server's coreEventMap

* fix(kimi-inspect): drop the unused TranscriptTodo import
2026-07-20 15:33:05 +08:00
github-actions[bot]
a05228c671
ci: release packages (#1868)
Some checks are pending
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
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
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 / Release (push) Waiting to run
Release / Deploy docs (push) Blocked by required conditions
Release / Native release artifact (push) Blocked by required conditions
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-07-20 13:55:49 +08:00
qer
d71bf9e5a5
feat(web): add a cache invalidation note to the model switcher (#1940)
* feat(web): add a cache invalidation note to the model switcher

* feat(web): show the cache note in the mobile settings sheet
2026-07-20 13:43:05 +08:00
liruifengv
11c1683a1c
feat: scope thinking effort to the current session (#1933)
* feat: scope thinking effort to the current session

Selecting a model or thinking mode in the TUI or web UI no longer
persists the concrete effort to the config; only the boolean thinking
toggle is saved. The web UI now restores and submits each session's own
daemon-reported level instead of a browser-wide per-model pick, whose
localStorage store is removed.

* feat: persist bounded thinking efforts and migrate persisted max once

Only low/medium/high/xhigh are written to the config on a pick; max and
unrecognized levels stay session-only with just the boolean toggle saved.
A one-shot migration rewrites a previously persisted thinking.effort max
to high (recorded in migrations.json, never re-run). The web UI brings
back the per-model localStorage pick as the seed for new sessions, while
a session's own daemon-reported level keeps winning for existing ones.

* feat: gate effort persistence on the model top declared level

The session-only tier is now the last entry of the model's own
support_efforts (ordered by strength) instead of a fixed name list, so
custom provider-declared levels get the same treatment; anything below
the top persists as before, and unknown model metadata keeps the
concrete effort.

* docs: align changeset wording with the top-tier persistence rule

* refactor: rename the config migration marker file to migrations-effort.json

* feat: drop the legacy global thinking pick fallback in the web UI

A raw single-level string left in localStorage by older versions was
migrated into a '*' fallback entry applied to every model; stale values
(typically max) could silently steer any session. Non-map content is now
discarded instead of inherited.

* feat: drop the web per-model thinking pick store entirely

Thinking in the web UI is now sourced only from the daemon: a session's
own reported level wins when the model declares it, and everything else
falls back to the model's catalog default. The stale localStorage key is
cleaned up on startup.

* feat: skip the effort write when the picker confirms its initial value

Re-confirming the effort shown when the model/effort picker opened is not
an explicit choice: the TUI persists only the model (no effort key, or no
write at all when nothing changed), and the web skips the global config
write the same way it already did for model switches.

* revert: keep the web global thinking write on re-confirm for now

Only the TUI skips the effort write when the picker confirms its initial
value; the web behavior stays unchanged until the interaction is
reconsidered.

* fix: carry the draft thinking pick into a newly created session

A level picked on the empty composer lived only in rawState.thinking, so
selectSession's watcher overwrote it with the catalog default and the
first prompt/skill submitted that instead of the pick. createDraftSession
now captures the draft level and seeds the new session's own entry.

* docs: simplify the changeset wording

* fix: close the remaining top-tier persistence and draft-capture gaps

The provider-add default-model flow now resolves support_efforts through
effectiveModelForHost (overrides + protocol-profile inference), so a
top-tier pick on catalog models without declared efforts no longer
persists. The draft thinking level is captured before the session
creation awaits, so a concurrent session switch can no longer seed the
new session with another session's effort.

* fix: wait for the session status fold before resolving prompt thinking

In the cold window right after a reload or session switch, the session's
own level has not landed from /status yet; resolving straight to the
catalog default would carry the wrong level to the daemon, which writes
it into the session profile. Send, steer, side-chat and skill-activation
paths now await the fold when the session entry is missing.
2026-07-20 12:57:22 +08:00
7Sageer
a3e773f90c
fix(agent-core-v2): make host fs stat follow symlinks and add explicit lstat (#1843)
* fix(agent-core-v2): make host fs stat follow symlinks and add explicit lstat

* chore: simplify changeset wording

* fix(agent-core-v2): preserve dangling git root markers
2026-07-19 16:18:02 +08:00
Haozhe
a41a09c33c
feat(cli): replace the kimi server command tree with kimi web and share one home across servers (#1826)
Some checks are pending
CI / test-windows (push) Waiting to run
CI / lint (push) Waiting to run
CI / typecheck (push) Waiting to run
CI / test (4) (push) Waiting to run
CI / test (5) (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-pi-tui (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(kap-server): enable multi-server shared home by default

- always register kap-server instances under server/instances and drop the
  legacy single-instance lock (acquireLock/getLiveLock/ServerLockedError)
- remove the multi_server experimental flag and its
  KIMI_CODE_EXPERIMENTAL_MULTI_SERVER env var from agent-core-v2
- discover running servers via the instance registry in server
  ps/kill/rotate-token, kimi web daemon reuse, and the desktop app
- remove the pending minidb changesets

* feat(cli): add per-instance targeting to server kill and ps

- `kimi server kill [serverId]` stops only the matching instance; without
  an id it still stops the longest-running one, and an unknown id errors
  with the live server ids listed
- `kimi server ps` lists connections grouped per server id (`--json`
  nests them under a per-server object); an unreachable instance degrades
  to a per-server note instead of failing the whole listing
- update the zh/en command reference and the multi-server changeset

* feat(cli): replace kimi server with the kimi web command tree

- `kimi web` now runs the local server in the foreground and opens the
  browser; the background daemon (ensureDaemon / spawn / idle-exit) is
  removed, so repeated runs simply start another instance on the next
  free port
- drop the OS-service lifecycle (install/uninstall/start/stop/restart/
  status) together with kap-server's svc layer
- `kimi web kill [serverId]`, `kimi web ps`, and `kimi web rotate-token`
  manage instances from the registry
- the TUI /web command now connects to an already-running instance
  instead of spawning a background daemon
- update the zh/en command reference, dev scripts, and tests

* feat(cli): route kimi server invocations to a deprecation notice

Any `kimi server …` call — bare or with any legacy subcommand/flags —
now prints a deprecation notice pointing at `kimi web` and exits 1,
instead of failing with an opaque "unknown command". The shim is
scheduled for removal in the next major version.

* feat(cli): add the `all` keyword to kimi web kill

`kimi web kill all` stops every live instance in the registry (ULIDs
can never collide with the keyword). Each instance still gets the API
shutdown + SIGTERM/SIGKILL treatment; a failure on one instance does
not stop the sweep and is reported at the end.

* docs(changeset): drop the web-foreground-default changeset

The kimi web command tree replaces the foreground-default behavior this
entry describes: --background, daemon reuse, and the version-mismatch hint
no longer exist, so the pending entry would contradict the actual release
notes.

* docs(changeset): tighten the multi-server entry wording

* feat(cli): let /web pick a running server or start a new one

The /web picker now lists the live instances from the registry with
their versions (flagging a CLI mismatch) instead of only connecting to
the longest-running one, and offers starting a new server: that one
runs in the foreground attached to the terminal after the TUI exits,
via the restored exit-takeover wiring. formatReadyBanner is exported
and adapts its Stop hint to Ctrl+C for the attached case.

* feat(cli): skip the /web picker and start a new server when none is running
2026-07-19 11:43:46 +08:00
qer
4f3c7240c4
feat(cli): run kimi web and the TUI /web command in the foreground by default (#1853)
Some checks are pending
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
CI / test-pi-tui (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
* feat(cli): run kimi web and the TUI /web command in the foreground by default

kimi web and /web now start the server attached to the terminal (Ctrl+C
stops it) instead of backgrounding a daemon; --background opts back into
the daemon behavior. kimi server run is unchanged. A foreground kimi web
reuses an already-running server instead of failing to bind, the /web
handoff prints the same ready banner as kimi web (plus the session deep
link), and the banner Stop hint adapts to the hosting mode.

* fix(cli): resolve the server token after the server is up in /web

The token lookup had moved ahead of ensureDaemon()/startServerForeground()
during the foreground-default refactor, so a first-time start (no
server.token on disk yet) opened the browser without the #token= fragment
and left the user at the auth gate. Resolve the token after the daemon is
healthy, and inside the foreground ready hook.

* docs(changeset): flag the web foreground default as breaking in the changelog

The bump stays minor, but the entry now calls out the behavior break and
the --background mitigation for scripts that expect kimi web to return
immediately.

* feat(cli): flag a version mismatch when reusing a server from an older CLI

After an upgrade, an older still-running server is reused as-is. The
lock's host_version is now surfaced: kimi web / server run print a
version-mismatch line next to the reuse notice, /web shows the same as a
status warning, and the docs plus changeset tell users a single
kimi server kill switches them onto the new version.
2026-07-18 23:32:41 +08:00
Kai
3086e47039
fix: unify YOLO and Auto permission mode descriptions across surfaces (#1867)
Some checks are pending
CI / test-windows (push) Waiting to run
CI / lint (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
CI / test-pi-tui (push) Waiting to run
CI / build (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
* fix(tui): correct YOLO and Auto permission mode descriptions

* fix: unify YOLO and Auto permission mode descriptions across CLI, ACP, web, and docs

* docs: correct YOLO and Auto mode descriptions in the interaction guide

* fix: correct YOLO mode notices in session replay and vscode extension

* feat(vscode): rename /afk command to /auto, keeping afk as hidden alias

Also correct the stale 'afk' mode reference in the built-in MCP config
skill guidance of both agent engines.

* fix(vscode): forward engine approval requests instead of blanket-approving them

The extension-level approval handler auto-approved every request when
legacy yolo/afk was on, silently swallowing the sensitive-file,
plan-review, and ask-rule prompts the engine yolo mode still sends.
Forward every request to the user and let the engine permission mode do
the auto-approving, matching TUI and web behavior.
2026-07-18 02:09:31 +08:00
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
56ba8e0196
fix(server): allow inline styles in CSP so web math renders on non-loopback binds (#1847)
* fix(server): allow inline styles in CSP so web math renders on non-loopback binds

The security CSP fell back to default-src 'self' for styles, which strips
the inline style attributes that KaTeX (injected via innerHTML) uses for
all glyph positioning — formulas collapsed into overlapping characters on
any non-loopback-served web UI. Shiki highlighting and Mermaid diagrams
hit the same mechanism. Add style-src 'self' 'unsafe-inline'; scripts
remain strictly restricted.

* test(server): assert the effective script-src in the CSP regression test

A negative substring check only rejects one exact string: default-src
gaining 'unsafe-inline' (inline <script> allowed via fallback) or an
explicit script-src 'unsafe-inline' would both slip through. Parse the
directives and assert the effective script policy (script-src, falling
back to default-src) excludes 'unsafe-inline'/'unsafe-eval'/data:.
2026-07-17 20:21:10 +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
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
7Sageer
fa7e4ba421
fix(agent-core-v2): follow symlinks when loading AGENTS.md (#1840) 2026-07-17 18:55:46 +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
44f3341919
feat(minidb): ClusterDb sharding, incremental reader catch-up, and engine hardening (#1816)
* feat(minidb): add ClusterDb sharding and harden engine under stress

Cluster layer:
- add ClusterDb: hash-routed keys over N MiniDb shard directories with a
  per-shard lock pool (lease renewal, lockHoldMs yield, takeover on dead PID),
  merged ordered scans, cross-shard index registry, live cross-process read
  visibility, and crash-recovery handoff
- add cluster bench suite and multi-process test suites

Engine hardening (stress-driven fixes):
- compaction: pre-copy now gives up when the tail copy is not converging and
  rotation seals the old WAL (retryable), so compaction always terminates
  under sustained write storms and no committed write slips through rotation
- recovery: re-sync WAL size bookkeeping after torn-tail truncation; read-only
  opens create/modify no files and never compact under a live writer
- lockfile: stale-lock takeover via atomic bid-rename + settle; release only
  unlinks its own pid
- query: streaming candidates with skip/limit applied before materialization,
  plus Store.rawKeys for value-free key scans
- eviction: O(1) LRU victim picking via insertion-ordered access set
- TTL: adaptive expire budget drains simultaneous-expiry storms in seconds
- store: O(1) size fast path when no TTL is set; has() no longer materializes
  disk-backed values
- openOrRebuild preserves data when only a sidecar definition file is corrupt;
  sidecar definitions written atomically; stale compaction temps cleaned on open
- RESP server serializes replies per connection; over-64KiB tokens can no
  longer poison the full-text index

* feat(minidb): incremental WAL catch-up for cluster readers

- cluster readers: track a per-shard WAL watermark (dev/ino/offset) and
  catch up from appended frames instead of fully reopening; fall back to a
  full reopen on rotation, truncation, or index-definition changes
- MiniDb.catchUpFromWal applies WAL tail frames to a live instance (store
  plus secondary/dt/compound/text indexes), sharing recover()'s frame
  interpretation; RecoveryInfo exposes walScanEnd/dev/ino as the safe anchor
- lock-pool: incremental refresh with stats (incrementalCatchups,
  catchupFramesApplied); bench/reader-catchup shows p50 read latency drop
  from 130ms to 0.4ms at 10k keys and from 532ms to 0.4ms at 50k keys
  while a neighbor process writes
- compaction: keep the db writable on rotation failure (fresh WAL swap,
  remap once); count stats.compactions only on full success including the
  onCompacted hook
- restoreKey: seq guard so a failed op never wipes a concurrently
  committed value; eviction DELs retry on WAL seal
- text index: atomic build (stage then swap), createTextIndex registers
  only after a successful build, open-time cleanup of db.text-*.postings.tmp
- RESP server: swallow per-connection socket errors, reset the parser
  buffer after an oversized request, isolate per-command errors while
  preserving reply order

* chore(minidb): add changesets for reader catch-up and review hardening

* fix(minidb): support Windows in WAL rotation and lock takeover

- compaction rotation: on Windows, renaming over an open destination is
  EPERM, so replace renames with a retrying renameReplace helper and let
  go of the db's own ValueReader handles for the renames (reopened right
  after the pointer remap)
- value reader: hold snapshot/WAL handles only in valueMode 'disk'; in
  memory mode the handles were idle and, on Windows, blocked rotation
- lock takeover: retry the takeover bid's rename on Windows, but re-check
  the corpse before every attempt — a blind retry loop could land our bid
  late and overwrite an already-verified winner, double-holding the lock
- lock takeover: raise the co-bidder settle window to 50ms so loaded CI
  machines with tens-of-milliseconds descheduling still elect one winner
- test hardening for shared CI runners: a 30s minidb-wide timeout floor,
  batched prefills instead of sequential setup loops, explicit timeouts
  for process-spawning and heavy e2e tests, and Vitest 4 test() signature
  normalization

Verified green across macOS (arm64), Windows Server 2022 (x64, 2-core),
and Ubuntu 22.04 (launchpad aarch64): 321 passed, 1 skipped in each.

* fix(minidb): make stale-lock takeover exactly-one under CI load

- takeover now registers a liveness watch file before touching the lock,
  so every contender is visible to every other for the whole attempt;
  the settle/verify loop abstains while any live foreign watch exists.
  Settle-only heuristics could not survive a bidder descheduled before
  its bid write on shard-parallel CI runners (observed double-holds on
  ubuntu-latest and on a 2-core Windows box).
- adaptive settle scales with the attempt's own wall clock (floored,
  capped), replacing the fixed window.
- keep the Windows EPERM tolerance in the bid rename, re-inspecting the
  corpse before every attempt on ALL platforms, not just win32.
- lint: fix restrict-template-expressions in an e2e RESP helper.
- test: widen the cluster wait-read budget for slow CI spawns.

* fix(minidb): clean remaining CI lint and consumer-test failures

- wrap the compaction-storm error interpolation as String() so the
  type-aware restrict-template-expressions lint passes
- agent-core-v2's minidb query-store corruption test now expects the
  intended semantics: a corrupt index-definition sidecar is dropped while
  the data survives, and the definition can be re-registered

* fix(minidb): harden ClusterDb index administration across processes

Address three multi-process administration races in the cluster layer:

- findRange now merges candidates from all shards first and only then
  applies reverse/offset/count globally, instead of clipping per shard
  and discarding reverse
- cluster.indexes.json mutations go through a compare-and-swap loop
  (reload, re-apply idempotently, publish, verify) with an in-process
  mutex, so concurrent create/drop from two processes loses neither
  registry entries nor shard sidecars
- a failed createIndex/createTextIndex fan-out now rolls back exactly the
  shards it already created on, so the registry and every shard agree
  whether an index exists

Also make LockFile sidecars (tmp/bid/watch) unique per acquire attempt:
two lock users in the same process (independent shard pools) must never
share a path, or one user's cleanup would delete the other's in-flight
file.
2026-07-17 15:57:10 +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
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
b53e00db91
fix(oauth): include the transport root cause in connection error messages (#1808)
* fix(oauth): include the transport root cause in connection error messages

* chore: add changeset for oauth connection error diagnostics
2026-07-17 14:45:16 +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
31449728b7
fix(security): close FetchURL SSRF bypasses and DNS-rebinding window (#1791)
Some checks are pending
Nix Build / nix build .#kimi-code (push) Blocked by required conditions
Release / Desktop 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
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 / VSIX package audit (darwin-arm64) (push) Waiting to run
CI / VSIX package audit (all) (push) Waiting to run
CI / VSIX package audit (win32-x64) (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
CI / typecheck (push) Waiting to run
Nix Build / Check flake.nix workspace sync (push) Waiting to run
Release / Native release artifact (push) Blocked by required conditions
* fix(security): close FetchURL SSRF bypasses and DNS-rebinding window

- resolve hostnames via DNS and reject any target resolving to loopback /
  RFC1918 / link-local / CGNAT / ULA ranges, including IPv4-mapped IPv6
  forms (e.g. localtest.me, [::ffff:7f00:1]) — the static host denylist
  only matched literals and could be bypassed by crafted domains
- follow redirects manually with full per-hop revalidation (10-hop cap)
  instead of auto-following, so a public URL cannot 302 the fetcher at
  internal services or cloud metadata endpoints
- pin each connection to the DNS answers the check validated (per-hop
  undici Agent with a pinned lookup), closing the TOCTOU / DNS-rebinding
  window between the check and the connect; skipped when a proxy is
  configured or allowPrivateAddresses is set
- apply to both agent-core and agent-core-v2 providers, with SSRF /
  redirect / pinning test coverage

* chore: add changeset for FetchURL SSRF hardening

* test: bridge undici type declarations in fetch pinning tests

* fix(security): keep dispatcher option lib-agnostic for DOM typecheck consumers

* fix(security): address review — pin NO_PROXY bypasses, drain oversized bodies, header-only comments
2026-07-17 11:21:05 +08:00
Kai
373abb02f0
fix(agent-core): record and close tool calls interrupted mid-stream (#1790)
Some checks are pending
CI / build (push) Waiting to run
CI / VSIX package audit (darwin-arm64) (push) Waiting to run
CI / VSIX package audit (all) (push) Waiting to run
CI / typecheck (push) Waiting to run
CI / VSIX package audit (win32-x64) (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
CI / test-pi-tui (push) Waiting to run
CI / test-windows (push) Waiting to run
CI / lint (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
When the provider stream breaks off mid-tool-call (pause_turn, engine
overload, token limit), the step ended with the partial tool call dropped
silently: never executed, never recorded. If the response carried no other
usable content, the persisted assistant message was effectively empty and
every subsequent request — including compaction — was rejected with
"assistant must not be empty" (HTTP 400), wedging the session.

Record each unexecuted call instead (arguments sanitized to {} when the
truncated JSON is unparseable) and immediately close it with a synthetic
interrupted error result. The exchange stays wire-valid, the history stays
truthful, and the model learns the calls never ran so it can re-issue them.
2026-07-16 21:12:00 +08:00
Haozhe
319001ae5c
refactor: remove git detection from workspace wire and folder browse (#1787) 2026-07-16 21:05:19 +08:00
Kai
365ba0001d
fix: use timestamped default filename for session debug exports (#1788)
Some checks are pending
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
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 / VSIX package audit (darwin-arm64) (push) Waiting to run
CI / VSIX package audit (all) (push) Waiting to run
CI / VSIX package audit (win32-x64) (push) Waiting to run
CI / typecheck (push) Waiting to run
Nix Build / nix build .#kimi-code (push) Blocked by required conditions
Nix Build / Check flake.nix workspace sync (push) Waiting to run
Release / Native release artifact (push) Blocked by required conditions
Release / Desktop 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
Both engine implementations defaulted the export ZIP path to
<sessionId>.zip, so running /export-debug-zip or kimi export twice for
the same session silently overwrote the first archive. The default
filename is now kimi-debug-<shortId>-<timestamp>.zip (UTC, second
precision), matching the /export-md naming convention. Explicit
-o/outputPath behavior is unchanged.
2026-07-16 19:32:06 +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
Haozhe
d465591eb3
fix(agent-core-v2): skip no-op agent registration on session resume (#1784)
- SessionMetadata.registerAgent now compares the incoming metadata with
  the persisted entry (tolerating persisted-JSON artifacts: dropped
  undefined keys, null parentAgentId, label order) and skips the write
  entirely when nothing changed
- previously the first resume per server process rewrote state.json via
  the status rollup's ensureMainAgent -> doCreate path, bumping
  updatedAt and pushing the session to the top of the list
2026-07-16 18:50:12 +08:00
Haozhe
072eed476b
fix(agent-core-v2): keep context size readings on the measured path (#1782)
* fix(agent-core-v2): keep context size readings on the measured path

The step fold creates the assistant message in the context before the
exchange finishes (a skeleton at step.begin, filled by content.part folds
during streaming), and the input array llmRequester passes to
contextSize.measured() is that same live array — it already includes the
output. Taking input.length + output.length therefore counted the folded
output twice, storing a measured prefix length one past the live context.
The inflated length permanently failed get()'s measured fast path, so
reads silently fell back to per-message estimates (e.g. ~50 tokens shown
for a ~29k-token "hi").

Take the live context length as the measured prefix instead (input and
context are identical under the identity guard), and clamp the measured
prefix to the context length in get() so a bad record can never knock
reads off the measured path again.

Add contextSize tests driving real turns that assert the wire model,
get(), and rpc getContext against the exchange totals.

* feat(klient): add context-usage example tracing a fresh session

Polls agent.getContext()/agent.getUsage() and streams agent events for a
new session after one "hi" against a real server, printing a timeline of
when the context/token readings move, plus a final consistency check
comparing the measured tokenCount to cumulative usage. Model seeding is
optional via KIMI_EXAMPLE_* env; the server token resolves from
<kimi-home>/server.token like the v2 e2e helpers.

* chore: add changeset for the context size fix
2026-07-16 18:37:28 +08:00
_Kerman
09e855401b
fix: report task stop reasons to the model (#1781)
* fix: report task stop reasons to the model

* chore: add task stop reason changeset
2026-07-16 18:32:06 +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
Haozhe
ffaf0b98ca
feat(agent-core): align coder subagent tools with v2 and drain background tasks (#1776)
* feat(agent-core): align coder subagent tools with v2 and drain background tasks

- align the bundled coder subagent profile tools with agent-core-v2
  CODER_TOOLS (Skill, Agent, AgentSwarm, Task* trio, plan-mode tools,
  TodoList); cron tools stay declared-but-undelivered on sub agents,
  matching v2
- rebuild builtin tools in setActiveTools: profile-gated capabilities
  (Bash/Agent allowBackground via the Task* trio) were baked at
  construction with an empty enabled set, so profiles applied later
  (every subagent) silently lost them
- hold subagent completion until the child agent's background tasks
  settle (print-mode drain semantics) and suppress their terminal
  notifications, so no unobserved follow-up turn runs on a finished
  subagent; the run's timeout/cancel signal bounds the drain
- cover with real-Session e2e (tool execution, nested Agent/AgentSwarm,
  drain blocking and cancel) and update profile/subagent-host tests

* chore: add changeset for coder subagent tool alignment

* docs(agents): sync sub-agent capabilities with expanded coder tool set

- coder sub-agents can now dispatch nested sub-agents and use background
  tasks, todo lists, Plan mode, and skills; drop the stale claim that
  sub-agents cannot schedule nested sub-agents
- note that a sub-agent run reports completion only after its background
  tasks settle

* fix(agent-core): close drain race for tasks settled before completion

A background task that terminated before the drain's suppression pass was
excluded from the active-only list, so its terminal notification escaped
suppression and could still steer an orphan turn onto the finished
subagent. Suppress every child task (including settled ones whose
notification may still be in flight) and run the pass both before and
after the settle wait; notification delivery re-checks suppression after
its async output snapshot, which makes the block deterministic. Cover the
mid-turn delivery path with an e2e case.
2026-07-16 17:14:53 +08:00
Kai
3d5d630c12
fix: honor explicit thinking off on OpenAI-compatible providers (#1774)
An explicit withThinking('off') collapsed to the same internal state as
"never configured" on chat-completions providers, so the history-based
auto reasoning_effort injection (#1616) silently switched reasoning back
on and could leak the field to models that reject it. Store the requested
effort verbatim and derive the wire encoding per request, suppress the
auto-enable for an explicit 'off', and report the accurate current effort
('on'/'off') instead of recording 'off' for both.
2026-07-16 17:11:18 +08:00
Kai
1169a6d5fd
fix: replay empty thinking verbatim on preserved-thinking endpoints (#1773)
Remove the wire-level fallback that substituted a space for empty
thinking content when replaying assistant history to Anthropic-compatible
and Kimi endpoints with thinking keep=all. The strict endpoints that
motivated the placeholder no longer reject empty thinking, so the
placeholder only distorted replayed history.
2026-07-16 17:00:06 +08:00
Haozhe
ba49e3a2f3
chore: drop changesets merged with the klient facade (#1768) (#1775)
The klient facade PR landed with its release notes planned for a later
feature release; removing the two pending changesets so the next release
does not pick up version bumps and changelog entries for it.
2026-07-16 16:49:44 +08:00
Haozhe
9e3e6700f9
fix(agent-core-v2): settle killed subagent runs only after the child loop goes idle (#1759)
- awaitRun: cancel by turn id so aborts also reach turns still queued, not just the active turn
- awaitTurn: cancel first, then wait for turn.result instead of racing against the abort signal, so task settlement, the task.killed notification, and the resume guard never observe the run as finished while the loop is still unwinding
- rethrow the original abort reason so consumers keep matching it by identity
- add regression test for the manual stop -> auto-resume "already running" race
2026-07-16 16:47:56 +08:00
Haozhe
4cffd732c2
feat(klient): contract-driven facade with http/ipc/memory transports (#1768)
* feat(klient): contract-driven facade with http/ipc/memory transports

- add zod-validated contract sections (global/session/agent) under
  src/contract and a facade exposing global.*, session(id).*, agent(id).*
- select transport once at creation via subpath entries
  (@moonshot-ai/klient/http|ipc|memory); drop legacy channel/client/
  httpChannel/wsChannel/wsKlient/proxy implementations
- absorb packages/server-e2e into packages/klient test/e2e suites
  (dual-backend, legacy v1, v2 wire) and remove the server-e2e workspace
- expose model registry and catalog services on kap-server v2 RPC surface

* fix(klient): derive session status from agentActivityView

The engine retired its sessionActivity service in #1751 (session busy is
now derived from agent activity views), but the facade still called the
deleted wire channel and imported the deleted engine module, breaking
typecheck and every klient suite at import time.

- drop the sessionActivity contract/registry entries and mirror the
  agentActivityView service instead (agent scope)
- compose session status() client-side from the pending interaction lists
  and each agent's agentActivityView, keeping the retired service's
  precedence and typing SessionStatus locally in the facade
- replace the deleted-channel call in the v2 smoke suite with a
  sessionInteractionService probe
- fix the legacy image-file suite to wait with the harness's
  waitForSessionBusy
2026-07-16 16:43:09 +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