mirror of
https://github.com/MoonshotAI/kimi-code.git
synced 2026-07-25 17:14:54 +00:00
238 commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
83e175399f
|
feat: auto-background timed-out foreground bash commands (#1591)
* feat: auto-background timed-out foreground bash commands * fix: discourage blocking TaskOutput waits on background tasks * test: avoid unsafe string conversion in bash timeout test * fix: align bash timeout description with auto-background opt-out * feat(agent-core-v2): auto-background timed-out bash commands - detach timed-out foreground Bash tasks and re-arm the background deadline - align TaskOutput guidance with non-blocking background task handling - add klient SEA end-to-end coverage for the v2 server * fix(klient): clean up lint errors in auto-background e2e example --------- Co-authored-by: haozhe.yang <yanghaozhe@moonshot.ai> |
||
|
|
d601847f22
|
fix: send the product User-Agent on provider registry and catalog fetches (#1597)
* fix: send the product User-Agent on provider registry and catalog fetches Registry (api.json) and models.dev catalog fetches only carried the runtime default User-Agent while every other outbound request sends kimi-code-cli/<version>. Thread an optional userAgent through fetchCustomRegistry / fetchCatalog and the shared refresh host, pass the product UA from the CLI, TUI, and both daemons, and seed a default product UA in kap-server that hosts can override via opts.seeds. * refactor: use options for registry fetches |
||
|
|
5502b90b42
|
test: cut slow suite runtimes and isolate experimental flag from host env (#1595) | ||
|
|
ceb158dc54
|
feat(v2): land agent-core-v2 engine and kap-server behind experimental flag (#1441)
Some checks are pending
CI / typecheck (push) Waiting to run
CI / test-pi-tui (push) Waiting to run
Nix Build / nix build .#kimi-code (push) Blocked by required conditions
Release / Release (push) Waiting to run
Release / Deploy docs (push) Blocked by required conditions
Release / Native release artifact (push) Blocked by required conditions
CI / test-windows (push) Waiting to run
CI / lint (push) Waiting to run
CI / build (push) Waiting to run
CI / test (push) Waiting to run
Nix Build / Check flake.nix workspace sync (push) Waiting to run
Release / Desktop release artifact (push) Blocked by required conditions
Release / Publish native release assets (push) Blocked by required conditions
* fix: adapt grep tool to agent-core-v2 * fix(agent-core-v2): enrich PATH from the user's login shell at startup - port probeLoginShellPath/mergeLoginShellPath/applyLoginShellPath into _base/execEnv/loginShellPath.ts as a pure helper (no DI) - export execFileText from environmentProbe for reuse by the probe - run applyLoginShellPathFromNode concurrently with the host probe in HostEnvironmentService, mirroring kaos LocalKaos.create() Aligns agent-core-v2 with kaos |
||
|
|
faefad0e29
|
feat(agent-core): configurable subagent timeout with 2h default (#1562)
* feat(agent-core): make subagent timeout configurable and raise default to 2h - add `[subagent] timeout_ms` config (env `KIMI_SUBAGENT_TIMEOUT_MS` overrides) to replace the hardcoded 30-minute cap for Agent / AgentSwarm subagents - raise the default subagent timeout from 30 minutes to 2 hours - thread the value through tool construction so foreground and background subagents use it, with the timeout message reflecting the effective value |
||
|
|
c6e02daf42
|
feat(background): add print_background_mode steer for multi-turn -p runs (#1497)
* feat(background): add print_background_mode with steer for multi-turn -p runs - add `[background].print_background_mode` (`exit`/`drain`/`steer`) and `print_max_turns`; when unset, falls back to `keep_alive_on_exit = true` mapping to `drain`, preserving existing behavior - core: `Session.handlePrintMainTurnCompleted()` returns `finish`/`continue`; in `steer` mode the run stays alive so a background-task completion `turn.steer`s the main agent into a new turn (matching background subagents), bounded by `print_wait_ceiling_s` and `print_max_turns` - cli: print driver follows every main turn instead of only the first and defers `finish()` until the run quiesces or a limit is hit - plumb `handlePrintMainTurnCompleted` through node-sdk RPC; docs + tests |
||
|
|
2f97917bb5
|
feat(cli): keep kimi -p running while a goal is active or cron tasks are pending (#1555)
Some checks are pending
CI / typecheck (push) Waiting to run
CI / lint (push) Waiting to run
CI / test-windows (push) Waiting to run
CI / build (push) Waiting to run
CI / test (push) Waiting to run
CI / test-pi-tui (push) Waiting to run
Nix Build / Check flake.nix workspace sync (push) Waiting to run
Nix Build / nix build .#kimi-code (push) Blocked by required conditions
Release / Publish native release assets (push) Blocked by required conditions
Release / Release (push) Waiting to run
Release / Deploy docs (push) Blocked by required conditions
Release / Native release artifact (push) Blocked by required conditions
Release / Desktop release artifact (push) Blocked by required conditions
A kimi -p run settled the moment the main agent's turn ended (end_turn), so a goal created mid-run was cancelled during cleanup and a scheduled cron task never fired in the same run. - runPromptTurn now re-evaluates completion when the main agent goes idle and stays alive while a goal is still active (the goal driver runs the continuation turns) or while cron tasks with a future fire remain (their fire steers a fresh turn). A ref'd handle keeps the event loop alive during the wait since the cron scheduler tick is unref'd. - a terminal goal.updated (e.g. the driver blocking a goal on a hard budget, which emits no further turn.ended) also re-evaluates so the run cannot hang. - add getCronTasks RPC and Session.getCronTasks() so the print flow can enumerate pending cron tasks. |
||
|
|
37bb4b870e
|
fix(web): keep ReadMediaFile media rendering after session resume (#1552)
Tool-role messages reached the snapshot/messages REST projection with their content flattened to text, dropping image/video/audio parts, so a ReadMediaFile result rendered as an image while streaming but fell back to a generic tool card after a reload. Pass the raw content parts through when a tool result carries media, matching the live tool.result event shape the web client already parses. |
||
|
|
7bd29ab011
|
refactor(kosong): rename select_tools capability to dynamically_loaded_tools (#1488)
* refactor(kosong): rename select_tools capability to dynamically_loaded_tools Rename the `ModelCapability` bit from `select_tools` to `dynamically_loaded_tools` everywhere it is declared, detected, catalogued, and forwarded: kosong `ModelCapability`/catalog, agent-core capability resolution and the `toolSelectEnabled` gate, the SDK catalog-to-alias mapping, and the built-in catalog pruner's keep list. The old `select_tools` spelling is removed outright rather than kept as an alias — no catalogued model or shipped configuration used the capability, so there is nothing to migrate. Client-side vocabulary (the `select_tools` builtin tool and the `tool-select` experimental flag) is intentionally untouched. * chore: shorten changeset description --------- Co-authored-by: fengchenchen <fengchenchen@moonshot.ai> |
||
|
|
db61c9e2dd
|
fix: refuse unsupported image formats instead of poisoning sessions (#1536)
* fix: refuse unsupported image formats instead of poisoning sessions Images in formats providers reject (AVIF, HEIC, BMP, TIFF, ICO) used to pass through to the API, and the resulting HTTP 400 repeated on every later turn because the image_url stayed in the session history. Add a single format policy (accepted set: PNG/JPEG/GIF/WebP) enforced at every ingestion point: ReadMediaFile refuses with a per-OS conversion command; MCP tool results, REST uploads, and ACP prompts replace the image with a text notice; and turn.prompt/steer gates as the last-funnel backstop so the SDK/RPC path cannot poison a session either. Accepted MIME aliases (image/jpg, case/whitespace) are forwarded in canonical form, and data URLs carrying MIME parameters can no longer slip past the gate. Remote image URLs pass through (no bytes to inspect). * fix: canonicalize accepted data URLs with MIME parameters The format gate compared only the MIME token when deciding whether to rebuild a data URL, so an accepted image carrying MIME parameters (`data:image/jpeg;charset=utf-8;base64,...`) was forwarded with its original header. The Anthropic provider splits the data URL and exact-matches the full header against its whitelist, so the part still poisoned the session. Rebuild to the byte-exact canonical URL whenever the original differs, covering aliases, case/whitespace, and parameters with one comparison. Addresses review feedback on PR #1536. * fix: parse data URLs case-insensitively in the image format gate An uppercase `;BASE64,` marker is legal (RFC 2045 encoding names are case-insensitive), but the parser required a lowercase match and returned null, so the gate treated the URL as remote and forwarded it: an unsupported image could still land in the session history, and the Anthropic provider's lowercase-only split then threw on every turn. Match the scheme and marker case-insensitively; the canonical rebuild emits the lowercase form. Addresses review feedback on PR #1536. * fix: harden image format handling against mislabeled and legacy images Two more ways an unsupported image could reach the provider are closed: - Bytes, not labels, decide the format. A data-URL image whose declared MIME disagrees with its magic bytes (e.g. AVIF bytes an image search tool labels image/png) is now gated on the sniffed format at every entry point (MCP results, ACP, SDK/RPC prompt, REST inline and file uploads), so a mislabel cannot slip past the gate. - A poisoned image already in the session history no longer kills the session: a server image-format 400 (or kosong's client-side image rejection) now retries once with every media part replaced by a text marker, mirroring the 413 media-degraded recovery. The recovery also fires during compaction, and the transient-retry fallback no longer burns the retry budget on image-format errors before the dedicated recovery can run. * fix: reject remote image URLs ending in an unsupported extension Remote image URLs (MCP resource_link, REST `kind: 'url'`) carry no bytes to sniff, so a link ending in `.avif` (or `.heic`, `.bmp`, `.tiff`, `.ico`) would pass through and be fetched server-side — and rejected. Reject such URLs by their path extension instead (query/fragment ignored, case-insensitive); extensionless or accepted-extension URLs still pass through to the provider and the 400 recovery. * fix: tighten image format handling for parameterized MIMEs and recovery scope Address two review findings on PR #1536: - A declared media type with parameters (e.g. image/jpeg; charset=utf-8) is no longer misread as unsupported: normalizeImageMime now strips parameters, matching the data-URL parser, so an accepted image with parameters is forwarded instead of dropped. - The image-format recovery predicate is narrowed to specific format/data rejection phrases, so a 400 about image count, size, or image-input support no longer triggers a media-stripped resend that would let the model answer blind to the user's images. * fix * fix: scope image format recovery to images and flag remote SVG URLs - The media_type/mime_type recovery match now requires the message to mention an image, so a video/audio media_type rejection surfaces instead of triggering a blind media-stripped resend. - unsupportedImageMimeFromUrl flags .svg URLs as image/svg+xml without touching the shared suffix map (SVG stays text for the file tools), so remote SVG images get the intended notice instead of a provider rejection. Addresses review feedback on PR #1536. * fix: reject remote MCP images by their declared MIME type An MCP resource_link with an extensionless or signed URL gives the extension gate nothing to work with, and convertMCPContentBlock was discarding the declared mimeType — an honestly-declared AVIF/HEIC link from an image search tool still became an image_url and poisoned the session. Reject on the declared MIME when the server provides one: unsupported declarations become a text notice that keeps the URL so the model can fetch and convert it; accepted declarations pass through as before. Addresses review feedback on PR #1536. * fix: keep image format recovery image-specific and preserve dropped URLs in notices - Drop the bare `media` alternative from the image-format recovery patterns so audio/video media rejections ("unsupported media type", "invalid media type") can never be misclassified as image errors and blindly media-stripped; every pattern now mentions "image" literally. - Remote image URLs rejected by their extension now keep the URL in the replacement notice (gateImageFormatParts and the REST url path), so the model can still fetch and convert the image — matching the declared-MIME resource_link path. Addresses review feedback on PR #1536. * fix: drop malformed data URLs at ingestion instead of letting them poison the session A `data:` URL that fails to parse (missing `;base64,` separator, empty MIME, …) was treated like a remote URL and passed through the format gate; the provider then rejects it on every turn, and the read-side media-stripped recovery keeps paying that round-trip until compaction. Detect unparseable `data:` URLs in gateImageFormatParts and replace them with a (truncated) notice at ingestion, covering the MCP/ACP/SDK/turn paths that share the gate. Addresses review feedback on PR #1536. |
||
|
|
9f66ec416c
|
feat: harden LLM API fault tolerance against 429 and overload (#1530)
* feat(retry): harden LLM API fault tolerance against 429/overload - retry more transient errors: 408/409/429/5xx/529, an embedded upstream status_code=429 in OpenAI Responses stream errors, and unclassified provider errors as a last-resort fallback - honor server Retry-After (parsed into APIStatusError.retryAfterMs by the OpenAI and Anthropic providers); chatWithRetry prefers it over its backoff - align app-level backoff with claude-code (500ms base, 32s cap, factor 2, up to 25% jitter) so high-attempt configs ride out multi-minute overload - emit a turn.step.retrying meta line in -p --output-format stream-json |
||
|
|
046b6c4175
|
fix(agent-core): scope [image] config limits to the owning core (#1521)
* fix(agent-core): scope [image] config limits to the owning core * fix(agent-core): thread harness [image] max_edge_px to TUI paste and ACP ingestion * chore(changeset): simplify entry to user-facing wording |
||
|
|
1bf2c9afee
|
feat: keep image-heavy sessions within provider request-size limits (#1508)
* feat(kosong): classify HTTP 413 request-body-too-large as a dedicated error type * feat(agent-core): lower default image downscale cap to 2000px and make it configurable * feat(agent-core): strip media to text markers and retry when the compaction request is too large * feat(agent-core): cap model-initiated image reads with a configurable byte budget * feat(agent-core): resend with degraded media when the provider rejects the request body as too large * test(agent-core): add explicit timeouts to encode-heavy image budget tests * feat: add WebP decoding support with wasm integration - Introduced a new WebP decoding module using @jsquash/webp's wasm decoder. - Implemented functions to decode WebP images and check for animated WebP formats. - Updated image compression tests to include scenarios for WebP handling, including encoding and decoding. - Enhanced error handling for API request size limits to accommodate various error messages. - Updated pnpm lockfile to include new dependencies for WebP encoding and decoding. * chore(changeset): consolidate this PR's entries into one * fix(nix): update pnpmDeps hash for merged lockfile * feat(agent-core): refuse HEIC/HEIF reads with platform-matched conversion guidance |
||
|
|
fe9479d89a
|
fix: rewrite repeated tool call reminders to redirect instead of prohibit (#1518)
Some checks are pending
CI / build (push) Waiting to run
CI / test (push) Waiting to run
CI / test-pi-tui (push) Waiting to run
CI / test-windows (push) Waiting to run
CI / lint (push) Waiting to run
CI / typecheck (push) Waiting to run
Nix Build / Check flake.nix workspace sync (push) Waiting to run
Nix Build / nix build .#kimi-code (push) Blocked by required conditions
Release / Release (push) Waiting to run
Release / Deploy docs (push) Blocked by required conditions
Release / Native release artifact (push) Blocked by required conditions
Release / Desktop release artifact (push) Blocked by required conditions
Release / Publish native release assets (push) Blocked by required conditions
The r1/r2/r3 reminders injected into repeated tool results led with prohibition verdicts and, in r2, echoed the repeated tool name and full arguments back into the context, reinforcing the very pattern they were meant to break. Rewrite them to state the situation factually and hand the model a concrete next action: an expectation-setting sentence for the next call (r1), a forced decision menu of falsify / ask-user / conclude (r2), and a final hand-off summary without further tool calls (r3). Detection, thresholds (3/5/8/12), force-stop, and telemetry are unchanged. |
||
|
|
173bdfdab1
|
fix: resume sessions with missing workdir (#1517) | ||
|
|
e83511a711
|
fix: surface provider auth error for unavailable models (#1506)
* fix: surface provider auth error for unavailable models When an OAuth-managed model returns 401 after a forced token refresh, the token is valid but the provider rejected it for that model (the account lacks access). Emit provider.auth_error carrying the provider's message instead of auth.login_required with a misleading "OAuth login expired. Send /login" prompt. * fix(agent-core): preserve provider auth errors through compaction Treat provider.auth_error like auth.login_required in the compaction path so an auth rejection during compaction surfaces the provider's message instead of being wrapped as a generic compaction failure. |
||
|
|
9b76e5bff6
|
feat(agent-core): discard loaded tool schemas on compaction (#1471)
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> |
||
|
|
131700097a
|
fix: clarify goal blocked audit guidance (#1481) | ||
|
|
150206a6f7
|
fix: count goal creation turn (#1477) | ||
|
|
474ce289dd
|
fix(agent-core): report EXIF-rotated image dimensions and raise edge cap to 3000px (#1460)
* 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.
|
||
|
|
d1a964fba9
|
fix: forbid model-driven goal pauses (#1476) | ||
|
|
063bce2a2f
|
fix(agent-core): hide console window when running hooks on Windows (#1466)
Pass windowsHide:true when spawning the hook process so a visible console no longer flashes and steals focus on Windows. The Bash-tool path was already hardened (KAOS buildLocalSpawnOptions); the hook runner missed the flag even though its own taskkill helper already set it. Extract the spawn options into a pure builder and add a regression test asserting windowsHide, mirroring the existing KAOS spawn-options test. Relates to #1298. |
||
|
|
e9ef9399d0
|
fix(agent-core): harden goal-mode budget and outcome flow (#1456) | ||
|
|
65d30177ad
|
feat(agent-core): record llm request trace in wire.jsonl (#1448)
* feat(agent-core): record llm request trace in wire.jsonl Add three observability record types so every request sent to the model can be reconstructed from the wire log at the logical-request level: - llm.tools_snapshot: content-addressed snapshot of the top-level tools table as sent (post deferred-strip), written once per unique table - llm.request: one record per outbound request (retries, strict resends, and compaction rounds included) carrying the effective request params and hash links to the system prompt and tools snapshot - mcp.tools_discovered: the server's verbatim tools/list result plus the agent's gating (allow-list, collisions), deduplicated by content hash Observability records never feed state rebuild; replay only restores the write-dedup cursors. The records/types.ts contract now documents the two record classes explicitly (persisted is not the same as replayed). Recording happens at the single Agent.generate choke point. The LLMRequestLogFields side channel gains kind/projection/maxTokens/ droppedCount, chatWithRetry preserves caller-set fields, and compaction tags its requests. The vis wire view renders the new record kinds. * fix(agent-core): record the provider-clamped completion cap in the request trace The llm.request trace recorded the client-requested budget cap, but chat-completions providers tighten the actual wire value inside withMaxCompletionTokens (remaining-context sizing, transport ceilings, model-default resolution) — with the default budget the clamp is active on nearly every non-empty-context request, so the recorded value did not match what was sent. Providers now expose the effective cap they computed as a readonly maxCompletionTokens field on the clone, and the recorder reads it from the effective provider at the Agent.generate choke point. This replaces the side-channel recomputation, which is removed along with the appliedCompletionBudgetCap helper. * fix(agent-core): park pre-replay MCP discovery records and hash the collision outcome Two wire-hygiene fixes for the mcp.tools_discovered trace: Parking: the real Session ordering connects MCP servers concurrently with agent construction, so ToolManager can observe a connected server before agent.resume() has replayed the wire. Recording at that point bypassed the restored dedup cursor (duplicating a 1-50KB record on every resume) and appended a stray metadata record ahead of replay. AgentRecords now exposes a one-shot opened latch — set when replay completes (after the migration rewrite flushes) or when the first live record is logged — and ToolManager parks discoveries until then, re-running the dedup check at drain time. A frozen range-limited replay never opens; those agents are transient previews. Collision hashing: the dedup hash now covers the collision outcome, not just the raw list and allow-list. Collisions depend on which other servers hold a sanitized qualified name at registration time, so a server can re-register with identical tools but a flipped outcome; that gating change must produce a new record instead of being suppressed. * fix(agent-core): skip the request trace for pre-flight-aborted calls Mirror kosong generate()'s pre-flight abort check at the Agent.generate choke point: a call whose signal is already aborted never reaches the wire (generate throws before dispatching), so it must not leave an llm.request/llm.tools_snapshot trace or a diagnostic log line claiming a request was sent. Recording stays before dispatch for every call that passes the gate, preserving the crash-safety of the trace. * chore(agent-core): remove a leftover adaptive-thinking override hook The adaptiveThinkingOverride option was a temporary local hook explicitly marked for removal before commit. Nothing passes it, so resolution falls back to the alias-level adaptiveThinking value in all cases; drop the option and the dead indirection. * fix(kosong): derive the exposed completion cap from generation kwargs maxCompletionTokens was a field stored only by withMaxCompletionTokens, so caps that reach the wire through other paths were invisible to the request trace: with completion budgeting disabled via env, Anthropic still sends the constructor-resolved max_tokens (required by the Messages API), and constructor-level kwargs like OpenAILegacyOptions maxTokens were likewise unreported. Replace the stored field with a getter derived from each provider's generation kwargs — the single source the request body reads — covering constructor defaults, direct withGenerationKwargs configuration, and budget application in one place. Kimi mirrors its request-time legacy max_tokens alias normalization; openai-legacy reuses the same normalizeGenerationKwargs the request path uses. * feat(agent-core): add thinkingKeep passthrough for Kimi providers and update tests |
||
|
|
244ec077f9
|
fix(agent-core): remove print-mode subagent drain deadline (#1452)
- drop the session-wide absolute drain deadline that gated print-mode turn holds - hold the turn until background subagents reach a terminal state, bounded by each subagent's own timeout - fixes late or long-running subagents being abandoned (results suppressed) in long `kimi -p` runs |
||
|
|
743f66e547
|
refactor: move tool-result metadata into a structured note side channel (#1437)
* fix: stop rendering <system> notes from tool results in the terminal and web UIs
Tool results carry <system> blocks as side-channel notes for the model (ReadMediaFile summaries, Read status, MCP image captions, error/empty sentinels). Keep them in history for the model, but strip them at every core-to-UI boundary so they no longer render as plain text. vis is intentionally left untouched to preserve the model's-eye view for debugging.
* fix: keep error/empty status text visible when stripping tool-result <system> tags
Unwrap the tool error/empty sentinels (<system>ERROR: ...</system>, <system>Tool output is empty.</system>) instead of deleting them: keep the human-readable text and drop only the tags. Otherwise a failed or empty tool result rendered as a blank output, indistinguishable from a rendering bug. The model still reads the wrapped form in history.
* refactor: move tool-result metadata into a structured note side channel
Tool-produced model-facing metadata (ReadMediaFile summaries, Read status
lines, MCP image-compression captions) was baked into tool output as
<system> text, so every UI had to strip it back out and three copies of
the model-view normalization had silently drifted apart.
- ExecutableToolResult gains `note`: content rendered to the model but
never to UIs; records and history now store the raw output plus the
structured isError/note fields
- the model view is rendered exactly once at the LLM projection boundary
by renderToolResultForModel; the transcript and vis hand-copies are
deleted (vis now calls the same function for its model view, fixing
their drifted empty-output checks)
- ReadMediaFile / Read / MCP captions write `note`; tool outputs stay
pure data, and text-only results keep a single text part (note joined
with a newline) so provider tool content stays a plain string
- all UI-side <system> stripping is removed; failed tools show their own
error text with the structured isError flag
- wire protocol 1.4 -> 1.5 migrates existing records' tool-produced
<system> blocks into `note` on resume
* fix: provider-neutral wording, no wire migration, direct optional fields
- "The attached image was downsampled" replaces directional wording that
depended on provider serialization order (inline media vs flatten-and-
re-attach)
- drop the 1.4 -> 1.5 wire migration: legacy records replay verbatim, so
the model view of old sessions stays byte-identical to what the model
originally saw and UIs show the legacy <system> text as-is; this also
removes the risk of the migration misclassifying user data that quotes
tool metadata, and the additive note field needs no version bump
- pass optional result fields as undefined instead of conditional spreads
(repo convention)
* fix: enforce the note contract at the trust boundary; narrow the TUI system-tag guard
- normalizeToolResult now keeps a note only when it is a non-empty string:
tools and finalize hooks are arbitrary JS, and a malformed note (null,
number, object) would previously persist into the record and crash every
subsequent LLM projection of the session. Everything downstream now
trusts note to be string | undefined.
- the TUI tool body suppression matches the full <system-reminder> tag
instead of any <system prefix: reminder piggy-backing stays hidden,
while real output that merely starts with a literal <system> tag (file
contents, MCP text) stays visible, covered through the real
ToolCallComponent path.
* fix: return MCP compression captions as data instead of extracting them from text
compressImageContentParts now returns { parts, captions } — captions come
back from the compressor as structured data and are never inserted into
the parts, so the MCP pipeline no longer pattern-matches text to move
them into the note side channel. Tool output that merely quotes a
caption (a doc, a log, a test fixture) stays verbatim in the output.
Also corrects the stale claim that prompt ingestion uses this helper
(it compresses per image while constructing the part).
* docs: correct the image-compression re-export comment; export CompressedContentParts
The package-root comment still described compressImageContentParts as the
input-stage helper every ingestion site calls; prompt ingestion compresses
per image with compressBase64ForModel / compressImageForModel, and the MCP
pipeline is the walker's only caller. Also export the walker's
CompressedContentParts return type so public-API consumers can name it.
* feat: wrap tool status sentinels in <system> so the model can tell harness verdicts from tool output
The error/empty status text is model-only after the note refactor (UIs
render the raw output and style failures via the structured isError
flag), so the earlier plain-text wording served no remaining audience.
Wrapping the statuses in <system> gives every piece of system-generated
text inside a tool result the same marker:
- failed calls get '<system>ERROR: Tool execution failed.</system>'
unconditionally — the ERROR:-prefix guard is removed, so the harness
verdict can no longer be confused with tool output that happens to
start with error-like text
- empty outputs render as '<system>Tool output is empty.</system>'; the
plain placeholder the loop layer bakes into records is still
recognized and upgraded at projection time
* style: collapse an internal helper docstring per the services subtree convention
|
||
|
|
25a655cf88
|
feat(agent-core): enable Preserved Thinking by default on the Anthropic provider (#1432)
* feat(agent-core): enable Preserved Thinking by default on the Anthropic provider Default thinking.keep to "all" for the Anthropic provider (Claude and Kimi in Anthropic-compatible mode) while Thinking is on, via a context_management clear_thinking_20251015 edit, mirroring the Kimi default. Reuses [thinking] keep and KIMI_MODEL_THINKING_KEEP (env > config > default "all"); off-values disable it. * feat(kosong): route Anthropic Preserved Thinking through the beta Messages API Force the beta endpoint (client.beta.messages.create) when thinking.keep is enabled, since clear_thinking_20251015 is only honored there. Also prepend clear_thinking to any existing context-management edits (for example clear_tool_uses) instead of replacing them, keeping it first as Anthropic requires when combining edits. * docs: clarify Anthropic beta endpoint and compaction keep behavior Note in code comments and bilingual docs that enabling Anthropic Preserved Thinking routes requests to the beta Messages API (client.beta.messages.create), with keep=off as the escape hatch back to the standard endpoint. Correct the resolveThinkingKeep comment to reflect that compaction shares ConfigState.provider and intentionally carries the same keep. * test(kosong): cover Anthropic beta endpoint (streaming and forced betaApi) Add a streaming beta-endpoint capture and a test that withThinkingKeep forces the beta endpoint even when constructed with betaApi: false, pinning down the documented behavior. |
||
|
|
dd9077595d
|
chore(agent-core): classify turn_interrupted telemetry cause (#1431)
Add an `interrupt_reason` field to the `turn_interrupted` telemetry event so the data can tell a deliberate user cancel (`user_cancelled`) apart from a programmatic abort (`aborted`), max-steps exhaustion (`max_steps`), an error (`error`), or a hook-filtered turn (`filtered`). The user-cancel signal comes from the existing UserCancellationError carried as the abort signal's reason, reused here without changing any loop control or external protocol semantics. |
||
|
|
c12f30951f
|
feat(agent-core): feed AskUserQuestion answers back as question text and option labels (#1414)
* feat(agent-core): feed AskUserQuestion answers back as question text and option labels
The flattened answers record the model receives was keyed by synthesized
ids (q_0 / opt_0_1), forcing a cross-message positional lookup against the
original tool call to understand what the user picked — both unreadable in
transcripts and a real model-misreads-the-choice badcase.
- toAgentCoreResponse now takes the original broker request and translates
wire ids back to question text (keys) and option labels (values);
unknown ids are kept verbatim, missing request falls back to raw ids
- wire protocol unchanged: clients still answer with option ids; the
resolve route reads the pending request before settling it
- question texts must be unique per call and option labels unique per
question, enforced in the tool execution path (AJV cannot express the
zod refine) and mirrored on the exported schemas
- web transcript card resolves both the new label form and legacy id
transcripts; TUI and ACP paths already produced the text form
* fix(agent-core): align multi-select answer join across clients and harden question schema
- Join multi-select labels with ', ' in the server translator, matching
what the TUI reverse-RPC path already emits, so the model sees one
format regardless of which client answered
- Trim segments in the web transcript resolver before label matching:
TUI-answered multi-select transcripts (', '-joined) previously lost
their highlight to a spurious leading-space Other row
- Move the question-text/legacy-q_<i> answer lookup out of the SFC into
askUserToolParse as answerFor(), per that module's testability intent
- Require non-empty question text and option labels (.min(1)) so empty
strings are rejected by AJV at the tool boundary instead of failing
deeper in the protocol layer
* fix(agent-core): resolve option ids only within the answered question
The translator's option-id lookup was a single flat map across all
questions, so a stale or malformed response pairing one question with
another question's option id (q_1 + opt_0_0) was silently translated
into a label that was never offered for that question. Scope the lookup
to the answered question's own options; cross-question and unknown ids
now both pass through verbatim, staying diagnosable.
|
||
|
|
f0896a53b0
|
feat(agent-core): progressive tool disclosure via select_tools (#1369)
Some checks are pending
CI / build (push) Waiting to run
CI / test (push) Waiting to run
CI / test-pi-tui (push) Waiting to run
CI / test-windows (push) Waiting to run
CI / lint (push) Waiting to run
CI / typecheck (push) Waiting to run
Nix Build / Check flake.nix workspace sync (push) Waiting to run
Nix Build / nix build .#kimi-code (push) Blocked by required conditions
Release / Release (push) Waiting to run
Release / Deploy docs (push) Blocked by required conditions
Release / Native release artifact (push) Blocked by required conditions
Release / Desktop release artifact (push) Blocked by required conditions
Release / Publish native release assets (push) Blocked by required conditions
* feat(agent-core): progressive tool disclosure via select_tools Keep MCP tool schemas out of the immutable top-level tools[] and let the model load them on demand, preserving the provider prompt cache: - kosong: Message.tools (append-only load primitive, serialized as Kimi messages[].tools with type:function wrapping and no content), Tool.deferred (stripped once in generate() so loaded tools stay executable without re-entering the top level), select_tools capability bit (UNKNOWN/catalog default false). - select_tools builtin: load-by-exact-name, three-branch semantics settled per name (Loaded / Already available / Unknown), schemas read from the live registry, injection-origin schema messages survive undo. - ToolsDiffInjector: <tools_added>/<tools_removed> announcements at turn boundaries and post-compaction, folded from history (undo/compaction/ resume self-heal), appended only when the loadable set changes. - Loaded-tools ledger = history scan + defer-window pending set (cleared on /clear); loop re-reads the executable table per step so a selected tool dispatches on the next step of the same turn; preflight distinguishes not-loaded from loaded-but-disconnected. - Cross-cuts: projection strips protocol context for non-select_tools models (lossless mid-session model switch both ways), compaction filters it from the summarizer input and rebuilds loaded schemas keep-all after folding, token estimation counts message.tools, request logging reflects the post-strip wire tools. - Three-condition gate: capability.select_tools x capability.tool_use x tool-select experimental flag (KIMI_CODE_EXPERIMENTAL_TOOL_SELECT). Any gate closed reproduces the inline request byte-for-byte; all current models keep the capability off, so behavior is unchanged until a supporting model is catalogued. The SDK catalog-to-alias mapping forwards the capability so catalog-driven setups can enable it. * feat(kosong): skip tool-declaration-only messages in non-Kimi providers Message-level tool declarations (messages[].tools) are a Kimi wire feature. The other providers' explicit field construction already keeps the tools field off the wire, but the content-free leftover message would be rejected (OpenAI: system message without content) or serialize as a garbage <system></system> turn (Anthropic/Google system-to-user wrapping). Skip such messages entirely via a shared predicate; a message that also carries content only loses the tools field, as before. Unreachable in kimi-code (the projection gate strips dynamic-tool context for models without the select_tools capability before any provider sees it) — defense-in-depth for direct kosong consumers. * fix(agent-core): survive runtime flag flips and align tool table with post-compaction state Two fixes from PR review: - Register select_tools unconditionally and gate only its exposure in loopTools. The tool-select flag can flip at runtime (config reload calls setConfigOverrides on the live resolver) without initializeBuiltinTools re-running; previously the disclosure shape activated while the tool itself was unregistered, cutting the session off from MCP entirely until a model/cwd change rebuilt the builtins. A profile listing the name explicitly still never surfaces it in inline mode, and execution guards the flip race defensively. - Resolve the per-step tool table AFTER beforeStep, next to buildMessages. beforeStep can run full compaction, which trims loaded schemas and rewrites the ledger; a table captured before it could still dispatch a tool whose schema the model no longer has. The executable table and the request messages now always reflect the same state, so a trimmed tool is rejected with select guidance instead of executed. * fix(agent-core): drop unused Tool import in dynamic-tools * fix(agent-core): baseline compaction guard after post-compaction reinjection The reinjected reminders (loadable-tools manifest, goal) are re-appended after every compaction, but the nothing-new-since-compaction baseline was captured before injectAfterCompaction. With a large manifest the guard could re-trigger auto-compaction against a floor that cannot shrink. Raise the baseline to the true post-compaction floor once reinjection completes; the earlier capture stays as a fallback when reinjection throws. --------- Co-authored-by: fengchenchen <fengchenchen@moonshot.ai> |
||
|
|
79b360c96a
|
feat(thinking): enable Preserved Thinking by default for kimi models (#1417)
Default `thinking.keep` to "all" when Thinking is on so prior `reasoning_content` is kept across turns. Add `[thinking] keep` to config.toml and keep `KIMI_MODEL_THINKING_KEEP` as an override (env > config > default); off-values disable it. |
||
|
|
083d0caf05
|
fix(session): rebuild index on boot to find missing sessions (#1390)
Some checks are pending
CI / build (push) Waiting to run
CI / test (push) Waiting to run
CI / test-pi-tui (push) Waiting to run
CI / test-windows (push) Waiting to run
CI / lint (push) Waiting to run
CI / typecheck (push) Waiting to run
Nix Build / Check flake.nix workspace sync (push) Waiting to run
Nix Build / nix build .#kimi-code (push) Blocked by required conditions
Release / Release (push) Waiting to run
Release / Deploy docs (push) Blocked by required conditions
Release / Native release artifact (push) Blocked by required conditions
Release / Desktop release artifact (push) Blocked by required conditions
Release / Publish native release assets (push) Blocked by required conditions
* feat(session): rebuild session index on boot and self-describe workDir - persist workDir into state.json so session dirs are self-describing and summaries do not depend on the index's one-way-hashed workDir - relax readSessionIndex so a stale or non-absolute index workDir no longer drops an otherwise valid entry - serialize in-process index appends to avoid torn jsonl lines - add SessionStore.reindex() and run it once at server boot so the scan-free request path can find sessions whose index line is missing or stale * chore: add changeset for session index rebuild * fix(session): repair index entries with a stale workDir during reindex |
||
|
|
ebdffc7df7
|
fix(gemini): fix Gemini tool calling and thought-signature round-trip (#1389)
- send tool declarations, system prompt, and sampling/thinking settings in the camelCase shape the Google SDK forwards so tool calls reach the model - thread tool-call extras (thought signatures) through the loop tool-call event into context so Gemini 3 can resume a tool turn - update and add tests for the corrected request shape and signature round-trip |
||
|
|
d111c02ea0
|
fix(agent-core): cap background shell output to match foreground (#1372)
Background (detached) shell commands were exempt from the 16 MiB output ceiling, so a runaway background command could fill the disk or crash the process. Apply the same cap to background shell commands and stop feeding the disk write chain once it trips. Scope the ceiling to process tasks so subagent and user-question results, which are appended once and must be persisted, are left untouched. |
||
|
|
5394feaabb
|
feat: hold print-mode turn until background subagents drain (#1371)
* feat: hold print-mode turn until background subagents drain In `kimi -p` (print mode), when the main agent ends a turn while background subagents (`kind === 'agent'`) are still running, hold the turn open and idle-wait until they finish, flushing their completions into the turn so the model can react before the run exits. Previously, the main agent could end its turn after launching background subagents; the print flow then drained them with their completion notifications suppressed, so the main agent never saw the results and the run exited with the work abandoned (e.g. no nomination). This was the root cause of the swarm-alpha-mining eval failures. The hold is gated on a new `drainAgentTasksOnStop` session option (set by the print flow), only affects `kind === 'agent'` background tasks, and is bounded by `background.printWaitCeilingS`. Backfill / fan-out is handled by re-enumerating active tasks. Other background task kinds and non-print modes are unaffected. |
||
|
|
e9db9cafcf
|
feat: record model response id in wire logs (#1349)
Some checks are pending
CI / build (push) Waiting to run
CI / test (push) Waiting to run
CI / test-windows (push) Waiting to run
CI / lint (push) Waiting to run
CI / typecheck (push) Waiting to run
Nix Build / Check flake.nix workspace sync (push) Waiting to run
Nix Build / nix build .#kimi-code (push) Blocked by required conditions
Release / Release (push) Waiting to run
Release / Deploy docs (push) Blocked by required conditions
Release / Native release artifact (push) Blocked by required conditions
Release / Desktop release artifact (push) Blocked by required conditions
Release / Publish native release assets (push) Blocked by required conditions
* feat: record model response id in wire logs * chore: include sdk in response id changeset * chore: include agent-core in response id changeset |
||
|
|
02da587795
|
feat(cli): wait for background subagents before exiting kimi -p (#1347)
* feat(agent-core): guide the model away from repeating denied or failed tool calls - system.md: add a diagnose-before-retrying paragraph next to the existing permission-denial guidance, covering failed tool calls - permission: when the user rejects an approval on the main agent, tell the model not to re-attempt the exact same call (sub agents already had an equivalent hint) * fix(agent-core): close abandoned tool exchanges and dedupe duplicate tool_use ids A turn that dies between a recorded tool.call and its paired tool.result (e.g. a transcript write failure mid-batch) used to leave pendingToolResultIds open forever: every later message was stranded in deferredMessages and user input was silently swallowed. - runOneTurn now defensively closes any dangling tool calls when a turn ends (completed, cancelled, or failed), synthesizing an error result that names the cause, with a warn log and a tool_exchange_abandoned telemetry event - the projector drops assistant tool calls whose id already appeared earlier (first occurrence wins): a duplicate id is wire-invalid on strict providers and not repairable by the strict resend; reported via the existing projection-repair log and telemetry - resume-side closePendingToolResults now logs what it closes (warn for a mid-history gap, info for the routine trailing interruption) * chore: add changesets for tool exchange fixes * fix(agent-core): scope duplicate tool_use id dedup to the strict resend Unconditional dedup regressed providers that emit per-response counter ids (e.g. call_0 in every step) and accept their own duplicates: later tool exchanges silently vanished from the projected history, and a duplicate call's own recorded result was left dangling. - the dedupe pass is now opt-in via dedupeDuplicateToolCalls and enabled only in strictMessages, so the normal projection keeps the history the provider produced - the pass also drops every tool result after the first for an id, so no dangling tool message survives; when the kept call has no result of its own, the surviving one is reattached by the adjacency repair - kosong now classifies the Anthropic "tool_use ids must be unique" 400 as a recoverable request-structure error so it triggers the strict resend * feat(cli): wait for background subagents before exiting kimi -p When `background.keep_alive_on_exit` is enabled, `kimi -p` now waits for all background subagents to reach a terminal state before exiting, bounded by `background.print_wait_ceiling_s` (default 3600s). This lets concurrent background subagents run to completion in single-turn runs instead of being torn down when the main agent's turn ends. |
||
|
|
175b95f3af
|
fix(agent-core): route image-compression captions through hidden system reminders (#1348)
* fix(agent-core): route image-compression captions through hidden system reminders
Prompt ingestion (server upload/base64 route, TUI paste, ACP) annotates a
compressed image with an inline <system> caption inside the user's own
message. That raw markup rendered verbatim in every user-visible history
projection (TUI session replay, web UI) and leaked into session titles.
Split the caption out at the appendUserMessage chokepoint and deliver it
through the built-in system-reminder injection (origin
{kind: 'injection', variant: 'image_compression'}), which every UI already
hides. The model still receives the full note; ingestion sites and the wire
protocol are unchanged. Session titles/lastPrompt strip the caption the same
way. Tool-result captions (MCP) keep the established <system> convention.
Covered by unit tests plus an end-to-end smoke suite that drives
rpc.prompt/steer through the real turn pipeline and asserts the provider
wire request, stored history, replay records, and resume parity.
* chore: tighten changeset wording per gen-changesets conventions
|
||
|
|
e2fe62a5ef
|
fix(agent-core): harden tool_use/tool_result exchange integrity (#1340)
Some checks are pending
CI / lint (push) Waiting to run
CI / test (push) Waiting to run
CI / test-windows (push) Waiting to run
CI / build (push) Waiting to run
CI / typecheck (push) Waiting to run
Nix Build / Check flake.nix workspace sync (push) Waiting to run
Nix Build / nix build .#kimi-code (push) Blocked by required conditions
Release / Release (push) Waiting to run
Release / Deploy docs (push) Blocked by required conditions
Release / Native release artifact (push) Blocked by required conditions
Release / Desktop release artifact (push) Blocked by required conditions
Release / Publish native release assets (push) Blocked by required conditions
* feat(agent-core): guide the model away from repeating denied or failed tool calls - system.md: add a diagnose-before-retrying paragraph next to the existing permission-denial guidance, covering failed tool calls - permission: when the user rejects an approval on the main agent, tell the model not to re-attempt the exact same call (sub agents already had an equivalent hint) * fix(agent-core): close abandoned tool exchanges and dedupe duplicate tool_use ids A turn that dies between a recorded tool.call and its paired tool.result (e.g. a transcript write failure mid-batch) used to leave pendingToolResultIds open forever: every later message was stranded in deferredMessages and user input was silently swallowed. - runOneTurn now defensively closes any dangling tool calls when a turn ends (completed, cancelled, or failed), synthesizing an error result that names the cause, with a warn log and a tool_exchange_abandoned telemetry event - the projector drops assistant tool calls whose id already appeared earlier (first occurrence wins): a duplicate id is wire-invalid on strict providers and not repairable by the strict resend; reported via the existing projection-repair log and telemetry - resume-side closePendingToolResults now logs what it closes (warn for a mid-history gap, info for the routine trailing interruption) * chore: add changesets for tool exchange fixes * fix(agent-core): scope duplicate tool_use id dedup to the strict resend Unconditional dedup regressed providers that emit per-response counter ids (e.g. call_0 in every step) and accept their own duplicates: later tool exchanges silently vanished from the projected history, and a duplicate call's own recorded result was left dangling. - the dedupe pass is now opt-in via dedupeDuplicateToolCalls and enabled only in strictMessages, so the normal projection keeps the history the provider produced - the pass also drops every tool result after the first for an id, so no dangling tool message survives; when the kept call has no result of its own, the surviving one is reattached by the adjacency repair - kosong now classifies the Anthropic "tool_use ids must be unique" 400 as a recoverable request-structure error so it triggers the strict resend |
||
|
|
84d8d5b063
|
feat(agent-core): make compaction notes capture a forward plan, not just the next step (#1342)
Compaction runs at the point of maximum context for the task, and the next turn resumes with less. So the handoff note now records the plan for the remaining work — upcoming steps, settled decisions, and foreseeable obstacles, plus any work that can be pre-committed — instead of only the immediate next command. Update the affected compaction snapshots and one hardcoded input-token assertion (the instruction is ~163 tokens longer). |
||
|
|
78a058acd2
|
chore(agent-core): remove experimental micro compaction (#1317)
* chore(agent-core): remove experimental micro compaction * fix(docs): drop micro compaction row from env-vars table |
||
|
|
4dd926b0ac
|
fix(agent-core): recover sessions bricked by orphan tool results (#1308)
* fix(agent-core): recover sessions bricked by orphan tool results A stray `tool` message with no preceding assistant `tool_calls` permanently bricked a session on OpenAI-compatible providers: every turn re-sent the same malformed history and got a 400, and switching model/provider did not help. Two independent gaps caused this: - kosong did not recognize the OpenAI / DeepSeek / vLLM / Qwen phrasings of the tool-exchange structural 400 (`role 'tool' must be a response to a preceding message with 'tool_calls'` and the mirror `assistant message with 'tool_calls' must be followed by tool messages`), so the post-400 strict-resend fallback that drops the orphan never fired. - The legacy-restore compaction path kept a verbatim tail `history.slice(compactedCount)`; when the cut landed inside a tool exchange the tail began with an orphan tool result whose assistant was summarized away. The normal projection does not repair a leading orphan, so the malformed history was baked in and re-sent every turn. Recognize the additional phrasings so the strict resend un-bricks any session, and trim leading tool results from the legacy-restore tail so the orphan is never persisted in the first place. * fix(agent-core): drop orphan tool results at the projection boundary Rework the legacy-restore half of the fix based on review feedback: mutating `_history` at restore time desyncs every consumer that models the history from the wire records — the transcript reducer's fold length would overcount and make MessageService skip unflushed live-tail messages. Keep the restored history faithful to the wire records instead, and drop a `tool` result whose call is nowhere in the history at the projection boundary, on every request-building projection: the normal wire (`messages`), the post-400 strict resend (`strictMessages`), and the compaction summarizer. An orphan is wire-invalid on strict providers and useless to the model either way, so it never reaches a provider — no longer relying on recognizing the provider's 400 phrasing to recover. Fragment projections (e.g. token-estimating a history slice) leave results untouched, since a matching call may legitimately sit outside the slice. |
||
|
|
329846c569
|
feat(agent-core): keep head and tail of user messages during compaction (#1313)
* feat(agent-core): keep head and tail of user messages during compaction Compaction used to keep only the most recent 20k tokens of real user input, so the original task statement was the first thing to vanish in long sessions. Now, when the user-message pool fits the 20k budget it is still kept whole; when it overflows, the oldest 2k tokens and the most recent 18k are kept instead, with an elision marker between the two segments telling the model what was omitted and that the summary covers it. The summary prefix and the default system prompt describe the new shape as well. The new `keptHeadUserMessageCount` record field keeps restore and the wire-transcript folded length consistent: records without it (written by older versions) restore with the original tail-only selection that produced them, and the vis model-mode projection mirrors the same head/marker/tail rebuild. * style(agent-core): drop redundant spread over slice in head selection |
||
|
|
0fc0ae380b
|
feat(agent-core): announce image compression and keep originals readable (#1304)
* feat(agent-core): announce image compression and keep originals readable Every image ingestion point (ReadMediaFile, MCP tool results, clipboard paste, REST upload/inline base64, ACP) now places a <system> caption next to a compressed image stating the original vs. delivered dimensions, byte size, and format, so downsampling is never silent to the model. Originals stay readable: file uploads point at the stored blob, and in-memory images are persisted into the session's media-originals dir (content-addressed, size-capped, removed with the session; temp-dir fallback when no session is known). ReadMediaFile gains region (crop in original-image pixel coordinates, delivered at full fidelity) and full_resolution (skip downscaling, with an explicit error over the per-image byte limit), so the model can zoom into fine detail instead of silently degrading on large images. * fix(agent-core): exempt compression captions from the MCP text budget The caption announcing an image's compression was inserted before the shared 100K text budget was applied, so a chatty MCP result (page text + screenshot) consumed the budget first and the caption was evicted — or sliced mid-string into an unclosed <system> fragment — while the downsampled image survived, silently reintroducing the exact degradation the caption exists to report, and orphaning the persisted original. Split the size-limit pass in two and reorder the pipeline: the text budget now runs on the tool's own text BEFORE compression inserts captions (exempt by construction), and the per-part binary cap still runs after compression so compressible screenshots are kept. * fix(agent-core): harden crop error reporting and document readback semantics - cropImageForModel rejects non-finite region coordinates with a clean message instead of surfacing the codec's internal validation dump - the full_resolution and cropped-region over-budget errors now include exact byte counts alongside the rounded sizes, so a file a hair over budget no longer reads "is 3.8 MB, over the 3.8 MB limit" - read-media.md notes that re-reading a file without region or full_resolution reproduces the same downsampled image |
||
|
|
93ec6cb652
|
fix(kosong): recognize OpenAI-compatible tool_call_id 400 as a recoverable tool-exchange error (#1292)
Some checks are pending
CI / build (push) Waiting to run
CI / test (push) Waiting to run
CI / test-windows (push) Waiting to run
CI / lint (push) Waiting to run
CI / typecheck (push) Waiting to run
Nix Build / Check flake.nix workspace sync (push) Waiting to run
Nix Build / nix build .#kimi-code (push) Blocked by required conditions
Release / Release (push) Waiting to run
Release / Deploy docs (push) Blocked by required conditions
Release / Native release artifact (push) Blocked by required conditions
Release / Desktop release artifact (push) Blocked by required conditions
Release / Publish native release assets (push) Blocked by required conditions
Moonshot / Kimi (OpenAI-compatible) rejects a history whose tool message references a tool_call_id with no matching tool_calls entry in the preceding assistant message as `400 tool_call_id is not found`. The TOOL_EXCHANGE_ADJACENCY_MESSAGE_PATTERNS only covered Anthropic's tool_use/tool_result phrasing, so isRecoverableRequestStructureError returned false, the strict-resend fallback in executeLoopStep never fired, and the session stayed permanently stuck re-sending the same rejected history every turn (observed in the field after a manual compaction busted the prompt cache and forced full revalidation of a latently misordered prefix). Add the tool_call_id-anchored pattern so the whole recovery chain — strict projection (adjacency repair, orphan-result drop, synthetic results) plus the one-shot resend — now also covers the default provider. Covered by classifier unit tests and an e2e resend-and-recover case. |
||
|
|
ea55911062
|
feat(agent-core): sharpen the compaction handoff prompt (#1283)
* feat(agent-core): sharpen the compaction handoff prompt Refine the first-person handoff note the model writes at compaction so it preserves what actually gets dropped instead of what already survives: - Lead with the intent of the latest request, not a verbatim re-transcription (the recent user messages are already kept verbatim beside the summary); name which request governs when several are in play. - Carry forward tool results — the concrete values, key lines, schemas — not just the commands that produced them. - Keep settled decisions separate from still-open questions, and name the context the next turn must go and re-check. - Write in the conversation's language, keep the note proportional to the task, and don't re-transcribe the auto-attached TODO list. Also correct the system prompt's description of the post-compaction shape: the recent user messages come first, followed by a first-person summary (not a rigidly sectioned report), and a newer kept message supersedes the summary. Update the affected inline snapshots and the compaction request token count. * fix(agent-core): preserve an oversized latest request in the handoff note selectRecentUserMessages truncates a kept user message to the size cap, keeping only its prefix, so when the latest request itself exceeds the cap only its head survives verbatim beside the summary. Telling the summary "don't re-transcribe, it survives verbatim" then permanently dropped the tail — often the actual ask. Keep the intent-not-transcription guidance, but require preserving the at-risk parts of a long current request. |
||
|
|
c434b4c3e6
|
fix(agent-core): cap foreground shell output to prevent OOM crash (#1285)
* fix(agent-core): cap foreground shell output to prevent OOM crash A foreground command that streams a very large or unbounded amount of output (e.g. `b3sum --length 18446744073709551615`) grew the live-output buffer until Node aborted with a JavaScript heap out-of-memory error (exit 134). The per-command output is now capped at 16 MiB: on breach the command is gracefully terminated (SIGTERM -> grace -> SIGKILL) and the result carries a message pointing at redirecting large output to a file. The per-task output ring buffer is also made O(1) per chunk (was O(n^2)), which previously starved the event loop and the foreground timeout. Background/detached tasks are exempt. * fix(agent-core): stop buffering output after the foreground cap trips After the 16 MiB foreground ceiling tripped, appendOutput still enqueued every subsequent chunk into the per-task disk write chain during the SIGTERM grace window. A producer that ignores SIGTERM could keep that chain — and the chunk strings each pending write retains — growing until SIGKILL, re-introducing the same out-of-memory risk the cap prevents. Once the cap has tripped, keep only the bounded in-memory ring buffer and stop feeding the disk write chain. Interrupt/timeout capture behaviour is unchanged (they do not set outputLimitTripped). |
||
|
|
a5db546d77
|
feat: add KIMI_MODEL_THINKING_EFFORT to force a thinking effort (#1275)
Some checks are pending
CI / build (push) Waiting to run
CI / test (push) Waiting to run
CI / test-windows (push) Waiting to run
CI / lint (push) Waiting to run
CI / typecheck (push) Waiting to run
Nix Build / Check flake.nix workspace sync (push) Waiting to run
Nix Build / nix build .#kimi-code (push) Blocked by required conditions
Release / Release (push) Waiting to run
Release / Deploy docs (push) Blocked by required conditions
Release / Native release artifact (push) Blocked by required conditions
Release / Desktop release artifact (push) Blocked by required conditions
Release / Publish native release assets (push) Blocked by required conditions
* feat: add KIMI_MODEL_THINKING_EFFORT to force a thinking effort Send thinking effort only when the model declares it in support_efforts, and add the KIMI_MODEL_THINKING_EFFORT environment variable as an escape hatch to force a specific effort regardless of declared support. * test: align thinking effort expectations with support_efforts gating Update the kimi adapter e2e and compaction tests that asserted the previous pass-through behavior on models without support_efforts. |
||
|
|
b905dd4910
|
feat(web): redesign web UI and add design system (#1258)
* feat: redesign web ui & add design system * feat(web): add motion to redesigned UI and add changesets Animate toast enter/leave, dialog open, and workspace-list and tool-row expand/collapse instead of snapping, and add the changesets covering the web redesign. * fix(web): remove undo message exit animation * fix(web): route agent tools to AgentTool and focus dialog on open - toolRegistry matched the raw 'agent' name, but normalizeToolName folds agent/subagent into 'task', so agent calls fell through to GenericTool and lost the inline Open button for the subagent detail panel. - Dialog's focus watcher only fired on change; callers that mount with open already true (Login, Settings, ...) never moved focus into the modal. Run it immediately so initial focus and restore-on-close work. * feat(web): add logo long-press design-system easter egg Hold the sidebar logo for 3 seconds to open a dialog showing the design system page. Also trim and rebalance the redesign changesets. * fix(web): silence Sidebar v-show warning by making it single-root Nest the design-system Teleport inside the sidebar <aside> so the component has a single element root. App applies v-show to Sidebar, which needs an element root to attach to; the fragment root logged a "non-element root node" warning on every reactive update and the collapse did not take effect. * fix(web): thinking toggle, tool-group i18n, agent detail button - Show the default-thinking switch as on whenever thinking is effectively enabled (enabled !== false), matching the core resolver. - Route the grouped tool-call header and status through vue-i18n. - Hide the subagent "Open detail" button when no matching task exists (e.g. a completed foreground subagent after a refresh). * fix(web): use strict equality in agent detail button guard oxlint eqeqeq flagged the loose != null check; resolveAgentTaskId returns string | undefined, so compare with !== undefined. * chore(web): remove stray design mockups and screenshots Remove the design exploration mockups, screenshots, prompts, and notes that were accidentally committed under apps/kimi-web/design. Keep design-system.html, which the sidebar logo easter egg still references. * fix(web): keep sessions on continuation failure, treat absent thinking as on - Keep sessions already loaded from earlier pages when a continuation page fetch fails, instead of replacing the workspace with an empty page. - Treat an absent thinking config as enabled in the settings toggle, matching the core resolver (thinking is on unless explicitly disabled). * feat(web): open design-system easter egg on 10 logo clicks Replace the 3-second long-press trigger with 10 consecutive clicks on the Kimi mark; the count resets after a short idle. The long-press was unreliable because pointerleave cancelled the timer on any drift. * fix(web): treat cancelled swarm members as finished phaseForTask now lets a terminal status (completed/failed/cancelled) override a stale subagentPhase, so a cancelled swarm member no longer stays live and suppresses the finished AgentSwarm card. * feat(web): use 1s long-press for design-system easter egg Switch the logo easter egg back to a long-press, shortened to 1 second, and make it reliable this time: use pointer capture plus touch-action:none so a slight drift no longer cancels the hold. * fix(web): use plain Spinner for activity notices ActivityNotice renders for non-chat loading states (e.g. compaction), so it must use Spinner per the design-system rule that reserves MoonSpinner for the chat first-response state. * docs(web): add a11y guidance to design system * docs(web): drop stale design README link * fix(web): restore model search focus and define panel header weight - Bind the model picker's search Input to searchRef so useDialogFocus moves focus into it on open instead of the dialog's close button. - Use the defined --weight-semibold token for panel header titles (--weight-bold is not declared, so the shorthand was invalid). * fix(web): let any open dialog own Escape over the side panel Track open design-system Dialog instances in a shared count and include it in App.vue's anyOverlayOpen, so a dialog whose open state lives outside App.vue (such as the sidebar session search) captures Escape before the background side panel closes. * fix(web): base dock-work flag on filtered dock task lists Foreground subagents are excluded from the dock task lists, so a session whose only task is a foreground subagent no longer renders an empty workbar above the composer. * fix(web): create subagent task before forwarding text deltas A client that subscribes from a snapshot after subagent.spawned already fired never received the lifecycle taskCreated; the reducer only applies taskProgress to existing tasks, so assistant text deltas were dropped and the live subagent detail stayed blank. Emit taskCreated (via patchSubagent) before the text progress, mirroring the tool-progress path. * fix(web): keep plan, swarm, and goal mode toggles per session Plan, swarm, and goal modes were stored as global scalars on the web client and a single localStorage key each, so they leaked across sessions. Bind them to the active session via per-session maps, persist per session, and apply server status/events to the originating session so background sessions keep independent state. * fix(web): keep subagent detail reachable for synthesized tasks When the web client subscribes after a subagent already spawned, the synthesized subagent task has no parentToolCallId, so the Agent tool's Open-detail button was hidden and the panel would not open. Fall back to the single unmapped subagent task when resolving the detail target in both the button visibility and the panel open paths. * fix(web): keep session kebab menu from being clipped Teleport the SessionRow kebab menu to body and anchor it with fixed positioning so the collapsing group-sessions list's overflow:hidden no longer clips the dropdown. * fix(web): apply staged modes to the created session by id When starting the first prompt, apply the staged plan/swarm/goal modes to the just-created session's per-session maps by id instead of via the activeSessionId-based setters, so a session switch during the selectSession await can't drop the modes for this session or pollute another. * refactor(web): unify confirmation dialogs into a single modal Replace the inconsistent confirmation patterns (native confirm(), two-step menu arming, hand-rolled inline strips, bare buttons) with one modal ConfirmDialog driven by a global useConfirmDialog() composable, and consolidate the duplicated confirm/cancel i18n keys. * feat(web): inline message queue with separate stop button - Send button always sends/enqueues; interrupt moves to a separate red Stop button shown only while running, so the two can no longer be confused. - Queued prompts now render inline at the tail of the transcript (after the running turn) instead of behind the dock panel: click to edit, remove, drag the grip to reorder, with image thumbnails and a "next up" marker. - Remove the dock queue panel and the QueuePane component; Steer stays on Ctrl/Cmd+S. * chore: add changeset for web queue UX * feat(web): prompt reliability, sidebar menu, and composer/markdown polish - Fix spurious errors when question/approval/task actions were already complete - Add loading feedback to question and approval prompts; block double-clicks - Make the question "Other" option selectable by row click and let Enter advance/submit - Consolidate workspace section actions into an overflow menu - Tighten markdown prose line-height and block spacing - Recall input history only when the caret is at text start |
||
|
|
3cb7936cf5
|
chore(telemetry): track conversation undo, shell mode, and effort changes (#1271)
* chore(telemetry): track conversation undo, shell mode, and effort changes
- conversation_undo: fires when an undo reverts history (double-Esc selector, /undo, /undo N)
- shell_command: fires when a ! shell command runs in the TUI
- thinking_toggle: now fires on any effort change (not just on/off flips) and carries { enabled, effort, from }; align the no-session TUI path to the same payload
* chore(telemetry): track conversation_undo in core undoHistory
Tracking it in the TUI only covered the TUI; web/acp/REST undos reach
core.rpc.undoHistory directly and were missed. Emit the event from the
agent undoHistory RPC after the undo succeeds so every host is covered,
and drop the now-redundant TUI track.
Addresses review feedback on #1271.
|