mirror of
https://github.com/MoonshotAI/kimi-code.git
synced 2026-08-23 07:37:18 +00:00
898 commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
c41fadf0f7
|
ci: release packages (#3062)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> |
||
|
|
5c661f4610
|
chore: sync web dist from code-app (#3061)
code-app: 221c548c98587fa1bd2aef458e1e20aaceece587 |
||
|
|
1e553fc73b
|
ci: release packages (#3049)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> |
||
|
|
95cede82b4
|
fix(agent-core-v2): stop the legacy video resolver from shadowing the media resolver (#3053)
#2593 replaced AgentVideoResolverService with the image+video AgentMediaResolverService and reduced videoResolverService.ts to a pure deprecated alias with no DI registration. #2909's squash merge restored the pre-#2593 file wholesale, bringing back the legacy class and its registerScopedService call. Both classes then registered the same token ('agentVideoResolverService') at the Agent scope and the legacy video-only resolver won on the production import order, so image kimi-file:// references reached the provider unresolved. Gateways reject the unknown scheme with a 400 ("unsupported image url"), the media-strip fallback then hid the image from the model, and pasted images only worked on undo-resend via the inline base64 fallback. Delete the legacy alias files and their index exports, drop the stale alias assertion, and pin the behavior with a klient e2e regression: a kimi-file image prompt part must reach the provider as a data: URL, never verbatim. |
||
|
|
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 |
||
|
|
04944f380a
|
ci: release packages (#2932)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> |
||
|
|
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 |
||
|
|
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. |
||
|
|
13857f3832
|
chore: rewrite pending changesets for the new changelog conventions (#3026)
* Rewrite pending changesets for the new changelog conventions * chore: drop the /tower changeset per reviewer request * chore: trim pending changeset entries further per reviewer feedback --------- Co-authored-by: bj456736 <bj456736@users.noreply.github.com> |
||
|
|
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 |
||
|
|
a7dc1ea284
|
fix(agent-core-v2): degrade media tool registration when the bound model alias is stale (#2985)
* fix(agent-core-v2): degrade media tool registration when the bound model alias is stale A restored session replays its persisted profile.bind without catalog validation, so the profile can carry a model alias that no longer resolves (e.g. the managed kimi-code models were removed from config.toml on logout). AgentMediaToolsRegistrar.refresh() called modelCatalog.getRequester() unguarded on that alias; the throw escaped the agent.status.updated listener and was reported as an [unexpected] Error2 (config.invalid) on startup. Catch the resolution failure and degrade to "no model": media tools stay registered off the profile-reported capabilities, just without a model-bound video uploader, matching the tryResolveRawModel style used elsewhere in the profile service. * test(agent-core-v2): reproduce the stale-alias regression with production-consistent collaborators A stale alias makes the real AgentProfileService report UNKNOWN_CAPABILITY, so the regression now binds unknown capabilities, asserts the tool stays unregistered without surfacing an [unexpected] error, and covers recovery once the alias resolves again. The rationale moves into the mediaToolsRegistrar file header per the package comment conventions. --------- Co-authored-by: Mira <bj456736@users.noreply.github.com> |
||
|
|
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> |
||
|
|
1cf617d769
|
fix(google-genai): preserve Gemini tool-call thought signature and trailing user text order (#2914)
* fix(agent-core-v2): preserve tool call extras in tool.call loop events * fix(google-genai): keep trailing user text before function results when merging --------- Co-authored-by: Selene <mahaoyang@corp.netease.com> |
||
|
|
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. |
||
|
|
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.
|
||
|
|
04d23e2dab
|
fix(agent-core-v2): unify text/binary classification for UTF-8 multibyte files (#2972) | ||
|
|
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. |
||
|
|
61591bce09
|
feat(agent-core-v2): bundle multiple skill activations into one prompt submission (#2934)
* feat(agent-core-v2): support grouped multi-skill prompt submissions Add IAgentSkillService.promptWithSkills: one or more skill activations are validated up front (an unknown or empty submission rejects with no side effects), recorded with a shared submissionId, and enqueued ahead of the prompt through the prompt queue's messagesBefore support, so the whole group materializes atomically as a single turn. Undo cuts, the transcript projection, and the undo precheck treat the group as one unit (stopping at the next anchor even when submission ids collide); hook-result messages are skipped like injections during those walks. Submit hooks run against every message of the group, and user-slash skill activations count as user-submitted content for the UserPromptSubmit hook's origin filter. Surface it through the contract layers: protocol gains submissionId on the user / skill_activation origins and on the skill.activated event (kap-server zod mirrored), klient exposes agentSkillContract.promptWithSkills with parity assertions, and the SDK grows session.promptWithSkills — implemented on the v2 engine and rejecting loudly on the deprecated v1 engine, which is otherwise untouched. * fix(agent-core-v2): reject empty skill lists in grouped prompt submissions - Validate that promptWithSkills receives at least one skill, enforced in the engine and as a non-empty constraint in the klient wire schema. - Restore the released versions and changelog sections for agent-core-v2, klient, and node-sdk that the branch cut had reverted. - Move statement-level narration into the owning file headers per the package comment conventions. - Align the hook-result undo tests with the reachable record ordering (hook results are recorded before the group materializes). * refactor(agent-core-v2): bundle grouped skill activations into the prompt message Replace the submissionId-correlated message group with a single bundled user message: the rendered skill blocks precede the caller's parts in the content, and every activation's metadata rides the prompt origin's new skillActivations field. The bundle is one anchor by construction, so undo needs no group-cutting logic and the messagesBefore prompt seam disappears; the submit hook fires once per submission. skill.activated still fires per skill (transient ops, live-only); resume rebuilds the per-skill view from the prompt origin. Contract chain (protocol, kap-server, klient, node-sdk) drops submissionId accordingly. * fix(agent-core-v2): keep bundled skill blocks out of prompt-facing projections - The transcript cold rebuild expands a bundled prompt's origin skillActivations back into per-skill markers (the live path already projects them from skill.activated events). - turn.started.prompt, the session title excerpt source, and the fork lastPrompt now derive from the caller's own parts, excluding the rendered skill blocks the engine prepends to the bundled content. - Drop the redundant undefined unions from the new origin fields. - Move the activateSkill test narration into the file header. |
||
|
|
ee564e5ec9
|
fix(agent-core-v2): persist token counting ledger so resume restores measured context size (#2969)
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
|
||
|
|
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> |
||
|
|
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> |
||
|
|
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. |
||
|
|
13d86f8b7b
|
ci: release packages (#2881)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> |
||
|
|
d60d8ff7e8
|
chore: downgrade plan viewer changeset from minor to patch (#2923) | ||
|
|
cd489955bb
|
chore: sync web dist from code-app (#2922)
code-app: af7ed8fa03278bbe2f988732b3195649e7a6f2bc |
||
|
|
6cf315b7bd
|
fix(pi-tui): stop GFM autolinks at CJK punctuation boundaries (#2917)
* fix(pi-tui): stop GFM autolinks at CJK punctuation boundaries marked's GFM autolink accepts any non-space characters after the domain and its backpedal strips only ASCII trailing punctuation, so CJK or full-width punctuation right after a bare URL was absorbed into the link text and href (`.../pull/232(本地` rendered as one anchor whose OSC 8 target contained raw CJK and opened a broken address). Register a CjkBoundaryUrlTokenizer (subclass of the upstream StrictStrikethroughTokenizer, which stays byte-identical for re-vendoring) that cuts the autolink match at the first CJK punctuation character before the ASCII backpedal. CJK ideographs inside the URL path itself are preserved. Guarded by new bare-URL CJK cases in test/markdown.test.ts and listed in pi-tui's local-divergence inventory. * fix(pi-tui): keep balanced full-width parens inside autolinked URLs Address review feedback on the CJK autolink boundary: cutting at the first full-width parenthesis anywhere in the match also truncated URLs that legitimately contain balanced full-width parens in their path (e.g. wiki disambiguation pages like .../wiki/中华人民共和国(1949年)). Full-width parens now follow GFM's ASCII-paren rule: a paren-depth scan keeps balanced pairs in the URL and only an unbalanced ( or ) terminates the match. Non-paren CJK punctuation still always terminates it. * fix(pi-tui): keep CJK punctuation inside balanced full-width parens Punctuation inside a balanced full-width parenthetical is deliberate URL content (e.g. .../wiki/中华人民共和国(北京,1949年)), so the non-paren CJK terminator now only applies at paren depth 0. Prose parentheticals contain spaces and never survive marked's match this far, and an unbalanced ( still cuts the match at the open paren. |
||
|
|
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. |
||
|
|
249d8faa34
|
fix(agent-core-v2): mint interaction ids engine-side (#2911)
* fix(agent-core-v2): mint interaction ids engine-side Self-hosted OpenAI-compatible endpoints may renumber tool call ids on every response (Bash_0, Bash_1, ...). The approval/question/user_tool facades used the provider toolCallId as the interaction id, so a repeated id was silently swallowed by client-side pending-interaction dedupe: the approval prompt never appeared and the turn parked forever (#2908). Interaction ids are now minted by the engine (approval_<uuid> / question_<uuid> / user_tool_<uuid>); the provider toolCallId stays on the payload for correlation. This matches v1 semantics, where the approval id was already a daemon-minted id independent of the tool call id. * fix(agent-core-v2): normalize duplicate provider tool call ids at ingestion Self-hosted OpenAI-compatible endpoints may renumber tool call ids on every response (Bash_0, Bash_1, ...), and every downstream keying assumes an id identifies exactly one call: context rebuild silently drops the second tool result with a duplicated id, the strict projector discards duplicate calls, transcript frames merge, and approval/activity correlation misfires. A per-agent ToolCallIdNormalizer in the llmRequester stream boundary now tracks ids already claimed (seeded from the restored context). The first occurrence passes through unchanged; later occurrences — across responses or within one — are rewritten to a readable <id>__<n> suffix, kept consistent between streamed deltas and the finalized message, and logged for provenance. A failed attempt rolls its claims back so a projection retry re-streams the same logical calls under the same ids. * fix(agent-core-v2): thread the minted approval id through events and status The permission.approval.requested/resolved events only carried the provider toolCallId, so AgentActivityView exposed approvalId = toolCallId and the agent.status.updated approval phase forwarded an id that POST /sessions/{sid}/approvals/{id} cannot resolve — the kernel parks under the minted approval_<uuid>. Mint the interaction id at the agent call site and include it in the approval request payload: the kernel honors the explicit id, the events carry it, and the activity view keys pendingApprovals by it (falling back to the toolCallId for id-less events). * fix(agent-core-v2): surface minted interaction ids in facade listPending The approval/question facades returned only the original payload from listPending(), so once the kernel id stopped deriving from the provider toolCallId, hosts listing pending requests had no id to feed back into decide()/answer()/dismiss() without reaching into the kernel. Merge the parked interaction id into each returned request — the klient contract schemas already carry the optional id field, so the RPC surface becomes round-trippable as well. |
||
|
|
53909d91e3
|
fix(kimi-web): cache content-hashed assets (#2865) | ||
|
|
eb72aebeeb
|
fix(kap-server): remove the 64 MiB web session export limit (#2910) | ||
|
|
245e3d56a6
|
fix(tui): sanitize background task output (#2863)
Co-authored-by: liruifengv <liruifeng1024@gmail.com> |
||
|
|
102984aa66
|
fix: settle cancelled MCP OAuth callbacks (#2899)
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-windows (push) Waiting to run
CI / lint (push) Waiting to run
CI / typecheck (push) Waiting to run
Nix Build / Check flake.nix workspace sync (push) Waiting to run
Nix Build / nix build .#kimi-code (push) Blocked by required conditions
Release / Native release artifact (push) Blocked by required conditions
Release / Publish native release assets (push) Blocked by required conditions
Release / Release (push) Waiting to run
Release / Deploy docs (push) Blocked by required conditions
Co-authored-by: yuchengzhen <yuchengzhen@moonshot.cn> |
||
|
|
4a93f70aa2
|
feat(oauth): add browser-safe ./device subpath export (#2885)
Some checks are pending
CI / test (2) (push) Waiting to run
CI / test (3) (push) Waiting to run
CI / test (4) (push) Waiting to run
CI / test (5) (push) Waiting to run
CI / test-pi-tui (push) Waiting to run
CI / test-windows (push) Waiting to run
CI / lint (push) Waiting to run
CI / typecheck (push) Waiting to run
CI / build (push) Waiting to run
CI / test (1) (push) Waiting to run
Nix Build / Check flake.nix workspace sync (push) Waiting to run
Nix Build / nix build .#kimi-code (push) Blocked by required conditions
Release / 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(oauth): add browser-safe ./device subpath export * fix(oauth): guard env override lookup for browser consumers * chore(oauth): add changeset for ./device subpath export * fix(oauth): resolve env overrides via globalThis for DOM-only consumers |
||
|
|
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>
|
||
|
|
6be26978b1
|
feat: auto-generate session titles via the managed chat_title tool (#2351)
* feat: auto-generate session titles via the managed chat_title tool
With the auto-title experimental flag on and a managed OAuth login, the
session title is generated from the first prompt, replacing the
truncated-prompt easy title. A custom title set by the user is never
overwritten, and generation failures degrade silently to the easy title.
- oauth: fetchChatTitle for the platform /tools chat_title method
- agent-core (v1): fire-and-forget generation on the first prompt
- agent-core-v2: sessionTitle domain watching the easy-title event
- kap-server: POST /sessions/{id}/title/generate for manual regeneration
* fix: harden auto-generated session titles
* fix: preserve managed title request headers
* Pair auto-title endpoint overrides with matching OAuth credentials
* fix: preserve legacy custom session titles
* fix: preserve automatic session title invariants
* refactor: keep only the on-demand session title generation interface
Drop the automatic wiring on both engines: the v1 (TUI) first-prompt
trigger and the v2 easy-title event watcher. SessionTitleService's
generateTitle() stays as the single on-demand entry point behind the
auto-title flag, backing the kap-server title/generate route. The
changeset goes away too: with no shipped consumer, the remaining
surface is not user-perceivable.
* feat: generate session title from the first recorded prompts
Record up to three sanitized natural-language prompts in session
metadata (skill / plugin activations excluded) and compose the
chat_title input as order-labeled lines truncated to a 1000-char
budget, falling back to lastPrompt for sessions without recorded
prompts.
* test: make session title race tests deterministic
* Generate session titles from agent conversation history
* fix: reject title generation without user prompts
* fix: bound session title prompt history
* feat: enable session title generation without an experimental flag
* test: cover session title generation through the public REST path
* feat: request session title generation from the TUI after each turn
* Retry auto title generation for prompt-derived session titles
* feat: record session title source and harden the generation lifecycle
- persist titleSource (prompt/generated/custom); skip auto-generation over
an already-generated title unless forced, and never over a custom one
- plumb the force option from the core through klient and node-sdk to the
REST title/generate endpoint
- drop the title write-back when the session scope was superseded
mid-flight, and retry once with a force-refreshed token on a 401
- stop closing sessions a concurrent public resume has handed out in the
temporary resume paths (generateSessionTitle, renameSession)
- accept session.meta.updated patches without lastPrompt in klient event
validation, and emit exactly one metadata event per applied title
- remove the retired prompts field heal and drop the changeset (the
behavior is only perceivable on the experimental v2 engine)
* chore: follow agent-core comment convention
* fix: ignore stale session title callbacks
* fix: preserve session title state invariants
* refactor: seed session lifetime instead of querying the workspace handler
The session title service must not depend on the Workspace-tier handler
registry. The handler now seeds each session scope with an abort signal,
fires it synchronously when a close begins, and the title service carries
the signal on its request, drops the write-back once aborted, and drains
an in-flight generation through the onWillCloseSession hook.
* fix: honor the legacy custom title marker over a stale titleKind
A v1 rename spreads the original state.json document, so an explicit
isCustomTitle: true can travel with a stale titleKind. The explicit
marker now wins on load, and every persist double-writes the derived
isCustomTitle so released v1 builds keep recognizing the custom title.
* fix: serialize session access and expose the session title state
The temporary resume/rename/close paths and the public lifecycle
operations now share a per-session queue, so a public resume can never
receive a handle whose cleanup close is already in flight. Session
summaries carry the canonical title state, letting the TUI skip title
generation for sessions whose title was already generated or customized
instead of re-asking after every turn.
* chore: add session title changesets
* fix: close the session lifecycle races around close and title generation
A close/archive is now tracked in a closing registry from its first
synchronous step until disposal: get/list hide the closing session and
resume waits the close out instead of returning the doomed handle, and
fork waits out an in-flight source close. The title service tracks the
whole generateTitle call as the unit the close hook drains, and the
generated-title write re-checks the lifetime signal inside the serialized
metadata update so an abort landing while the update is queued still
vetoes the write-back.
* feat: project the session title state through the session index
readSummary and the read-model mirror carry titleKind, so listSessions
reports the same canonical title state as a resumed session's summary.
* fix: serialize the remaining session access paths in the SDK
forkSession and explicit-id createSession join the per-session queue, and
the harness resume fast path skips a session whose close is in flight
instead of returning the closing facade (which then failed every call
with session.closed); its late onClose no longer evicts the fresh
session either. The harness rename event now carries isCustomTitle so
the TUI stops asking for a generated title after a local rename.
* fix: detach the external abort listener once the chat title request settles
* fix: harden the session close/archive and create/fork lifecycle
The closing registry now records the operation kind: an archive arriving
during a plain close waits it out and lands the archived flag on the
persisted document instead of riding the close to success, and a failing
close hook no longer strands a half-closed session — the teardown always
completes while the hook error still reaches the caller. create and fork
reserve their target id synchronously with the existence check, so a
concurrent create/fork of the same id loses up front and can never tear
down the winner's scope or directory.
* fix: keep forced title regeneration independent and veto queued title writes atomically
Plain generateTitle calls still coalesce onto one shared in-flight
generation, but a forced regeneration always runs on its own so it is
neither swallowed by a plain call's early exit nor shares its result; the
close hook drains every active generation. The allowWhen veto now runs
inside applyUpdate with no await between the check and the mutation, so
an abort cannot slip into the gap.
* fix: carry the title state through the session index and klient contract
The klient session summary schema no longer strips titleKind, and the
index readSummary honors a legacy isCustomTitle marker over a stale
titleKind, so listSessions reports the same canonical title state as a
resumed session.
* fix: coalesce harness resumes, lock fork targets, and cover the title state end to end
Concurrent public resumeSession calls now share one in-flight resume and
one facade instead of building parallel facades over the same engine
handle (a close on either would strand the other). forkSession takes the
source and target queues in sorted order, so fork(A->X) is atomic against
create(X) and fork(B->X) without an ABBA deadlock. The emitMetaUpdated
patch type drops the redundant undefined union, and the SDK tests now
cover facade coalescing and the title state across list and resume.
* fix: serve the canonical title state from the session index and version the read-model cache
readSummary now derives the title state with the same priority chain as
the metadata document's canonical normalization (explicit custom marker,
valid titleKind, legacy false marker, customTitle, plain title), so list
and resume agree on legacy documents too. Read-model cache entries carry
a summary version stamp and older-stamped entries are treated as cold
misses, so an upgraded reader never serves a stale-shaped summary.
* fix: let the newest title generation request win the write-back
A forced regeneration could be followed on disk by an earlier plain
call's slower backend response. Each generation now carries a
monotonically increasing sequence (assigned only once a request actually
proceeds to generation), and the serialized metadata write is vetoed
unless the writer is still the newest request.
* fix: fold archive into close and own the create/fork rollback
An archive requested during a plain close is applied through the live
metadata during the teardown (or lands on the persisted document when it
arrives too late or the close fails), publishes the archived event, and
works on cold sessions too. A resume waiting on a failed close retries
instead of propagating the hook error, the teardown completes even when
the agent drain fails, and the create/fork rollback only ever removes
its own handle — a loser of the reservation race can no longer tear down
the winner's live scope.
* fix: key harness resume coalescing by the full input
Concurrent resumes only share a facade when their inputs match — a
caller passing different dirs, replay, profile, or kaos options gets its
own resume instead of having its options silently dropped.
* refactor(agent-core-v2): drop session close-awareness from title generation
Auto title is best-effort: a generation racing session close no longer
cancels its fetch or guards its write-back, so the per-session
sessionLifetime AbortSignal seed, the onWillCloseSession drain, and the
close-time invalidation go away. The newest-request-wins write-back
predicate stays.
* Delete .changeset/sdk-session-title-kind.md
Signed-off-by: 7Sageer <sag77r@hotmail.com>
* refactor(session-title): drop the unused force regeneration path
Nothing calls force: with it gone, plain calls always coalesce onto the
shared in-flight generation, so the generation sequence and the
caller-supplied allowWhen veto lose their only purpose and go with it.
The title/generate REST route takes no body anymore.
* refactor(agent-core-v2): drop the title state projection from the session index
The listed-session titleKind had no consumer: the TUI's title-generation
gate seeds from the resumed summary, which reads the live metadata
document, and the kap-server REST wire never carried the field. Removing
the projection also retires the read-model summary version stamp (the
remaining shape is fully field-checkable) and the duplicate title-kind
derivation that had to stay in lockstep with sessionMetadata. The klient
list contract and the node-sdk list mapper drop the field with it; the
resumed/live summary still reports the canonical title state.
* refactor(agent-core-v2): inline the transcript live-tail merge into messageLegacy
mergeContextTranscriptWithLive had a single caller; move the logic into
messageLegacyService as the private mergeLiveTail and drop the export.
* refactor(agent-core-v2): drop the closing registry from the session lifecycle
Auto title no longer consumes close-awareness, so the machinery goes
back to the simple forms: close/archive run straight through, resume
no longer waits out an in-flight close, create/fork drop their target
reservation, and a cold archive is a no-op again. Reverts the behavior
of
|
||
|
|
5912d4c7d1
|
fix: stop repeated file-watcher errors on Windows drive-root and UNC workspaces (#2876) | ||
|
|
b6144f94ea
|
ci: release packages (#2846)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> |
||
|
|
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
|
||
|
|
504e6292ed
|
feat(mcp): inspect effective authorization state in v1 (#2856)
* feat(mcp): inspect effective authorization state * test(agent-core-v2): register MCP auth coordinator fixture * fix(mcp): validate runtime names against full catalog * fix(mcp): reconnect after pending auth updates * docs(mcp): describe auth coordinator collaborator * fix(mcp): ignore disabled runtime name collisions * fix(mcp): serialize OAuth token refresh * test(mcp): await OAuth credential writes * fix(mcp): queue trailing credential reconnect * fix(oauth): preserve access-only refresh winners * fix(mcp): preserve legacy offline auth state * fix(mcp): redact inspection credentials * refactor(mcp): keep app inspection on v1 * fix(mcp): guard legacy auth status mutations * fix(mcp): avoid deterministic legacy auth probes * fix(mcp): cover initialization credential updates --------- Co-authored-by: 刘仲诺 <liuzhongnuo@dev.msh.team> |
||
|
|
fe3cdae5f8
|
fix(agent-core-v2): drop interrupted thinking-only assistant messages at step settle (#2819)
* fix(agent-core-v2): drop interrupted thinking-only assistant messages at settle A turn interrupted while the model is still streaming thinking leaves the open assistant holding only an unsigned thinking fragment. The fold used to seal it into history because a non-empty thinking block is not vacuous; on OpenAI-compatible providers the serialized message then carries neither content nor tool_calls, and strict gateways reject every later request with a 400 (#1404). Treat unsigned-thinking-only content as unsendable at settle so the fold drops the message instead — replaying the records of an already bricked session repairs it. * fix(agent-core-v2): preserve reasoning-only assistant history --------- Signed-off-by: 7Sageer <sag77r@hotmail.com> |
||
|
|
30f56a2d2d
|
fix(agent-core-v2): disable SDK-internal retries that blocked cancellation (#2855)
* fix(agent-core-v2): disable SDK-internal retries that blocked cancellation
The OpenAI and Anthropic SDK clients default to maxRetries=2 with a
backoff sleep that never observes the request AbortSignal, so Ctrl+C
during a 429/5xx/connection-error retry only took effect after the
sleep elapsed, and the hidden attempts were invisible to the engine
(no turn.step.retrying) while double-counting its retry budget.
Build those clients with maxRetries: 0 so retryable failures surface
to the engine's step-retry layer immediately (observable countdown,
abortable sleep, single retry budget). The Google GenAI main request
path only retries when httpOptions.retryOptions is explicitly set, so
there is nothing to disable; instead its error converter now recovers
the server-directed delay from the wire body's google.rpc.RetryInfo
detail, since the SDK's ApiError drops the Retry-After header.
* chore: simplify the retry-cancellation changeset entry
* fix(agent-core-v2): recover GenAI retry delay from prefixed mid-stream error chunks
Mid-stream error chunks throw ApiError with the message wrapped as
"got status: <STATUS>. {json}", so a strict JSON.parse of the whole
message missed the google.rpc.RetryInfo detail. Locate the JSON object
start before parsing; the non-stream path (pure JSON body) is
unaffected.
|
||
|
|
ec84a6f9a3
|
feat(kimi-code): re-baseline pi-tui on upstream v0.84.1 and add fullscreen tui_mode (#2830)
* feat(kimi-code): re-baseline pi-tui on upstream v0.84.1 and add fullscreen tui_mode Re-baseline the vendored pi-tui fork on upstream @earendil-works/pi-tui v0.84.1, keeping all local patches: narrow-terminal hardening, processed-line render caching (re-implemented into TuiMainScreen), editor history hooks, the paste-burst fallback, and multi-root @ completion. Upstream highlights absorbed: the renderer splits into TuiMainScreen and TuiAltScreen behind a TUI interface, the Markdown component gains opt-out LaTeX rendering (disabled on the kimi-code side), paste-registry repair on delete/undo, Windows input-latency and Shift+Enter fixes, and Kitty image layout fixes. Editor.setText gains a preservePasteRegistry option so paste-marker expansion survives wholesale text replacement. New tui_mode = "fullscreen" preference mounts TuiAltScreen: the transcript lives in a primary ScrollView with follow-end, the chrome docks at the bottom, mouse selection and scrollbar come from the renderer, and full-screen viewers (tasks browser, output viewer, approval preview) swap the layout root via screen-takeover. Viewport navigation keys fall through to the focused component when the primary scroll view cannot scroll. * feat(pi-tui): merge upstream main through 40a3d85 (post-0.84.1) Bring in upstream's merged-but-unreleased changes on top of the v0.84.1 re-baseline: - Fullscreen transcript search (ctrl+shift+f, next/previous navigation) - Alternate-screen render-churn reduction (9-18x less per-frame allocation by painting full-width rows as direct line references) - Unbound single-line scroll actions (tui.altScreen.lineUp/lineDown), wired into the fork's canScroll gating like the other viewport keys - SSH-aware escape-timeout default and PI_TUI_ESC_TIMEOUT override - Search snapping and SGR-mouse fragmentation fixes; LaTeX newline argument fix Conflicts resolved by union: upstream's search/line scroll bindings stay ungated, fork's primaryScrollable guard applies to all scroll actions. * fix(kimi-code): keep fullscreen dock from crushing the editor box The fullscreen layout gave the transcript ScrollView its intrinsic content height as basis and let the dock participate in shrink distribution with no minSize. Once the transcript exceeded the screen, the VStack shrink pass crushed the dock to a couple of rows, and the editor (3 rows: top border / input / bottom border) lost its bottom border row to clipping. Adopt pi's sizing contract: the ScrollView starts from basis 0 and grows, the dock keeps its intrinsic height, the editor never shrinks below 3 rows, and the footer below 1. Adds a VirtualTerminal-level regression test that replays a full streaming cycle in fullscreen. * docs(kimi-code): document the tui_mode preference in tui.toml * fix(pi-tui): let terminal focus reports fan out in fullscreen TuiAltScreen's viewport input listener consumed FOCUS_IN/FOCUS_OUT reports. Since the renderer installs that listener at construction — before any app-level listeners — terminal focus tracking and clipboard-image hints never saw focus transitions in fullscreen mode (notification_condition = "unfocused" went blind, refocus clipboard hints stopped). Keep the selection cleanup but stop consuming, matching the main-screen fan-out. Addresses Codex review on PR #2830. * fix(kimi-code): wire openUrl and right-click paste in fullscreen Mouse capture in the alternate screen intercepts the terminal's native link activation, leaving OSC 8 hyperlinks (like the footer's PR link) unclickable in fullscreen. Route renderer link clicks to the app's openUrl, and on Windows feed right-clicks to the focused component as a bracketed paste read from the clipboard. * feat(kimi-code): fullscreen prompt navigation, exit replay, progress resync - Mark user/assistant transcript messages with OSC 133 zones (start / end / final) so the fullscreen renderer's Ctrl-Shift-Up/Down prompt jumps work; GutterContainer keeps the markers at byte 0 when prefixing its gutter, and message render caches store already-marked lines. - On exit from fullscreen, preserve the frame and replay the transcript through a fresh main-screen renderer so native scrollback gets the regular inline layout (pi's "transcript" exit form). - Re-sync the OSC 9;4 progress indicator after a stop/start cycle: terminal.stop() clears it, and the cached progressActive flag used to suppress the re-send when returning from the external editor mid-turn. * feat(kimi-code): enable Markdown LaTeX rendering with a render_latex opt-out Align with the upstream pi-tui default: LaTeX math in Markdown messages renders as Unicode text. The explicit renderLatex:false we set during the re-baseline becomes a shared Markdown options helper fed by a new tui.toml preference (render_latex, default true), wired at startup and refreshed on /reload. * refactor(kimi-code): gate fullscreen behind KIMI_CODE_TUI_FULL_SCREEN Drop the public tui_mode preference from tui.toml before release; the fullscreen UI is experimental, so enable it with the KIMI_CODE_TUI_FULL_SCREEN=1 env var instead. Docs move from the config-file reference to the env-vars page. * chore(changesets): clarify fullscreen mode and LaTeX formula entries * chore(changesets): trim fullscreen mode entry * chore(changesets): trim LaTeX formula entry * chore(changesets): drop redundant kimi-code entries * test(kimi-code): add stepRetry to fullscreen layout fixture after main merge * fix(kimi-code): apply render_latex before theme-driven Markdown rebuilds Codex review on PR #2830: applyReloadedTuiConfig set the shared LaTeX toggle after applyTheme(), but theme application invalidates transcript components and their rebuilt Markdown children copy the options at construction — so a /reload that only flipped render_latex kept the old value until some later invalidation. Move the setter before applyTheme and pin the ordering with a test. * fix(kimi-code): carry renderLatex through TUI config saves Codex review on PR #2830: currentTuiConfig omitted renderLatex, so saving an unrelated preference (theme/editor/upgrade/cache-hint) serialized render_latex as the default true and silently reset a user's opt-out. Carry the appState value through the shared save payload. * feat(kimi-code): report tui_mode in lifecycle telemetry Tag startup_perf and exit events with the active renderer mode (regular/fullscreen) so fullscreen adoption is measurable while it is gated behind KIMI_CODE_TUI_FULL_SCREEN. |
||
|
|
3b0936d8e0
|
fix(agent-core): only discover the root SKILL.md for plugin skills fallback (#2847)
When a plugin manifest omits `skills` and the plugin root contains a SKILL.md, the fallback treated the whole plugin root as a generic skill scan directory, so sibling Markdown files such as CHANGELOG.md were misidentified as skills and inflated the plugin skill count. Mark the fallback root as root-skill-only so discovery parses only the root SKILL.md; explicit `skills` entries (including "./") keep the directory scan semantics. Applied to both agent-core and agent-core-v2. |
||
|
|
43c68f58f5
|
feat(agent-core-v2): keep session updatedAt stable across meta management writes (#2815)
* feat(agent-core-v2): keep session updatedAt stable across meta management writes Rename, archive/restore, and fork no longer bump a session's updatedAt, so recency-sorted session lists stop reshuffling on management actions: - setTitle/setArchived pass touchUpdatedAt: false; an explicit patch.updatedAt always wins (fork inherits the source's recency, so a fork lands next to the source instead of floating to the top) - new SessionMeta.archivedAt records the archive moment (cleared on restore) and is surfaced through the session index, the v1/v2 session routes (archived_at), and the klient contract, so the archived list keeps an accurate archive time without relying on the updatedAt bump * fix(agent-core-v2): normalize a legacy ISO-string updatedAt when forking a cold session A cold legacy/v1 state.json read from disk can still carry an ISO-string updatedAt; passing it through as the fork's explicit patch.updatedAt would persist a string into the v2 metadata. Normalize with toEpochMs (falling back to now when absent/unparseable). * fix(agent-core-v2): write fork metadata after agent recreation Registering each copied agent during fork is an ordinary metadata write that bumps updatedAt, which overwrote the inherited source recency and still floated normal forks (sessions with agents) to the top. Move the fork's metadata update after the agent recreation loop so the inherited updatedAt is the final write. * fix(agent-core-v2): preserve persisted recency when restoring a cold session Resume creates the main agent for a cold session that has no persisted agents.main entry (e.g. an empty session), and that registration bumps updatedAt — so unarchiving an empty session still floated it to the top. Capture the index summary's updatedAt before resume and re-apply it in the restore write (archived:false, archivedAt cleared, explicit updatedAt wins over the bump). * fix(agent-core-v2): make agent registration non-touching for recency Registering an agent is a structural write, not content activity — but it went through an ordinary metadata update that bumped updatedAt. That reordered recency-sorted listings whenever materialization created an agent: resume of a cold session without a persisted agents.main (so archive-via-resume and restore of empty sessions still floated), and runtime subagent registration mid-turn. registerAgent now passes touchUpdatedAt: false; restore goes back to the plain unarchive write and no longer needs the capture/reapply workaround. * fix(agent-core-v2): duplicate cron tasks only after the fork metadata is durable With the metadata write moved after agent recreation, cron duplication ran before it — a rejected metadata update left cloned cron records pointing at a fork whose directory the catch block just removed. Keep cron duplication after the durable metadata write. * style(agent-core-v2): fold new invariants into module headers The package convention keeps comments in the top-of-file block only — move the touchUpdatedAt precedence, non-touching registration, and fork ordering notes out of statement-level positions into the respective module headers. * chore: scope the changeset to agent-core-v2 |
||
|
|
c212ae9715
|
fix(kimi-code): show MCP launch targets in the workspace trust prompt (#2843)
* fix(kimi-code): show MCP launch targets in the workspace trust prompt Render each gated project MCP server's launch target (transport, command, args, cwd, or url) in the workspace trust prompt without leaking env or header secrets, stripping terminal control characters from the workspace-supplied text, default the prompt to "Don't trust", and resolve fd binaries to absolute paths so untrusted workspaces cannot plant a bare-name fd executable that runs before trust confirmation. * fix(kimi-code): resolve stty to an absolute path before the trust gate |
||
|
|
f6ee44e426
|
ci: release packages (#2710)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> |
||
|
|
101c4d1997
|
feat(agent-core-v2): remove Agent and AgentSwarm from builtin profile tool lists (#2837)
* feat(agent-core-v2): remove Agent and AgentSwarm from builtin profile tool lists The builtin agent and coder profiles no longer expose the Agent and AgentSwarm tools, so sessions on the v2 engine do not offer subagent delegation by default. The tools themselves remain registered; profiles that list them explicitly can still opt in. * feat(agent-core): remove Agent and AgentSwarm from builtin profile tool lists Align the v1 builtin agent/coder profiles with the v2 change: the default profiles no longer offer subagent delegation, while the tools stay registered for profiles that list them explicitly. The parity projection drops v1's inactive Agent/AgentSwarm roster entries: v1 reports registered-but-inactive builtin tools where v2 only registers the tools a profile lists, so an inactive entry has no v2 counterpart. Active entries still compare in full. * fix: keep Agent and AgentSwarm in the builtin agent profile Scope the removal to the coder subagent profile on both engines: the main agent keeps Agent/AgentSwarm so default sessions can still delegate, while coder subagents no longer spawn nested subagents by default. Snapshots and token counts shift only for the embedded coder tool list; the v1 parity projection needs no change since the main agent rosters match again. |
||
|
|
68ce3c7a0c
|
chore: sync web dist from code-app (#2840) | ||
|
|
df8ce73e45
|
feat(kimi-code): show step retry progress in the activity indicator (#2825)
* feat(kimi-code): show step retry progress in the activity indicator Wire the engine's turn.step.retrying event into the TUI: while a failed model request is backing off for another attempt, the waiting spinner shows 'retrying (N/M) · errorName · in Xs' with a dim detail line for the status code and provider error message, and the loading tip is suppressed. The retry state clears on the step's terminal events (completed / interrupted), turn.ended, and tool.result. It intentionally survives turn.step.started because the v2 engine re-emits that event for every retried attempt of the same step. * fix(kimi-code): show the retry indicator for mid-stream failures A retryable failure raised after thinking/assistant deltas had already streamed left the pane in thinking/composing mode, so the retry label and detail never rendered during the backoff. Drive the pane and the streaming phase back to waiting when a retry begins. * fix(kimi-code): drop the stale retry countdown once the attempt starts The v2 engine re-emits turn.step.started when the retried attempt begins running after the backoff sleep. Track a backoff/attempt phase so the label keeps showing the retry attempt and error but drops the already-elapsed 'in Xs' countdown, instead of either clearing the state or showing stale timing through a slow attempt. * fix(kimi-code): advance the retry phase on a timer instead of step starts The legacy engine retries inside the same step and never re-emits turn.step.started, so the backoff-to-attempt transition keyed on that event never fired there and the stale countdown stayed up through the attempt. Schedule the flip from delayMs instead, which matches when both engines actually start the next attempt, and drop the step-start hook. * fix(kimi-code): cancel the retry phase timer on TUI shutdown A pending backoff timer survived KimiTUI.stop(), keeping the event loop alive and firing setAppState against a disposed UI when stop() runs without an immediate process exit. Expose the timer cleanup and invoke it from the shutdown path. * fix(kimi-code): align the retry detail line with the spinner label * fix(kimi-code): capitalize the retry spinner label |
||
|
|
3c9e3b297c
|
feat(kimi-code): paginate the session picker list (#2826)
* feat(kimi-code): paginate the session picker list The /sessions picker and kimi -r used to materialize the full session list before showing anything, which gets slow with hundreds of sessions. - node-sdk: add listSessionsPage (limit/before -> items + nextCursor); the v2 engine pages through the session index (draining past entries whose workDir is unrecoverable), the v1 engine answers one full page - TUI: open the picker on the first page, fetch the next page when the cursor reaches the fetched end, and drain remaining pages in the background once a search query is typed so search still covers all sessions - kimi -r now fetches a one-item page for the latest session * chore: simplify session picker changeset * fix(kimi-code): join in-flight page fetch in session search drain A query typed while a scroll-triggered page fetch was still running stopped the background drain at the loadingMore early return, leaving the search covering only the pages fetched so far. fetchMoreSessions now optionally joins the in-flight fetch and continues with the next page; scroll triggers still drop when busy. |