* fix(agent-core-v2): only pre-shrink compaction history when the request overflows the window
The pre-shrink budget (~0.74 of the window) sat below the auto-compaction trigger (min(0.85W, W-50k)), so every auto compaction on windows above ~195k dropped the oldest messages and broke the prompt-cache prefix. Gate pre-shrinking on the real input limit (window minus output reserve) and keep the safety budget as the shrink target.
* fix(agent-core-v2): pin degraded media to the count captured at the 413
Media degradation recomputed 'keep the 2 most recent' on every request, so each new image after a 413 replaced an older kept image and broke the prompt-cache prefix. Record how many leading media were degraded when the provider rejected the request, re-capture only when an inherited degraded request is rejected again with more than 2 visible media, and drop the prompt-cache guard's tolerance for rolling degradation.
* feat(agent-core-v2): probe compaction prompt cache prefixes
* chore: drop duplicate degraded media changeset
* fix(agent-core-v2): report prefix loss after compaction shrinks history
* fix(agent-core-v2): clamp compaction completion budget to the remaining window
Sending the full history leaves less room than the configured output cap,
so pass the estimated request size as usedContextTokens and let the
provider clamp max_tokens to the window that is actually left.
* Delete .changeset/fix-compaction-pre-shrink-cache.md
Signed-off-by: 7Sageer <sag77r@hotmail.com>
---------
Signed-off-by: 7Sageer <sag77r@hotmail.com>
* fix(agent-core-v2): answer questions without edits and clean up test leftovers
Let the request type decide the deliverable in Delivering work: change
requests (including "can you add..." questions) get the change, while
questions, explanations, and discussions get an answer that leaves the
project untouched. Diagnosis requests find the cause before naming it,
separate confirmed facts from hypotheses, and apply a fix only when asked.
Before replying after running tests or builds, check for new files and
delete the logs and test output those commands created when the task
does not need them, without touching files the agent did not create.
* chore: drop changesets for the delivery prompt change
* fix(agent-core-v2): scope delivery rules by request type and keep files of unclear origin
* fix(agent-core-v2): scope verification to the change and stop repeating passed checks
The delivery verification rule in the default system prompt applied to every
turn, required the project's standard build and test commands regardless of
what changed, and had no point at which checking stops. After compaction, the
summary prefix also told the agent to re-verify everything the summary
reported as done or passed, contradicting the system prompt's instruction not
to redo work the summary reports as done.
- Apply the build-and-test requirement to changes, and only to the standard
build and test commands that cover the change; keep verifying the
deliverable itself with real calls.
- Once those checks pass, repeat or broaden them only for a new change, a
failure, or an unresolved concern.
- After compaction, rely on what the summary reports as done or passed and
re-check only what it marks as unverified.
- Have the compaction summary mark a passed check as unverified when the code
it covers changed afterwards.
* fix(agent-core-v2): report unrelated failures instead of fixing them
The red-tests rule in the delivery paragraph applied to any failing test,
and nothing said what to do with bugs or broken tests the change did not
cause. Read literally, a pre-existing failure left the agent two options:
fix code outside the task or never call the work complete.
- Only tests that cover the change keep the work from being marked
complete.
- Do not attempt to fix unrelated bugs or broken tests; mention them in
the final message instead.
- Drop the changeset; this PR ships without one.
* feat(kap-server): push approvals and questions as interaction events
Replace event.approval.* / event.question.* with a single volatile
interaction event carrying the draft Interaction shape. Every subscriber
of a session receives it, including interactions raised by sub-agents
whose transcripts are subscribed. After each subscribe ack the server
sends the session's pending interactions to that connection, so clients
rebuild their list on (re)subscribe instead of relying on journal replay.
Map engine-cancelled questions to cancelled instead of answered.
* docs: describe the interaction event in the server API reference
* chore: add changeset for the interaction event
* feat(kap-server): flatten interaction frames and unify the response route
Send `interaction` as a flat, unsequenced frame `{ type, session_id, agent_id,
event_created_at, interaction_id, kind, status, ... }` outside the event
envelope, add approval `expires_at`, and register the frame in the WS schema
and AsyncAPI document.
Replace `POST /sessions/{sid}/approvals/{id}`, `POST /sessions/{sid}/questions/{id}`
and `:dismiss` with `POST /sessions/{sid}/interactions/{id}`, whose body is
discriminated by `kind` and whose result is `{ outcome, ended_at }`; dismissing a
question now succeeds with code 0. Error codes become 40404 (interaction not
found), 40902 (interaction ended) and 40001 (kind mismatch).
* chore: update the interaction changeset for the unified response route
* feat(kap-server): remove the pending approval and question list routes
Pending interactions now reach clients only through the interaction frames
sent after each subscribe ack, so drop GET /sessions/{sid}/approvals and
GET /sessions/{sid}/questions with their schemas, docs and harness helpers.
The wire helpers still used by the WS broadcaster and the snapshot route move
to protocol/interaction-wire.ts.
* chore: note the removed list routes in the interaction changeset
* fix(transcript): require the engine cancellation shape for cancelled interactions
* refactor(kap-server): derive interaction outcomes in one place
* feat(agent-core-v2): add cross-process session occupancy lock (#4246)
* feat(agent-core-v2): add cross-process session occupancy lock
Multiple clients (TUI / kap-server / VS Code / ACP) running core in
separate processes could open the same session and corrupt each
other's wire.jsonl and metadata. Each session now holds a cross-process
occupancy lock ({homeDir}/locks/sessions/{id}.lock) taken only while
writing: runs acquire it on submit / compact / undo / cron / delete /
fork / cold-archive, the append log holds it before flushing, and a
stale check compares the main agent wire's on-disk length with the
length this process knows.
Collisions surface as session.occupied (holder in details); writes by
another client detected after acquire surface as session.stale and the
caller reloads before continuing. kap-server maps them to
SESSION_OCCUPIED (40903) / SESSION_STALE (40930), reloads the session
on stale, and re-attaches WS subscriptions; the TUI shows an occupancy
dialog (retry/cancel) or reloads and replays history keeping the input;
print exits with code 4 on occupied and reloads automatically on stale;
VS Code / ACP get minimal occupancy notices. Locks are released a few
seconds after the session goes idle, on close, and on process exit;
locking I/O failures degrade to unlocked operation with a warning.
* docs: document session occupancy error codes and lock directory
* fix: address codex review on session occupancy lock
- metadata: re-read state.json on the first write of each lock-hold
epoch (holdToken), so pure-metadata changes made by another client
(title, archive) are not clobbered by turn-end lastPrompt writes
- lifecycle: hold an explicit lease across live archive(), matching
delete(), so the idle release timer cannot drop the lock mid-teardown
- tui: route the occupied dialog's Cancel selection through the same
cleanup as Esc (restore input, release recalled media)
- acp: tell stale-session users to close and reopen the session
- append-log: warn when retirement drops records kept in memory because
the session lock was unavailable
* fix: always run the staleness check in beginSessionRun
The prompt channels (AgentPromptChannel.submit/submitSteer, kap
/prompts, promptWithSkills) write prompt metadata before calling
loop.submit, and the metadata write acquires the session lock first.
beginSessionRun then short-circuited on 'already held' and skipped the
wire-length staleness check, so the main prompt path never detected
updates written by another client. Drop the already-held shortcut: the
check is a single stat and always passes while the lock is continuously
held, so every acquisition path now runs it regardless of order.
* fix(print): preserve occupied handling on the stale retry
If another client acquires the session between the reload and the
retry, the retried turn now goes through the same conflict handler:
session.occupied reports to stderr / stream-json and exits with code 4
instead of bubbling out as exit code 1.
* fix: do not mistake own in-flight writes for external modification
storage.append grows the file (writeFile) before fsync/close complete,
and knownLength only catches up afterwards; a beginSessionRun landing
in that window (steer, queued prompts, cron fires during a turn) was
misjudged as session.stale and deleted the lock file mid-turn. Track
in-flight bytes per log and compare disk against known + in-flight.
Cover the analogous rewrite window (post-rename syncDir) with an
expected-final-length marker.
* fix: bound the staleness comparison and check once per lock hold
The equality check disk !== known + inFlight moved the false-positive
window to before the append lands (mkdir/open) and into chunked
writes, so beginSessionRun during a turn (steer, cron, queued prompts)
could misjudge, delete the lock file mid-turn, and later append behind
another client. Compare known <= disk <= known + inFlight instead, and
evaluate the check only on a fresh hold (holdToken epoch): re-entrant
runs pass through without a stat, matching the RFC's 'already held
passes through' while every acquisition path still triggers the check
exactly once per hold.
Also unify the occupancy prompt copy across clients: the engine error
message is now the single user-facing text ('该会话正在被其他 Kimi
Code 进程使用;请停止其他进程中的会话或退出该进程后重试'), and the
TUI dialog, /compact /undo errors, print output, and the VS Code
fallback all display it instead of maintaining their own wordings.
* fix: address third-round codex review
- metadata: hold an explicit lease across each state.json write so the
idle release timer cannot unlink the lock mid-write
- print: preflight the session lock before goal creation; make goal
creation idempotent and emit the goal summary only for attempts that
did not fail with session.stale, so a stale reload + retry neither
duplicates the goal nor the summary
- tui: route every prompt-launch path (plain, goal steer, plugin
command, inline skill, batch steer) through one conflict handler
with the occupied dialog and stale reload + input restore
- tui: snapshot and restore the attachment store across stale reloads
so pasted media placeholders still resolve after history replay
* fix: move session occupancy to a fresh error code, use English copy, release locks on exit
SESSION_OCCUPIED reused 40903, which belonged to PROMPT_ALREADY_COMPLETED until #3747 retired it, and web clients still allowlist 40903 for that old meaning; reusing it conflated the two semantics. Move session occupancy to the never-used 40931 and update the server API reference in both locales.
The occupied error message was hardcoded in Chinese while every other engine error is English, so the TUI, print mode, VS Code, and ACP clients all rendered mixed-language output; replace it with an English equivalent.
The lock release hook ran on 'beforeExit', which never fires when the process leaves through process.exit (SIGHUP in the TUI goes through emergencyTerminalExit), leaving stale lock files that could keep a session looking occupied forever after a hostname change or pid reuse. releaseAll is fully synchronous, so hook 'exit' instead.
* fix(agent-core-v2): decide prompt-derived titles after re-reading session metadata
applyPromptMetadataUpdate decided whether to derive a title from the
prompt from the in-memory metadata cache, which is only refreshed when
the session lock is re-acquired. If another client named the session in
the meantime, the stale cache still looked untitled, so the next prompt
from this client overwrote the other client's title — both on the stale
path, where the prompt is rejected afterwards, and on the fresh path,
where the other client only changed the title on disk.
Move the untitled check into SessionMetadata.applyPromptMetadata, which
runs after ensureWriteOwnership() has re-read state.json, so the
decision is based on the latest persisted metadata. The published
SessionMetaUpdated event now carries the derived title only when it was
actually written.
* fix: address fourth-round codex review
- vscode: classify session.occupied and session.stale as preflight errors so the webview toasts them instead of silently swallowing the send, and give session.occupied the same English copy the engine emits (the toast text comes from getUserMessage in the extension host).
- engine: beginSessionRun now takes the session context and agent lifecycle service, and on the first check of each lock hold it runs externallyModified over every loaded agent's wire (scope agents/<agentId>, key wire.jsonl) instead of only the main agent's, so a fork that only touched a subagent journal is detected as stale. All four call sites (loop submit, manual compaction begin, undo, print preflight) pass the extra dependency.
- tui: keep the request in flight across a stale reload instead of dropping to idle, so input submitted during the reload queues rather than racing the closing session; park the queue around switchToSession (resetSessionRuntime would clear it) and drain it into the reloaded session afterwards; when restoring the rejected text, prepend it before content the user typed during the reload instead of overwriting it.
- kap-server: clear the in-flight turn tracker and subagent roster in detachSession so a reattached session no longer reports stale in-flight turns or running subagents in REST snapshots and WS resyncs.
* fix: harden session conflict recovery
Detect external appends that race journal reads so a later run cannot miss session.stale. Keep deferred wire migrations pending and finish them before appending, while rejecting the append if migration still cannot complete. Restore the complete plugin command after an occupied cancellation, including commands without arguments.
* fix: scope session staleness cache to session context
Replace the process-global session-id map with a WeakMap keyed by ISessionContext so closed and reloaded sessions do not retain cache entries for the lifetime of the process.
* fix: document session conflicts for metadata routes
Declare SESSION_OCCUPIED and SESSION_STALE in the response schemas for profile updates, title generation, and child creation. Cover the OpenAPI declarations and the existing HTTP 200 conflict envelopes with regression tests.
* refactor: centralize session conflict handling
Move session error mapping and stale reloads into one shared route helper, reuse shared conflict response declarations, and cover skill activation conflicts. Remove the duplicate prompt, session, and skill error mappers while preserving their existing envelopes.
* fix(sdk): restate session conflict errors
* fix(tui): keep the reloaded session open and resend after a stale conflict
* fix(agent-core-v2): lazily rebuild registered agents after a session reload
* refactor: simplify session conflict handling
* refactor: hygiene fixes from session-lock review
- kap-server: move SESSION_STALE to 40932 (collided with
DEFAULT_WORKSPACE_UNAVAILABLE at 40930) and pin ErrorCode value
uniqueness with a test
- node-sdk: fold six catch-and-restate copies into a restate() helper
- acp-server: sync mapPromptLaunchError docstring with the
occupied/stale branches
- replace hardcoded session.stale/session.occupied literals with
ErrorCodes constants
- inline the single-use sessionStaleError
- restore the strict timer-leak assertion with a warmup baseline
instead of a blanket +1 allowance
- narrow SessionState.detached to ?: true
* refactor: introduce session-level run gate
- replace the beginSessionRun free function with ISessionRunGate, a
Session-scoped service; loop/compaction/undo now inject one
dependency instead of four, and sessionLock.ts no longer imports
session-layer modules
- tryHold() absorbs the duplicated try/catch hold copies in
wireService and sessionMetadataService
- resolve IAgentLifecycleService lazily inside the gate to avoid a
construction-time cycle with agentLifecycleService
* refactor: tighten session lock and conflict recovery structure
- Narrow ISessionLockService to hold/tryHold/release/lease/held and share
the hold bookkeeping between the memory and node-fs backends; drop
holdToken and the module-level exit hook state.
- Session metadata refreshes on a new hold via onDidHold and skips agent
registration through tryHold instead of catching session.occupied.
- AppendLogStore reads sizes through IFileSystemStorageService.sizeSync and
exposes a generic write guard instead of a session-lock binding.
- kap: reload stale sessions only at the call sites that can raise
session.stale and log reload failures; error mapping stays side-effect free.
- TUI: recover from a stale conflict through the in-place session reload with
history replay, removing the same-session switch special case, the media
store snapshot, and the queue parking.
- print: bound stale reloads with a simple retry loop.
* refactor(agent-core-v2): merge session lock release into run gate
* test(agent-core-v2): reuse append log fixtures
* refactor(agent-core-v2): break run gate and agent lifecycle dependency cycle
* refactor: reload stale sessions in core instead of each host
Stale detection already lived in core, but every host re-implemented the
recovery: TUI, print, and kap each closed and resumed the session, and TUI
and web resent the rejected input themselves.
Core now exposes reloadOnStale: on session.stale it closes and resumes the
session, lets the caller refresh, then retries the call once (undo reloads
and still rejects, since its count depends on what the user saw).
- kap: prompt submit, skill activation, and compact retry in place instead
of returning 40932; undo still returns 40932 after reloading
- print: retries through reloadOnStale on the same session id and keeps the
original permission mode to restore
- SDK: run-starting calls reload and retry internally; Session gains
setStaleReloadHandler so hosts refresh before the retry
- TUI: drops its own reload/resend/queue-merge path; the stale handler
replays history and keeps in-flight messages
* docs(server-api): note that only undo returns 40932 after a stale reload
* refactor(agent-core-v2): ask each agent's wire whether it was modified externally
* refactor(agent-core-v2): clarify agent restoration helper name
* Delete .changeset/ensure-agent-lazy-rebuild.md
Signed-off-by: 7Sageer <sag77r@hotmail.com>
* fix(tui): wrap session occupied notice
---------
Signed-off-by: 7Sageer <sag77r@hotmail.com>
* revert: remove default workspace session creation (#4295)
* chore: merge release/2026-10-20 into release/2026-10-21 (#4294)
* feat(kap-server): add remote control device deletion (#4192) (#4195)
* feat(kap-server): add remote control device deletion
* fix(kap-server): coordinate with the local tunnel before deleting this machine's device
Co-authored-by: liruifengv <liruifeng1024@gmail.com>
* ci: enable recoverable publishing for the October release (#4218)
* ci: publish core releases from isolated release branches
* ci: simplify release recovery to the CLI package
* ci: publish VS Code from approved release commits
* test: await release test registration for type-aware lint
* test: avoid assuming order of independent tower events
* ci: limit release migration to existing publishing flow
* chore: sync web dist from code-app (#4221)
* chore: sync web dist from code-app
code-app: f828bac439ab5b9266ff8387c2afb96ecfcb59ae
* ci: retrigger (flaky rpc-events teardown ENOTEMPTY)
---------
Co-authored-by: kimi-agent-bot <kimi-agent-bot@users.noreply.github.com>
* ci: release packages (#4220)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
* feat(tui): queue /compact behind the running turn (#4276)
* feat(tui): queue /compact behind the running turn
* docs: note /compact queueing while the agent is busy
* fix(tui): harden queued /compact edge cases
- keep /compact blocked while a compaction holds the turn (busy reason normalized to compacting)
- treat the first queued /compact as a FIFO barrier for Ctrl-S manual steer
- gate the failed-fire retry timer on the session actually going idle
* fix(tui): cover remaining /compact queue review edges
- advertise Ctrl-S in the queue pane only for items ahead of the compact barrier
- queue /compact when the awaited lazy session creation lands on a running turn
- document the auto-compaction skip for queued /compact in docs and the changeset
* fix(tui): guard queued /compact against concurrent compactions
- requeue without firing when another compaction already started (the SDK's begin() swallows this as a silent no-op; declared residual window needs the contract to expose the result)
- restore the input when a duplicate /compact is refused after lazy session creation
* fix(tui): render requeued /compact and mark it always available in docs
- request a render after restoring a compact item to the queue so it does not appear vanished during backoff
- the reference table's availability column now matches the queueing behavior (/compact is accepted during streaming and runs in order)
* fix(tui): steer messages ahead of a queued /compact automatically
The automatic (tower/WaitFor) steer batch now slices at the first queued /compact just like the manual path, so pre-barrier prompts still end a WaitFor promptly instead of being blocked by an unrelated compact item. Batches containing bash or skill items ahead of the barrier keep their existing all-or-nothing semantics.
* test(tui): drive the tower steer barrier case through the WaitFor event path
* chore: downgrade the /compact queue changeset to patch (#4279)
* feat: add cloud-gated model switch confirmation (#4286)
* feat(cli): add cloud-gated model switch confirmation dialog
* feat(server): expose named client_configs endpoint for cloud-controlled clients
* fix(server): keep broadcasting compaction events to transcript subscribers
* Revert "fix(server): keep broadcasting compaction events to transcript subscribers"
This reverts commit e489915bd8bf753033f982ce1b0845cea64c0784.
* fix(cli): address review findings on the model switch confirmation
* test(cli): fix the auth-flow gate test typing
* fix: close the login-path holes in the model switch gate
- install the refreshed model catalog before matching the cloud rule,
so an alias introduced by /login no longer bypasses the confirmation
- return 'gated' from refreshConfigAfterLogin and forward the picked
effort to the dialog; setDefaultModel skips its follow-up activation
while the gate owns the switch
- ignore Escape once a dialog action was chosen so the input lock
survives the new-session creation
* fix: keep displaced panels alive when the new-session switch settles
The async finalizer of a settled dialog action now only restores the
editor while the model-switch dialog still owns the editor replacement,
so an approval or question panel mounted over it is never wiped out
with its reverse-RPC request still pending.
* feat: support id lists and always-fresh model switch confirmation configs (#4292)
* feat: accept id lists in model switch confirmation rules
* feat: fetch client configs fresh on every request
The /client_configs endpoint now proxies the cloud per request instead
of serving a copy cached at server startup, so config changes take
effect without a server restart. The startup warm-up and the in-memory
store are removed; region is resolved per request.
---------
Co-authored-by: qer <wbxl2000@outlook.com>
Co-authored-by: Kimi Agent <zhangkaiyi@moonshot.ai>
Co-authored-by: kimi-agent-bot <kimi-agent-bot@users.noreply.github.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
* chore: sync web dist from code-app (#4304)
code-app: 913137036c35c608f3a487eb2c7b4a8815b53b95
Co-authored-by: kimi-agent-bot <kimi-agent-bot@users.noreply.github.com>
* ci: release packages (#4288)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
* fix: decouple workspace trust from runtime initialization (#4348)
* fix(kap-server): decouple workspace trust from runtime initialization
* fix: preserve workspace trust errors
* fix: emit workspace trust recovery changes
* fix: complete workspace trust recovery
* fix(agent-core-v2): reclaim session locks from zombie processes (#4320)
(cherry picked from commit 0a90e2e8a2)
* feat: let users take over a session lock left by an exited process (#4328)
* feat: let users take over a session lock left by an exited process
Session locks judged alive by mistake (a different hostname, pid reuse, or a failed process-state probe) blocked the session with no way out. Add a takeover path: the lock service evicts another process's lock, the SDK exposes Session.takeOverLock(), kap-server adds POST /api/v1/sessions/{id}:takeover, and the TUI offers Take over once a retry is still occupied. KIMI_CODE_SESSION_LOCK=0 turns the lock off for a process.
* fix(agent-core-v2): evict the lock directly on takeover
Takeover went through the reclaim marker, so a stale marker left by a crashed reclaimer on another host made takeover a silent no-op while the API reported success. Unlink the lock file directly instead.
* fix(agent-core-v2): surface takeover failures when the lock cannot be removed
Takeover swallowed unlink errors, so a lock file another user owns in a shared sticky directory was reported as taken over while the next retry stayed occupied. Ignore only ENOENT and let other errors reach the caller. Also fix the run gate test bootstrap stub typing.
* fix(tui): simplify the session takeover copy
* fix(tui): tie the takeover warning to the takeover action
* fix(tui): drop the holder line from the takeover dialog
* refactor(tui): move the session occupancy dialogs into a controller
(cherry picked from commit e7f1a99fc5)
* chore: sync web dist from code-app
code-app: 76483646b7c46a61bff868f1473535e0a32b473d
---------
Co-authored-by: 7Sageer <sag77r@hotmail.com>
* feat: show session lock holder client names (#4356)
* chore: sync web dist from code-app (#4357)
code-app: 7be8bf55306625ade7b5dbead86bc789d22d0159
Co-authored-by: kimi-agent-bot <kimi-agent-bot@users.noreply.github.com>
* ci: release packages (#4353)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
* test: isolate cloud config in message flow tests (#4363) (#4383)
(cherry picked from commit 087db850e0)
Co-authored-by: grapedge <grapedge@gmail.com>
* fix(agent-core-v2): leave swarm mode to the user (#4384)
* fix(agent-core-v2): leave swarm mode to the user
AgentSwarm now runs only while swarm mode is on, and only the user can
turn swarm mode on, so the model can no longer start a swarm by itself.
- Reject AgentSwarm at resolve time while swarm mode is off and check
again right before it runs; no subagents start and swarm mode stays off.
- Stop AgentSwarm from entering swarm mode, reject the tool trigger, and
ignore tool-triggered entries that older versions recorded on replay.
- Skip the one-AgentSwarm-per-response veto while swarm mode is off so
the other tools in that response still run.
- Say in the AgentSwarm description and the swarm exit reminder that only
the user can turn swarm mode on.
- Update the tools and slash-command reference docs.
* fix(transcript): ignore model-started swarm entries when folding history
Swarm mode entries with the tool trigger come only from older versions,
where the model started a swarm by itself. The engine already ignores
them on replay, so a session that stopped mid-swarm resumes with swarm
mode off. Folding history still marked swarm mode on from that entry,
and no later status update turned it off, so the web UI kept showing
swarm mode as on.
Keep the swarm.enter marker in history, but leave meta.modes untouched
for tool-triggered entries.
* fix(agent-core-v2): launch subagents only on explicit request (#4386)
The main agent launched subagents on its own: the explore type told it to
delegate any exploration needing more than three searches and to run several
explore agents at once, the Agent description sold delegation, and the
EnterPlanMode description sent plan mode to an explore subagent first.
- Agent description: launch a subagent only when the user asks for one, or
AGENTS.md, a skill, or a multi-agent mode the user turned on calls for it;
agent type descriptions only pick a type once launching is authorized.
- System prompt: state the same rule for Agent.
- Drop the delegation nudges from the Agent and explore descriptions and from
the EnterPlanMode description.
- Swarm exit reminder: once swarm mode ends, handle the next request without
subagents unless it asks for them.
* chore: clarify subagent changeset
* ci: release packages (#4385)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
* fix: hide AgentSwarm calls rejected before any subagent starts (#4414)
While swarm mode is off the core rejects AgentSwarm before any subagent
starts, but the TUI still drew the call as a swarm whose members all
failed, and resumed sessions replayed it as a failed swarm card.
- Do not create the swarm panel while swarm mode is off, and remove the
panel when a call ends before any subagent started (swarm mode off,
input validation, a rejected approval, or a blocking hook).
- Create the panel when the first subagent spawns, so a swarm that does
run is never hidden when the TUI's swarm state lags behind.
- Skip such calls when replaying a session.
- Tell the model to point the user to /swarm only when they asked for a
swarm, and otherwise to continue without mentioning swarm mode.
* feat: warn about high usage when swarm mode turns on (#4423)
Swarm runs many agents in parallel and can use many times the tokens of
a normal task. Show a warning line under "Swarm activated" whenever
/swarm, /swarm on, or /swarm <task> turns swarm mode on, suggesting a
small task first and turning swarm off when done.
* feat: add session-aware model switch confirmations (#4424)
* feat: add session-aware model switch confirmations
* chore: remove changesets
* fix: harden model switch confirmation loading
* fix(cli): merge duplicated permission-mode notice lines in swarm and goal start prompts (#4432)
* feat: add cloud-controlled effort switching prompts (#4436)
* feat: add cloud-controlled effort switching prompts
* fix: rename effort switch client config
* fix: refine model and effort switch prompts
* docs: update model switch release note to English (#4440)
* chore: sync web dist from code-app (#4443)
code-app: e3e6af32171e939399cea2edc728757dcae0ba18
Co-authored-by: kimi-agent-bot <kimi-agent-bot@users.noreply.github.com>
* ci: release packages (#4417)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
* test(tui): use the release gate fixtures in auth-flow.test.ts
The merged gate path needs the release fixtures (session_type rules +
model capabilities); keep main's unique auth/refreshProviderModels tests
alongside release's.
* fix: drop unused isSessionOccupiedError import left by the sync resolution
* test(tui): align goal start prompt expectations with the merged notice copy
#4432 merged the duplicated permission-mode notice lines; update the goal
and fullscreen dock expectations to the merged copy (the body now fits at
maxBodyRows=6, so no scroll suffix).
---------
Signed-off-by: 7Sageer <sag77r@hotmail.com>
Co-authored-by: 7Sageer <sag77r@hotmail.com>
Co-authored-by: qer <wbxl2000@outlook.com>
Co-authored-by: liruifengv <liruifeng1024@gmail.com>
Co-authored-by: kimi-agent-bot <kimi-agent-bot@users.noreply.github.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Kai <me@kaiyi.cool>
Co-authored-by: grapedge <grapedge@gmail.com>
* test(agent-core-v2): pin prompt-cache prefix parity across media recovery
Add failing tests showing that requests after a media-degraded or
media-stripped recovery do not extend the previous request's prefix:
the next turn and compaction requests resend full media first, and a
compaction request re-captures the strip snapshot, hiding media that
the loop request still showed.
* fix(agent-core-v2): keep media recovery across turns and compaction
Media recovery after a provider rejection was keyed by turn id and only
honored for turn requests, so the next turn and compaction requests
resent full media, got rejected again, and re-captured the strip
snapshot. Keep a single agent-level recovery keyed by model alias and
context prefix revision, applied to every later request until the model
changes or the history is rewritten.
* fix(agent-core-v2): keep media recovery across turns and compaction
* fix(agent-core-v2): recover new media and invalidate recovery on undo
Keep the per-request strip retry budget separate from the inherited media
snapshot so newly added media can recover from 413 and image-format
rejections. Stop recapturing once this request has stripped its media or
an inherited snapshot leaves no visible media.
Advance the context prefix revision when context state is restored so
undo invalidates media recovery without adding service dependencies.
Cover later turns, automatic and manual compaction, bounded retries, and
real prompt-to-undo flows for both degraded and stripped media.
* test(agent-core-v2): guard prompt-cache prefix parity in the test harness
Every harness agent now checks that the first attempt of each turn
request (a new turn or a later step) and of each full compaction request
extends the last successful turn request: same model profile fields
(cache key, thinking, extra params, response format, tool message
conversion), same system prompt, same tools unless the tool-name set
changed, and a history prefix. Requests after a model switch or a context
prefix rewrite are skipped, and media turned into the degraded
placeholder at the same position is allowed while the request runs under
media-degraded recovery. Violations fail the running test.
Add an end-to-end case that runs a turn after media recovery so the
next-turn check sees both degraded and stripped projections.
* test(agent-core-v2): check stable prefix fields on every generate attempt
Recovery retries were recorded as the next baseline without being compared.
Compare profile fields, system prompt, and tools on every attempt, and only
treat a same-position media replacement as a prefix when it is the degraded
placeholder or a single media path tag. Count real comparisons so an unwired
guard fails the media-recovery test.
A subagent run only wrote to its task output when it completed. When it was
stopped (TaskStop, a user interrupt, or a cancelled parent turn), timed out,
or failed, the parent agent saw a bare status and lost whatever the subagent
had already written.
runAgentTurn now collects the run's public assistant text from the child's
AssistantDelta, TurnStepStarted and TurnStepRetrying events: current turn
only, the text of retried step attempts dropped, kept to the most recent 32K
characters on code point boundaries, and released once the run settles. The
run handle exposes it through partialOutput().
SubagentTask writes that text to the task output, marked as incomplete,
before settling a killed or failed task, and flushes it in forceStop when
cancellation outlives the grace period. The output is written at most once,
so a completion that arrives after a forced settle cannot append to it.
TaskStop on an agent task now shows that output together with the agent_id,
output_path and a resume hint, and still suppresses the terminal
notification. A failed or cancelled foreground Agent call inlines the same
block. When the subagent wrote nothing, both say so explicitly.
No extra model request is made, and completion and cancellation semantics
are unchanged.
* fix(workspace): reclaim instances when last owner releases
- hold reference-counted leases across sessions and temporary callers
- await MCP and stdio process cleanup before rebuilding instances
- restore persisted directories and clear ephemeral directories on reclamation
* fix(workspace): bound MCP stdio termination
- escalate stdio shutdown to SIGKILL after a bounded SIGTERM grace
- cover workspace reacquisition while an uncooperative MCP is closing
* fix(workspace): retain workspaces during MCP probes
- hold the containing workspace lease until stdio probe shutdown completes
- cover lease ordering across connection and shutdown
The compaction request ran stripDynamicToolContext() before handing the
history to the LLM requester, while loop requests only go through
toolSelect.shapeHistory(). With tool select enabled and any deferred tool
configured, this removed the loadable-tools announcement (and the tools
field of loaded schema messages), so the compaction request diverged from
the previous loop request early in the history and lost almost all prompt
cache reuse.
Drop the extra strip so both requests share the single shaping point in
the LLM requester. Add a harness helper that reports the first prefix
break between two provider-visible requests, and a compaction test matrix
(tool select off / announced / schema loaded x manual / auto) asserting
the compaction request extends the last loop request.
Resolve#4389
* fix(agent-core-v2): truncate tool results on code point boundaries
Co-authored-by: ahyangyi <ahyangyi@gmail.com>
* fix(agent-core-v2): cap accumulated tool output on a code point boundary
Co-authored-by: ahyangyi <ahyangyi@gmail.com>
* fix(agent-core-v2): saturate tool output accumulator after a retention cut
Co-authored-by: ahyangyi <ahyangyi@gmail.com>
* fix(agent-core-v2): apply code point boundaries to remaining tool-result truncation paths
Review follow-ups:
a) agentTool: truncateReason now slices the model-visible sub-agent stop
reason via sliceOnCodePointBoundary so it cannot split a surrogate
pair. No dedicated agentTool test home exists; the helper stays
covered by the code-points tests only.
b) taskService: readOutput clamps the tail length first and then moves
the slice start to a code point boundary, so a tail window cannot
begin with a lone low surrogate. Covered by a focused output-access
test.
c) toolResultTruncation test: verified the 1_983-'a' padding makes the
2000-14=1986 per-line cut land mid-pair — the case fails on the
pre-fix service and passes on the fixed one, so no test change was
needed (and this package forbids comments).
d) code-points: suppress unicorn/prefer-code-point on the intentional
charCodeAt calls. The helpers only avoid creating new mid-pair cuts
and deliberately do not clean pre-existing lone surrogates in the
input — the request-side sanitizer handles those.
Co-authored-by: ahyangyi <ahyangyi@gmail.com>
* fix(agent-core-v2): preserve full output for nonpositive task-output tails
The code point boundary rewrite of readOutput changed the established
tail=0 behavior: slice(-0) starts at zero and returned the entire
output, while clamping the tail window to zero returned ''. Return the
output unchanged when the requested tail truncates to zero (zero and
negative tails alike), and pin the contract with a focused test.
Co-authored-by: ahyangyi <ahyangyi@gmail.com>
---------
Co-authored-by: kimi-agent-bot <kimi-agent-bot@users.noreply.github.com>
Co-authored-by: ahyangyi <ahyangyi@gmail.com>
Co-authored-by: kimi-agent-bot <317908118+kimi-agent-bot@users.noreply.github.com>
* feat: add standalone session import conversion package
* fix(session-import): address rollout and image review findings
- Extend a rollback cutoff only over records that share a defined turn id.
- Keep tool-call arguments verbatim instead of rewriting image-shaped values.
- Keep media from an assistant projection that its matching response lacks.
- Count unknown event and projection kinds as unrecognized source content.
* feat(session-import): render imported history as visible messages
Follow the Codex /import format: each source turn becomes an ordinary user
message and one assistant message, tool calls and results become tagged text
notes clipped to 2,000 and 4,000 characters, and thinking, source events and
injected context stay only in the full history attachment. A final user notice
records the source session, included turns, attachment paths and known
limitations.
Prefer a user-set title over later generated ones and fall back to the first
prompt line, treat agent-name rows as metadata, stop reporting missing
reasoning text, return EMPTY_SOURCE_HISTORY for sources without a prompt, and
explain why forked or restarted Codex sessions are rejected.
* feat: import Claude Code and Codex sessions
Add /import-sessions and `kimi import-sessions`, which open the same
page: scan recent Claude Code and Codex sessions, choose the last 15
days (the default) or 30 days, import the newest 100 with progress, and
summarize what was imported and skipped. Reruns skip sessions imported
before.
Imported sessions are written into the workspace of their original
folder with their title, last activity time, and synthesized turn
records, using the migration-legacy session writer, and show a
[Claude Code] or [Codex] badge in /sessions. Discovery lives in
session-import and leaves out subagent, non-interactive, and prompt-less
sessions. Sessions still being written are reported as in use, and
fallback titles skip lines without letters or digits.
* fix: list imported sessions in /sessions right away
/import-sessions writes sessions straight to disk, which the running
session index only noticed on its next periodic reconcile, so /sessions
missed them until a restart or up to a minute later. Look each imported
session up by id after the run so the index records it immediately.
* fix(session-import): store each source record once in the full history
Records without text of their own, such as titles, mode changes, and
request dumps, were kept three times in the full history attachment: in
a header list, as the step text, and again in the step's source
metadata. Keep them only as the step text with the source line in the
metadata, and do the same for unknown content blocks and unknown Codex
response items. On local sessions the attachments shrink from 346 MB to
141 MB for 240 MB of source files.
* feat(session-import): import the conversation as one framed user message
Render the imported history as a single user message: a system note
before and after it, session info, attachment locations, known
limitations, and the included turns. Every tool call keeps the first
2,000 characters of its recorded input instead of three picked fields.
The session list shows the last real prompt from the source.
* fix(session-import): skip the Claude Code API request log
Claude Code 2.1.300 and later write API request records into the
session file. Their message copies were read as conversation records,
which duplicated tool calls and results and wrote the request log into
the full history attachment.
* fix: fit session imports to the model and skip missing folders up front
Size the imported message from the default model's input limit and keep it
below the automatic compaction threshold, so continuing an imported session
does not start with a compaction or an over-limit request. Sessions whose
project folder no longer exists are left out when scanning, so they no
longer take the newest-100 slots of every run.
* fix(session-import): keep late-written parents and clean up titles
Link a Claude Code record to its parent even when the parent was written
after it, placing the parent first, instead of cutting the history there.
Titles and last prompts skip IDE context ahead of the request and image
reference lines. Codex tool search results and three recent Claude Code
metadata records are no longer reported as unrecognized content.
* fix(session-import): find the chain end and publish without hard links
Take the last record that no other record names as its parent as the end
of a Claude Code conversation, so a parent written after its child at the
end of the file no longer hides the final prompt. On filesystems without
hard links, publish attachments with an exclusive copy instead of failing
the import.
* fix: apply model overrides before sizing session imports
Resolve the default model through its overrides, as the engine does,
before taking its input limit, so an override that lowers the context
size also lowers the import budget.
* feat(session-import): keep a copy of the source session with each import
Save the source session file in the import's attachments, minus Claude
Code's API request log, together with its Claude Code folder of subagent
transcripts and saved tool output, so history from before compaction and
abandoned branches outlive the source files. The imported message says
what the full history and the copy each hold, and clipped tool notes point
to the full history.
* feat(session-import): keep more of the source session in imports
- Copy the rollouts of the subagent threads a Codex session spawned under source/subagents/.
- Show what the user did during a turn (interruptions, slash commands, messages sent while
the agent worked, Codex shell commands) as [user_event] notes.
- Show the work recorded after a source compaction with its summary instead of dropping it.
- Title Codex sessions with the thread name from Codex's state database.
- Point omitted media and injected source context to the attachments that keep them.
* fix(session-import): show what came before a prompt at the start of its turn
- Show work and commands recorded before a turn's prompt at the start of that turn,
including Codex work before the first prompt, instead of dropping them without a summary.
- Wait up to 3 seconds for a Codex state database lock before falling back to the
first prompt as the title.
* fix(session-import): stop a Codex turn start from claiming earlier turns
- When a turn starts, claim only the records written before it that can open a turn
(user input and context), so a rollback no longer drops the previous turn's
answer and tool work in sessions that mix records with and without turn ids.
* fix(agent-core-v2): leave swarm mode to the user (#4384)
* fix(agent-core-v2): leave swarm mode to the user
AgentSwarm now runs only while swarm mode is on, and only the user can
turn swarm mode on, so the model can no longer start a swarm by itself.
- Reject AgentSwarm at resolve time while swarm mode is off and check
again right before it runs; no subagents start and swarm mode stays off.
- Stop AgentSwarm from entering swarm mode, reject the tool trigger, and
ignore tool-triggered entries that older versions recorded on replay.
- Skip the one-AgentSwarm-per-response veto while swarm mode is off so
the other tools in that response still run.
- Say in the AgentSwarm description and the swarm exit reminder that only
the user can turn swarm mode on.
- Update the tools and slash-command reference docs.
* fix(transcript): ignore model-started swarm entries when folding history
Swarm mode entries with the tool trigger come only from older versions,
where the model started a swarm by itself. The engine already ignores
them on replay, so a session that stopped mid-swarm resumes with swarm
mode off. Folding history still marked swarm mode on from that entry,
and no later status update turned it off, so the web UI kept showing
swarm mode as on.
Keep the swarm.enter marker in history, but leave meta.modes untouched
for tool-triggered entries.
# Conflicts:
# packages/agent-core-v2/test/agent/fullCompaction/fullCompaction.test.ts
# packages/agent-core-v2/test/agent/loop/loop.test.ts
# packages/agent-core-v2/test/tool/tool.test.ts
* fix(agent-core-v2): launch subagents only on explicit request (#4386)
The main agent launched subagents on its own: the explore type told it to
delegate any exploration needing more than three searches and to run several
explore agents at once, the Agent description sold delegation, and the
EnterPlanMode description sent plan mode to an explore subagent first.
- Agent description: launch a subagent only when the user asks for one, or
AGENTS.md, a skill, or a multi-agent mode the user turned on calls for it;
agent type descriptions only pick a type once launching is authorized.
- System prompt: state the same rule for Agent.
- Drop the delegation nudges from the Agent and explore descriptions and from
the EnterPlanMode description.
- Swarm exit reminder: once swarm mode ends, handle the next request without
subagents unless it asks for them.
* ci: release packages (#4385)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
* test(agent-core-v2): update snapshots and token expectations for merged prompt changes
---------
Co-authored-by: kimi-agent-bot <kimi-agent-bot@users.noreply.github.com>
Co-authored-by: Kai <me@kaiyi.cool>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
* fix: decouple workspace trust from runtime initialization (#4348)
* fix(kap-server): decouple workspace trust from runtime initialization
* fix: preserve workspace trust errors
* fix: emit workspace trust recovery changes
* fix: complete workspace trust recovery
* fix(agent-core-v2): reclaim session locks from zombie processes (#4320)
(cherry picked from commit 0a90e2e8a2)
* feat: let users take over a session lock left by an exited process (#4328)
* feat: let users take over a session lock left by an exited process
Session locks judged alive by mistake (a different hostname, pid reuse, or a failed process-state probe) blocked the session with no way out. Add a takeover path: the lock service evicts another process's lock, the SDK exposes Session.takeOverLock(), kap-server adds POST /api/v1/sessions/{id}:takeover, and the TUI offers Take over once a retry is still occupied. KIMI_CODE_SESSION_LOCK=0 turns the lock off for a process.
* fix(agent-core-v2): evict the lock directly on takeover
Takeover went through the reclaim marker, so a stale marker left by a crashed reclaimer on another host made takeover a silent no-op while the API reported success. Unlink the lock file directly instead.
* fix(agent-core-v2): surface takeover failures when the lock cannot be removed
Takeover swallowed unlink errors, so a lock file another user owns in a shared sticky directory was reported as taken over while the next retry stayed occupied. Ignore only ENOENT and let other errors reach the caller. Also fix the run gate test bootstrap stub typing.
* fix(tui): simplify the session takeover copy
* fix(tui): tie the takeover warning to the takeover action
* fix(tui): drop the holder line from the takeover dialog
* refactor(tui): move the session occupancy dialogs into a controller
(cherry picked from commit e7f1a99fc5)
* chore: sync web dist from code-app
code-app: 76483646b7c46a61bff868f1473535e0a32b473d
---------
Co-authored-by: 7Sageer <sag77r@hotmail.com>
* feat: show session lock holder client names (#4356)
* chore: sync web dist from code-app (#4357)
code-app: 7be8bf55306625ade7b5dbead86bc789d22d0159
Co-authored-by: kimi-agent-bot <kimi-agent-bot@users.noreply.github.com>
* ci: release packages (#4353)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
---------
Co-authored-by: liruifengv <liruifeng1024@gmail.com>
Co-authored-by: 7Sageer <sag77r@hotmail.com>
Co-authored-by: kimi-agent-bot <kimi-agent-bot@users.noreply.github.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
* feat(agent-core-v2): leave Plan mode to the user and review every plan
Plan mode becomes a user-controlled mode: the model no longer has a tool
to enter it, and every plan waits for the user's review before plan mode
ends, including in Never Ask mode.
- Remove the EnterPlanMode tool, its default approval and its telemetry
event, and drop both plan tools from the coder and Tower worker profiles.
- Review ExitPlanMode in every permission mode, and keep plan mode active
when the plan could not be shown for review.
- Tell the main agent that only the user can turn plan mode on, and to
answer plan-first or approach questions with a recommendation before
changing code.
- Rework the plan-mode reminders and the ExitPlanMode description: plans
lead with context and list verification steps and assumptions; questions
are limited to what the code cannot answer; in auto mode open decisions
are recorded as assumptions.
- Stop the exit reminder from implying that a plan was approved after the
user turns plan mode off.
- Make kimi -p skip default_plan_mode and refuse to resume a session that
is still in plan mode.
- Update the Never Ask copy, the goal and swarm start dialogs, and the docs.
* chore: merge the plan mode changesets into one
* fix(agent-core-v2): never wait for a person in non-interactive runs
kimi -p declares the host non-interactive, but approvals and questions
still queued for a person with no timeout. Print mode only avoided this
because auto mode never asked. Plan reviews now ask in auto mode too, so
a subagent left in Plan mode by an older version made kimi -p wait for a
review nobody could give.
Approvals now resolve at once in non-interactive hosts and are never
treated as approved: a plain approval denies the call, the plan review
keeps Plan mode on and tells the agent not to retry, and AskUserQuestion
returns that nobody can answer.
* fix: say that plans still wait for review in Never Ask copy and docs
The goal and swarm start dialogs, the /auto tip, the VS Code Auto mode
copy and the reference docs still promised that Never Ask never
interrupts. Plans in Plan mode now wait for review in every permission
mode, so these surfaces name that exception. The agents page also no
longer says that coder subagents can enter Plan mode.
* test(agent-core-v2): pin how bindings recorded with EnterPlanMode resume
Sessions created before EnterPlanMode was removed keep their recorded
system prompt and tool list. Resuming one keeps that prompt, keeps
ExitPlanMode usable for user-started Plan mode, and answers an
EnterPlanMode call with a not-found error.
* fix(agent-core-v2): keep Plan mode on the main agent only
Only the user can turn Plan mode on, and every user-side switch targets
the main agent. Subagents in sessions from older versions could still be
restored in Plan mode, because they used to enter it themselves through
EnterPlanMode. In the TUI their plan review popped up while the main
agent was not in Plan mode, and a subagent whose tool list lacked
ExitPlanMode could not leave Plan mode at all.
The plan state now ignores plan mode entries from any agent but the main
one, live and on replay, and entering Plan mode for a subagent fails.
Restored subagents simply continue their work.
* fix: let kimi -p run sessions that are still in Plan mode
kimi -p refused to resume a session that was in Plan mode, so a session
that any older version could run in print mode now failed with exit 1.
Nobody can review a plan in print mode, but the run does not need to
stop: the plan review already returns at once in non-interactive runs.
A resumed session in Plan mode now runs as usual and stays in Plan mode.
The run can only update the plan, which waits for review the next time
the session is opened interactively, and stderr says so up front.
* chore: rewrite the plan mode changeset
* fix: stop promising that Plan-mode print runs are read-only
The note for a resumed Plan-mode session said the run could only update
the plan, but Plan mode blocks only Write and Edit outside the plan
file. Bash and delegated work follow the permission mode, as they do in
interactive Never Ask.
The note, the docs and the changeset now state what is guaranteed: the
plan cannot be approved in print mode and waits for review.
* fix(agent-core-v2): let the agent answer in text while Plan mode is on
Every Plan-mode reminder told the agent to end each turn with
AskUserQuestion or ExitPlanMode, and in auto permission mode only with
ExitPlanMode, so even a plain question in Plan mode was supposed to end
in a question or a plan review. A planning turn still ends with one of
the two, but a message that calls for an answer rather than a plan,
such as a question about the code, now gets a text answer, and Plan
mode stays on until the user approves a plan or turns it off.
The Plan-mode note in the system prompt now keeps only the facts and
the user's intent: only the user can turn Plan mode on, and when they
ask to plan first, the agent waits for their go-ahead before changing
code. It no longer treats every question about how to approach a task
as a reason to stop, and no longer prescribes the shape of the reply.
In non-interactive runs, the plan review and AskUserQuestion results
now only state that nobody can review or answer, and that Plan mode
stays on for the rest of the run, instead of telling the agent what to
do next.
* test: pin that every plan waits for review across modes and hosts
Plan reviews now run in every permission mode, so pin the cases where a
regression would most likely slip through:
- yolo mode, allow rules for ExitPlanMode, and an earlier approval for
the whole session do not skip the review of the next plan;
- in Never Ask, kap-server surfaces the review as a pending plan_review
approval over REST and in the transcript, and approving, revising and
dismissing it leave Plan mode in the expected state;
- kimi -p keeps stream-json stdout parseable when the resumed session
is still in Plan mode;
- the reminder sent after leaving Plan mode only carries out a plan the
user approved.
* fix(agent-core-v2): update import-from-cc-codex for current Claude Code and Codex layouts
Honor CLAUDE_CONFIG_DIR and CODEX_HOME, import project CLAUDE.md, CLAUDE.local.md, Claude rules, Codex AGENTS.override.md, and Claude project-local MCP servers, and map the current Claude Code and Codex MCP fields. Stop scanning .claude/AGENTS.md, .codex/AGENTS.md, and .codex/skills, which neither tool reads.
* fix: preserve imported instructions and server settings
* fix(agent-core-v2): keep placeholder examples literal in import-from-cc-codex
The skill renderer substitutes argument and KIMI placeholders anywhere in a skill body, so the migration guidance reached the model with the session ID and a builtin path in place of the Kimi placeholder names, and invocation arguments were spliced into the prose. Name the placeholders without their literal forms and pin the rendered body in a test.
---------
Co-authored-by: qer <wbxl2000@outlook.com>
* refactor: remove unused session terminal service
The terminal_* WebSocket controls lost their dispatch when the v1 connection was rewritten, so terminals could be created over REST but never attached, written to, or resized. No client uses them: the web UI has no terminal and the desktop app runs its own native PTY. Remove the REST routes, WebSocket schemas, meta capability, error code, session terminal service, host PTY backend, runtime terminal capability, and the node-pty dependency.
* chore: update nix pnpmDeps hash after node-pty removal
* chore: upgrade to TypeScript 7 (native compiler)
- typescript 6.0.2 -> 7.0.2, tsdown 0.22.0 -> 0.23.1 (peer range now allows typescript ^7)
- CI typecheck uses the workspace tsc (native) instead of the frozen @typescript/native-preview beta snapshot
- product entry-points test spawns the tsc binary; the JS compiler API no longer ships with typescript
- check-no-comments.mjs pins typescript-legacy (TS6) for AST access
Co-authored-by: chengluyu <2239547+chengluyu@users.noreply.github.com>
* chore: align remaining typescript pins and refresh the nix pnpmDeps hash
- apps/kimi-inspect and apps/vis/web pin their own typescript; bump to 7.0.2 (sherif multiple-dependency-versions)
- flake.nix fetchPnpmDeps hash tracked the old lockfile; recompute for the new one (fixes nix build)
Co-authored-by: chengluyu <2239547+chengluyu@users.noreply.github.com>
* test(product): fail loudly when the tsc subprocess cannot run
runTsc only scanned output text; a missing native binary, a timeout, or
a spawn failure would yield empty diagnostics and the boundary checks
would pass vacuously. Assert the spawn error and exit status first.
Co-authored-by: chengluyu <2239547+chengluyu@users.noreply.github.com>
---------
Co-authored-by: kimi-agent-bot <kimi-agent-bot@users.noreply.github.com>
Co-authored-by: chengluyu <2239547+chengluyu@users.noreply.github.com>
* feat: let users take over a session lock left by an exited process
Session locks judged alive by mistake (a different hostname, pid reuse, or a failed process-state probe) blocked the session with no way out. Add a takeover path: the lock service evicts another process's lock, the SDK exposes Session.takeOverLock(), kap-server adds POST /api/v1/sessions/{id}:takeover, and the TUI offers Take over once a retry is still occupied. KIMI_CODE_SESSION_LOCK=0 turns the lock off for a process.
* fix(agent-core-v2): evict the lock directly on takeover
Takeover went through the reclaim marker, so a stale marker left by a crashed reclaimer on another host made takeover a silent no-op while the API reported success. Unlink the lock file directly instead.
* fix(agent-core-v2): surface takeover failures when the lock cannot be removed
Takeover swallowed unlink errors, so a lock file another user owns in a shared sticky directory was reported as taken over while the next retry stayed occupied. Ignore only ENOENT and let other errors reach the caller. Also fix the run gate test bootstrap stub typing.
* fix(tui): simplify the session takeover copy
* fix(tui): tie the takeover warning to the takeover action
* fix(tui): drop the holder line from the takeover dialog
* refactor(tui): move the session occupancy dialogs into a controller
* feat(kap-server): reclaim idle sessions
* test: trim idle session reclaim test duplication
* test: fix idle reaper fake typing
* fix(kap-server): preserve session state during idle reclaim
* fix(kap-server): resume cold sessions for tool routes
* fix(kap-server): report failed WS session resumes
* refactor(agent-core-v2): let features declare session idle retention
Move the cron, goal, and lifecycle-hook reclaim guards out of SessionManager
into a SessionRetentionContribution collection that each feature contributes,
check retention once under the session lease, and drop the dead optional
task-service branches in the tasks route.
* refactor(agent-core-v2): measure session idle from the activity view
* refactor(agent-core-v2): reclaim idle sessions silently instead of retaining hooked sessions
* fix(kap-server): only resume explicitly requested sessions in tool routes
* refactor(agent-core-v2): inline the cron idle-reclaim guard and stop retaining active goals
* fix(kap-server): reject invalid idle reclaim timeouts
* fix(kap-server): preserve rating evaluation across idle reclaim
* test(kap-server): trim idle reclaim tests
* fix(kap-server): validate idle reclaim config early
* refactor(agent-core-v2): rename the reclaim resume source and derive lifecycle literal unions
* refactor(agent-core-v2): keep the reclaim lifecycle tags off the public lifecycle interface
* fix(agent-core-v2): drop the reclaim marker once a session is archived
* Implement session unload after 30 minutes of idle time
Updated session management to unload sessions after 30 minutes of inactivity for both web and desktop applications.
Signed-off-by: 7Sageer <sag77r@hotmail.com>
* fix(agent-core-v2): reclaim sessions with exited terminals
---------
Signed-off-by: 7Sageer <sag77r@hotmail.com>
* ci: release packages (#4288)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
* revert: remove default workspace session creation (#4295)
(cherry picked from commit 83c7909be9)
* chore: merge release/2026-10-20 into release/2026-10-21 (#4294)
Bring the cloud-gated model switch confirmation (#4286, #4292) and the
queued /compact (#4276, #4279) onto main via the release back-sync.
The two changesets that rode along with the original squash
(.changeset/model-switch-confirm-dialog.md,
.changeset/queue-compact-command.md) are excluded here: they were
consumed by the 2.3.0 release and must not be re-consumed on main.
(cherry picked from commit 3f80896a79)
* chore: sync web dist from code-app (#4304)
code-app: 913137036c35c608f3a487eb2c7b4a8815b53b95
The stray .changeset/web-interaction-polish.md carried by the dist
payload is excluded: it was consumed by the 2.3.0 release and never
existed on main.
(cherry picked from commit d36071ede6)
Co-authored-by: kimi-agent-bot <kimi-agent-bot@users.noreply.github.com>
---------
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: qer <wbxl2000@outlook.com>
Co-authored-by: kimi-agent-bot <kimi-agent-bot@users.noreply.github.com>
* fix(transcript): realign rebuilt turn ordinals to wire ids and mark wedged turns
Cold transcript rebuilds numbered materialized turns 0..N while the live
projector assigns ordinal=wireId, so a session rebuilt after a restart got a
shifted axis that no longer aligned with what clients had cached live, and a
turn whose loop died before writing a terminal event was rebuilt as a fake
completed turn with no timestamps.
foldWireRecordFacts now reassigns every matched rebuilt turn to its wire turn
id (leaving holes for folded turns), restores startedAt from the
agent.turn.started / turn.prompt records, and marks a turn with no terminal
event as wedged (state failed + wedged flag) once a later turn prompt exists
in the wire; the tail turn is only wedged when it is not the live active one.
The live projector closes the previous running turn as wedged when a new turn
starts. The wedged marker is an additive optional field so released clients
keep parsing transcripts and render the turn as failed.
* feat(agent-core-v2): surface silent notification-delivery and persistence failures
The wedged-turn incident showed a main agent loop can stall for hours with
zero diagnostics: task notifications pooled in memory, a latched or hung wire
flush swallowed every subsequent record, and the running turn produced no
watchdog output.
- AppendLogStore warns once when an append is accepted while a storage
failure is latched, and warns when a flush stays pending past 30s
(repeating every 10min) with scope/key/pending counts.
- WireService routes append failures to ILogService in addition to
onUnexpectedError so they land in the daemon log.
- AgentLoopService arms a per-turn watchdog that warns after 10min without
observable progress (steps, streaming partials, machine phase), including
pending notification/pause/queue state; loop.notify and nudge consumption
log buffered/drained counts at debug level.
- AgentTaskService warns when lifecycle guards silently drop task
notifications or terminal events, and debug-logs in-process redelivery
suppression.
- EventDispatcherService warns once per episode when dispatch is stuck for
over 30s and events keep queueing behind it.
* test(agent-core-v2): quiesce the spawned notification turn before counting leaked timers
With the loop turn watchdog armed, a notification-driven turn that happens
to be running at the assertion holds a legitimate interval, making the
global timer count scheduling-dependent. Cancel any spawned turn and pump
the clock until the loop is idle before counting.
* chore: add changeset for the wedged-turn transcript fix
* feat(agent-core-v2): add cross-process session occupancy lock
Multiple clients (TUI / kap-server / VS Code / ACP) running core in
separate processes could open the same session and corrupt each
other's wire.jsonl and metadata. Each session now holds a cross-process
occupancy lock ({homeDir}/locks/sessions/{id}.lock) taken only while
writing: runs acquire it on submit / compact / undo / cron / delete /
fork / cold-archive, the append log holds it before flushing, and a
stale check compares the main agent wire's on-disk length with the
length this process knows.
Collisions surface as session.occupied (holder in details); writes by
another client detected after acquire surface as session.stale and the
caller reloads before continuing. kap-server maps them to
SESSION_OCCUPIED (40903) / SESSION_STALE (40930), reloads the session
on stale, and re-attaches WS subscriptions; the TUI shows an occupancy
dialog (retry/cancel) or reloads and replays history keeping the input;
print exits with code 4 on occupied and reloads automatically on stale;
VS Code / ACP get minimal occupancy notices. Locks are released a few
seconds after the session goes idle, on close, and on process exit;
locking I/O failures degrade to unlocked operation with a warning.
* docs: document session occupancy error codes and lock directory
* fix: address codex review on session occupancy lock
- metadata: re-read state.json on the first write of each lock-hold
epoch (holdToken), so pure-metadata changes made by another client
(title, archive) are not clobbered by turn-end lastPrompt writes
- lifecycle: hold an explicit lease across live archive(), matching
delete(), so the idle release timer cannot drop the lock mid-teardown
- tui: route the occupied dialog's Cancel selection through the same
cleanup as Esc (restore input, release recalled media)
- acp: tell stale-session users to close and reopen the session
- append-log: warn when retirement drops records kept in memory because
the session lock was unavailable
* fix: always run the staleness check in beginSessionRun
The prompt channels (AgentPromptChannel.submit/submitSteer, kap
/prompts, promptWithSkills) write prompt metadata before calling
loop.submit, and the metadata write acquires the session lock first.
beginSessionRun then short-circuited on 'already held' and skipped the
wire-length staleness check, so the main prompt path never detected
updates written by another client. Drop the already-held shortcut: the
check is a single stat and always passes while the lock is continuously
held, so every acquisition path now runs it regardless of order.
* fix(print): preserve occupied handling on the stale retry
If another client acquires the session between the reload and the
retry, the retried turn now goes through the same conflict handler:
session.occupied reports to stderr / stream-json and exits with code 4
instead of bubbling out as exit code 1.
* fix: do not mistake own in-flight writes for external modification
storage.append grows the file (writeFile) before fsync/close complete,
and knownLength only catches up afterwards; a beginSessionRun landing
in that window (steer, queued prompts, cron fires during a turn) was
misjudged as session.stale and deleted the lock file mid-turn. Track
in-flight bytes per log and compare disk against known + in-flight.
Cover the analogous rewrite window (post-rename syncDir) with an
expected-final-length marker.
* fix: bound the staleness comparison and check once per lock hold
The equality check disk !== known + inFlight moved the false-positive
window to before the append lands (mkdir/open) and into chunked
writes, so beginSessionRun during a turn (steer, cron, queued prompts)
could misjudge, delete the lock file mid-turn, and later append behind
another client. Compare known <= disk <= known + inFlight instead, and
evaluate the check only on a fresh hold (holdToken epoch): re-entrant
runs pass through without a stat, matching the RFC's 'already held
passes through' while every acquisition path still triggers the check
exactly once per hold.
Also unify the occupancy prompt copy across clients: the engine error
message is now the single user-facing text ('该会话正在被其他 Kimi
Code 进程使用;请停止其他进程中的会话或退出该进程后重试'), and the
TUI dialog, /compact /undo errors, print output, and the VS Code
fallback all display it instead of maintaining their own wordings.
* fix: address third-round codex review
- metadata: hold an explicit lease across each state.json write so the
idle release timer cannot unlink the lock mid-write
- print: preflight the session lock before goal creation; make goal
creation idempotent and emit the goal summary only for attempts that
did not fail with session.stale, so a stale reload + retry neither
duplicates the goal nor the summary
- tui: route every prompt-launch path (plain, goal steer, plugin
command, inline skill, batch steer) through one conflict handler
with the occupied dialog and stale reload + input restore
- tui: snapshot and restore the attachment store across stale reloads
so pasted media placeholders still resolve after history replay
* fix: move session occupancy to a fresh error code, use English copy, release locks on exit
SESSION_OCCUPIED reused 40903, which belonged to PROMPT_ALREADY_COMPLETED until #3747 retired it, and web clients still allowlist 40903 for that old meaning; reusing it conflated the two semantics. Move session occupancy to the never-used 40931 and update the server API reference in both locales.
The occupied error message was hardcoded in Chinese while every other engine error is English, so the TUI, print mode, VS Code, and ACP clients all rendered mixed-language output; replace it with an English equivalent.
The lock release hook ran on 'beforeExit', which never fires when the process leaves through process.exit (SIGHUP in the TUI goes through emergencyTerminalExit), leaving stale lock files that could keep a session looking occupied forever after a hostname change or pid reuse. releaseAll is fully synchronous, so hook 'exit' instead.
* fix(agent-core-v2): decide prompt-derived titles after re-reading session metadata
applyPromptMetadataUpdate decided whether to derive a title from the
prompt from the in-memory metadata cache, which is only refreshed when
the session lock is re-acquired. If another client named the session in
the meantime, the stale cache still looked untitled, so the next prompt
from this client overwrote the other client's title — both on the stale
path, where the prompt is rejected afterwards, and on the fresh path,
where the other client only changed the title on disk.
Move the untitled check into SessionMetadata.applyPromptMetadata, which
runs after ensureWriteOwnership() has re-read state.json, so the
decision is based on the latest persisted metadata. The published
SessionMetaUpdated event now carries the derived title only when it was
actually written.
* fix: address fourth-round codex review
- vscode: classify session.occupied and session.stale as preflight errors so the webview toasts them instead of silently swallowing the send, and give session.occupied the same English copy the engine emits (the toast text comes from getUserMessage in the extension host).
- engine: beginSessionRun now takes the session context and agent lifecycle service, and on the first check of each lock hold it runs externallyModified over every loaded agent's wire (scope agents/<agentId>, key wire.jsonl) instead of only the main agent's, so a fork that only touched a subagent journal is detected as stale. All four call sites (loop submit, manual compaction begin, undo, print preflight) pass the extra dependency.
- tui: keep the request in flight across a stale reload instead of dropping to idle, so input submitted during the reload queues rather than racing the closing session; park the queue around switchToSession (resetSessionRuntime would clear it) and drain it into the reloaded session afterwards; when restoring the rejected text, prepend it before content the user typed during the reload instead of overwriting it.
- kap-server: clear the in-flight turn tracker and subagent roster in detachSession so a reattached session no longer reports stale in-flight turns or running subagents in REST snapshots and WS resyncs.
* fix: harden session conflict recovery
Detect external appends that race journal reads so a later run cannot miss session.stale. Keep deferred wire migrations pending and finish them before appending, while rejecting the append if migration still cannot complete. Restore the complete plugin command after an occupied cancellation, including commands without arguments.
* fix: scope session staleness cache to session context
Replace the process-global session-id map with a WeakMap keyed by ISessionContext so closed and reloaded sessions do not retain cache entries for the lifetime of the process.
* fix: document session conflicts for metadata routes
Declare SESSION_OCCUPIED and SESSION_STALE in the response schemas for profile updates, title generation, and child creation. Cover the OpenAPI declarations and the existing HTTP 200 conflict envelopes with regression tests.
* refactor: centralize session conflict handling
Move session error mapping and stale reloads into one shared route helper, reuse shared conflict response declarations, and cover skill activation conflicts. Remove the duplicate prompt, session, and skill error mappers while preserving their existing envelopes.
* fix(sdk): restate session conflict errors
* fix(tui): keep the reloaded session open and resend after a stale conflict
* fix(agent-core-v2): lazily rebuild registered agents after a session reload
* refactor: simplify session conflict handling
* refactor: hygiene fixes from session-lock review
- kap-server: move SESSION_STALE to 40932 (collided with
DEFAULT_WORKSPACE_UNAVAILABLE at 40930) and pin ErrorCode value
uniqueness with a test
- node-sdk: fold six catch-and-restate copies into a restate() helper
- acp-server: sync mapPromptLaunchError docstring with the
occupied/stale branches
- replace hardcoded session.stale/session.occupied literals with
ErrorCodes constants
- inline the single-use sessionStaleError
- restore the strict timer-leak assertion with a warmup baseline
instead of a blanket +1 allowance
- narrow SessionState.detached to ?: true
* refactor: introduce session-level run gate
- replace the beginSessionRun free function with ISessionRunGate, a
Session-scoped service; loop/compaction/undo now inject one
dependency instead of four, and sessionLock.ts no longer imports
session-layer modules
- tryHold() absorbs the duplicated try/catch hold copies in
wireService and sessionMetadataService
- resolve IAgentLifecycleService lazily inside the gate to avoid a
construction-time cycle with agentLifecycleService
* refactor: tighten session lock and conflict recovery structure
- Narrow ISessionLockService to hold/tryHold/release/lease/held and share
the hold bookkeeping between the memory and node-fs backends; drop
holdToken and the module-level exit hook state.
- Session metadata refreshes on a new hold via onDidHold and skips agent
registration through tryHold instead of catching session.occupied.
- AppendLogStore reads sizes through IFileSystemStorageService.sizeSync and
exposes a generic write guard instead of a session-lock binding.
- kap: reload stale sessions only at the call sites that can raise
session.stale and log reload failures; error mapping stays side-effect free.
- TUI: recover from a stale conflict through the in-place session reload with
history replay, removing the same-session switch special case, the media
store snapshot, and the queue parking.
- print: bound stale reloads with a simple retry loop.
* refactor(agent-core-v2): merge session lock release into run gate
* test(agent-core-v2): reuse append log fixtures
* refactor(agent-core-v2): break run gate and agent lifecycle dependency cycle
* refactor: reload stale sessions in core instead of each host
Stale detection already lived in core, but every host re-implemented the
recovery: TUI, print, and kap each closed and resumed the session, and TUI
and web resent the rejected input themselves.
Core now exposes reloadOnStale: on session.stale it closes and resumes the
session, lets the caller refresh, then retries the call once (undo reloads
and still rejects, since its count depends on what the user saw).
- kap: prompt submit, skill activation, and compact retry in place instead
of returning 40932; undo still returns 40932 after reloading
- print: retries through reloadOnStale on the same session id and keeps the
original permission mode to restore
- SDK: run-starting calls reload and retry internally; Session gains
setStaleReloadHandler so hosts refresh before the retry
- TUI: drops its own reload/resend/queue-merge path; the stale handler
replays history and keeps in-flight messages
* docs(server-api): note that only undo returns 40932 after a stale reload
* refactor(agent-core-v2): ask each agent's wire whether it was modified externally
* refactor(agent-core-v2): clarify agent restoration helper name
* Delete .changeset/ensure-agent-lazy-rebuild.md
Signed-off-by: 7Sageer <sag77r@hotmail.com>
* fix(tui): wrap session occupied notice
---------
Signed-off-by: 7Sageer <sag77r@hotmail.com>
The default workspace lived in <Documents>/KimiCode/Default. On macOS the
first chat there triggered a Documents privacy prompt, and a denial blocked
every later chat until the user found the setting. Documents is also synced
by iCloud and OneDrive, and covered by Windows Controlled Folder Access.
Use ~/KimiCode/Default on every platform instead. This drops the Windows
PowerShell lookup, the XDG user-dirs parsing, and the documents_unavailable
reason; the error path is now always present.
* feat(server): create sessions in a default workspace when none is chosen
POST /api/v1/sessions accepts default_workspace: true, exclusive with
workspace_id and metadata.cwd. The server resolves <Documents>/KimiCode/Default
on every request, creates it on first use, and reuses it afterwards. A path
blocked by a file fails with 40409 and a permission error with 40411; the
server never relocates silently.
Workspace payloads on REST and WebSocket events carry is_default.
* fix(server): fail instead of relocating when Windows Documents cannot be resolved
A failed registry lookup used to fall back to %USERPROFILE%\Documents, which
could silently create a second default workspace when Documents is redirected
(for example by OneDrive). The lookup now fails with 40409, the failure is not
cached so the next request retries, and is_default reports false meanwhile.
* fix(server): resolve Windows Documents through the shell folder API
Reading User Shell Folders with reg.exe misses the default when the value is
absent, cannot expand every variable, and is blocked where registry tools are
disabled by policy. Ask Windows for the Documents folder through PowerShell
instead, forcing UTF-8 output so non-ASCII paths survive. A reported folder
that does not exist is treated as unresolved, so garbled output can never
create a workspace in the wrong place.
* test(server): assert the existing error code for a missing Windows Documents folder
* fix(server): start Windows PowerShell by absolute path
The Documents lookup spawned the bare name powershell.exe, so Windows
resolved it through the current directory and PATH. In an untrusted
checkout that lets a repository-provided powershell.exe run before the
trust gate. Resolve it under SystemRoot (falling back to windir and
then C:\Windows) instead.
* feat(server): report an unusable default workspace with its path and reason
Creating a session in the default workspace used to surface folder problems
as generic codes: 40409 when a file blocked the path or the Windows
Documents folder could not be located, 40411 on permission errors, and a
plain internal error for anything else. Clients could not tell the default
workspace apart from an ordinary missing directory, nor explain what to fix.
DefaultWorkspaceService now throws DefaultWorkspaceUnavailableError
(default_workspace.unavailable) carrying the folder path and one of
blocked_by_file, permission_denied, missing_parent, no_space, read_only,
invalid_path, documents_unavailable or unknown. ensure() also verifies that
a file can be created and removed in the folder, so a read-only folder is
reported before a session is placed there. The server maps the error to
40930 with { path, reason } in data.
* feat(server): make the default workspace location configurable
The default workspace was fixed at <Documents>/KimiCode/Default. Users who
keep chats on another disk, or whose Documents folder is synced, had no way
to move it.
Add a default_workspace.path setting in config.toml. It accepts an absolute
path or ~/..., and on Windows only a drive path or a full UNC share; the
value is checked for writability before it is saved and is never moved
silently. An invalid value in the file is reported as invalid_path instead
of falling back to the system location.
The server exposes GET and PATCH /api/v1/default-workspace with the
effective path, the saved custom path, the system path and the folder's
health. The section is kept out of GET /api/v1/config and rejected by
POST /api/v1/config so it has a single writer. Whenever the location
changes, through PATCH or an edit to config.toml, every registered
workspace broadcasts event.workspace.updated with its current is_default.
* feat(cli): add --default-workspace and expose the default workspace in the SDK
Add kimi --default-workspace to work in the shared default workspace instead
of the current directory. Both the interactive shell and print mode resolve
the folder through the engine before the session starts and change into it,
keeping PWD in sync; when the folder cannot be used, startup fails with its
path and reason. Resume hints become kimi --default-workspace -r <id> for
such sessions.
The session picker labels sessions in the default workspace as "Default
workspace" and copies a resume command without a cd for them. kimi doctor
reports the location and whether it can be used. The stty lookup excludes
both the launch directory and the new working directory, so a binary
planted in either is never executed.
The SDK gains KimiHarness.ensureDefaultWorkspace(), getDefaultWorkspacePath()
and getDefaultWorkspaceStatus(), maps default_workspace.unavailable to a
KimiError, and exports workspaceRootKey and DEFAULT_WORKSPACE_PROBLEM_MESSAGES.
* fix(server): never remove a write probe the check did not create
createExclusive resolves to false when the probe name already exists. The
write check ignored that result and went on to remove the path, which is a
recursive delete of an entry it did not create. Report the folder as
unknown instead and leave the entry alone.
* fix(server): declare 40001 on PATCH /default-workspace and log failed broadcasts
The PATCH route returns 40001 when the body fails validation, but its
OpenAPI response union only listed success and 40930, so generated clients
could not model it.
When the default workspace location changed but the workspace catalog
could not be read, the publisher dropped the broadcast without a trace.
Log a warning so the missing event.workspace.updated can be diagnosed.
* fix(cli): point the resume hint at the session folder after the default moves
A session started with --default-workspace printed
kimi --default-workspace -r <id> on exit even if default_workspace.path
changed while it was open. Following that hint moved into the new default
folder and refused the session, which still lives in the old one.
Repeat the flag only while the session folder is still the default
workspace; otherwise print cd <folder> && kimi -r <id>.
* refactor: trim the default workspace to what clients use
Keep session creation in the default workspace, the configurable
location, the 40930 error with its reason, and `kimi --default-workspace`.
Remove what no client needs:
- write probes, ancestor walks, and dangling-link checks; saving a
location now creates the folder, and use-time errors come from mkdir
- `system_path` and `problem` in the status response
- the workspace-update publisher; clients refetch workspaces after PATCH
- special handling of `default_workspace` in GET/POST /config
- the stray FS_PERMISSION_DENIED mapping on session routes
- the session picker label, the doctor check, the extra stty exclusion,
the moved-default resume hint, and the SDK path/status methods
Fix three gaps found along the way:
- clearing the location left `path` in config.toml, so it came back on
reload; drop the section instead
- treat an empty or relative XDG_CONFIG_HOME as unset
- honor HostProcessOptions.timeout so a stalled PowerShell lookup ends
* refactor: drop the --default-workspace CLI flag
No CLI user needs it yet. Desktop and web reach the default workspace
through the server, so the flag, its resume hint, and the SDK's
ensureDefaultWorkspace() go away along with their changesets.
* refactor: drop the default workspace settings endpoint
The location is now changed only by editing default_workspace.path in
config.toml, which the config watcher picks up without a restart. Remove
GET/PATCH /api/v1/default-workspace and the engine's status() and
setCustomPath(). Session creation keeps mapping the unavailable error to
40930 with { path, reason }, and the reason list moves to the
POST /sessions docs.
* fix: require a drive or UNC share for a Windows default workspace path
Rooted paths such as \Chats resolve against the server's current drive,
so the same setting could point at different folders. Treat them as
invalid_path instead.
* feat: notify the agent of external changes after /reload
* refactor: route watch-based AGENTS.md change alerts through the change notifier
* test: pin the missed detection of trailing AGENTS.md content deletions
* fix(agent-core-v2): anchor change-notifier comparisons to delivered beliefs
* fix(agent-core-v2): diff the full subagent model pool in change notifications
* fix(agent-core-v2): mute stale-session auto notifications during session reload
* chore(agent-core-v2): regenerate manifests and complete notifier stubs for the reload fixes
* chore: update the reload-change-reminders changeset
* test(agent-core-v2): cover the announced-then-deleted AGENTS.md sequence
* fix(vis): cover the changeNotifier.snapshot wire record
* fix(agent-core-v2): harden change-notifier beliefs for legacy wires and edge baselines
* fix(agent-core-v2): unmute the stale notifier when a session reload fails midway
* fix(agent-core-v2): seed change-notifier beliefs from the rendered snapshot
* fix(agent-core-v2): await the change-notifier snapshot dispatch
* fix(vis): harden the change-notifier snapshot renderer for legacy records
* test: isolate the reload e2e from the watch channel and tighten seed assertions
* chore(agent-core-v2): regenerate the wire manifest for optional belief fields
* chore: drop the changeset for reload change reminders
* fix(agent-core-v2): diff reload reminders against persisted beliefs first
* fix(agent-core-v2): recompute the bound subagent allowlist on catalog changes
* fix(agent-core-v2): roll back change-notifier beliefs on conversation undo
* feat(agent-core-v2): inline added profile metadata in subagent change reminders
* fix(agent-core-v2): detect legacy AGENTS.md truncation and gate skill reminders
* fix(agent-core-v2): reset change-notifier beliefs on context clear
* feat(kap-server): project the reload session action into the OpenAPI document
* fix(node-sdk): keep plugin-only reloads from refreshing workspace catalogs
* fix(agent-core-v2): gate and filter subagent change reminders
* fix(agent-core-v2): evaluate reminder gates against the persisted binding
* fix(kap-server): scope reload catalog refreshes to the target workspace
* fix(node-sdk): scope reload catalog refreshes to the target workspace
* fix(agent-core-v2): reset change-notifier beliefs on compaction
* fix(agent-core-v2): hash AGENTS.md files from one atomic snapshot
* fix(agent-core-v2): close first-watch and legacy gaps in change detection
* fix(kap-server): block reload during compaction and reconcile after failed reload
* fix(node-sdk): block reload during compaction and reconcile after failed reload
* feat(agent-core-v2): let dangerous_command_guard=false disable all built-in guard ask policies
* docs(agent-core-v2): name git control-path read relaxation and scope the only-ask-rules claim
Agent-level hooks such as PreToolUse, UserPromptSubmit, Stop and PreCompact,
plus hooks fired from the event bus, did not pass a cwd, so the runner fell
back to the directory the engine was started from. Session-level hooks
already used the session directory, and the hooks documentation says the
working directory is the session's project directory. A server hosting
several workspaces, or a CLI that changes directory after the engine starts,
ran these hooks in the wrong place.
* fix(agent-core-v2): nudge the agent to keep its todo list current
- Remind only while the list still has pending or in_progress items, and
drop the first-use arming gate so compaction and /undo no longer arm the
reminder for agents that never used TodoList.
- Remind after 5 steps without an update (was 10) with text that names the
step count, progress, and the item still in progress, instead of the
gentle "ignore it if not applicable" wording.
- Replace the static TodoList write echo with a next step for the current
list state.
- Drop the churn warnings that discouraged updates from the TodoList
description, say the list is a live progress panel, and add a TodoList
paragraph to the system prompt when the tool is active.
- Track todo_list_written, todo_reminder_shown, and todo_turn_ended.
* fix(agent-core-v2): address todo reminder review feedback
- Ask the agent to mark the last open item done only once it is finished;
if it stays blocked or unfinished, keep it in_progress and say what
remains in the reply.
- Emit todo_turn_ended for turns that cleared the list, so cleared lists
count toward the stale-list ratio instead of being dropped.
* feat(tui): auto-generate session title after the first turn
* Update tui-auto-session-title.md
Signed-off-by: 7Sageer <sag77r@hotmail.com>
* feat(tui): gate auto session title behind a tui.toml auto_session_title toggle
---------
Signed-off-by: 7Sageer <sag77r@hotmail.com>
* fix(google-genai): map thinking effort off to LOW for Gemini 4 models
Gemini 4 supports only LOW/MEDIUM/HIGH thinking levels and always thinks;
sending MINIMAL for effort=off makes every request fail with a 400. Degrade
'off' to LOW (thoughts still hidden) for gemini-4 models in both the
agent-core-v2 requester and the legacy kosong provider, keeping MINIMAL for
Gemini 3.
Co-authored-by: 7Hanrui <qihanrui@moonshot.ai>
* fix(google-genai): drop comment in comment-free agent-core-v2 source
Co-authored-by: 7Hanrui <qihanrui@moonshot.ai>
* fix(google-genai): preserve effort off in thinkingEffort for Gemini 4
LOW with suppressed thoughts is the Gemini 4 encoding of 'off', so map it
back to 'off' in the thinkingEffort getter instead of reporting 'low'.
Co-authored-by: 7Hanrui <qihanrui@moonshot.ai>
* fix(google-genai): keep explicit LOW kwargs reported as low on non-Gemini-4
Only Gemini 4 encodes 'off' as LOW with suppressed thoughts, so gate the
reverse mapping on the model; a caller-supplied LOW/includeThoughts=false on
other models keeps reporting 'low'.
Co-authored-by: 7Hanrui <qihanrui@moonshot.ai>
* fix(google-genai): report LOW as low in thinkingEffort again
Gemini 4 encodes 'off' as LOW with suppressed thoughts, which is
indistinguishable from an explicit LOW/includeThoughts=false kwarg, so no
reverse mapping can serve both callers. Nothing in the repo reads this
getter to persist or display effort, so keep the derive-from-kwargs
semantics; for Gemini 4's 'off', 'low' is the honest report of what runs.
Co-authored-by: 7Hanrui <qihanrui@moonshot.ai>
* fix(google-genai): reject thinking effort off for Gemini 4 models
Gemini 4 supports only LOW/MEDIUM/HIGH thinking levels and always thinks,
so requesting effort 'off' now fails fast with a clear thinking-config
error (matching the always-reasons rejection other protocols already use)
instead of sending an invalid MINIMAL level or silently degrading to LOW.
Co-authored-by: 7Hanrui <qihanrui@moonshot.ai>
---------
Co-authored-by: kimi-agent-bot <kimi-agent-bot@users.noreply.github.com>
Co-authored-by: 7Hanrui <qihanrui@moonshot.ai>
* fix(google-genai): use thinking level for Gemini 4 models
The thinking parameter is picked by a hard-coded `gemini-3` name check, so
Gemini 4 model ids fall through to `thinkingBudget`, which the Gemini API
rejects with HTTP 400 ("Thinking budget is not supported for this model.
Use thinking level instead."). Add `gemini-4` to the same check in both the
engine copy and the legacy kosong provider; Gemini 3 and earlier keep their
current encoding.
* chore(changeset): keep the Gemini 4 entry to the user-facing outcome
* test(google-genai): use the real Gemini 4 model id in the thinking tests
---------
Co-authored-by: Selene <mahaoyang@corp.netease.com>
* feat(agent-core-v2): add global default for deferred MCP tool disclosure
* feat(agent-core-v2): capture the deferred MCP default at server connect time
* feat: support model migration metadata
* chore: revert web bundle sync
* feat: persist model upgrade metadata
* fix: resolve model upgrade targets
* fix: keep model upgrade metadata consistent
* fix: complete model upgrade metadata handling
* fix: handle aliased upgrade targets and RFC3339 variants
* fix: resolve bare model upgrade targets
* feat: prompt for model migration in the TUI
* fix(tui): defer model migration until the editor is idle
* fix(tui): preserve model migration prompts and preferences
* fix(tui): hold the model migration prompt while /new replaces the session
* fix(tui): keep the resume cache hint from replacing an open dialog
* fix(tui): keep the thinking effort across a model migration when supported
* fix(tui): strip terminal control sequences from migration notices
* fix(tui): warn about prompt cache loss before a mid-conversation migration
* docs: drop the effort persistence claim from the migration wording
* test: trim redundant migration test cases
* fix: switch to the declared replacement when a registry drops the default model
A registry retires a model by dropping it after declaring `upgrade`, but
the refresh deleted the alias together with its upgrade metadata and
cleared the default, leaving users without a model and with a misleading
login hint. Refresh and re-import now move a dropped default to the
upgrade target persisted by an earlier refresh, clearing it as before
when that target is gone too. The TUI warns about the switch and moves
the live session off the dropped model once no dialog is open.
* docs: fix the /reload-tui command spelling in config-files
* fix: resolve migration placeholders at import and keep acknowledgements out of tui.toml
Migration text placeholders were only filled in by the TUI, so the model
catalog served raw templates to every other client. The registry import
now stores the final text and the TUI renders it as is.
Migration acknowledgements are display state rather than preferences, so
they now live in model-migrations.json under the data directory instead of
tui.toml; preference saves and acknowledgements no longer rewrite each
other, and /reload has nothing to sync.
Also carry the dropped model's record in the refresh result instead of
snapshotting it in the TUI, give performModelSwitch explicit persistence
modes instead of a boolean-or-mode flag, fold the Markdown notice into the
picker's notice option, and rename waitForLazyCreation to
waitForSessionAssembly now that it also waits for /new.
* refactor(tui): move the shared model switch out of the /model command
performModelSwitch and its helpers became shared once the migration
prompt and the retired-default switch started calling them, yet they
stayed in the /model command module, leaving a controller depending on
command internals. Move them unchanged to tui/utils/model-switch.ts, which
the command module and the migration controller now both import.
* feat: surface retired default model redirects in refresh results and catalog events
A registry-side retirement only existed in the OAuth orchestrator's
in-process RefreshResult, so kap-server's scheduled refreshes redirected
the default model without any client-visible signal. The refresh response
now carries an optional retired_default { from, to, from_model }, mapped
through to the REST refresh endpoints and the global
event.model_catalog.changed broadcast, and mirrored in the klient
contract.
* refactor(tui): consume the engine's provider refresh instead of orchestrating its own
The TUI ran the OAuth refresh orchestrator in-process through its own
persistence host (including a dead v1 fallback), duplicating what the
engine's discovery service already does. KimiHarness now exposes
refreshProviderModels (via the klient facade over
IProviderDiscoveryService), so the TUI deletes its orchestration glue and
reads retired_default from the engine response. Also fix the klient
facade deriving RefreshProviderModelsResponse from the OAuth login
refresh, which hid retired_default from clients.
* feat: move model retirement follow-through into the engine
The discovery service now records retirements, emits onDidRetireModel,
and answers retiredModelReplacement; agent profiles follow a retirement
live and on restore, so the TUI only shows the notice and steers the
session-less selection. The wire retired_default drops from_model in
favor of an engine-resolved from_display_name. Removes the now-dead
supportsAtomicSectionReplace / replaceConfigSections /
removeProviderFromConfig SDK exports (no remaining consumers).
* Update model migration guidance in documentation
Prompt for migration when the TUI editor is idle and switch to the declared replacement when a registry drops the default model.
Signed-off-by: 7Sageer <sag77r@hotmail.com>
* test(oauth): drop the duplicate dropped-default redirect case
---------
Signed-off-by: 7Sageer <sag77r@hotmail.com>
* feat(agent-core-v2): distinguish session-unavailable tools in select_tools
* fix(agent-core-v2): explain tool selection failures and repeated guesses
* fix(agent-core-v2): keep failure counts honest in mixed select_tools batches
- Reuse the globalPolicy cache for denial checks instead of bypassing it.
- A successful name now only resets the consecutive-failure streak and
clears its own failure record; failed names in the same batch keep
accumulating attempts instead of being masked by sibling successes.
* fix(agent-core-v2): honor tool source in policy checks and announce empty loadable sets
- Evaluate deny globs with the registered tool's real source; only
unregistered names fall back to the mcp__ prefix inference.
- Keep select_tools callable via the disclosure check so a profile
allowlist omission no longer mislabels it as policy-denied.
- Announce the empty loadable state once for legacy sessions whose
last announcement lacks it, then stay silent.
* feat(kap-server): add remote control device management
* feat(remote-control): tag share URLs with the caller's platform source
The share link's from query parameter was hardcoded to kimi_code_cli, so
links created by the desktop app were mislabeled as CLI traffic.
buildRemoteControlUrl now takes the source explicitly and
RemoteControlOptions carries it as urlSource; kap-server threads
hostIdentity.platform through, so desktop hosts produce
from=kimi_code_desktop while CLI hosts keep from=kimi_code_cli.
* fix(kap-server): stop the local tunnel when the current device is deactivated or deleted
The device management routes only called the cloud API. When the target
device is the machine itself, the local tunnel kept running: after the
relay drops it, the client reconnects and re-registers, so the device
came back online (or was recreated after a delete) and the local status
never returned to off.
After a successful cloud deactivate/delete of the current device, the
routes now also disable the local tunnel manager.
* feat(kap-server)!: drop the remote-control device delete route
Device removal is collapsed to a single deactivate verb: the desktop UI
deactivates devices instead of deleting them, so the DELETE
/remote-control/devices/{id} route and the device client's deleteDevice
go away before this API ever ships.
* feat(kap-server): carry the lock holder in the already-running error
The enable route now returns the RemoteControlAlreadyRunningError holder
(pid, local origin, device id, url, started_at) in the error envelope's
details, documented with a details schema, so desktop can render a
structured conflict state instead of the raw message.
* feat(kap-server): expose the lock conflict in the remote control status
The manager now surfaces the holder when its last enable error is an
already-running lock conflict, and GET /remote-control returns it as a
structured conflict field, so clients can present the conflict state
proactively instead of parsing the error message.
* feat(kap-server): resolve the remote control lock conflict live in status
GET /remote-control now re-inspects the machine lock whenever the
manager is off: a live holder is reported as the conflict even without
a prior enable attempt in this process, and a stored conflict plus its
error text are dropped once the holder's process is gone. Panel state
refreshes such as after device deactivation now reflect reality.
* feat(kap-server): wait for the lock holder to release on current-device deactivation
When the deactivated device is the machine's own and the lock is held
by another live process (e.g. a CLI instance whose tunnel the relay is
about to terminate with user_requested), the route now polls the lock
for up to two seconds before responding, so a client's immediately
following status read sees the settled conflict-free state.
* style(kap-server): silence promise-executor-return in lock release wait
* test(remote-control): pass urlSource in the close-reason cases
* feat(kap-server)!: register remote control devices with the host product name
* refactor(cli): source the remote control identity from app constants
* fix(remote-control): make starts cancellable and harden logout cleanup
* fix(remote-control): close the remaining cancellation and invalidation races
* fix(kap-server): sweep remote control cleanup once more after logout
* docs: note the lock-release failure of remote control deactivation
* fix(remote-control): key cloud sessions by credential and settle cancelled starts
* fix(kap-server): block remote control starts during logout and serialize tunnel toggles
* fix(kap-server): gate local starts during current-device deactivation
* fix(kap-server): match active tunnel ids on deactivation and re-clear sessions after logout
* fix(kap-server): serialize Kimi logouts, harden lock cleanup, and whitelist device fields
* fix(remote-control): abort pending handshakes on close and answer logout before tunnel teardown
* fix(remote-control): cancel enables that are still queued behind a pending disable
* fix(kap-server): recognize foreign logout gates and retry post-logout tunnel teardown
* fix(remote-control): verify the machine lock after disable and recheck deactivation
* feat(kap-server): export loadOrCreateServerToken for host token provisioning
* feat(agent-core-v2): report fork request prefix parity in the prompt cache probe
The fork prompt cache probe only reported the child's cache usage, so a low
hit rate could not be told apart between a client-side prefix divergence and
a server-side miss, and the hit rate itself was diluted by the new tail that
can never be cached.
The requester now keeps a snapshot of each agent's last completed turn
request (the tools and system prompt hashes it already records in
llm.request, input tokens and timing). A forked agent captures its parent's
snapshot when it is created and reports tools_match, system_prompt_match,
parent_input_tokens and parent_request_gap_ms with its first request.
* fix(agent-core-v2): clear the parent request snapshot when the context is rewritten
Undo and compaction rewrite the parent context without touching the
non-persisted snapshot, so a /btw fork made before the next turn request
compared against a request whose input the child no longer inherits.
* fix(agent-core-v2): drop stale parent baselines from the fork cache probe
The probe compared the child's first request against the parent snapshot
captured at scope creation, but a parent undo or compaction landing
before the context copy left that snapshot describing a request the
child no longer inherits, and a model switch between the parent's last
turn and the fork compared across cache namespaces that can never share
a prompt cache.
The probe now watches the parent's ContextSpliced/ContextUndone events
and drops the captured snapshot once the parent context is rewritten,
and the snapshot carries the model alias so the comparison fields and
the parent input token baseline are only reported when both sides ran
the same model.
* fix(agent-core-v2): keep the turn request snapshot across conversation undo
Undo only truncates the tail of the parent context, so the request a
/btw fork inherits is still a strict prefix of the request the parent
last cached: the server cache keeps hitting and the snapshot's hashes
stay accurate. Clearing the snapshot on undo threw away a baseline
that was still valid, and made the requester depend on the undo
domain for no benefit. Only compaction rewrites the prefix, so it
remains the sole event that clears the snapshot.
* refactor(agent-core-v2): validate the fork cache probe baseline by context prefix revision
The requester subscribed to ContextSpliced solely to keep a telemetry
snapshot fresh, which made the module understand context rewrites and
grow an event bus dependency for the probe's sake. The probe's own
subscription could not cover forks created after a compaction either,
which is the only reason the requester had to listen at all.
Context memory now exposes a prefix revision that increments on every
deleting splice, the turn request snapshot records the revision it was
taken at, and the probe compares it with the parent's current revision
at probe time. Read-time validation covers both the compact-then-fork
and the fork-then-compact windows with no subscriptions, and the
requester is back to only recording facts about its own requests.
* fix(agent-core-v2): stamp the turn request snapshot with the build-time context revision
The public SDK clearContext path has no busy check and can empty the
parent context while a turn request is in flight. Reading the revision
at completion stamped the snapshot with the post-clear value, so a
later fork saw matching revisions and hashes and silently attributed
the client-side prefix divergence to the server. The request input is
fixed by recordRequest time, so capture the revision in the same
synchronous block; a mid-flight clear then leaves the snapshot on the
pre-clear revision and the probe drops the baseline on comparison.
* fix(agent-core-v2): capture the snapshot context revision where the request messages are copied
The build-time capture still ran after the awaited media resolution,
so a clearContext or compaction landing in that window stamped the
snapshot with the post-rewrite revision for a request whose messages
were copied earlier. resolveRequest is synchronous and copies the
context messages there, so the revision captured alongside the copy
always describes the same context the request was built from.
* fix(agent-core-v2): omit the parent token baseline when the parent request reported no usage
A provider that completes without a usage event left the snapshot with
inputTokens: 0, so a later same-model fork emitted parent_input_tokens: 0
and the input_cache_read / parent_input_tokens analysis divided by zero
or treated an unknown baseline as real. The field is now optional and
absent for that snapshot, which the optional telemetry property already
allows.
* feat: show NotifyUser progress in desktop and web
Give NotifyUser a one-line title, report the latest update on the
session status, and forward subagent updates to the parent so GUI
clients can show them on subagent cards.
* feat: turn on NotifyUser progress updates by default
Flip the notify_user flag default to true; the flag stays as the opt-out
(`[experimental] notify_user = false` or KIMI_CODE_EXPERIMENTAL_NOTIFY_USER=0).
Register the new subagent.update wire record in the vis renderers and
record-type union so the manifest coverage test passes.
* refactor: trim NotifyUser surfaces to conversation progress rows
* feat: make NotifyUser title optional and enforce single-line titles
* feat: make NotifyUser opt-in via the settings lab
* docs: describe NotifyUser as off by default with the settings lab opt-in
* fix(remote-control): exit the web server when the relay takes the device offline
* test(cli): satisfy the remote-control handle contract in tui web tests
* feat(tower): coordinate missions with deterministic orchestration
* fix(tower): disable goal mode
* feat(tower): degrade gracefully on tower state loss
When .tower/comms is deleted while .tower/ survives (e.g. git clean),
tower tools no longer hard-fail with not-initialized:
- TowerStatus returns a state-lost report; never-initialized repos
(no .tower/ at all) keep the existing not-initialized behavior
- TowerSend/TowerComplete recover a minimal state (directory skeleton,
empty missions/roster, recoveredAt marker, base taken from the
current checkout branch) and keep delivering; a caller whose roster
entry was lost is re-registered as a placeholder named after its
agent id
- TowerComplete degrades explicitly after a loss: the report is
delivered to the tower as a completion-report-state-lost message and
no mission is marked completed
- TowerMerge/TowerRebase and every other locked mutation fail with an
explicit state-lost error instead of silently running against an
empty history
- the recoveredAt marker is shown on the TowerStatus dashboard and in
the status summary, reminding the tower to report the history loss
to the user
* feat(tower): veto destructive bash commands in the tower main checkout
* fix(tower): allow git clean dot pathspecs inside tower worktrees
* fix(tower): restore attachment guidance for mission context
* fix(tower): engage bash guard only while the workspace has an owner
* fix(tower): veto git clean only with force and -x/-X; drop markInboxRead
* chore(tower): drop per-fix changesets to keep a single concise one
* fix(tower): check tower owner before preparing the user base
* feat(tower): show a notice when goal activation ends tower mode
* fix(tower): unify the mode-mutex exit reason across goal, plan, and swarm
* refactor(tower): drop token stamping from protocol artifacts
* fix(agent-core-v2): deny tower workers subagent delegation
Remove the Agent tool and the subagents allowlist from the tower-worker
profile so workers and reviewers cannot nest subagents inside a mission;
nested subagents bypassed the tower concurrency budget and the tower's
steering. Drop the now-unreachable reviewer-only Agent-resume veto and
its tests; the branch-lease and foreground-resume vetoes stay.
* test(tower): stub ILogService in every AgentTowerService test scope
The fork-restore tests build second DI scopes that register
AgentTowerService without stubbing ILogService, leaving this.log
undefined. When reconcileForeignTower fires a fire-and-forget exit and
the ownership release rejects, the rejection handler's this.log.warn
throws a TypeError that surfaces as an unhandled rejection in the full
suite. Add ix2.stub(ILogService, stubLog()) to all nine scopes that
lacked it, matching the existing sibling pattern.
---------
Co-authored-by: konghuanjun <konghuanjun@moonshot.ai>
* fix(agent-core-v2): stop discouraging mid-turn progress notes and calm NotifyUser nudges
- system prompt: drop the inaccurate "text between tool calls may not be
shown" premise; ask for one- or two-sentence progress notes instead
- remove the mid-turn text nudge that asked the model to repost visible
text as a NotifyUser update
- count the silence nudge in tool-call rounds (one per step) instead of
individual tool calls, so parallel calls no longer trip it early
* feat(agent-core-v2): track NotifyUser nudges and updates
- notify_user_nudge_shown: rounds since the last update and the nudge's
position within the current silent stretch
- notify_user_sent: rounds before the update, whether a nudge preceded
it, message length (no content), and whether it was displayed
* chore: add changeset for mid-turn progress notes and NotifyUser nudges
* feat(cli): launch desktop with kimi app
* fix(cli): report desktop opener failures and complete command tests
* fix(cli): resolve system openers from filesystem roots
* feat(kap-server): add allow_ignored_globs to fs:list for un-ignoring named paths
The list filter keeps gitignored entries hidden unless they match the
new allow_ignored_globs request field, letting callers surface
specific ignored paths (e.g. .tmp) while the rest of gitignore still
applies.
* docs: document allow_ignored_globs in the fs:list API reference
* fix: traverse ignored ancestor directories of allow_ignored_globs matches
A nested allow glob such as ignored-dir/keep.txt or ignored-dir/** used
to be unreachable in recursive listings because the ignored parent was
skipped before any child could match. Keep an ignored entry when it is a
literal-prefix ancestor of an allowed glob (directories only), and
document that dot-path allowances additionally require show_hidden.
* fix: match ignored ancestor directories across wildcard glob segments
The literal-prefix check still dropped ancestors when a directory
segment of an allow glob contained a wildcard, e.g. ignored-*/keep.txt
or ignored-dir/**/keep.txt. Decide reachability by consuming the
candidate path segment-wise against a prefix of the glob instead.
* fix: correct and harden workspace fs glob matching
Compile **/ to (?:[^/]+/)* so recursive wildcards preserve path
segment boundaries; a/**/b no longer matches a/xxb. This affects
include_globs, exclude_globs, and allow_ignored_globs across
fs:list / fs:search / fs:suggest / fs:grep.
Match glob prefixes iteratively in globCanMatchBelow so deeply
segmented globs cannot exhaust the call stack.
* chore: add changesets for allow_ignored_globs and the glob boundary fix
* fix: satisfy noUncheckedIndexedAccess in the glob prefix grid
* fix: restrict globstar segment expansion to whole path segments
foo**/keep.txt regressed when **/ gained segment-boundary semantics:
the stars sat mid-segment but were expanded as a whole-segment
globstar. Only expand **/ when the stars open a path segment, treat
mid-segment stars as an ordinary within-segment wildcard, and keep a
trailing ** matching across segments as before.
* fix: match workspace fs globs with picomatch and probe speculative ancestors
Replace the hand-rolled glob compiler with picomatch, the standard
engine already used elsewhere in the package, which settles segment
boundary, mid-segment globstar, and deep-pattern complexity edge
cases for good. An ignored directory kept only as a possible ancestor
of an allow glob is now probed (depth and entry capped, fail-open) and
withheld when no descendant actually matches.
* fix: cast readonly glob list for picomatch isMatch
* fix: parse globs whole in the ancestor check and share the probe budget
Splitting allow globs on '/' corrupts brace and extglob constructs
that contain slashes; take path segments from picomatch.scan instead
and fall back to a conservative answer for opaque single-token globs,
letting the descendant probe decide visibility with whole-pattern
matching.
Probe budgets are now shared across one fs:list request instead of
resetting per candidate directory, so a tree full of speculative
ancestors cannot multiply filesystem operations.
* fix: tolerate empty globs and slash-bearing alternatives
picomatch throws on empty patterns where the old compiler simply
never matched, turning previously valid requests into server errors;
filter them out up front. Brace and extglob alternatives that embed
a slash inside one scanned part now fall back to the conservative
ancestor answer as well, leaving visibility to the whole-pattern
descendant probe.
* fix: disable picomatch negation in workspace fs globs
A leading exclamation mark was a literal character in the old
matcher, so exclude_globs like !foo only matched a path literally
named !foo; as a picomatch negation the same request would exclude
everything except foo. Pin the prior semantics with nonegate.
* fix: scan allowance globs with nonegate and skip empty segments
The scan call still used default negation, stripping a literal
leading bang from allowance prefixes like !foo/keep.txt. Consecutive
separators produced empty scanned segments that made picomatch throw
during prefix compilation; empty segments now simply match nothing.
---------
Co-authored-by: qer <wbxl2000@outlook.com>
Fork copies the wire journal, so cron tasks persisted as cron.add
records were inherited by the fork and fired from both sessions.
Fold the forked marker record into the cron actor: the task table is
cleared at the fork point and a one-time cron_fork_cleared reminder is
delivered (tracked by a durable forkNotice flag confirmed via
ContextAppendMessage), mirroring the goal feature's fork handling.
The forked event class moves from features/goal to
session/agentLifecycle so both features share it.
* fix(agent-core-v2): fold UserPromptSubmit hook output into prompt content parts
UserPromptSubmit hook messages now merge into the user prompt message as
meta-marked text content parts (contentType text/xml, source 'user prompt
submit hook') at content position 0 instead of a standalone hook_result
user message, so one prompt always yields exactly one user message and
turn pairing can no longer shift. Structured hook JSON without a message
field (e.g. {"continue":true}) no longer injects anything; plain-text
stdout still falls through. turn.prompt records, TurnStarted prompts,
fold turn openings, and steer content keys all strip hook parts; existing
wires with standalone hook_result messages keep their current behavior.
* feat(agent-core-v2): keep UserPromptSubmit hook parts in turn records and handle them across client surfaces
* feat(agent-core-v2): meta-mark bundled skill parts and replace positional slicing
Bundled skill blocks are now constructed with meta { source: 'skill
activation' }, matching the hook-part convention. Every former
content.slice(skillActivations.length) site — turn prompt text, steer
merge/echo, session and fork titles, transcript fold, TUI replay — now
annotates then filters by meta, so prompt content no longer depends on
block positions and the loop needs no hook-part knowledge. A shared
annotate step keeps legacy wires working: leading unmarked blocks (one
per bundled activation) are marked on read before filtering.
* feat(agent-core-v2): strip hook parts from the public TurnStarted prompt at construction
* feat(kimi-code): show what trusting activates in the workspace trust prompt
* fix(kimi-code): keep trust prompt non-blocking and disclose only external additional dirs
* fix(kimi-code): mark trust disclosure incomplete when discovery times out or fails
* fix(kimi-code): exclude disabled MCP servers from trust disclosure and cap key lists
* fix(kimi-code): disclose disabled user MCP servers shadowed by project config
* test(kimi-code): merge redundant trust prompt disclosure cases
* test(node-sdk): merge mirrored user/project MCP override cases
* Update trust-panel-disclosure.md
Signed-off-by: 7Sageer <sag77r@hotmail.com>
* fix(kimi-code): disclose symlinked additional dirs by real target in the trust prompt
* fix(kimi-code): bound trust disclosure I/O and harden path containment
* fix(kimi-code): tighten trust prompt disclosure copy
* refactor(kimi-code): compute the trust disclosure inside the engine
The disclosure composition (project-origin MCP servers, additional dirs
with realpath containment, instruction sources) moves from the SDK into
agent-core-v2's workspace scope as WorkspaceTrustDisclosureService,
constructed alongside the other generation services. The SDK's
getWorkspaceTrustInfo becomes a thin forward, so the config-merge
semantics live next to the workspaceTrust gate that owns them instead
of being re-derived downstream.
Also drops the shadowed-user-server section and the per-section
deadlines with their disclosureComplete flag: the engine reads the
same loaded state session creation awaits, so the prompt needs no
timeout plumbing of its own, and the empty state stays a single honest
copy.
* refactor(kimi-code): compress trust disclosure tests and disclose only effective profiles
Merge mirrored test cases across the three suites and share fixtures;
small source trims (shared wrap helper, context.workspaceId).
The disclosed agent profiles now pass the same resolution the session
catalog applies: a workspace profile clashing with a builtin without
override: true, or losing to a higher-priority candidate, is no longer
reported as activated.
* test(kimi-code): drop the engine disclosure suite in favor of e2e coverage
The SDK e2e suite exercises the disclosure through the real engine, so
the stub-based engine unit file duplicated it; a builtin-clash agent
case moves there to keep the suppressed-profile behavior pinned.
Also folds the sanitize vectors into the MCP render case and trims the
trust type docs.
* fix(kimi-code): cap untrusted MCP target fields and correct profile wording
A crafted .mcp.json can supply arbitrarily long command/args/url
strings; each field now truncates at 80 chars before wrapping. The
profiles line no longer claims they feed the agent — a non-default
workspace profile is only made available as a subagent, the default
agent still binds the default profile.
* fix(kimi-code): honor profile overrides and reveal instruction symlinks in trust disclosure
A workspace profile with override: true replaces a same-name builtin
or higher-priority candidate in the session catalog, so the disclosure
now applies the same replace semantics instead of dropping it.
Instruction paths get the same realpath treatment as additional dirs:
a project AGENTS.md symlinked to a file outside the project renders
its real target instead of hiding behind the lexical path.
* fix(kimi-code): disclose skill roots escaping the project in the trust prompt
Read/Write/Edit/Grep/Glob all add getSkillRoots() to their allowed
roots, so a .kimi-code/skills symlink to an external directory is an
outside-project file access grant that trusting activates. The
disclosure now merges those roots (resolved via the same projectRoots()
the skill source uses) into the outside-directories list. Also caps
each displayed MCP key name, not just the key count.
* fix(kimi-code): cap the bearer-token env var label in the trust prompt
* refactor(kimi-code): disclose only real locations in the trust prompt
The path → realPath arrow is dropped; gated directories and AGENTS.md
entries are disclosed as their resolved real locations, and the pair
type collapses back to plain strings. Entries resolving to the same
real directory collapse into one.
* fix(kimi-code): tighten the trust prompt list caps to fit one screen
* fix(kimi-code): lower the trust prompt list caps further
* fix(agent-core-v2): keep explicit agent-file precedence in the trust disclosure
* Revert "fix(agent-core-v2): keep explicit agent-file precedence in the trust disclosure"
This reverts commit 6e5c45d1b69061a041cc991a0882b1c651fc6003.
* fix(agent-core-v2): preserve profile priority in trust disclosure
* fix(kimi-code): keep workspace trust choices visible
* fix(kimi-code): align trust prompt paging keys
* docs: remove trust prompt keyboard additions
* feat(kimi-code): summarize workspace trust with configuration sources
* chore(agent-core-v2): sync workspace instruction state manifest
* fix(kimi-code): recognize dot-prefixed project directories in trust summary
* refactor(kimi-code): reuse existing trust configuration sources
* fix(kimi-code): keep trust disclosure independent of skill watchers
* refactor(kimi-code): condense additional directory disclosure
---------
Signed-off-by: 7Sageer <sag77r@hotmail.com>
* feat: trust workspace via KIMI_CODE_TRUST_WORKSPACE env var
* fix: honor trust env override in MCP registry and untrust route
* test: add getEnv to mcpManagement bootstrap mock
* test: fold trust-env route case into existing workspaces suite
* fix(cli): mention KIMI_CODE_TRUST_WORKSPACE in the print-mode MCP trust warning
* docs: describe KIMI_CODE_TRUST_WORKSPACE by its semantics, not its MCP effect
The nudge reminders were gated on the notify_user flag and on the tool
being registered, but not on the host. A session created in the TUI keeps
the tool after it is reopened elsewhere, so a host that cannot display
updates kept telling the agent to send them while every call reported
that nothing was displayed.
Gate the reminders on the same availability check the tool uses when it
runs: the flag is on and the host declared the update_panel capability.
- tower mode: a message sent while the agent is running aborts the in-progress inbox-wake turn and runs as its own turn; notification-seeded turn waiters now settle launched/completion
- TowerTeardown: keeps worktrees of live agents (not overridable with force), supports exclude and dry_run
- tower roster: died/revived store writes are guarded to the active tower session and attributed with session+pid; agents stopped by the user are marked dead by intent with no revival nag, and unintended deaths get diagnose-first guidance
- workers must TowerInbox before marking a mission completed: the store refuses completion with unread inbox messages, and the briefing carries the inbox-before-completion, mission re-read, and long-operation chunking instructions
- tower store: state.json read-modify-write is serialized with a cross-process lockfile (token-checked release) and written atomically via per-writer tmp + rename
Co-authored-by: konghuanjun <konghuanjun@moonshot.ai>
* chore: add regression and user-impact review to the PR workflow
The review-pr skill now runs a separate regression pass with impact levels
and a repository-specific list of user populations and contract files, the
PR template asks authors for a behavior-change table, Code Review Rules in
AGENTS.md encode the same checks for automated review, and changesets must
name the behavior users lose when a default flips.
* chore(agents): stop instructing agents to invoke the review-pr skill
The skill is user-triggered only (disable-model-invocation), so AGENTS.md
no longer tells agents to run it; the Code Review Rules keep pointing at
surfaces.md for the population and contract list.
* chore(review): define impact levels by consequence and close inventory gaps
Impact levels are now decided in three steps from the change itself
(consequence, escape hatch, silent-input upgrade) with changeset and docs
kept in the remediation check; escape-hatch wording allows explicit
maintainer sign-off; prompt-text rule names the receiving population;
one-sided diff evidence is accepted; release and packaging CI, the klient
facade, the transcript contract, kosong providers, and all env-var access
forms are added to the surfaces inventory.
* revert: roll back workspace trust-boundary hardening
Roll back the hardening added in #3964 that guarded against malicious
content inside an already-trusted repository:
- background git invocations no longer override repo-local git config
(fsmonitor, hooksPath, filters, drivers, gpg, editor)
- agent file tools no longer re-resolve paths through symlinks
- project-local local.toml applies without workspace-trust gating,
additional_dir entries are no longer rejected for resolving to the
home directory or filesystem root, and writing it is auto-approved
again inside a git worktree
Kept, since they only affect the pre-trust window or the trust prompt
itself: the fail-closed trust prompt and lazy footer git detection.
* Update revert-trust-boundary-hardening.md
Signed-off-by: 7Sageer <sag77r@hotmail.com>
---------
Signed-off-by: 7Sageer <sag77r@hotmail.com>
* docs(changelog): sync 2.1.0 from apps/kimi-code/CHANGELOG.md
* docs(changelog): shorten the fullscreen TUI entry to how to enable it
* docs(changelog): reword the fullscreen TUI entry
* chore: sync web dist from code-app
code-app: 955d6b744b76630828c3d80406e16d73a7a7454f
* chore: collapse web dist sync changesets into one summary entry
* fix(remote-control): pick credential slot and relay from login region
Global login stores tokens in kimi-code-env-*.json and should use
code-rc.kimi.ai; Remote Control always read kimi-code.json and
code-rc.kimi.com. Resolve both from the persisted oauth ref.
* fix(telemetry): skip the dead global collector and keep shutdown alive
The global collector host has no DNS. Drop it from the region profile and
skip the send instead of falling back to the mainland endpoint. Keep the
shutdown timeout timer referenced so a failed flush cannot exit before the
TUI hands the process to the foreground server.
* fix(remote-control): require explicit relay origin and drop hardcoded .com defaults
* feat(agent-core-v2): add repeat_breaker config switch
Add a top-level repeat_breaker boolean (default true) to config.toml,
overridable with the KIMI_CODE_REPEAT_BREAKER env var. When disabled,
the agent skips repeat reminders and forced stops on consecutively
repeated identical tool calls; same-step dedupe is unaffected.
* Update repeat-breaker-config-switch.md
Signed-off-by: 7Sageer <sag77r@hotmail.com>
* feat(agent-core-v2): add tool_dedupe config switch
* refactor(agent-core-v2): drop the tool_dedupe config switch
Same-step dedupe stays always on. repeat_breaker remains the only public switch.
* refactor(agent-core-v2): read the repeat breaker from the environment only
KIMI_CODE_REPEAT_BREAKER is the only switch. Unset keeps reminders and forced stops on.
---------
Signed-off-by: 7Sageer <sag77r@hotmail.com>
* chore: rewrite PR template in Chinese with structured sections
* chore: switch PR template to English, add write-pr and review-pr skills
* chore: add mermaid to write-pr, disable model invocation for review-pr, require write-pr skill in AGENTS.md
* fix: restore swarm members from persisted lifecycle events
* test(agent-core-v2): register subagent lifecycle records in the wire vocabulary
* chore(agent-core-v2): regenerate the wire manifest for subagent lifecycle records
The manifest inventory drives the vis wire-renderer coverage check, so vis gains a renderer for each new record type and treats them as timeline-neutral in the context projector.
* fix(transcript): associate a folded subagent with the task registered after its spawn
Some emitters register the task after publishing the spawn, so the durable
spawn record carries no task id and the cold fold keyed the member by its
agent id. A later task.started then created a second task that no subagent
terminal event ever reached, leaving a duplicate stuck in the running state.
Folding task.started now records the agent-to-task association the live
projector keeps, and terminal subagent records update both representations
when both are retained.
* fix(transcript): restore subagent token usage when folding a completed member
The durable subagent.completed record carries the member's token usage and the
live projector copies it onto the task, but the cold fold dropped it, so a
reopened conversation showed a completed member without usage metadata. The
fold now validates the payload's four counters and restores them.
* fix(kap-server): base the lost-member decision on the turn that spawned it
The cold snapshot only terminalised an unfinished foreground member when the
parent agent was idle, so a member left behind by a restart stayed marked as
running forever once the user started a new parent turn before backfill.
The restored agent refs already name the tool call that spawned each member,
so the decision now uses the turn owning that call: a member is lost when its
own loop is not running and its spawning turn is not the turn the parent is
running now.
* fix(transcript): adopt the spawn placeholder into the task registered afterwards
A spawn published before its task is registered — the Tower worker path —
carries no task id, so the fold created a placeholder keyed by the agent id
and the later task.started added a second task under the registered id. Only
the registered task was ever terminalised, and the detached placeholder was
skipped by the lost-member pass, so a restored transcript kept a duplicate
worker shown as running forever. task.started now folds the placeholder into
the registered task instead of leaving it behind.
* fix(kap-server): drain the live wire before reading a cold snapshot
The append log buffers records and writes them in batches, which is why its
own reader flushes the log first. The cold transcript reads the wire file
directly and skipped that drain, so a snapshot could stop on a stale prefix.
When the transcript binding is created after a turn has already ended, the
whole store is built from that snapshot and nothing repairs it afterwards,
leaving the turn's last tool call without its result and its member marked
lost. Reading now drains the live agent's wire first, as session export
already does before it reads the same files.
* feat(tower): enforce protocol invariants and deny AgentSwarm in tower mode
- split missions by functional boundary and workload; simplify spawn briefings
- flip completed missions back to active on non-clean reviews and brief respawns with review history
- refuse completed with open tasks or zero-diff build branches; cap review rounds at 5 with a task_drop escape hatch
- name the next step in review/merge/status tool results; flag unspawned missions in TowerStatus
- veto AgentSwarm execution while tower mode is active
- deflake kap-server catalog test: waitForServerState timeout 3s to 10s
* fix(spawnTool): remove striction
---------
Co-authored-by: konghuanjun <konghuanjun@moonshot.ai>
* feat(cli): support audience targeting for startup banners
* feat(cli): rename the banner audience field to kfc_audience
* chore: revert unintended oxlint autofixes introduced by the merge commit
The lint-staged oxlint --fix pass applied warning-level unicorn autofixes to
main's files during the merge (prefer-at, prefer-string-replace-all,
toSorted, catch-error-name). The catch-error-name rename also renamed object
shorthand properties (reason -> error), which no longer matches the declared
types and fails tsc. Restore main's versions of these four files; committing
with --no-verify so the same autofix does not re-apply.
* refactor(cli): type the banner wire shape concretely
* refactor(cli): narrow the banner audience fields to literal unions
* fix(cli): classify 403 as anonymous and map REGION_OVERSEA in banner audience
* refactor(cli): require banner_id for banner entries
* fix(cli): drop the banner subtext indent when it cannot fit
* fix(cli): avoid cache filename collisions across config endpoints
* fix(cli): keep banner loading from rejecting when the token lookup fails
* fix(kap-server): reject malformed and null Origins
isOriginAllowed conflated an unparseable Origin header with an absent
one and allowed both, so a sandboxed document (Origin: null) passed the
WebSocket upgrade check and had its Origin echoed by the CORS hook.
An absent Origin is still allowed for non-browser clients, but a
present Origin that fails to parse, is null, or uses a non-http(s)
scheme is now rejected: the WS upgrade responds 403 and the CORS hook
withholds Access-Control-Allow-Origin.
* fix(kimi-code): sync web bundle for kimi_origin query gating
* fix(agent-core-v2): gate workspace local.toml behind trust and tighten additional_dir
* fix(agent-core-v2): block symlink escapes in agent file tools
* fix: harden background git invocations against repo-local config
* fix(agent-core-v2): close trust-gate gaps in add-dir and additional_dir validation
* fix: neutralize repo-configured clean and process filters in hardened git calls
* fix(agent-core-v2): fail closed when a path is too deep to verify
* fix: probe worktree-scoped filter config in hardened git calls
* fix: fail closed when git filter discovery fails and cache probes for repo subdirectories
* fix(agent-core-v2): block symlink aliases of the project-local config in write tools
* fix: neutralize smudge, merge drivers, and repo-controlled commit signing in hardened git calls
* fix: expand included git configs when discovering repo-defined drivers
* Revert "fix(kimi-code): sync web bundle for kimi_origin query gating"
This reverts commit cd6af49cf1dfebe505f0e890453c3207a74d3e80.
* fix: keep git hardening fail-closed on includes and probe failures in scanners
* fix: fail closed on uncodable driver names and disable repo-controlled signature verification
* fix: neutralize repo-controlled editors and match include sections case-insensitively
* fix: probe config names only and reject dangling symlinks in file tools
* fix: fail closed when core.worktree escapes the repository
* fix(agent-core-v2): match project config paths across separators and case
* fix: arm footer git only after workspace trust persists
* fix(agent-core-v2): check resolved sensitive targets for absolute outside paths
* fix: only skip git config caching for actual include sections
* refactor: share git config hardening primitives between agent-core-v2 and the CLI
* fix(agent-core-v2): clear trusted additional dirs synchronously on trust revocation
* fix(agent-core-v2): compare resolved write targets with Windows path semantics
* fix: fail closed when the git directory disappears during hardening probes
* refactor: move shared git hardening primitives into @moonshot-ai/git-hardening
* fix: close symlinked-gitdir and submodule-recurse gaps in git hardening
* fix(agent-core-v2): reject home-directory ancestors as additional_dir roots
* fix: compare core.worktree with Windows path semantics
* fix: skip git config probes on renders after a negative repo detection
* fix: resolve gitfile pointers against the .git file directory and match includeIf sections
* test: drop duplicated symlink and plain-file cases covered elsewhere
* fix: only treat project-config writes as aliased when the lexical path misses the config pattern
* test: move the realpath guard matrix into a module test
* test: cover git-hardening primitives in the shared package
* refactor: inline git hardening helpers and drop the shared package
Keep the constants and parsers next to the async engine spawn path and
the sync CLI spawn path instead of a third workspace package.
* refactor: drop footer git trust gate and config probing
Workspace trust is a process-level decision, so the TUI never mounts
the footer until the user has trusted. Footer git status now uses
static hardening flags only.
* refactor: route git execution through IGitService.runGit
- Add runGit(cwd, args, options) to IGitService as the public door for
running git with repo-local config hardening applied.
- GitService implements runGit using the existing hardenedGitConfigArgs
helper; status/diff now delegate to it.
- collectGitContext drops its own spawn/probe logic and calls
IGitService.runGit instead, receiving the service through the
promptPrefix context.
- Tower keeps its own git() function (accepted gap).
* refactor: move git hardening helpers to app/git/hardening
* Revert "fix(kap-server): reject malformed and null Origins"
This reverts commit b799a21d0390ff0e4e66437f0d7b90c0b8171efe.
* perf: cut CLI startup time and memory in the native binary and config reads
Ship the single-file binary with a V8 code cache and a 16 MB semi-space
(execArgv), lower dynamic import() to require() in the CommonJS bundle and
fail the native build if any survives, and stop the engine from redoing
work on every config read: env-bound sections are re-validated only when
their env values change, the tool policy service caches the global tools
section and refreshes it on section-change events, Ajv instances are built
lazily, the macOS product version is read from SystemVersion.plist instead
of spawning sw_vers, and the host time zone and date formatter are cached.
Measured on the 100-round mock benchmark: CU 4.1-4.4 -> 3.0-3.2,
CPU 2.6-2.8 -> 2.0-2.2 core-seconds, peak RSS 550-610 MB -> 370-465 MB,
`kimi --version` 0.48 s / 401 MB -> 0.33 s / 311 MB.
* fix(native): keep dynamic import() in the code-cached binary and cover plugin entries in smoke
Lowering import() to require() broke `__plugin_run_node` (the bundler dropped
the node:url binding, and require() cannot load file:// URLs or top-level-await
modules), so every plugin MCP server that runs on Node failed under the native
binary. The CommonJS SEA main script keeps a working import() with the V8 code
cache on Node 24, so the lowering and the lexer guard go away and the native
smoke now runs a plugin entry. Also assert that tool policy checks do not read
the tools section, and drop the host time zone cache.
* fix(agent-core-v2): key the env-bound config memo by section identity
A domain re-registered by a different section definition could hit the memo
entry left by the previous definition when the validated base and env values
were unchanged, returning a value parsed by the old schema.
* feat(plugins): update kimi-webbridge plugin to the 2.0.11 skill
Bring the official plugin in line with the Kimi Browser Extension 2.x
daemon: document the ok/data response envelope, self-serve daemon
recovery via start, the movable listen address (10086 is only the
default), file-body retries on macOS/Linux, and the rewritten
operations reference (CLI subcommands, disk layout, new status
fields). Sync the manifest and marketplace versions to 2.0.11 and
point the help links at the new product pages.
* fix(plugins): point the Browser Extension help links at the canonical product path
The site canonicalizes /products/kimi-webbridge to
/products/kimi-browser-extension; ship the final URL in the skill,
manifest, and docs instead of relying on the redirect.
* feat(agent-core-v2): report active plugins and plugin toggles in telemetry
* fix(agent-core-v2): refresh active plugin context before recording plugin_toggle
* refactor(agent-core-v2): publish plugin telemetry from committed state
* refactor(agent-core-v2): split plugin value import from type imports
* refactor(agent-core-v2): report enabled plugins on turn and toggle events
* feat(transcript): include step timing and usage in cold-folded steps
* refactor(transcript): carry only TTFT and stream duration in cold-folded steps
* refactor(transcript): rename step timing field to llmTiming
* docs(skill): list all entries for versions with 10 or fewer upstream entries in sync-changelog
* docs(changelog): sync 2.0.2 from apps/kimi-code/CHANGELOG.md
* chore: sync web dist from code-app
code-app: 44d7281c7a63ee3c7a907f9406e490efdbec7411
* chore: collapse web dist sync changesets into one summary entry
* fix(agent-core-v2): floor the human turn clock at the wire-wide max on engine journal reset
* fix(kap-server): fold the cold transcript snapshot over the restorable branch chain
* fix(agent-core-v2): advance the human turn clock when a turn starts
* fix(kap-server): split reused wire turn ids in the live transcript projector
* fix(kap-server): adopt the completed cold tip for mid-turn attach events
Continuation deltas after a lazy transcript attach belong on the last
cold turn, not a newly split export id.
This reverts 64505e36e3.
The v1 WS + legacy REST + transcript surfaces are alive on main and remain
the single protocol surface; kimi-inspect returns to the transcript-based
data model (keeping #3747's removal of the prompt input, which adapted to
the agent-core-v2 prompt-queue fold).
* fix(agent-core-v2): reuse the queued prompt id for a single steer
A follow-up sent during a turn minted a second context id, so cancel
while the model was still requesting could show the same text twice
and make the host prompt un-undoable. Reuse the queued prompt id when
one prompt is steered, mark that user as in-turn so undo targets the
host turn, and project it as a non-anchor origin for the client.
* fix(agent-core-v2): type in-turn steer origin on the human prompt
The human PromptOrigin type rejected the inTurn field, which failed
typecheck/build and left the generated state manifest stale.
Reserve the context message id when a prompt is steered, emit turn.steer only after that message is appended, and join live/cold transcript plus fork by the id instead of content.
* fix(agent-core-v2): avoid root watch inventory and add a kill switch
Watch concrete candidate paths so a flooded project root cannot fill the V8 heap. Allow disabling every watcher with [watch] enabled or KIMI_CODE_WATCH.
* fix(agent-core-v2): stop watching the whole .kimi-code directory
Watch missing config files on the file path itself so session writes do not reload.
* fix(agent-core-v2): keep session deletion from hanging
Deleting a live session closes it first, and three waits on that path
had no upper bound:
- AgentLifecycleService.remove() checked its 3s quiesce deadline only
after awaiting loop.settled() and the compaction promise, so a loop
that never settled after cancel() blocked agent removal, session
close and delete forever. The settle wait now races the deadline and
falls through to the existing forced-teardown path when it expires.
- HostProcess.kill() on Windows waited for taskkill to exit with no
bound. It now stops waiting after 5s.
- setSessionArchived() awaited a pending resume inside its lifecycle
critical section. A resume queued behind the archive then waited for
the archive while the archive waited for it, wedging the session's
lifecycle chain so that every later operation on the session,
delete included, never started. The resume is now settled before
the critical section is entered.
* fix(agent-core-v2): terminate a taskkill that outlives its timeout
When the 5s wait on taskkill timed out, kill() returned but the spawned
taskkill child stayed alive and referenced, so every hang leaked a
taskkill.exe process and its handle could keep the CLI or server
process from exiting. Keep the handle and, when the timeout wins, unref
it and terminate it.
* fix(agent-core-v2): never apply a compaction summary that arrives after abort
Cancelling a full compaction, or removing its agent, aborts the
compaction's signal, and the abort listener records the cancellation
and detaches the compaction right away. compactionRound() never checked
the signal again after the summary request returned, so a provider
request that finished despite the abort still went on to
applyCompaction(). That rewrote the context of a cancelled compaction,
and since agent removal now stops waiting for the compaction at its
quiesce deadline, it could also write records after the agent was torn
down and recreate files under a deleted session directory. Check the
signal after the last await and before applying the summary.
The print background policy only watched goals, future cron fires, and
background tasks, so a turn steered by a cron fire was invisible to it:
- a fired one-shot task is deleted from the schedule at once, so the
policy saw an empty schedule and returned; teardown then cancelled the
steered turn mid-flight
- a recurring task whose fire comes due during a long turn keeps
reporting the same past fire time (the tick waits for the loop to go
idle), so the anti-spin guard misread a held fire as a wedged tick and
gave up on cron
The policy now checks turnActive (the same loop-state predicate the cron
tick uses) at the top of every round and waits the in-flight turn out
before drawing any quiescence or wedge conclusion.
* feat(agent-core-v2): support api_key_env for provider credentials
Providers can declare api_key_env = "VAR" in config.toml to read their
Bearer token from an environment variable instead of storing it inline.
The value is resolved per request (rotation-friendly); an unset or empty
variable fails with config.invalid naming the provider and variable.
apiKey+apiKeyEnv and apiKeyEnv+oauth are rejected as conflicts.
The /models refresh path resolves the same chain (inline apiKey ->
apiKeyEnv -> env sub-table); a provider whose variable is unset lands in
failed[] without affecting others, and open-platform providers keep the
env declaration instead of persisting the resolved secret.
Custom registries: applyCustomRegistryProvider consumes the api.json env
field — imported providers get apiKeyEnv, the legacy inline apiKey is
dropped, and a hand-edited apiKeyEnv survives refreshes (tracked via
source.envKey). kimi provider add --api-key and the TUI registry token
field are now optional so public registries import without a key.
* docs: document api_key_env provider credentials
* fix(oauth): preserve hand-edited apiKeyEnv on registry refresh and address review nits
* fix(oauth): validate apiKeyEnv conflicts during refresh and address review nits
* fix(oauth): mirror apiKeyEnv in the klient provider contract and harden refresh validation
* fix(oauth): stop auto-binding registry env names to provider credentials
* fix(oauth): preserve hand-edited apiKeyEnv across registry re-imports
* fix(oauth): scope apiKeyEnv preservation to the owning registry
* fix(agent-core-v2): preserve apiKeyEnv across models.dev catalog re-imports
* fix(kap-server): drop apiKeyEnv when the provider update route receives a replacement key
* fix(agent-core-v2): replace provider credential fields on TOML writeback
* fix(kap-server): normalize whitespace-only api_key in the provider update route
* refactor(oauth): unify provider credential rules into shared primitives
A new provider-credential module in the oauth package owns the credential
precedence chain, the mutual-exclusion conflicts, the user-facing messages,
and the write-side tri-state reconciliation; the refresh orchestrator,
agent-core-v2 (model-auth, catalog, auth readiness), and the kap-server
provider routes compose it instead of re-deriving the rules per layer.
applyOpenPlatformConfig now takes credential: {apiKey}|{apiKeyEnv} and
merges onto the existing record, so open-platform refreshes no longer drop
hand-written fields (customHeaders, the env sub-table) on the inline-key
path either, and the refresh's write-then-undo block is gone.
Registry imports converge on removeCustomRegistryEntries +
applyCustomRegistryEntries (prior-snapshot provenance): the exported
capture/restore helpers are gone, the CLI persists a re-import with a
single setConfig instead of per-id removeProvider RPCs, and
default_model/default_provider survive a re-import whenever they still
resolve. kap-server's importCustomRegistry keeps the purge-then-rewrite
two-phase replace the config service's raw-merging writeback requires.
kap-server create/replace routes accept api_key_env (tri-state, mutually
exclusive with api_key), and GET /config plus the provider catalog item
expose the binding. A declared-but-unset api_key_env now fails readiness
checks with config.invalid (was auth.token_missing), matching request-time
behavior. Registry 401/403 without a key prints an authentication hint.
* fix(cli): persist registry re-import removals on both harness generations
The v2 harness's setConfig deep-merges per domain (retaining omitted keys
and skipping undefined defaults), so a shrunk re-import left removed
providers and aliases in config.toml and kept a cleared default dangling.
The CLI and TUI registry flows now share persistRegistryImport: on v2 the
removal phase and the rebuilt records go out as two atomic
replaceConfigSections writes (the writeback merges alias fields from raw
TOML, so the purge must be persisted first); on v1 the removeProvider RPC
cascades the removals before one merge write. defaultModel/defaultProvider
snapshots are captured before any removal so the restore works on both
paths.
* fix(oauth): reject oauth before the legacy env fallback and persist cleared thinking on re-import
refreshProviderModels' env sub-table fallback now throws the same
apiKey+oauth conflict the chat path rejects, instead of authenticating
with the fallback key and rewriting the provider to drop oauth.
persistRegistryImport writes the cleared thinking section through the
replacement payload, and drops the v1 fallback branch — registry imports
always persist through replaceConfigSections, so the removal phase no
longer needs per-id removeProvider RPCs.
* chore: consolidate provider credential changesets into one entry
* chore: trim changeset to the api_key_env entry
* fix: harden provider credential status and registry imports
* fix(oauth): expose browser-safe provider credential rules
* fix(node-sdk): replace serialized provider credentials
* fix(oauth): reject credential conflicts during refresh
* fix: harden custom registry re-imports
Reject reserved provider collisions and credential endpoint drift before mutation. Replace registry-owned sections atomically with compare-and-set guards, and carry the replacement contract through klient and node-sdk.
* refactor(registry): centralize custom registry import ownership
Route CLI and TUI imports through the engine via the typed SDK and Klient operation. Keep registry replacement policy inside the engine and preserve credential edits made while fetching the registry.
Cover the shared entry point with real configuration storage and memory/IPC transport tests.
* test: trim redundant api_key_env and registry-import coverage
* fix(cli): reuse the stored registry key on keyless re-import
Both first-party clients encoded "no key given" as an empty string, but
the engine's fallback chain (options.apiKey ?? registryKeyFromExisting)
only reuses the key parked in the provider's source blob when the option
is undefined. A keyless re-import of a private registry therefore went
out without an Authorization header and failed with 401 from the CLI
and the TUI dialog, while the kap-server REST route (which passes
undefined) reused the stored key fine.
Pass undefined from the CLI handler and the TUI import dialog instead
of normalizing to ''. The dialog's result type now marks apiKey
optional, matching the engine contract.
* test: pin CAS rejection and credential-conflict behavior
The coverage trim went past redundancy in three places, leaving the
PR's headline guards untested. Restore the compare-and-set rejection
path (guarded value changed on disk -> retry error, no write) and the
absent-section guard normalization in config.test.ts, the
apiKey+apiKeyEnv / apiKeyEnv+oauth conflict rejections at runtime
resolution (modelAuth) and at the refresh orchestrator (discovery,
with per-provider failure isolation), and the open-platform refresh
through api_key_env proving the resolved secret never lands in the
provider record. Also add the first direct unit tests for the shared
declaredProviderCredential / reconcileProviderCredentialUpdate
primitives.
* refactor(oauth): share reserved provider id rules between import and refresh
* Delete .changeset/registry-import-concurrent-credentials.md
Signed-off-by: 7Sageer <sag77r@hotmail.com>
* test: trim cross-layer duplicate registry and credential coverage
---------
Signed-off-by: 7Sageer <sag77r@hotmail.com>
* fix(agent-core-v2): mark MCP servers needs-auth when a tool call returns 401
* fix(agent-core-v2): cover retried MCP calls with unauthorized handling and recheck entry after close
* fix(agent-core-v2): bind MCP unauthorized reports to the originating client
* fix(agent-core-v2): invalidate rejected MCP OAuth tokens when entering needs-auth
* fix(agent-core-v2): preserve concurrent MCP OAuth grants and recheck entry after credential awaits
* fix(agent-core-v2): flip MCP servers to needs-auth before invalidating rejected tokens
* fix(agent-core-v2): compare-and-clear rejected MCP OAuth tokens and tolerate cleanup failures
* refactor(agent-core-v2): move rejected-grant handling into the MCP OAuth service
* fix(agent-core-v2): compare MCP OAuth grant age with the scheduler clock
* fix(agent-core-v2): reject future timestamps from the concurrent MCP grant window
* fix(agent-core-v2): distinguish concurrent MCP grants from freshly rejected ones
* fix(agent-core-v2): ignore application-level MCP errors in the unauthorized sniff
* test(agent-core-v2): trim MCP unauthorized tests to the core contract
* Delete .changeset/mcp-oauth-clear-if-current.md
Signed-off-by: 7Sageer <sag77r@hotmail.com>
---------
Signed-off-by: 7Sageer <sag77r@hotmail.com>
* feat(cli): add /desktop slash command and kimi install-app subcommand
Both entry points print the Kimi Code desktop app page
(https://www.kimi.com/code) and open it in the default browser:
the kimi install-app subcommand from the shell, and /desktop
(alias /install-desktop) from the TUI.
* fix(cli): derive the desktop app page from the active region
Replace the hardcoded .com URL with the existing region-aware
kimiCodeOfficialInstallUrl() resolved at invocation time, so
global-region users land on the .ai site and region changes are
respected.
* docs(cli): describe the desktop app page URL as region-dependent
* feat(agent-core-v2): add skill scopes and mark custom-theme as tui-only
Builtin skills can now declare a scopes whitelist (tui | web); skills
without scopes stay visible everywhere. The value is carried through
SkillSummary, the klient RPC contract, and the kap-server REST wire,
and the TUI slash commands filter by it. HTTP clients (code-app) drop
skills whose scopes exclude their own UI mode, so TUI-only skills like
/custom-theme no longer leak into app/web.
* fix: filter scoped skills from the acp and vscode palettes
ACP slash commands and the VS Code GetSlashCommands handler also
project SkillSummary lists; both now drop skills that carry a scopes
restriction so tui-scoped skills stay out of non-tui clients.
* fix(agent-core-v2): watch Windows 8.3 short paths through their long form
Watching a directory given in 8.3 short form (for example
C:\Users\RUNNER~1\...) makes libuv's Windows file watcher abort the process
with "Assertion failed: !_wcsnicmp(filename, dir, dirlen)" in fs-event.c,
because change notifications arrive under the long path.
On Windows, resolve a watch root containing a short-name segment to its long
form (the nearest existing ancestor when the root does not exist yet) and map
reported changes and ignore checks back to the requested path, so callers that
compare change paths with their own keep working.
* test(agent-core-v2): keep watch tests portable to Windows runners
* test(agent-core-v2): show received events when the Windows short path watch test fails
* test(agent-core-v2): derive the Windows short path without cmd quoting
* chore: add changeset for the Windows short path watch fix
* fix(agent-core-v2): keep watch children whose names start with two dots inside the root
relative() returns "..cache/x" for a child named "..cache", which the
startsWith('..') check treated as a path outside the watched root. Mapped
change paths then kept the resolved long form, and native signal events for
such children skipped the ignore predicate. Only a bare ".." segment, ".."
followed by a separator, or an absolute result is outside the root.
Store the optional metadata object on prompt submission and skill activation as opaque client metadata on the user or skill-activation origin, carry it through prompt events, the transcript projector, snapshots and history rebuilds, and echo it on prompt items without adding it to model content. Use display_text entries for session titles, undo labels and fork titles when every entry provides one. Project user turn payloads, live and rebuilt, through a path-free projection.
Accept skill_activation origins with a user-slash trigger in onTurnSteered so a slash command sent while a turn is running gets a transcript frame, and keep those frames from draining a queued prompt's id. Project origin file attachments onto steered frames on both the immediate and deferred paths, matching onTurnStarted and the history rebuild.
Widen activateSkillAttachmentSchema so the attachments array accepts text parts alongside image, video and file, letting a caller interleave text and media in the order the model should read them. Schema only; the route already resolves attachments as WireContent.
Media name preservation already ships: the REST schemas expose a name on
image and video content, and kap-server reads imageUrl.name and videoUrl.name
when projecting messages and prompt content. The kosong part types never
declared the field, so it survived only because object literals keep excess
properties through an assignment. Consumers that read these types directly
cannot see the name at all.
Declare it where the other optional part fields live. No runtime change; the
existing media name projection tests continue to cover the behavior.
* feat(agent-core-v2): upload images as file references for Kimi models
* fix(agent-core-v2): scope media upload cache by endpoint and recognize SDK auth error status
* fix(agent-core-v2): restore the stripped /v1 for kimi files uploads on the anthropic route
* feat(agent-core-v2): omit oldest media when accumulated inline media exceed the request budget
* fix(agent-core-v2): key the media upload cache by the stable JWT subject
* fix(agent-core-v2): harden media request recovery
* fix(agent-core-v2): correct image memo accounting
* feat(agent-core-v2): include saved paths in dropped media placeholders
* docs: drop the saved-path re-read guarantee from the media budget note
The guarantee does not hold for inline data-URL media (no persisted
path exists), and the runtime warning already conveys path availability
when it applies. Addresses the codex P2 review suggestion by removal.
* fix(tui): stop /usage and /status from prompting login before the first message
* docs(changeset): simplify the /usage fix entry
* fix(tui): mute the session-usage error line and cover the session-less path in tests
* docs(changeset): restore the confirmed wording
* perf(tui): batch and cache agent swarm progress rendering
* perf(agent-core-v2): evict completed subagent scopes behind an LRU cache
* fix(agent-core-v2): stop the agent actor in the session machine when its scope is removed
* fix(agent-core-v2): emit a terminal event for the final rate-limited run and defer eviction while background tasks run
* fix(agent-core-v2): include in-flight retries when abandoning suspended agents
* fix(agent-core-v2): roll back the closing flag when scope removal fails and wait per the configured eviction timeout
* fix(agent-core-v2): terminalize subagents spawned after batch cancellation and report task timeouts as failures
* chore: restore upstream/main state of files mangled by lint-staged autofix in the merge commit
oxlint --fix rewrites (prefer-set-has, catch-error-name, prefer toSorted/.at(-1)/toReversed)
touched seven files the branch never modified; one of them (oauth managed-kimi-code.ts)
no longer typechecks. Restore all seven to their exact upstream/main state. Committed
with --no-verify so the same autofix does not re-mangle them.
* fix(agent-core-v2): retry deferred scope evictions without exhausting the attempt budget, dedupe terminal events for starting retries, and patch-level the changeset
* fix(agent-core-v2): finish scope teardown instead of reactivating a partially torn-down scope
* fix(agent-core-v2): terminalize subagent-start hook cancellations and re-acquire resume targets after metadata reads
* fix(tui): record cancelled background agents in the transcript
* fix(agent-core-v2): route mirror terminal events through the batch exactly-once guard
* fix(tui): guard terminal swarm members from re-cancellation and deliver terminal status to cancelled foreground subagent cards
* fix(agent-core-v2): classify eviction as removed when teardown already closed the scope
* Merge remote-tracking branch 'upstream/main' into perf/subagent-scope-eviction
Skip lint-staged for this merge: its oxlint --fix pass rewrites unrelated files
(prefer-set-has etc. mangled seven files in the previous merge commit), while the
merge content is upstream's lint-clean code plus conflict resolutions already
verified by oxlint.
* chore: restore upstream/main changes dropped by a stash roundtrip during the merge
A git stash/pop cycle used to isolate an unrelated flaky test reverted 82 files of the
merge result to the pre-merge branch state, so commit 375ff2a4a missed them. The
working tree already held the correct merged content; this commit restores it.
* fix(agent-core-v2): propagate the cancellation reason from turn abort to tool signals
Cancelling a turn aborted the turn's llm scope and every tool scope
without a reason, so tools and subagents only ever saw the default
AbortError and could not tell a user cancellation from a system abort.
With the swarm terminal-event classification this surfaced as
`subagent.failed` ("This operation was aborted") on Ctrl+C instead of
`subagent.cancelled`.
Carry the reason end to end: `MachineEngine.abort(reason)` sends it on
`input.abort`, the agent machine remembers it for the turn and forwards
it to `turn.abort` and to every tool scope it aborts, the turn machine
passes it to the llm scope, and the loop service hands its cancellation
(a `UserCancellationError` by default) to the engine.
* fix(agent-core-v2): stop rebuilding an evicted subagent after its caller aborts
`createAgentAwaitingClose` polled for the previous scope to finish
closing without ever looking at the caller's abort signal, so cancelling
an `Agent` or `AgentSwarm` resume during that wait still recreated the
scope afterwards. In the `Agent` path the run then rejected before any
terminal event was mirrored, leaving an idle scope that the scope cache
no longer tracked.
Thread the caller's signal into the helper and check it before every
attempt; the Agent tool passes its call controller and the swarm service
passes the attempt signal. Also trim the eviction changeset to the one
sentence the changeset rules ask for.
* fix(agent-core-v2): report task-manager aborts of a subagent as cancelled
When the task manager times out or stops a background subagent it aborts
the run with a plain reason ('Timed out', the TaskStop reason), and the
run rejects with that raw reason. The task manager therefore classifies
the task as timed_out or killed, but the mirror only recognised
`UserCancellationError` as a cancellation and emitted `subagent.failed`,
so the transcript recorded a failure before the authoritative
`background.task.terminated` event could land.
Classify a terminated run by the shape of its abort reason: a real
Error that is not an AbortError (the swarm's own per-item timeout) stays
a failure, everything else that aborted the run is a cancellation. The
swarm's start-failure path uses the same rule.
* fix(agent-core-v2): flush the wire before evicting a subagent scope and re-pick LRU candidates
Two gaps in the scope-cache eviction pass:
- `AgentLifecycleService.remove()` swallows a failed wire flush and still
tears the scope down, so an eviction that hit a disk-full or transient
I/O error would drop the in-memory scope and let a later resume rebuild
from a stale wire. Flush the agent's wire explicitly before removing;
if that fails, keep the scope resident and retry on a later pass. The
idle and task checks are repeated after the flush so a resume that
started meanwhile is not cancelled by the removal.
- The pass snapshotted its candidates up front and re-validated them by
attempt count only, so a scope that was resumed and completed again
while an earlier eviction was awaited kept matching with attempts 0 and
was evicted despite now being the most recently used. Pick the oldest
eligible entry from the live map on every step instead, skipping only
the entries this pass already tried.
---------
Co-authored-by: Kaiyi <me@kaiyi.cool>
* fix(plugins): rename WebBridge display name to Kimi Browser Extension
The product has renamed WebBridge to Kimi Browser Extension in work and
Chrome. Update the user-facing display name across the built-in
capability entry, TUI plugins selector, managed plugin manifest,
marketplace catalog, skill docs, and user docs to match. Internal ids,
binary names, CDN paths, and daemon references are unchanged.
* fix(plugins): bump webbridge to 1.11.4 and keep legacy docs anchor
Address review: point the changeset at the scoped CLI package, bump the
renamed plugin's manifest/catalog/docs version so installed users are
offered the renamed package as an update, and preserve the published
#kimi-webbridge documentation anchor beside the renamed heading.
* docs(plugins): match only the known extension-update error messages
Broadening the version-mismatch trigger to the generic prefix 'Please
update the' would misclassify unrelated tool errors as an outdated
browser extension; match the two known full messages instead.
---------
Co-authored-by: kimi-agent-bot <kimi-agent-bot@users.noreply.github.com>
The platform /usages payload now serves per-window used ratios
(goods_version, a usages map of limit5h/limit7d/monthTotal/monthCode
entries with used_ratio/reset_time, and boosterWallet) instead of
absolute used/limit rows.
- oauth: parse and fetch the quota model; keep the platform helpers
- toolkit / agent-core-v2 / node-sdk: getManagedUsage returns the quota
- kap-server: /oauth/usage passes the camelCase quota shape through
(the legacy snake_case wire is gone)
- TUI /usage: plan rows are presence-driven from the quota payload;
the monthly row carries a kimi/code breakdown line
- docs: server-api reference updated for the new response shape
* docs(web): add common slash commands table and rename startup options section
* docs(interaction): fold the goal mode guide into the interaction page and remove the orphaned page
* feat(agent-core-v2): fail fast on unsupported [secondary_model].default_effort
* fix(agent-core-v2): reject default_effort off for always-thinking pool models
* fix(agent-core-v2): surface adaptive_thinking on the assembled catalog model
* perf(tui): batch and cache agent swarm progress rendering
* perf(tui): honor the render-cache escape hatch for cell memoization
* fix(tui): stop swarm frame ticks after tool call end and cap retained label code units
* fix(tui): house swarm label caps in TUI constants and stop splitting surrogate pairs
* fix(tui): close OSC 8 hyperlinks sliced by the swarm label storage cap
* fix(tui): reset SGR styling sliced by the swarm label storage cap
* fix(tui): back the swarm label storage cap off to grapheme cluster boundaries
* test(tui): verify swarm terminal-state retention through the render seam
* perf(agent-core-v2): shard the session event bus by agent id
* fix(agent-core-v2): preserve full-stream-before-typed ordering in agent channels and render perAgent counts in the inspector
* fix(agent-core-v2): deliver agent full streams before session typed handlers and dispose sharded channels with the bus
* fix(agent-core-v2): prevent rebuilding agent channels after the event bus is disposed
* fix(agent-core-v2): isolate agent channels on generation replace and keep typed handlers on the shared per-type channel
* fix(agent-core-v2): require the active lifecycle context before firing an agent full-stream channel
* fix(agent-core-v2): reject stale lifecycle contexts when subscribing to an agent full-stream channel
* fix(agent-core-v2): honor the agent context in typed subscribeAgent subscriptions
* fix(agent-core-v2): release disposed agent scopes from the DI dependency graph
* fix(agent-core-v2): drop retired service instances from the tracked edge nodes and the dependency graph
* fix(agent-core-v2): resolve tower roster identities to the latest registration
Agent ids restart from agent-0 in each session, so a freshly spawned
worker collided with the previous session's roster entry and every
find-first lookup (caller identity, write guard, death bookkeeping)
resolved to the stale entry: workers read the wrong inbox, sent under
the wrong name, and were denied their own writes, mission updates, and
reviews.
- resolveAgent picks the latest roster entry for an agent id; all
callers (resolveCallerName, write guard, resume veto, died/revived)
go through it
- registerAgent retires same-agent-id entries before appending
- enter() (/tower on) now adopts the workspace roster itself instead of
relying on a manual TowerInit
* fix(agent-core-v2): adopt the tower roster when a replayed tower mode finds a dead foreign owner
A forked or restored session replays the durable TowerModeEnter into an
active tower mode without ever calling enter(), so the roster adoption
added there never ran on the restore path: with the previous owner
session gone, the fork kept the mode (and the stale roster) until a
manual TowerInit. The dead-owner branch of the restore-time
reconciliation now adopts the workspace, mirroring enter()'s takeover
of an idle owner; a live foreign owner still causes an exit.
* fix(agent-core-v2): await the outgoing tower owner's release before adopting its roster
Taking tower mode over from a live but idle owner called the owner's
synchronous exit(), whose ownership release ran fire-and-forget, and
immediately adopted the roster — two concurrent load-modify-save cycles
on .tower/comms/state.json that could lose the new owner or resurrect
the stale roster. exit() now awaits the ownership release and the
takeover awaits the owner's exit(); the remaining callers either await
(RPC/HTTP tower-mode-off) or explicitly ignore the promise (mode mutex,
restore-time self-exit).
* fix(agent-core-v2): fail tower activation when the roster adoption cannot complete
Adoption failures (unreadable or unpersistable state.json) were
swallowed into a log warning, so enter() and the stale-owner restore
both activated tower mode with the foreign owner and stale roster still
in place — the exact identity mismatch the adoption is meant to
prevent, now with a false success report. adoptTowerRoster() now throws
a TowerProtocolError with the underlying cause: enter() propagates it
before any tool is activated, and the restore path drops the replayed
tower mode instead (it must not throw inside the restore hook chain).
* fix(agent-core-v2): propagate unreadable tower state during roster adoption
adopt() treated any state.json read failure as an uninitialized
workspace via isInitialized()'s catch-all, so an existing but
unreadable state file (EACCES, EISDIR, transient I/O) reported a
successful no-op adoption and enter() activated tower mode against the
foreign state — bypassing the activation guard added in the previous
commit. Only ENOENT now means uninitialized; every other read error
propagates into the fail-activation / deactivate-on-restore paths.
---------
Co-authored-by: konghuanjun <konghuanjun@moonshot.ai>
* docs(skills): clarify flat-form skill naming and its limits
The flat-form description "Skill name is taken from the filename (minus
.md)" read as if the on-disk file should drop the extension, which
repeatedly confused readers. Rewrite the Creating-a-Skill section with
a concrete directory layout, explicit name-derivation rules for both
forms, the directory-over-flat precedence, and the flat form's top-level
scanning and KIMI_SKILL_DIR limitations.
* docs(skills): attribute directory-form names to the required frontmatter
parseSkillText requires name in directory-form SKILL.md and always
registers under it, so the subdirectory name is only a convention, not
the source of the Skill name. Reword the derivation bullets accordingly
(flat form keeps its filename fallback).
---------
Co-authored-by: kimi-agent-bot <kimi-agent-bot@users.noreply.github.com>
@ -5,7 +5,7 @@ description: Use when generating changesets in the kimi-code repository — deci
# Generate Changesets
The only user-facing published package is the CLI: `@moonshot-ai/kimi-code`. All other `@moonshot-ai/*` packages (sdk, kosong, kaos, oauth, telemetry, and so on) are internal.
The only user-facing published package is the CLI: `@moonshot-ai/kimi-code`. All other `@moonshot-ai/*` packages (sdk, kosong, oauth, telemetry, and so on) are internal.
## 1. Whether to Write
@ -32,6 +32,7 @@ Fix occasional loss of tool call results in long conversations.
Wording:
- One short, user-facing English sentence that states only what changed. Drop trailing clauses that explain the cause, the benefit, or the mechanism.
- When a default flips or an existing behavior is removed, name the behavior users lose, not only the new default. State the escape hatch if one exists, e.g. `Stop hot-reloading config, skills, and AGENTS.md by default; set [watch] enabled = true or KIMI_CODE_WATCH=1 to turn it back on.`; if there is none, say plainly that the old behavior is no longer available.
- New features: say plainly what it is plus one line on how to use it, e.g. `Add the /foo slash command to list active sessions. Run /foo to see them.`
- Experimental features: also state how to enable them (the flag, config key, or env var).
- No file, class, or function names, and no PR numbers. No vague words like refactor, optimize, or improve. No real internal identifiers — use neutral placeholders such as `example.com` or `YOUR_API_KEY`.
@ -60,4 +61,4 @@ Wording:
4. **Show the changeset text to whoever requested the work and get their confirmation before committing.**
5. Do not guess at changes you do not understand: finish the parts that are clear, then list what is unclear and ask whether you may dig into the code.
Before a release, review the accumulated `.changeset/` entries and delete the non-user-facing ones — the release PR regenerates from `.changeset/` on main, so deleting a file removes its changelog entry without touching shipped code.
Before a release, review the accumulated `.changeset/` entries and delete the non-user-facing ones — the release PR regenerates from `.changeset/` on the selected release branch, so deleting a file removes its changelog entry without touching shipped code.
Select the PR targeting the intended `release/*` branch, not the legacy main version PR. Report its latest head CI link and result alongside the preview.
```bash
gh pr list --state open --search "ci: release packages in:title" \
@ -126,7 +126,12 @@ Public-text rule: do not copy real internal endpoints, key names, account names,
### 4. Merge, Deduplicate, And Classify Entries
Before classifying, merge related entries and drop redundant ones from the user-facing changelog:
First, count the stripped upstream entries in the version range being synced. The count decides how much curation happens before the human review checkpoint:
- **10 entries or fewer: list everything and let the user decide.** Classify every entry into its section and list them all — do not fold, merge, or drop entries on your own, and do not write a catch-all line. At the human review checkpoint (step 8), present the full list with advisory notes (which entries look low-signal under the two gates below, and why) so the user decides what stays, what merges, and what folds.
- **More than 10 entries: curate first, then review.** Merge related entries and fold low-signal ones into the catch-all line before writing the page, using the rules below. The reviewer still owns the final cutoff — surface the borderline calls instead of resolving them silently.
The rules below are the shared curation standard: they drive the curator's fold/merge decisions when there are more than 10 entries, and they drive the advisory notes when there are 10 or fewer.
- **Curate for end users: collapse low-signal entries into one catch-all line.** The docs changelog is the only curated, user-facing outlet; the full entry list always remains in the upstream package changelog, so hiding detail here loses nothing. Apply two gates to every candidate entry. Gate 1, the reader-action test: **after reading this, is there something the reader must do, or something they must re-evaluate?** Gate 2, the channel test: **is the changelog the only channel that can deliver this?** The changelog is the channel of last resort — when the product itself surfaces the information in context, at the moment of need, to exactly the affected users, the entry is redundant no matter how real the improvement is. "Surfaced" means pushed into the user's path, not merely present on screen: an event-triggered card, prompt, or post-install screen forces the encounter, while a toggle, menu item, command, or settings page only waits to be found. Users do not explore — a capability that lives only in ambient UI is effectively undiscoverable, so the changelog must announce it. What in-product surfacing cannot deliver: hidden controls (env vars, config keys, opt-out flags nobody would find unprompted), invalidations of existing habits or expectations (in-product discovery comes as confusion), and capabilities users would not know to seek. An entry that fails either gate folds. Anchor both gates to the changelog's reader, never to the bug's victim: someone who hit a loud failure does not need the changelog to confirm the fix — the product working again is the notification — and a reader who never hit it gets nothing from the entry.
- `Features`: keep when users would try it or must react to it — new capabilities create demand readers did not know to seek. Collapse only behavior that takes effect solely behind an experimental flag.
@ -324,7 +329,7 @@ Check:
- Each version has the same section set and order on both pages.
- Each section has the same number of entries on both pages.
- Within each section, the most valuable, obvious, and larger entries appear before smaller or narrower entries.
- Low-signal entries were collapsed into the single catch-all line, placed last under `Bug Fixes` — or under `Polish` when nothing folded is a fix (both the reader-action test and the channel test applied); the catch-all wording matches what was folded and never claims fixes that did not happen; section sizes stay within the density defaults (about 2 Polish, 3 Bug Fixes) unless extra qualifying entries were deliberately kept and flagged for review. The catch-all line ends with the upstream changelog pointer (file link, no version anchor).
- Entry count decided the curation level: a version range with 10 or fewer upstream entries lists everything classified, with no catch-all line (fold/merge decisions deferred to the review checkpoint); with more than 10, low-signal entries were collapsed into the single catch-all line, placed last under `Bug Fixes` — or under `Polish` when nothing folded is a fix (both the reader-action test and the channel test applied); the catch-all wording matches what was folded and never claims fixes that did not happen; section sizes stay within the density defaults (about 2 Polish, 3 Bug Fixes) unless extra qualifying entries were deliberately kept and flagged for review. The catch-all line ends with the upstream changelog pointer (file link, no version anchor).
- PR links and commit hashes were stripped.
- No `Thanks ...!` credit remains (remove it every time).
- Real internal identifiers were replaced with neutral placeholders.
2. Summarize synced versions, section counts, and anything that needed manual classification. List every entry folded into a catch-all line (short titles, one line each), any section that exceeds the density defaults, and every borderline call flagged during curation — the reviewer cannot own a cutoff they cannot see.
2. Summarize synced versions, section counts, and anything that needed manual classification. When the synced version range had 10 or fewer upstream entries and everything was listed, present the full entry list with advisory notes (which entries look low-signal and why) and ask the user to decide what stays, what merges, and what folds. When entries were folded into a catch-all line (more than 10 upstream entries), list every folded entry (short titles, one line each), any section that exceeds the density defaults, and every borderline call flagged during curation — the reviewer cannot own a cutoff they cannot see.
3. Tell the user to reply when they are done reviewing, or to ask for edits.
4. Do **not** commit, push, or open a PR until the user explicitly says review is complete, or asks to proceed.
@ -434,6 +439,7 @@ Return the PR URL to the user when done.
- Never edit upstream `apps/kimi-code/CHANGELOG.md`.
- Do not backfill unreleased `.changeset/*.md` drafts into the docs site.
- If upstream wording is wrong, leave upstream alone and fix it in a future changeset.
- A version range with 10 or fewer upstream entries is listed in full; fold/merge/drop decisions belong to the user at the review checkpoint, not the curator.
- Always sync on a `docs/changelog-sync-*` branch and open a PR; never push changelog docs sync directly to `main`.
- Wait for the human review checkpoint before committing, pushing, or opening a PR.
@ -446,7 +452,8 @@ Return the PR URL to the user when done.
| Leaving the `Thanks ...!` credit in docs | Remove it every time, including the multi-author form |
| Leaving near-duplicate micro-tweaks as separate bullets | Merge small tweaks to the same surface into one higher-level entry (e.g. composer height + font → composer's default styling) |
| Listing many narrow fixes to the same surface as separate bullets | When three or more fixes target the same UI area or the same class of problem, merge them into one higher-level fix entry; keep genuinely distinct or high-value fixes standalone |
| Listing low-signal fixes or internal changes as standalone bullets | Collapse them into the single catch-all line (`Fix several known issues.`) placed last under Bug Fixes; treat the section-size defaults (about 2 Polish, 3 Bug Fixes) as a density guard, not a quota |
| Listing low-signal fixes or internal changes as standalone bullets when curating (>10 entries) | Collapse them into the single catch-all line (`Fix several known issues.`) placed last under Bug Fixes; treat the section-size defaults (about 2 Polish, 3 Bug Fixes) as a density guard, not a quota |
| Folding, merging, or dropping entries on your own when the version range has 10 or fewer upstream entries | List everything classified, with no catch-all line, and let the user decide at the review checkpoint |
| Folding a qualifying entry just to hit the section-size default | The defaults are density guards; keep entries that genuinely pass the reader-action test and flag the overflow for the human reviewer |
| Keeping a fix because it was severe or hard-won | Severity makes the announcement redundant — the fix itself notifies whoever was hit; keep only behavior-change fixes and retrospective notices with a concrete, locatable action |
| Keeping an improvement the product surfaces in context (recovery cards, post-install guidance, progress displays) | The product is the better channel — right users, moment of need; fold it (channel test) |
description: Test-driven development. Use when the user wants to build features or fix bugs test-first, mentions "red-green-refactor", or wants integration tests.
---
# Test-Driven Development
TDD is the red → green loop. This skill is the reference that makes that loop produce tests worth keeping: what a good test is, where tests go, the anti-patterns, and the rules of the loop. Every section applies on every cycle: consult them before and during the loop, not after.
When exploring the codebase, read `CONTEXT.md` (if it exists) so test names and interface vocabulary match the project's domain language, and respect ADRs in the area you're touching.
## What a good test is
Tests verify behavior through public interfaces, not implementation details. Code can change entirely; tests shouldn't. A good test reads like a specification: "user can checkout with valid cart" tells you exactly what capability exists, and it survives refactors because it doesn't care about internal structure.
See [tests.md](tests.md) for examples and [mocking.md](mocking.md) for mocking guidelines.
## Seams: where tests go
A **seam** is the public boundary you test at: the interface where you observe behavior without reaching inside. Tests live at seams, never against internals.
**Test only at pre-agreed seams.** Before writing any test, write down the seams under test and confirm them with the user. No test is written at an unconfirmed seam. You can't test everything, so agreeing the seams up front is how testing effort lands on the critical paths and complex logic instead of every edge case.
Ask: "What's the public interface, and which seams should we test?"
When the shape of that interface is itself in question (how deep the module is, where the seam belongs, what the interface should expose), call the Skill tool with "codebase-design" for the vocabulary. It is the shared source of the module, interface, depth, seam, adapter, leverage and locality terms, and it is a reference to consult, not a session to run.
## Anti-patterns
- **Implementation-coupled**: mocks internal collaborators, tests private methods, or verifies through a side channel (querying the database instead of using the interface). The tell: the test breaks when you refactor but behavior hasn't changed.
- **Tautological**: the assertion recomputes the expected value the way the code does (`expect(add(a, b)).toBe(a + b)`, a snapshot derived by hand the same way, a constant asserted equal to itself), so it passes by construction and can never disagree with the code. Expected values must come from an independent source of truth: a known-good literal, a worked example, the spec.
- **Horizontal slicing**: writing all tests first, then all implementation. Bulk tests verify _imagined_ behavior: you test the _shape_ of things rather than user-facing behavior, the tests go insensitive to real changes, and you commit to test structure before understanding the implementation. Work in **vertical slices** instead: one test → one implementation → repeat, each test a **tracer bullet** that responds to what the last cycle taught you.
## Rules of the loop
- **Red before green.** Write the failing test first, then only enough code to pass it. Don't anticipate future tests or add speculative features.
- **One slice at a time.** One seam, one test, one minimal implementation per cycle.
- **Refactoring is not part of the loop.** It belongs to the review stage (see the `code-review` skill), not the red → green implementation cycle.
| Behavior | Before | After | Who relies on the old behavior | Escape hatch |
|---|---|---|---|---|
| reasoning parsing when `reasoning_details` is an array | string `reasoning` also read | string ignored | OpenAI-compatible gateways that send both (OpenRouter dialect) | none |
- `src/tui/commands/` — slash-command declaration, parsing, ordering, and dynamic skill-command generation. Parsing and types only; execution is dispatched from `KimiTUI`'s slash-command handler section, and complex execution sinks into `utils` or focused components.
All other workspace packages are private internal packages, are not published to npm, and are excluded via `ignore` in `.changeset/config.json`:
- `@moonshot-ai/kaos`
- `@moonshot-ai/kimi-code-oauth`
- `@moonshot-ai/kimi-telemetry`
- `@moonshot-ai/kosong`
@ -100,23 +99,27 @@ Commit messages must follow Conventional Commit style. Do not include any author
### 4. CI generates the release PR
Once the changeset file is merged into `main`, `.github/workflows/release.yml` uses `changesets/action@v1` to create or update a release PR.
Changesets accumulate on `main`. Create `release/<cycle>` from the selected commit; pushes to that branch use `changesets/action@v1` to create or update its release PR. Manual **Release** runs on a release branch only prepare the PR. The workflow explicitly dispatches CI for the generated version branch.
Once the release PR is merged into `main`, the same workflow runs:
Review the version PR, latest CI and CLI/extension pre-changelog together. Once the matching version PR is merged into `release/<cycle>`, publication waits for that exact commit’s push CI. Ordinary pushes do not publish. The existing publication steps then run:
- `pnpm install --frozen-lockfile`
- `pnpm build`
- `pnpm changeset publish`
The packages are then published via npm Trusted Publishing, and a GitHub Release is created.
The packages are then published via npm Trusted Publishing, and a GitHub Release is created. CLI native builds still depend on Changesets reporting a newly published CLI package; npm-succeeded/native-failed whole-run recovery is not changed by this migration.
VS Code uses the same approved release commit and CI gate, publishing only when the extension version changed in that push. It retains its existing build and marketplace checks. It no longer publishes on main pushes or through a standalone manual entry. The documentation workflow is dispatched on main after CLI artifacts are uploaded; its deployment result is tracked separately. CDN remains manual.
Migration: merge the main infrastructure PR first to disable the old publisher, then the release infrastructure PR. These PRs do not publish. Close the obsolete main version PR separately. After the actual release, synchronize versions, changelogs, lockfile and consumed changeset removals back to main, preserving later main work. Main synchronization does not publish npm or VS Code. Do not change Desktop's submodule pointer as part of this step.
## Manual Publishing (Not Recommended)
@ -141,7 +144,7 @@ The root-level `pnpm run publish` first runs typecheck, lint, sherif, test, buil
- Every PR that affects publishable-package behavior or public API should include a corresponding changeset.
- Changes under `plugins/` (the bundled official plugins such as `kimi-datasource`) do **not** need a changeset: each plugin carries its own version in `kimi.plugin.json` and `plugins/marketplace.json` and is distributed via the marketplace CDN, separately from the `@moonshot-ai/kimi-code` npm package.
- Changeset files must be committed to the repository — release PRs are only triggered after they're merged.
- Release PRs require human review and merge; they will not publish automatically.
- Release PRs require human review and approval. They do not publish before merge; merging the approved PR automatically publishes after its commit passes CI.
- Do not add release changesets for private internal packages; only select `@moonshot-ai/kimi-code` and `@moonshot-ai/kimi-code-sdk`.
- If a change in an underlying internal package alters user-visible behavior or public API of a publishable package, add a changeset to the affected publishable package. For example, when a bug fixed in `@moonshot-ai/kosong` resolves an issue CLI users encounter, add a changeset to `@moonshot-ai/kimi-code` describing the user-visible fix.
- `@moonshot-ai/kimi-code` is the official CLI package name; after a global install it provides the `kimi` command.
Add mouse selection to fullscreen approval, Settings, permission, and workspace trust dialogs, and fade the editor and waiting panels when they lose focus.
Update cross-tool imports for custom homes, instruction duplicate checks, project skills, and MCP settings; stop importing standalone `.claude/AGENTS.md`, project `.codex/AGENTS.md`, `.codex/CLAUDE.md`, and loose skill files, which can still be copied manually.
Add `/import-sessions` and `kimi import-sessions` to import Claude Code and Codex sessions from the last 15 or 30 days. Run either one and choose a time range.
Replace the server's WebSocket `event.approval.*` and `event.question.*` events with a single flat `interaction` frame, and replace `POST /sessions/{id}/approvals/{id}`, `POST /sessions/{id}/questions/{id}` and `POST /sessions/{id}/questions/{id}:dismiss` with `POST /sessions/{id}/interactions/{id}`; the old events and endpoints, along with `GET /sessions/{id}/approvals` and `GET /sessions/{id}/questions`, are removed.
Stop resending full-size images on every turn after the provider rejects them as too large or unsupported; later turns and compaction keep the reduced images until you switch models or the conversation history is rewritten.
Reconnect workspace MCP servers automatically with backoff when the initial connection fails or the connection drops, so their tools come back on their own; tune the interval with `[mcp] reconnect_initial_delay_ms` and `reconnect_max_delay_ms`.
Plan mode now starts only when you turn it on, and every plan waits for your review: the agent can no longer enter Plan mode by itself, Never Ask no longer approves plans automatically, and `kimi -p` skips `default_plan_mode` and cannot approve the plan of a session that is already in Plan mode.
Add `/provider refresh [providerId]` to re-fetch provider model lists at runtime; unknown `/provider` subcommands now show a usage hint instead of opening the manager.
Raise Tower's default agent concurrency limit from 16 to 256, support the existing `max_running_tasks` limit and per-spawn model selection, and reduce lock contention during inbox, review, and status processing.
Add keyboard and mouse navigation to the experimental Updates panel, with clearer focus styling and a reading position that stays put; enable it with `KIMI_CODE_EXPERIMENTAL_NOTIFY_USER=1`.
Fix the conversation view freezing at an old point after restarting the app when a turn had been interrupted; interrupted turns are now marked instead of shown as completed.
@ -6,24 +6,40 @@ External PRs are accepted for approved bug fixes only: link an issue that a main
See https://github.com/MoonshotAI/kimi-code/blob/main/CONTRIBUTING.md for more.
-->
## Related Issue
## Requirement or Bug
<!-- Link the issue this change came from. External PRs must link an issue approved by a maintainer (an `/approve` comment) — PRs without one may be closed. -->
<!-- If there's an issue, write Resolve #(issue_number).
If it's a requirement, describe it briefly in plain language (under 100 characters). -->
Resolve #(issue_number)
## Bug Reproduction Steps
## Problem
<!-- Required for bug PRs only; write N/A for feature PRs.
Prefer writing in the issue and linking here; if no issue exists, write directly. -->
<!-- What user need or limitation does this address? If the linked issue already covers this, write "See linked issue". -->
## Root Cause
## What changed
<!-- Required for bug PRs only. Explain the root cause.
State whether this is a fundamental fix or a workaround. -->
<!-- What did you implement, and why does this approach fit Kimi Code? -->
## Code Changes
<!-- Describe the code changes in plain, easy-to-understand language for the reviewer. -->
## Behavior Changes and Affected Users
<!-- List every observable behavior this PR changes, one row each, including behavior you
consider "unchanged" whose branch conditions moved, and prompt-text edits sentence by sentence.
| Behavior | Before | After | Who relies on the old behavior | Escape hatch |
Name affected users concretely (client, provider dialect, platform, config state, data written
by older versions, external scripts); see .agents/skills/review-pr/surfaces.md.
Write "None" with evidence if nothing observable changes.
Then list affected modules and the test coverage for each row. -->
## Checklist
- [ ] I have read the [CONTRIBUTING](https://github.com/MoonshotAI/kimi-code/blob/main/CONTRIBUTING.md) document.
- [ ] I have linked a related issue (external PRs: the issue must have a maintainer's `/approve`).
- [ ] I have linked a related issue (external PRs: issue must have a maintainer's `/approve`).
- [ ] I have added tests that prove my feature works.
- [ ] The behavior-change table above is complete, and every removed behavior or flipped default is named in the changeset and either has an escape hatch or was explicitly approved by a maintainer in this PR.
- [ ] Ran `gen-changesets` skill, or this PR needs no changeset.
- [ ] Ran `gen-docs` skill, or this PR needs no doc update.
@ -21,10 +21,10 @@ This is a TypeScript monorepo built for agent-assisted development. Keep the roo
- `packages/agent-core-v2`: the DI × Scope agent engine (the v2 port behind kap-server). Four `LifecycleScope` tiers — `App` / `Workspace` / `Session` / `Agent` (`app/scopes.ts`) — plus the L3 unit layer (`Service`/`Fiber` units, collection contribution points, the Feature seam in `src/features/`); there is no App-level session lifecycle facade — callers compose `ISessionIndex` → `IWorkspaceLifecycleService.handlerFor` → the handler.
- `packages/node-sdk`: the public TypeScript SDK and harness.
- `packages/kosong`: the LLM / provider abstraction layer.
- `packages/kaos`: the execution environment and file/process abstractions.
- `packages/oauth`: Kimi OAuth and managed auth utilities.
- `packages/transcript`: the isomorphic transcript rendering data layer — L1 agent-granular store, L2 idempotent operations, L3 `off/turn/block/delta` subscription granularity, L4 framework-free view registry, plus turn-cursor pagination. Pure TypeScript (browser-safe, no engine imports); the sole owner of the transcript contract types (`src/contract/`) and the op-batch sequencing contract.
- `packages/session-import`: standalone Node.js conversion of external sessions into a single user message that carries the conversation, full effective-history attachments, and image references. It has no engine or UI dependency; callers provide the destination session attachment directory and context budget.
- `packages/kap-server`: the Kimi Code server, backed by `@moonshot-ai/agent-core-v2`; exposes sessions over REST + WebSocket (`/api/v1` + `/api/v1/ws`), plus the `/api/v1/debug/*` reflection RPC surface (`--debug-endpoints`, loopback bind + bearer auth).
- `packages/remote-control`: the Kimi Remote Control tunnel client — registers this machine with the relay and forwards HTTP/WebSocket traffic to the local server, with a machine-wide single-instance lock; consumed by kap-server (the `/api/v1/remote-control` toggle) and by the CLI (`kimi web --remote-control`).
- `packages/klient`: the client SDK — a contract-driven facade over agent-core-v2 (`global.*` / `session(id).*` / `agent(id).*`, zod-validated); transport via subpath entry (`@moonshot-ai/klient/ipc|memory`, both return the same `Klient`); also hosts the e2e suites. See `packages/klient/AGENTS.md`.
@ -58,7 +58,6 @@ This is a TypeScript monorepo built for agent-assisted development. Keep the roo
- Internal methods with only a single parameter should not be turned into options objects just for stylistic uniformity.
- Split functions only along abstraction levels: each function reads as one level of narrative (Step-down Rule), and a wrapper that adds no new abstraction level — especially one with a single call site — is inlined instead of extracted.
- Except for a package's `index.ts`, other `index.ts` files should prefer `export * from './module';`.
- When writing or updating tests, follow the `tdd` skill (`.agents/skills/tdd/SKILL.md`).
- Do not add too many new test files. Prefer adding tests to the existing test file of the corresponding component or module.
- When a test fails because of a user modification, default to fixing the test first; do not change the implementation to satisfy an old test unless the implementation truly has a bug.
- Do not sacrifice code quality for external compatibility unless the user explicitly asks for it. Breaking changes go through changesets and a `major` bump, gated by the rule below.
@ -75,12 +74,17 @@ This is a TypeScript monorepo built for agent-assisted development. Keep the roo
- Project-map entries stay at 1–2 sentences; deep package docs live in the package's own `AGENTS.md`.
- Keep instruction updates focused and supported by code facts.
## Release Workflow
- `main` accumulates changesets; `release/*` maintains `ci: release packages` PRs. Merging the matching version PR authorizes publication after its merge-commit CI succeeds. Ordinary pushes do not publish.
- npm publishing and native artifacts retain the existing Changesets flow. CDN upload and routing remain separately approved operations. See `.changeset/README.md` for migration and main synchronization.
## Workflow Requirements
- Prefer `rg` / `rg --files` when reading code.
- When designing changes, follow existing boundaries and local patterns first.
- In public text and test data, replace real internal identifiers with neutral placeholders such as `example.com`, `example.test`, and `YOUR_API_KEY`. Before opening a PR, ask a read-only agent to audit the diff for context-specific internal identifiers.
- When creating a PR, the PR title must follow Conventional Commit style, e.g. `chore: remove legacy format commands`.
- When creating a PR, use the `write-pr` skill (`.agents/skills/write-pr/SKILL.md`) to write the PR description. The PR title must follow Conventional Commit style, e.g. `chore: remove legacy format commands`.
- When an AI agent opens or updates a PR, fill in `.github/pull_request_template.md` — link the related issue or explain the problem, then describe what changed. Do not leave placeholder text or submit a generic summary of the diff.
- Do not submit vague AI-generated PR text. The human author must understand the change well enough to explain the code, edge cases, and why the approach fits this repository.
- After finishing a task and before submitting a PR, you must run the `gen-changesets` skill (see `.agents/skills/gen-changesets/SKILL.md`) and generate a changeset under `.changeset/` according to its rules.
@ -91,3 +95,39 @@ This is a TypeScript monorepo built for agent-assisted development. Keep the roo
- Agent working notes or handoff/summary documents (e.g. `HANDOVER-*.md`, `HANDOFF-*.md`, `handoff.md`).
- Throwaway UI/UX prototypes or design mockups (e.g. `*-designs.html`, `*-mockup.html`, `*-demo(s).html`) at the repo root or under a `design/` folder. The only tracked `.html` files should be Vite `index.html` entrypoints.
Before committing or opening a PR, run `git status` and `git diff --staged --stat` and remove anything matching these patterns. Put scratch work under `.tmp/` (gitignored) instead of the repo root or the source tree.
## Code Review Rules
These rules apply to every pull request review, automated or human. The user populations and contract files they refer to are listed in `.agents/skills/review-pr/surfaces.md`.
### Review scope: the PR's diff, not the repo
- Review only the PR's diff against its base. A problem in code the PR does not touch (pre-existing on base) is reported as a non-blocking note, never as a blocking finding — the sole exception is pre-existing code that the PR's behavior directly triggers or amplifies.
- Report a class of issues in one pass: when a finding generalizes to several locations in the diff, list all of them at once instead of raising them across successive rounds.
- No pure style or personal-preference findings. Every finding carries a severity: P0 blocking / P1 should-fix / P2 suggestion.
- Re-reviews are incremental: review only what changed since the previous review; do not restate findings on unchanged code or re-raise resolved threads. Before treating a resolved thread as settled, verify the current code: suppress a finding only when the problem is actually fixed or the finding no longer holds.
- Domain-specific rules keep their own canonical homes — changesets (`.agents/skills/gen-changesets/SKILL.md`), comment-free zones (General Coding Rules), PR text (`write-pr` skill); review against those, not ad-hoc taste.
### Enumerate changed behavior, not just bugs
Any input that worked before the change — a config key, env var, CLI flag, provider response shape, session written by an older version, client request, or hook payload — must behave the same after it unless the PR declares the change. For every deleted or narrowed branch, condition, default, or prompt sentence, ask who reached it before and where they go now; for every new condition, ask which existing inputs now match it first. Flag a PR that calls a path "unchanged" when its branch condition moved.
### A flipped default or removed behavior needs a named loss and an escape hatch
Everyone on the old default is affected. Require the changeset to name the behavior users lose, not only the new default; a config, env, or flag escape hatch or a maintainer's explicit sign-off in the PR; and a test that pins the old behavior for the population that keeps it.
### Prompt text is behavior
Editing or deleting sentences under `packages/agent-core-v2/src/**/*.md` (system prompt, tool descriptions, reminders, overlays, built-in skills) changes agent behavior for every user who receives that prompt. "No test references the sentence" is not evidence of no impact. Require the PR to name the population that receives the text (every session, plan mode, a flag-gated feature such as Tower), what the sentence enforced, who relied on it, and what enforces it now.
### Contract files are tripwires
The manifests under `packages/agent-core-v2/docs/` (`config-manifest.toml`, `wire-manifest.d.ts`, `state-manifest.d.ts`), `packages/kap-server/test/__snapshots__/apiSurface.snapshot.test.ts.snap`, `packages/node-sdk/src/index.ts`, `packages/agent-core-v2/src/features/externalHooks/`, `packages/acp-server/`, and `apps/kimi-code/src/cli/` are consumed outside this repository: desktop and web in code-app, the VS Code extension, ACP clients such as Zed, SDK users, hook scripts, and headless-output parsers. When they change, require the PR to name the consumers and how data and clients from the previous release keep working.
### Ports and refactors carry the old path's feature inventory
When a change replaces or bypasses an existing path, require a list of what the old path did — env vars honored, fallbacks, accepted inputs — and where each item lives in the new path. A silently dropped item is a regression, not a cleanup.
### Silent failure outranks a crash
A change that makes the product silently ignore configuration, silently approve or skip an action, or silently drop data is the most severe finding: users get no signal to report.
- [#4414](https://github.com/MoonshotAI/kimi-code/pull/4414) [`f36c127`](https://github.com/MoonshotAI/kimi-code/commit/f36c127ae10feb9cfd4cd2a44ddd9ddf585f9775) Thanks [@RealKai42](https://github.com/RealKai42)! - Stop showing a failed swarm for `AgentSwarm` calls rejected before any subagent starts, such as while swarm mode is off; after such a rejection, the agent points you to `/swarm` if you asked for a swarm.
- [#4423](https://github.com/MoonshotAI/kimi-code/pull/4423) [`07a34fe`](https://github.com/MoonshotAI/kimi-code/commit/07a34fe92664c77fb5f3e17757f0e7e00e9a03d4) Thanks [@RealKai42](https://github.com/RealKai42)! - Show a high-usage warning when you turn on swarm mode with `/swarm`.
- [#4432](https://github.com/MoonshotAI/kimi-code/pull/4432) [`be427e0`](https://github.com/MoonshotAI/kimi-code/commit/be427e0b2ab338b33f925b52e80e73696c397db4) Thanks [@RealKai42](https://github.com/RealKai42)! - Merge the two repeated permission-mode notice lines in the swarm and goal start confirmation prompts into one sentence each.
- [#4443](https://github.com/MoonshotAI/kimi-code/pull/4443) [`60c888f`](https://github.com/MoonshotAI/kimi-code/commit/60c888f300ab02cbd2cc08a3f6769ca23712cb12) Thanks [@kimi-agent-bot](https://github.com/kimi-agent-bot)! - web: Improved interactions and fixed known bugs.
## 2.3.2
### Patch Changes
- [#4386](https://github.com/MoonshotAI/kimi-code/pull/4386) [`fd2e2c7`](https://github.com/MoonshotAI/kimi-code/commit/fd2e2c7e2a3672dda91365d66b2ba74a1630521e) Thanks [@RealKai42](https://github.com/RealKai42)! - Handle tasks directly by default instead of proactively launching subagents; request delegation explicitly in your message, AGENTS.md, a skill, or an enabled multi-agent mode.
- [#4384](https://github.com/MoonshotAI/kimi-code/pull/4384) [`c9a4836`](https://github.com/MoonshotAI/kimi-code/commit/c9a48369843fdee1b494b9d49b1f4b3aeee73497) Thanks [@RealKai42](https://github.com/RealKai42)! - Agent swarms now start only when you turn on swarm mode: the agent can no longer launch `AgentSwarm` by itself, so swarms are no longer available in `kimi -p` or ACP clients such as Zed.
## 2.3.1
### Patch Changes
- [#4348](https://github.com/MoonshotAI/kimi-code/pull/4348) [`c569bab`](https://github.com/MoonshotAI/kimi-code/commit/c569bab55d4d510b09f01205d760001d16117aff) Thanks [@liruifengv](https://github.com/liruifengv)! - Fix workspace trust failures when Git Bash is unavailable on Windows.
- [#4348](https://github.com/MoonshotAI/kimi-code/pull/4348) [`c569bab`](https://github.com/MoonshotAI/kimi-code/commit/c569bab55d4d510b09f01205d760001d16117aff) Thanks [@liruifengv](https://github.com/liruifengv)! - Fix sessions occasionally remaining occupied after an application crash on macOS and Linux.
- [#4356](https://github.com/MoonshotAI/kimi-code/pull/4356) [`0e3980e`](https://github.com/MoonshotAI/kimi-code/commit/0e3980e75e9af9d28955a5e93cff82d616b11353) Thanks [@7Sageer](https://github.com/7Sageer)! - Show the holding client's name in session-in-use errors.
- [#4348](https://github.com/MoonshotAI/kimi-code/pull/4348) [`c569bab`](https://github.com/MoonshotAI/kimi-code/commit/c569bab55d4d510b09f01205d760001d16117aff) Thanks [@liruifengv](https://github.com/liruifengv)! - Offer to take over a session that is still in use after a retry, so sessions left locked by an exited process can be used again, and add a `KIMI_CODE_SESSION_LOCK=0` switch that turns off the session lock.
- [#4348](https://github.com/MoonshotAI/kimi-code/pull/4348) [`c569bab`](https://github.com/MoonshotAI/kimi-code/commit/c569bab55d4d510b09f01205d760001d16117aff) Thanks [@liruifengv](https://github.com/liruifengv)! - web: Improved interactions and fixed known bugs.
## 2.3.0
### Minor Changes
- [#4294](https://github.com/MoonshotAI/kimi-code/pull/4294) [`3f80896`](https://github.com/MoonshotAI/kimi-code/commit/3f80896a79c5b17867898bd829b6d7a78f569f47) Thanks [@liruifengv](https://github.com/liruifengv)! - Add a confirmation dialog when switching models in a session with conversation history: start a new session, compact and continue, continue directly, or cancel. Shown only when enabled by cloud configuration.
- [#4188](https://github.com/MoonshotAI/kimi-code/pull/4188) [`3f0857e`](https://github.com/MoonshotAI/kimi-code/commit/3f0857eb7d612c4890c45dfa6acc8381f1e761c2) Thanks [@oocz](https://github.com/oocz)! - List plugin skills under their qualified `<pluginId>:<skillName>` name so same-named skills from different plugins all stay visible and invocable; invoking a plugin skill by its bare name no longer works, use the qualified name instead.
- [#4214](https://github.com/MoonshotAI/kimi-code/pull/4214) [`ef727e5`](https://github.com/MoonshotAI/kimi-code/commit/ef727e517923cb312cb48d42825c49dfacd528e7) Thanks [@sailist](https://github.com/sailist)! - Support streaming large requests and responses through remote control, raising the transfer limit from 10 MiB to 100 MiB, with concurrent transfers bounded to protect memory.
- [#4288](https://github.com/MoonshotAI/kimi-code/pull/4288) [`df0ff3c`](https://github.com/MoonshotAI/kimi-code/commit/df0ff3c5492e8f896927c4af9b4ae00b2b488607) Thanks [@github-actions](https://github.com/apps/github-actions)! - When the same session is open in multiple clients, sending from a second one now shows an occupancy prompt with retry/cancel, or reloads updates made elsewhere first and resends your message.
- [#4200](https://github.com/MoonshotAI/kimi-code/pull/4200) [`5bab748`](https://github.com/MoonshotAI/kimi-code/commit/5bab748dddaae4b2ac15822d72b83446e5fd7bf4) Thanks [@7Sageer](https://github.com/7Sageer)! - Automatically generate a session title after the first turn and show it as the terminal window title.
### Patch Changes
- [#4250](https://github.com/MoonshotAI/kimi-code/pull/4250) [`6e7cc90`](https://github.com/MoonshotAI/kimi-code/commit/6e7cc909da9084a424f822dd5a9aae5b98070b73) Thanks [@sailist](https://github.com/sailist)! - Setting `[permission] dangerous_command_guard = false` now turns off every built-in confirmation guard — dangerous commands, sensitive-file access, and git control-path access — instead of only the dangerous-command guard.
- [#4281](https://github.com/MoonshotAI/kimi-code/pull/4281) [`5710c54`](https://github.com/MoonshotAI/kimi-code/commit/5710c5496a213742ca0687f69dd8eeb5bde52dbf) Thanks [@sailist](https://github.com/sailist)! - Fix session events stopping after a session is archived and restored.
- [#4201](https://github.com/MoonshotAI/kimi-code/pull/4201) [`83a39b4`](https://github.com/MoonshotAI/kimi-code/commit/83a39b49ed62627c7a604b39e26d8e9d17ade296) Thanks [@Grapedge](https://github.com/Grapedge)! - Stop blocking the terminal while the footer reads git status.
- [#4230](https://github.com/MoonshotAI/kimi-code/pull/4230) [`b260326`](https://github.com/MoonshotAI/kimi-code/commit/b2603264ec4687cee766cf5d5e6d4976e432961c) Thanks [@kimi-agent-bot](https://github.com/kimi-agent-bot)! - Report a clear error when thinking effort is set to off for Gemini 4 models.
- [#4208](https://github.com/MoonshotAI/kimi-code/pull/4208) [`20b5991`](https://github.com/MoonshotAI/kimi-code/commit/20b59915bb11fc56ec389aa96720f1b7daac0862) Thanks [@SeleneXX](https://github.com/SeleneXX)! - Fix Gemini 4 requests always failing with a 400 error.
- [#4242](https://github.com/MoonshotAI/kimi-code/pull/4242) [`c1459a6`](https://github.com/MoonshotAI/kimi-code/commit/c1459a61845056832b85a0a220ee11a2bc5132f8) Thanks [@wbxl2000](https://github.com/wbxl2000)! - Run hooks such as `PreToolUse` and `Stop` in the session's project directory, as documented, instead of the directory the server was started from.
- [#4210](https://github.com/MoonshotAI/kimi-code/pull/4210) [`f28c51c`](https://github.com/MoonshotAI/kimi-code/commit/f28c51c01f025bba9f0d0f58f6309e7517a8cd02) Thanks [@sailist](https://github.com/sailist)! - Add the [mcp] default_deferred config key: when the experimental tool-select flag is on, set default_deferred = true in config.toml to load every MCP server's tools on demand by default; a server can opt out with deferred: false.
- [#4171](https://github.com/MoonshotAI/kimi-code/pull/4171) [`0327e8e`](https://github.com/MoonshotAI/kimi-code/commit/0327e8e9a875949fd40141ef79f902d756dfd3db) Thanks [@7Sageer](https://github.com/7Sageer)! - Support custom-registry model upgrade metadata in model configuration.
- [#4188](https://github.com/MoonshotAI/kimi-code/pull/4188) [`3f0857e`](https://github.com/MoonshotAI/kimi-code/commit/3f0857eb7d612c4890c45dfa6acc8381f1e761c2) Thanks [@oocz](https://github.com/oocz)! - Allow uppercase letters, dot-separated segments, and @ suffixes in plugin names when installing plugins.
- [#4294](https://github.com/MoonshotAI/kimi-code/pull/4294) [`3f80896`](https://github.com/MoonshotAI/kimi-code/commit/3f80896a79c5b17867898bd829b6d7a78f569f47) Thanks [@liruifengv](https://github.com/liruifengv)! - Queue /compact while the agent is busy: the command now joins the message queue and runs in order instead of failing with an error; it is skipped if the context was auto-compacted while it waited.
- [#4190](https://github.com/MoonshotAI/kimi-code/pull/4190) [`e4c99a3`](https://github.com/MoonshotAI/kimi-code/commit/e4c99a3bad19b00bd1939b1cf254f427b50061f0) Thanks [@starquakee](https://github.com/starquakee)! - Clarify unavailable tools, announce when no tools can be loaded, and discourage repeated failed tool selections.
- [#4272](https://github.com/MoonshotAI/kimi-code/pull/4272) [`ce0834a`](https://github.com/MoonshotAI/kimi-code/commit/ce0834a8f301b89cc4523499a3e074c36851de8f) Thanks [@Grapedge](https://github.com/Grapedge)! - Move the session rating opt-out from tui.toml to config.toml.
- [#4066](https://github.com/MoonshotAI/kimi-code/pull/4066) [`85077a4`](https://github.com/MoonshotAI/kimi-code/commit/85077a419001285e49ede3574924f001935aed62) Thanks [@7Sageer](https://github.com/7Sageer)! - Keep subagent tool descriptions aligned with currently available tools.
- [#3799](https://github.com/MoonshotAI/kimi-code/pull/3799) [`7e58f2d`](https://github.com/MoonshotAI/kimi-code/commit/7e58f2d2410d2b541ffd57f4501c0436bba4ccbd) Thanks [@kimi-agent-bot](https://github.com/kimi-agent-bot)! - Fix the latest reply disappearing from the transcript after a background task completion notification arrives; the fold boundary is now anchored to the notification turn itself instead of the asynchronously mounted task card.
- [#4239](https://github.com/MoonshotAI/kimi-code/pull/4239) [`da5918c`](https://github.com/MoonshotAI/kimi-code/commit/da5918c0c861a1d670f4523a9f9dd968647fe4cb) Thanks [@RealKai42](https://github.com/RealKai42)! - Remind the agent to update its todo list after 5 steps without an update instead of 10, only while the list still has unfinished items, and ask it to bring the list up to date before its final reply.
- [#4248](https://github.com/MoonshotAI/kimi-code/pull/4248) [`8fcd332`](https://github.com/MoonshotAI/kimi-code/commit/8fcd3326e7488e5e0d4c595115ef74a10c340d5b) Thanks [@kimi-agent-bot](https://github.com/kimi-agent-bot)! - Show a single overflowing preview line in full instead of swapping it for a same-height expand hint.
- [#4304](https://github.com/MoonshotAI/kimi-code/pull/4304) [`d36071e`](https://github.com/MoonshotAI/kimi-code/commit/d36071ede6ae3cbdc08cbb8a0f7daf7bc6d06f2d) Thanks [@kimi-agent-bot](https://github.com/kimi-agent-bot)! - web: Improved interactions and fixed known bugs.
## 2.2.0
### Minor Changes
- [#4159](https://github.com/MoonshotAI/kimi-code/pull/4159) [`0a3a2fb`](https://github.com/MoonshotAI/kimi-code/commit/0a3a2fbafab473df9e9f6609f825af78d2c1ce6d) Thanks [@RealKai42](https://github.com/RealKai42)! - Add an opt-in NotifyUser progress update feature (off by default): new sessions can show the agent's mid-turn progress with an optional one-line title, enabled in the TUI `/experiments` or the web/Desktop settings' Lab section; set `[experimental] notify_user = true` or `KIMI_CODE_EXPERIMENTAL_NOTIFY_USER=1` to turn it on.
- [#4145](https://github.com/MoonshotAI/kimi-code/pull/4145) [`242ac23`](https://github.com/MoonshotAI/kimi-code/commit/242ac230006447f8601e1678c49182bfbae89fe9) Thanks [@wbxl2000](https://github.com/wbxl2000)! - Add `kimi app [path]` to open a new desktop chat in the current or specified directory.
- [#4061](https://github.com/MoonshotAI/kimi-code/pull/4061) [`20a2cea`](https://github.com/MoonshotAI/kimi-code/commit/20a2cea72f5d2c4be3a3845e4ff24ce302a6630e) Thanks [@chengluyu](https://github.com/chengluyu)! - Cap the agent's in-turn wait for background tasks at 90 seconds (longer timeouts are no longer accepted), discourage repeated waits, and let a new message end the wait immediately.
- [#3998](https://github.com/MoonshotAI/kimi-code/pull/3998) [`09af3b4`](https://github.com/MoonshotAI/kimi-code/commit/09af3b483faac51a767aee50adb1bc1a8de4cc53) Thanks [@tpoisonooo](https://github.com/tpoisonooo)! - Tower mode: a message sent while the agent is running aborts the in-progress turn and runs as its own turn.
- [#4031](https://github.com/MoonshotAI/kimi-code/pull/4031) [`33e368a`](https://github.com/MoonshotAI/kimi-code/commit/33e368a1eff8d958856af8e2fb64f4db122b2247) Thanks [@Grapedge](https://github.com/Grapedge)! - Add sticky user message summaries in fullscreen mode; click a summary to jump to the original message.
- [#4074](https://github.com/MoonshotAI/kimi-code/pull/4074) [`c9f01f0`](https://github.com/MoonshotAI/kimi-code/commit/c9f01f07388abc2cdca40c9c5267f58c65086710) Thanks [@tpoisonooo](https://github.com/tpoisonooo)! - Tower mode: deterministic orchestration with TowerRebase and LLM-free status, goal/tower mutual exclusion, and workspace protection.
- [#4059](https://github.com/MoonshotAI/kimi-code/pull/4059) [`1f6f0b1`](https://github.com/MoonshotAI/kimi-code/commit/1f6f0b1fa2117cc9374c1be62c534b4a4d60e412) Thanks [@7Sageer](https://github.com/7Sageer)! - Add the KIMI_CODE_TRUST_WORKSPACE environment variable to mark the workspace as trusted.
- [#4165](https://github.com/MoonshotAI/kimi-code/pull/4165) [`afd95f6`](https://github.com/MoonshotAI/kimi-code/commit/afd95f64a5444b649a4cc33504513bf88e4c54ba) Thanks [@Grapedge](https://github.com/Grapedge)! - Replace user skill cards with highlighted prompts and support Ctrl-S for skill prompts.
### Patch Changes
- [#4183](https://github.com/MoonshotAI/kimi-code/pull/4183) [`c15638b`](https://github.com/MoonshotAI/kimi-code/commit/c15638b0a1234e5aa97659fbc625d7dc4d60d93f) Thanks [@RealKai42](https://github.com/RealKai42)! - Cancel in-flight video uploads when a media read is interrupted.
- [#4081](https://github.com/MoonshotAI/kimi-code/pull/4081) [`06ebfc8`](https://github.com/MoonshotAI/kimi-code/commit/06ebfc821e304bd4ca3f828ae739954166463d65) Thanks [@sailist](https://github.com/sailist)! - Fix shifted turns and lost replies when a UserPromptSubmit hook injects text; hook JSON output without a "message" field is no longer added to the conversation.
- [#4154](https://github.com/MoonshotAI/kimi-code/pull/4154) [`419aced`](https://github.com/MoonshotAI/kimi-code/commit/419aced0e97fa04b75f8f71b089e1667f6de6d0a) Thanks [@7Sageer](https://github.com/7Sageer)! - Fix media file reading on the first turn after resuming a session.
- [#4083](https://github.com/MoonshotAI/kimi-code/pull/4083) [`f409caa`](https://github.com/MoonshotAI/kimi-code/commit/f409caa21e71ce7beb158d29ffca1fed76216a64) Thanks [@sailist](https://github.com/sailist)! - Cron tasks from the source session no longer fire inside a forked session.
- [#4152](https://github.com/MoonshotAI/kimi-code/pull/4152) [`f0bc988`](https://github.com/MoonshotAI/kimi-code/commit/f0bc988bbc7db08d650f37ae0eaa69bcc6e90696) Thanks [@7Sageer](https://github.com/7Sageer)! - Forked subagents now inherit the parent's available agent types, including custom agents.
- [#4057](https://github.com/MoonshotAI/kimi-code/pull/4057) [`a940f2f`](https://github.com/MoonshotAI/kimi-code/commit/a940f2ff049ed57abce68c59526de7a6628a4112) Thanks [@huangzheng2016](https://github.com/huangzheng2016)! - Fix the permission mode switch in the goal-start approval prompt not taking effect on the v2 engine.
- [#4081](https://github.com/MoonshotAI/kimi-code/pull/4081) [`06ebfc8`](https://github.com/MoonshotAI/kimi-code/commit/06ebfc821e304bd4ca3f828ae739954166463d65) Thanks [@sailist](https://github.com/sailist)! - Fix messages rendering out of place in sessions with a UserPromptSubmit hook.
- [#4153](https://github.com/MoonshotAI/kimi-code/pull/4153) [`7091138`](https://github.com/MoonshotAI/kimi-code/commit/7091138f5e66289d04b9b7a5cd77f94af9308be3) Thanks [@RealKai42](https://github.com/RealKai42)! - Let the agent write one- or two-sentence progress notes between tool calls instead of bare status lines; in the experimental Updates panel (`KIMI_CODE_EXPERIMENTAL_NOTIFY_USER=1`), stop asking the agent to repost its mid-turn text as an update and remind it to post updates less often.
- [#4054](https://github.com/MoonshotAI/kimi-code/pull/4054) [`4fbe065`](https://github.com/MoonshotAI/kimi-code/commit/4fbe065442179435c43d3c3dc8d11bb408b3fd30) Thanks [@RealKai42](https://github.com/RealKai42)! - Stop prompting the agent to send NotifyUser updates when a session is opened in a host that does not display them.
- [#4091](https://github.com/MoonshotAI/kimi-code/pull/4091) [`21406fb`](https://github.com/MoonshotAI/kimi-code/commit/21406fb4c805cc8c715e6d1f16ad3fb5f25f4fe3) Thanks [@sailist](https://github.com/sailist)! - Omit max_tokens for models without an explicit output limit, fixing repeated 400 errors on strict serving stacks (e.g. bare vLLM) when the model's max output size is unknown.
- [#4184](https://github.com/MoonshotAI/kimi-code/pull/4184) [`cbd7540`](https://github.com/MoonshotAI/kimi-code/commit/cbd754049a114ee36d1b27675c82819e4963c692) Thanks [@7Sageer](https://github.com/7Sageer)! - Remove the never-implemented `executor = "kaos"` option from MCP stdio server configuration; configs using it now fail validation at load time.
- [#2919](https://github.com/MoonshotAI/kimi-code/pull/2919) [`0f052fe`](https://github.com/MoonshotAI/kimi-code/commit/0f052fee1399f34086bb6343a56867d7df9b70b5) Thanks [@duyphan154](https://github.com/duyphan154)! - Sanitize foreground Bash tool output so captured terminal escape sequences no longer change the host terminal state.
- [#4164](https://github.com/MoonshotAI/kimi-code/pull/4164) [`3395f5b`](https://github.com/MoonshotAI/kimi-code/commit/3395f5bea6c77feb6a835d1ca3f4935970f96c6b) Thanks [@huangzheng2016](https://github.com/huangzheng2016)! - Show a "Shutting down" status hint while shutdown hooks run during exit, and let a Ctrl+C during that window force an immediate exit instead of waiting for the hook timeout.
- [#4033](https://github.com/MoonshotAI/kimi-code/pull/4033) [`eec9bbd`](https://github.com/MoonshotAI/kimi-code/commit/eec9bbd94958248034f5dbf109a639970308f05e) Thanks [@Grapedge](https://github.com/Grapedge)! - Feedback surveys now appear less often.
- [#4160](https://github.com/MoonshotAI/kimi-code/pull/4160) [`72fc1d0`](https://github.com/MoonshotAI/kimi-code/commit/72fc1d0a3375b2a87dff602a1d91726a99f360e0) Thanks [@kimi-agent-bot](https://github.com/kimi-agent-bot)! - Show "Working…" in the status spinner while thinking.
- [#4056](https://github.com/MoonshotAI/kimi-code/pull/4056) [`395d537`](https://github.com/MoonshotAI/kimi-code/commit/395d537237d737de0239159b60d1cc160bdf04e6) Thanks [@7Sageer](https://github.com/7Sageer)! - Replace inline MCP commands in the workspace trust prompt with activation summaries and configuration paths for manual inspection.
- [#4076](https://github.com/MoonshotAI/kimi-code/pull/4076) [`e3bf50c`](https://github.com/MoonshotAI/kimi-code/commit/e3bf50c0831d91ceb3656acf1b7a3f480eaada56) Thanks [@7Sageer](https://github.com/7Sageer)! - Remove interruption reminders from the conversation when their interrupted turns are undone.
- [#4031](https://github.com/MoonshotAI/kimi-code/pull/4031) [`33e368a`](https://github.com/MoonshotAI/kimi-code/commit/33e368a1eff8d958856af8e2fb64f4db122b2247) Thanks [@Grapedge](https://github.com/Grapedge)! - Replace the user message bullet ✨ with ❯.
- [#4221](https://github.com/MoonshotAI/kimi-code/pull/4221) [`2e6821d`](https://github.com/MoonshotAI/kimi-code/commit/2e6821dcbd8034f128b2326a73d4704201b7c108) Thanks [@kimi-agent-bot](https://github.com/kimi-agent-bot)! - web: Improved interactions and fixed known bugs.
## 2.1.1
### Patch Changes
- [#4013](https://github.com/MoonshotAI/kimi-code/pull/4013) [`929403b`](https://github.com/MoonshotAI/kimi-code/commit/929403b6db219eae6099dd23eae2f0efa2bb2823) Thanks [@7Sageer](https://github.com/7Sageer)! - Roll back some of the overly defensive changes in 2.1.0
- [#4015](https://github.com/MoonshotAI/kimi-code/pull/4015) [`c7dd841`](https://github.com/MoonshotAI/kimi-code/commit/c7dd84124a00d2dc1a68fbbc3e54b1095ee9ac23) Thanks [@sailist](https://github.com/sailist)! - Revert filesystem watchers for config and workspace files to on by default. Set `[watch] enabled` to `false` or `KIMI_CODE_WATCH=0` to keep them off.
## 2.1.0
### Minor Changes
- [#3990](https://github.com/MoonshotAI/kimi-code/pull/3990) [`32000d0`](https://github.com/MoonshotAI/kimi-code/commit/32000d08afcd019016fa60db0d68eaf21f3e35a6) Thanks [@Grapedge](https://github.com/Grapedge)! - In fullscreen, click a folded block to open or close it.
- [#4004](https://github.com/MoonshotAI/kimi-code/pull/4004) [`e17ff67`](https://github.com/MoonshotAI/kimi-code/commit/e17ff679281e340fd328bc2ebaba1237e79a9ad3) Thanks [@Grapedge](https://github.com/Grapedge)! - Add a TUI mode setting to /settings with an experimental fullscreen layout; switching modes takes effect after restarting Kimi Code. You can also set `tui_mode` in `~/.kimi-code/tui.toml` directly: `"regular"` renders into the terminal scrollback, while `"fullscreen"` makes parts of the UI mouse-interactive for a smoother experience.
### Patch Changes
- [#3964](https://github.com/MoonshotAI/kimi-code/pull/3964) [`6451f1e`](https://github.com/MoonshotAI/kimi-code/commit/6451f1e056e90037bbf832f3578955cf8e55db64) Thanks [@7Sageer](https://github.com/7Sageer)! - Block file tools from accessing files outside the working directory through symlinks.
- [#3975](https://github.com/MoonshotAI/kimi-code/pull/3975) [`bb96d80`](https://github.com/MoonshotAI/kimi-code/commit/bb96d80748921618182369a40a6610f38fa16acb) Thanks [@RealKai42](https://github.com/RealKai42)! - Reduce the CLI's startup time and memory usage.
- [#3957](https://github.com/MoonshotAI/kimi-code/pull/3957) [`6a214b8`](https://github.com/MoonshotAI/kimi-code/commit/6a214b85e53e58a9ef6480f27bcb7b0103c0e34e) Thanks [@huangzheng2016](https://github.com/huangzheng2016)! - Fix a "Maximum call stack size exceeded" error when opening large sessions.
- [#3974](https://github.com/MoonshotAI/kimi-code/pull/3974) [`994287a`](https://github.com/MoonshotAI/kimi-code/commit/994287acb3a7428b580c09cabd5aba1b5a818aaa) Thanks [@liruifengv](https://github.com/liruifengv)! - Fix forked sessions getting their titles overwritten by auto-generated titles.
- [#3964](https://github.com/MoonshotAI/kimi-code/pull/3964) [`6451f1e`](https://github.com/MoonshotAI/kimi-code/commit/6451f1e056e90037bbf832f3578955cf8e55db64) Thanks [@7Sageer](https://github.com/7Sageer)! - Apply project-local configuration only after the workspace is trusted.
- [#3964](https://github.com/MoonshotAI/kimi-code/pull/3964) [`6451f1e`](https://github.com/MoonshotAI/kimi-code/commit/6451f1e056e90037bbf832f3578955cf8e55db64) Thanks [@7Sageer](https://github.com/7Sageer)! - Block repository git configuration from executing commands during background git operations.
- [#3969](https://github.com/MoonshotAI/kimi-code/pull/3969) [`b3212fd`](https://github.com/MoonshotAI/kimi-code/commit/b3212fd9abc1f4670fb47df16f0137957aed6f2d) Thanks [@sailist](https://github.com/sailist)! - Skip cloud telemetry on the global login until its collector is live, and keep shutdown from aborting TUI exit.
- [#3979](https://github.com/MoonshotAI/kimi-code/pull/3979) [`e796bb5`](https://github.com/MoonshotAI/kimi-code/commit/e796bb5d482ecb7976c25dad0563b74cc9ec2c68) Thanks [@sailist](https://github.com/sailist)! - Fix MCP OAuth not requesting the offline_access scope, which caused hourly browser re-authorization with providers like Vercel.
- [#3969](https://github.com/MoonshotAI/kimi-code/pull/3969) [`b3212fd`](https://github.com/MoonshotAI/kimi-code/commit/b3212fd9abc1f4670fb47df16f0137957aed6f2d) Thanks [@sailist](https://github.com/sailist)! - Fix Remote Control failing to start after `kimi login --region global`.
- [#3964](https://github.com/MoonshotAI/kimi-code/pull/3964) [`6451f1e`](https://github.com/MoonshotAI/kimi-code/commit/6451f1e056e90037bbf832f3578955cf8e55db64) Thanks [@7Sageer](https://github.com/7Sageer)! - Reject additional directories that resolve to the home directory or filesystem root.
- [#3995](https://github.com/MoonshotAI/kimi-code/pull/3995) [`a54e6f6`](https://github.com/MoonshotAI/kimi-code/commit/a54e6f6a9b36bc1346e41da576d96ff54aa557b6) Thanks [@7Sageer](https://github.com/7Sageer)! - Turn off repeat reminders and forced stops with `KIMI_CODE_REPEAT_BREAKER=0`.
- [#3970](https://github.com/MoonshotAI/kimi-code/pull/3970) [`895e9d9`](https://github.com/MoonshotAI/kimi-code/commit/895e9d9b868cf9899e444784130fa1946c6cef6c) Thanks [@chengluyu](https://github.com/chengluyu)! - Fix missing Swarm members after reopening an interrupted conversation.
- [#3989](https://github.com/MoonshotAI/kimi-code/pull/3989) [`a268ad7`](https://github.com/MoonshotAI/kimi-code/commit/a268ad775d9451d008d3c73de053655f6ab0772a) Thanks [@Grapedge](https://github.com/Grapedge)! - Show a Thinking… spinner and keep the status row height steady during streaming.
- [#3989](https://github.com/MoonshotAI/kimi-code/pull/3989) [`a268ad7`](https://github.com/MoonshotAI/kimi-code/commit/a268ad775d9451d008d3c73de053655f6ab0772a) Thanks [@Grapedge](https://github.com/Grapedge)! - Add a clickable "Jump to bottom" indicator to the fullscreen TUI.
- [#3976](https://github.com/MoonshotAI/kimi-code/pull/3976) [`f7012aa`](https://github.com/MoonshotAI/kimi-code/commit/f7012aa23b38d90d1a643166e2e78f706a606a34) Thanks [@tpoisonooo](https://github.com/tpoisonooo)! - Tower mode: smaller missions, stricter completion and review checks, and review history in rework briefings.
- [#3847](https://github.com/MoonshotAI/kimi-code/pull/3847) [`65ae3e3`](https://github.com/MoonshotAI/kimi-code/commit/65ae3e368c7cfa096242cacc12eeeb661e682977) Thanks [@tpoisonooo](https://github.com/tpoisonooo)! - Tower mode: mission titles must be ASCII, and task cards show the mission id.
- [#3931](https://github.com/MoonshotAI/kimi-code/pull/3931) [`f17a22e`](https://github.com/MoonshotAI/kimi-code/commit/f17a22ebf4e21b8196bfaaa9a2490707676bf519) Thanks [@sailist](https://github.com/sailist)! - Turn off filesystem watchers for config and workspace files by default. Set `[watch] enabled` to `true` or `KIMI_CODE_WATCH=1` to turn them back on.
- [#4005](https://github.com/MoonshotAI/kimi-code/pull/4005) [`ba41d07`](https://github.com/MoonshotAI/kimi-code/commit/ba41d07810cae37a9e529cad6b0f8e8abc8d7aaf) Thanks [@liruifengv](https://github.com/liruifengv)! - web: Improved interactions and fixed known bugs.
## 2.0.2
### Patch Changes
- [#3922](https://github.com/MoonshotAI/kimi-code/pull/3922) [`9df7a9c`](https://github.com/MoonshotAI/kimi-code/commit/9df7a9ccfb0d7d0713b51ae1ac165d0634fc40d2) Thanks [@sailist](https://github.com/sailist)! - Fix new messages occasionally landing at an old position in the conversation after resuming a session.
- [#3911](https://github.com/MoonshotAI/kimi-code/pull/3911) [`88a7d93`](https://github.com/MoonshotAI/kimi-code/commit/88a7d932f1cda95c78bf5488d34ff974a160fb08) Thanks [@Grapedge](https://github.com/Grapedge)! - Fix compaction failing after switching to a model with a smaller context window.
- [#3929](https://github.com/MoonshotAI/kimi-code/pull/3929) [`6ffdf0d`](https://github.com/MoonshotAI/kimi-code/commit/6ffdf0d579fa1a9f30c2d00c437c6c8de0c03997) Thanks [@7Hanrui](https://github.com/7Hanrui)! - The agent no longer assumes the current working directory is the project root.
- [#3933](https://github.com/MoonshotAI/kimi-code/pull/3933) [`9721259`](https://github.com/MoonshotAI/kimi-code/commit/97212596f7d694b9683046ec7aad4d1247d29468) Thanks [@liruifengv](https://github.com/liruifengv)! - Fix a message sent while the agent was running sometimes appearing twice in the chat.
- [#3934](https://github.com/MoonshotAI/kimi-code/pull/3934) [`2e605b1`](https://github.com/MoonshotAI/kimi-code/commit/2e605b10be906fb627c1f3aeaaa066b14ddbdbba) Thanks [@liruifengv](https://github.com/liruifengv)! - web: Improved interactions and fixed known bugs.
## 2.0.1
### Patch Changes
- [#3878](https://github.com/MoonshotAI/kimi-code/pull/3878) [`f233f9d`](https://github.com/MoonshotAI/kimi-code/commit/f233f9de04d71c42d27df870b18e1bc1a6605f16) Thanks [@7Sageer](https://github.com/7Sageer)! - Stop listing unavailable media tools in subagent menus and clarify Read errors for images and videos.
- [#3762](https://github.com/MoonshotAI/kimi-code/pull/3762) [`c5ad17f`](https://github.com/MoonshotAI/kimi-code/commit/c5ad17f06aa12702d2b30dff63acba9ed08f688d) Thanks [@7Sageer](https://github.com/7Sageer)! - Providers can read their API key from a named environment variable (`api_key_env` in `config.toml` or the provider API).
- [#3896](https://github.com/MoonshotAI/kimi-code/pull/3896) [`73ebe9a`](https://github.com/MoonshotAI/kimi-code/commit/73ebe9ab2fbf9bf0ffc8fe5a5375060ce8e984b5) Thanks [@sailist](https://github.com/sailist)! - Keep follow-up messages sent during a turn from appearing twice after a session reload.
- [#3889](https://github.com/MoonshotAI/kimi-code/pull/3889) [`5108cad`](https://github.com/MoonshotAI/kimi-code/commit/5108cad9b65845ae992520dcf732cbbccc6a34b9) Thanks [@huangzheng2016](https://github.com/huangzheng2016)! - Compact the session index on startup when many entries point to deleted sessions.
- [#3889](https://github.com/MoonshotAI/kimi-code/pull/3889) [`5108cad`](https://github.com/MoonshotAI/kimi-code/commit/5108cad9b65845ae992520dcf732cbbccc6a34b9) Thanks [@huangzheng2016](https://github.com/huangzheng2016)! - Start the CLI faster by removing a duplicate skills directory scan.
- [#3889](https://github.com/MoonshotAI/kimi-code/pull/3889) [`5108cad`](https://github.com/MoonshotAI/kimi-code/commit/5108cad9b65845ae992520dcf732cbbccc6a34b9) Thanks [@huangzheng2016](https://github.com/huangzheng2016)! - Open the session list and resume sessions faster on workspaces with many sessions.
- [#3889](https://github.com/MoonshotAI/kimi-code/pull/3889) [`5108cad`](https://github.com/MoonshotAI/kimi-code/commit/5108cad9b65845ae992520dcf732cbbccc6a34b9) Thanks [@huangzheng2016](https://github.com/huangzheng2016)! - Resume long sessions faster.
- [#3889](https://github.com/MoonshotAI/kimi-code/pull/3889) [`5108cad`](https://github.com/MoonshotAI/kimi-code/commit/5108cad9b65845ae992520dcf732cbbccc6a34b9) Thanks [@huangzheng2016](https://github.com/huangzheng2016)! - Index sessions for search in capped, incremental background passes, tunable via the [database] searchSyncSessionCap and searchSyncDebounceMs settings.
- [#3864](https://github.com/MoonshotAI/kimi-code/pull/3864) [`25dd4ce`](https://github.com/MoonshotAI/kimi-code/commit/25dd4ce97345c7ebfd9c036898e5eef955c45ea8) Thanks [@wbxl2000](https://github.com/wbxl2000)! - Rename the kimi install-app subcommand to kimi install-desktop; the old name keeps working as a hidden alias.
- [#3910](https://github.com/MoonshotAI/kimi-code/pull/3910) [`7dc253c`](https://github.com/MoonshotAI/kimi-code/commit/7dc253c5cefac7c277e5fc7071acf3e855fd503e) Thanks [@sailist](https://github.com/sailist)! - Restore thinking display for OpenAI-compatible providers that send a reasoning string next to an unrecognized reasoning_details array.
- [#3875](https://github.com/MoonshotAI/kimi-code/pull/3875) [`c1047a6`](https://github.com/MoonshotAI/kimi-code/commit/c1047a6bbe0e37d097e5da54d92dd3fb1b7a28da) Thanks [@7Sageer](https://github.com/7Sageer)! - Fix `kimi -p` exiting early and cancelling the active turn when a cron task fires.
- [#3894](https://github.com/MoonshotAI/kimi-code/pull/3894) [`910aba2`](https://github.com/MoonshotAI/kimi-code/commit/910aba273e2bee3a8815666a3a9c4fff609f2366) Thanks [@7Sageer](https://github.com/7Sageer)! - Drop literal decision clauses from the system prompt and the auto permission mode reminder.
- [#3889](https://github.com/MoonshotAI/kimi-code/pull/3889) [`5108cad`](https://github.com/MoonshotAI/kimi-code/commit/5108cad9b65845ae992520dcf732cbbccc6a34b9) Thanks [@huangzheng2016](https://github.com/huangzheng2016)! - Restore sessions with large file-edit histories faster.
- [#3885](https://github.com/MoonshotAI/kimi-code/pull/3885) [`5acc863`](https://github.com/MoonshotAI/kimi-code/commit/5acc863ae60c092457bd9ecdde55266b5fad7040) Thanks [@starquakee](https://github.com/starquakee)! - select_tools now points out already-available tools to call directly and suggests announced names for near-miss selections.
- [#3887](https://github.com/MoonshotAI/kimi-code/pull/3887) [`e3f48a2`](https://github.com/MoonshotAI/kimi-code/commit/e3f48a225bbedbe167b30510e67d66ee67fcb0ec) Thanks [@RealKai42](https://github.com/RealKai42)! - Fix deleting or archiving a session sometimes never finishing.
- [#3891](https://github.com/MoonshotAI/kimi-code/pull/3891) [`53e5e3f`](https://github.com/MoonshotAI/kimi-code/commit/53e5e3fca6bf377662bc4d61d220dde4ded8e7b7) Thanks [@sailist](https://github.com/sailist)! - Keep follow-up messages sent during a turn attached to that turn in the conversation history.
- [#3906](https://github.com/MoonshotAI/kimi-code/pull/3906) [`60f2a63`](https://github.com/MoonshotAI/kimi-code/commit/60f2a63278f28b77d9358cf589b2da6193bd2614) Thanks [@sailist](https://github.com/sailist)! - Keep a follow-up sent during a turn as one message, and undo the host turn instead of the follow-up alone.
- [#3879](https://github.com/MoonshotAI/kimi-code/pull/3879) [`1fddc16`](https://github.com/MoonshotAI/kimi-code/commit/1fddc16e3ea2de4c26a18acd764380adf9e2ed64) Thanks [@7Sageer](https://github.com/7Sageer)! - Remove the system-prompt rule that forbade all file access outside the working directory.
- [#3892](https://github.com/MoonshotAI/kimi-code/pull/3892) [`a80fe31`](https://github.com/MoonshotAI/kimi-code/commit/a80fe31cff0ebae686e60d7d5d0559ad2d2e843d) Thanks [@sailist](https://github.com/sailist)! - Stop workspace file watchers from scanning an unbounded project root, and add `[watch] enabled` / `KIMI_CODE_WATCH` to disable watching entirely.
- [#3869](https://github.com/MoonshotAI/kimi-code/pull/3869) [`86e0820`](https://github.com/MoonshotAI/kimi-code/commit/86e08208e3445171f6500c948234d7e3a0684324) Thanks [@sailist](https://github.com/sailist)! - Stop asking for approval of bash commands that cannot be statically analyzed in Ask When Needed permission mode.
## 2.0.0
### Major Changes
- [#3849](https://github.com/MoonshotAI/kimi-code/pull/3849) [`34ec5d2`](https://github.com/MoonshotAI/kimi-code/commit/34ec5d2a1700e540c39d2a1b6c905c9fa96af2db) Thanks [@wbxl2000](https://github.com/wbxl2000)! - Add the /desktop slash command (alias /install-desktop) and the kimi install-app subcommand to open the Kimi Code desktop app page in the browser.
### Minor Changes
- [#3851](https://github.com/MoonshotAI/kimi-code/pull/3851) [`faec32c`](https://github.com/MoonshotAI/kimi-code/commit/faec32c7e7c1df5d27b7060f75a2a665efb59268) Thanks [@Grapedge](https://github.com/Grapedge)! - Render mermaid code blocks as diagrams in the terminal; turn it off under /settings → Mermaid diagrams, or set `mermaid = "off"` in the `[markdown]` section of tui.toml.
### Patch Changes
- [#3838](https://github.com/MoonshotAI/kimi-code/pull/3838) [`f4e5822`](https://github.com/MoonshotAI/kimi-code/commit/f4e5822164800ec138f77b0f323e2374963bfe19) Thanks [@sailist](https://github.com/sailist)! - Fix an occasional crash when a running turn is canceled.
- [#3844](https://github.com/MoonshotAI/kimi-code/pull/3844) [`0cf413f`](https://github.com/MoonshotAI/kimi-code/commit/0cf413f7759c314766ba30250a9875ac1af1185e) Thanks [@Grapedge](https://github.com/Grapedge)! - Fix a steered message appearing twice after interrupting the turn.
- [#3848](https://github.com/MoonshotAI/kimi-code/pull/3848) [`b4d0b8a`](https://github.com/MoonshotAI/kimi-code/commit/b4d0b8a3889c1a6f0115c527341ccd2066bd180a) Thanks [@Grapedge](https://github.com/Grapedge)! - Fix @ file mentions duplicating or dropping the path when accepting a suggestion while still typing.
- [#3828](https://github.com/MoonshotAI/kimi-code/pull/3828) [`2bb6e12`](https://github.com/MoonshotAI/kimi-code/commit/2bb6e12c7e3ecc19521d21fcffc832075ed13200) Thanks [@wbxl2000](https://github.com/wbxl2000)! - Fix duplicate user messages when steering an ongoing conversation.
- [#3832](https://github.com/MoonshotAI/kimi-code/pull/3832) [`1c7e996`](https://github.com/MoonshotAI/kimi-code/commit/1c7e996aa8bdd7a6f32447f1d89e43c0646b41bc) Thanks [@chengluyu](https://github.com/chengluyu)! - Fix files attached while steering an ongoing conversation appearing only after a reload.
- [#3853](https://github.com/MoonshotAI/kimi-code/pull/3853) [`bd06178`](https://github.com/MoonshotAI/kimi-code/commit/bd06178913d2cc4ebd229ecee6714081105e5ae1) Thanks [@wbxl2000](https://github.com/wbxl2000)! - Keep the original prompt visible when steering a running turn, instead of replacing it with the steered text.
- [#3832](https://github.com/MoonshotAI/kimi-code/pull/3832) [`1c7e996`](https://github.com/MoonshotAI/kimi-code/commit/1c7e996aa8bdd7a6f32447f1d89e43c0646b41bc) Thanks [@chengluyu](https://github.com/chengluyu)! - Fix slash commands sent while steering an ongoing conversation missing from the transcript.
- [#3837](https://github.com/MoonshotAI/kimi-code/pull/3837) [`cafd9b5`](https://github.com/MoonshotAI/kimi-code/commit/cafd9b5a65509fe57332cbc0eb1fb19f0443fb0e) Thanks [@wbxl2000](https://github.com/wbxl2000)! - Fix missing and reappearing chat messages after undoing a steered message.
- [#3818](https://github.com/MoonshotAI/kimi-code/pull/3818) [`19ce4b3`](https://github.com/MoonshotAI/kimi-code/commit/19ce4b3f0600c846e2f28828d8169829630fc666) Thanks [@liruifengv](https://github.com/liruifengv)! - Fix /usage showing a wrong error message when no session has been created yet.
- [#3784](https://github.com/MoonshotAI/kimi-code/pull/3784) [`5653c73`](https://github.com/MoonshotAI/kimi-code/commit/5653c739b9c0dbafd1e4b3d36edeb0fe44ec71f4) Thanks [@7Sageer](https://github.com/7Sageer)! - Images sent to Kimi models are uploaded as file references instead of inline data, and a warning is shown when media are dropped from a retried request.
- [#3778](https://github.com/MoonshotAI/kimi-code/pull/3778) [`7d174ac`](https://github.com/MoonshotAI/kimi-code/commit/7d174ac93352ccfc835d186ac7b8185d0194e6b5) Thanks [@huangzheng2016](https://github.com/huangzheng2016)! - Report background subagents that time out or are stopped as cancelled instead of failed.
- [#3846](https://github.com/MoonshotAI/kimi-code/pull/3846) [`31f1b68`](https://github.com/MoonshotAI/kimi-code/commit/31f1b6824d1900aeea71b69c824c6354a0dd9019) Thanks [@7Sageer](https://github.com/7Sageer)! - Fix OAuth login never triggering for MCP servers that allow anonymous tool discovery but reject tool calls with 401.
- [#3784](https://github.com/MoonshotAI/kimi-code/pull/3784) [`5653c73`](https://github.com/MoonshotAI/kimi-code/commit/5653c739b9c0dbafd1e4b3d36edeb0fe44ec71f4) Thanks [@7Sageer](https://github.com/7Sageer)! - When accumulated images and videos exceed the request size budget, the oldest media are omitted from requests with a warning instead of failing.
- [#3778](https://github.com/MoonshotAI/kimi-code/pull/3778) [`7d174ac`](https://github.com/MoonshotAI/kimi-code/commit/7d174ac93352ccfc835d186ac7b8185d0194e6b5) Thanks [@huangzheng2016](https://github.com/huangzheng2016)! - Report tools and subagents interrupted by the user as cancelled instead of aborted or failed.
- [#3803](https://github.com/MoonshotAI/kimi-code/pull/3803) [`39a7455`](https://github.com/MoonshotAI/kimi-code/commit/39a74556a662a96293e9838663e91b31dbdc7a0b) Thanks [@kimi-agent-bot](https://github.com/kimi-agent-bot)! - The built-in browser plugin now appears as "Kimi Browser Extension" in the plugins panel, marketplace catalog, and docs, matching the product rename.
- [#3858](https://github.com/MoonshotAI/kimi-code/pull/3858) [`95d4a9d`](https://github.com/MoonshotAI/kimi-code/commit/95d4a9d0411b18f468345717c01c40f5ac996cdc) Thanks [@liruifengv](https://github.com/liruifengv)! - Sign the Windows CLI executable with a trusted publisher certificate.
- [#3795](https://github.com/MoonshotAI/kimi-code/pull/3795) [`a7bdabb`](https://github.com/MoonshotAI/kimi-code/commit/a7bdabbe82a089cd8631f6d613c252d96cbab639) Thanks [@Grapedge](https://github.com/Grapedge)! - Feedback surveys now appear at better times in long conversations.
- [#3840](https://github.com/MoonshotAI/kimi-code/pull/3840) [`9c5e9b4`](https://github.com/MoonshotAI/kimi-code/commit/9c5e9b48634be1dfae921fb48f9afc3a23ffd1ad) Thanks [@chengluyu](https://github.com/chengluyu)! - Fix a Windows crash when a project or config folder is opened through a short 8.3 path.
## 0.43.1
### Patch Changes
- [#3780](https://github.com/MoonshotAI/kimi-code/pull/3780) [`486dcd2`](https://github.com/MoonshotAI/kimi-code/commit/486dcd26c76f2854fc351515a45d8f0fc2d31ed6) Thanks [@Grapedge](https://github.com/Grapedge)! - Update the terminal UI engine, fixing link colors in wrapped markdown tables and `@` file-completion ordering, and adding native clipboard support on Linux X11.
- [#3776](https://github.com/MoonshotAI/kimi-code/pull/3776) [`48fad6e`](https://github.com/MoonshotAI/kimi-code/commit/48fad6e7d9cd00511c6118e84ae6d22dafb96be9) Thanks [@huangzheng2016](https://github.com/huangzheng2016)! - Fix memory not being released when subagent scopes are disposed.
- [#3777](https://github.com/MoonshotAI/kimi-code/pull/3777) [`dc76b0c`](https://github.com/MoonshotAI/kimi-code/commit/dc76b0caaa6e0aa406bf87957e6b5f8aca722221) Thanks [@huangzheng2016](https://github.com/huangzheng2016)! - Fix progressively slower rendering on each round of large agent swarm runs.
- [#3631](https://github.com/MoonshotAI/kimi-code/pull/3631) [`1336be3`](https://github.com/MoonshotAI/kimi-code/commit/1336be38777959cc558fed2b51dc53caa07da5db) Thanks [@wbxl2000](https://github.com/wbxl2000)! - Stop returning deleted sessions from global search before the search index catches up.
- [#3775](https://github.com/MoonshotAI/kimi-code/pull/3775) [`f248624`](https://github.com/MoonshotAI/kimi-code/commit/f2486241fb5e268d7a43cb589b5c2d59af8db571) Thanks [@huangzheng2016](https://github.com/huangzheng2016)! - Reduce event-loop stalls and GC churn in sessions with many concurrent subagents.
- [#3779](https://github.com/MoonshotAI/kimi-code/pull/3779) [`82ec469`](https://github.com/MoonshotAI/kimi-code/commit/82ec469e47eed52feb664fd361e3d8dcb774596e) Thanks [@huangzheng2016](https://github.com/huangzheng2016)! - Fix pressing Ctrl+C while subagents are running exiting the whole CLI instead of just interrupting the subagents.
import{casO,wasI,aasJ,fasP,basE,sasA}from"./chunk-RYQCIY6F-Csv6QbcC.js";import{_asw,amasv,anasD,aoasH,apasY,l,cas_,aqasW,aras$,agasj,asasq,ahasR,afasF,atasz,auasK,avasG}from"./mermaid.core-CgZTgv7Y.js";import{GasQ}from"./graph-DOmOIIwC.js";import{lasU}from"./layout-D-LzfAck.js";import"./map-DxJ2ADlA.js";import"./index-7SHxad1A.js";import"./_commonjsHelpers-CqkleIqs.js";varC=w((s,t,g)=>Math.max(t,Math.min(g,s)),"clamp"),B=w((s="TB")=>{switch(s){case"BT":return"bottom";case"LR":return"right";case"RL":return"left";case"TB":default:return"top"}},"getDefaultSelfLoopSide"),V=w(s=>s==="flowchart"||s==="flowchart-v2"||s==="stateDiagram","shouldMergeSelfLoopSegments"),Z=w((s,t,g,m,c)=>{consto=[],r=newSet;if(g.forEach(({start:i,end:n})=>{i!==m&&r.add(i),n!==m&&r.add(n)}),r.forEach(i=>{constn=s.node(i);typeofn?.x=="number"&&typeofn?.y=="number"&&o.push(n)}),o.length===0&&g.forEach(({edge:i})=>{(i.points??[]).forEach(n=>{typeofn?.x=="number"&&typeofn?.y=="number"&&o.push(n)})}),o.length===0)returnB(c);constf=o.reduce((i,n)=>({x:i.x+n.x/o.length,y:i.y+n.y/o.length}),{x:0,y:0}),h=f.x-t.x,a=f.y-t.y;returnMath.abs(h)>Math.abs(a)?h>0?"right":"left":Math.abs(a)>0?a>0?"bottom":"top":B(c)},"getSelfLoopSide"),ee=w((s,t="top",g=0,m=0)=>{constc=s.x,o=s.y-g,r=s.width/2,f=s.height/2,h=Math.max(36,Math.min(100,s.width*.8)),a=C(Math.max(m,s.width*.35),36,h),i=C(Math.min(s.width,s.height)*.45,24,48);switch(t){case"bottom":{constn=o+f;return[{x:c-a/2,y:n},{x:c-a/2,y:n+i},{x:c+a/2,y:n+i},{x:c+a/2,y:n}]}case"right":{constn=c+r;return[{x:n,y:o-a/2},{x:n+i,y:o-a/2},{x:n+i,y:o+a/2},{x:n,y:o+a/2}]}case"left":{constn=c-r;return[{x:n,y:o-a/2},{x:n-i,y:o-a/2},{x:n-i,y:o+a/2},{x:n,y:o+a/2}]}case"top":default:{constn=o-f;return[{x:c-a/2,y:n},{x:c-a/2,y:n-i},{x:c+a/2,y:n-i},{x:c+a/2,y:n}]}}},"getSelfLoopPoints"),te=w((s,t,g="top",m=0,c={})=>{constr=s.x,f=s.y-m,h=c.width??0,a=c.height??0;switch(g){case"bottom":return{x:r,y:Math.max(...t.map(i=>i.y))+a/2+4};case"right":return{x:Math.max(...t.map(i=>i.x))+h/2+4,y:f};case"left":return{x:Math.min(...t.map(i=>i.x))-h/2-4,y:f};case"top":default:return{x:r,y:Math.min(...t.map(i=>i.y))-a/2-4}}},"getSelfLoopLabelPosition"),ne=w((s,t=0,{mergeSelfLoops:g=!0}={})=>{constm=newMap,c=[],o=s.graph()?.rankdir;returns.edges().forEach(r=>{constf=s.edge(r);if(g&&f.selfLoop){consth=f.selfLoop.id;m.has(h)||m.set(h,[]),m.get(h).push({edge:f,start:r.v,end:r.w})}elsec.push({edge:f,start:r.v,end:r.w})}),m.forEach(r=>{if(r.length!==3){r.forEach(L=>c.push(L));return}r.sort((L,d)=>L.edge.selfLoop.order-d.edge.selfLoop.order);const[f,h,a]=r,i=f.edge.originalEdge??h.edge.originalEdge??a.edge.originalEdge??h.edge,n=s.node(i.start);if(!n){r.forEach(L=>c.push(L));return}constp={width:h.edge.width,height:h.edge.height},y=Z(s,n,r,i.start,o),X=ee(n,y,t,p.width??0),S=te(n,X,y,t,p),b={...h.edge,...i,id:i.id,points:X,start:i.start,end:i.end,x:S.x,y:S.y,width:p.width,height:p.height,labelStyle:h.edge.labelStyle,fromCluster:f.edge.fromCluster??h.edge.fromCluster??a.edge.fromCluster,toCluster:f.edge.toCluster??h.edge.toCluster??a.edge.toCluster};deleteb.selfLoop,deleteb.originalEdge,c.push({edge:b,start:b.start,end:b.end})}),c},"getEdgesToRender"),T=w(async(s,t,g,m,c,o)=>{l.warn("Graph in recursive render:XAX",I(t),c);constr=t.graph().rankdir;l.trace("Dir in recursive render - dir:",r);constf=s.insert("g").attr("class","root");t.nodes()?l.info("Recursive render XXX",t.nodes()):l.info("No nodes found for",t),t.edges().length>0&&l.info("Recursive edges",t.edge(t.edges()[0]));consth=f.insert("g").attr("class","clusters"),a=f.insert("g").attr("class","edgePaths"),i=f.insert("g").attr("class","edgeLabels"),n=f.insert("g").attr("class","nodes"),p=V(g);awaitPromise.all(t.nodes().map(asyncfunction(d){conste=t.node(d);if(c!==void0){constu=JSON.parse(JSON.stringify(c.clusterData));l.trace(`Setting data for parent cluster XXX
Node.id=`,d,`
data=`,u.height,`
Parentcluster`,c.height),t.setNode(c.id,u),t.parent(d)||(l.trace("Setting parent",d,c.id),t.setParent(d,c.id,u))}if(l.info("(Insert) Node XXX"+d+": "+JSON.stringify(t.node(d))),e?.clusterNode){l.info("Cluster identified XBX",d,e.width,t.node(d));const{ranksep:u,nodesep:x}=t.graph();e.graph.setGraph({...e.graph.graph(),ranksep:u+25,nodesep:x});const N=await T(n,e.graph,g,m,t.node(d),o),M=N.elem;W(e,M),e.diff=N.diff||0,l.info("New compound node after recursive render XAX",d,"width",e.width,"height",e.height),$(M,e)}else t.children(d).length>0?(l.trace("Cluster - the non recursive path XBX",d,e.id,e,e.width,"Graph:",t),l.trace(P(e.id,t)),E.set(e.id,{id:P(e.id,t),node:e})):(l.trace("Node - the non recursive path XAX",d,n,t.node(d),r),await j(n,t.node(d),{config:o,dir:r}))})),await w(async()=>{const d=t.edges().map(async function(e){const u=t.edge(e.v,e.w,e.name);if(l.info("Edge "+e.v+" -> "+e.w+": "+JSON.stringify(e)),l.info("Edge "+e.v+" -> "+e.w+": ",e," ",JSON.stringify(t.edge(e))),l.info("Fix",E,"ids:",e.v,e.w,"Translating: ",E.get(e.v),E.get(e.w)),p&&u.selfLoop){if(u.selfLoop.order!==1)return;const x=u.id;u.id=u.selfLoop.id,await G(i,u),u.id=x;return}await G(i,u)});await Promise.all(d)},"processEdges")(),l.info("Graph before layout:",JSON.stringify(I(t))),l.info("############################################# XXX"),l.info("### Layout ### XXX"),l.info("############################################# XXX"),U(t),l.info("Graph after layout:",JSON.stringify(I(t)));let X=0,{subGraphTitleTotalMargin:S}=q(o);await Promise.all(A(t).map(async function(d){const e=t.node(d);if(l.info("Position XBX => "+d+": ("+e.x,","+e.y,") width: ",e.width," height: ",e.height),e?.clusterNode)e.y+=S,l.info("A tainted cluster node XBX1",d,e.id,e.width,e.height,e.x,e.y,t.parent(d)),E.get(e.id).node=e,R(e);else if(t.children(d).length>0){l.info("A pure cluster node XBX1",d,e.id,e.x,e.y,e.width,e.height,t.parent(d)),e.height+=S,t.node(e.parentId);const u=e?.padding/2||0,x=e?.labelBBox?.height||0,N=x-u||0;l.debug("OffsetY",N,"labelHeight",x,"halfPadding",u),await F(h,e),E.get(e.id).node=e}else{const u=t.node(e.parentId);e.y+=S/2,l.info("A regular node XBX1 - using the padding",e.id,"parent",e.parentId,e.width,e.height,e.x,e.y,"offsetY",e.offsetY,"parent",u,u?.offsetY,e),R(e)}}));const b=S/2;return ne(t,b,{mergeSelfLoops:p}).forEach(function({edge:d,start:e,end:u}){l.info("Edge "+e+" -> "+u+": "+JSON.stringify(d),d),d.points.forEach(k=>k.y+=b);const x=t.node(e),N=t.node(u),M=z(a,d,E,g,x,N,m);K(d,M)}),t.nodes().forEach(function(d){const e=t.node(d);l.info(d,e.type,e.diff),e.isGroup&&(X=e.diff)}),l.warn("Returning from recursive render XAX",f,X),{elem:f,diff:X}},"recursiveRender"),le=w(async(s,t)=>{const g=new Q({multigraph:!0,compound:!0}).setGraph({rankdir:s.direction,nodesep:s.config?.nodeSpacing||s.config?.flowchart?.nodeSpacing||s.nodeSpacing,ranksep:s.config?.rankSpacing||s.config?.flowchart?.rankSpacing||s.rankSpacing,marginx:8,marginy:8}).setDefaultEdgeLabel(function(){return{}}),m=t.select("g");v(m,s.markers,s.type,s.diagramId),D(),H(),Y(),O(),s.nodes.forEach(o=>{g.setNode(o.id,{...o}),o.parentId&&g.setParent(o.id,o.parentId)}),l.debug("Edges:",s.edges),s.edges.forEach(o=>{if(o.start===o.end){const r=o.start,f=r+"---"+r+"---1",h=r+"---"+r+"---2",a=g.node(r);g.setNode(f,{domId:f,id:f,parentId:a.parentId,labelStyle:"",label:"",padding:0,shape:"labelRect",style:"",width:10,height:10}),g.setParent(f,a.parentId),g.setNode(h,{domId:h,id:h,parentId:a.parentId,labelStyle:"",padding:0,shape:"labelRect",label:"",style:"",width:10,height:10}),g.setParent(h,a.parentId);const i=structuredClone(o),n=structuredClone(o),p=structuredClone(o),y=structuredClone(o);n.originalEdge=i,n.selfLoop={id:i.id,order:0},p.originalEdge=i,p.selfLoop={id:i.id,order:1},y.originalEdge=i,y.selfLoop={id:i.id,order:2},n.label="",n.arrowTypeEnd="none",n.endLabelLeft="",n.endLabelRight="",n.startLabelLeft="",n.id=r+"-cyclic-special-1",p.startLabelRight="",p.startLabelLeft="",p.endLabelLeft="",p.endLabelRight="",p.arrowTypeStart="none",p.arrowTypeEnd="none",p.id=r+"-cyclic-special-mid",y.label="",y.startLabelRight="",y.startLabelLeft="",y.arrowTypeStart="none",a.isGroup&&(n.fromCluster=r,y.toCluster=r),y.id=r+"-cyclic-special-2",y.arrowTypeStart="none",g.setEdge(r,f,n,r+"-cyclic-special-0"),g.setEdge(f,h,p,r+"-cyclic-special-1"),g.setEdge(h,r,y,r+"-cyclic-special-2")}else g.setEdge(o.start,o.end,{...o},o.id)}),l.warn("Graph at first:",JSON.stringify(I(g))),J(g),l.warn("Graph after XAX:",JSON.stringify(I(g)));const c=_();await T(m,g,s.type,s.diagramId,void 0,c)},"render");export{ne as getEdgesToRender,le as render};
import{casO,wasI,aasJ,fasP,basE,sasA}from"./chunk-RYQCIY6F-MtH8gVIz.js";import{_asw,amasv,anasD,aoasH,apasY,l,cas_,aqasW,aras$,agasj,asasq,ahasR,afasF,atasz,auasK,avasG}from"./mermaid.core-AN7v7N47.js";import{GasQ}from"./graph-DOmOIIwC.js";import{lasU}from"./layout-D-LzfAck.js";import"./map-DxJ2ADlA.js";import"./index-Dnz6Zudc.js";varC=w((s,t,g)=>Math.max(t,Math.min(g,s)),"clamp"),B=w((s="TB")=>{switch(s){case"BT":return"bottom";case"LR":return"right";case"RL":return"left";case"TB":default:return"top"}},"getDefaultSelfLoopSide"),V=w(s=>s==="flowchart"||s==="flowchart-v2"||s==="stateDiagram","shouldMergeSelfLoopSegments"),Z=w((s,t,g,y,c)=>{consto=[],r=newSet;if(g.forEach(({start:i,end:n})=>{i!==y&&r.add(i),n!==y&&r.add(n)}),r.forEach(i=>{constn=s.node(i);typeofn?.x=="number"&&typeofn?.y=="number"&&o.push(n)}),o.length===0&&g.forEach(({edge:i})=>{(i.points??[]).forEach(n=>{typeofn?.x=="number"&&typeofn?.y=="number"&&o.push(n)})}),o.length===0)returnB(c);constf=o.reduce((i,n)=>({x:i.x+n.x/o.length,y:i.y+n.y/o.length}),{x:0,y:0}),h=f.x-t.x,a=f.y-t.y;returnMath.abs(h)>Math.abs(a)?h>0?"right":"left":Math.abs(a)>0?a>0?"bottom":"top":B(c)},"getSelfLoopSide"),ee=w((s,t="top",g=0,y=0)=>{constc=s.x,o=s.y-g,r=s.width/2,f=s.height/2,h=Math.max(36,Math.min(100,s.width*.8)),a=C(Math.max(y,s.width*.35),36,h),i=C(Math.min(s.width,s.height)*.45,24,48);switch(t){case"bottom":{constn=o+f;return[{x:c-a/2,y:n},{x:c-a/2,y:n+i},{x:c+a/2,y:n+i},{x:c+a/2,y:n}]}case"right":{constn=c+r;return[{x:n,y:o-a/2},{x:n+i,y:o-a/2},{x:n+i,y:o+a/2},{x:n,y:o+a/2}]}case"left":{constn=c-r;return[{x:n,y:o-a/2},{x:n-i,y:o-a/2},{x:n-i,y:o+a/2},{x:n,y:o+a/2}]}case"top":default:{constn=o-f;return[{x:c-a/2,y:n},{x:c-a/2,y:n-i},{x:c+a/2,y:n-i},{x:c+a/2,y:n}]}}},"getSelfLoopPoints"),te=w((s,t,g="top",y=0,c={})=>{constr=s.x,f=s.y-y,h=c.width??0,a=c.height??0;switch(g){case"bottom":return{x:r,y:Math.max(...t.map(i=>i.y))+a/2+4};case"right":return{x:Math.max(...t.map(i=>i.x))+h/2+4,y:f};case"left":return{x:Math.min(...t.map(i=>i.x))-h/2-4,y:f};case"top":default:return{x:r,y:Math.min(...t.map(i=>i.y))-a/2-4}}},"getSelfLoopLabelPosition"),ne=w((s,t=0,{mergeSelfLoops:g=!0}={})=>{consty=newMap,c=[],o=s.graph()?.rankdir;returns.edges().forEach(r=>{constf=s.edge(r);if(g&&f.selfLoop){consth=f.selfLoop.id;y.has(h)||y.set(h,[]),y.get(h).push({edge:f,start:r.v,end:r.w})}elsec.push({edge:f,start:r.v,end:r.w})}),y.forEach(r=>{if(r.length!==3){r.forEach(L=>c.push(L));return}r.sort((L,d)=>L.edge.selfLoop.order-d.edge.selfLoop.order);const[f,h,a]=r,i=f.edge.originalEdge??h.edge.originalEdge??a.edge.originalEdge??h.edge,n=s.node(i.start);if(!n){r.forEach(L=>c.push(L));return}constp={width:h.edge.width,height:h.edge.height},m=Z(s,n,r,i.start,o),X=ee(n,m,t,p.width??0),S=te(n,X,m,t,p),b={...h.edge,...i,id:i.id,points:X,start:i.start,end:i.end,x:S.x,y:S.y,width:p.width,height:p.height,labelStyle:h.edge.labelStyle,fromCluster:f.edge.fromCluster??h.edge.fromCluster??a.edge.fromCluster,toCluster:f.edge.toCluster??h.edge.toCluster??a.edge.toCluster};deleteb.selfLoop,deleteb.originalEdge,c.push({edge:b,start:b.start,end:b.end})}),c},"getEdgesToRender"),T=w(async(s,t,g,y,c,o)=>{l.warn("Graph in recursive render:XAX",I(t),c);constr=t.graph().rankdir;l.trace("Dir in recursive render - dir:",r);constf=s.insert("g").attr("class","root");t.nodes()?l.info("Recursive render XXX",t.nodes()):l.info("No nodes found for",t),t.edges().length>0&&l.info("Recursive edges",t.edge(t.edges()[0]));consth=f.insert("g").attr("class","clusters"),a=f.insert("g").attr("class","edgePaths"),i=f.insert("g").attr("class","edgeLabels"),n=f.insert("g").attr("class","nodes"),p=V(g);awaitPromise.all(t.nodes().map(asyncfunction(d){conste=t.node(d);if(c!==void0){constu=JSON.parse(JSON.stringify(c.clusterData));l.trace(`Setting data for parent cluster XXX
Node.id=`,d,`
data=`,u.height,`
Parentcluster`,c.height),t.setNode(c.id,u),t.parent(d)||(l.trace("Setting parent",d,c.id),t.setParent(d,c.id,u))}if(l.info("(Insert) Node XXX"+d+": "+JSON.stringify(t.node(d))),e?.clusterNode){l.info("Cluster identified XBX",d,e.width,t.node(d));const{ranksep:u,nodesep:x}=t.graph();e.graph.setGraph({...e.graph.graph(),ranksep:u+25,nodesep:x});const N=await T(n,e.graph,g,y,t.node(d),o),M=N.elem;W(e,M),e.diff=N.diff||0,l.info("New compound node after recursive render XAX",d,"width",e.width,"height",e.height),$(M,e)}else t.children(d).length>0?(l.trace("Cluster - the non recursive path XBX",d,e.id,e,e.width,"Graph:",t),l.trace(P(e.id,t)),E.set(e.id,{id:P(e.id,t),node:e})):(l.trace("Node - the non recursive path XAX",d,n,t.node(d),r),await j(n,t.node(d),{config:o,dir:r}))})),await w(async()=>{const d=t.edges().map(async function(e){const u=t.edge(e.v,e.w,e.name);if(l.info("Edge "+e.v+" -> "+e.w+": "+JSON.stringify(e)),l.info("Edge "+e.v+" -> "+e.w+": ",e," ",JSON.stringify(t.edge(e))),l.info("Fix",E,"ids:",e.v,e.w,"Translating: ",E.get(e.v),E.get(e.w)),p&&u.selfLoop){if(u.selfLoop.order!==1)return;const x=u.id;u.id=u.selfLoop.id,await G(i,u),u.id=x;return}await G(i,u)});await Promise.all(d)},"processEdges")(),l.info("Graph before layout:",JSON.stringify(I(t))),l.info("############################################# XXX"),l.info("### Layout ### XXX"),l.info("############################################# XXX"),U(t),l.info("Graph after layout:",JSON.stringify(I(t)));let X=0,{subGraphTitleTotalMargin:S}=q(o);await Promise.all(A(t).map(async function(d){const e=t.node(d);if(l.info("Position XBX => "+d+": ("+e.x,","+e.y,") width: ",e.width," height: ",e.height),e?.clusterNode)e.y+=S,l.info("A tainted cluster node XBX1",d,e.id,e.width,e.height,e.x,e.y,t.parent(d)),E.get(e.id).node=e,R(e);else if(t.children(d).length>0){l.info("A pure cluster node XBX1",d,e.id,e.x,e.y,e.width,e.height,t.parent(d)),e.height+=S,t.node(e.parentId);const u=e?.padding/2||0,x=e?.labelBBox?.height||0,N=x-u||0;l.debug("OffsetY",N,"labelHeight",x,"halfPadding",u),await F(h,e),E.get(e.id).node=e}else{const u=t.node(e.parentId);e.y+=S/2,l.info("A regular node XBX1 - using the padding",e.id,"parent",e.parentId,e.width,e.height,e.x,e.y,"offsetY",e.offsetY,"parent",u,u?.offsetY,e),R(e)}}));const b=S/2;return ne(t,b,{mergeSelfLoops:p}).forEach(function({edge:d,start:e,end:u}){l.info("Edge "+e+" -> "+u+": "+JSON.stringify(d),d),d.points.forEach(k=>k.y+=b);const x=t.node(e),N=t.node(u),M=z(a,d,E,g,x,N,y);K(d,M)}),t.nodes().forEach(function(d){const e=t.node(d);l.info(d,e.type,e.diff),e.isGroup&&(X=e.diff)}),l.warn("Returning from recursive render XAX",f,X),{elem:f,diff:X}},"recursiveRender"),ce=w(async(s,t)=>{const g=new Q({multigraph:!0,compound:!0}).setGraph({rankdir:s.direction,nodesep:s.config?.nodeSpacing||s.config?.flowchart?.nodeSpacing||s.nodeSpacing,ranksep:s.config?.rankSpacing||s.config?.flowchart?.rankSpacing||s.rankSpacing,marginx:8,marginy:8}).setDefaultEdgeLabel(function(){return{}}),y=t.select("g");v(y,s.markers,s.type,s.diagramId),D(),H(),Y(),O(),s.nodes.forEach(o=>{g.setNode(o.id,{...o}),o.parentId&&g.setParent(o.id,o.parentId)}),l.debug("Edges:",s.edges),s.edges.forEach(o=>{if(o.start===o.end){const r=o.start,f=r+"---"+r+"---1",h=r+"---"+r+"---2",a=g.node(r);g.setNode(f,{domId:f,id:f,parentId:a.parentId,labelStyle:"",label:"",padding:0,shape:"labelRect",style:"",width:10,height:10}),g.setParent(f,a.parentId),g.setNode(h,{domId:h,id:h,parentId:a.parentId,labelStyle:"",padding:0,shape:"labelRect",label:"",style:"",width:10,height:10}),g.setParent(h,a.parentId);const i=structuredClone(o),n=structuredClone(o),p=structuredClone(o),m=structuredClone(o);n.originalEdge=i,n.selfLoop={id:i.id,order:0},p.originalEdge=i,p.selfLoop={id:i.id,order:1},m.originalEdge=i,m.selfLoop={id:i.id,order:2},n.label="",n.arrowTypeEnd="none",n.endLabelLeft="",n.endLabelRight="",n.startLabelLeft="",n.id=r+"-cyclic-special-1",p.startLabelRight="",p.startLabelLeft="",p.endLabelLeft="",p.endLabelRight="",p.arrowTypeStart="none",p.arrowTypeEnd="none",p.id=r+"-cyclic-special-mid",m.label="",m.startLabelRight="",m.startLabelLeft="",m.arrowTypeStart="none",a.isGroup&&(n.fromCluster=r,m.toCluster=r),m.id=r+"-cyclic-special-2",m.arrowTypeStart="none",g.setEdge(r,f,n,r+"-cyclic-special-0"),g.setEdge(f,h,p,r+"-cyclic-special-1"),g.setEdge(h,r,m,r+"-cyclic-special-2")}else g.setEdge(o.start,o.end,{...o},o.id)}),l.warn("Graph at first:",JSON.stringify(I(g))),J(g),l.warn("Graph after XAX:",JSON.stringify(I(g)));const c=_();await T(y,g,s.type,s.diagramId,void 0,c)},"render");export{ne as getEdgesToRender,ce as render};
import{pasB}from"./chunk-JWPE2WC7-BxqMRrNb.js";import{_asb,Aasu,Das$,easC,lasm,basS,aasD,nasT,oasP,gasz,sasA,yasE,BasF,pasW}from"./mermaid.core-CgZTgv7Y.js";import{pas_}from"./cynefin-VYW2F7L2-DYKf_Z-v.js";import"./index-7SHxad1A.js";import"./_commonjsHelpers-CqkleIqs.js";varN=F.packet,w=class{constructor(){this.packet=[],this.setAccTitle=S,this.getAccTitle=D,this.setDiagramTitle=T,this.getDiagramTitle=P,this.getAccDescription=z,this.setAccDescription=A}static{b(this,"PacketDB")}getConfig(){constt=u({...N,...E().packet});returnt.showBits&&(t.paddingY+=10),t}getPacket(){returnthis.packet}pushWord(t){t.length>0&&this.packet.push(t)}clear(){W(),this.packet=[]}},L=1e4,M=b((t,e)=>{B(t,e);letr=-1,o=[],n=1;const{bitsPerRow:l}=e.getConfig();for(let{start:a,end:i,bits:d,label:c}oft.blocks){if(a!==void0&&i!==void0&&i<a)thrownewError(`Packet block ${a} - ${i} is invalid. End must be greater than start.`);if(a??=r+1,a!==r+1)thrownewError(`Packet block ${a} - ${i??a} is not contiguous. It should start from ${r+1}.`);if(d===0)thrownewError(`Packet block ${a} is invalid. Cannot have a zero bit field.`);for(i??=a+(d??1)-1,d??=i-a+1,r=i,m.debug(`Packet block ${a} - ${r} with label ${c}`);o.length<=l+1&&e.getPacket().length<L;){const[p,s]=Y({start:a,end:i,bits:d,label:c},n,l);if(o.push(p),p.end+1===n*l&&(e.pushWord(o),o=[],n++),!s)break;({start:a,end:i,bits:d,label:c}=s)}}e.pushWord(o)},"populate"),Y=b((t,e,r)=>{if(t.start===void0)thrownewError("start should have been set during first phase");if(t.end===void0)thrownewError("end should have been set during first phase");if(t.start>t.end)thrownewError(`Block start ${t.start} is greater than block end ${t.end}.`);if(t.end+1<=e*r)return[t,void0];consto=e*r-1,n=e*r;return[{start:t.start,end:o,label:t.label,bits:o-t.start},{start:n,end:t.end,label:t.label,bits:t.end-n}]},"getNextFittingBlock"),v={parser:{yy:void0},parse:b(asynct=>{conste=await_("packet",t),r=v.parser?.yy;if(!(rinstanceofw))thrownewError("parser.parser?.yy was not a PacketDB. This is due to a bug within Mermaid, please report this issue at https://github.com/mermaid-js/mermaid/issues.");m.debug(e),M(e,r)},"parse")},I=b((t,e,r,o)=>{constn=o.db,l=n.getConfig(),{rowHeight:a,paddingY:i,bitWidth:d,bitsPerRow:c}=l,p=n.getPacket(),s=n.getDiagramTitle(),h=a+i,g=h*(p.length+1)-(s?0:a),k=d*c+2,f=$(e);f.attr("viewBox",`0 0 ${k}${g}`),C(f,g,k,l.useMaxWidth);for(const[x,y]ofp.entries())O(f,y,x,l);f.append("text").text(s).attr("x",k/2).attr("y",g-h/2).attr("dominant-baseline","middle").attr("text-anchor","middle").attr("class","packetTitle")},"draw"),O=b((t,e,r,{rowHeight:o,paddingX:n,paddingY:l,bitWidth:a,bitsPerRow:i,showBits:d})=>{constc=t.append("g"),p=r*(o+l)+l;for(constsofe){consth=s.start%i*a+1,g=(s.end-s.start+1)*a-n;if(c.append("rect").attr("x",h).attr("y",p).attr("width",g).attr("height",o).attr("class","packetBlock"),c.append("text").attr("x",h+g/2).attr("y",p+o/2).attr("class","packetLabel").attr("dominant-baseline","middle").attr("text-anchor","middle").text(s.label),!d)continue;constk=s.end===s.start,f=p-2;c.append("text").attr("x",h+(k?g/2:0)).attr("y",f).attr("class","packetByte start").attr("dominant-baseline","auto").attr("text-anchor",k?"middle":"start").text(s.start),k||c.append("text").attr("x",h+g).attr("y",f).attr("class","packetByte end").attr("dominant-baseline","auto").attr("text-anchor","end").text(s.end)}},"drawWord"),j={draw:I},G={byteFontSize:"10px",startByteColor:"black",endByteColor:"black",labelColor:"black",labelFontSize:"12px",titleColor:"black",titleFontSize:"14px",blockStrokeColor:"black",blockStrokeWidth:"1",blockFillColor:"#efefef"},H=b(({packet:t}={})=>{conste=u(G,t);return`
.packetByte{
font-size:${e.byteFontSize};
}
.packetByte.start{
fill:${e.startByteColor};
}
.packetByte.end{
fill:${e.endByteColor};
}
.packetLabel{
fill:${e.labelColor};
font-size:${e.labelFontSize};
}
.packetTitle{
fill:${e.titleColor};
font-size:${e.titleFontSize};
}
.packetBlock{
stroke:${e.blockStrokeColor};
stroke-width:${e.blockStrokeWidth};
fill:${e.blockFillColor};
}
`},"styles"),J={parser:v,get db(){return new w},renderer:j,styles:H};export{J as diagram};
Some files were not shown because too many files have changed in this diff
Show more