Commit graph

301 commits

Author SHA1 Message Date
Kai
5000f981c3
fix(agent-core-v2): make large file reads resumable (#3645)
* fix(agent-core-v2): avoid repeated truncation of file reads

* fix(agent-core-v2): resume reads within long lines

Add column offsets and forward recovery for bounded long-line reads. Reject malformed UTF-16 in Read instead of replacing invalid input.

* fix(agent-core-v2): warn on lossy UTF-16 reads

Return readable text after strict UTF-16 decoding fails, with a warning retained in budgeted read continuations.

* fix(agent-core-v2): avoid redundant tail scans and detect file changes
2026-09-08 19:47:27 +08:00
Kexin Liu
d4d20d21d7
feat(agent-core-v2): allow read-only tools in /btw side questions (#3613)
* feat(agent-core-v2): allow read-only tools in /btw side questions

The btw child agent previously vetoed every tool call. Allow the
read-only tools Read, Grep, and Glob so side questions about the
codebase can be answered from current file contents; write and
execute tools stay disabled.

* docs: address review — single-sentence changeset, inline-code tool names

* chore: drop the benefit clause from the btw changeset
2026-09-08 14:50:48 +08:00
Kai
f6a9c39e22
feat(kimi-code): NotifyUser tool and mid-turn update panel (#3524)
* feat(kimi-code): two-line collapsed tool cards with width-aware headers

Collapsed cards now show the call on line one and a single dim outcome
row on line two: a Bash command's last output line (the live tail while
running) with a line-count chip, a Grep/Glob path sample, or a generic
tool's first output line. Read groups stay header-only and honor the
global expand state; failed calls keep their preview. Headers split into
head / flexible / tail segments so a long command fills the terminal
width before the chip.

* feat(kimi-code): NotifyUser tool and mid-turn update panel

Add an experimental NotifyUser tool (flag notify_user, off by default)
that agent-core-v2 offers only when the host declares the update_panel
UI capability at bootstrap; the TUI declares it through the SDK harness
options. The system prompt's guidance to send updates is injected only
when the tool is active for the agent. The TUI renders the turn's
updates in a panel above the editor (stacked, 8-row window, Ctrl+N pages
back) that the next turn clears, and keeps a one-line card in the
transcript.

* feat: add subagent updates and bidirectional paging

* fix: preserve session tools and prompts when toggling updates

* fix: correct notification delivery and CI regressions
2026-09-08 13:47:23 +08:00
7Sageer
e831fd1ea9
feat(secondary-model): drop the experimental flag from the subagent model pool (#3634)
* feat(secondary-model): drop the experimental flag from the subagent model pool

* feat(secondary-model): sync the web bundle without the experimental gate
2026-09-08 11:37:03 +08:00
7Sageer
924da9cdb4
feat(agent-core-v2): add env bindings for print-mode and bash task timeout config keys (#3598)
* feat(agent-core-v2): add env bindings for print-mode and bash task timeout config keys

Wire bashTaskTimeoutS, printBackgroundMode, printWaitCeilingS, and
printMaxTurns into taskEnvBindings as KIMI_CODE_BACKGROUND_* variables
so headless/print runs (e.g. container harnesses) can bound or disable
the print background policy without mounting a config file. bashTaskTimeoutS
accepts 0 (no timeout); invalid values are ignored. Env-set values count
as set for applyPrintModeConfigDefaults and win over the print fill.

Both integer parsers use Number.isSafeInteger: zod's int() rejects
unsafe integers, and since applySectionEnvBindings validates the combined
overlay, one unsafe value would otherwise discard the whole section's
env bindings, including valid siblings.

* Delete .changeset/print-task-env-bindings.md

Signed-off-by: 7Sageer <sag77r@hotmail.com>

---------

Signed-off-by: 7Sageer <sag77r@hotmail.com>
2026-09-07 14:36:09 +08:00
Haozhe
3f967e1410
refactor(agent-core-v2): unify fs watching into a single xstate watch service (#3502) 2026-09-07 13:30:25 +08:00
Haozhe
f0434f2d25
feat: graduate remote-control, minidb read model and search worker from experimental flags (#3552)
- remote-control is always available: kimi rc, kimi web --remote-control and /remote-control no longer require KIMI_CODE_EXPERIMENTAL_REMOTE_CONTROL or the master flag
- add the [database] config section with base (session-index read model) and search (global search worker) kill switches, both defaulting to true
- rename the kill-switch env vars to KIMI_CODE_PERSISTENCE_MINIDB_READMODEL and KIMI_CODE_SEARCH_WORKER (precedence: env > config > default)
2026-09-07 11:28:35 +08:00
Haozhe
bb16383aa1
refactor: remove the legacy agent-core v1 package (#3542)
* refactor: remove the legacy agent-core v1 package

Delete packages/agent-core (v1 engine) and migrate node-sdk fully onto
agent-core-v2:

- node-sdk owns its public contract (error protocol, KimiConfig schema
  and file IO, process-global logging facade, telemetry client
  interface); event and interaction types re-export from
  @moonshot-ai/protocol or agent-core-v2
- createKimiHarness is now the single harness factory, backed by the
  v2 client; the v1 SDKRpcClient is deleted
- CLI and vscode run agent-core-v2 unconditionally;
  KIMI_CODE_LEGACY_FLAG and useAgentCoreV1 are removed
- the wire.jsonl replay fold moves into agent-core-v2's replayBuilder
  (foldWireRecords), and the v2 journal now persists tool-call display
  so replay restores diff/todo displays
- TUI engineV2 branches collapse to the v2 path

* test(vscode): retry temp-dir teardown in replay-resume integration test
2026-09-07 11:17:51 +08:00
qer
2fbb2714af
docs(changelog): sync 0.41.0 from apps/kimi-code/CHANGELOG.md (#3551)
Some checks are pending
CI / test-pi-tui (push) Waiting to run
CI / test-vscode-legacy (push) Waiting to run
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 / 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
Publish VS Code extension / Publish VSIX to marketplaces (push) Waiting to run
Co-authored-by: kimi-agent-bot <kimi-agent-bot@users.noreply.github.com>
2026-09-04 21:14:59 +08:00
Haozhe
ac01bc4c0a
feat(cli): open permission selector from /yolo and /auto (#3515)
- rename the mode commands to /yolo and /auto (formerly
  /ask-when-needed and /never-ask) and drop their on/off arguments
- running either command now opens the permission mode list with the
  corresponding mode preselected; Enter confirms the switch
- decouple the choice picker's initial cursor from the current-value
  marker via a new initialValue option
- after a mode switch, show the mode-specific description as the yellow
  status line instead of the generic unconfirmed-changes warning
2026-09-04 17:24:45 +08:00
Grapedge
60136588a4
feat(kimi-code): add session rating survey (#3516)
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 (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-vscode-legacy (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
Publish VS Code extension / Publish VSIX to marketplaces (push) Waiting to run
2026-09-04 12:32:24 +08:00
Haozhe
b184b31497
feat(agent-core-v2): drop the dangerous command guard in auto permission mode (#3529)
* feat(agent-core-v2): allow unanalyzable bash commands in auto permission mode

* feat(agent-core-v2): drop the dangerous command guard in auto permission mode
2026-09-04 11:33:11 +08:00
Kai
523d35b54b
fix(agent-core-v2): keep background questions open past turn end and inline their answers (#3522)
Some checks are pending
CI / test (4) (push) Waiting to run
CI / test (5) (push) Waiting to run
CI / test-pi-tui (push) Waiting to run
CI / test-vscode-legacy (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 / 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
Publish VS Code extension / Publish VSIX to marketplaces (push) Waiting to run
* fix(agent-core-v2): keep background questions open past turn end and inline their answers

Background AskUserQuestion reused the generic task pipeline end to end, which
broke it in two ways: the pending interaction was still bound to the asking
turn, so it was cancelled the moment the agent finished its turn, and the
turn-end cancel response was then misread as an answer. On top of that the
completion notification only carried a pointer to the task output file,
forcing an extra Read round trip for a few bytes of JSON.

- Detach background question interactions from the asking turn so they stay
  pending until answered, stopped, or the agent closes.
- Treat cancelled interaction responses as dismissals.
- Inline the answer JSON in the question task notification and word the
  notification as answered or dismissed; fall back to the output file only
  when the answer exceeds the inline budget.
- Trim the background launch result to task id, status, and one next step.
- Fold transcript notification summaries before inline answer blocks.

* fix(agent-core-v2): fail background questions on tool errors and translate interaction cancellations in the question service

Follow-up hardening from review:

- The interaction kernel's cancellation response now has a named shape,
  InteractionCancellation, and SessionQuestionService.request translates it
  into a dismissal (null) before handing the result to callers. The
  AskUserQuestion tool no longer inspects answer maps for a cancelled key,
  so a bare answer map can never be mistaken for a cancellation.
- QuestionBackgroundTask settles as failed with the tool's message as the
  stop reason when the question tool reports an error, instead of writing
  the error text as completed output. The generic task notification then
  carries the reason, and the answered/dismissed wording is not used.
- The question notification only says answered or dismissed when the task
  output parses as an answers payload; any other output keeps the generic
  completed wording.
2026-09-03 20:53:09 +08:00
wenhua020201-arch
494df61ce9
docs(zh,en): restyle configuration and customization sections (#3485)
* docs(zh): restyle configuration and customization sections

Editorial pass across 11 pages: clear explanatory dashes, replace arrow
cross-references with inline links, compress oversized table cells while
keeping operational facts (value ranges, override precedence, activation
conditions), split >5-sentence paragraphs by theme, fold interface
contracts and low-frequency internals into details blocks, add map
sentences to multi-paragraph sections, add subcommand overview table to
kimi-command reference. Add /provider manager screenshot to media.

* docs(zh): restore dangerous_command_guard, fix trust prompt default and secondary-model default

- config-files: restore the dangerous_command_guard paragraph dropped
  during the style pass (regression, content from upstream #3290)
- mcp: the trust prompt defaults to Trust this folder per
  trust-prompt.test.ts; docs had the direction reversed (pre-existing)
- config-files: subagent model pool defaults on since #3334;
  KIMI_CODE_EXPERIMENTAL_SECONDARY_MODEL=0 disables (pre-existing staleness)

* docs(zh,en): sync en mirrors and fix anchor slugs

Add restyled en mirrors for all 11 configuration/customization pages,
mirroring the zh structure (section parity, map sentences, compressed
cells, details folds) while keeping en phrasing.

Fix anchor slugs in both locales (underscore kept, dots dropped per
@mdit-vue slugify): loop_control, openai_responses, kimi_model_,
systemmd variants; retarget renamed permission-mode section
(yolo/auto -> The three permission modes / 三种权限模式).
2026-09-03 16:12:29 +08:00
wenhua020201-arch
e2e5859815
docs: annotate renamed permission modes with bilingual names and former aliases (#3476) 2026-09-02 20:19:50 +08:00
liruifengv
a7087694bb
docs(changelog): sync 0.40.1 from apps/kimi-code/CHANGELOG.md (#3475)
Some checks are pending
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-vscode-legacy (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 / Publish native release assets (push) Blocked by required conditions
Publish VS Code extension / Publish VSIX to marketplaces (push) Waiting to run
2026-09-02 18:37:26 +08:00
qer
bec34e17c4
docs(changelog): sync 0.40.0 from apps/kimi-code/CHANGELOG.md (#3457)
Co-authored-by: kimi-agent-bot <kimi-agent-bot@users.noreply.github.com>
2026-09-02 15:00:11 +08:00
Haozhe
a23ff56358
feat(kap-server): add POST /workspaces/{id}/add-dir endpoint (#3451) 2026-09-02 12:47:15 +08:00
Haozhe
ae7a6dc6fb
refactor(acp): remove the legacy acp-adapter package (#3434) 2026-09-01 23:59:20 +08:00
Haozhe
442b56391b
fix(agent-core-v2): let experimental config override the master flag env (#3427) 2026-09-01 21:36:57 +08:00
Haozhe
19413c2803
docs: rename permission modes to Always Ask, Ask When Needed, and Never Ask (#3406)
Follow-up to #3403 for the user docs: living pages (en + zh mirrors)
now use the new mode names and descriptions — slash-commands and
interaction references for /ask-when-needed (aliases /yolo, /yes) and
/never-ask (alias /auto), CLI flag and config glosses, guides,
customization pages, and the docs AGENTS.md terminology table. Wire
and config ids (manual/yolo/auto) are unchanged; release notes are
historical and untouched.
2026-08-31 23:02:07 +08:00
wenhua020201-arch
70e35f5f82
docs: add Remote Control guide (zh/en) (#3398) 2026-08-31 20:26:12 +08:00
Haozhe
4b9888b73d
feat(agent-core-v2): require approval for dangerous bash commands in all permission modes (#3290)
* feat(agent-core-v2): require approval for dangerous bash commands in all permission modes

* feat(agent-core-v2): deny dangerous commands in auto mode, unwrap command launchers, and skip the guard for non-interactive hosts

* feat(agent-core-v2): support disabling the dangerous-command guard via config

* test(node-sdk): project v2's env-materialized empty permission section in config parity
2026-08-31 20:24:53 +08:00
7Sageer
5f0aa7f6d6
fix(cli): default the workspace trust prompt to "Trust this folder" (#3391)
Some checks are pending
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-vscode-legacy (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 / Publish native release assets (push) Blocked by required conditions
Publish VS Code extension / Publish VSIX to marketplaces (push) Waiting to run
2026-08-31 16:01:46 +08:00
7Sageer
971a8b24c1
feat(secondary-model): graduate the subagent model pool out of experimental (#3334)
* feat(secondary-model): enable the subagent model pool by default

* feat(secondary-model): graduate the subagent model pool out of experimental

* fix(secondary-model): honor the v2 default_model key on the legacy engine

* fix(secondary-model): enforce forced subagent pools on the legacy engine

* fix(tower): keep reviewers on primary model

* fix(secondary-model): live-apply picker saves and replace the legacy model key

* fix(secondary-model): accept default_model in the live-apply setter and clear on removal

* fix(secondary-model): revert the legacy v1 engine, keep the v2 pool opt-out

* feat(secondary-model): graduate pool with model source telemetry

* fix(secondary-model): keep legacy engine opt-in

* test(sdk): pin secondary-model engine divergence

* fix(tower): honor forced model for reviewers
2026-08-31 15:33:26 +08:00
7Sageer
9fa1e77df5
refactor(agent-core-v2): remove the ${now} system prompt template variable (#3341)
Some checks are pending
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-vscode-legacy (push) Waiting to run
CI / test-windows (push) Waiting to run
CI / lint (push) Waiting to run
CI / typecheck (push) Waiting to run
Nix Build / Check flake.nix workspace sync (push) Waiting to run
Nix Build / nix build .#kimi-code (push) Blocked by required conditions
Release / Publish native release assets (push) Blocked by required conditions
Release / Release (push) Waiting to run
Release / Deploy docs (push) Blocked by required conditions
Release / Native release artifact (push) Blocked by required conditions
2026-08-28 19:09:53 +08:00
Kimi Agent
0312baaf13
docs(changelog): sync 0.39.1 from apps/kimi-code/CHANGELOG.md (#3342) 2026-08-28 18:34:17 +08:00
liruifengv
2bf7ed22d7
feat(auth): split model readiness from sign-in state in /api/v1/auth (#3293)
* feat(auth): split model readiness from sign-in state in /api/v1/auth

GET /api/v1/auth now reports models_ready (the default model resolves
against the configured catalog, providerless and env-injected models
included) instead of the compound ready flag, and no longer carries
default_model — config values are served by /config alone. The v1
summary schema follows.

OAuth managed-model refreshes now heal a lost default model: the
refresh snapshot includes defaultModel, so an unchanged catalog with
a missing default still lands the write-back branch and re-selects
one. The refresh also rebases onto a fresh config read after the
remote fetch, so a model or thinking change made during the fetch is
no longer overwritten. The shared discovery refresh path (scheduler,
POST /providers/{id}:refresh) heals the default the same way.

Config changes are now published to WS clients on every write path:
a debounced+trailing publisher bridges IConfigService section changes
to ConfigChanged with camelCase changedFields and a full config
projection, and the broadcaster forwards event.config.changed and
event.model_catalog.changed (both previously published but never
delivered). All three event types are registered in the event unions,
so session_event parsing and AsyncAPI describe them.

BREAKING CHANGE: GET /api/v1/auth drops the ready and default_model
fields in favor of models_ready; event.config.changed's changedFields
is now camelCase domain names instead of the raw snake_case request
keys (v1 summary schema follows).

* fix(kap-server): expose the session model in session list projections

GET /api/v1/sessions hardcoded agent_config.model to '' and the v2
projection had no model field at all, so clients could only learn a
session's model via the post-select /status read — which races the WS
replay and often never lands. SessionFacts now carries the live
session's model (same source as the snapshot route), toWireSession
emits it, and the v2 activity domain gains a nullable model field.

* fix(kap-server): gate prompt submission on the effective session model

The submit gate called ensureReady() with no override, so it only ever
validated config.default_model: a session with a bound model (or a
prompt carrying one) was rejected with 40113 whenever default_model was
missing or dangling. Pass the effective model (request model, then the
agent profile's bound model, falling back to default_model inside
ensureReady) on both the prompt submit and btw routes.

* fix(agent-core-v2): honor defaultProvider in model readiness resolution

resolveModelForReady stopped at the flat baseUrl fallback, so a model
that omits provider/providerId and relies on the configured
defaultProvider resolved at runtime (ModelCatalog.resolveProviderContext
falls back to it) while /api/v1/auth reported models_ready:false and the
send gate rejected the prompt. Mirror the runtime order (providerId ->
provider -> defaultProvider -> flat baseUrl) and pass the configured
default provider from both readiness callers.

* fix(kap-server): redact inline model credentials from config responses

toConfigResponse only redacted the providers section, so a model's
inline apiKey/oauth rode GET /config verbatim and, via the new
event.config.changed publisher, every WS connection plus the persistent
event journal. Project the models section the same way: strip
credential fields and report has_api_key.

* fix(agent-core-v2): honor defaultProvider in ensureReady credential checks

The readiness phase learned the defaultProvider fallback, but the
credential phase right after still derived the provider only from the
model's explicit fields: a model omitting provider/providerId passed
readiness yet missed the default provider's apiKey/OAuth material and
prompts failed with auth.token_missing. Mirror the same provider chain
(providerId -> provider -> defaultProvider) when resolving credentials.

* fix(kap-server): validate the model a profile bind will select at the prompt gate

The gate validated the session's current model even for a prompt that
switches profile without a model — but bind falls back to defaultModel
in that case, so a stale session model drew a misleading 40113 before
bind could run. Gate on bind's selection order instead: the request's
explicit model, then the default on a profile switch, then the session's
bound model.

* fix(kap-server): redact inline service credentials from config responses

The earlier redaction covered providers and models, but toConfigResponse
still passed the services section through verbatim: inline or
env-injected apiKey, oauth references, and credential-bearing
customHeaders rode GET /config and, via the event.config.changed
publisher, every WS connection plus the persistent event journal.
Project services the same way: strip apiKey/oauth into has_api_key and
report only the header names as custom_header_keys (the MCP
envKeys/headerKeys convention).

* fix(kap-server): keep unlisted config domains through event validation

The config.changed broadcaster returned the zod-parsed config, which
strips domains absent from configResponseSchema (mcp, identity,
model_catalog, image, tools, token_counting): changedFields named them
while the advertised full snapshot no longer matched GET /api/v1/config.
Make the response projection passthrough (defineRoute validates only
requests, so REST responses are unaffected).

* fix(agent-core-v2): use the exact configured key for model readiness lookups

resolveModelForReady trimmed the model id before the models-table lookup
while ModelCatalog and ensureReady use the configured string as an exact
record key: a whitespace-padded default_model was reported ready and then
crashed the submit gate with an internal error instead of 40113, and a
legitimate key containing spaces was reported dangling. Trim only rejects
blank values now; the lookup always uses the raw key.

* fix(protocol): keep unlisted config domains in the shared event projection

The shared configResponseSchema stripped domains it does not enumerate
(mcp, identity, model_catalog, image, tools, token_counting, subagent,
secondary_model), so event.config.changed parsed through agentEventSchema
named them in changedFields while omitting their values. Make the shared
projection passthrough like the kap-server-local one.

* fix(agent-core-v2): use the exact default_provider key in readiness checks

The defaultProvider fallback trimmed the configured value before the
providers-table lookup while ProviderService and ModelCatalog use the
configured string verbatim: a whitespace-padded default_provider could
build successfully yet report not-ready (40113), or report ready for a
provider runtime resolution cannot find. Trim only rejects blank values;
the lookup uses the raw key.

* chore: sync web dist from code-app

Rebuild the bundled web UI against this branch's /auth contract (models_ready, no ready/default_model): the previous bundle still read the old fields and stayed in the not-ready flow against this server.

code-app: 000d2594ff3e95b553be326126bab3f939b62944

* Revert "chore: sync web dist from code-app"

This reverts commit 9400a24a03863b3e8b780dda251540f824f02f3a.

* fix(oauth): rebase the default selection after the refresh fetch

A provider refresh snapshots the config before the remote catalog fetch;
when the user selects a default model while the fetch is in flight, the
stale snapshot's empty default made an otherwise unchanged catalog enter
the write path and the self-heal persisted the generated default over the
user's newer selection. Each branch now re-reads and rebases the
default/thinking selection after its fetch, before cloning, comparing,
or writing.

* style(kap-server): pass optional custom_header_keys without conditional spread
2026-08-28 15:53:56 +08:00
liruifengv
676e4d8224
docs(changelog): sync 0.39.0 from apps/kimi-code/CHANGELOG.md (#3300) 2026-08-27 20:03:44 +08:00
7Sageer
bd5e32f683
fix(secondary-model): stop rewriting the section when providers refresh or are removed (#3284)
* fix(secondary-model): stop rewriting the section when providers refresh or are removed

Provider refresh, provider deletion/rename, catalog/registry import,
OAuth logout, and SDK removeProvider used to cascade into the user's
[secondary_model] block: pool entries were silently pruned, and the
whole section was deleted when its effective default dangled. The
cascade ran from a cache-refresh path (including an unattended 6h
scheduler), so upstream model-list changes could irreversibly destroy
hand-written configuration without any notice.

Config is user intent; the catalog is an availability snapshot. Stop
rewriting the section on every provider/models writer. An entry whose
model no longer resolves fails pool validation on the next session
create with a message naming the offending alias, which is the same
fail-fast contract hand-written typos already had.

* chore(sdk): add changeset for the removed secondary-model cascade export

* Delete .changeset/sdk-remove-secondary-model-cascade.md

Signed-off-by: 7Sageer <sag77r@hotmail.com>

* Delete .changeset/secondary-model-no-silent-rewrite.md

Signed-off-by: 7Sageer <sag77r@hotmail.com>

---------

Signed-off-by: 7Sageer <sag77r@hotmail.com>
2026-08-27 16:50:42 +08:00
liruifengv
692bb0a409
feat(kap-server): add task detach action to move foreground tasks to background (#3273)
* feat(kap-server): add task detach action to move foreground tasks to background

* test(kimi-code-sdk): normalize v2-only parentToolCallId in task parity projections

* fix(agent-core-v2): distinguish user-initiated detach in tool result text

* feat(agent-core-v2): mention user-initiated backgrounding in the bash tool description

* feat(agent-core-v2): use a client-agnostic background-task panel hint in the bash tool description

* ci: retrigger checks

* feat(agent-core-v2): client-agnostic human_shell_hint and a detached_by_user marker in tool results

* fix(protocol,docs): declare parent_tool_call_id in the shared task schema and document the detach action

* style: remove added comments
2026-08-27 15:38:21 +08:00
wenhua020201-arch
db792c34ce
docs: rework web guide, interaction and getting-started pages (#3260)
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 (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-vscode-legacy (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
* docs: rework web guide, interaction and getting-started pages (zh/en)

* docs: rename guides/server to guides/web to match the reworked page

* docs: neutralize screenshot workspace names and sync en web guide to zh

* docs: trim web UI screenshot to a single neutral workspace
2026-08-26 17:00:57 +08:00
Haozhe
e6a302b310
feat(agent-core-v2): add KIMI_CODE_INFINITE_RETRY infinite retry mode (#3240)
- retry every failed LLM request indefinitely in runRequest when
  KIMI_CODE_INFINITE_RETRY is set, covering turn steps and operation
  requests such as compaction
- keep projection recovery ahead of the infinite retry branch, honor
  Retry-After, and keep abort effective during backoff waits
- exclude context overflow from infinite retry so the deterministic
  turn-level and compaction-level overflow recovery paths still run
- extract retryBackoffDelay for single-attempt backoff computation
2026-08-25 19:51:46 +08:00
Haozhe
496bb6ce4e
feat(agent-core-v2): add independent [swarm] timeout_ms for AgentSwarm (#3198)
Add a dedicated [swarm] config section so AgentSwarm subagent timeouts no longer follow [subagent] timeout_ms: timeout_ms (default 7200000, i.e. 2 hours; 0 = no timeout) with the KIMI_CODE_SWARM_TIMEOUT_MS env override, mirroring the [subagent] section. Deliberate behavior change with no fallback: a value previously set in [subagent] timeout_ms to cover swarms needs to move to [swarm]. Print mode (kimi -p) keeps its no wall-clock cap semantics: [swarm] timeout_ms also defaults to 0 there unless explicitly set. Docs updated in both locales (config-files, env-vars, tools) and the config manifest regenerated.
2026-08-24 19:42:55 +08:00
Haozhe
dceb3fd634
docs: sync recent CLI releases into guides and reference pages (#3195)
Some checks are pending
CI / test (5) (push) Waiting to run
CI / test-pi-tui (push) Waiting to run
CI / test-vscode-legacy (push) Waiting to run
CI / test-windows (push) Waiting to run
CI / lint (push) Waiting to run
CI / typecheck (push) Waiting to run
CI / test (2) (push) Waiting to run
CI / test (3) (push) Waiting to run
CI / build (push) Waiting to run
CI / test (1) (push) Waiting to run
CI / test (4) (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-08-24 15:59:50 +08:00
7Sageer
ee53d84fb0
fix(agent-core-v2): honor default thinking effort for secondary-bound subagents (#3191)
* fix(agent-core-v2): honor default_effort for secondary-bound subagents

[secondary_model].default_effort was parsed but never consumed, so a
subagent bound to a secondary/pool model resolved its thinking effort
from the main-oriented global [thinking].effort or the middle of the
bound model's support_efforts. Pass the section's default_effort as the
explicit spawn thinking, then fall back to the bound model entry's own
declared default_effort, both ahead of the global thinking config.

* docs(configuration): document secondary_model default_effort precedence

* fix(agent-core-v2): apply the model-default thinking fallback to tower spawns

TowerSpawnTool resolves the subagent binding directly and bypasses
planSpawn, so pool-bound tower workers never saw the bound model
entry's default_effort fallback. Apply the same resolution in launch.

* fix(agent-core-v2): preserve disabled thinking for subagents
2026-08-24 14:14:47 +08:00
Haozhe
cd2d91020a
docs: document detailed parameters for all server API endpoints (#3155)
* docs: document detailed parameters for all server API endpoints

* docs: correct model alias id and skill attachment shapes in server api reference
2026-08-21 15:05:38 +08:00
wenhua020201-arch
e0936bfc3e
docs(plugins): rework Kimi Datasource section with authoritative source names (#3153)
- Name authoritative data sources in the intro (World Bank, IMF, OECD, FRED,
  WHO, FAO, NBS, Wind, S&P Capital IQ, SEC EDGAR, Caixin, Xinhua Finance,
  Gildata) so users can see what backs the plugin
- Turn the eight 'what you can do' scenarios into collapsible details blocks
  with question-style headers (title — question?) so the page stays scannable
- Coverage table: add a financial-news/industry-data row (Caixin, Xinhua
  Finance); name Yuandian Legal for the legal row; macroeconomics row now
  also covers China government data and IGO official statistics
- Only sources approved for both domestic and overseas publicity are named;
  restricted sources are described by capability instead
2026-08-21 14:59:49 +08:00
7Sageer
f6736d7c0d
feat(agent-core-v2): add a fork parameter to the Agent tool (#3007)
* feat(agent-core-v2): add a fork parameter to the Agent tool

Spawning with fork: true starts the subagent from a one-time snapshot of
the calling agent's completed conversation history — same profile, tool
set, and model — instead of zero context. The seed trims the trailing
open tool exchange (the in-flight Agent call itself) before appending
into the child's context memory, and the first prompt carries an
inheritance notice framing the seeded history as reference material.

Fork rejects resume, a different subagent_type, or a model override as
tool errors, and skips the subagents allowlist since a self-inheritance
is not a delegation.

* fix(agent-core-v2): bind the stale-todo reminder only into the main agent

Subagents share the session todo list but no longer receive the
stale-todo nudge — the reminder injector now registers only on the main
agent, so delegated and forked agents are not prompted to maintain a
list they do not own.

* fix(agent-core-v2): inherit the caller's live binding and label fork launches correctly

Review follow-ups for the Agent tool fork mode:

- overlay the caller's live profile.data() via applyBindingSnapshot after
  the catalog re-bind, so ephemeral addActiveTool deltas, the rendered
  system prompt, and runtime model/subagents updates survive the fork;
  skip the profile prompt prefix since the caller's prefixed first
  prompt is already part of the seeded history
- resolve the fork activity label and approval-rule subject from the
  caller's own profile instead of falling back to the default subagent
  type, so an Agent(<other profile>) rule cannot approve a fork

* fix(agent-core-v2): close inherited in-flight tool calls instead of trimming them

Fork seeding now answers the source's trailing open tool calls with a
synthetic in-flight result instead of cutting the whole trailing
exchange: the seeded history stays protocol-valid, keeps the source's
final step visible as reference, and no longer confuses side-question
(btw) agents forked while the main agent is mid-turn. The close helper
is shared by the Agent tool fork and IAgentLifecycleService.fork.

Fork launches also stop requiring the caller's profile to still exist
in the session catalog: the child is created unbound and overlaid with
the caller's live binding snapshot, matching the lifecycle fork path,
and now records forkedFrom provenance.

* refactor(agent-core-v2): route Agent tool forks through agentLifecycle.fork

* feat(agent-core-v2): add a fork parameter to the AgentSwarm tool

* fix(agent-core-v2): seal partial assistant forks

* fix(agent-core-v2): align fork parameter descriptions

* fix(agent-core-v2): drop the main-only registration gate from goal tools

* fix(agent-core-v2): disclose dates via reminders to keep the system prompt byte-stable

* docs: condense the fork changesets to single sentences

* docs(agent-core-v2): frame the tool-contribution when gate as a fork parity trade-off

* test(agent-core-v2): plug fork coverage gaps and decouple swarm tests from spawn internals

* docs(agent-core-v2): keep the when-gate guidance in the contribution JSDoc only

* fix(agent-core-v2): contribute cron tools to every agent for fork prefix-cache parity

CronCreate/CronList/CronDelete were registered directly into the main
agent's tool registry by SessionCronServiceImpl, bypassing the
AgentToolContribution seam and keying on per-agent identity — so a forked
agent rebuilt a tool surface three tools shorter than its caller and the
inherited prompt prefix missed the cache.

Register the three tools through registerAgentToolService like the goal
tools do (no when gate, identical surface for every agent) and enforce
the main-agent restriction at execution time instead. Also fall back to
DEFAULT_CRON_CONFIG when the config section is absent, since the service
can now be constructed after the main agent exists.

* feat(agent-core-v2): track the fork parameter in the subagent_created event

* fix(agent-core-v2): gate tower orchestration tools at execution time

TowerInit/TowerPlan/TowerSpawn/TowerMerge/TowerTeardown were contributed
with a when predicate keyed on agentId === 'main', so a forked agent
rebuilt a tool surface missing TowerInit (always present for the default
profile) plus the rest of the tower set once it was enabled — breaking
prompt prefix-cache parity with the caller.

Contribute the tools with no when gate (profile policy still controls
visibility) and reject non-main callers at execution time instead.

* test(agent-core-v2): expect the fork field in the subagent_created mirror assertion

* test(agent-core-v2): cover fork subagent first-request prefix parity

* refactor(agent-core-v2): share the main-agent-only tool refusal across cron and goal tools

Goal tools rejected subagent callers by throwing GOAL_UNSUPPORTED_AGENT
from the service, which the executor wrapped as a resolution failure;
cron tools returned a clean refusal but each tool open-coded the same
identity check. Centralize the check and both messages in
agent/tools/mainAgentOnly.ts and use it from all seven tools, keeping
AgentGoalService.assertSupportedAgent as the coded boundary for RPC and
SDK callers.

* refactor(agent-core-v2): keep the goal main-agent gate at the tool layer only

* fix(agent-core-v2): preserve the fork tool surface when inheriting user tools

* Revert "refactor(agent-core-v2): keep the goal main-agent gate at the tool layer only"

This reverts commit fc09a8fa32de43bb6223bf96fcab9a7b200ac051.

* test(agent-core-v2): complete fork lifecycle stub

* Delete .changeset/btw-inflight-tool-calls.md

Signed-off-by: 7Sageer <sag77r@hotmail.com>

* Delete .changeset/todo-reminder-main-only.md

Signed-off-by: 7Sageer <sag77r@hotmail.com>

* Delete .changeset/swarm-fork-context.md

Signed-off-by: 7Sageer <sag77r@hotmail.com>

* Add optional 'fork' parameter to subagent tools

Signed-off-by: 7Sageer <sag77r@hotmail.com>

* docs(agent-core-v2): drop the fork JSDoc comments

* feat(agent-core-v2): add prompt_cache_probe telemetry for forked agents

* feat(agent-core-v2): gate the subagent fork parameter behind an experimental flag

---------

Signed-off-by: 7Sageer <sag77r@hotmail.com>
2026-08-20 21:50:44 +08:00
liruifengv
8ca1a3fd91
docs(changelog): sync 0.38.0 from apps/kimi-code/CHANGELOG.md (#3137) 2026-08-20 21:40:27 +08:00
7Sageer
38c55501ad
docs: restructure the secondary_model config section for scannability (#3123)
Split the dense prose walls into a minimal config, a one-line-per-field
table, constraint bullets, a numbered resolution order, and a separate
advanced subsection for per-entry thinking efforts. All behavioral facts
are preserved; zh and en stay mirrored.
2026-08-20 17:25:13 +08:00
qer
03dcfcf6d0
feat(datasource): add NDA/NBS, standards, IGO, xhcj, and caixin sources (#3115)
* feat(datasource): add NDA/NBS, standards, IGO, xhcj, and caixin sources

* fix(datasource): narrow real-time-news ban to coverage gaps, require PublishTime citation

* fix(datasource): scope the real-time-news limitation to coverage gaps only

* fix(datasource): trim redundant clause in the real-time-news limitation

* fix(datasource): stop on a result that covers the question, not the first success

* fix(datasource): front-load trigger terms in the skill listing description

* fix(datasource): exempt discovery calls from the one-call workflow
2026-08-20 14:16:15 +08:00
Haozhe
15da84606a
feat(kap-server): add workspace-grouped sessions view and lifecycle events (#3114)
Some checks are pending
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-vscode-legacy (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 / 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
Nix Build / Check flake.nix workspace sync (push) Waiting to run
GET /api/v2/sessions gains view=by_workspace: one request returns every
workspace with a matching session, each carrying its first group.page_size
sessions under the requested sort plus the workspace's full matching total,
with group-level page_token pagination (40922 on condition drift). Groups
key on the alias-canonical workspace id, so legacy split buckets of one
physical directory merge into a single group, matching the v1 alias
semantics. meta.has_prompt filters sessions by prompt presence (the v1
exclude_empty equivalent) in both views. The flat view and v1 routes stay
byte-compatible.

The global WS stream now fans out event.session.archived (live and cold
paths; payload carries the session id and workspace_id) and
event.workspace.created/updated/deleted, published by the core
IWorkspaceService on every mutation path including the implicit
createOrTouch on session creation.

kimi-inspect consumes the grouped projection as a single-column
workspace/session tree in the chat view; the session pane merges into the
right dock as the Session tab. The server API reference (en + zh) documents
the new parameters, the grouped response, and the new events.
2026-08-20 13:45:38 +08:00
Haozhe
ca87c58e62
fix(agent-core-v2): cap default subagent delegation at one level (#3012)
- give the builtin agent profile an explicit subagents allowlist (coder, explore, plan), restoring v1 semantics
- inherit the default profile's allowlist when a caller profile declares none, instead of leaving delegation unrestricted
- pass a lone "*" subagents field through as an explicit unrestricted marker
2026-08-20 09:53:11 +08:00
Kimi Agent
fa9865f2ee
docs: document KIMI_CODE_CUSTOM_HEADERS on the env vars page (#3097)
Some checks are pending
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-vscode-legacy (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 / Publish native release assets (push) Blocked by required conditions
* docs: document KIMI_CODE_CUSTOM_HEADERS on the env vars page

* docs: address review on KIMI_CODE_CUSTOM_HEADERS entry

- use a neutral gateway header name in the example
- correct the release version to 0.20.2
- scope the override claim to exact-name matches and warn against
case-variant auth headers

* docs: describe protocol-dependent Authorization precedence

On the OpenAI-compatible protocols (kimi/openai/openai_responses) an
exact Authorization custom header is applied after the SDK-generated
bearer token and therefore replaces it; /models listing keeps its own
authentication.

* docs(zh): add the required space before the config-files link

Per the mixed-content spacing rule in docs/AGENTS.md.

---------

Co-authored-by: bj456736 <bj456736@users.noreply.github.com>
2026-08-19 23:02:36 +08:00
Luyu Cheng
8440801de4
feat(agent-core-v2): add the WaitFor tool for waiting on background tasks (#3060)
* feat(agent-core-v2): add the WaitFor tool for waiting on background tasks

* fix(agent-core-v2): mark WaitFor deliveries only after formatting succeeds

* fix(agent-core-v2): cancel losing waits once the WaitFor race resolves

* test(node-sdk): project WaitFor out of the v1-v2 resume parity roster

* fix(agent-core-v2): gate WaitFor goal guidance behind the wait_for flag

* fix(agent-core-v2): gate WaitFor goal guidance on actual tool availability

* fix(agent-core-v2): enforce the wait_for flag at WaitFor execution time

* fix(agent-core-v2): consult the live tool policy in the WaitFor availability check
2026-08-19 11:26:59 +08:00
qer
cdaa80b778
docs(changelog): sync 0.37.2 from apps/kimi-code/CHANGELOG.md (#3063)
Some checks are pending
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-vscode-legacy (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 / Publish native release assets (push) Blocked by required conditions
2026-08-19 02:05:30 +08:00
liruifengv
c11da4fbd8
docs(changelog): sync 0.37.1 from apps/kimi-code/CHANGELOG.md (#3055) 2026-08-18 22:50:19 +08:00
liruifengv
589ed5467c
docs(changelog): sync 0.37.0 from apps/kimi-code/CHANGELOG.md (#3048)
* docs(changelog): sync 0.37.0 from apps/kimi-code/CHANGELOG.md

* docs(changelog): align English 0.37.0 entries with reviewed wording
2026-08-18 19:49:36 +08:00
liruifengv
8c865f4817
feat(kimi-code): support automatic updates for native installations via staged swap (#2994)
* feat(kimi-code): support automatic updates for native installations via staged swap

Native (SEA) installs previously could not self-update on Windows and
relied on 'curl | bash' re-install on Unix. Replace both with a staged
swap updater:

- startup swaps in a staged binary (verified against the release
  manifest sha256, smoke-checked via --version) and re-execs it, so the
  running process never replaces itself (Windows-safe)
- downloads run in a self-spawned hidden sub-command, in the background
  from the update preflight or in the foreground from 'kimi upgrade'
- rollback from .bak on any swap failure; install failures keep the
  existing retry/prompt thresholds

* fix(kimi-code): fully clean staged artifacts on swap discard paths

Real-binary smoke testing on macOS surfaced two cleanup gaps in the
discard path: the claimed metadata file was unlinked after the staging
dir rmdir (so the empty dir survived), and the staged exe was
rediscovered via the already-claimed staged.json (so it leaked on the
downgrade-guard path). Pass the known metadata through and order the
unlink before the rmdir.

* fix(kimi-code): restore staged metadata on swap failure and sweep update leftovers at startup

* fix(kimi-code): address codex review on lock contention and swap crash window

- The background native install no longer takes the outer install lock:
  the self-spawned downloader holds it for the whole download, and the
  parent's spawn-time lock raced the child into a false lastSuccess.
- Smoke-check the staged exe before moving anything, so a bad staged
  binary is discarded with the install path never left empty; the
  remaining crash window is two adjacent atomic renames (documented,
  recoverable via the .bak or by re-running the install script).

* test(kimi-code): align swap test expectation with smoke-before-rename order

The restore-on-failure case now observes the early smoke check's
--version spawn; only the re-exec spawn must be absent.

* fix(kimi-code): stage the bare CDN binary instead of unzipping

The published per-release artifacts are the bare platform binaries
(kimi-code-<target>[.exe]), not zip archives — the staging flow now
streams the download straight to the staged exe after the manifest
sha256 check, and the zip reader is dropped. Verified end-to-end on
macOS against the live CDN: download -> sha256 match -> swap ->
re-exec into the real released binary.

* fix(kimi-code): address second codex review round

- re-exec: forward 128 + signo when the swapped-in child dies by signal
  instead of reporting exit 0
- __update_download: only exit 0 without staging when the lock holder is
  staging the SAME version; a different in-flight version (or a vanished
  lock) no longer surfaces as a successful foreground upgrade
- staging: sweep orphaned .part downloads and unreferenced staged exes
  before downloading, preserving live swap claims and their payloads

* feat(kimi-code): show download progress for native updates

The foreground 'kimi upgrade' path streamed 180 MB with a single static
'Downloading…' line. Render progress instead: a throttled in-place
percentage line on a TTY, one line per 32 MB when piped, and plain MB
counts when Content-Length is unknown.

* fix(kimi-code): bound native update downloads with an idle timeout

Codex review: the manifest fetch cleared its timer once headers arrived,
so a stalled response body hung the worker forever, and the binary
download had no abort at all. The manifest timeout now covers body
consumption, and the binary stream aborts after 30 s without a chunk
(total duration stays unbounded for slow networks). The idle timeout is
injectable for tests.

* fix(kimi-code): retry native updates blocked by an orphaned active record

Windows real-machine verification surfaced that a parent exiting before
the downloader's exit event leaves a fresh-looking 'active' record that
silently blocks every background retry for the 6 h TTL. For native
installs, lock liveness is the truth past a 60 s spawn grace window:
a held lock means a download is running, a free lock means the record
is an orphan and a new attempt may start. Package-manager sources keep
the TTL behavior (no lock to prove liveness).

* fix: skip staged swap while another instance holds a fresh claim

sweepStaleNativeUpdateArtifacts already detected an in-progress swap in
a concurrent instance, but the result stayed inside the cleanup helper:
startup still claimed a newly published staged.json and ran a second
swap, so the two launchers could rename the install path and delete each
other's rollback backup. Propagate the in-progress signal and skip
claiming until the existing claim is released or goes stale.

* fix: keep the install lock while its holder process is alive

The install lock went stale purely by age (30 min), but the native
downloader is idle-bounded, not duration-bounded: a slow link can
legitimately take longer. Another startup would then sweep the lock and
spawn a second downloader, and both would write and clean the same
.staging paths. Past the age threshold, fall back to a pid liveness
probe (signal 0) — the lock is stale only when the holder is gone.

* fix: keep recovery artifacts on rollback failure and wait out same-version downloads

Two robustness fixes from review:

- native-swap: when moving the staged exe into place fails AND the
  rollback rename fails too (transient lock, AV), the install path is
  left absent and no next launch can start. Discarding the staged
  payload and claim on top of that removes the second recovery copy.
  rollback() now reports its result; on a double failure the swap keeps
  the .bak (which IS the old exe), the staged exe and the claim so
  manual recovery or a re-install still works.
- update-download: a foreground `kimi upgrade` racing a background
  downloader of the same version exited 0 immediately, so the CLI
  printed a success message for a download that could still fail. The
  worker now waits while the same-version holder is in flight, adopts
  the verified staged result (staged.json lands before the lock is
  released), and takes over the download when the holder finished
  without staging.

* fix: stamp the swap claim with a fresh mtime when claiming

rename() preserves the staged metadata's mtime, which can be arbitrarily
old — the background download often finishes hours before the next
launch claims it. A concurrent launch's sweep would then classify the
live claim as crash residue (older than the 5-minute window) and delete
the claim, the staged exe, and eventually the first swap's rollback
backup. Stamp the claim file with the claim time so the staleness check
measures the swap's liveness, not the download's age.

* fix: stamp the claim before the rename so it is born fresh

Stamping after the rename left a window: a concurrent launch could
inspect the claim between the two syscalls, see the staged metadata's
old mtime, and delete the staged executable mid-swap. utimes the state
file first so the claim carries a fresh timestamp from the instant it is
published — no fresh-looking-later intermediate state exists.

* fix: chmod the staged download before publishing it at its final name

A swap claims only the staged METADATA; the staged exe stays in
.staging/. A concurrent same-version downloader (possible because swaps
do not hold the install lock) then re-downloads and renames its .part
over that path. If the swap moves the file into the install path between
the downloader's rename and its post-publish chmod, the chmod lands on a
path that is already gone and the installation is left non-executable —
every future launch fails. Apply the executable mode to the private
.part file before the publishing rename so the staged exe is executable
from the instant it appears.

* fix: publish the install lock atomically via hard link

The 'wx' open exposed a momentarily empty lock file before its contents
were written. A concurrent acquirer reading in that window got a
SyntaxError, treated the lock as stale, swept it and also won — two
"holders" then ran stageNativeUpdate against the same .staging paths.
Write the lock contents to a unique temp file and hard-link it into
place: link() fails when the destination exists (same exclusivity as
'wx') and the lock path only ever appears fully written.

* fix: serialize stale-lock takeover through a secondary lock

A pathname-level delete can never be conditioned on the file still being
the inspected stale instance, so a plain compare-and-delete still loses
exclusivity: two workers classifying the same stale lock could interleave
unlink and publish such that both won (proven by a 20-way contention
test). Takeovers now go through a secondary create-if-absent lock
(install.lock.takeover): the delete+publish section only ever runs in
one process, staleness is re-validated inside it, and a fast-path creator
that wins the briefly-free path simply beats the takeover. The takeover
lock itself is age-swept (a live section lasts microseconds), and handles
only release the lock instance they own.

* fix: verify lock ownership after publish and preserve freshly staged exes

Two more race fixes from review:

- install-lock: the stale-marker sweep repeats the inspect-then-delete
  race one level up — two contenders sweeping the same aged takeover
  marker could both win and enter the main-lock section together.
  Pathname APIs offer no conditional delete, so both the takeover marker
  and the main lock now verify ownership after publishing (unique marker
  content, read-back compare): a racing sweep converts to a single
  survivor instead of two holders. The irreducible residual (a delete
  landing in the microsecond link-to-verify window) degrades to a wasted
  download cycle, never a corrupt install — swap claims guard the exe
  independently.
- native-swap: sweeping a stale swap claim deleted the exe it referenced
  even when a FRESH staged.json referenced the same version-derived name
  (a downloader re-staged the version after the swap crashed), throwing
  away a verified ~180 MB stage. The sweep now preserves any exe the
  current staged metadata still references.

* fix: reject mismatched manifests, take over from dead holders, unique .part names

Three robustness fixes from review:

- native-manifest: the per-release endpoint can answer with ANOTHER
  release's manifest (stale cache, mispublish); its checksums would then
  be applied to this version's binary and fail verification on every
  attempt. Compare the parsed manifest version with the requested one.
- install-lock/update-download: a killed lock holder skips its finally
  and never releases, stranding a waiting foreground `kimi upgrade`
  forever. A lock whose recorded pid is dead is now stale at any age
  (the atomic publish guarantees the pid was alive when written), and
  the same-version wait loop polls the acquisition itself, so a dead
  holder's lock is taken over within one poll instead of never.
  Package-manager spawns are unaffected: they hold the lock only around
  the spawn, and the active-record bookkeeping guards that layer.
- native-stage: the download intermediate is now unique per worker
  (`.part` carries pid + counter), so overlapping same-version workers
  can no longer interleave writes into the same file.

* fix: restrict staging cleanup to updater-owned names and retry short writes

- cleanupStagingOrphans recursively deleted anything it did not
  recognize; the staging dir sits next to the exe and can contain files
  belonging to the user or another tool. Deletion now requires a
  positive match on updater-owned artifact names (staged exes and .part
  intermediates) and only ever unlinks files.
- FileHandle.write may persist fewer bytes than requested (short write,
  e.g. near disk exhaustion) while the running hash and size already
  accounted for the whole chunk — publishing a truncated binary under a
  valid checksum. The chunk write now loops until fully persisted.

* fix: scope failure cleanup, recognize all semvers, reverify staged checksums

Three fixes from review:

- native-stage failure cleanup deleted whatever staged update was
  currently published — including a concurrent worker's valid result
  that its caller had already reported as success. The catch path now
  removes only this attempt's own artifacts: its unique .part file and
  its staged exe name when the current metadata does not reference it.
- The orphan-cleanup ownership check only matched stable x.y.z names;
  prerelease/build-metadata versions (1.2.3-rc.1, 1.2.3+build) would
  never be cleaned and accumulate ~180 MB each. Ownership now derives
  from the semver contract via the semver package's valid().
- The swap path trusted a staged exe whose size matched, though the
  metadata records the release checksum; post-download on-disk damage
  could pass the --version smoke check with corrupted bytes.
  claimStagedUpdate now re-verifies the staged exe's sha256 before
  claiming and discards the stage (for a later re-download) on mismatch
  — paid only when an update is actually pending.

* fix: validate versions before path derivation and honor the update opt-out in the swap

- native-stage: stageNativeUpdate derived staging paths (including the
  cleanup rm targets) from the version before fetchNativeReleaseManifest
  rejected it; a traversal string like `x/../../kimi` would resolve the
  staged-exe cleanup onto the running installation. The semver check now
  happens before any path is derived, and the staged-metadata schema
  constrains exeFileName to a plain file name.
- native-swap: the startup swap ran before the update preflight, so
  KIMI_CODE_NO_AUTO_UPDATE / KIMI_CLI_NO_AUTO_UPDATE stopped gating
  update behavior once a payload was pending. The swap now honors the
  same opt-out: the staged payload stays in place for a later launch
  without the variable, and the current exe starts.

* fix: restrict backup cleanup to updater-owned .bak names

cleanupBackups treated every <exe>.*.bak sibling as swap residue, so a
user's own backup like kimi.config.bak in a shared bin directory was
silently deleted on startup. Only the exact <exe>.bak and the numeric
PID fallback <exe>.<pid>.bak are updater-created — cleanup now
positively matches those two formats.

* fix: claim staged metadata before validating it and let manual upgrades bypass the opt-out

- native-swap: claimStagedUpdate validated the metadata and hashed the
  staged exe BEFORE the atomic rename, so a concurrent downloader
  superseding staged.json in between could get its fresh metadata
  claimed under the older object — the smoke check then failed and
  discard() deleted the newly published stage, recording a failure for
  the wrong version. The claim (utimes + rename) now happens first and
  validation acts on exactly the claimed file; discards use a new
  discardClaimedUpdate that never removes anything a meanwhile-published
  stage references.
- The auto-update env opt-out gated the startup swap unconditionally,
  so an explicit `kimi upgrade` with the variable set staged the
  version but no launch ever applied it. Stages now record
  `manual: true` when they answer a user-initiated install
  (`__update_download --manual`, threaded from installUpdate through
  the hidden sub-command), and the swap applies manual stages even when
  automatic updates are opted out.

* fix: promote adopted stages to manual and preserve claim-referenced payloads

Three follow-up fixes from review:

- An explicit `kimi upgrade` adopting an auto-staged payload (already
  on disk, or still downloading via the wait path) returned before the
  manual marker applied, so under the env opt-out the swap still skipped
  it despite the success message. Both adoption paths now promote the
  staged metadata to manual: true via a new promoteStagedUpdateToManual.
- The download-failure cleanup checked only the current staged metadata,
  but a live swap holds the metadata renamed aside as its claim — a
  failing same-version downloader could delete the exe an active swap
  was about to move into place. The catch path now also preserves names
  referenced by any live swap claim.
- Restoring a claimed stage after a failed exe move used rename, which
  on POSIX replaces a newer staged.json a downloader published during
  the smoke check. The restore is now a create-if-absent hard link: it
  only lands when the state-file path is still free, and the older claim
  is discarded when a newer stage has taken it.

* fix: drop exe deletion from stale-claim cleanup

The stale-claim sweep deleted the referenced exe based on a metadata
snapshot taken before the loop; a downloader republishing the same
version between the read and the unlink would have its fresh payload
deleted after reporting success. Publication can never be synchronized
with a pathname-level snapshot, so the sweep now removes only the claim
files themselves — genuinely unreferenced exes are reaped by the
downloader's own orphan cleanup (keep-set aware) before its next stage.

* fix: never delete the staged exe when discarding a claim

The same publication race existed one level down: a same-version
downloader can rename its fresh payload onto the shared exe path after
the discard's metadata snapshot but before the unlink (payloads publish
before their metadata), and the discard would delete a download whose
caller then reports success with nothing behind it. discardClaimedUpdate
now removes only the claimed metadata file; unreferenced exes are reaped
by the downloader's own orphan cleanup before its next stage.

* fix: only reap staging orphans old enough to be abandoned

The orphan sweep could delete a concurrent worker's freshly renamed
staged exe in the gap before its staged.json lands (payloads publish
before their metadata), turning the admitted duplicate-worker race into
a successful stage with no payload behind it. Unreferenced artifacts are
now only deleted once older than a one-hour grace period — publication
takes milliseconds, so unreferenced AND old means definitively
abandoned.

* fix: honor the persisted auto-update preference in the swap and drop claim-unsafe deletions

- The startup swap gated only on the env opt-out, so a payload staged
  automatically still installed after the user disabled automatic
  updates via [upgrade] auto_install = false. The swap now loads the
  persisted preference (only when an automatic stage is actually
  pending) and skips it, exactly like the env opt-out; manual stages
  still always apply.
- Superseding a staged version deleted its exe through an uncoordinated
  read-then-remove that could pull the payload from a live swap. The
  supersede now removes only the old metadata record — the metadata
  write atomically replaces it, and an unreferenced exe is reaped by a
  later orphan cleanup. removeStagedNativeUpdate, left with no callers,
  is removed.
- docs: the kimi upgrade reference (en + zh) no longer claims Windows
  native installations cannot upgrade automatically; native installs
  download and verify in the foreground and swap on the next start.

* fix: gate on claimed metadata, stop shared-path deletes on failure, exact smoke match

- The opt-out gate evaluated a pre-claim snapshot of the staged
  metadata, but the claim could pick up a different (automatic) stage a
  downloader published in between — smuggling it past the gate. The
  env/preference check now runs on the CLAIMED metadata; when disabled,
  the claim is restored via create-if-absent link so a newer stage is
  never overwritten and a later launch can still apply it. The checksum
  re-verify moves after the gates so opted-out launches stop paying for
  the hash.
- The download-failure cleanup still deleted the shared staged-exe path
  based on snapshot reference checks — the same publication race as the
  paths already fixed. It now removes only the attempt's privately owned
  .part file; the shared exe is left for the age-gated orphan cleanup.
- The smoke check accepted the staged version as a substring of the
  --version output, so a mispublished 1.2.30 binary would satisfy a
  1.2.3 target with a matching manifest checksum. It now requires the
  trimmed output to equal the staged version exactly.

* fix: confirm the manual marker before reporting stage adoption

promoteStagedUpdateToManual silently no-oped when a startup swap had
claimed the state file, while the adoption paths still reported success
with manual: true synthesized — under the env opt-out the restored
automatic metadata would then be skipped on every later launch despite
the upgrade's success message. The helper now verifies the marker with a
confirming read (one retry) and returns whether it persisted; the
already-staged branch falls through to a fresh stage when it does not,
and the same-version wait loop only adopts after a confirmed promotion.

* fix(cli): verify the staged payload digest before adopting it as already-staged

readStagedNativeUpdate checks only the recorded size, so a same-size
corruption after the download was adopted and reported as success, only
for the startup swap's claim-time re-verify to reject and discard it.
Compare the actual sha256 before returning already-staged; a mismatch
falls through and re-stages from the CDN.

* fix(cli): keep staged metadata until its replacement is ready

Two related races around staged.json, both reported against the
duplicate-downloader residual:

- stageNativeUpdate deleted the previous record before downloading its
  replacement; a pathname-only delete can remove a concurrent worker's
  freshly published record, orphaning a payload whose worker already
  reported success. The old record now stays until the final atomic
  metadata write replaces it.
- promoteStagedUpdateToManual wrote the marker unconditionally onto
  whichever generation owned staged.json. It now takes the adopted record
  and promotes only while the on-disk metadata still matches it, and the
  post-write confirmation requires the promoted candidate itself.

* fix(cli): preserve the exe referenced by the current staged record during orphan cleanup

Since the supersede path now keeps the previous staged.json until the
final atomic write replaces it, an aged staged exe is still the
applicable update while its replacement downloads — but
cleanupStagingOrphans only pinned exes referenced by swap claim files,
so a payload older than the grace period was unlinked out from under
its own record. Read staged.json itself in the pinning pass so the
current record's exe is preserved like any live claim's.

* chore(kimi-code): reword the native auto-update changeset

* chore(kimi-code): trim the native auto-update changeset

* fix(cli): support update locking on filesystems without hard links

link() fails with ENOTSUP/ENOSYS/EPERM on FAT/exFAT and some network
mounts, which aborted every native update before the download. Add a
shared createFileIfAbsent primitive (hard-link a fully written temp
file, falling back to an exclusive create + write) and use it for the
install lock, its takeover marker, and the swap's claim restore. The
fallback's create->write gap is observable, so the lock inspection now
grants young unparseable content a publish grace before sweeping it as
crash residue.

* fix(cli): publish staged exes under unique names and recover orphaned claims

Two related robustness fixes in the staged swap flow:

- A staged executable is now published under a unique per-worker name
  (kimi-<version>.<pid>.<epoch-ms>.<n>[.exe]) and never replaced; the
  atomic metadata write retargets the pointer. The pathname a swap
  validates at claim time can no longer be exchanged by a concurrent
  same-version publisher between validation and install.
- restoreClaimedUpdate only drops the claim when the restore landed or a
  newer stage holds the state-file path; transient failures retain it.
  The stale-claim sweep now restores aged claims (create-if-absent)
  instead of deleting them, so a stage orphaned by a dead swap or a
  transient restore failure is retried on a later launch.

* fix(cli): verify the staged payload digest in the lock-wait adoption path

waitForStagedUpdate relied on readStagedNativeUpdate, which checks only
the recorded size: while a holder re-stages a same-size-corrupted
payload (its metadata is replaced only when the repaired generation
publishes), a waiter could promote and report the corrupt stage as
downloaded, and startup would later reject its checksum. Apply the same
integrity bar as stageNativeUpdate's already-staged path — adopt only a
payload that hashes to its recorded checksum; a mismatch falls through
to the lock poll, which takes over once the holder finishes without
repairing it.

* fix(cli): serialize swap critical sections and preserve in-flight publishes

- The fresh-claim sweep is only a directory snapshot: two processes could
  both pass it before either claimed, then rename the same installed exe
  concurrently and delete each other's rollback backup. A create-if-absent
  swap mutex (swap.lock, age-gated like the takeover marker) now serializes
  the executable-renaming section; the loser restores its claim and defers.
  The mutex is released as soon as the new exe is in place, before the
  re-exec, so it is never held for the child session's lifetime.
- claimStagedUpdate no longer destroys a claimed record that is unparseable
  but was young at claim time: on filesystems without hard links the
  exclusive-create publish is observable mid-write, and discarding it would
  orphan the staged exe while the writer reports success. Such a record is
  put back with the same inode so the writer completes it; aged corrupt
  residue and well-formed records with a missing/changed exe are still
  discarded.

* fix(cli): keep backup cleanup inside the swap mutex

The early release let a subsequent swap rename the just-installed exe to
the shared .bak path while the previous swap's cleanup was still about to
unlink that same path, destroying the second swap's rollback source. The
mutex now covers the backup cleanup; the cosmetic staging-dir rmdir and
the re-exec stay outside it.
2026-08-18 17:17:18 +08:00