* feat(web): redesign cron reminder as a message bubble
Restyle the cron trigger notice as a right-aligned user-style message bubble that shows the scheduled prompt in full (wrapping across lines), with a small meta row beneath it for the schedule, status, job id and run time. Extract a shared MessageTime component used by both user messages and the cron reminder so the timestamp format and click-to-expand behavior stay consistent, and give the CronCreate/CronList/CronDelete tools distinct calendar icons.
* refactor(web): render cron reminders only as standalone turns
Remove the embedded cron block path from the web transcript projector so cron reminder fires always render through the standalone right-aligned bubble path.
* chore(web): simplify cron redesign changeset
Headless (`kimi -p`) failures could exit with code 0 when the event loop
drained during the shutdown cleanup (e.g. telemetry's unref'd retry backoff
when the network is blocked), because the rejection never reached the
process.exit(1) call. Set the failure exit code before any await in both
the run-prompt catch and the main catch, and keep the cleanup timeout ref'd
so the loop stays alive long enough for the rejection to propagate.
Align progressive tool disclosure with the discard-on-compaction model:
compaction no longer rebuilds loaded dynamic tool schemas. The boundary
announcement re-lists every loadable name, the model re-selects what it
still needs, and a from-memory call to a no-longer-loaded tool is
rejected by preflight with select guidance.
This removes the keep-all rebuild and its half-trigger budget heuristics
entirely: the post-compaction floor is back to users + summary, which is
structurally outside the auto-compaction trigger band, and the guard
baseline degenerates to summary + reinjected reminders. Every downstream
mechanism already treated the empty loaded set as its consistent base
state (ledger scan, pending clear at the compaction boundary, deferred
extras, preflight wording), so this is a strict simplification.
Co-authored-by: fengchenchen <fengchenchen@moonshot.ai>
* fix(agent-core): report EXIF-rotated image dimensions and raise edge cap to 3000px
Image compression now reports original dimensions in the decoded
(EXIF-rotated) space, matching the coordinate system of the sent image
and of ReadMediaFile region readback; previously portrait JPEGs
(orientation 5-8) got swapped width/height in captions. The longest-edge
downscale cap rises from 2000px to 3000px, and the default jimp resize
path is documented as the anti-aliased area-average one so it is not
accidentally switched to a point-sampled interpolation mode.
* test: shrink oversized image fixtures to fit CI timeouts
The 3600x3600 fixtures introduced for the 3000px edge cap nearly doubled
the pixel area jimp has to decode and deflate, pushing the slowest
compression tests past the 5s vitest timeout on CI runners. 3600x1800
keeps every fixture over the cap while restoring roughly the workload of
the old 2600x2600 fixtures that CI handled comfortably.
* test: pin anti-aliased downscale quality with executable guards
A 1px checkerboard probe pins the compressor to full-coverage averaging
at integer and fractional ratios, with jimp's point-sampled BILINEAR
mode kept as the executable aliasing counter-example (it collapses the
50%-gray pattern to solid black at 4:1). Also guards the other classic
downscale bugs: transparent-pixel color bleed, mean-brightness drift,
iterative recompression degradation, and zero-size collapse on extreme
aspect ratios.
* fix(agent-core): report decoded EXIF-rotated dimensions in ReadMediaFile notes
The media note derived its original-dimensions line from the header
sniff, which reports pre-rotation values for EXIF orientation 5-8
JPEGs. The sent image and region readback both live in the decoded
(rotated) space, so portrait photos got axis-swapped coordinate
guidance. Once a decode has happened — compression or crop — its
dimensions now overwrite the sniffed ones.
* fix(agent-core): improve handling of EXIF orientation in image dimensions and metadata
* fix(agent-core): sniff EXIF orientation and step budget fallback through 2000px
Two follow-ups to the EXIF and 3000px-cap changes:
sniffImageDimensions now reads the JPEG EXIF Orientation tag (pure
header parse, both byte orders) and reports display-space dimensions
for orientations 5-8. Passthrough images — never decoded — previously
kept the pre-rotation header size in compression results and media
read notes, disagreeing with the decoded space that region readback
uses.
encodeWithinBudget steps the over-budget fallback through 2000px
before the 1000px last resort. Raising the cap to 3000px had left a
regression window: an image whose 2000px encode fits the byte budget
was sent at 1000px where the old 2000px cap used to send it at
2000px.
* fix(kimi-code): record pasted image dimensions in display space
The TUI paste path recorded attachment and original dimensions from its
raw header parser, which ignores EXIF orientation. For a portrait JPEG
the submit-time caption then contradicted the sent image's aspect and
region readback coordinates were axis-swapped. Dimensions now come from
the compression result, which reports display space on both the
compressed and passthrough paths; parseImageMeta remains only the
format/mime gate.
* feat(agent-core): add image compression and crop telemetry
Every image ingestion path now reports an image_compress event —
outcome (compressed / passthrough fast, guard, unsupported, unhelpful,
error), input/output formats, byte and pixel sizes, EXIF transposition,
and duration — and region readback reports an image_crop event with a
failure classification and the region's share of the original area.
Wiring is per call site via a new CompressImageOptions.telemetry
option, so the outcome split and timing are measured inside the
compressor while each caller only names its source: ReadMediaFile
(tool construction, like GrepTool), MCP tool results (McpOutputOptions),
server prompt ingestion (ICoreProcessService now exposes the host
telemetry client), ACP prompts (session track adapter), and TUI paste
(host.track adapter). Properties are numeric/enum only — never paths
or content — and a throwing client can never affect the compression
result.
* fix(agent-core): run the full JPEG quality ladder at fallback sizes
The fallback rescales encoded only at quality 20, so a JPEG whose
ladder failed at the fitted size collapsed straight to the lowest
quality even when the smaller size left budget headroom for a higher
rung (the realistic window is the 1000px step, where the 4x pixel
drop pays for q80/q60). Each fallback edge now walks the same
q80-to-q20 ladder as the fitted size.
* test: shrink heavy JPEG fixtures and add explicit timeouts
The fallback-ladder test runs ~11 pure-JS JPEG encodes and the EXIF
paste test decodes, rotates, and re-encodes a 6.5MP frame; both sat at
the edge of the 5s vitest timeout on CI runners. Narrower fixtures cut
the pixel area (the ladder test keeps its width above 2000px so the
full fallback chain still runs) and explicit 15s timeouts absorb runner
variance.
* fix(server): scope prompt image compression telemetry to the session
The prompt-ingestion image_compress events were emitted with the bare
host telemetry client, while every agent-side source inherits a
session-scoped client — so prompt_inline/prompt_file events could not
be correlated with their session. The route now wraps the client with
withTelemetryContext({ sessionId }) like rpc/core-impl does for
session telemetry.
* chore(changeset): consolidate image compression changesets
One entry covering the cap raise and the EXIF dimension fix, listed
for both the CLI and the SDK so the SDK changelog's compression
description (previously pinned at 2000px) stays accurate.
Clarify that the kc-review lenses apply only to packages/agent-core-v2
(the DI x Scope engine), not to the legacy packages/agent-core or other
packages.
- make CreateSessionOptions.sessionId optional; SessionLifecycleService.create and fork now mint `session_<lowercase-uuid>` via a shared createSessionId helper, so edge layers stop minting their own ids (drop randomUUID in the v2 harness, ulid in kap-server)
- rework V2Session.waitForBackgroundTasksOnPrint to re-enumerate each round, suppress terminal notifications while waiting, and bound the drain by [task].print_wait_ceiling_s (default 1h) instead of a hardcoded 30s cap, so kimi -p can run long tasks to completion without being steered into a new turn
- add v2-session unit tests; seed session/agent/bootstrap context in the tool-dedupe harness for the real executor
- add agent-core-review umbrella skill with slop and test sub-skills
- move write-tests rules into agent-core-review/test and drop the standalone skill
* feat(kimi-web): add approval notification storage key and i18n copy
* feat(kimi-web): add approval notification helpers and tests
* feat(kimi-web): wire approval notifications and guard completion alerts
* fix(kimi-web): extract shouldNotifyCompletion helper and add tests
* feat(kimi-web): add approval notification settings toggle
* chore(kimi-web): add changeset and tidy notification module comment
- Align approval notification tag with spec (kimi-approval-${approvalId})
- Update module header to describe all three notification kinds
* fix(kimi-web): make notifications fire reliably
- Key completion notification tags by turn (sid + promptId) and question
tags by request id, so a stale notification left in the notification
center no longer swallows every follow-up alert in the same session
- Suppress notifications only while the window is actually focused, not
merely visible (document.hasFocus() on top of visibilityState)
- Play the attention sound when a tool needs approval, matching the
completion and question sounds
* chore(kimi-web): simplify changeset
The KIMI_MODEL_* effective overlay was registered by ModelService on construction, but the DI layer does not auto-instantiate Eager services, so the overlay never took effect when nothing resolved IModelService. This broke the release-e2e wire-llm-request-trace scenario, where KIMI_MODEL_NAME must synthesize the env model and its thinking capability.
Move registration to module load via a new configOverlayContributions collector, drained by ConfigRegistry on construction — mirroring the existing configSectionContributions pattern. ModelService no longer depends on IConfigRegistry.
The module-level TextDecoder is stateful in stream mode: it buffers a
trailing incomplete multi-byte sequence until the next decode. Sharing it
across reads let leftover state from an earlier read that returned early
(e.g. ensureWireMetadata bailing on the leading metadata record) leak into
the next read and prepend a U+FFFD to its first line, corrupting the
metadata envelope and breaking session fork with "corrupted line 1".
Give each read its own TextDecoder so decoder state never leaks between
reads.
Remove the three replay-only Ops that were kept for pre-alignment / 1.5 sessions, now that v2 persists natively in the v1 vocabulary:
- turn.launch (replaced by turn.prompt)
- todo.set (replaced by tools.update_store with key 'todo')
- context.splice (replaced by context.append_message / append_loop_event)
Also drop the dead code that handled them (transcript reducer, task-origin extraction, blob dehydration, harness helpers) and migrate the affected tests to the v1 record types. The live write path already emitted only v1 records, so wire.jsonl output is unchanged.
Three independent fixes for release-e2e failures that only appeared with the experimental v2 engine (KIMI_CODE_EXPERIMENTAL_FLAG):
- agent-core-v2: register the KIMI_MODEL env overlay statically so it takes effect even when ModelService is not instantiated (the DI layer does not auto-instantiate Eager services). Fixes wire-llm-request-trace.
- cli: omit the leading system.version meta line in stream-json prompt mode so the role sequence stays clean. Fixes stream-json-cron.
- agent-core-v2: honor --skills-dir via a new explicit skill source seeded from the host. Fixes interactive-skills-dir.
Cherry-picked from 2a7232737 (v2-migration), excluding the node-sdk V2Host change (not applicable on this branch).
- extract rewriteMediaUrls and blobref parse/format helpers to dedupe URL rewriting
- move the byte-bounded LRU cache into a module-private ByteLruCache with focused unit tests, dropping the protected maxCacheSize test seam
- rewrite blob service tests against the contract on in-memory storage, removing cache-internals cases
- ReadTool.finishReadResult now returns rendered lines as `output` only and
rides the `<system>` status block on the model-only `note` side channel
- drop the finishOutput helper that concatenated content and status
- update read.test.ts expectations to assert `note` separately from `output`
Remove the persist-time v1 rewrite layer (serializeV1WireRecord): ops now
write v1-shaped records directly, live-only state is declared persist:false
on the op instead of being stripped at write time, and the swarm-exit
reminder pop replays from the swarm_mode.exit record via a cross-model
reducer. Fixes resumed sessions losing the todo list, drifting turn
counters after retries, and removed reminders reappearing on resume.
- read the Edit target with errors:'strict' so a non-UTF-8 file fails the
edit instead of being silently rewritten as U+FFFD (matches v1 kaos)
- declare readWriteFile access since Edit reads before it writes, matching v1
- render edit.md directly instead of through renderPrompt: it has no template
vars, and raw avoids treating literal {{ }} as a template
- restore the replace_all usage example in edit.md (v1 #1102)
- add a regression test asserting a non-UTF-8 file fails the edit and keeps
its bytes untouched
- inject IHostProcessService into ExternalHooksRunnerService and thread it
through runMatchedHooks to runHook instead of spawning node:child_process
- route hook termination through the service's cross-platform process-tree kill
- settle on the exit code plus drained stdout/stderr so fast-exiting hooks
keep their trailing output
- hide the child console window on Windows via the service default
- update externalHooks tests for the new dependency
- killswitch: read KIMI_DISABLE_CRON live by re-applying the ConfigService
env overlay on every get(); CronCreate reads it via ISessionCronService
instead of a value frozen at tool registration
- delivery: resolve fire delivery on promptService.steer().launched so a
rejected launch retains one-shot tasks for retry instead of deleting
them; tick() is now async and awaits delivery before advancing cursors
- ids: switch cron task ids to ULIDs (from 32-bit hex) so two sessions
sharing a workspace cannot overwrite each other's persisted task;
CronDelete and persistence accept both ULID and legacy 8-hex ids
- display: CronCreate reports nextFireAt through the service so it honors
KIMI_CRON_NO_JITTER and matches the scheduler and CronList
- migration: adopt shape-valid tasks with no sessionId tag on
loadFromStore and stamp the tag back to disk
- persistence: create cron directories 0700 and files 0600 via
FileStorageService dirMode/fileMode
Gate SessionCronService startup on config.ready and resolve clocks after
ready so config is never read before it is loaded; start() is now async.
- add ProjectionAnomaly + onAnomaly sink through the project / projectStrict
passes (reorder, synthesize, orphan / duplicate drop, leading drop, merge,
blank-text drop) so the pure projection reports every wire-repair it applies
- AgentContextProjectorService injects ILogService and emits a single
signature-deduped 'repaired the request to keep it wire-valid' warning,
excluding trailing-tail synthesis, matching agent-core parity
- cover the trace and its dedup in the projector tests
- delete the microCompaction domain (service, wire model/op, config section,
experimental flag) and its dedicated tests
- stop truncating old tool results in the context projector and drop the
projector's now-unused instantiation dependency
- remove the domain from the layer map, package exports, and the DI x Scope
dependency diagram
- retarget the flag-registry test and skill examples at a neutral flag
* fix(agent-core-v2): align media reads with v1 note channel and EXIF handling
Port two agent-core changes into agent-core-v2:
- Move the ReadMediaFile media summary from an inline <system> text part
onto the tool result's note side channel, so raw <system> markup never
renders in UIs (matching the MCP output path).
- Report image dimensions in the decoded EXIF-rotated space: the header
sniff now reads the JPEG Orientation tag, and once a decode happened
(compression or crop) its dimensions overwrite the sniffed ones, so
portrait photos no longer get axis-swapped coordinate guidance.
- Raise the longest-edge downscale cap from 2000px to 3000px, step the
over-budget fallback through 2000px before the 1000px last resort, and
run the full JPEG quality ladder at fallback sizes.
- Report image_compress / image_crop telemetry for media reads (source
read_media), with EXIF transposition and crop failure classification.
The tool description also regains the downsampling recovery guidance
(region / full_resolution readback) that the v2 copy predated.
* fix(agent-core-v2): align v1 wire records
* fix(agent-core-v2): hide compression captions and register media tools in production
Port the remaining v1 media gaps into agent-core-v2:
- Reroute inline image-compression captions out of user messages: the
prompt service splits them at the append chokepoint (prompt and steer
flush) and delivers them through the built-in system-reminder
injection (origin {kind: 'injection', variant: 'image_compression'}),
which every UI hides. Session titles/lastPrompt strip the caption the
same way. The model still receives the full note.
- Register ReadMediaFile in production: media tools cannot use the
module-level contribution table (capabilities are unknown until a
model binds), so a new Eager agent-scope registrar re-runs
registerMediaTools on every agent.status.updated where the model
alias or its media capabilities changed, rebinding the video uploader
and dropping the tool when the model loses media input.
* fix(agent-core-v2): port v1 parity fixes for hooks, anthropic, thinking config and add-dir (#1504)
* fix(agent-core-v2): hide console window when running hooks on Windows
Port the v1 hooks runner fix: extract buildHookSpawnOptions and pass
windowsHide:true so hook child processes no longer flash a console
window on Windows, mirroring the node-local process host defaults.
Includes the same regression tests as v1.
* fix(agent-core-v2): port anthropic max_tokens ceiling and override fixes
Port two v1 kosong fixes to the v2 anthropic provider:
- Fall back to the nearest lower catalogued minor when resolving the
Claude output ceiling, and catalogue Opus 4.8's documented 128k cap,
so an uncatalogued minor no longer drops to the family baseline.
- Treat an explicit defaultMaxTokens as the final max_tokens value
instead of clamping it to the built-in ceiling.
Mirrors the v1 regression tests in a new anthropic-max-tokens test file.
* refactor(agent-core-v2): converge thinking config to enabled/effort
Port the v1 thinking-config overhaul (#1132's config side) to v2:
- ThinkingConfigSchema becomes { enabled, effort, keep }; the mode enum,
the separate defaultThinking section, and the KIMI_MODEL_THINKING_MODE /
KIMI_MODEL_DEFAULT_THINKING env bindings are removed.
- The effort resolver drops the mode/defaultThinking branches and no
longer normalizes a requested 'on' to a concrete effort in core; 'on'
is taken verbatim and normalization stays at the UI boundary.
- OAuth login/refresh and catalog refresh now persist the thinking.enabled
value computed by the shared oauth apply/restore logic instead of
dropping it and writing the removed default_thinking key, so
[thinking] enabled = false actually disables thinking and the login
default survives on disk.
Mirrors the v1 resolver regression tests and adds a persistence
regression for the refresh path.
* docs(agent-core-v2): fix stale loop-event comments after wire parity
The v1.4 wire-parity alignment switched the v2 live loop to stream turns
as context.append_loop_event records, but three comments still described
the old world (restore-only Op, "v2 never emits loop events"). Update
them to match the actual write path: non-loop appends use append_message,
the loop persists loop events byte-compatible with v1, and the fold runs
both at live dispatch time and on replay.
* fix(agent-core-v2): load workspace additional dirs on session create and resume
The /add-dir command persisted remembered dirs to .kimi-code/local.toml,
but session materialization never read them back and offered no caller
additionalDirs entry point — a remembered dir silently stopped applying
to new, resumed, and forked sessions.
Mirror v1's createSession/resumeSession: merge the project-local
local.toml dirs with caller-supplied additionalDirs (relative paths
resolve against workDir) and seed the session workspace context in
materializeSession, so create/resume/fork all pick them up. A broken
local.toml fails the create loudly with CONFIG_INVALID, same as v1.
Tests mirror v1's runtime coverage for the load/merge/dedupe/resume/fork
scenarios.
* fix(kimi-code): forward create-session additional dirs from the v2 harness
The in-process v2 print-mode harness dropped the SDK CreateSessionOptions
additionalDirs when calling ISessionLifecycleService.create, so --add-dir
never reached the v2 resolver. Pass it through.
* fix(agent-core-v2): report video_upload telemetry for media reads
Port the v1 video-upload telemetry wrapper into createVideoUploader:
every upload emits a video_upload event with outcome (success/error),
byte size, mime type, duration, and the caller's static props (model
alias, protocol tags), and a throwing telemetry client never affects
the upload outcome. The media-tools registrar supplies the sink and
props from the bound model.
Also restores two v1 rationale comments in ReadMediaFile (original-size
reporting and the full_resolution hard refusal) that were dropped
during the earlier port.
---------
Co-authored-by: 7Sageer <7sageer@djwcb.cn>
Co-authored-by: liruifengv <liruifeng1024@gmail.com>
* fix(agent-core-v2): hide console window when running hooks on Windows
Port the v1 hooks runner fix: extract buildHookSpawnOptions and pass
windowsHide:true so hook child processes no longer flash a console
window on Windows, mirroring the node-local process host defaults.
Includes the same regression tests as v1.
* fix(agent-core-v2): port anthropic max_tokens ceiling and override fixes
Port two v1 kosong fixes to the v2 anthropic provider:
- Fall back to the nearest lower catalogued minor when resolving the
Claude output ceiling, and catalogue Opus 4.8's documented 128k cap,
so an uncatalogued minor no longer drops to the family baseline.
- Treat an explicit defaultMaxTokens as the final max_tokens value
instead of clamping it to the built-in ceiling.
Mirrors the v1 regression tests in a new anthropic-max-tokens test file.
* refactor(agent-core-v2): converge thinking config to enabled/effort
Port the v1 thinking-config overhaul (#1132's config side) to v2:
- ThinkingConfigSchema becomes { enabled, effort, keep }; the mode enum,
the separate defaultThinking section, and the KIMI_MODEL_THINKING_MODE /
KIMI_MODEL_DEFAULT_THINKING env bindings are removed.
- The effort resolver drops the mode/defaultThinking branches and no
longer normalizes a requested 'on' to a concrete effort in core; 'on'
is taken verbatim and normalization stays at the UI boundary.
- OAuth login/refresh and catalog refresh now persist the thinking.enabled
value computed by the shared oauth apply/restore logic instead of
dropping it and writing the removed default_thinking key, so
[thinking] enabled = false actually disables thinking and the login
default survives on disk.
Mirrors the v1 resolver regression tests and adds a persistence
regression for the refresh path.
* docs(agent-core-v2): fix stale loop-event comments after wire parity
The v1.4 wire-parity alignment switched the v2 live loop to stream turns
as context.append_loop_event records, but three comments still described
the old world (restore-only Op, "v2 never emits loop events"). Update
them to match the actual write path: non-loop appends use append_message,
the loop persists loop events byte-compatible with v1, and the fold runs
both at live dispatch time and on replay.
* fix(agent-core-v2): load workspace additional dirs on session create and resume
The /add-dir command persisted remembered dirs to .kimi-code/local.toml,
but session materialization never read them back and offered no caller
additionalDirs entry point — a remembered dir silently stopped applying
to new, resumed, and forked sessions.
Mirror v1's createSession/resumeSession: merge the project-local
local.toml dirs with caller-supplied additionalDirs (relative paths
resolve against workDir) and seed the session workspace context in
materializeSession, so create/resume/fork all pick them up. A broken
local.toml fails the create loudly with CONFIG_INVALID, same as v1.
Tests mirror v1's runtime coverage for the load/merge/dedupe/resume/fork
scenarios.
* fix(kimi-code): forward create-session additional dirs from the v2 harness
The in-process v2 print-mode harness dropped the SDK CreateSessionOptions
additionalDirs when calling ISessionLifecycleService.create, so --add-dir
never reached the v2 resolver. Pass it through.