Commit graph

684 commits

Author SHA1 Message Date
liruifengv
d4e0ad4b2d
chore: sync web dist from code-app (#3166)
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 / Deploy docs (push) Blocked by required conditions
Release / Native release artifact (push) Blocked by required conditions
Release / Publish native release assets (push) Blocked by required conditions
Release / Release (push) Waiting to run
* chore: sync web dist from code-app

code-app: 8c911c08adf094509dea5655becd2896352a72a2

* chore: drop the rc device switcher changeset
2026-08-21 21:03:29 +08:00
liruifengv
491ebd050f
chore: sync web dist from code-app (#3157)
code-app: 9c669bd16983e6b734ca5e4102f91ebc9f34935a
2026-08-21 15:30:17 +08:00
Grapedge
54bb49e138
fix(kimi-code): keep the previous turn's answer visible when a cron turn ends (#3154) 2026-08-21 15:01:07 +08:00
qer
0f44537c13
feat(agent-core-v2): turn /tower into a mode parallel to plan mode (#3099)
* feat(agent-core-v2): turn /tower into a mode parallel to plan mode

* feat(agent-core-v2): align /tower command semantics with the original skill behavior

* fix(tui): harden the /tower command against mid-turn objectives, legacy engines, and stale status

* fix(agent-core-v2): keep restored tower state inert while the feature flag is off

* fix(agent-core-v2): include TowerInit in the mode tool overlay and report flag-gated tower state

* feat(agent-core-v2): expose tower control tools statically and make the mode injection history-derived

* fix(tui): warn that tower mode needs a restart after a live flag flip; drop redundant undefined from SessionStatus mode fields

* fix(agent-core-v2): let tower mode exit clear persisted state while the flag is off

* fix(agent-core-v2): emit the tower exit reminder through a disabled flag and drop the redundant REST state comparison

* fix(tui): show the Tower mode status row only when the experiment is available

* fix(agent-core-v2): confine tower mode to the main agent and always re-assert it for objectives

* feat(kap-server): project tower mode into transcript modes and reassert explicit toggles

* fix(agent-core-v2): fold the main-agent invariant into the effective tower state

* fix(kap-server): gate the cold tower mode badge behind the experiment flag

* fix(agent-core-v2): reapply the tower tool overlay after a profile bind; fix(kap-server): clear cold tower badges on non-main agents

* fix(protocol): mirror towerMode and tower_mode in the shared zod schemas

* test(agent-core-v2): adapt tower tests to the agent lifecycle context architecture

* chore(agent-core-v2): regenerate the wire manifest; test(node-sdk): look up the main agent via findAgentHandle

* fix(agent-core-v2): exit a replayed tower mode when the workspace belongs to another session

* fix(agent-core-v2): carry tower ownership in the enter record so forks clear inherited mode

* fix(agent-core-v2): apply the tower overlay before dispatching enter and repair it on status updates

* fix(agent-core-v2): validate store ownership before entering tower mode

* fix(agent-core-v2): claim the repository tower owner at enter; fix(tui): confirm mode activation before reporting success

* fix(agent-core-v2): make the first tower claim exclusive and refuse adoption over a live owner

* feat(agent-core-v2): guard tower adoption and teardown with a cross-process ownership lease

* revert(agent-core-v2): drop the cross-process lease and enter-time claim, keep ownership checks process-local

* fix(agent-core): hide the v2-only tower flag from the legacy experiments list

* fix(agent-core-v2): keep tower mode inert until the tower feature is assembled

A live /experiments flip refreshes the flag but cannot re-run App-scope
feature assembly, so the tower tools/profile stay unregistered until a
restart. Gate enter()/isActive on the assembly fact and say so in the
TUI error. Also resolve the AGENTS.md conflict block committed by the
merge, and widen the TUI experimentalFlag type to string now that flags
live in two registries.

* fix(kap-server): gate the cold tower badge on tower feature assembly

Same live-flip gap as the mode machinery: a persisted tower_mode.enter
plus a flag enabled without a restart would still show the badge while
the feature is inert. Require isTowerFeatureAssembled() alongside the
flag, re-exported from agent-core-v2.

* fix(agent-core-v2): liveness-aware tower entry and per-App assembly state

enter() now mirrors TowerInit's adoption rule: a stored owner blocks
entry only while that session is live in this process, so a new session
can enter the mode and reach TowerInit to adopt a stale tower. The
assembly marker is keyed by each App's flag service (WeakSet) instead of
process-global module state, so coexisting Apps no longer leak assembly
into one another.

* fix(agent-core-v2): keep stale-owner adoption across resume; reject tower updates that do not take

exitForeignTower now treats a stored owner as foreign only while that
session is live, so a mode entered by adopting a dead owner's tower
survives close/restart. The TUI validates the model prerequisite before
enabling tower for an objective, and the REST agent_config path throws
session.tower_mode_invalid when enter() did not take effect instead of
acknowledging a no-op.

* fix(agent-core-v2): clear the tower assembly marker when the feature unloads

Register the WeakSet cleanup via the feature's onDispose so the
capability follows the managed unit's lifecycle — after
unprovideUnit('tower'), isActive/enter() no longer treat the retracted
tool set as assembled.

* fix(agent-core-v2): publish tower deactivation on gate loss; reject refused setTowerMode in the SDK

isActive now reconciles its projection: restore and feature-manager unit
changes publish AgentStatusUpdated({ towerMode: false }) when the
persisted mode lost a gate (flag off at runtime, feature unloaded), so
live transcript badges and TUI state stop showing an inert mode. The
SDK's setTowerMode(true) verifies the effective state and throws
session.tower_mode_invalid, matching the REST path.

* fix(agent-core-v2): reconcile tower projection on config changes and cold ownership moves

The last unreconciled gate inputs: a live setConfig writing the
experimental section (no session reload, no units event) now republishes
towerMode:false through the config-change subscription, and the cold
transcript badge mirrors the same ownership/liveness rule as enter() —
retained while the store owner is this session or no live session, cleared
once a live session elsewhere owns the tower.

* fix(agent-core-v2): reconcile the tower projection in both directions

The OFF-only reconcile left a hole: re-enabling the flag in the same
process made isActive true again with no towerMode:true publish, and
enter() could not heal it (it early-returns when already effective).
The projection now tracks the last published state and emits both
false→true and true→false transitions from the same restore/units/config
triggers; direct publishes (enter/exit/restoreTowerTools) keep the
tracker in sync.

* fix(agent-core-v2): validate forked tower ownership even while the flag is off

exitForeignTower's flag short-circuit let a fork restored while the
experiment was disabled keep its inherited enter record; re-enabling the
flag later revived both source and fork over the same store. Ownership
validation is flag-independent state hygiene, so restore now runs it
regardless of the effective flag — a live foreign owner clears the
fork's persisted mode before any gate can rise again.

* fix(agent-core-v2): veto tower tools while the tower experiment is off

With the feature assembled and the tool overlay active, disabling the
flag live left TowerInit/TowerTeardown callable — they have no flag
check — so prompts could still mutate or dismantle .tower/ while the
experiment reported disabled. A dedicated onBeforeExecuteTool hook now
denies every tower tool whenever the flag is off, mirroring the TodoList
veto.

* fix(agent-core-v2): keep tower worker write isolation when the flag turns off

The worker Write/Edit guard is identity-scoped (tower-worker profile),
not feature-activity-scoped: disabling the experiment live must not let
already-spawned detached workers write into the main checkout or other
worktrees. The guard no longer checks the flag; the tower-tool veto
added earlier covers protocol access instead.

* test(agent-core-v2): make tower tests hermetic for CI

Three CI-only failures: towerService git fixtures committed without a
repo-local identity (CI has no global gitconfig), the node-sdk tower
positive tests relied on the developer shell's
KIMI_CODE_EXPERIMENTAL_FLAG=1 master switch instead of enabling the
tower flag explicitly, and the legacy harness experimental-features
expectation still listed the tower entry removed from the v1 registry.
2026-08-21 14:47:37 +08:00
liruifengv
3090c1c482
chore: sync web dist from code-app (#3152)
Some checks are pending
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
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 / Publish native release assets (push) Blocked by required conditions
code-app: a46821efc62fda06230f5100560aa7dda6058f14
2026-08-21 11:31:35 +08:00
github-actions[bot]
0999454bdc
ci: release packages (#3074)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-08-20 21:11:45 +08:00
liruifengv
2c5415f930
chore: sync web dist from code-app (#3135)
code-app: 6c99acc282b048208e457572309c808f6aeb00d5
2026-08-20 21:09:01 +08:00
Haozhe
fc0f275dd4
refactor(agent-core-v2): extract goal domain into a self-contained feature (#3130) 2026-08-20 19:52:57 +08:00
Haozhe
3fdce983f8
fix: drain in-flight persistence and log writes on session close and shutdown (#3122) 2026-08-20 17:19:20 +08:00
liruifengv
97c2159791
feat(kimi-code): load startup banner from client_configs (#3117)
The startup banner now comes from the backend client_configs endpoint
(config name client_banner) instead of the CDN-hosted tips.json, and is
fetched fresh on every startup with no caching. The payload keeps the
tips.json shape, plus two targeting additions:

- banner_platform (top level and fallback entries) limits display to
  the given platform; missing, empty, or all means every platform, and
  the CLI only shows entries targeting all or cli.
- banner_start_time/banner_end_time on fallback entries add scheduled
  visibility windows with the same semantics as the active banner.
2026-08-20 16:54:55 +08:00
Haozhe
a09d904140
refactor(agent-core-v2): migrate agent domains to model-as-container architecture (#3103) 2026-08-20 16:06:59 +08:00
Grapedge
cfc3350483
feat(kimi-code): collapse long ! shell command output (#3054)
Finished `!` output collapses to the first 10 visual rows with a `... (N more lines, ctrl+o to expand)` marker, sharing the global ctrl+o toggle with agent tool output; ctrl+o also expands the live buffer while the command runs. Replayed output mounts the same card and behaves identically. The shared truncation component, the running card's default view, and the agent bash path are unchanged.
2026-08-20 14:55:38 +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
liruifengv
3d7762003a
feat(kimi-code): support two OAuth login endpoints (#2862)
* feat(kimi-code): add China/International region selection for OAuth login

- Add region profiles (cn/overseas) and resolver in @moonshot-ai/kimi-code-oauth:
  env override → persisted login host → install-channel marker → default cn
- /login now offers Kimi Code (China) / Kimi Code (International); the CLI
  login entries (kimi login, kimi acp --login) accept --region cn|overseas
- Update/plugin/site/telemetry endpoints derive from the selected region;
  plugin trust list covers both .com and .ai hosts
- kap-server: POST /oauth/login accepts an optional region; new GET /oauth/region

* fix(oauth): keep an explicit default-slot login ahead of the install marker

A China login persists no oauthHost (the default credential slot carries
no host trace), so after switching back from International the resolver
fell through to a stale overseas install marker. Treat a persisted
default-slot oauth ref (key === oauth/kimi-code) as an explicit-cn signal
that outranks the marker; getRegion() on the v2 side mirrors it.

* fix(agent-core-v2): thread the default-slot key through capability region resolution

Capability installs resolved the region from the persisted oauthHost only,
so an explicit China login (which persists no host) lost to a stale
overseas install marker. Pass the oauth ref key through as well, matching
getRegion(). Also move the region contract notes into the auth.ts file
header per the package comment convention.

* fix(agent-core-v2): honor the region-marker opt-out for the telemetry endpoint

Hosts that set KIMI_CODE_REGION_MARKER=off (the desktop embedded server)
skip the install marker in getRegion(), but the default telemetry endpoint
still consulted it, so a stale overseas marker could split the reported
region from the telemetry destination.

* feat(cli): show region site domains in login platform selector

* chore: reword oauth login changesets

* fix: honor the region marker opt-out in the CLI and capability resolvers

* refactor: rename login region values to mainland-cn and global

* fix: keep the --region help text in English

* fix: simplify the --region help text to site domains

* feat: drop the suggested login platform order

* feat: split a browser-safe region profile table out of the region resolver

* Revert "feat: split a browser-safe region profile table out of the region resolver"

This reverts commit a037b1143e.

* fix: read the install marker from the bootstrapped home directory

* fix: resolve the server plugin marketplace from the active login region

* feat: expose the login region option through the klient auth facade

* fix: drop a comment from the v2 auth region test

* fix: keep scoped base-only logins on their environment for a bare login

* fix: invalidate the region cache on the provider-manager logout path

* fix: route client-config fetches through the active region profile

* fix: resolve the telemetry endpoint per flush so a login region switch applies in-process

* test: expect the telemetry endpoint resolver in the CLI init assertions

* fix: resolve the default telemetry endpoint from the bootstrapped home

* chore: reword the oauth login changeset around the two login methods

* chore: trim the oauth login changeset to the headline

* feat: let hosts override the region marker env through the server bootstrap env bag
2026-08-20 11:24:02 +08:00
Haozhe
16499408d5
refactor(agent-core-v2): extract externalHooks into a scope-organized feature (#2805)
Move the external hook services out of app/externalHooksRunner,
session/externalHooks, and agent/externalHooks into
features/externalHooks, assembled as the ExternalHooksFeature unit:

- services live under per-scope subdirectories (app/, session/, agent/);
  shared pure helpers (types, hook matching/dispatch, process spawn,
  prompt result rendering) live under internal/
- the runner and the two observers are contributed through the Feature
  seams (ScopeUnits materialization); the hooks config section stays on
  the static import=register channel
- update the package entry leaf exports, the plugin domain imports, the
  kap-server events-zod import, and the affected tests; regenerate the
  state manifest
2026-08-19 16:52:51 +08:00
Rick
35befdcef2
fix(vscode): multi-select question jumps to next after only one answe… (#3079)
* fix(vscode): multi-select question jumps to next after only one answer selected

* chore: add changeset

---------

Co-authored-by: gaoyuan <gaoyuan@moonshot.ai>
2026-08-19 16:27:07 +08:00
Luyu Cheng
01eeacb59b
feat(kimi-code): specialize the WaitFor tool's transcript display (#3066)
* feat(kimi-code): specialize the WaitFor tool's transcript display

* feat(agent-core-v2): emit status progress while WaitFor is pending

* fix(kimi-code): route WaitFor dimming through the TUI theme

* feat(kimi-code): support replaceable status updates in tool progress

* fix(kimi-code): forward status progress to subagent activity surfaces

* fix(agent-core-v2): drop the redundant undefined from ToolUpdate.replace

* fix(kimi-code): honor replace semantics in the subagent live status path

* test(agent-core-v2): drive the WaitFor progress test through a manual tick

* fix(kap-server): mirror ToolUpdate.replace in the ws event schema

* refactor(agent-core-v2): expose the WaitFor progress scheduler as a public seam

* fix(kimi-code): pass child wait statuses without the trailing newline

* feat(agent-core-v2): tick the WaitFor progress status every second

* feat(agent-core-v2): format WaitFor progress durations as 1m 15s

* feat(agent-core-v2): omit zero seconds and minutes in WaitFor durations
2026-08-19 14:15:38 +08:00
github-actions[bot]
c41fadf0f7
ci: release packages (#3062)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-08-19 01:37:58 +08:00
qer
5c661f4610
chore: sync web dist from code-app (#3061)
code-app: 221c548c98587fa1bd2aef458e1e20aaceece587
2026-08-19 01:33:29 +08:00
github-actions[bot]
1e553fc73b
ci: release packages (#3049)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-08-18 22:29:43 +08:00
7Sageer
c9c34ae5a8
fix(kimi-code): upload pasted videos to the daemon file store (#3047)
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
* fix(kimi-code): upload pasted videos to the daemon file store

Video paste staged a cache copy and submitted a bare file:// video_url,
which the v2 engine no longer resolves, so the submission failed and the
persisted history retried it on every turn. Mirror the image flow
instead: upload the paste to the daemon file store in the background and
submit a kimi-file:// reference that the engine's prompt intake
materializes. A video whose upload is still in flight, failed, or
expired now refuses the submission with an actionable error, since video
bytes have no inline fallback form.

* test(kimi-code): fix MessageDriver recallStashedMedia signature
2026-08-18 20:52:27 +08:00
github-actions[bot]
04944f380a
ci: release packages (#2932)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-08-18 19:19:18 +08:00
liruifengv
e31b3a335e
chore: sync web dist from code-app (#3043)
* chore: sync web dist from code-app

code-app: 1fb57f0ee3c424675d8768dcc70e56f5c953cd9f

* chore: sync web dist from code-app

code-app: 93da508d079b0118cc0338da97dcb738f0a3d46f

Adds the web session admin page. Built from the code-app PR #261 branch tip before its merge; the squash-merged main tree is expected to be identical (will be checked at merge time and rebuilt here if not).

* chore: correct the code-app trailer of the previous sync commit

The previous commit's trailer had a mistyped code-app SHA. The dist content is byte-identical to a build of code-app main at the merge below (tree verified identical to the branch tip it was built from), so the watermark for the next sync is:

code-app: 025805b33f1e87a4bc479574971c6177ad1403a0
2026-08-18 19:15:01 +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
bj456736
d3150fe947
chore: remove internal-network references from comments and test fixtures (#3029)
* chore: remove internal-network references from comments and test fixtures

- Reword two comments that named the internal free-tokens model
  registration flow; the generic OAuth / managed wording carries the
  same meaning
- Replace the qianxun.example placeholder base URL in google-genai and
  runtime-provider tests with genai-gateway.example
- Swap realistic-looking LAN fixture IPs in the kimi web banner tests
  (192.168.98.66, 10.8.12.216) for RFC 5737 documentation addresses
  (192.0.2.66, 198.51.100.216)

* chore: retrigger CI (flaky kap-server searchRoute title-indexing test)

---------

Co-authored-by: bj456736 <bj456736@users.noreply.github.com>
2026-08-18 14:14:56 +08:00
Haozhe
98ebda840a
fix(kimi-code): revert the todo panel to its pre-turn state on undo (#3016)
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
* fix(kimi-code): revert the todo panel to its pre-turn state on undo

* fix(kimi-code): hide all-done todo lists on undo refresh and detach SDK todo state
2026-08-18 12:00:19 +08:00
Haozhe
8267bb8fce
feat(kap-server): add workspace fs:suggest file completion endpoint (#3019) 2026-08-18 11:53:48 +08:00
bj456736
09976b0914
feat(cli): add --web-title and expose it via /meta (#2989)
* feat(cli): add --web-title and expose it via /meta

* refactor(kap-server): pass optional web_title directly in /meta

Per the repo rule for optional object properties, pass undefined directly
instead of a conditional spread; serialization omits the unset value.

* fix(cli): sync web bundle with instance tab title support

The committed dist-web bundle predates the document title feature, so a
released `kimi web --web-title` served a client that never read web_title.
Rebuilt from code-app (feat/web-document-title) via sync:web; the bundle
now titles tabs from web_title or the active workspace directory.

* ci: retrigger checks after flaky harness cleanup failure

---------

Co-authored-by: wbxl2000 <wbxl2000@outlook.com>
Co-authored-by: bj456736 <bj456736@users.noreply.github.com>
2026-08-17 20:19:15 +08:00
Haozhe
2265305e81
refactor(agent-core-v2): replace defineOp/Model with Event2 dispatch and replayable states (#2909)
* refactor(agent-core-v2): replace defineOp/Model with Event2 dispatch and replayable states

- replace defineOp/Op/OpDescriptor/toEvent and defineModel/defineCheckpointedModel with Event2 subclasses: durable classes declare static durable + schema, serialize() keeps the wire record shape byte-frozen, transient classes stay off the journal
- define states via defineState(...).replayable(...).on(Event2, fold): immer produceWithPatches folds with atomic prepare/commit, .undoable() trait drives prompt-submit checkpoints and context.undo, ephemeral kv keys keep imperative set
- degrade IWireService to a journal adapter; the agent event dispatcher owns the pipeline (fold -> set -> appendRecord -> publish) and silent restore
- align downstream event surfaces: kap-server WS envelope timestamp from event.time, klient event schemas gain time, node-sdk/acp-server/print wiring updated
- rewrite gen-wire-manifest/gen-state-manifest for the unified registry and replace the op-uniqueness lint with event-uniqueness

* fix(ci): repair Event2 prompt and media projections

- restore prompt admission and session media materialization
- align transcript, WS, SDK, and replayable media state projections
- update affected tests and generated state manifest

* fix(ci): update prompt event and projection expectations

- update snapshots for the durable prompt.accepted event
- normalize prompt.steered media in transcript projections
2026-08-17 17:38:50 +08:00
7Sageer
ee55c4d523
fix(kimi-code): persist pasted-image originals into the session dir at dispatch (#2993)
* fix(kimi-code): persist pasted-image originals into the session dir at dispatch

Paste-time original persistence ran before the session existed on a
fresh TUI, so the compression caption baked a shared temp-dir path the
OS can reap. Keep the pre-compression bytes on the attachment in memory
and let dispatch-time caption resolution (sendMessageInternal,
steerMessage, runInlineSkillActivations) write them into the session's
media-originals dir — owned by the session, cleaned up with it, immune
to OS temp reaping.

* fix(kimi-code): harden pasted-image original lifecycle

Address review feedback:

- carry the pre-compression original in the resend snapshot so a
  cache-hint "new session" resend still persists it into the new
  session's originals dir and authors the compression caption
- release the in-memory original bytes once persistence succeeds,
  keeping only the metadata the caption needs
- apply the same 1 GiB mtime-bounded eviction to the sync originals
  store as the engine's async twin

* fix(kimi-code): keep compression captions consistent with the sent image

Address review feedback:

- author a caption only when the image part still matches the
  attachment's current state, so a paste whose ingestion landed after
  extraction (inline pre-compression fallback) is not described as
  downsampled
- leave the original's path unset when persistence fails so a later
  dispatch retries the write instead of dropping the original for good

* fix(kimi-code): keep staged media across lazy session creation

setSession() released ALL staging leases on the assumption that they
belong to the session being replaced. On the lazy first-creation path
there is no previous session: the outstanding lease belongs to the new
session's first prompt, whose dispatch continues right after. The
premature release deleted a pasted image's daemon upload before the
engine's intake could read it, so the model only received
'[image omitted: the uploaded file is no longer available]'.

Gate the release on actually replacing a live session; shutdown and
explicit close keep their own releaseAll().

* Delete .changeset/lazy-session-staging-lease.md

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

* Delete .changeset/pasty-image-originals-session-dir.md

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

---------

Signed-off-by: 7Sageer <sag77r@hotmail.com>
2026-08-17 15:04:52 +08:00
7Sageer
59dde734f3
feat(agent-core): unify the v1 MCP management plane (#2858)
* feat(agent-core): unify the v1 MCP management plane

- McpServerRegistry: one config view over global (layered mcp.json),
  plugin (manifests, read-only, final effective config), and caller
  (SDK-injected) servers; name collisions keep both entries.
- Write plane: add/update/removeGlobalMcpServer mutate the user-level
  file and push into live sessions; getGlobalMcpServer returns the
  effective config; mutations of read-only entries are rejected.
- testGlobalMcpServer accepts an inline config; addSessionMcpServer
  connects a server in one live session with an optional persist flag;
  reconnect accepts a replacement config and re-resolves via the registry.
- One process-wide McpOAuthService shared with every session: obtained_at
  stamps, offline token state, single-flight and proactive refresh, and
  credential events. Sessions self-subscribe in the constructor, so even
  initializing sessions see every event; token writes serialize through
  the process-local OAuthTokenTransaction per credential identity.
- inspectAppMcpServers + locator-addressed begin/complete/cancel/reset
  cover plugin servers; inspection output redacts env/headers to sorted
  key lists; locator OAuth ops reject ambiguous shared runtime names.
- The legacy auth-status surface reads the registry (offline by default,
  verify=true probes) and never mutates credentials.
- VS Code panel receives source/origin/mutable and hides mutating
  actions on read-only entries.
- v2 client facade in node-sdk mirrors the surface over agent-core-v2
  (plugin inventory stays v1-only for now).

* fix(agent-core): close the v1 MCP live-session reconciliation gaps

Recompute each live session's MCP target from the registry's runtime
resolution (enabled plugin > project layer > user file; caller injection
shadows everything) behind every config mutation, instead of per-path
patching: shadowed file layers recover when a plugin winner is disabled or
removed, removing a user-level entry resurrects its project-layer shadow,
disabled plugin descriptors no longer block removals, persisted session
adds validate against the session's project layer and broadcast to other
live sessions, and per-session sync failures are logged with context.

Session status entries and read-only management entries now report
redacted config views (envKeys/headerKeys instead of literal env/headers
values); core-internal reconciliation compares full configs via the
connection manager's raw-entry accessor.

OAuth: interactive flows are serialized per credential (concurrent
begins join the in-flight flow instead of clobbering its PKCE/state), a
malformed credential meta sidecar no longer aborts core start, grants
inside the refresh-ahead window refresh immediately while far-future
grants re-arm through a max-length timer, and the service shuts its
timers and flows down with KimiCore/SDKRpcClient close.

* fix(agent-core): route the proactive MCP OAuth refresh through the token transaction

 refreshNow ran its /token request with the SDK default fetch, outside the
 credential-serializing OAuthTokenTransaction that every other token write
 uses; a slower response carrying an older rotating refresh token could
 overwrite a newer grant written by a concurrent transport-side refresh.

* fix(agent-core): keep disabled MCP servers out of auth-state classification

The unified mcpServerAuthState dropped the previous enabled short-circuit,
so a disabled oauth-flagged server reported oauth-required — or was even
probed over the network — instead of not-applicable.

* fix(kimi-code-sdk): short-circuit disabled MCP servers in the v2 auth-status classifier

The v2 parity copy of v1's mcpServerAuthState missed the same enabled
guard v1 just regained; a disabled oauth-flagged entry would report
oauth-required (or be probed). The parity suite now pins the disabled
case on both engines.

* fix(kimi-code): refresh the VS Code MCP list with the workspace cwd after mutations

The add/update/remove RPCs return a cwd-less management list, so the
webview broadcast dropped project-layer entries until the next full load;
re-list with the workspace cwd after every mutation instead.

* fix(agent-core): keep SDK token saves matched to the OAuth token transaction

saveTokens stamped obtained_at onto a fresh object before calling
tokenTransaction.save, so it never matched the exact payload the
transaction recorded for a grant fetch; the consume path was dead and
every save re-wrote. Between the fetch and the SDK callback an intervening
clear could then be overwritten — the resurrected grant came back after a
reset. The write callback stamps the durable record instead.

* fix(agent-core): reject ambiguous legacy name-based MCP auth lookups

The legacy begin/reset auth RPCs took the registry's first name match,
silently starting OAuth for one entry of a runtime-name collision while
the locator path refused the same ambiguity; align them on the shared
uniqueness rule and point callers at the locator-addressed variants.

* fix(agent-core): propagate registry errors during live-session MCP sync

resolveMcpRuntimeTarget collapsed every registry failure into "no target":
a project config file that turned malformed mid-session made sync treat a
still-configured server as gone (tearing down the live connection) and
made config-aware reconnects report "no longer configured" instead of the
actionable config error. Absence still resolves to undefined; malformed
config now propagates — per-session sync logs and keeps the entry, and
reconnect surfaces config.invalid.

* fix(agent-core): close the remaining registry-error and ambiguity gaps

The management guard lookup mapped every registry failure to "absent",
so a malformed project config let a persisted session add write a
user-level entry over an unknown state; only not-found is a miss now. And
the name-only connection test now shares the auth paths' uniqueness rule
instead of probing the first match of a runtime-name collision.

* fix(agent-core): probe the enabled MCP entry under a disabled-name collision

The name-only connection test counted enabled matches for its ambiguity
guard but still probed the first registry match, and the file layers list
before plugins. With a disabled file entry shadowing an enabled plugin of
the same runtime name, Test probed the disabled entry instead of the one a
live session would run. Select the sole enabled match, falling back to the
first entry only when every match is disabled so it reports as disabled.

* fix(agent-core): let session-local MCP adds shadow plugin entries

Caller injection shadows every registry source at session start, plugins
included, and reconciliation leaves caller entries untouched; the live
non-persist add path rejected plugin-owned names anyway, so SDK clients
could not apply the same per-session override without a restart. Gate the
plugin-source rejection on persist: session-local adds connect as caller,
while persisted adds stay rejected as user-level writes behind a read-only
owner.

* fix(agent-core): normalize session MCP names before connecting

The persisted store trims server names, but addSessionMcpServer used the
raw name for the live connect and cross-session reconciliation: a padded
name persisted under the trimmed key while the requesting session ran and
reconciled the raw one, and a blank name connected with no identity at
all. Normalize once up front (rejecting blank) so the store write, the
session entry, and reconciliation agree on the same server.

* fix(agent-core,node-sdk): close the collision-selection and probe-freshness gaps

The legacy name-only auth resolver started from the first registry match,
so a disabled file-layer shadow plus an enabled plugin of the same runtime
name was misread as an ambiguity conflict; select the sole enabled match
before judging ambiguity, exactly like the test probe path. On the v2
client, addSessionMcpServer connected the raw name while the store wrote
the trimmed key — normalize once for both, and route the verify-triggered
auth probes through the per-call OAuth service instead of the cached one
whose providers snapshot tokens at construction, so a grant saved after
the first probe is honored.

* fix(agent-core): normalize global MCP mutation names and guard disabled reconnect swaps

The global add/update/remove mutations guarded and reconciled with the raw
server name while the store persisted the trimmed key, so a padded name
left live sessions unreconciled and could slip past the plugin read-only
guard; normalize once before lookup, persistence, and reconciliation. And
a config-carrying reconnect assigned the replacement before the disabled
check fired, leaving a connected entry that reported the disabled config;
reject disabled replacements before mutating, keeping the same error.

* fix(agent-core): skip proactive refresh while an interactive flow owns the credential

refreshNow reset the shared provider's flow state before and after the
token request; when a proactive timer (or a manual refresh) fired while
beginAuthorization was waiting on the browser callback for the same store
key, that wiped the redirect URL, PKCE verifier, and state the in-flight
flow needed — complete() then failed the exchange even though the user
authorized. Refresh now skips when an interactive flow is active for the
credential: the flow delivers fresh tokens on completion, and the 401
transport path is the backstop if it fails.

* fix(agent-core): allow global MCP adds over disabled plugin descriptors

A disabled plugin entry is absent from the runtime target, but the
read-only guard still treated it as the owner, so a user-level fallback
could only exist if it predated the plugin disable. Relax the shared
guard: disabled plugin descriptors never block mutations (disabled
project entries still shadow the user file and keep their rejection).

* fix(node-sdk): close the v2 session-MCP parity gaps

A v2 reconnect with an explicit enabled:false replacement config used
connect()'s upsert semantics — closing the live client and reporting
success where v1's manager reconnect rejects before applying anything;
reject disabled replacements up front with the same error. And a persisted
v2 session add never consulted the workspace config, so a same-named
project-layer entry was silently shadowed: the user-level write never
takes effect while the direct workspace-manager upsert displaces the
project config for every live session. Resolve the workspace layers and
reject like v1's read-only rule.

* fix(agent-core): keep __proto__-named MCP servers through config parsing

A z.record() parse rebuilds its output via property assignment, so a
server literally named __proto__ hit the prototype setter and vanished
before validation; the layer merge then repeated the same trap with plain
object accumulators. Parse the server map entry-by-entry over the JSON own
keys and accumulate into null-prototype maps, so session startup and the
unified registry keep the declared server and its origin.

* fix(node-sdk): begin v2 MCP auth against a fresh OAuth service

The v2 begin path ran through the cached globalMcpOAuth, whose providers
snapshot tokens at construction: a grant another process saved (or reset)
after that cache materialized was invisible, so begin could open a browser
flow over a valid grant, or report already-authorized off a removed one.
Build the service per call — the read path and the verify probes already
do — and route the status list through the same helper. The test fixture
grows a real token endpoint honoring one rotating refresh token; the
regression fails against the cached-service implementation on v2.

* fix(agent-core): broadcast SDK-driven MCP token invalidations to live sessions

* test(agent-core-v2): give the no-op reconnect test runtime plumbing

The branch added the case against a bare McpConnectionManager, but #2961
made stdio connects resolve the runtime through runtimeResolver, matching
every other case in the file.
2026-08-17 13:19:51 +08:00
7Sageer
d833a1a893
feat: engine-native image references via kimi-file:// media resolver (#2593)
* feat: engine-native image references via kimi-file:// media resolver

* fix(agent-core-v2): regenerate state manifest for media resolver rename

* feat(agent-core-v2): add audio MediaKind and tag/ref fold helpers to media ref contract

* fix(agent-core-v2): synthesize image path tag when degrading bare file references

* fix(agent-core-v2): scrub dangling alias re-exports in contract type generator

* feat(transcript): project paired media tag+ref as single attachments in read models

* fix(kimi-code): fall back to inline image when cache write fails after upload

* fix(agent-core-v2): pair media path tags with refs by adjacency and path, keep unpaired tags

* fix(kap-server): fold media tag+ref pairs out of prompt snapshot projection

* fix(kap-server): list attachment-only prompts as empty user messages

* fix(kap-server): keep live attachment ids across transcript overlay and heal

* fix(kap-server): keep promptAttachments off the legacy session event wire

* fix(kap-server): inherit the backfilled turn header on mid-turn terminal projection

A projector that attached after turn.started built the terminal turn.upsert
with an empty header, and the whole-header replace downstream wiped the
backfilled origin / prompt / attachmentIds — only the debounced best-effort
heal could restore them. Fall back to the producer store's seeded header
(via a new optional ProjectorLookups.turn) when currentTurn misses, and
cover the mid-turn attach path with a service-level regression test.

* refactor(agent-core-v2): move media ref contract out of kosong into agent/media

The kimi-file:// daemon reference grammar, media path tags, and the tag/ref
fold are engine-internal conventions, not provider-wire contract; keep
src/kosong untouched. Root exports and SDK re-exports are unchanged.

* feat(agent-core-v2): materialize prompt media into the session media dir

Pasted and uploaded media now materialize under the session's own media/
dir instead of the shared cache, so the copies follow the session's
lifecycle: fork carries them along, session deletion cleans them up.

A new Session-scope ISessionMediaStore owns the dir: atomic tmp+rename
materialization with a unified extension policy, and canonical-vs-hint
display-path resolution. The persisted ?path= is a write-time snapshot —
readers prefer the session-canonical location, so fork and home relocation
never hand the model a dead path. Prompt intake normalizes every daemon
reference through the single enqueue funnel (REST edge, SDK prompt/steer,
gateway), serialized in arrival order to keep the FIFO across the async
file I/O. The kap-server edge materializes through the same store with a
shared-cache fallback, and the request-time resolver refreshes stale
persisted and memoized path tags; a claimed video reference degrades to
its tag alone instead of duplicating it.

* fix(agent-core-v2): take prompt media intake off the enqueue critical path

The record now joins the FIFO synchronously and its daemon-ref intake runs
as a per-record promise, awaited by the launch and steer paths before the
message is consumed — queue order, list/abort visibility, and prompt
submission latency no longer wait on file I/O, and a slow intake no longer
head-of-line blocks later prompts. The launching record is tracked so abort
and clear stay reachable inside the launch window; startNext re-checks
cancellation after every await (intake race, hook, turn admission), a
cancelled record is never re-queued, and a compaction requeue waits for
onDidFinishCompaction instead of busy-looping the scheduler.

* fix(agent-core-v2): record the claiming ref in the media path-tag pairing

pairMediaPathTagRefs now exposes claimingRefByTagIndex, and claimingRefIndex
reads it instead of recovering the claimer by path equality — which
mis-attributed a tag when two different fileIds carried the same path in an
interleaved sequence, breaking the pair and leaking the tag as user text.
Also covers the memoized-video-tag claimed-drop branch.

* fix(transcript): fold upload pairs in user-slash turns and pin pairing parity

The cold rebuild's user-slash branch now folds the turn-opening input like
any user turn (claimed tag out of the prompt text, one attachment entity),
matching the live projection. The ref extraction is consolidated into the
contract module (daemonFileRefFromPairingPart, the mirror of the engine's
daemonFileRefFromPart) and the mirror carries the new claimingRefByTagIndex
map. A new kap-server parity test imports both implementations and asserts
identical pairings over shared fixtures, so the engine/mirror pair can no
longer drift silently.

* fix(kap-server): fold upload media tags out of the search index

The global search indexer concatenated every text part of a persisted user
message, so the upload pair's <image path> tag made pure-image prompts
searchable and wrote the materialization path into the index — breaking the
module's documented pure-image invariant and diverging from the live route.
textOfContent now folds the pair like every other read model (with a
fold-safe coercion for malformed wire parts). Also pins the prompt-media
cache-dir fallback with a read-only session media dir test (skipped as root).

* feat(node-sdk): re-export the media fold helpers and cover the v1 uploadFile rejection

foldMediaPathTagRefs and matchSingleMediaPathTag join the daemon
file-reference helper re-exports so hosts can fold the upload tag+ref pair
without importing agent-core-v2; the v1 harness's uploadFile not_implemented
rejection is pinned by a test.

* fix(kimi-code): fold upload pairs in replay/export and keep media tags atomic in steer input

Resumed-session replay rendered the upload pair raw — the <image path> tag
as user text and the kimi-file:// url as an XML-ish reference — and the
markdown export leaked the tag into both the turn body and the overview
topic. contentPartsToText and the exporter now fold the pair, and daemon
references render as a bare [image]/[video] placeholder. combineSteerInput
moves to tui/utils/steer-input and no longer merges a standalone media tag
into adjacent text, which would have broken the engine-side pairing for
steered image messages.

* fix(kimi-code): drop the steer separator before a leading media tag

A queued pure-image message opens with a standalone `<media path>` tag,
which combineSteerInput keeps atomic. With the previous item ending in a
media part, the '\n\n' separator landed as a stranded whitespace-only text
part between the media part and the tag, normalizePromptInput rejected the
steer, and the already-cleared queue lost the messages. Treat a leading
standalone tag as media so the separator is dropped there.

* fix: clean staged media lifecycle

* refactor(agent-core-v2): narrow the mediaRef root exports and drop a deprecated alias

* fix: keep staged media alive through turn

* fix(agent-core-v2): reject non-upload ids at the session media store

A daemon reference's fileId becomes a storage key in the session media
store, but only the file domain validated the id shape — a crafted
kimi-file://<id> reaching the request-time resolver's canonical-read
fallback could traverse out of the session media dir. Share the file
domain's id regex and guard every store entry point: reads miss,
materialize declines, and the display path falls back to the hint.

* fix(kap-server): project steered prompt content without leaking daemon refs

prompt.steered published the raw engine content parts — kimi-file://
refs carrying the absolute materialization path plus the paired
<media path> tag — to both the legacy session_event wire (whose schema
declares the protocol content shape) and the transcript prompt entity.
Route both through one shared prompt-content projection: the upload
pair folds into a single {kind:'file'} part, matching the REST prompt
list and the no-path-leak rule every sibling surface already follows.

* refactor: align daemon-ref naming and drop a duplicate re-export

The deprecated videoResolverService alias also re-exported
mediaResolvedKey, which made the package root's star exports ambiguous
and silently dropped the name. The new transcript contract mirror now
uses the canonical daemon-ref vocabulary instead of the deprecated
kimi-file spelling.

* test(agent-core-v2): pin image abort rethrow, video canonical read-through, release-once

Mirror the video abort contract on the new image path (an aborted read
cancels the request instead of degrading to a tag), cover the video
fallback that uploads the session-canonical bytes after the transient
upload is released, and assert the staged-upload release fires exactly
once on the intake success path.

* fix(kimi-code): bind goal-steer staging leases to the running turn

sendMessageInternal read the turn context only after beginSessionRequest
had cleared it, so a steer buffered into a running goal turn never got
its staging lease bound — the staged daemon upload and cache copies
lived until session close instead of being released at the consuming
turn's end. Capture the live turn id before the reset (only while a
turn is actually streaming; the id outlives its turn otherwise).

Also move the staging-lease state machine off the KimiTUI coordinator
into a self-contained StagingLeaseTracker with injected effects, drop
the duplicate media-tag builder in image-placeholder in favor of the
SDK helper, and fix the paste-in-flight comment to match the gate's
real granularity.

* fix(kap-server): project prompt.queued content without leaking daemon refs

The broadcaster projected prompt.steered and stripped turn.started
attachments but forwarded prompt.queued raw, leaking kimi-file:// URLs
and absolute materialization paths to every subscribed WS connection
and the journal. Fold the tag+ref pair into a {kind:'file'} part, same
as steered.

* fix: keep compressed uploads retrievable and close the steer abort window

Two review fixes around prompt media intake:

- The compressed re-save was released right after intake (and carried a
  1h expiry) while every client read model projects its file id,
  leaving historical compressed images unfetchable. Keep the re-save as
  an ordinary upload; roll it back only when preparation or submission
  fails before the engine takes the prompt. The engine's
  PromptInput.release hook loses its only producer and is removed.
- A prompt aborted while its steer awaited the loop's step assignment
  was flipped back to 'steered' and its content could still
  materialize into a later turn. Re-check the reservations after the
  assignment await and abort the undispatched request when the check
  fails.

* perf(agent-core-v2): memoize inlined image parts across request steps

A successful image inline depends only on the immutable upload bytes, so
it is memoized per file id (size-bounded) in media.resolved and reused
across steps, retries, and media-recovery reprojections instead of
re-reading and re-encoding on every request. Degrade forms are never
memoized since they depend on the message's tag pairing. Also make the
never-empty message placeholder kind-aware (video vs image).

* refactor: author media tag+ref pairs in the engine prompt intake

Edges (TUI, kap-server REST) now submit bare kimi-file references and the
engine intake materializes the bytes, synthesizes the paired media path
tag, and falls back to the shared cache dir when the session store is
unavailable, replacing per-edge pair construction and duplicate
materialization copies.

Thread the prompt id from submission through to turn.started (REST
prompt_id, WS event, SDK prompt option) so the TUI binds staged-media
leases to turns exactly; the origin heuristic stays as fallback and
ambiguous claims now surface a staging_lease_invariant telemetry warning.

Also lands the pending resendable-extraction fix for cache-hint resubmits
after a session switch.

* fix: decouple media persistence from prompt intake

* refactor(agent-core-v2): project the turn prompt in a single fold pass

* test: slim redundant media-ref coverage across layers

Fold duplicate pinning of the same media tag+ref rules into shared
helpers and it.each tables, and drop assertions that restate behavior
already covered at another layer:

- drop the kimiFileUrl alias describe (mediaRef.test.ts covers the
  aliased functions with more cases)
- drop pairMediaPathTagRefs describe in favor of the parity fixtures
- merge the identical prompt.steered/prompt.queued broadcast tests
- parameterize the resolver degradation matrix and prompt intake
  fixtures (enqueueMedia/gatedImage/expectMediaPair helpers)
- drop REST-level context-memory pairing assertions (engine-level
  intake tests pin the same shapes); keep the caption->system-reminder
  assertion, the only cover of extractCompressionCaptions
- drop the turn-finish-during-intake steer-cancel vector and the
  switch-session release driver test (unit-level lease tests remain)

Net -762 lines; 645 tests green across agent-core-v2, kap-server,
transcript, node-sdk, klient, and the TUI.

* chore: fix oxlint warnings introduced by image-file-ref changes

* fix: harden image file reference lifecycle

* fix: close image reference lifecycle gaps

* fix: preserve session media paths on replay

* chore: streamline image-file-ref changesets

* refactor: make daemon media references self-contained, dropping tag+ref pairing

A daemon-ref media part now carries everything a read model needs — the
kind from the part type and the materialization path from the reference's
`?path=` — so prompt intake no longer authors a paired `<media path>`
tag, and the pairing/fold machinery (pairMediaPathTagRefs /
foldMediaPathTagRefs and their mirror copy) is deleted across the engine,
transcript, kap-server, node-sdk, and the TUI. The request-time resolver
synthesizes the degrade tag from the reference path whenever bytes cannot
reach the provider. Standalone tags stay user-visible text, and never
reach the search index or prompt metadata.

* fix: reconcile image file references with main after rebase

Main removed the agent RPC aggregation layer (agent/rpc) and moved
LifecycleScope to app/scopes. Fold the branch's RPC-side behavior into
the new structure: PromptPayload carries promptId/disabledTools, and
AgentPromptService.submit admits the client-chosen id through the
reservation (duplicate rejects before any session state changes) and
applies the denylist through toolPolicy. Regenerate the wire/state
manifests.

* fix(kimi-code): run paste ingestion in the background, wait bounded at submit

The paste callback awaited compression + original persistence + the
daemon upload while CustomEditor queued every keystroke, so a slow
ingestion stalled all typing. Settle the callback once the placeholder
lands and track the rest as ImageAttachment.pending; the send path gives
a referenced pending ingestion a bounded wait (2s) so paste-then-Enter
still submits the compressed/daemon-ref form, and falls back to the
inline form when ingestion has not finished. Media-free submits stay
fully synchronous.

* fix(protocol): mirror prompt_id in the shared prompt submission schema

kap-server's local REST schema accepts a client-chosen prompt_id, but
the shared promptSubmissionSchema stripped it as an unknown key, so
clients validating through @moonshot-ai/protocol lost the id and the
turn.started promptId correlation never matched.

* fix(klient): normalize file-store errors to public RPC errors on both transports

The fileService save/get wire adaptation ran outside the dispatcher's
error normalization, so a stale or expired upload id surfaced as the
engine's raw Error2 on the memory transport and as a generic 50001 on
ipc. Map file.not_found to the public NOT_FOUND RPCError in the shared
dispatcher so both transports reject identically, and pin the parity in
the conformance suite.

* fix(agent-core-v2): keep launching media prompts visible in the queue snapshot

startNext shifts the launching record out of pending before its media
intake settles, so list()/GET /prompts reported neither an active nor a
queued prompt during the intake window even though the submission was
accepted and abortable. Report the launching record as still queued,
matching the prompt.queued event already published for it.

* fix(node-sdk): strip internal promptAttachments from SDK turn.started events

The in-process v2 event mapper forwarded the whole domain event, so SDK
session.onEvent consumers saw the transcript-projection-only
promptAttachments field that kap-server explicitly strips from the WS
wire event. Drop it in the mapper so both consumers share the same
turn.started field set.

* fix(kimi-code): align staging lease id multiplicity with retain count

A lease's flat id list conflated two cases: one submission referencing
the same image twice (one retain) and a batched steer merging two queued
messages sharing the image (two retains). Occurrence-wise release
over-consumed in the first case and batch-wise release would
under-consume in the second. Dedupe each extraction's ids at the lease
creation sites so list multiplicity always equals the retain count, and
release one retain per occurrence.

* fix(agent-core-v2): check video_in before honoring memoized video uploads

The video memo hit path returned a cached ms:// part before the current
model's capability check, so switching to a same-provider model with
video_in:false sent a video part the model cannot accept instead of
degrading to the path tag. Gate on capability first, mirroring the image
strategy.

* fix(kimi-code): keep recalled queued media staged instead of releasing it

Recalling a queued media prompt into the editor is not a discard, but
the recall path released the staged files: image attachments lost their
daemon upload (resubmit silently downgraded to inline), and a recalled
video's cache copy was deleted even though re-materialization needs a
source that may already be gone. Recall now consumes only the retain
(the next submit re-retains), retires the cache copy to session
lifetime, and rebases the video attachment onto that copy.

* fix(agent-core-v2): count launching media prompts in prompt.queued queueLength

startNext shifts the record into launchingItem before publishQueued
computes the count, so a media prompt's prompt.queued reported
queueLength 0 even though the prompt is accepted, abortable, and listed
as queued. Compute the count from the same snapshot list() exposes.

* refactor(agent-core-v2): drop the session media shared-cache fallback

Intake keeps the upload-backed reference when the canonical write fails
instead of double-writing into an unowned global cache scope; the session
media store's reads collapse to the canonical scope, and non-filesystem
deployments no longer write every media blob twice.

* refactor(agent-core-v2): stop persisting materialization paths in daemon file references

The kimi-file:// reference persisted in context memory bundled a durable
identity (fileId) with a perishable machine-local absolute path (?path=),
which forked sessions and home relocations would stale. The reference now
carries only the file id; the display path is derived from the session
media store by file id at read time. Parsers tolerate and strip the legacy
?path= query so old records keep resolving.

* fix(agent-core-v2): skip atomic-write temp siblings in session media by-id resolution

The fs backend stages atomic writes at <key>.tmp.<pid>.<hex> next to the
target key, and the media store's prefix-listing predicate matched them, so
a lookup racing an unfinished materialize could return the partial copy as
the canonical file.

* fix(kimi-code): close the staging-lease gap between extraction and dispatch

Create the staging lease right after extraction so every pre-dispatch exit
releases through the tracker: validation/session failures release it,
queueing defers it to the queue item's raw ids/paths, and the cache-hint
stash takes over ownership. A forgotten exit now degrades to an unclaimed
lease swept at session close instead of a permanently retained upload.

The cache-hint restore exits (dismiss, chained restore, session switch
during fetch, failed compact/new-session) previously returned only the
text to the editor, leaking the extraction's retains and staged cache
copies. They now go through queue-recall semantics: retains are consumed,
staged copies retire, and recalled videos rebase onto them.

* fix(agent-core-v2): bound the inline image memo with a private byte-budgeted LRU

A memoized inline image part pins a multi-MB base64 string, and the agent
state registry's snapshot/inspect path serializes every registered state
in full — so the memo no longer lives in agentState. It is now a private
per-file-id LRU with the existing 8MB per-entry cap plus a 64MB total
budget; eviction simply re-reads the bytes on the next request. The video
memo stays in agentState.

* fix(kap-server): fall back to the staged upload on the session media route

Prompt intake materializes bytes into the session media store
asynchronously and best-effort, but a session_media ref is projected to
clients as soon as the prompt is queued — so the download route could 404
during the intake window, and forever after an intake failure. The route
now reads the canonical session store first and falls back to the App-scope
staged upload, adapting it to the same served shape; only a double miss is
a 404. The header note also records that resolving the store resumes cold
sessions, an accepted short-term semantic with a TODO for a cold-read
channel.
2026-08-17 13:11:28 +08:00
Luyu Cheng
44a6c70e66
feat(kimi-code): recognize multiple inline skill activations in one prompt (#2935)
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
* feat(kimi-code): recognize multiple inline skill activations in one prompt

Inline /skill: tokens are recognized anywhere in the prompt (after
whitespace, including on following lines) with completion, highlighting,
and de-duplication. Submitting goes through session.promptWithSkills, so
the engine bundles every activation into the prompt's own user message —
one turn, one undo anchor. Replay rebuilds the per-skill cards from the
prompt origin's skillActivations and shows only the caller's own parts in
the user bubble; undo removes the prompt together with its marked bundle
cards; hook results ahead of a bundle are projected inside its window.
Enter accepts an inline completion without submitting (pi-tui
inlineSlashTrigger), and cache-hint plus /btw pass activations through.

* fix(kimi-code): harden bundled skill submissions against review findings

- Mark bundle cards by entry id, not by index into a captured array: the
  transcript window trim may replace the entries array mid-call.
- The replay turn limiter no longer cuts between a bundled prompt and the
  hook results recorded immediately before it; the oldest visible bundle
  keeps its hook context.
- A leading-combo bundle (/skill:a args /skill:b) now queues while busy
  like any other inline-skill prompt, instead of being rejected by the
  single-skill slash gate.

* fix(kimi-code): fetch one extra replay turn on resume

The SDK trims the replay to the requested limit before returning it, so a
trim landing between a bundled prompt and its preceding hook results would
make them unrecoverable to the TUI-side limiter. Resume now fetches one
extra turn of margin; preserveBundleHookResults does the final cut without
losing the hook context.

* fix(pi-tui): retrigger inline slash completion as the token grows

When the terminal delivers `/rev` in one stdin chunk, the slash starts an
autocomplete request but the following letters arrived to a null
autocomplete state and — unlike a leading slash command — matched no
retrigger context, so the stale request was discarded and the menu never
appeared. Typing token characters inside an inline slash token now
retriggers completion (with a regression test). Also aligns the startup
resume tests with REPLAY_FETCH_TURN_LIMIT.

* fix(pi-tui): retrigger inline completion on later lines too

isSlashMenuAllowed confines the slash-command menu to the first line, so
reusing it for the inline-slash retrigger context silently disabled
retriggering on every later line — the bare-slash request went stale and
the menu never appeared. The inline context now covers a token-opening
slash on subsequent lines as well (with a regression test).

* fix(kimi-code): activate leading skill tokens in /btw and repeated-token combos

- /btw's initial prompt lives entirely in the slash arguments, so a skill
  token there sits at position 0; scan it with includeLeading so
  `/btw /skill:review …` actually activates the skill.
- Combo-ness is now decided by the raw inline token count rather than the
  deduplicated activation count, so `/skill:review check /skill:review`
  submits as a bundled prompt instead of falling through to the
  single-skill path with the repeated token swallowed into the args.

* fix(kimi-code): rewrite media placeholders in leading combo arguments

A leading combo's first activation carries the raw slash arguments, so a
pasted media placeholder in them reached the engine unresolved — unlike
the standalone sendSkillActivation path, which rewrites placeholders into
escape-proof plain-text file references first. sendInlineSkillUserInput
now rewrites any arg-carrying activation the same way (covering the busy
queue and /btw intercept paths too), while the media themselves continue
to ride the prompt as extracted parts.

* refactor(kimi-code): skill mentions never carry args in bundled prompts

Align bundled submissions with the mention model: two or more skill
tokens anywhere in the input (the leading one included) make one bundled
prompt in which every token activates by name only, and args stay a
standalone /skill:<name> args concept. This removes the leading combo's
command+args parsing, so the first skill's arguments can no longer leak
the next token (displayed as a duplicated prompt under its card), media
placeholders no longer need arg rewriting, and newline-separated bundles
behave exactly like space-separated ones (parseSlashInput's literal-space
separator no longer decides bundle-ness).

* fix(kimi-code): recognized builtin and plugin commands outrank the bundle rule

The no-args bundle rule claimed any input with two or more skill tokens
before checking what led it, so `/btw check /skill:a /skill:b` was
submitted to the main agent as a bundled prompt instead of opening the
side panel. The intent is now resolved first: builtin and plugin commands
always keep their own path regardless of how many skill tokens their
arguments mention, while skill-led and newline-led inputs still bundle as
before.

* fix(pi-tui): retrigger inline completion on colons and register the local divergences

External skill tokens are shaped /skill:<name>, but the inline-slash
retrigger character classes excluded ':' — typing the colon launched no
replacement request, the bare-slash request went stale, and the menu never
appeared for prefixed skill names. Colons now retrigger completion like
other token characters (with a regression test). Also registers the
inlineSlashTrigger and autocomplete-data divergences in the package's
re-vendor protection list.

* fix(kimi-code): preserve FIFO behind unsteerable bundles and reach indented inline completion

- Ctrl-S steering now stops at the first inline-skill bundle: a later
  queued message (or the editor draft) no longer jumps ahead of the
  unsteerable bundle into the running turn, so the conversational order
  survives steering.
- The leading-whitespace slash-path suppression now yields to the inline
  skill context first, so an indented token (`  /skill:rev`) completes
  like its column-0 equivalent instead of being suppressed as a path.
2026-08-17 00:09:18 +08:00
Haozhe
84da6629b1
refactor(agent-core-v2): decouple workspace from session DI via runtime binding (#2961)
* refactor(agent-core-v2): decouple workspace from session DI via runtime binding

* fix(agent-core-v2): unblock session external hooks and scope workspaceMcp seeds

- externalHooksService: inject App-level ISessionManager instead of the
  unregistered ISessionLifecycleService so SessionStart/SessionEnd hooks
  actually activate in production; keep sessionId matching and tolerate
  absent lifecycle events
- workspaceMcpService: ignore onWillCreateSession events whose session
  belongs to another workspace, preventing cross-workspace
  ISessionMcpHandle seed overrides
- update externalHooks integration tests, agent harness, and workspaceMcp
  tests; add reloadSources coverage in skillCatalog tests

* fix(agent-core-v2): honor the bound runtime in prompt context, swarm spawn, and ACP sessions

- map system-prompt cwd, directory listing, and additional dirs through
  RuntimeWorkspaceView, and skip the listing when the bound runtime has
  no fs capability
- pass the caller agent's runtime binding to AgentSwarm child creation
  and prompt-prefix execution instead of hardcoding local
- expose the ACP client filesystem through the ACP session runtime and
  build its shell/path environment from the probed host instead of
  hardcoded Linux
- dispatch klient facade createChild to sessionManager.createChild so
  child sessions keep their parent markers

* fix(agent-core-v2): resolve routed fs and tool paths with runtime path semantics

- WorkspaceFsService resolves via the bound runtime's RuntimePath (extended with basename/dirname) instead of node:path, so mapped roots such as C:\\repo stay runtime-local.
- Read/Write/Glob/Grep pass skill roots through mapRoots via RuntimeWorkspaceView input, matching Edit.
- acp-server unbinds session runtimes on session/close, not only on delete.
- apps/kimi-code drops the /runtime slash command; SDK runtime methods stay.

* fix(agent-core-v2): retire idle session controllers, untrack disposed runtime resources, and rebuild fs watches on generation replace

* fix(agent-core-v2): resolve oxlint errors in runtime lifecycle fixes

* fix(kap-server): untrack download stream from runtime generation on completion

* fix(kap-server): drop meaningless void operator on tracked dispose
2026-08-16 20:24:12 +08:00
tpoisonooo
f492cd7c9e
feat(agent-core): add tower command to orchestrate multi-agents (#2633)
* feat(packages): implement cowork

feat(packages): update throttle control

feat(agent-core): rename to /tower

feat(agent-core-v2): support tower mode

fix(packages): keep tower teardown from stranding submodule worktrees

A plain `git worktree remove` refuses worktrees containing initialized
submodules even when they are clean, so tower teardown silently left
behind exactly the worktrees whose workers had run builds (the failure
only reached the tool report, never the activity log).

The dirty check is the data-loss gate; once it passes, removal always
passes --force (harmless on a clean worktree, and precisely what
bypasses git's submodule refusal). Kept and failed removals now also
land in the activity log as worktree.keep / worktree.remove.failed.

feat(packages): allow the tower to AskUserQuestion, workers still cannot

The tower-mode AskUserQuestion deny only ever fired on the tower itself:
workers never enter tower mode, and their tower-worker profile simply
does not list the tool. Drop the deny so the tower can clarify
requirements with the human up front; workers and reviewers stay
ask-less and escalate via TowerSend. Auto permission mode still
disables AskUserQuestion for everyone.

fix(agent-core-v2): import LifecycleScope from #/app/scopes

main moved the enum out of #/_base/di/scope; follow the new location in
the two tower services.

test(agent-core-v2): refresh fullCompaction token expectations

main's #2699 counts compaction tokens on the full-request basis, so the
tower tool schemas (default registry) and the /tower skill catalog entry
(system prompt) shift the pinned numbers: +2789 with the default tool
set, +173 with the explicit harness tool list. The 20k-window test keeps
its shape with a 22k window so the post-compaction floor still fits.

feat(agent-core-v2): tower command support secondary model

fix(tower): disable todo-list tool

feat(tower): reviewer keep primary model

fix(tower): tower worker call for authroization

update

* refactor(tower): drop agent-core-v1 version

* feat(tower): remove builtin.ts

* fix(agent-core-v2): verify the recorded base branch before tower merges

* fix(agent-core-v2): activate tower missions only after a successful spawn

* chore(kap-server): correct the search-service activation comment

* fix(tower): allowActivationWhileBusy for all skill

* update

* update

---------

Co-authored-by: konghuanjun <konghuanjun@moonshot.ai>
2026-08-16 15:13:42 +08:00
bj456736
6b72345f8b
feat(tui): print the fork resume command and copy it to the clipboard (#2940)
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
* feat(tui): print the fork resume command and copy it to the clipboard

* fix(tui): use pushd in the Windows fork resume command so it switches drives

cmd.exe's `cd` only updates the target drive's remembered directory, so a
terminal on another drive would run `kimi --resume` in the wrong working
directory. `pushd` switches drive + directory in both cmd.exe and
PowerShell (`cd /d` would break PowerShell). Addresses the Codex review
comment.

* fix(tui): label OSC 52 clipboard delivery as unverified after fork

copyTextToClipboard falls back to an OSC 52 escape when no native
clipboard provider works; terminals without OSC 52 support silently
drop the sequence, so only native delivery may claim success. Matches
the wording convention of /copy. Addresses the Codex review comment.

---------

Co-authored-by: bj456736 <bj456736@users.noreply.github.com>
2026-08-15 14:09:43 +08:00
Luyu Cheng
d96cd03770
fix(cli): pre-send warning and clearer error for over-long /goal objectives (#2928)
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-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 / build (push) Waiting to run
CI / test (1) (push) Waiting to run
Nix Build / Check flake.nix workspace sync (push) Waiting to run
Nix Build / nix build .#kimi-code (push) Blocked by required conditions
Release / Release (push) Waiting to run
Release / Deploy docs (push) Blocked by required conditions
Release / Native release artifact (push) Blocked by required conditions
Release / Publish native release assets (push) Blocked by required conditions
* fix(cli): warn on over-long /goal objectives before sending and keep the input

- Show a live footer warning in the TUI while a typed /goal objective
  exceeds the 4000-character limit, measuring paste-expanded text only
  when the input can be a /goal command.
- Restore the rejected /goal input into the editor instead of losing it.
- Include the file-reference workaround in the GOAL_OBJECTIVE_TOO_LONG
  error messages (TUI, goal queue, agent-core, agent-core-v2).

* fix(cli): keep the /goal length warning in its own footer slot

A transient hint (exit confirm, detach, image paste) that displaced the
warning previously left the footer blank after clearing, because no
editor change re-applied it. The footer now renders the warning from a
dedicated slot whenever no transient hint is active, so the warning
returns on its own.

* fix(cli): gate the /goal length warning on trimmed text

Submitted text is trimmed before slash-command dispatch, so leading
whitespace still runs /goal — normalize with trimStart in the gate and
in the length check to match.

* fix(cli): restore input rejected by the slash-command busy gate

An idle-only command submitted while streaming/compacting was rejected
after the editor buffer had already been cleared, losing hand-typed
input (e.g. an over-long /goal objective that never reached the local
validation).

* fix(cli): restore input at the post-creation busy re-check

The lazy-session race rejects an idle-only command after a first prompt
has already started a turn; the editor buffer is long cleared by then,
so give the submitted input back like the dispatch blocked branch does.

* fix(cli): close the remaining input-loss and gate gaps around /goal

- Restore the submitted input when lazy session creation fails before a
  session-requiring command runs.
- Restore only into a still-empty editor after async gates, so a draft
  typed while creation was pending is never overwritten.
- Expand pastes that can complete a partially typed /goal command
  (e.g. /go[paste #1 …]) in the length-warning gate.

* fix(cli): never displace newer UI state with a delayed input restore

A session-less /goal submission restores its input only after an async
gap (lazy session creation). If the user opened an editor-replacement
panel meanwhile, restoring would tear it down (and leave activeDialog
inconsistent). Track editorReplacementMounted in TUIState and gate all
delayed restores through canRestoreSubmittedInput.

* refactor(cli): move canRestoreSubmittedInput into commands/resolve

Avoids the goal.ts <-> dispatch.ts runtime import cycle flagged by
import/no-cycle; the helper takes a structural host shape instead.

* fix(cli): match the slash parser's delimiter in the /goal length warning

parseSlashInput splits the command name at a literal space only, so a
newline or tab after /goal dispatches as a plain message — the warning
must not fire for inputs the dispatcher will not treat as a goal.
2026-08-15 02:55:33 +08:00
github-actions[bot]
13d86f8b7b
ci: release packages (#2881)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-08-14 20:51:17 +08:00
qer
cd489955bb
chore: sync web dist from code-app (#2922)
code-app: af7ed8fa03278bbe2f988732b3195649e7a6f2bc
2026-08-14 20:36:54 +08:00
Grapedge
7475c2e2e3
feat(vscode): switch the extension to the v2 engine with a rollback switch (#2916)
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
The extension now runs on the agent-core-v2 engine by default. The
interface, sessions, and workflows do not change. Two rollback paths
exist, and one function makes the decision
(config/vscode-settings.ts):

- the kimi.useAgentCoreV1 setting (temporary; a window reload applies
  the change);
- the KIMI_CODE_LEGACY_FLAG environment variable, which wins over the
  setting and has the same semantics as in the CLI.

An engine startup failure shows an explicit error that names the
rollback setting. There is no silent fallback. CI runs the extension
test suite on both engines: the sharded run covers the default v2
engine, and a new test-vscode-legacy job reruns the suite with
KIMI_CODE_LEGACY_FLAG=1.

To keep the v2 path identical to v1 for every method the extension
uses, this change also completes the v2-backed SDK client and the v2
engine:

- Implement session deletion in the v2 SDK client.
- Implement fork truncation at a turn index in the v2 engine, with the
  same rules as v1, and reject a fork while the source session has an
  active turn.
- Stop the session-level /init run when the turn is cancelled, as v1
  does.
- Read session metadata without the archived field as not-archived, so
  sessions written by the v1 engine open correctly.

The SDK parity suite now covers session deletion, cancel, and fork
truncation. The known-difference list for the methods the extension
uses is empty.
2026-08-14 17:22:10 +08:00
qer
741708f948
feat(kap-server): add plugin marketplace and capability REST routes (#2868)
* feat(agent-core-v2): surface a machine-key note from capability installs

CapabilityEntry.install now resolves an optional note exposed through
CapabilityInstallProgress.note (wire-visible). The webbridge entry
returns 'user-skill-migrated' when it migrates a pre-existing
standalone skill copy onto the plugin-managed one — clients can
localize the migration instead of the skill silently disappearing
from the user's directory.

* feat(kap-server): add plugin management and capability REST routes

Expose the App-scope plugin and capability services over the wire so
non-CLI hosts (desktop, web) can manage plugins and built-in
capabilities end to end:

- GET  /api/v1/plugins, POST /api/v1/plugins {source},
  POST /api/v1/plugins/{id}:{enable,disable,remove}
- GET  /api/v1/plugins/marketplace — catalog (pluginMarketplaceUrl
  server option / KIMI_CODE_PLUGIN_MARKETPLACE_URL env / production
  default) merged on demand with live install state; updateAvailable
  only on strict semver catalog > installed (no semver dependency)
- GET  /api/v1/capabilities, GET /api/v1/capabilities/{id},
  POST /api/v1/capabilities/{id}:install with client-polled progress
- New wire codes 40418 capability.not_found, 40419 plugin.not_found,
  40923 capability.install_in_progress, 40924 capability.unsupported

Mutations flow through IPluginService, so they serialize with other
install paths and fire onDidReload (session skill catalogs and the
capability shelf-install hook converge).

* fix(kap-server): map plugin input errors to 4xx and correct the unsupported test code

- mapPluginError now translates the domain's validation.failed (40001)
  and fs.path_not_found (40409) instead of collapsing client-fixable
  input mistakes (relative source, nonexistent local path) into a
  50001 internal error
- the non-macOS capability install test expected 40923, which this
  branch assigns to capability.install_in_progress; the unsupported
  code is 40924 (macOS runners skip the case, which is why it only
  fails on Linux/Windows CI)

* fix(kap-server): resolve catalog-relative marketplace sources and widen the unsupported-test skip

- The production CDN catalog carries sources relative to the catalog
  URL (./official/*.zip); clients handing them back to POST /plugins
  would hit the local-path normalizer's 40001. Resolve entry sources
  against the configured catalog URL so every returned source is
  directly installable.
- The 40924 install-rejection test only skipped macOS, but kimi-cu is
  also supported on Windows x64 — running it there would start the
  real installer. Skip on every supported platform.

* fix(kap-server): accept the legacy url/downloadUrl marketplace source aliases

Custom catalogs that the CLI already accepts can carry an entry's source
under url or downloadUrl instead of source; the route's strict schema
rejected the whole catalog with 50001. Normalize the aliases before
validation (same precedence as the CLI parser) so those catalogs keep
working through /api/v1/plugins/marketplace.

* fix(kap-server): support local marketplace catalogs and drop conditional spreads

- KIMI_CODE_PLUGIN_MARKETPLACE_URL accepts a plain path or file://
  catalog in the CLI loader; the route only fetched over HTTP, so local
  catalogs 50001'd for desktop/web hosts. Read local catalogs from disk
  and resolve their relative sources against the catalog's directory.
- Replace the marketplace mapping's conditional spreads with direct
  possibly-undefined properties per the repo rule.

* fix: surface capability install notes through klient and convert file:// entry sources

- The klient capabilities contract omitted install.note, so zod parsing
  stripped it and facade callers (node-sdk, TUI) never saw
  'user-skill-migrated'. Add the field and pin it in the facade test
  fixture.
- A marketplace entry source given as a file:// URL fell through to the
  relative-branch and came back as a garbage path; convert with
  fileURLToPath so the advertised source stays installable.

* test(kap-server): keep the new route tests portable to Windows x64

- The capabilities list assertion treated every non-macOS host as
  unsupported, but kimi-cu is supported on Windows x64 — derive the
  expectation from the same platform predicate.
- file:///abs/... is not a valid absolute file URL on Windows (no drive
  root); build the fixture with pathToFileURL from a temp path instead.

* refactor: align the capability note and test helper with repo conventions

- agent-core-v2 keeps explanatory docs in the top-of-file block only;
  the note contract already lives in the capability types header, so
  drop the two member-level doc blocks.
- The plugins route test helper sets the optional fetch body directly
  instead of via a conditional spread.

* fix(kap-server): expand ~ in local marketplace catalog paths

The CLI loader expands ~/ against the home directory; the route read
the path literally, so KIMI_CODE_PLUGIN_MARKETPLACE_URL=~/catalog.json
50001'd for desktop/web hosts while working in the CLI. Share one
localCatalogPath helper (file:// conversion + tilde expansion) between
the catalog read and the relative-source resolver.

* fix(kap-server): expand home-relative marketplace entry sources

A catalog entry with source '~/...' fell through to the catalog-relative
branch and came back as <catalog-dir>/~/... — unresolvable by POST
/plugins. Expand ~ via the shared helper before the absolute/relative
decision.

* fix(kap-server): match CLI field semantics for source aliases and stub the Windows home

- A blank or non-string source no longer shadows the url/downloadUrl
  aliases; the first valid (non-blank, trimmed) of source/url/downloadUrl
  wins, mirroring the CLI parser's stringField.
- The tilde test also stubs USERPROFILE so os.homedir() resolves to the
  fixture home on Windows runners.

* fix(kap-server): read a blank marketplace tier as missing

The CLI parser trims tier and treats a blank as absent (third-party);
the route's enum rejected the whole catalog with 50001. Normalize the
tier alongside the source aliases in the same preprocess.

* fix(kap-server): derive marketplace versions from GitHub release sources

Entries that omit version but encode it in a GitHub release/tag (or
tree/commit) source never surfaced updateAvailable. Derive the version
from the resolved source — same URL shapes as the CLI parser, validated
with the route's strict x.y.z rule (no semver dependency).

* fix(kap-server): fail catalog validation on a source with no usable value

A whitespace-only source with no valid alias passed z.string().min(1)
untrimmed and resolved against the catalog URL into nonsense. Drop the
key during normalization so the schema reports the entry as missing its
source (same outcome as the CLI's 'must define source').

* fix(kap-server): resolve latest versions for bare GitHub marketplace entries

A catalog row whose source is a bare GitHub repo (the production curated
rows are shaped this way) kept version undefined, so updateAvailable
never fired for exactly the entries most likely to update. Resolve the
latest release tag through the /releases/latest redirect — the UI route,
not the rate-limited API — same as the CLI, degrading to no version on
any failure.

* docs(kap-server): note the marketplace version resolution in the plugins route header

* feat(kap-server): mark capability wiring rows in the marketplace response

A client following only /plugins/marketplace + POST /plugins would
install a capability's wiring plugin without its binary runtime, with
no wire-level way to tell. Entries whose id matches a capability's
wiring plugin now carry capabilityId, so clients route them through
/capabilities/{id}:install — the client-side routing pattern the CLI
established (the upstream design that replaced the server-side hook).

* fix(kap-server): fall back to the source-checkout catalog for the default location

When the marketplace location is the built-in default (no server option
or env override) and the fetch fails, read the repo checkout's own
plugins/marketplace.json — the CLI loader's behavior for offline
source-checkout dev. An explicitly configured catalog still fails hard
with 50001. Bundled installs have no checkout file, so the fallback
simply never fires there.

* fix(kap-server): resolve fallback catalog sources against the fallback file

readMarketplaceCatalog returned only the JSON, so entries from the
source-checkout fallback resolved their relative sources against the
(unreachable) CDN URL — coming back as unusable https paths instead of
local directories. The reader now returns the location actually read,
and source resolution uses it.

* fix(kap-server): honor the CLI's marketplace metadata aliases

Custom catalogs using name / shortDescription / websiteURL (accepted by
the CLI parser) lost those fields to schema stripping, falling back to
the entry id. Normalize the aliases in the same preprocess as the
source/tier normalization.

* fix(kap-server): filter marketplace keywords instead of rejecting the catalog

A keywords array with non-string or blank members failed the strict
schema and took the whole catalog down with 50001. Normalize to the CLI
parser's semantics: non-array reads as missing, arrays keep trimmed
non-blank strings only.

* fix(kap-server): treat a blank or non-string marketplace version as missing

The CLI parser reads version through its lenient stringField and falls
through to source-derived versions; the route's schema rejected a
numeric version with 50001 for the whole catalog. Normalize version in
the preprocess like the other fields — the gh-plugin fixture now
carries a numeric version and still derives 2.0.0 from its tag source.

* fix(kap-server): trim marketplace entry ids before the install-state join

A whitespace-padded id survived validation raw and never matched the
installed records (updateAvailable silently lost). Normalize the id in
the preprocess — trimmed, blank rejected — matching the CLI's
requiredString.

* fix(kap-server): gate capability markers to the default catalog

A custom catalog (env or server option) may legitimately carry a
same-id fork of a capability's wiring plugin; marking it capabilityId
would route users to the built-in install. Apply the marker only for
the default catalog (including the source-checkout fallback), matching
the CLI injecting built-in rows only for the default catalog.

* fix(kap-server): compare marketplace versions with real semver

The hand-rolled strict x.y.z check rejected valid semver the CLI
accepts (v-prefixed, prerelease tags), so updateAvailable diverged
between CLI and wire clients. Take the semver package (already in the
monorepo via the CLI) for the update check and the two source-derived
version validators.

* fix(kap-server): validate marketplace entry types and count the dev server as default

- Custom catalog rows with an unsupported type (e.g. integration) were
  stripped by the schema and advertised as installable plugins; the CLI
  rejects the catalog outright. Model the same plugin/managed/guide
  vocabulary.
- scripts/dev.mjs marks its repo-owned catalog with
  KIMI_CODE_PLUGIN_MARKETPLACE_FROM_DEV_SERVER=1 — honor the flag in
  the isDefault check so capability markers and the checkout fallback
  behave exactly like the CLI under the dev marketplace.

* fix(kap-server): join capability rows through their platform wiring plugin id

kimi-cu installs its wiring plugin as kimi-cu-win on Windows x64, so a
catalog row keyed kimi-cu never matched the installed record there (no
installed state, no updateAvailable). The row mapping now knows each
capability's wiring plugin ids and joins through them.

* fix(kap-server): map plugin load failures to 40001

An install source pointing at a directory/zip with a missing or invalid
manifest throws plugin.load_failed — a client-fixable input error that
fell through to 50001. Map it to validation.failed alongside the other
input mistakes.

* build(kap-server): align @types/semver with the workspace version

sherif rejects multiple workspace versions of one dependency; the CLI
pins @types/semver at ^7.7.0.

* refactor(agent-core-v2): share the plugin marketplace client/parser across hosts

The kap-server marketplace route grew its own copy of the CLI's catalog
loading/parsing logic (lenient aliases, blank-means-missing fields,
source resolution, GitHub version derivation) — two implementations of
a public, hand-writable format would drift on every catalog change.
Move the read/parse/version machinery into the plugin domain as
app/plugin/marketplace (pure functions, no DI): the CLI keeps a thin
wrapper owning configured-source resolution and its checkout fallback,
and the route keeps only the wire concerns (install-state merge,
capabilityId markers, error envelopes). plugins.ts drops ~230 lines of
duplicated machinery.

One deliberate behavior fix rides along: tilde entry sources now expand
against the home directory at parse time (the CLI previously passed
them through literally, failing later at install validation).

* docs(agent-core-v2): fold the marketplace module's member docs into the file header

The package convention keeps explanatory comments in the top-of-file
block only; the moved parser carried several function/member-level
JSDoc blocks from its CLI home. The header now carries the format
contract, leniency rules, source/version resolution order, built-in
masking semantics, and the fallback gating rule.

* docs(agent-core-v2): drop the remaining statement comments in the marketplace module

The header carries the rationale (update semantics, GitHub ref shapes,
the releases/latest choice); the convention allows nothing beside
statements.

* fix(kimi-code): import the shared marketplace module by its deep path

constant/app.ts is evaluated on every CLI invocation; re-exporting from
the agent-core-v2 root would pull the whole engine module graph into
startup. The package's wildcard subpath export lets both CLI files take
only the pure marketplace module (node builtins + semver).

* feat(kap-server): fan plugin and capability lifecycle out as global WS events

Clients currently poll the plugins/capabilities REST surfaces and can
hold stale rows while another client mutates the set. Publish two global
events instead:

- event.plugin.changed — fired off IPluginService.onDidReload, so any
  install/enable/disable/remove from any client reaches every host
- event.capability.changed — every capability install progress
  transition (CapabilityService gains onDidChangeInstall), so rows
  update live and settle is observable without polling

Both ride the existing global fan-out (no subscription needed) and are
documented in the wire schema registry.

* fix: register the lifecycle events in the wire union and tidy the contract header

- event.plugin.changed / event.capability.changed were declared but not
  part of agentEventSchema, leaving the wire catalog incomplete.
- The onDidChangeInstall member doc moves into the capability contract
  file header (package comment convention).

* feat(protocol): mirror the plugin/capability lifecycle events in the shared WS schema

Clients and e2e harnesses validating server frames against
@moonshot-ai/protocol would reject event.plugin.changed /
event.capability.changed. Register both in the shared catalog (TS
interfaces, zod schemas, and both unions), matching the
model_catalog.changed precedent for global events.

* fix(kap-server): prefer the platform wiring plugin when joining capability rows

A stale same-id record (e.g. a raw kimi-cu plugin next to the real
kimi-cu-win wiring on Windows x64) previously won the join, showing the
wrong installed state and update availability. Capability rows now join
through the wiring plugin ids in platform preference order before
falling back to the catalog id.

* fix(kap-server): put the github metadata of plugin summaries on the wire schema

GitHub-sourced plugin summaries carry github {owner, repo, ref,
installedSha} from the domain; the route serializes raw domain objects,
so the field reached clients undocumented. Declare it in
pluginSummarySchema so the OpenAPI surface matches reality.

* test(node-sdk): cover the new lifecycle events in the exhaustive switch

The event-type exhaustiveness test broke when the shared protocol union
gained event.plugin.changed / event.capability.changed.

* fix(kap-server): mark capability progress events volatile

Per-chunk download progress transitions ride the same fan-out as
durable frames and were being persisted to the __global__ journal —
hundreds of stale frames per install. event.capability.changed is
live-only state, so it joins the volatile list alongside
event.di.unit_changed; the settle frame stays recoverable via a direct
capability read. event.plugin.changed remains durable (rare, and a
reconnecting client should replay it).

* feat(kap-server): inject built-in capability rows into the default catalog response

The checked-in production catalog carries kimi-webbridge but not
kimi-cu — the CLI injects built-in rows client-side, so wire clients
never saw Kimi Computer Use in /plugins/marketplace. For the default
catalog the route now appends supported capabilities the catalog lacks
(static descriptors via ICapabilityService.describeCapabilities — no
detector probes), marked with capabilityId and a capability:<id>
sentinel source so installs still route through the capability
surface.

* fix(kap-server): run injected capability rows through the install-state join

The injected kimi-cu row hardcoded installed: undefined, so an
already-installed capability still read as installable. Injection now
happens before projection, so injected rows get the same backing-plugin
join (installed state, update badge, capabilityId marker) as catalog
rows. Also moves the describeCapabilities note into the contract header
(package comment convention).

* test(kap-server): gate the injected-row assertions on platform support

kimi-cu injects only where supported (macOS / Windows x64); on Linux CI
the row is correctly absent.

* fix(protocol): classify capability progress as volatile in the shared catalog

kap-server never journals event.capability.changed (it is in the
server-local volatile list); shared-protocol clients reading
isVolatileEventType would treat per-chunk progress frames as durable
and replayable. Mirror the classification.

* fix(kap-server): hide capability rows on unsupported platforms

Catalog-carried capability rows (kimi-webbridge in the default catalog)
were marked with capabilityId regardless of host support — on an
unsupported platform clients would route into an impossible capability
install. Rows whose capability is unsupported are now excluded from the
default-catalog response entirely (the CLI hides its built-in rows the
same way).
2026-08-14 15:45:34 +08:00
Louis Gu
245e3d56a6
fix(tui): sanitize background task output (#2863)
Co-authored-by: liruifengv <liruifeng1024@gmail.com>
2026-08-14 11:19:27 +08:00
liruifengv
1811bd4baf
fix(tui): keep banner main text readable with long tags on narrow terminals (#2884)
* fix(tui): keep banner main text readable with long tags on narrow terminals

The banner layout inlines the tag and wraps the main text into the
remaining width. Remote banner configs can set a full-sentence tag
(e.g. the 38-char K3 thinking-effort banner), which on narrow terminals
leaves the main text only a few columns, so it wraps into a ragged,
hard-broken column ("balan/ce", "capab/ility").

When the inline tag would leave the main text fewer than 16 columns,
render the tag on its own line and give the main text and subtext the
full width, aligned with the tag text. Short tags stay inline; tags
wider than the terminal are still dropped as before.

* chore: add changeset for banner narrow-terminal fix

---------

Co-authored-by: Mira <mira-bot@moonshot.cn>
2026-08-13 18:12:46 +08:00
Haozhe
4739284fb9
refactor(features): extract session init feature (#2887)
- move the session init domain under features
- contribute the session service through SessionInitFeature
- cover feature withdrawal and restoration
2026-08-13 18:12:08 +08:00
github-actions[bot]
b6144f94ea
ci: release packages (#2846)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-08-13 13:47:30 +08:00
Haozhe
314b39489e
refactor(agent-core-v2): extract swarm into a scope-organized feature (#2874)
* refactor(agent-core-v2): extract swarm into a scope-organized feature

- move src/agent/swarm, src/session/swarm, and src/agent/tools/agent-swarm
  into src/features/swarm/{agent,session,tools/agent-swarm}; swarmOps.ts
  stays a static import=register wire channel at the feature root
- add SwarmFeature carrying the three runtime registrations
  (IAgentSwarmService, ISessionSwarmService, IAgentSwarmTool) with
  ScopeActivation.OnScopeCreated preserved
- switch src/index.ts to precise leaf exports and update import sites,
  including the kap-server and kimi-inspect deep-path imports
- move tests to test/features/swarm and re-assert service overrides in
  the test harness so stubs keep winning over feature contributions

* fix(agent-core-v2): keep feature-contributed tools in Agent tool descriptions

SubagentTool.knownToolReferences() now reads the full AgentToolContribution
collection (static registrations and feature contributions alike) instead
of the static contribution table. A caller profile that does not activate
a feature-contributed tool (e.g. AgentSwarm) no longer drops it from the
per-profile tool listings the description advertises for spawned profiles
when a workspace/session restriction forces explicit enumeration.

Add a regression test with a caller profile lacking AgentSwarm under a
global tool restriction.

* refactor(kap-server): lift session profile updates to the route edge

- add sessionProfile.ts/sessionAgentConfig.ts route helpers that resume
  the session and dispatch title/metadata and the agent_config patch to
  the native v2 services directly
- drop updateProfile from ISessionLegacyService, leaving only the
  status rollup and the goal read in the legacy adapter
- wire shape and client-visible behavior unchanged
2026-08-13 13:35:49 +08:00
7Sageer
c9bfe8b2c8
feat: replace the secondary-model experiment with a declarative subagent model pool (#2700)
* feat: replace secondary-model experiment with [subagent.models] pool

Add a declarative subagent model pool to agent-core-v2: [subagent.models]
maps [models] entry ids to selection hints rendered in the Agent/AgentSwarm
tool descriptions, and [subagent].default_model picks the spawn model when
the caller passes none. The tools' model parameter becomes a free-form
alias string (stripped when no pool is configured), description rendering
is caller-aware (primary (alias) [main model]), and a session-start
validation service fails fast with CONFIG_INVALID on a missing/invalid
default_model or an unresolvable pool alias.

Remove the secondary-model experiment from the v2 engine, node-sdk,
kap-server, and the TUI (the /secondary_model command), and drop the
agent-profile modelPreference / model_preference frontmatter field on v2.
The legacy v1 engine keeps the experiment unchanged; v2 ignores leftover
[secondary_model] config silently.

* fix(agent-core-v2): harden subagent model-pool validation and error/picker mapping

Deep-review follow-ups to the [subagent.models] pool:

- validate the pool before session materialization (after config.ready)
  and before the fork file copy, so a broken pool no longer leaves
  orphaned session dirs or leaked MCP overlay connections; the
  Session-scope validation service stays as a backstop
- reject the reserved "primary" pool alias at startup, and again
  defensively in resolveSubagentBinding so a pool broken by a runtime
  config edit fails loudly at spawn instead of binding the wrong model
- keep the [default] marker when the caller's own model is the pool
  default (primary (alias) [main model] [default])
- recompile the cached tool-args validator when a tool advertises a new
  schema object (mid-session pool edits no longer hit a stale validator)
- map config.invalid to VALIDATION_FAILED in kap-server's session routes,
  the debug transport mapper, and the catch-all error handler
- hide the v1-synthesized __secondary__ entry from the /model and
  /provider pickers again
- fold per-export doc blocks into file headers per package comment
  conventions; add pre-flight/reserved-key/validator/mapping tests and
  document that create/resume/fork all fail on a broken pool

* feat: re-add /secondary_model and accept a lone subagent default_model

- v2 engine: a pool-less [subagent] default_model forms an implicit
  single-entry pool — validated at session create/resume/fork like an
  explicit pool, and advertised through the Agent/AgentSwarm model
  parameter.
- Tool descriptions: the caller's own alias is a normal pool entry
  marked [main model]; the primary line stays distinct because only it
  inherits the caller's thinking level.
- TUI: /secondary_model returns, persisting [subagent] default_model
  (merging into an existing pool with an empty description); the picker
  hides the no-op Thinking footer and rejects the reserved primary
  alias.
- kap-server: /api/v1/config accepts and echoes subagent; the
  snake-to-camel patch conversion preserves user-defined map keys under
  providers/models/experimental/raw without leaking preserve mode into
  a colliding alias's own fields.
- v1 config schema learns subagent.defaultModel/models so the shared
  config.toml round-trips; the v1 engine still ignores them at runtime.
- Docs (en/zh) and changesets updated.

* docs: use public model identifiers in the subagent model pool examples

* refactor: rename /secondary_model to /secondary-model

* test: cover the /secondary-model command name resolution

* Revert "test: cover the /secondary-model command name resolution"

This reverts commit 98a4a6d999.

* feat(agent-core-v2): move the subagent model pool to [secondary_model]

The pool keys (default_model, [secondary_model.models]) now live in their
own [secondary_model] config section instead of [subagent], which keeps
only timeout_ms; legacy [subagent] pool keys are ignored with a
deprecation warning. The SDK config contract carries the pool on the
secondaryModel field, so the TUI /secondary-model command (now also
aliased /subagent-model) and the kap-server /config wire read and write
it directly with no translation layer.

* docs: correct default engine guidance

* feat(agent-core-v2): pin subagents to default_model with [secondary_model] force

force = true removes the main agent's per-spawn model choice: the Agent
and AgentSwarm tools stop advertising the model parameter and every spawn
binds default_model; an explicit choice, "primary" included, is rejected.
The setting requires default_model, rejects a [secondary_model.models]
table, and is validated loudly at session create/resume/fork (lifecycle
preflight plus the Session-scope backstop). The v1 engine declares the
key for write round-trips and excludes it from the recipe patch.

Also documents pool entries as per-alias thinking-level variants via
default_effort overrides.

* docs: use real managed model aliases in the secondary_model examples

The pool examples invented aliases (kimi-hs, fable, codex) and referenced
non-existent model IDs (model = "codex"); they now reference only the
managed aliases provisioned by /login (kimi-code/k3,
kimi-code/kimi-for-coding, kimi-code/kimi-for-coding-highspeed), with the
effort variant derived as kimi-for-coding-highspeed-deep. Also replaces the
versioned kimi-k2.5 alias with kimi-for-coding per the docs model-ID rule.

* chore: simplify the subagent model pool changeset

* feat(agent-core-v2): honor the legacy [secondary_model] model key as a fallback default

* refactor(node-sdk): export the reserved model-alias constants from the SDK

Restore the SECONDARY_DERIVED_MODEL_ALIAS re-export and add
PRIMARY_SUBAGENT_MODEL_CHOICE so the TUI imports both from
@moonshot-ai/kimi-code-sdk instead of vendoring local copies.

* feat(agent-core-v2): keep the subagent model pool behind the secondary-model experiment

Restore the secondary-model flag gating so this change only adds the pool:
with the experiment off the [secondary_model] pool keys stay inert — the
Agent/AgentSwarm tools strip the model parameter, spawns inherit the
caller's model, and startup pool validation is skipped. The /secondary-model
slash command is gated behind the experiment again, and the docs and
changeset describe the flag.

* fix(node-sdk): cascade provider removal into the subagent model pool

Deleting a provider left [secondary_model] entries pointing at the removed
model aliases; with the secondary-model experiment on, every subsequent
session create/resume/fork then failed pool validation. planProviderRemoval
now filters dangling pool entries, and drops the whole section when its
effective default (defaultModel, or the legacy recipe's model fallback)
dangles — folded into the same atomic multi-section replace.

* fix(agent-core-v2): close two subagent model pool validation gaps

Spawn-time resolveSubagentBinding now rejects force combined with a
[secondary_model.models] table, matching the startup pre-flight — a live
session could otherwise reach that invalid state through a deep-merged
config patch and only fail on the next create/resume. The session
lifecycle pre-flight also awaits the kosong model/provider registries'
ready alongside config.ready, so a cold bootstrap no longer fails a valid
pool with CONFIG_INVALID against an empty registry.

* test(agent-core-v2): stub the model/provider registries in handler-chain tests

SessionLifecycleService now awaits IModelService/IProviderService readiness
in its pool pre-flight, so the tests that assemble the real service through
a hand-built container must register the two tokens.

* fix(kap-server): cascade REST provider deletion into the subagent model pool

The DELETE /providers route rewrote only the providers and models
sections, so a pool referencing one of the deleted provider's aliases was
left dangling and the engine's create/resume/fork pool validation failed
every subsequent session until the user repaired the TOML by hand. Filter
dangling pool entries and drop the section when its effective default
dangles, mirroring the SDK's planProviderRemoval semantics.

* fix: keep the subagent pool consistent on provider replace and preserve legacy recipe fields

PUT /providers rebuilds the provider's alias set and can drop or rename
aliases referenced by [secondary_model]; the pool now cascades there too —
renamed aliases are repointed (mirroring the global default-pointer
migration), dropped aliases are filtered, and the section is cleared when
its effective default dangles.

The v2 [secondary_model] schema also declares the legacy recipe patch
fields (default_effort, max_output_size, ...) so validation no longer
strips them: pool resolution keeps ignoring them, but config reads/writes
now round-trip losslessly instead of silently deleting them from
config.toml on any pool write.

* fix(agent-core-v2): cascade the subagent pool on catalog refresh writes

A background provider/model refresh rewrites the [models] table without
touching [secondary_model], so a dropped alias left the pool dangling and
every subsequent session create/resume/fork failed validation until the
user repaired the TOML by hand — the same gap the SDK and REST write
paths already had, but triggered unattended.

The cascade helper now lives in agent-core-v2 next to the section it
protects (cascadeSubagentModelPool): the discovery service folds the pool
into the same atomic replaceSections transition, and kap-server's
provider write routes reuse the shared helper instead of a local copy.

* fix: cover the last two model-table write paths for the subagent pool

ModelsDevImportService's catalog and custom-registry imports rebuild the
[models] table without the pool cascade, so an import that drops a pooled
alias left a dangling pool behind; both final write passes now fold the
pool through cascadeSubagentModelPool (the drop passes deliberately skip
it). The StubConfigService test double now treats a null section value as
a delete, matching the real ConfigService.

The TUI's provider overwrite flow removes an existing provider before
re-adding it, which ran the removal cascade against a model table where
every alias of that provider was absent and silently dropped the pool;
the flow now snapshots secondaryModel up front and restores the entries
that survive the re-add, via the cascade helper re-exported from the SDK.
2026-08-13 12:29:03 +08:00
Haozhe
23e68eee8b
refactor(agent-core-v2): remove the agent RPC aggregation layer (#2871)
* refactor(agent-core-v2): remove the agent RPC aggregation layer

- delete src/agent/rpc/ (AgentRPCService, IAgentRPCService, core-api,
  prompt-metadata, types) and sink each method's orchestration into its
  owning domain service
- prompt: new submit/submitSteer composing disabledTools gating,
  MAIN-only session metadata, and engine-side {turn_id} settlement
- skill: activate now returns PromptLaunchResult and writes session
  metadata internally (MAIN-only, unified across prompt/steer/skill/
  pluginCommand); node-sdk and kap-server drop their edge-side writes
- pluginCommand: new agent-scope domain owning command activation and
  the plugin_command.activated domain event
- permissionMode/loop/fullCompaction: new setModeAndBroadcast /
  cancelFromUser / cancel; setMode and loop.cancel stay pure for
  internal callers
- klient: agentRpcContract split into per-domain contracts; facade
  re-routes to domain channels with its public API unchanged
- node-sdk, kap-server, kimi-inspect and the v2 test harness now call
  domain services directly; ctx.rpc keeps its name as a composed
  adapter
- externally visible: the agentRPCService debug channel is gone and
  session metadata writes are now MAIN-agent-only (see changeset)

* refactor(agent-core-v2): move disabledTools gating out of the prompt domain

Prompt should not own session tool policy: submit no longer accepts or
applies disabledTools. The klient facade keeps its prompt({ disabledTools })
API and composes it edge-side — applying agentToolPolicyService
setSessionDisabledTools before calling agentPromptService.submit, the same
way kap-server's prompt route already does. Over klient, a profile-less
engine now surfaces the raw profile error instead of request.invalid.

Also restores the RPC-removal changeset, which did not make it into the
previous commit.

* chore(agent-core-v2): drop the RPC-removal changeset

* refactor(klient): drop disabledTools from the prompt entry entirely

The prompt path no longer carries session tool gating on any surface:
the klient facade prompt() loses the disabledTools field and calls
agentPromptService.submit directly, and the node-sdk
SessionPromptRpcInput stops accepting or forwarding it (v1 always
ignored the field). Session tool gating remains available through
IAgentToolPolicyService.setSessionDisabledTools, composed at the edge
the way kap-server's prompt route does; the klient toolPolicy contract
added for facade-side composition is removed as unused.
2026-08-13 10:57:01 +08:00
qer
719da94648
chore: rebuild web dist against kimi-code 0.35.0 (#2853)
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 / 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
code-app: c5440b863d02f99457f48520c16c609eae73f12b
2026-08-13 01:27:30 +08:00