mirror of
https://github.com/MoonshotAI/kimi-code.git
synced 2026-08-24 16:17:57 +00:00
1217 commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
71ff2a0fff
|
fix(kimi-code): close pre-trust-gate bare command resolution on Windows (#2695)
On Windows, cmd.exe / CreateProcess resolve a bare command name from the current directory before PATH. Several startup-path child processes ran before the workspace trust prompt, so a binary planted in an untrusted workspace (stty.exe, npm.cmd, fd.exe) could execute before the user confirmed trust. - skip the POSIX-only stty save/restore entirely on win32 - defer fd detection from the KimiTUI field initializer to startBackgroundFdAutocomplete(), which runs after the trust gate - add resolveCommandPath(): resolve commands through PATH (PATHEXT-aware on win32) to an absolute path and refuse hits inside the cwd - route update-preflight package-manager spawns and the npm global-prefix probe through it - run the workspace trust prompt before the migration branch as well, closing the blind spot where a pending ~/.kimi migration skipped it - document the no-bare-command-before-trust-gate rule in apps/kimi-code/AGENTS.md |
||
|
|
2acf22f66e
|
refactor(agent-core-v2): invert sessionLifecycle/MCP dependency via lifecycle event (#2803)
- add onWillCreateSession to ISessionLifecycleService: a synchronous participation event fired before a session's services activate, exposing a session-domain facade (readSeed / contributeSeed / onSessionDispose) - workspaceMcp subscribes and activates ephemeral-server overlays itself: the configs travel as the new ISessionEphemeralMcpServers session seed, the stdio cwd is read from ISessionContext, the merged ISessionMcpHandle is contributed over the seed adapter's workspace projection, and the overlay shutdown is attached to the session's teardown - sessionLifecycle drops its IWorkspaceMcpService dependency, the overlay tracking map, handle-dispose wrapping, and the dispose backstop - rename ScopeOptions.extra to seeds and ScopeOptions.assemble to configureContainer |
||
|
|
0401ec4286
|
refactor(agent-core-v2): extract btw into a features/btw Feature unit (#2724)
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
Release / Publish native release assets (push) Blocked by required conditions
CI / test-pi-tui (push) Waiting to run
CI / test-windows (push) Waiting to run
CI / lint (push) Waiting to run
CI / 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
- move session/btw to features/btw, mirroring the plan feature layout - contribute ISessionBtwService at Session scope through BtwFeature (contributeService) instead of a static registerScopedService call - keep the package root exports unchanged; move the test to test/features/btw |
||
|
|
01c74e9372
|
fix(agent-core): isolate builtin profile catalogs per session (#2740)
Some checks failed
CI / build (push) Has been cancelled
CI / test (1) (push) Has been cancelled
CI / test (2) (push) Has been cancelled
CI / test (3) (push) Has been cancelled
CI / test (4) (push) Has been cancelled
CI / test (5) (push) Has been cancelled
CI / test-pi-tui (push) Has been cancelled
CI / test-windows (push) Has been cancelled
CI / lint (push) Has been cancelled
CI / typecheck (push) Has been cancelled
Nix Build / Check flake.nix workspace sync (push) Has been cancelled
Release / Release (push) Has been cancelled
Release / Native release artifact (push) Has been cancelled
Nix Build / nix build .#kimi-code (push) Has been cancelled
Release / Deploy docs (push) Has been cancelled
Release / Publish native release assets (push) Has been cancelled
|
||
|
|
437a1b8ba1
|
fix(sdk): probe MCP auth status through connection (#2731)
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 / 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
|
||
|
|
0b2e803d5e
|
feat(sdk): expose global MCP auth status (#2706)
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
|
||
|
|
7cd64766c8
|
feat: isolate the full-text search index from the session index and the main thread (#2701)
* feat(minidb): instrument open lifecycle with phase timings and status Add MiniDb.lifecycleStatus() exposing the no-generation/generation-load/ wal-catch-up/full-rebuild/ready/degraded state machine plus per-phase timings (generation candidate load, store/non-text/text image load, postings integrity check, WAL scan/apply, full recovery, text rebuild hosting), so snapshot load, WAL catch-up and full rebuild can be told apart in diagnostics. Also add a repeatable open-lifecycle bench (small data, large WAL delta, large full-text generation, corrupt generation) and fixtures proving a healthy generation open performs no full-corpus tokenization while a corrupt or missing generation falls back. Log search-index and query-store open diagnostics in kap-server and agent-core-v2 so a listSessions call can be attributed to the database it touches. No persistence format or product behavior change. * feat(agent-core-v2): isolate the session index from the global search index Harden the separation between the session read model and the full-text search index so session operations never depend on search availability: - Reject text index definitions in MiniDbQueryStore at definition level, keeping the session query-store a structural-only read model with no postings/tokenizer artifacts, and assert its generation carries no full-text files. - Share one authoritative scan between the first list and the initial projection (single-flight) instead of scanning twice; reads may only join an in-flight scan, and every fallback read folds the mirror's pending queue so read-your-writes holds while preparing. - Keep withReadModel() fallback semantics pinned by tests: uninitialized/preparing reads hit authoritative metadata immediately, ready reads use the read model, degraded keeps falling back with a diagnosable status reason. - Guard session metadata writes so a mirror failure degrades only the read model and never fails the session lifecycle. - Prove via tests that listSessions/--resume/--continue never open the global search DB (including when search-index is unopenable), and that only real full-text search requests report building/stale/degraded. * perf(minidb): slice open-time work so it never blocks the main thread Make the whole generation-open path cooperative: - Replace the synchronous postings/store CRC verification with chunked async variants (readGenerationFileCheckedAsync, verifyFileIntegrityAsync) that keep the exact bytes/crc-mismatch error semantics. - Give the WAL-delta apply a primitive-op + wall-clock budget (walApplySlicer), so a batch frame unrolling into thousands of ops can no longer run as one uninterruptible slice; torn-tail, corrupt-batch and read-only behaviors are unchanged. - Slice the big attach loops: Store.bulkLoadRefsAsync + SkipList.bulkLoadAsync for the store image, async parsers and loadImageAsync for secondary/compound images, and TextIndex.attachImageAsync for the docs/dictionary map construction. - Queue text builds on worker-slot pressure (WorkerSlots.acquireBounded, bounded by MiniDb.textBuildSlotWaitMs, abort-aware) instead of falling back to an unbounded inline build; a persisted drought hosts the bounded inline core as the explicit last resort with stats accounting. Bench (bench/open-lifecycle, seed 42): event-loop delay max across the four open scenarios drops from 45/734/331/492 ms to ~12-28 ms with wall time flat or better. * feat(kap-server): run the global search index in a dedicated worker Move the whole search-index MiniDb lifecycle (open, generation load, WAL replay, sync, rebuild, compaction) off the main thread into a long-lived worker_threads host, so it never shares the event loop with TUI input: - Add a versioned request/response protocol and worker entry hosting a host-agnostic SearchIndexCore; the same core also backs an inline backend kept as the explicit rollback (KIMI_CODE_EXPERIMENTAL_SEARCH_WORKER=false, flag default ON). - The worker exclusively owns the search-index handle. The lock token is reported at acquire time (new MiniDb OpenOptions.onLockAcquired hook) and reaped on dirty exit; an orphan-lock detector (same-pid lock row whose token no live holder owns) recovers the window where the token report is lost, so a mid-open crash can never freeze the index into a silent permanent read-only. - Crash handling: in-flight requests are rejected with typed errors, respawn uses capped exponential backoff, per-request watchdogs terminate wedged workers, and beginClose propagates into the worker so dispose stays bounded during a long sync. Page tokens pin a boot-salted generation, so tokens issued before a transparent worker restart fail closed with invalid_page_token. - The main process keeps the sync coordinator (debounce/coalescing/ single-flight), live transcript routing, query normalization and page-token codec; searches keep reading the published generation and report building/stale/degraded instead of waiting for sync/rebuild. - Wire the worker into the CLI packaging: self-contained worker bundles for npm dist and the SEA asset manifest/installer/smoke check, plus a dev runtime (type-stripping + .ts resolve hook) scoped to worker execArgv. * feat(kap-server): model search and session-index lifecycles explicitly Consolidate the two-index separation into explicit, diagnosable lifecycles: - Surface the global search state machine (stopped / opening / building / ready / degraded / closing) end to end: SearchIndexCore.lifecycleState, SearchWorkerHost lifecycle snapshots cached from RPC responses (and invalidated across worker generations), a never-throwing status() carrying the lifecycle, and a synchronous lifecycleReport() that neither kicks the open nor spawns the worker. Corrupt search-index rebuilds are announced with a dedicated warn log so building, stale, degraded, corrupt and worker-unavailable stay distinguishable. - Turn MiniDb read-only replica catch-up fully cooperative: catchUpWalAsync scans frames with the windowed async scanner and yields per primitive op on the shared walApplySlicer budget, while a per-instance catchUpChain serializes concurrent catch-ups so each caller keeps its atomic watermark advance. The stale synchronous implementations are removed. - Pin the dependency direction and availability timing with tests: session list/create/resume survive a corrupt or unopenable search index (also end-to-end with a dead query-store), search generation reuse and stale-serving keep working across restarts, concurrent cold callers open the index / spawn the worker exactly once, resume-then- fetchSessions performs no duplicate authoritative scan, and a clean dispose releases the lock and settles at stopped. - Document the experimental flag surface (persistence_minidb_readmodel, search_worker) in the root guide. * feat(agent-core-v2): default the session read model on and roll out the separation Rollout and validation for the index separation plan: - Flip persistence_minidb_readmodel to default ON (rollback via KIMI_CODE_EXPERIMENTAL_PERSISTENCE_MINIDB_READMODEL=false or the experimental config section); session list/--resume/--continue now always go through the isolated session read model with the authoritative fallback. Test harnesses pin the flag off where shared fixtures require hermetic homes, while the dedicated suites keep explicit on/off coverage. - Add a probe proving the main thread stays responsive while the search worker rebuilds and swaps a generation (reindex), completing the TUI responsiveness matrix. - Record the rollout state in the agent-core-v2 guide (session index section) and the root flag line. - Add changesets for the CLI (worker isolation, session index independence) and minidb (cooperative open lifecycle). Validation: full suites green across minidb (551), agent-core-v2 (4760), kap-server (1005), node-sdk (343), klient (91) and the CLI app (2567); open-lifecycle bench event-loop delay max is down from 45/734/331/492 ms to ~16-22 ms across the four scenarios with wall time flat or better. * fix(agent-core-v2): evict deleted sessions from the mirror queue and drain the index on close Two issues surfaced by the read-model default in the acp-server suite: - ISessionIndex.remove only deleted from the query store, but a summary still queued in the mirror was folded back into reads (and re-written by the next flush), resurrecting a deleted session in listings. The mirror now exposes evict(id): drop the queued summary and wait out an in-flight flush before the store delete. - RunningAcpServer.close and SDKRpcClientV2.close disposed the engine without awaiting the asynchronous mirror flush / query-store close, so a host removing homeDir right after close() raced in-flight shard closes (ENOTEMPTY). Both now follow the kap-server shutdown order: drain the mirror while the store is open, dispose, then await the drains. * fix(minidb): pause active expiry during the sliced bulk load The store's active-expire timer is armed at construction, so during a sliced bulkLoadRefsAsync a tick can fire mid-load: it reaps a TTL key from the map while the order skiplist is still the old empty one, and the final bulkLoadAsync then rebuilds order from the stale orderEntries snapshot — resurrecting the expired key in the ordered index (and duplicating it if the key is later set again). The sync bulkLoadRefs had no yield windows, so guard the async path with a bulkLoading flag that defers expiry ticks until the load settles (finally-safe). * chore: consolidate changesets into the TUI startup freeze fix |
||
|
|
c0b61c6e55
|
fix(agent-core-v2): count compaction tokens on the full-request basis (#2699)
Some checks are pending
CI / test-windows (push) Waiting to run
CI / lint (push) Waiting to run
CI / build (push) Waiting to run
CI / test (1) (push) Waiting to run
CI / test (2) (push) Waiting to run
CI / test (3) (push) Waiting to run
CI / test (4) (push) Waiting to run
CI / test (5) (push) Waiting to run
CI / test-pi-tui (push) Waiting to run
CI / 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
- tokensBefore/tokensAfter now include the system prompt and non-deferred tool schemas, matching the measured-anchor basis the context gauge uses between exchanges - the post-compaction ledger rebase carries the same full-request size, so the reported context size no longer dips to a messages-only estimate and jumps back on the next exchange - the PreCompact hook tokenCount uses the same basis |
||
|
|
476787fec9
|
docs(agents): rework changelog curation rules for the user-facing changelog (#2708)
* docs(agents): rework changelog curation rules for the user-facing changelog * docs(agents): refine catch-all wording and add reviewer notes to the changelog preview * docs(agents): surface folded entries at the sync review checkpoint |
||
|
|
d9ec566e51
|
docs(changelog): sync 0.34.0 from apps/kimi-code/CHANGELOG.md (#2704)
Some checks are pending
CI / test (5) (push) Waiting to run
CI / lint (push) Waiting to run
CI / typecheck (push) Waiting to run
CI / build (push) Waiting to run
CI / test (1) (push) Waiting to run
CI / test (2) (push) Waiting to run
CI / test (3) (push) Waiting to run
CI / test (4) (push) Waiting to run
CI / test-pi-tui (push) Waiting to run
CI / test-windows (push) Waiting to run
Nix Build / Check flake.nix workspace sync (push) Waiting to run
Nix Build / nix build .#kimi-code (push) Blocked by required conditions
Release / Publish native release assets (push) Blocked by required conditions
Release / Release (push) Waiting to run
Release / Deploy docs (push) Blocked by required conditions
Release / Native release artifact (push) Blocked by required conditions
* docs(changelog): sync 0.34.0 from apps/kimi-code/CHANGELOG.md * chore: link |
||
|
|
51ef78b8c9
|
docs: add Official Plugins section with WebBridge and Computer Use (#2653)
* docs: add Official Plugins section with WebBridge and Computer Use Group the three official capabilities (Kimi Datasource, Kimi WebBridge, Kimi Computer Use) under a new Official Plugins section on the plugins page, with a single shared install/upgrade flow. Add an authorization walkthrough screenshot for Computer Use and regroup the Datasource coverage table by category with named data sources. * docs: add browser extension install steps for Kimi WebBridge Installing via /plugins is not enough on its own: AI can only drive the browser after the Kimi WebBridge extension is present. Document both install paths (Chrome Web Store / Edge Add-ons, and manual load-unpacked via chrome://extensions with Developer mode) plus a quick way to verify. * docs: split WebBridge manual install into illustrated steps Break the manual extension install into numbered steps with per-step screenshots: enable Developer mode on chrome://extensions, then load the unpacked kimi-webbridge-extension folder. * docs: tighten WebBridge install screenshots to the relevant area * docs: add WebBridge ready-state verification screenshot * docs: note WebBridge's two-part install in the shared install steps * docs: even out WebBridge install screenshot edges * docs: replace WebBridge install screenshots with clean crops Re-shoot source images: split the two-step manual install guide into per-step screenshots with clean edges, and replace the ready-state popup screenshot with the toolbar-icon success indicator. * docs: use newly provided WebBridge step screenshots * docs: sharpen Computer Use auth screenshot and center it Replace the downscaled auth-window image with a crisp native capture, constrain its display width to 380px, and center it on the page. Also move the WebBridge two-part install note into an info callout directly under the shared install steps. * docs: drop the coverage start year from the Datasource table * docs: spell out the two WebBridge extension install options * docs: show the Kimi Code toggle enabled in the Computer Use auth screenshot * docs: show version badges for WebBridge and Computer Use, rework Computer Use scenarios Add version badges next to all three official plugin names. Rewrite the Computer Use capability list around verified task shapes and add a warning callout for operations that should not be delegated. Keep the final WebBridge install step inside the numbered list. * docs: add Windows (WinCU) notes to Computer Use Computer Use now ships a Windows runtime with a different install path and behavior: it may briefly take over the real mouse and keyboard instead of running fully in the background. Document the install command, system requirements, permission model, and privilege matching, and stop claiming the feature is macOS-only. * docs: break up the plugin manager wall of text Split the Installation and Management paragraph into bullets, drop the parts duplicated by the Official Plugins section (including the outdated macOS-only note), and link to that section instead. * docs: list the plugin manager tabs and drop the tab-behavior block * docs: give the WebBridge extension install section an English anchor * docs: restore the /reload or /new activation step for official plugins * docs: align the plugins page wording with the published docs site * chore: retrigger CI --------- Co-authored-by: qer <wbxl2000@outlook.com> |
||
|
|
f0614c53e5
|
ci: release packages (#2641)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> |
||
|
|
794714ebef
|
fix(agent-core-v2): gate plugin changes behind session baselines and reminders (#2702)
* fix(agent-core-v2): gate plugin changes behind session baselines and reminders
- capture a per-session MCP server baseline (ISessionMcpHandle.isBaselineServer)
so servers added mid-session (plugin install, mcp.json edit) never register
tools in live sessions; they take effect on /new, /reload, or resume, while
removed servers stay tombstoned and fail calls with a removal notice
- stop rebuilding the system prompt on plugin-source catalog changes: the
frozen skill listing and plugin sections cannot move anyway, and the rebuild
only churned the ${now} timestamp, invalidating the provider prompt cache
- freeze the Agent tool description's catalog profile list once the session
catalog has loaded, keeping the tools payload byte-stable across mutations
- append a plugin_change system reminder to live sessions on plugin mutations
(new IPluginService.onDidMutate; explicit reloadPlugins does not raise it)
- revert the TUI hint to "Run /new or /reload to apply plugin changes." and
update the plugin/MCP docs and changesets to the corrected contract
* fix(agent-core-v2): import LifecycleScope from app/scopes in sessionOutcomeMirror
#2666 imported LifecycleScope from #/_base/di/scope, which does not export
it (it lives in #/app/scopes), breaking the package build and typecheck on
main.
* fix(agent-core-v2): close the mutation-driven session-start refresh and overlay baseline leaks
Codex review on the PR found two contract leaks:
- a plugin mutation re-pulls the plugin skill source, and the existing
catalog listener answered with a fresh plugin_session_start reminder —
injecting the newly installed plugin's instructions into the live session
alongside (and contradicting) the plugin_change notice. The session-start
refresh now skips mutation-driven catalog changes (one per mutation,
counted; explicit reloads keep the old refresh behavior).
- a session created with ephemeral mcpServers kept its MCP baseline open
until the overlay connect finished; a workspace server added in that
window (plugin install, config edit) leaked into the live session through
the merged view. The overlay handle's baseline now freezes on the
workspace manager's initial load, with the ephemeral names baseline by
construction.
* fix(agent-core-v2): drop duplicate LifecycleScope import in sessionOutcomeMirror test
---------
Signed-off-by: Haozhe <yanghaozhe@moonshot.ai>
|
||
|
|
fa3325404b
|
fix(agent-core-v2): import LifecycleScope from the L3 scopes module (#2703)
The L3 unit layer refactor moved LifecycleScope out of the _base DI kernel into the app tier, leaving the session outcome mirror with a stale import that broke typecheck and import-time evaluation on main. |
||
|
|
03aa66ca0c
|
fix(tui): show WebBridge setup steps after install (#2692)
* fix(tui): show WebBridge setup steps after install * fix(tui): scope WebBridge hint to capability install * fix(tui): format WebBridge setup links * fix(tui): align WebBridge setup with live reload * fix(tui): retain WebBridge session activation step * fix(tui): compact WebBridge setup links * fix(tui): list WebBridge setup links clearly * fix(tui): show clickable WebBridge URLs * Revert "fix(tui): show clickable WebBridge URLs" This reverts commit 373ffae4b8cf4256131002b990f9a1869e9ee156. * fix(tui): restore WebBridge setup heading |
||
|
|
335588e259
|
feat(agent-core-v2): persist the last turn outcome into session metadata for cold listings (#2666)
* feat(agent-core-v2): persist the last turn outcome into session metadata for cold listings A cold session (no live handle) reported no lastTurnReason, so after a server restart the session list could not mark a session whose last turn failed until it was opened and resumed. A new Session-scope SessionOutcomeRecorder subscribes to the activity aggregate's turn_ended changes and persists the outcome (completed/failed) into the session metadata document; the summary pipeline (mirror + cold reader) carries it as SessionSummary .lastTurnReason, and toWireSession falls back to it when no live fact exists. 'cancelled' is deliberately not persisted: it is also what an in-flight turn ends with during scope disposal, and writing there races the host's home-dir teardown. Verified end to end with an isolated home and a dead provider: a turn fails, the server restarts, and GET /sessions reports last_turn_reason=failed without opening the session. * fix(klient): carry lastTurnReason/lastTurnOutcome in the validated contracts Review follow-up: zod strips unknown keys on parse, so the new outcome fields never reached klient callers; add them to the session summary and metadata/patch/key schemas (contract parity test covers the engine mirror). * fix(agent-core-v2): persist user-cancelled outcomes, never teardown aborts Review follow-up: skipping every 'cancelled' left a stale earlier outcome in the metadata (e.g. a prior failed reported for a session whose latest turn was stopped by the user). The recorder now subscribes to the main agent's turn.ended facts directly and keys on interruptReason: user_cancelled is persisted like any other terminal state, while programmatic aborts — including the cancel every in-flight turn suffers during scope disposal — are never written, so no metadata write races the host's home-dir teardown. * fix(kap-server): only fall back to the persisted outcome for cold sessions Review follow-up: a warm session that just started a new turn clears its live lastTurn, and the unconditional ?? fallback would then report the previous turn's persisted outcome for a turn that is still running. SessionFacts now reports whether a live handle exists, and the wire projection only reads the persisted value when the session is cold. * docs(agent-core-v2): keep the outcome-recorder header at role level * fix(agent-core-v2): settle turn outcomes on turn start and drain metadata writes on close Review follow-ups: - a new main turn now clears the persisted outcome (turn.started), so a process that dies mid-retry no longer reports the previous turn's terminal state for a turn that never ended - the dedupe marker only advances after a successful write, so a failed persist no longer suppresses the next identical outcome - session metadata writes are tracked in a module-level pending set with drainSessionMetadataWrites(), awaited by kap-server close alongside the mirror/query-store drains — an event-driven write (e.g. the outcome recorder) can no longer land in a session dir while the host removes it * fix(agent-core-v2): track the metadata dispose flag locally Disposable exposes no public isDisposed accessor; keep a class-local flag set in the dispose override. * fix(kap-server): drain session metadata writes before the mirror and disposal A write still in flight when close() begins must settle before the mirror flushes its summary into the read model and before scope disposal marks the service disposed — not after. * fix(agent-core-v2): reattach the recorder when the main agent is recreated Review follow-up: a failed bootstrap still fires onDidCreate before the handle is dropped; the subscription then pointed at a dead bus and the guard blocked any later reattach. Track onDidDispose and reset so the next main creation attaches cleanly. * test(agent-core-v2): resolve the recorder through the scoped DI harness Review follow-up: construct SessionOutcomeRecorder via registerScopedService + a Session-scope test host (stubbed lifecycle/metadata), so the test covers the production registration path; add the durable-value adoption case. * fix(agent-core-v2): unbreak CI — iterable Promise.all and the debug channel surface - Promise.all takes the pending-writes set directly (oxlint error) - the disposed flag moves into a _register'd marker instead of a public dispose() override, which the debug channels listing (and its test) correctly rejects as framework plumbing * fix(kap-server): surface persisted failures on the v2 session status The v2 list folds the outcome into activity.status, which previously read only live facts — a cold session always looked idle. Cold sessions now map a persisted failed outcome to status 'failed' (completed and cancelled stay idle, matching the live fold); warm sessions are unchanged, and the statuses filter inherits the mapping. * refactor(agent-core-v2): name the persisted field lastTurnReason Aligns with the established name for the same concept end to end (activity view's lastTurnReason, the v1 wire's last_turn_reason, and the SessionSummary mirror), instead of introducing a third variant. * fix(agent-core-v2): drain pending metadata writes before session teardown Review follow-up: closing/archiving a session right after a turn ended could dispose the scope while the outcome write was still queued, and delete() removes the session dir immediately after close. Await the pending metadata writes before the handle goes away. * fix(node-sdk): carry lastTurnReason through the SDK session summary Review follow-up: the in-process SDK path maps the engine summary through v2SummaryToSessionSummary, which dropped the new outcome field. Add it to the public SessionSummary type and the mapper; the parity gate projects it away (the v1 engine never records an outcome). * fix(node-sdk): populate lastTurnReason on live SDK summaries Review follow-up: resumeSession/reloadSession build their summary from the live session's metadata document, which now carries the outcome — surface it there too so the SDK reports it consistently for live and listed sessions. * fix(agent-core-v2): carry the last turn outcome across session forks Review follow-up: fork skips state.json when copying the session dir, so the fork's fresh metadata never had the outcome and a restart dropped a marker the warm fork was still reporting. The fork's metadata patch now inherits the source's lastTurnReason. * fix(agent-core-v2): settle pending outcome writes before reading a fork source Review follow-up: a fork requested right after the source's turn ended could read the metadata before the recorder's queued write landed, inheriting a stale or absent outcome. Drain pending metadata writes first. * fix(agent-core-v2): backfill restored outcomes into the session metadata Review follow-up: for sessions whose last turn ended before this field existed, the cold-resume seed restores the outcome into the activity view without a turn.ended fact, so the recorder never persisted it and cold listings stayed blank. The recorder now also watches the main agent's activity updates and backfills the restored outcome when nothing is persisted yet. * fix(agent-core-v2): never backfill restored cancellations Review follow-up: a restored 'cancelled' cannot be told apart from a programmatic abort (the activity event carries no interruptReason), and those are never persisted. Backfill now covers only completed/failed; user stops are still persisted from the live turn.ended fact. * refactor(agent-core-v2): rename the outcome recorder to outcome mirror Mirror is the codebase's established term for a write side that reflects live state into a store (SessionIndexMirror); Recorder has no precedent. * fix(agent-core-v2): backfill without bumping recency; header-only comments Review follow-ups: - a mere resume must not float an old session to the top of the list: metadata updates accept touchUpdatedAt:false and the outcome mirror's backfill uses it (live outcome writes keep bumping — turn end is a recency moment) - the mirror service's inline notes move into the file header per the package comment convention - drop the redundant |undefined from the SDK's optional outcome field * fix(node-sdk): read the live outcome for resumed session summaries Review follow-up: on a fresh resume the restored outcome can still be queued as a metadata backfill, so the document may lag a tick; the live activity aggregate already holds it. Resume/reload summaries now prefer the live value and fall back to the metadata field. * fix(agent-core-v2): confine the outcome backfill to pure resumes Review follow-up: the view publishes its turn.ended fold before this mirror's own turn.ended handler runs, so a live ending reached the backfill branch first and got persisted without the recency bump. The backfill now only applies when no turn ever started in this process — live endings always take the bumped write. * fix(agent-core-v2): drain the session-index mirror before session teardown Review follow-up: settling the metadata write alone left the fresh summary in the mirror's pending queue, so a list right after close could read a stale outcome from the read model. close/archive now also drain ISessionIndexMirror. Test harnesses register a mirror stub for the new dependency. * docs(agent-core-v2): fold the metadata drain contract into the file header * chore: include the SDK package in the changeset; fold the drain note into the header * fix(agent-core-v2): backfill restored cancellations too, quietly Review follow-ups: dropping every restored cancel loses legitimate user stops whose live write never landed (or was rejected) before a restart — cold surfaces never mark cancelled anyway, so healing them is harmless and strictly more accurate. The metadata disposal note moves into the file header per the comment convention. * fix(node-sdk): prefer the live outcome over the index in SDK listings Review follow-up: a live session that just started a new turn after a failure can briefly keep the stale outcome in the index while the mirror's clear is queued. listSessions now reads the live activity aggregate for warm sessions, matching the kap-server cold-only fallback. * fix(node-sdk): never read the metadata outcome for a live session Review follow-up: with a retry in flight the live aggregate has no outcome while the document may still hold the previous failure — the fallback showed the stale one. Live summaries now take the live aggregate's answer alone; the restored outcome is already seeded there on resume. |
||
|
|
e6e4ba2357
|
chore: sync web dist from code-app (#2697)
* chore: sync web dist from code-app * chore: add changesets for the synced web UI changes * chore: drop changesets already covered by the previous web bundle sync * chore: correct the drop-folder changeset for web * chore: drop the drop-folder changeset (desktop-only feature, no web announcement) |
||
|
|
02c026d487
|
feat(agent-core-v2): tombstone removed MCP servers and freeze plugin prompt inputs (#2694)
* feat(mcp): tombstone removed MCP servers and apply plugin changes immediately (20 files) - add 'removed' MCP server status: workspace config removals call markRemoved instead of remove, keeping tool registrations alive while short-circuiting calls with a removal notice - fire onDidReload after every plugin mutation (install/enable/disable/remove) so workspace consumers refresh contributions immediately - TUI renders the removed status in the MCP panel/startup summary and shows an apply-immediately hint on the v2 engine * feat(agent-core-v2): freeze plugin prompt inputs for live agents (2 files) - snapshot the model skill listing and plugin system-prompt sections on the first successful prompt build and reuse the frozen values for the agent's lifetime, so plugin install / enable / disable / remove / reload never rewrites a live agent's prompt (same keep-live-sessions-stable philosophy as the MCP tombstone) - freeze only on success: a not-yet-ready skill catalog or a failed enabledSystemPrompts() read must not pin empty values for the agent's lifetime - refreshSystemPrompt still rebuilds on catalog change events but reuses the frozen values, so the prompt only moves when non-plugin inputs change (AGENTS.md, [tools] section, session tool policy, compaction); new agents snapshot the then-current state * chore(changeset): add changesets for MCP tombstone and frozen plugin prompt inputs * docs: describe immediate plugin changes and the removed MCP status on the v2 engine * fix(klient): mirror the removed MCP server status in the wire contract * docs: drop the legacy-engine behavior notes from the plugin and MCP pages * fix(agent-core-v2): freeze plugin sections only on a loaded snapshot - enabledSystemPrompts() resolves to its consumption fallback (never rejects) while the initial plugin load has failed; freezing that empty read locked plugin sections out of the live agent even after a later successful reload - expose hasLoadedSnapshot() on IPluginService so resolvePluginSections can tell a real empty snapshot from the fallback before freezing |
||
|
|
cfd14a1fe2
|
feat(kap-server): accept attachments on skill activation (#2693)
* feat(kap-server): accept attachments on skill activation
The :activate endpoint only took {args?}, so REST clients (web/desktop
composers) could not attach uploads to a /skill invocation — attachments
were silently dropped at the edge.
- activateSkillRequestSchema gains an optional attachments field carrying
the image/video/file subset of the prompt content wire shape.
- The skills route resolves them through the same edge pipeline as prompt
submissions (validate file refs → materialize/compress → convert),
extracted from routes/prompts.ts into lib/promptMedia.ts.
- AgentSkillService.activate appends the resolved parts after the rendered
skill prompt in the activation's user message; SkillActivationInput
gains an optional content field. The native RPC/TUI path is unchanged.
- Attachment failures map to 40407 file.not_found / 40001
validation.failed, mirroring the prompts route.
* fix(kap-server): drop the unused parseKimiFileUrl import in promptMedia
* refactor: address review — header-only comments in the skill domain, provider id on protocol URL sources
- agent-core-v2 keeps comments solely in the top-of-file block (scoped
guide): SkillActivationInput.content documented in the skill.ts header,
the activate() note folded into the skillService.ts header.
- packages/protocol's image/video URL source gains the optional
provider-issued id, matching the kap-server wire schema so parsing the
public contract no longer strips it.
* fix(kap-server): validate the skill before materializing activation attachments
An unknown or non-user-activatable skill name with attachments ran the
media pipeline first, streaming bytes into the session/cache dirs and
compressing images for a request that activate() would reject with
40415/40912. The route now checks the session catalog up front (the
service still re-validates) so invalid activations leave no disk or CPU
side effects.
|
||
|
|
4d39f4fa6f
|
fix(agent-core-v2): omit max_context_tokens in REST session status when unknown (#2696)
- align the REST status rollup with the WS push: a bound alias that no longer resolves omits max_context_tokens instead of reporting 0 (0 is the engine's UNKNOWN_CAPABILITY marker, not a real limit) - fall back to the default model's limit only when no model is bound, resolved through IModelService like the WS side - mark max_context_tokens optional in the shared session status schema |
||
|
|
8c766a6c30
|
feat(agent-core-v2): add the L3 unit layer and the Feature seam (#2678)
* feat(agent-core-v2): add the L3 unit layer and the Feature seam - introduce the L3 Service/Fiber unit layer: the Service base class with this.provide/effect/on/get/ref capabilities, the fiber runtime with thenable FiberHandles, collection contribution points, and the per-scope-kind ScopeUnits materialization fold - provide each scope's static registration batch as one atomic provideAll cascade transaction (waiting-area activation, sticky Failed on construction error) - add the DI unit inspection surface: App-scope debug ledger / dependency graph / cascade history services and the kimi-inspect DI view - add the Feature unit seam (IFeatureManager + feature assembly), port plan mode onto it, and add the contributed-command seam (agent-command domain + node-sdk RPC types) - remove the legacy dep-graph tooling - apply the header-only comment convention across src and test: strip non-header narration, keep the file header, tooling pragmas, and NOTE comments * feat(kap-server): gate the event.di.* debug feed to kimi-inspect connections - add an opt-in target set in SessionEventBroadcaster; the global fan-out now skips event.di.* frames for connections that never opted in, so kimi-web and other clients no longer receive the high-churn DI feed - WsConnectionV1 opts a connection in when client_hello carries client_id 'kimi-inspect'; removeGlobalTarget drops the opt-in on close - temporary gate until a client-declared event-type whitelist lands * chore(agent-core-v2): fix oxlint errors in the DI unit layer - build the live-ref container chain without aliasing this (no-this-alias) - snapshot the materialized map with Array.from and document why the copy is required (no-useless-spread) * test(klient): use string scope kinds in the lifecycle handle fakes The engine's LifecycleScope is a string enum now; the facade test doubles still returned the old numeric kinds and failed the handleWireSchema output validation. * build(nix): update the pnpmDeps fetch hash |
||
|
|
ef61084009
|
fix(kimi-code): select compatible PowerShell for Computer Use (#2686)
* fix(kimi-code): select compatible PowerShell for Computer Use * fix(kimi-code): handle locked Computer Use plugin files * fix(kimi-code): align Windows Computer Use name * fix(agent-core-v2): reuse PowerShell fallback for detection * fix(agent-core-v2): refresh ready Computer Use plugin |
||
|
|
7b2784b9b7
|
feat: surface the bound model and thinking effort on subagent UIs (#2679)
* feat: surface the bound model on subagent UIs The subagent.spawned event now carries the display-normalized model alias (the derived __secondary__ entry resolves to its base alias), so clients can show which model a subagent is bound to. The TUI subagent card, swarm panel header, and background-agent entry show it at spawn; the WS snapshot roster and REST /tasks (background/detached subagents) carry it too, keeping the model visible across client reconnects. * feat: carry the subagent thinking effort alongside the model The spawned event, snapshot roster, and REST /tasks now also carry the child's effective thinking effort (read from the child profile at spawn, the same vocabulary as agent.status.updated). UIs show it only when it diverges from the main session's current effort — an inherited level adds no information, and 'off' is never shown. * feat(tui): show the bound model and effort in the /tasks browser The task browser's Detail pane renders Model and Effort rows for agent tasks (raw alias and level — it is the inspector surface, so no diff filtering), and its minimum height grows to fit the new rows. The values were already persisted on SubagentTaskInfo; the TaskInfo union, its zod schemas (protocol, kap-server, klient contract), and the v1 type declaration now carry them so nothing strips them in transit. * feat(tui): show concrete subagent effort levels unconditionally Display rule simplified: any concrete effort tier (low/high/max/…) is shown next to the model — including when it matches the main session's level. Only the boolean states stay hidden: 'off' (no thinking) and 'on' (generic thinking) carry no level information. * docs: trim the changeset entry * fix(tui): keep the model and effort on background-agent entries across resume replayBackgroundProjection only copied agentId/parentToolCallId/ description, so a background subagent that outlived a resume lost its model/effort on the later terminal transcript entry. The projection now threads the persisted values (catalog-mapped model; boolean effort states dropped), and session replay passes the loaded model catalog through. * fix(agent-core-v2): normalize the derived secondary alias regardless of the flag A child bound while the secondary-model experiment was on keeps __secondary__ in its persisted binding; if the flag is later switched off with the recipe still configured, resolveSecondaryModel() gated the normalization and the sentinel leaked back onto resumed subagents. subagentDisplayModel now reads the recipe straight from config (the flag gates new bindings, not the interpretation of existing ones), which also drops SessionSwarmService's now-unused IFlagService dependency. Also adds the SDK package to the release: the new SubagentSpawnedEvent/AgentTaskInfo fields are SDK-visible types. * fix(agent-core-v2): normalize the status-frame model at the source A derived-bound child republishes agent.status.updated right after spawn with its raw modelAlias, which overwrote the spawned event's normalized display model on single-subagent cards (swarm headers were first-wins and escaped). emitStatusUpdated now maps through subagentDisplayModel, a no-op for the never-derived main agent. Also moves the inline comments added by this branch into top-of-file headers per the v2 comment convention. * fix(tui): clamp the /tasks detail frame to the available body At terminals near the minimum height the forced 10-row detail frame overflowed the body and truncated the preview frame's border. The detail height now caps out at whatever leaves the preview its borders plus one content row, with a regression test at exactly MIN_HEIGHT. * fix: normalize inherited derived aliases and keep model/effort on replayed terminal entries - resolveSubagentBinding's caller-fallback branch also maps through subagentDisplayModel: a caller itself bound to the derived entry (a resumed subagent making a nested Agent call) no longer publishes __secondary__. - The replayed background-task terminal notification builds its metadata with the persisted model (catalog-mapped) and concrete effort, matching the live completion path. - Drops the inline comments this branch added inside v2 test bodies; the scenario context lives in the source file headers. |
||
|
|
013203421d
|
fix(tui): surface capability install errors (#2682)
Some checks are pending
CI / test (1) (push) Waiting to run
CI / test (2) (push) Waiting to run
CI / test (3) (push) Waiting to run
CI / test (4) (push) Waiting to run
CI / test (5) (push) Waiting to run
CI / test-pi-tui (push) Waiting to run
CI / test-windows (push) Waiting to run
CI / lint (push) Waiting to run
CI / build (push) Waiting to run
CI / typecheck (push) Waiting to run
Nix Build / Check flake.nix workspace sync (push) Waiting to run
Nix Build / nix build .#kimi-code (push) Blocked by required conditions
Release / Native release artifact (push) Blocked by required conditions
Release / Release (push) Waiting to run
Release / Deploy docs (push) Blocked by required conditions
Release / Publish native release assets (push) Blocked by required conditions
|
||
|
|
3c75a27da6
|
feat(tui): add cache-expiry hint dialog for resumed and idle sessions (v2 engine) (#2646)
* feat(agent-core-v2): detect prompt-cache breaks from per-step usage and emit telemetry Track consecutive turn-scoped LLM requests per agent; when the cache-read token count drops by more than 5% and by more than 2000 tokens between requests, log a debug line and emit cache_break_detected with both usages, the drop ratio, and the interval. Operation requests (e.g. compaction) act as a baseline barrier so expected drops are not reported. * feat(tui): add cache-expiry hint dialog for resumed and idle sessions (v2 engine) Resuming a long-idle session or submitting after a long idle stretch re-sends the whole history with an expired context cache. Show a dialog offering to compact, start a new session, continue as-is, or never ask again (persisted as cache_expiry_hint in tui.toml). Thresholds come from the client_configs endpoint (estimated_cache_duration) via a generic per-name cached client; only OAuth-managed providers participate. * fix(tui): preserve submit order and revalidate session in cache-hint flows Cold-cache submits during the in-flight config fetch are now swallowed and replayed through a FIFO chain, so a later prompt can never overtake the stashed one. Both the resume and idle paths re-check the current session after the async fetch: a switch mid-flight drops the dialog (resume) or hands the stashed input back to the editor instead of sending it into the wrong session (idle). * chore(agent-core-v2): regenerate state manifest after merging main * fix(tui): apply cache_expiry_hint on /reload and /reload-tui * fix(agent-core-v2): skip unmeasured all-zero usage in cache break detection * fix(tui): restore chained cache-hint submits when the dialog is not sent When several submits are swallowed during the cold-config fetch and the first dialog is dismissed (or its compact/new action fails), the stashed inputs were restored while later chained submits were still released — reordering the conversation. Chained submits now follow the fate of the message that opened the dialog, and multiple restores append newline-joined instead of overwriting the editor. * fix(agent-core-v2): reset cache-break baseline on model change Caches are per-model, so a cache-read drop after /model is expected, not a break. The baseline now carries the model and only same-model records are compared. * fix(tui): only count LLM-activity replay records for the resume cache hint The v2 resume replay also carries local-only state records (permission, plan, config updates, approval results) that slash commands append without an LLM request. Filter lastActiveAt to message/compaction records so a recent local change no longer masks an expired cache. * style(agent-core-v2): rewrite the cacheBreak impl header per package convention State the domain role, collaborators, and scope instead of narrating implementation steps; the behavior guards now live in the code alone. * fix(tui): drop the resume cache hint when a turn started mid-fetch The resume dialog is fire-and-forget over an async config fetch; if the user already sent the first prompt by the time it resolves, mounting would overlay an active turn and its actions would hit the live session. Re-check streamingPhase/isCompacting after the await, next to the session check. * style(agent-core-v2): trim the cacheBreak contract header to contract and scope * refactor: report cache-break detection from the TUI client Move the detector out of the engine so the telemetry event carries the client's own identity (which client produced it is now attributable). The TUI observes main-loop turn.step.completed usage directly, with the same guards: first-step/unmeasured/all-zero records skipped, model change and compaction reset the baseline. The agent-core-v2 cacheBreak module is removed. * chore: drop accidentally committed dist-web build output and ignore it * chore: revert the dist-web ignore rule * chore: restore dist-web to the tracked content from main * feat(tui): record cache breaks caused by mid-session model/effort switches A model or effort change mid-session busts the prompt-cache key — that is a real cache break worth attributing, not noise. The baseline now carries model and effort, the same-model exemption is gone, and cache_break_detected reports prev/curr model and effort alongside both usages. * chore(changeset): simplify the cache-expiry hint entry * chore(changeset): trim the cache-expiry hint entry to one line * fix(tui): cache-hint review follow-ups - carry the pre-dialog media extraction through compact/new resends so pasted attachments survive the image-store clear on a new session - reset the cache-break baseline after /undo — the context cut makes the next cache-read drop expected - release the stashed submit when a foreground operation started during the cold-config fetch instead of mounting the dialog over it - count a completed compaction as activity so the next submit is not judged against the pre-compaction timestamp * chore(changeset): drop the v2-engine-only suffix * fix(tui): seed the activity baseline when the resume check skips * fix(tui): cache-hint review follow-ups * fix(tui): record cache activity on completed steps, not turn begin * feat(cli): persist the client-configs cache across restarts |
||
|
|
713bf1a5a2
|
fix(kimi-code): render v2 background task notifications on session replay (#2677)
* fix(kimi-code): render v2 background task notifications on session replay * chore: add changeset for v2 task notification replay fix |
||
|
|
34c4181437
|
fix(kimi-code): keep kimi -p alive while background tasks are pending (#2675)
The 10-year default print wait ceiling (315360000s) overflowed Node's setTimeout limit (2^31-1 ms) into a 1ms fire, so the steer/drain wait returned instantly and kimi -p exited right after the main turn, killing pending background tasks and subagents. - add setClampedTimeout in agent-core-v2 _base, clamping delays to MAX_TIMER_DELAY_MS, and route every config-driven timer through it (timeoutOutcome, task wait/manager timeout, swarm attempt timeout) - chunk the print turn-endings wait against the real deadline instead of returning null on the first clamped timer fire - restore v1 semantics: a non-positive swarm subagent timeout is unbounded - default print_wait_ceiling_s to 2147483s (~24.8 days, the timer maximum) |
||
|
|
7bd3fd9f6e
|
feat(agent-core-v2): read UTF-16 text files by transcoding to UTF-8 (#2647)
- detect UTF-16 LE/BE from a BOM or a zero-byte parity heuristic (tolerant of CJK content), derived from VS Code's encoding detection - Read tool and workspace fs.read transcode UTF-16 text to UTF-8 instead of refusing it as binary; larger than 10 MiB still refused - refuse other non-UTF encodings (e.g. GBK) with a clearer message |
||
|
|
d1ded01b7c
|
fix(agent-core-v2): seed the activity view's lastTurn from the persisted turn.ended record (#2648)
Some checks are pending
CI / test (4) (push) Waiting to run
CI / test (5) (push) Waiting to run
CI / test-pi-tui (push) Waiting to run
CI / test-windows (push) Waiting to run
CI / lint (push) Waiting to run
CI / typecheck (push) Waiting to run
CI / build (push) Waiting to run
CI / test (1) (push) Waiting to run
CI / test (2) (push) Waiting to run
CI / test (3) (push) Waiting to run
Nix Build / Check flake.nix workspace sync (push) Waiting to run
Nix Build / nix build .#kimi-code (push) Blocked by required conditions
Release / Native release artifact (push) Blocked by required conditions
Release / Release (push) Waiting to run
Release / Deploy docs (push) Blocked by required conditions
Release / Publish native release assets (push) Blocked by required conditions
* fix(agent-core-v2): seed the activity view's lastTurn from the persisted turn.ended record A cold-resumed agent seeded its activity view only from live loop/task state, so the last turn's outcome was lost on a server restart: sessions came back with no lastTurnReason, and clients could not surface a previously failed turn (e.g. a provider 429 that killed the turn before the restart). The loop already persists the terminal turn.ended record (reason, error, durationMs); fold the latest one into the TurnModel as lastEnded and have AgentActivityView.seedFromLoop adopt it when no turn is active, so the session work aggregate (and everything built on it) reflects the last turn's outcome again after a cold start. * fix(agent-core-v2): seed lastTurn on wire restore and add a changeset Review follow-up: the agent scope (and with it this view) is constructed before wire.restore() replays the journal, so a constructor-time read of TurnModel.lastEnded always saw the initial state on a cold resume. Move the wire-backed seed behind the onDidRestore hook (constructor seed kept for views built after a restore), and drop the inline comments in favor of the file header per the package comment convention. * docs(agent-core-v2): trim the activityView header to role and collaborators Review follow-up: the previous revision narrated the restore-hook mechanics in the header; the package convention keeps headers at the module's external role plus collaborators, so drop the implementation narrative. * fix(agent-core-v2): keep TurnModel.lastEnded across clock advances Review follow-up: advanceTurnClock built a fresh state object without spreading, so a new prompt or a queued cancel silently dropped the stored last-ended outcome even though no new turn had ended — after a restart the activity view would again find nothing to seed. Spread the prior state and cover the prompt/queued-cancel/replace cycle with a model-level test. * fix(agent-core-v2): clear the stored turn outcome once a newer turn starts Review follow-up: with the clock advances preserving lastEnded, a prompt persisted without its turn ever starting would leave the previous turn's outcome to be seeded after a restart, reporting a stale result for a turn that never ended. The loop-event fold now drops lastEnded as soon as a newer turn's events land, while prompts and queued cancels keep it. * docs(agent-core-v2): keep the turnOps header at the domain role Review follow-up: the lastEnded keep/clear mechanics read as implementation narrative in the header; the convention there is role and collaborators only. |
||
|
|
68ba740ebf
|
feat(kimi-code): support Kimi Computer Use on Windows (#2652)
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 / Release (push) Waiting to run
Release / Deploy docs (push) Blocked by required conditions
Release / Publish native release assets (push) Blocked by required conditions
|
||
|
|
2b893733f9
|
fix(kap-server): bypass Node arg quoting for explorer /select, on Windows (#2645)
explorer.exe parses its raw command line rather than argv, so Node's default spawn quoting breaks the `/select,` argument whenever the path contains spaces: the command line becomes `"/select,\"C:\...\""`, which explorer rejects, silently opening the Documents folder instead of selecting the file. Quote only the path portion and launch with windowsVerbatimArguments so the command line keeps the documented `/select,"C:\some dir\f.txt"` form. |
||
|
|
510fbe7ec5
|
refactor(kap-server): wrap /api/v2/sessions in the v1 response envelope (#2644)
- return the domain-grouped page payload inside { code, msg, data,
request_id } and carry business outcomes in code (40001 invalid
params with details, 40922 page_token mismatch) instead of raw HTTP
statuses plus an { error: { code, message } } body
- add ErrorCode.PAGE_TOKEN_MISMATCH (40922)
- register the route via defineRoute (shared runtime validation and
envelope-wrapped OpenAPI docs); fold include-domain validation into
the query schema and replace the preprocess/doc-twin pair with
scalar-or-array union params
- update the kimi-inspect client to unwrap the envelope and sync the
two AGENTS.md guides
|
||
|
|
6f1cd7ca22
|
feat: add v2 sessions API and spreadsheet-like session table in kimi-inspect (#2640)
- kap-server: add GET /api/v2/sessions with a domain-grouped response (workspace / meta / activity, opt-in git), status / archived / updated_after filters, three sort orders, and fingerprint-bound opaque cursor pagination - kimi-inspect: rebuild the chat sidebar as a spreadsheet-like session table on the v2 endpoint — preset views (All / Opened / Archived / By workspace / Git), column visibility config, header sort toggles, cursor-paged Load more, and localStorage-persisted panel prefs - live activity frames from the WS hub override the REST status badge; session created / meta-updated events invalidate the v2-sessions query |
||
|
|
858812193a
|
fix(kimi-code): open /feedback to all signed-in users (#2639)
* fix(kimi-code): open /feedback to all signed-in users Gate the command on holding a kimi-for-coding OAuth token instead of the active model's provider, so signed-in users on API-key models can also submit feedback through the authenticated channel. When signed out, open the sign-up page alongside GitHub Issues. Also harden the failure paths: a failing auth status lookup or a rejected submit promise now falls back to GitHub Issues, while attachment-stage failures degrade to a non-fatal partial failure instead of triggering the fallback. * fix(kimi-code): print sign-up and issue links for signed-out /feedback Opening two browser pages at once is jarring; just print the links in the transcript instead. |
||
|
|
7c919f0376
|
docs(changelog): sync 0.33.0 from apps/kimi-code/CHANGELOG.md (#2636)
* docs(changelog): sync 0.33.0 from apps/kimi-code/CHANGELOG.md * docs(changelog): move the v2 engine entry to Refactors and the trust prompt to Polish |
||
|
|
53c832dfdf
|
ci: release packages (#2592)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> |
||
|
|
2b3e9a9f79
|
fix(tui): clarify curated plugin marketplace (#2635) | ||
|
|
7a631522fb
|
chore(changeset): trim the v2 engine entry and drop the custom identity entry (#2634) | ||
|
|
421e8f8065
|
fix(minidb): silence the transform-types warning from the dev worker (#2632) | ||
|
|
3bd098b806
|
chore(changeset): add changesets for the v2 TUI and web UI updates (#2630) | ||
|
|
75fe068a01
|
fix(cli): stabilize built-in capability installation (#2601)
* fix(cli): show built-in capabilities before the first session exists The lazy-session refactor left capability calls going through requireSession(), so on a session-less v2 startup /plugins reported the capabilities unavailable and hid the built-in rows behind the promo. Like plugin management, capability readiness and installs are app-global on the v2 engine: the node-sdk harness gains a capability facade over the global channel, and the TUI resolves session-or-harness for every capability call. * fix(cli): count the dev marketplace server as the default catalog dev.mjs always points KIMI_CODE_PLUGIN_MARKETPLACE_URL at its own repo-serving server, which the override gate mistook for a user-configured marketplace and suppressed the built-in capability rows in every dev run. The dev server now marks itself, and the gate treats that marked URL as the default catalog while still honoring real overrides (slash-command source, user-set env, KIMI_CODE_DEV_MARKETPLACE_URL). * fix(cli): align built-in capability updates |
||
|
|
f881cdd970
|
feat(cli): default CLI surfaces to the agent-core-v2 engine (#2627)
Some checks are pending
CI / lint (push) Waiting to run
CI / typecheck (push) Waiting to run
CI / build (push) Waiting to run
CI / test (1) (push) Waiting to run
CI / test (2) (push) Waiting to run
CI / test (3) (push) Waiting to run
CI / test (4) (push) Waiting to run
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
CI / test (5) (push) Waiting to run
CI / test-pi-tui (push) Waiting to run
CI / test-windows (push) Waiting to run
* feat(cli): default to agent-core-v2 engine with KIMI_CODE_LEGACY_FLAG opt-out - invert the engine gate: isKimiV2Enabled() now returns true unless KIMI_CODE_LEGACY_FLAG is truthy; KIMI_CODE_EXPERIMENTAL_FLAG no longer selects the engine - replace the experimental `kimi acp-v2` command with the native v2 implementation as the default `kimi acp`; the legacy acp-adapter path remains under the legacy flag - drop the acp-v2 experimental flag from the registry - rename the dev:cli:v2 script to dev:cli:legacy - update en/zh docs for the new default engine and the legacy flag * feat(cli): route export and provider through the engine gate - select the harness via isKimiV2Enabled(): agent-core-v2 by default, the legacy harness when KIMI_CODE_LEGACY_FLAG is truthy - close the harness after each one-shot command so the v2 engine's watchers do not keep the process alive - document both commands in the KIMI_CODE_LEGACY_FLAG env-var entry |
||
|
|
e3570280bd
|
fix(agent-core-v2): bound the project skill-root watch fd footprint (#2612)
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 / 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(agent-core-v2): bound the project skill-root watch fd footprint The workspace skill-root source recursively watches the skill-root candidates with chokidar, which holds one fs.watch fd per file and per directory on macOS. A skill bundling a large runtime tree can exhaust the process fd budget and break every subsequent spawn (EBADF). Mirror the scanner's own pruning (node_modules / dot entries, scan depth cap) in the watch filter, and add a signal mode to hostFsWatch: rescan-style consumers get ONE native recursive fs.watch on darwin/win32, whose fd footprint is constant in the subtree size. * fix(agent-core-v2): align the skill watch with scanner semantics and harden signal mode Review follow-up: - The scanner probes every entry's direct SKILL.md before gating recursion, so the watch filter now keeps an excluded entry itself and its direct SKILL.md (keepEntryFile) instead of pruning them — skills under node_modules / dot directories keep their hot reload. - The signal-mode native leg now owns its recovery: a native watch error fires one root invalidation and re-arms with capped exponential backoff; chokidar is used only where recursive fs.watch is unavailable, so a transient failure can neither silently end hot reload nor downgrade to the per-node watcher. - Native event path resolution handles absolute filenames and the root-basename case, clamping out-of-root events to a root invalidation instead of dropping them. - The event mapping is extracted into NativeSignalMapper with the stat call injected, so the native-branch decisions are unit-tested on any platform. * Fix spawn EBADF issue on macOS for large file trees The skill watcher no longer opens every file it watches, improving performance. Signed-off-by: 7Sageer <sag77r@hotmail.com> * fix(agent-core-v2): harden native signal watch recovery * fix(agent-core-v2): align skill watch with scanner traversal * fix(agent-core-v2): make skill watch handoff converge --------- Signed-off-by: 7Sageer <sag77r@hotmail.com> |
||
|
|
2a4990182d
|
docs: slim root AGENTS.md, move deep package docs to package guides (#2626)
- move the kimi-inspect, kap-server, transcript, and minidb project-map entries into new package-level AGENTS.md files - move the standalone Agent class rule to packages/agent-core/AGENTS.md - merge root-only agent-core-v2 facts (MCP persistence, trust routes, seed contract examples) into packages/agent-core-v2/AGENTS.md - compress the remaining long project-map entries to summaries with pointers, and add an anti-bloat rule for map entries - drop the stale server-e2e entry; the package no longer exists |
||
|
|
4e5f36aa66
|
test(agent-core-v2): stop the reconcile loop during the sessionIndex read baseline (#2625)
The baseline gates warm reads on behavioral work counts, including zero directory listings inside each counting window. The service's background reconcile loop (60s interval) runs an authoritative scan on every tick — two storage.list calls — and once a slow CI runner stretches the test past that interval, a tick lands inside a counting window on every attempt and is attributed to the read under test, failing fsLists === 0 even with the retry (observed on run 30975794688: 168s across two attempts, fsLists = 2 both times). Freeze the loop right after prepare() via a new stopReconcileLoop() test hook (sibling of reconcileNow/reprojectNow). The only remaining directory listing sources inside a window are the fallback read paths themselves, so a non-zero count is deterministically a real regression again. |
||
|
|
541ddd2d89
|
chore(web): replace apps/kimi-web with the code-app web bundle (#2599)
* chore(web): remove apps/kimi-web in favor of the code-app web bundle The web UI source now lives in the code-app repo (apps/web); this repo only ships the prebuilt bundle at apps/kimi-code/dist-web, synced from code-app via \. - delete apps/kimi-web (source, tests, docs) - root package.json: drop dev:web and the kimi-web typecheck leg - apps/kimi-code: drop the workspace dep and the build-from-source step; replace copy-web-assets.mjs with check-web-assets.mjs so packaging fails fast when the committed bundle is missing - CI: _native-build verifies the committed bundle instead of building from source; ci.yml and pkg-pr-new.yml drop the kimi-web legs - docs: AGENTS.md project map, changeset README, gen-changesets and sync-changelog skills now key web UI entries on dist-web * chore(web): stop ignoring dist-web now that the bundle is committed The ignore entry predates the code-app sync flow, when dist-web was a local build artifact. The bundle is now the canonical, committed form of the web UI, so ignoring it only forces every sync to git add -f. * docs(skills): drop the web-specific changeset and changelog rules The web: prefix convention and the web-specific dedup guidance belonged to the in-repo web app. With the source moved to code-app, web UI changes follow the same generic rules as any other CLI-bundle change. * chore(web): add changesets for the web UI changes in the bundle * chore(web): collapse the bundle changesets into one umbrella entry * ci: unbreak nix and lint after the kimi-web removal - flake.nix: drop apps/kimi-web from the source fileset and package lists, and verify the committed dist-web in the native build phase instead of building the web app from source - .oxlintrc.json: exclude dist-web (a committed build artifact) now that .gitignore no longer hides it from the linter * ci(nix): update pnpmDeps hash for the post-kimi-web lockfile * chore(web): resync dist-web from code-app main Bundle rebuilt from code-app main (upstream parity ports #175, pinned sidebar #176) with the CLI version 0.32.0 embedded. |
||
|
|
e7d5a0aee7
|
test(agent-core-v2): give the sessionIndex list baseline CI headroom (#2615)
* test(agent-core-v2): give the sessionIndex list baseline CI headroom * test(agent-core-v2): retry the sessionIndex baseline once to absorb runner load spikes * test(agent-core-v2): gate the sessionIndex baseline on work counts, not wall clock The baseline asserted wall-clock medians (list < 300ms, get/count < 50ms) plus a relative time check — all still load-sensitive on shared CI runners. Replace every time assertion with behavioral complexity assertions recorded at the IQueryStore and fs seams: list must be served by bounded pageByColumn fetches only, get is a single point lookup, count never touches the session collection, no warm read enumerates session directories, and the per-op work snapshot must be identical at 1k, 10k, and 50k sessions. Medians are still logged for phase-to-phase comparison; the retry now absorbs a background reconcile tick landing inside a counting window. --------- Co-authored-by: haozhe.yang <yanghaozhe@moonshot.ai> |
||
|
|
2ee6e43124
|
fix(mcp): re-register the OAuth client when its redirect URI no longer matches (#2620)
The callback listener binds a random port per flow, while DCR registration records the redirect URI of the flow that created it — so every interactive authorization after the first was rejected with "Invalid redirect URI", an error rendered only in the user's browser while the client waited for a callback that never came. Detect the mismatch before invoking auth() and drop the stale registration so the flow re-registers with the current callback URI (v1 + v2). Resolve #2606 Co-authored-by: zouying <zouying@moonshot.cn> |
||
|
|
8db7d42f23
|
feat(tui): add /bug as an alias for /feedback (#2614)
Some checks are pending
CI / lint (push) Waiting to run
CI / typecheck (push) Waiting to run
CI / build (push) Waiting to run
CI / test (1) (push) Waiting to run
CI / test (2) (push) Waiting to run
CI / test (3) (push) Waiting to run
CI / test (4) (push) Waiting to run
CI / test (5) (push) Waiting to run
CI / test-pi-tui (push) Waiting to run
CI / test-windows (push) Waiting to run
Nix Build / Check flake.nix workspace sync (push) Waiting to run
Nix Build / nix build .#kimi-code (push) Blocked by required conditions
Release / Publish native release assets (push) Blocked by required conditions
Release / Release (push) Waiting to run
Release / Deploy docs (push) Blocked by required conditions
Release / Native release artifact (push) Blocked by required conditions
|
||
|
|
98ee35afd2
|
feat(agent-core-v2): add custom agent identity (#2573)
* refactor(agent-core-v2): simplify context tags and shared copy
Rename the context-injection tags to `<skill-loaded>` and
`<plugin-instructions>`, drop the product prefix from the CronCreate tool
description and the default agent description, and point the MCP OAuth
callback page back to "your terminal" instead of naming one client.
The callback page is shared by the ACP host, the web UI, and embedding
hosts, so naming a single client was inaccurate there. The tags and the
two descriptions read exactly the same without the prefix. Verified no
runtime consumer matches the old tag names; the updated snapshots cover
the tool descriptions that changed.
* feat(agent-core-v2): add a switch for the product-documentation skills
Five builtin skills document this CLI itself — `update-config`,
`custom-theme`, `mcp-config`, `check-kimi-code-docs`, and
`import-from-cc-codex`. Their names and descriptions sit in the system
prompt on every turn, which is dead weight for runs that will never
reconfigure the CLI.
Add a top-level `builtin_product_skills` field (also settable through
`KIMI_CODE_BUILTIN_PRODUCT_SKILLS`) to drop them. On by default, so
nothing changes unless it is set; the trade when off is that the model
loses the guided flows for those tasks.
Filtering happens where the catalog is assembled — a later filter would
leave the skills advertised to the model. The whole section is one
scalar, so it exercises the section-level env binding branch and needs
its own strip: `stripEnvBoundFields` only walks object fields, so an env
override would otherwise be written back into `config.toml`.
* feat(agent-core-v2): add custom agent identity
Add an `[identity]` config section (`name`, optional `slug`, both also
settable through `KIMI_CODE_IDENTITY_NAME` / `KIMI_CODE_IDENTITY_SLUG`)
that sets the identity the agent presents: the name it calls itself in
the system prompt, the `User-Agent` product token sent to third-party
providers, and the client name announced to MCP servers. Leaving it
unset changes nothing.
Until now every one of these was fixed, which left no way to run the
agent as part of another product — an internal deployment, a fork with
its own branding, an embedding host.
The identity resolves inside the engine rather than being seeded by each
host, so it applies to every launch surface — including headless runs,
which today seed no display name at all and fall through to the built-in
default.
Two deliberate asymmetries:
- The display name is a filling value with a fallback chain (config >
host-declared > the consumer's own default); the slug is a rewriting
value with two states only, so with no identity configured the
rewriting paths are equivalent to not existing.
- The rewrite happens in the outbound header assembly, the one layer
that knows which vendor it is building for. Vendors declaring
`hostHeaders: 'full'` keep the host's own product token, which that
header set is built around and which backends key on; the configured
identity applies to the third-party path.
Resolution is lazy throughout: config loads asynchronously, and a
constructor snapshot would freeze the pre-load value under some startup
orderings.
Two input edges the resolver has to absorb, since both would otherwise
reach the User-Agent builder and either break it or quietly rewrite the
header: blank and whitespace-only values read as unset in the file just
as they already did in the env, so a stray `name = ""` cannot claim an
identity; and a name that folds away to nothing under slug
normalization (a CJK-only name, say) falls back to a neutral token
rather than producing a blank product, which the builder rejects.
* fix(agent-core-v2): keep the file value when a scalar env binding fails to parse
`config.ts` documents that an env value failing its binding's `parse` is
ignored, and `applyEnvBindings` honors that for object fields by
assigning only when the resolved value is defined. `applySectionEnv`
returned the parse result straight through for whole-section scalar
bindings, so a blank or mistyped variable resolved to `undefined` and
cleared the configured file value instead of being ignored.
Nothing hit this before: every existing section either binds object
fields or is env-only. `builtin_product_skills` is the first
whole-section scalar binding, where exporting an empty or misspelled
`KIMI_CODE_BUILTIN_PRODUCT_SKILLS` would silently undo a configured
`false`.
* feat(agent-core-v2): extend the custom identity to discovery and global MCP
Two outbound paths still announced the built-in product name under a
configured identity:
- `DiscoveryService` read the host User-Agent straight from bootstrap
args when refreshing provider models, so custom registries — which are
third-party endpoints — saw the original token while chat requests to
the same class of endpoint saw the configured one.
- `SDKRpcClientV2` builds its own global `McpOAuthService` plus a
throwaway `McpConnectionManager` for server testing, neither of which
goes through the workspace-owned manager that carries the resolver.
Both now resolve the identity from the App scope.
* refactor(agent-core-v2): neutralize remaining copy and align comments
The synthetic MCP authentication tool description is injected into the
model context and still named the product; it and the OAuth callback
pages now use client-neutral wording. "Return to your terminal" was no
improvement over naming a client — both assume what the host is, and
that page serves the ACP host, the web UI and embedding hosts alike.
Comments introduced by the identity work move into their module headers,
per the domain convention. Interface field docs stay: the rule names
functions, methods and statements, and field-level docs are established
across the codebase.
The new tests gain scenario headers and dispose the scoped hosts they
create, and the `[identity]` docs state which engine reads the section.
* fix(agent-core-v2): read the product-skill switch after config is ready
`BuiltinSkillSource` is the lowest-priority skill source, so the workspace
catalog loads it first — before `IConfigService` has finished loading — and
keeps the contribution it returns for the life of the handler, with no
reload path and no change event. Reading `builtin_product_skills` eagerly
therefore stranded the startup configuration: an explicit `false` could be
ignored for the whole process. `UserFileSkillSource` already awaits config
readiness for exactly this ordering; this source now does the same.
Also record the identity collaborator in the two module headers that gained
the dependency without documenting it, and scope the
`builtin_product_skills` docs to the engine that reads it, matching the
note the identity section already carries.
* fix(agent-core-v2): apply the product-skill switch to session-less listings
`builtin_product_skills = false` only reached the scoped skill source. The
SDK's `listWorkspaceSkills` and the server's `GET /workspaces/{id}/skills`
both composed the raw `BUILTIN_SKILLS` constant, and the web app feeds its
pre-session onboarding menu from that route — so the five product skills
stayed listed until a session existed, then vanished from the session's
catalog.
Move the decision into `visibleBuiltinSkills(enabled)` next to the constant
and route every consumer through it, reading the switch via the shared
`builtinProductSkillsEnabled`. Keeping "what counts as a product skill" in
one place is the point: three copies of the predicate would drift the next
time a builtin is added. The SDK listing also awaits config readiness,
which it did not do before.
* fix(node-sdk): await config before materializing the global MCP OAuth provider
`McpOAuthService` caches providers by store key and stamps the client name
when it first builds one, and the preceding `globalMcpConfig.get()` reads
`mcp.json` directly rather than through `IConfigService`. So a
`beginGlobalMcpServerAuth` call made right after the harness is created
could resolve the identity before config finished loading, pinning the
built-in label for the rest of the process — including the OAuth dynamic
registration a third-party MCP server records.
`testGlobalMcpServer` already awaited config readiness for its own reasons;
this path now does too.
* refactor(agent-core-v2): drop the unused builtin-skill registrar
`registerBuiltinSkills` stamped the raw constant into a catalog for "edge
composition without a Session" — exactly the shape that now has to respect
`builtin_product_skills`. It has no callers in v2 and is not exported from
the package index, so it was dead code that also stood as an invitation to
bypass the switch. v1 keeps its own copy.
Every remaining path composes builtins through `visibleBuiltinSkills`.
* fix(agent-core-v2): send the configured identity on custom-registry imports
`:import_registry` fetched a user-supplied third-party URL with a
hardcoded `kimi-code-kap-server` User-Agent, so the first request to a
registry announced the product while every scheduled refresh of the same
registry announced the configured identity. The hardcoded value was wrong
on its own terms too: that token names the server, and this path also runs
in the CLI.
Both services now project the identity through `identityUserAgent`, which
carries the two guards (no host header, or no identity) once instead of
per caller. The model catalog keeps an inline copy on purpose — kosong is
a foundational layer and must not import an app domain.
Sweeping the remaining outbound User-Agent sources found no further gaps:
WebFetch deliberately sends a Chrome-like UA, the models.dev catalog fetch
sends none from the CLI, and kap-server's `user-agent` reads are inbound.
* docs: scope the identity env vars and condense the changeset
The environment-variable reference advertised all three new variables
without noting that only the agent-core-v2 engine reads them; the
configuration page already carried that note. Added in both locales.
The changeset had grown into two paragraphs of implementation detail,
which is what would land in the CLI release changelog. `gen-changesets`
asks for one short sentence plus at most a one-line usage hint.
* docs(agent-core-v2): describe the identity as what the agent calls itself
The module headers had drifted into describing the feature by what it
keeps off the wire rather than what it configures. Reworded so they state
the capability: the identity is the name the agent uses for itself, and
the unset case is a no-op rather than something "safe". The product-skill
switch excludes skills rather than hiding them.
Wording only; behavior and structure unchanged.
* test(agent-core-v2): cover the identity on custom-registry imports
The import path switched from a hardcoded `kimi-code-kap-server` token to
the host User-Agent projected through the identity, but nothing asserted
it. Two cases pin both halves: a configured identity reaches the request,
and an unconfigured one leaves the host header intact — the second matters
because a single case would also pass if one hardcoded value had simply
replaced another.
Both fail against the previous implementation.
* fix(node-sdk): guard every global MCP OAuth path behind config readiness
`McpOAuthService` caches providers by store key and stamps the client name
when it first builds one, so any path that can materialize a provider has
to run after config has loaded. `beginGlobalMcpServerAuth` awaited
readiness, but `resetGlobalMcpServerAuth` reaches the same cache through
`invalidate()` -> `getProvider()` without waiting: resetting auth right
after the harness is constructed pinned the built-in client name, and the
await added to the begin path could not help because it then reused that
cached provider.
Rather than add the missing await, the accessor is now async and holds the
guard itself, so the service cannot be obtained before config is ready and
a future entry point cannot forget. The remaining `configReady` in
`testGlobalMcpServer` stays — that one is for its own `[mcp]` section read.
* fix(agent-core-v2): send the configured identity on models.dev requests
The directory fetch behind `listModelsDevProviders` / `getModelsDevProvider`
still hardcoded a `kimi-code-kap-server` User-Agent, so browsing or importing
from models.dev announced the built-in product — and claimed to be the server
even when running in the CLI. Only the custom-registry import had been fixed.
`getModelsDevCatalog` now takes the User-Agent from its caller: the module is
plain module-level state with no container access, and the value depends on
the host and the configured identity, which only the calling service can see.
All four third-party fetches in that service share one helper.
Where the host states no User-Agent, a neutral token stands in rather than
dropping the header — these are directories the service chooses to call, so
there is no host intent to preserve, unlike the provider requests the model
catalog assembles.
Both new tests fail against the previous hardcoded value.
* test(agent-core-v2): assert the product-skill set literally
The expected sets were derived from the same `productSpecific` field the
production filter reads, so a builtin silently losing its marker would just
move between sets and leave every assertion green — while staying visible to
the model once the switch is off. The five names are now literal, with a test
asserting the marked set matches them exactly.
Dropping the marker from one skill now fails four tests instead of none.
Also states the App scope in the identity contract header, per the domain's
comment convention for contract files.
* fix(agent-core-v2): normalize the host-declared display name too
Blank and padded values were normalized on the config side but not on the
host fallback, so an embedding host passing `displayName: " "` rendered
"You are ," into the system prompt, and a padded name kept its padding.
Same rule now applies to every source of the name.
Also names `agentIdentity` as the collaborator in the request-headers
adapter header, which described the value it obtains without saying which
domain resolves it.
The three new cases fail against the previous implementation.
* fix(agent-core-v2): keep the configured slug when the host sends no User-Agent
The neutral fallback added for hosts that state no `User-Agent` discarded a
configured identity along with it: `identityUserAgent` returns `undefined`
as soon as there is no host header to rewrite, so `?? DEFAULT_IDENTITY_SLUG`
sent the literal `agent` even when `[identity].slug` was set — precisely the
case that fallback exists to serve. The configured slug now stands on its
own, with the neutral token reserved for having neither.
The four combinations of (host header, configured slug) had three tests; the
missing one is the one that was wrong. It now fails without this change.
`outboundUserAgent` also awaits config readiness before reading the identity,
so a browse issued right after bootstrap cannot send the pre-load value — the
guard lives in the accessor rather than at its four call sites, matching how
the same race is handled elsewhere in this branch.
Both headers here and in `discoveryService` now name `agentIdentity` as the
collaborator resolving that token.
* test(acp-server): follow the renamed skill-activation tag
`acp-server` arrived on main after the tag rename, so its two assertions
still expected `kimi-skill-loaded` and failed once the branches met. Also
updates the web app's CSS comment, which named the old tag from the start
of this branch — a comment, so nothing ever failed on it.
Found by CI: the merge verification only ran agent-core-v2's suite, and
this package is neither a dependency nor a dependent of it.
* fix(agent-core-v2): present the configured slug on registry refreshes too
The previous round taught the import path to fall back to the configured
slug when the host states no `User-Agent`, but left the scheduled refresh
of the same registry on the bare projection — so one registry could see
`acme` on import and the runtime default on refresh.
Extracting `identityUserAgent` had made the two paths share a function
without sharing the policy. The choice itself is now the shared piece:
`identityUserAgentOrDefault` always yields a value, for the directories
this process chooses to call, while `identityUserAgent` stays the form
that rewrites only what the host already sends — what a provider request
needs, where the host's silence is its own choice.
* docs(agent-core-v2): move new member docs into the module headers
The domain's comment convention is absolute — comments live solely in the
top-of-file block — and I had read the "functions, methods, or statements"
clause as leaving interface members out. It does not: only 25 of 734 v2
sources carry an indented block, so the members I documented were the
exception, not the pattern.
Seven members across six files move into their headers. `types.ts` had no
header at all, so it gains one.
* fix(agent-core-v2): connect session MCP overlays after config is ready
The shared manager reaches `connectAll` through `initialize()`, which awaits
the config domain first; `sessionOverlay` called it straight away. A session
carrying ephemeral `mcpServers` created right after bootstrap therefore
resolved the client name before config had loaded and initialized under the
built-in one.
The blast radius is wider than that one connection: a remote server sends
the overlay through `hasTokens()`, which materializes an OAuth provider on
the *shared* service and caches it by store key — so the early name outlives
the connection that raced. The overlay now connects behind `mcpConfig.ready`,
leaving the returned readiness promise unchanged.
* fix(agent-core-v2): reload builtin skills when their switch changes
The workspace catalog keeps each source's contribution for the life of the
handler, so a `builtin_product_skills` toggle never reached an existing
handler's sessions. That was harmless while every surface read the same
constant — but routing the session-less listings through the config made the
two views disagree, since those read the switch on every call.
Follows `ExtraFileSkillSource`: subscribe to the owning section and fire
`onDidChange`, which the catalog already turns into a source reload. The
test asserts an unrelated section does not trigger it.
* fix(agent-core-v2): apply the identity to self-configured web services
`[services.moonshot_search]` and `[services.moonshot_fetch]` name their own
`base_url`, so both services can point at an endpoint the user chose — but
each forwarded the host request headers verbatim, sending the built-in
product token there under a configured identity.
Only the services-config path is rewritten; the managed OAuth path keeps the
host headers as they are, being the endpoint the session authenticated
against. The distinction is the same one the model catalog draws per vendor.
`identityHeaders` carries the rewrite across a whole header set, so this is
the fourth caller sharing the projection rather than repeating its guards.
A pair of tests pins both halves.
My earlier sweep classified these two as official by their names instead of
asking who chooses the URL, which is why they were missed. The contract
header is also condensed here, per the convention below.
* docs(agent-core-v2): condense the identity headers to their contracts
The comment convention is one sentence with two halves — comments live only
in the top-of-file block, *and* that block states the module's role without
narrating implementation. Moving the member docs up last round satisfied the
first and broke the second: the headers ended up spelling out the slug
folding algorithm, the strip mechanics, and the load order.
Kept what a caller or the next editor would get wrong without it (why the
value is read rather than snapshotted, what `undefined` obliges a consumer
to do, why this source waits for config). Dropped what the code already
says. 22/12/12/13 lines, against 53 in `catalogService.ts` — length was
never the problem.
* fix(agent-core-v2): rebuild active prompts when the builtin skills change
Reloading the catalog on a `builtin_product_skills` toggle left existing
agents holding the old listing: `AgentProfileService` refreshes the prompt
only for the plugin source, so a disabled switch kept advertising skills
that were gone, and enabling it left them missing until an unrelated
refresh.
The plugin source is special because it also contributes prompt sections
(#2314), and the file-backed sources are left out for cost — their fs
watches would rebuild every agent's prompt on each edit. The builtin source
has no watch: it changes only when its config switch is toggled, so it
belongs with the plugin source rather than with the file ones.
Subscribing to the catalog rather than the config section is load-bearing.
The catalog fires after the contribution is replaced, whereas a config
subscription would race the reload, and `resolveSkillListing` only awaits
the catalog's *initial* readiness — so the rebuilt prompt could read the
listing it was meant to replace.
The source id is a named constant now, so the subscription does not match
on a bare string.
* refactor(agent-core-v2): freeze the agent identity for the process lifetime
The identity is announced outward (MCP initialize, OAuth registration,
provider request logs) and cannot be re-announced, so mid-process changes
could only ever apply partially. Resolve it once when config first loads
and hold it for the life of the process: IAgentIdentity now hands out a
frozen snapshot via resolved()/current(), carrying finished products
(outbound User-Agent variants, rewritten header set) so call sites stop
composing host headers with the slug themselves. The kosong host-headers
port carries two finished layers and the catalog only picks one; consumers
gain no invalidation obligations because the value can never change after
the freeze. [identity] edits take effect on the next start (documented).
* fix(agent-core-v2): locate the User-Agent header case-insensitively
HTTP header names are case-insensitive, but the snapshot builder looked up
'User-Agent' by exact key: an embedding host spelling it 'user-agent' got no
third-party UA and kept its own product token on the services path even with
an identity configured. The builder now locates every case variant and
rewrites each in place, keeping the host's spelling. Also corrects the two
web-service headers that still described both paths as sending the bootstrap
headers, naming agentIdentity as the collaborator behind the config path,
and documents that a resumed session keeps its recorded system prompt.
* fix(agent-core-v2): attribute header provenance from the finished third-party layer
Inspection reconstructed the non-full host layer from the raw headers with an
exact-case 'User-Agent' lookup, so a host spelling the header 'user-agent'
got a resolved User-Agent with no provenance entry even though the runtime
sends the rewritten value. buildModel now captures the port's finished
third-party layer in the trace and attribution reads it, keeping inspect()
on the same resolution pass as get(). Also condenses the identity contract
header to its external role, and documents that an existing MCP OAuth
authorization keeps the client registration it was granted under (reset the
server's auth to register under the new identity).
* fix(agent-core-v2): keep web tool backends from racing the identity freeze
An env-configured [services] endpoint is visible before config finishes
loading, and FetchURLTool / WebSearchTool materialized their backends at
construction — so a fast bootstrap could hit the identity snapshot's
pre-freeze guard during agent creation, and the composed backend pinned
config and login state for the agent's lifetime against the service's
documented per-call resolution. Both tools now resolve their backend per
invocation, the WebSearch activation gate checks presence alone through the
new hasWebSearchProvider() (no provider composition, no identity read), and
bind() awaits the identity freeze before materializing the model, whose
resolution reads the identity through the host-headers port.
|