* feat(agent-core-v2): accept HEIC, HEIF, and BMP images for Kimi models
Key the accepted image MIME set by the provider the agent is bound to
instead of a single global allowlist: Kimi accepts PNG/JPEG/GIF/WebP plus
BMP/HEIC/HEIF, every other provider keeps the baseline set. The provider
image policy lives in the human layer (`llm/media/image-formats`) so both
the agent-side gates and the Anthropic protocol lowering share it.
- thread the provider type through every ingestion gate: ReadMediaFile,
prompt entry, MCP tool results, session media references, kap-server
prompt/skill routes; expose it via IAgentProfileService.getModelProviderType
- ReadMediaFile sends accepted formats it cannot re-encode locally inline
up to the provider's per-image budget (5MB for Kimi, 3.75MB otherwise)
and points at a conversion command above that; full_resolution uses the
same provider budget
- acp-server no longer gates formats itself and leaves the decision to the
engine prompt pipeline, which knows the bound provider
- the Anthropic lowering allowlist follows the bound provider so Kimi over
the Anthropic-compatible protocol no longer rejects HEIC client-side
* fix(agent-core-v2): resolve the image format policy on the production paths
Follow-ups from review of the provider-aware image formats:
- the Anthropic lowering read the policy from `LlmModel.provider`, which
the adapter registry sets to the route id (`anthropic`) rather than the
model's provider type, so Kimi over the Anthropic-compatible protocol
still rejected HEIC client-side; the accepted set now comes from a
`ProtocolTrait.acceptedImageMimes` hook that the Kimi traits implement,
and the test goes through the real trait with the routed provider id
- kap-server prompt ingestion gated media against the currently bound
model even when the same request selected another model; the profile
service now resolves the provider type for a requested alias
- the session media memo served a cached inline image to a provider that
does not accept its format; memo hits are re-judged against the current
provider before being returned
- the media tools registrar keyed ReadMediaFile only by alias and
capabilities, so a reload that changed the alias's provider type or
protocol kept the stale tool; both now take part in the key
* test(kap-server): keep the optional chain the lint autofix dropped
* refactor(interaction): replace per-agent InteractionService with human-native session facade
- add human/interaction: global interaction machine + tag-query facade (enqueue/respond/findAll/findOne/wait)
- add session-scope SessionInteractionService owning one interaction actor per session
- rewire approval/question/userTool/sessionActivity to the session facade and delete features/interaction
* refactor(interaction): rewire kap-server, klient and node-sdk to the session interaction facade
- kap-server routes, ws broadcaster and transcript binding read ISessionInteractionService (tags replace origin)
- klient memory dispatcher delegates to the session service; zod contract mirrors origin -> tags
- node-sdk session wiring subscribes the session service directly
- register interaction wire events in their new home and regenerate the wire manifest
* refactor(agent-core-v2): replace interaction DI services with a global human singleton
resolve rebase conflicts
- scrub all ambient KIMI_CODE_* env vars in the agent-core-v2 and
kap-server test setups so developer shell env cannot leak in
- drop the load-sensitive stepRetry fake-timer cases and the three
FSEvents-based skill catalog watch cases
- give node-sdk, klient and the vscode extension project an explicit
15s testTimeout matching their integration-test nature
- retry temp-dir removal in taskManager teardown to absorb async
terminal-effect writes
* fix(telemetry): stop silently dropping key event attributes
- turn_ended now carries error_type (the engine error code) when a turn
fails, matching the v2 event catalog definition that no emitter set
- TelemetryClient reports properties dropped by sanitizeProperties via a
new onUnexpectedError hook, wired to the CLI, web, and v2-print logs
- session_started client attribution keys emit empty strings instead of
null so they survive payload flattening
* fix(telemetry): sanitize at sink accept time and guard the drop handler
Addresses review on #3638:
- Properties are sanitized when the sink accepts the event (at track time
on the direct path, at drain time for the pre-init queue), so drops on
events queued before initialization are reported once
onUnexpectedError is installed
- The onUnexpectedError hook is invoked behind a guard, mirroring the
agent-core-v2 unexpectedError path, so a faulty host handler cannot
abort track()
* fix(telemetry): snapshot queued properties at track time
Addresses review on #3638: the pre-init queue now stores the sanitized
snapshot taken at track time (only primitives survive, so the snapshot
is exact), and the dropped keys ride along as pending diagnostics that
are replayed once the handler is installed. Callers mutating a reused
properties object between track() and initialization can no longer
alter the emitted event or its drop reports
* feat(kimi-code): two-line collapsed tool cards with width-aware headers
Collapsed cards now show the call on line one and a single dim outcome
row on line two: a Bash command's last output line (the live tail while
running) with a line-count chip, a Grep/Glob path sample, or a generic
tool's first output line. Read groups stay header-only and honor the
global expand state; failed calls keep their preview. Headers split into
head / flexible / tail segments so a long command fills the terminal
width before the chip.
* feat(kimi-code): NotifyUser tool and mid-turn update panel
Add an experimental NotifyUser tool (flag notify_user, off by default)
that agent-core-v2 offers only when the host declares the update_panel
UI capability at bootstrap; the TUI declares it through the SDK harness
options. The system prompt's guidance to send updates is injected only
when the tool is active for the agent. The TUI renders the turn's
updates in a panel above the editor (stacked, 8-row window, Ctrl+N pages
back) that the next turn clears, and keeps a one-line card in the
transcript.
* feat: add subagent updates and bidirectional paging
* fix: preserve session tools and prompts when toggling updates
* fix: correct notification delivery and CI regressions
* feat(secondary-model): drop the experimental flag from the subagent model pool
* feat(secondary-model): sync the web bundle without the experimental gate
* feat(agent-core-v2): anchor compaction resumption on the latest user message
* fix(agent-core-v2): count the continuation anchor in tokensAfter
* fix(agent-core-v2): simplify the continuation anchor to a single statement
* fix(agent-core-v2): avoid absolute claims in the compaction continuation texts
* fix(agent-core-v2): flag the continuation anchor in compaction records so old records replay unchanged
* fix(agent-core-v2): replay the continuation anchor unconditionally, drop hasContinuation
The anchor is an injection (policy, not history): like the system prompt
and other reminders, it is re-rendered by current code at replay time, so
pre-anchor compaction records gain it on resume. The divergence is
self-limiting (only records written before this change, absorbed on first
resume) and needs no per-record format flag. Fold-length accounting counts
the anchor unconditionally to stay at parity with the rebuilt context.
* fix(vis): align the context projector with the compaction continuation anchor
Locate the compaction summary by origin instead of assuming it is the last
shape message (the anchor now follows it), and pin the anchor in the
projection expectations.
* feat(tui): show each background agent's model in the /tasks list
* fix(agent-core-v2): carry model and thinking effort in tower spawn task metadata
* fix(tower): honor subagent timeout config for tower spawn tasks
TowerSpawnTool registered its detached subagent task with the hardcoded
DEFAULT_SUBAGENT_TIMEOUT_MS, bypassing the [subagent] timeout_ms setting
and KIMI_SUBAGENT_TIMEOUT_MS. Resolve the timeout through
resolveSubagentTimeoutMs(config) like AgentTool does, keeping the 2h
default when nothing is configured.
* test(agent-core-v2): deflake the sessionIndex perf baseline under load
* feat(agent-core-v2): carry user-verbatim mission context through tower briefings
TowerPlan missions accept an optional context field holding the user's
own key sentences verbatim, rendered into the mission file and every
briefing that embeds it. Reviewer briefings now include the full mission
text and the author's review-request, with an intent-first checklist;
worker briefings escalate substantive ambiguity to the tower before
acting and reconcile completion reports task by task.
* chore: merge tower changesets into one
* refactor(tui): narrow tasks-browser host appState with Pick
* docs(agent-core-v2): correct the subagent timeout description in the Agent tool
* docs(agent-core-v2): drop the timeout bullet from the Agent tool description
---------
Co-authored-by: konghuanjun <konghuanjun@moonshot.ai>
* feat(kimi-code): two-line collapsed tool cards with width-aware headers
Collapsed cards now show the call on line one and a single dim outcome
row on line two: a Bash command's last output line (the live tail while
running) with a line-count chip, a Grep/Glob path sample, or a generic
tool's first output line. Read groups stay header-only and honor the
global expand state; failed calls keep their preview. Headers split into
head / flexible / tail segments so a long command fills the terminal
width before the chip.
* feat(kimi-code): show short tool output whole and point at hidden output
Collapsed cards now show up to three output lines before falling back to
a single outcome row, the Grep chip counts files or matches according to
output_mode, and the tools' pagination and empty-result notices no longer
count as results. While the recent turns hold tool output that ctrl+o
would reveal or hide, the footer shows ctrl+o expand or ctrl+o collapse.
* feat(kimi-code): mark hidden tool output with counts and direction
A collapsed Bash card's chip now counts the hidden lines (`N more
lines`), its last-line outcome row carries a leading ellipsis, a generic
tool's first-line row a trailing one, and the Grep glance keeps its
"+N more" count in the fixed tail so width cuts drop samples, never the
count. Background Bash results identify the task by their first
metadata line instead of trailing internal hints, and header truncation
now treats ANSI escapes as atomic zero-width units and stays bounded by
the terminal width for huge arguments.
* fix(kimi-code): keep tool notices and context rows out of result counts
Glob's timeout, truncation, and warning lines no longer inflate the
file count or pose as glance samples, and unnumbered Grep content with
context flags falls back to an exact file count instead of claiming
matches it cannot distinguish. The footer's ctrl+o hint now also
appears when an outcome row is cut by the terminal width — one to three
very long lines hide the remainder that ctrl+o reveals wrapped.
* fix(kimi-code): align the ctrl+o hint with what expansion reveals
The Edit check now uses the same clustered diff render as the preview
(context rows and inter-hunk separators count toward the cap), and an
ExitPlanMode outcome card no longer reports hidden content — the plan
is fully rendered by the call preview and its result body is
expansion-independent. OUTCOME_MAX_LINES moves to the TUI constant
directory with the other shared rendering limits.
* fix(kimi-code): tighten the collapsed-card hidden-content signals
Outcome rows drop terminal control sequences before they are cut, a
failed Bash card leaves the hidden-line count to its preview trailer,
an unnumbered Grep glance lists each file once, a solo subagent card
never reports hidden content, and the glance sample cap joins the other
collapsed-card limits in the rendering constants.
* fix(kimi-code): count paginated Grep totals and wrapped error previews as hidden
The Grep chip and glance use the tool's count-mode summary and pagination
total instead of the current page, Windows drive letters stay inside
unnumbered content paths, Glob's ripgrep stderr continuation lines no
longer count as files, a background question follows the line-count rule,
and an error preview whose long line wraps past its row cap keeps the
footer's ctrl+o hint on.
* fix(kimi-code): treat spilled tool output as an envelope, not as results
An oversized result reaches the TUI as agent-core's truncation envelope;
cards now render its first line as the outcome row and carry no chip
instead of counting its metadata as files or lines. The Grep chip keeps
the count-mode totals on an empty page, and the Bash chip counts rows
the way the outcome rows do, so whitespace-only rows never claim hidden
lines.
* fix(kimi-code): widen the hidden-content signal to failed previews and ! cards
A paginated content search reports the tool's match total, a capped Edit
or Write preview counts as hidden even when the call failed, and a
user-run ! command card tells the footer about its running tail or
capped result.
* fix(kimi-code): keep result chips honest on incomplete, narrow, and cut-off cards
Grep and Glob counts read as lower bounds (`12+ files`) when the tool
reports a timeout or output cap, a header too narrow for its fixed parts
drops the middle and cuts the head before the chip, and a call whose
arguments were cut off by max_tokens no longer claims hidden content.
* fix(kimi-code): stop the expand hint lying on goal cards, ! cards, and paginated context
A pagination total only stands in for the match count when no context
flag is set, a ! command card that finished while expanded still counts
its rows past the preview cap, parsed goal snapshots and bodiless goal
updates report nothing to expand, header fitting measures graphemes
instead of trusting code-unit length, and the escape pattern and tail
window join the rendering constants.
* fix(kimi-code): read zero context flags as none and mirror the Edit cap exactly
A Grep call with -A/-B/-C set to zero produces no context rows, so its
matches stay countable; the Edit hidden-content check now renders the
preview capped and uncapped and compares them, so a body that fills the
cap exactly no longer counts as cut.
* fix(kimi-code): keep the collapse hint for expanded cards outside the window
Toggling ctrl+o off collapses every expanded card, including one that
slid before the three-turn cutoff since it was expanded, so the footer
now keeps offering collapse while any expanded card hides content. Grep
context detection follows the backend's -C precedence, where a defined
-C makes -A and -B moot.
* fix(kimi-code): render Edit and Write results the same way in both states
Their one-line success acknowledgements repeat what the header, chip and
preview already show, so they render in neither state; any other
successful output shows as an outcome row in both. The footer's expand
hint therefore depends only on the capped preview for these cards.
* fix(kimi-code): keep a Read group's failure count on narrow rows
The finished header is three segments — label, line count, `· N failed`
tail — so a narrow row drops the line count before the failure count,
which is the only sign of partial failure while the per-file body is
collapsed.
* fix(kimi-code): show cut-short empty searches and keep the hint with a status command
A Grep or Glob the tool cut short before any row renders its notice
instead of an exact-looking empty result and carries no chip, a
ReadMediaFile result that is not a media envelope follows the line-count
rule, and the footer's ctrl+o hint moves to line 2 when a
status_line.command owns line 1.
* fix(kimi-code): count unreadable directories as an incomplete Glob and stop hinting on notice-only searches
A Glob that skipped unreadable directories reports its file count as a
lower bound, and a search cut short before any row — which renders only
the tool's notice, the same way in both states — no longer makes the
footer offer ctrl+o.
* fix(kimi-code): let the ctrl+o hint displace an inline tip and count parsed WaitFor results as hidden
An inline tips slot gives way when the fixed hint would not fit beside
it, and a parsed WaitFor result — whose raw fields only render when
expanded — reports hidden content instead of falling to the line-count
rule.
* fix(kimi-code): keep spill pointers and sensitive-only notices out of counts and honor streaming previews
The per-line truncation pointer agent-core appends to an oversized
result stays out of line and file counts, byte sizes and outcome rows; a
search whose only matches were filtered as sensitive files shows the
tool's notice instead of an exact-looking empty result; and while
arguments still stream, only a Bash card (whose partial command appears
once expanded) reports hidden content.
The decode-window split measured part arrival at the moment the event
loop resumed the pending await, so any loop-busy work (rendering,
timers, GC) between parts was reported as server time. Measure loop
utilization across the decode window with perf_hooks and split it into
loop-idle wait (server), per-part processing (client), and a new
clientBlockedMs bucket (loop busy with other work), carried through to
the step events, wire record, transcript contract, and the TUI debug
timing line.
- remote-control is always available: kimi rc, kimi web --remote-control and /remote-control no longer require KIMI_CODE_EXPERIMENTAL_REMOTE_CONTROL or the master flag
- add the [database] config section with base (session-index read model) and search (global search worker) kill switches, both defaulting to true
- rename the kill-switch env vars to KIMI_CODE_PERSISTENCE_MINIDB_READMODEL and KIMI_CODE_SEARCH_WORKER (precedence: env > config > default)
* refactor: remove the legacy agent-core v1 package
Delete packages/agent-core (v1 engine) and migrate node-sdk fully onto
agent-core-v2:
- node-sdk owns its public contract (error protocol, KimiConfig schema
and file IO, process-global logging facade, telemetry client
interface); event and interaction types re-export from
@moonshot-ai/protocol or agent-core-v2
- createKimiHarness is now the single harness factory, backed by the
v2 client; the v1 SDKRpcClient is deleted
- CLI and vscode run agent-core-v2 unconditionally;
KIMI_CODE_LEGACY_FLAG and useAgentCoreV1 are removed
- the wire.jsonl replay fold moves into agent-core-v2's replayBuilder
(foldWireRecords), and the v2 journal now persists tool-call display
so replay restores diff/todo displays
- TUI engineV2 branches collapse to the v2 path
* test(vscode): retry temp-dir teardown in replay-resume integration test
* refactor(agent-core-v2): absorb the human layer into src/human and rename vendored kosong to llm-adapter
* feat(agent-core-v2): preserve media attachment names in human message contract
* refactor(agent-core-v2): remove llm-adapter code duplicated in human/llm
* refactor(agent-core-v2): replace the agent loop with a facade over the human turn state machine
* refactor(agent-core-v2): rename the loop adapter layer to loop/machine
* test(acp-server): implement gateway resolve in the scripted provider stub
* refactor(agent-core-v2): restore v2 batch semantics in the loop machine tools adapter
- delegate each step's tool calls to the v2 tool executor as one whole
batch: beginBatch arms the expected call set from the llm.done entry,
per-actor execute calls collect into a pending barrier, and the full
batch is submitted in tool-call order once complete
- drop the serial batchChain/batchStopped/skipped approximation so the
executor's prepare-stage stopBatchAfterThis skip, veto short-circuit,
and ToolScheduler access-based concurrency apply natively again
* fix(build): update flake pnpmDeps hash for the post-absorb lockfile
* fix(agent-core-v2): align loop facade reservations with the machine queue by id
* fix(agent-core-v2): drain persisted wire records before turn end
* fix(agent-core-v2): route executor-level tool errors to the loop error path and backfill aborted tool results on forced abort
* fix(agent-core-v2): keep turn step retrying in the facade step numbering space
`kimi web --host 0.0.0.0` (or `--host ::`) auto-opened the browser on the
wildcard bind address, which is not navigable. Open localhost on the same
port instead, matching the ready banner's Local: line. Also make the ready
banner's port parsing tolerate the `::` wildcard origin, which crashed
`new URL()` at startup.
Co-authored-by: kimi-agent-bot <kimi-agent-bot@users.noreply.github.com>
Co-authored-by: qer <wbxl2000@outlook.com>
* fix(kimi-code): flush wire journals before print-mode exit
A print-mode turn's tail records (step.end / turn.ended / prompt.completed)
are dispatched fire-and-forget and reach the journal only through the wire
service's async persist queue. The print cleanup path never awaited that
queue: with telemetry disabled (KIMI_DISABLE_TELEMETRY=1) cleanup returns
in microseconds, and process.exit on the error path cut off the pending
append-log flush, dropping the failed turn's closing records. Flush every
session agent's dispatcher (which awaits the wire persist queue and the
append-log store) before disposing the app, bounded by the shutdown
timeout and best-effort so a persist failure never masks the turn outcome.
* fix(kimi-code): settle each agent's wire flush independently
Promise.all rejects as soon as one agent's flush fails; the best-effort
caller then proceeds to app.dispose() and process.exit while the remaining
agents' flushes are still in flight, re-exposing their journals to the
truncation this cleanup is meant to prevent. Await every flush with
Promise.allSettled instead (matches the drain helper's convention), and
cover it with a two-agent regression test.
* fix(kimi-code): run print-mode shutdown phases concurrently
The wire flush, v2 telemetry shutdown, and v1 telemetry shutdown each hold
a 3s allowance; run sequentially they can take 9s, past the 8s outer
cleanup bound — the caller's process.exit would then cut off the tail
(app.dispose included). The phases are independent, so await them
concurrently (worst case one allowance) while keeping the v2 shutdown
failure propagation and the best-effort semantics of the other two.
* fix(kimi-code): quiesce active turns before the print-mode wire flush
A termination signal (SIGINT/SIGTERM/SIGHUP) can arrive mid-turn: the
cleanup ran the wire flush immediately, while the still-running turn only
produced its cancellation and closing records from dispose()'s
fire-and-forget teardown — after the flush, and after the signal handler's
process.exit. Cancel every session agent's queued and active turns and
await loop idleness before flushing (best-effort, bounded by the shutdown
timeout); idle loops make this a no-op on the normal exit paths. Torn-down
agent scopes are skipped defensively, mirroring the flush's per-agent
settlement.
* fix(kimi-code): drain prompts and await prompt completion in print quiesce
Two follow-ups from review:
- The loop settles (releaseActiveTurn) before the prompt-settle chain
dispatches prompt.completed, so awaiting loop idleness alone let the
wire flush race the final record. Quiesce now also drains each agent's
prompt queue and awaits the tracked prompt completions (registered at
enqueue time) before the flush runs.
- Restore drainBackgroundTasks to Promise.all: a previous edit
unintentionally switched it to allSettled alongside the per-agent wire
flush, silently discarding persistence failures from
suppressTerminalNotification()/wait(). Only the independent wire
flushes are meant to be best-effort.
* fix(kimi-code): cover every agent's prompt queue in print quiesce
Tracking only the main run's prompt completion left subagent and
background prompts racing the wire flush the same way: the loop reports
idle before that agent's prompt-settle chain dispatches prompt.completed.
Replace the per-handle completion tracking with a uniform wait — after
draining and cancelling, poll every agent's prompt queue snapshot until
none reports an active or pending prompt. settle() clears the active
prompt and dispatches the record in one synchronous block, so an empty
snapshot proves the record is already queued for the flush.
* fix(agent-core-v2,kimi-code): cover the prompt launch window in print quiesce
A termination signal can also arrive while startNext() is mid-launch: the
prompt has left pending and is not active yet, so drain() cannot cancel it
and an active/pending snapshot reads empty. Quiesce would then flush and
exit while the launch still dispatches records afterward.
Expose the service's launching phase on PromptQueueSnapshot and make the
print cleanup's quiesce a repeat-until-idle loop: every pass drains,
cancels, and awaits the loops, then re-checks launching/active/pending, so
a prompt surfacing from the launch window is cancelled on the next pass
instead of escaping the flush.
* fix(kimi-code): freeze loop producers across the print-mode flush
An empty prompt queue is only a point-in-time observation: background
task completions and cron fires enqueue straight into the loop, bypassing
the prompt queue, so a late producer could still start a turn — and new
records — after the journals were flushed. Quiesce now leaves a
quiescence guard held on every loop once queues read empty; the caller
holds the release across the wire flush and app disposal, so late
submissions queue behind the guard and are rejected by disposal instead
of racing process.exit. Loops that refuse the guard (still busy) send the
quiesce into another drain/cancel/settle pass.
* fix(agent-core-v2): flush the wire journal when removing an agent
AgentLifecycleService.remove() quiesced the agent but never flushed its
wire journal: the records reached the append log only through the
fire-and-forget retirement flush, so a process exiting right after a
subagent's removal (e.g. print mode on a termination signal) could
truncate the closing records. Flush the agent's event dispatcher after
the quiesce and before disposal; a persist failure is reported without
blocking the removal.
* fix(agent-core-v2): wait for the prompt queue to go idle before flushing a removed agent
The remove() flush could still snapshot the persistence queue before the
cancelled prompt's final record was appended: loop.settled() resolves in
releaseActiveTurn() before the prompt-settle chain dispatches
prompt.aborted/prompt.completed, and prompt.drain() does not await that
settlement. Wait (bounded) for the prompt queue to report no launching,
active, or pending prompts before flushing; an unreadable snapshot counts
as idle so a wedged service cannot stall the removal.
* chore: widen the print wire-flush changeset to termination signals
* fix(agent-core-v2): re-cancel prompts that finish launching during removal
A prompt mid-launch (startNext awaiting daemon materialization or a
pre-submit hook) is invisible to drain() and has no turn to cancel yet;
the passive idle wait would then let it run to the deadline and flush
before its closing records existed. Repeat drain/cancel/settle on every
pass until the queue reads idle, and once idle hold a quiescence guard
across the flush and disposal so a late producer cannot start new work
in between.
* fix(kimi-code): stop task producers before the print-mode wire flush
Task termination bypasses both the prompt queue and the loop quiescence
guard: AgentTaskService dispatches TaskTerminated straight to the wire,
and disposal would force-stop still-running tasks after the journals were
already flushed. Stop every session agent's tasks up front in the quiesce
phase (mirroring AgentLifecycleService.remove()), so each task's
termination record is dispatched before the flush instead of racing
process.exit. keepAliveOnExit tasks stay exempt, matching remove().
* fix(agent-core-v2): do not let suppression failure short-circuit stopAllOnExit
A detached task's terminal-notification suppression failing (e.g. the
persist write rejects) used to reject the whole stopAllOnExit before
stopAll() ran, so every task stayed active — and callers settling the
rejection (print-mode cleanup, agent removal) proceeded as if the tasks
were stopped, losing their termination records at exit. Settle each
suppression independently, log the failure, and always stop the tasks.
keepAliveOnExit tasks remain exempt by design.
* chore(kimi-code,agent-core-v2): halve print wire-flush tests and trim comments
- rename the mode commands to /yolo and /auto (formerly
/ask-when-needed and /never-ask) and drop their on/off arguments
- running either command now opens the permission mode list with the
corresponding mode preselected; Enter confirms the switch
- decouple the choice picker's initial cursor from the current-value
marker via a new initialValue option
- after a mode switch, show the mode-specific description as the yellow
status line instead of the generic unconfirmed-changes warning
* refactor(agent-core-v2): remove the staleGuard feature
Drop the read-before-edit runtime guard: Edit/Write executions are no
longer vetoed when the target file was never read or its mtime changed
since the last read, and successful Read/Edit/Write no longer refresh a
recorded mtime. Removes the staleGuard replayable state key and the
staleGuard.recorded / staleGuard.cleared durable wire events; old wires
keep replaying through the unknown-type skip path. apps/vis keeps
projecting and rendering those historical records via locally declared
legacy record types.
* fix(agent-core-v2): skip retired wire record types silently during restore
Restore reports every journal record whose type has no registered event
class through onUnexpectedError. Sessions written before the staleGuard
removal can hold a staleGuard.recorded entry per successful
Read/Edit/Write, so loading one floods the log with WireError stacks.
Keep a retired-type list of record types that were once durable
vocabulary; restore skips them without reporting, while genuinely
unknown records stay on the error path.
* fix(telemetry): deduplicate session_started and model switch events
* fix(telemetry): keep engine session_started for direct v2 SDK clients and avoid model_switch race
* fix(telemetry): preserve model_switch when activation rebinds the same alias
* fix(telemetry): route TUI reload through harness
* test: update /reload message-flow test for harness reload route
* fix(kimi-code): honor KIMI_DISABLE_TELEMETRY and restore crash telemetry in v2 print mode
* fix(kimi-code): attribute v2 print crash telemetry to the resolved session model
* refactor(agent-core-v2): rebuild telemetry context as a scope-bound layered registry
* fix(agent-core-v2): prefer ambient model in telemetry envelope and repair print-runner stub
* fix(agent-core-v2): delete only the fragment registered by this telemetry binding
* fix(agent-core-v2): bind telemetry context writes to the binding's own fragment
* fix(agent-core-v2): keep stale telemetry emissions on the binding-owned fragment
* refactor(agent-core-v2): mirror turn-request trace_id into ambient telemetry context
* refactor(agent-core-v2): telemetry key-resolution cleanups
Drop the dead defineAgentTelemetryEvent branch in declaredKeysFor: agent_id is already copied unconditionally as identity plumbing before the declared-key loop, and the loop itself skips agent_id, so the branch had no runtime effect. Name the wire session id property key (WIRE_SESSION_ID_PROPERTY) so the session_id-to-sessionId mapping is explicit.
* refactor(agent-core-v2): flow ambient telemetry context to every event unconditionally
Remove the registry-key filtering and the FILTERED_CONTEXT_KEYS hard
filter from composeTelemetryProperties: the merged ambient now reaches
every event's properties, with session_id still remapped to the
camelCase sessionId key and explicit defined values winning on
collision. Explicit undefined values no longer clobber ambient fields.
With filtering gone, correctness for time-varying fields rests on
lifecycle discipline, so the loop now clears the ambient trace_id when
a step starts (mirroring the activeRequestTrace reset) instead of
relying on an explicit undefined payload value to mask the stale
mirror.
* refactor(agent-core-v2): move thinking_effort into the turn telemetry context
* refactor(agent-core-v2): replace the telemetry fragment registry with parent references
* feat(kimi-code): show file-change warning when switching to ask-when-needed or never-ask mode
* Update permission-mode-file-warning.md
Signed-off-by: 7Sageer <sag77r@hotmail.com>
* feat(kimi-code): show file-change warning on goal and swarm permission switches
* fix(kimi-code): indent every line of multi-line notice details
* feat(kimi-code): render the permission-mode file-change warning in warning color
* refactor(kimi-code): render the file-change warning via showStatus
* fix(kimi-code): defer the goal permission-switch notice until the goal starts
* refactor(kimi-code): move the file-change warning copy into the TUI constant directory
---------
Signed-off-by: 7Sageer <sag77r@hotmail.com>
* fix(migration-legacy): faithfully migrate kimi-cli user data into kimi-code
* fix(migration-legacy): faithfully migrate kimi-cli user data into kimi-code
* fix(vscode): keep current and root work dirs visible in directory picker
* refactor(vscode): migrate the webview data layer from ahooks to TanStack Query
* refactor(vis): migrate vis from agent-core v1 to agent-core-v2
- swap vis-server engine deps to @moonshot-ai/agent-core-v2, drop kosong
- rebuild the wire record union from v2 durable payload types
- fold token_counting records into the context-window fill
- read cron state from wire records alongside legacy cron files
- read agent parent/swarm metadata from state.json labels
- align context projection and web rendering with the v2 wire vocabulary
- bundle agent-core-v2 into the vis-server build
* fix(vis): replay legacy compactions per engine rules, tolerate bad cron payloads
- treat every record without keptUserMessageCount (or with an explicit
legacyTail flag) as a verbatim-tail compaction, matching the engine's
readContextCompactionShapeInput instead of gating on compactedCount
- derive tokensAfter from the reconstructed shape when the record omits
it, instead of keeping the stale pre-compaction fill
- skip malformed cron.delete / cron.cursor payloads instead of throwing
* feat(cli): add the kimi session list subcommand
- add the session command group with list: cwd scope by default, --all/--cwd/--archived/--limit/--json
- pass includeArchived through the SDK listSessions options (v2 klient query, v1 includeArchive mapping)
* docs: condense the session list changeset to one sentence
* fix(cli): sanitize session list rows and tidy option types
- strip control characters and line breaks from user-controlled row fields
- declare cwd/limit as plain optional properties without undefined unions
* fix(agent-core-v2): persist plan.revision as agent-relative key
- replace the session-scoped `path` field in durable plan.revision records
with an agent-relative `key`, resolved to a display path at read and
projection time against the current agent/session scope
- migrate legacy `path` records during wire restore and rewrite the journal
once migration succeeds, emitting low-cardinality
`wire_plan_revision_migrated` telemetry
- tolerate journal truncation during the migration rebuild and keep the
legacy `path` display in cold transcript folds for unmigrated records
- keep external transcript marker `path` and meta `reviewPath` projections
unchanged
* perf(agent-core-v2): fork sessions without materializing the target
- copy session files byte-for-byte (wire.jsonl included, parallel file
writes) and append the forked marker per agent instead of parsing and
rewriting every wire journal
- write the forked session's state.json, agent registry, and session
index entry directly without creating a live session or agent scopes;
fork returns SessionMeta and SessionForkedEvent drops the scope handle
- keep turnIndex truncation on the per-record path
- kap-server fork/createChild routes, the klient facade and wire
contract, and the node-sdk forkSession resume the forked session
through the normal resume path when a live handle is needed
- stop healing truncated source wires during fork; the forked session
repairs its own copy lazily on resume
- add the `kimi fork [--cwd] [-y]` CLI command
Tower mode is now entered only manually — /tower on, or /tower
<base-branch> to also pin the local branch missions merge back into. The
agent can no longer enter it on its own: TowerInit refuses while the mode
is off and points at the slash command.
/tower <base-branch> is deterministic end to end: a missing base branch is
created from the current checkout (uncommitted changes committed onto it
as a labeled WIP snapshot), the checkout switches to it, and the tower
workspace is initialized immediately instead of relying on the model to
apply the base. With an existing workspace the tower rebases onto the
requested base when no missions are open and refuses naming the blocking
missions otherwise. The base rides the tower_mode.enter event into the new
tower.base state key and TowerInit falls back to it.
Tower agent deaths (failed/timed_out/killed/lost) are recorded in the
tower protocol: TowerStatus marks dead roster entries and warns about
missions whose owner died, with a resume hint. The tower console prompt
now requires a per-worker deliverables summary before TowerTeardown.
The session profile REST surface accepts tower_base so web clients can
turn tower mode on with /tower <base-branch> instead of sending the
argument as a prompt.
Co-authored-by: konghuanjun <konghuanjun@moonshot.ai>
* feat(agent-core-v2): report enabled experimental flags in session_started telemetry
Add a sorted, comma-separated experimental_flags property to the v2
session_started event so feature-gated metrics gain a session-granular
exposure denominator, and carry the post-apply enabled set on the TUI
experimental_features_apply event to bound mid-session flag flips.
* feat(node-sdk): carry experimental flags on the harness session_started row
The v2 in-process path emits session_started twice (engine-side from
SessionLifecycleService, harness-side from KimiHarness), and only the
engine row carried experimental_flags, so consumers could double-count
or miss the flag dimension. Merge a per-emission dynamic getter into the
harness row, wired to the in-process engine's flag service, so both
rows report the same enabled flag set.
* fix(node-sdk): keep experimental_flags canonical on session_started
A caller-supplied experimental_flags in per-call sessionStartedProperties
could overwrite the engine-owned dynamic value, breaking row-to-row flag
consistency. Merge the dynamic properties after the session-scoped ones
so engine-owned keys always win.
* fix(agent-core-v2): count tower as exposure only once assembled
The tower flag reacts live to mid-process flips, but TowerFeature's
tools and profiles are assembled at App scope construction, so a session
started after an in-process flip cannot actually use tower. Filter it
out of experimental_flags on both session_started producers unless
isTowerFeatureAssembled says the feature is assembled in this process,
keeping the exposure denominator honest.
* test: recalibrate fixtures for the added constructor param and drop timing-sensitive tower assertion
The WorkspaceInstanceManager test builds the service with positional
args; the new flags param shifted unitHostFactory one slot. The tower
inclusion assertion in the SDK integration test depended on feature
assembly winning the race against async config load, which slow CI
runners lose; tower assembled-state coverage stays with the tower
service tests.
* refactor(agent-core-v2): move flag exposure gating into the flag definition
Telemetry consumers hardcoded tower's assemble-at-startup semantics to
keep it out of experimental_flags until assembled. Push the gate into
FlagDefinitionInput as an optional isExposed predicate owned by the
flag's own domain, expose it as IFlagService.exposedIds(), and let both
session_started producers call it — no feature-specific knowledge leaks
into the SDK or the session lifecycle.
* feat(cli): compress native update artifacts and decompress them while staging
Co-authored-by: qer <wbxl2000@outlook.com>
* fix(cli): generate compressed native artifacts in the publish job
The zip's checksum never pairs with the bare executable the updater
verifies after inflating, and artifacts produced in the matrix job never
leave the runner (the workflow's upload-artifact path lists only the
zip). Extract each zip in the publish job, hash the bare executable, and
emit the .zst/.tar.gz artifacts there so the manifest entry pairs
checksum with what compressed inflates to, and everything new reaches
the release via the existing upload glob — no workflow changes needed.
Co-authored-by: qer <wbxl2000@outlook.com>
* fix(cli): keep the .exe suffix in windows manifest filenames
The updater's bare-binary fallback downloads entry.filename, and the CDN
layout carries .exe on Windows — without it the fallback 404s. The .zst
and .tar.gz artifact names stay unchanged. Also fold the manifest
coverage into the pre-existing release-artifacts test (its fake-zip
fixture predates extraction) and pin the end-to-end chain there.
Co-authored-by: qer <wbxl2000@outlook.com>
---------
Co-authored-by: kimi-agent-bot <kimi-agent-bot@users.noreply.github.com>
Co-authored-by: qer <wbxl2000@outlook.com>