mirror of
https://github.com/MoonshotAI/kimi-code.git
synced 2026-07-10 01:39:25 +00:00
13 commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
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 |
||
|
|
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
|
||
|
|
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 |
||
|
|
86e0c9201e
|
feat(agent-core): rework compaction to keep only user prompts and summary (#1214)
* feat(agent-core): rework compaction to keep only user prompts and summary
* refactor(agent-core): rewrite compaction summary as first-person handoff
Rework the full-compaction summary to read as the agent's own continuing
notes instead of a third-party report:
- compaction-instruction.md: free-form first-person continuation that
preserves exact commands, paths and outcomes, states the precise next
action, and flags claimed-but-unverified work rather than trusting it.
- compaction-summary-prefix.md: skeptical "your own working notes"
framing; drop the collaborative third-party prefix.
- system.md: add compaction-awareness guidance so the model continues
naturally from a summary and re-checks any reported "done".
- Rename the compaction helpers module to handoff.ts.
Update tests and regenerate snapshots for the new prompt text, and fill
in contextSummary in the restored-compaction replay expectations.
* fix(agent-core): count image/audio/video parts in token estimation
estimateTokensForContentPart returned 0 for image_url/audio_url/video_url,
so auto-compaction triggers, the overflow-shrink budget, the kept-user
budget, and the reported context size all went blind to media — a
media-heavy session could overflow the model window while the estimate
reported a near-empty context. Media parts now carry a fixed estimate
(MEDIA_TOKEN_ESTIMATE), and the content-part switch is exhaustive so a new
ContentPart kind must declare its estimate rather than silently count as
zero.
* feat(agent-core): re-surface active background tasks after compaction
Folding the live context to [recent user prompts, summary] drops the
messages that started background tasks and their status updates, so the
model could forget a task is still running and spawn a duplicate.
injectAfterCompaction now appends a system-reminder listing active
background tasks (with guidance to use TaskOutput/TaskList/TaskStop
instead of re-spawning). It runs only post-compaction and carries an
injection origin, so the next compaction drops and rebuilds it rather
than stacking copies; the all-user-role post-compaction shape is
preserved (no tool-pairing reintroduced).
* test(agent-core): add compaction scenario guards and risk probes
Adds compaction-scenarios.test.ts driving the real Agent/ContextMemory/
FullCompaction machinery:
- A guard test locking in that repeated compaction folds the prior summary
into the new one instead of stacking two summaries.
- Seven `it.fails` probes that executably reproduce known, currently-accepted
edge-case defects so the suite stays green while documenting each one
precisely; any of them will flip red (forcing removal of `.fails`) the day
the behavior is fixed. They cover: assistant/tool appended during an
in-flight summarizer call being dropped; unbounded shrink on empty
summaries; the fixed 20k kept-user budget overflowing a small model window;
a tool result orphaned when compaction starts mid-exchange; legacy
compaction records dropping their verbatim tail on replay; micro-compaction
clearing recent tool results in an overflow-shrunk suffix; and media being
discarded when the oldest kept user message is truncated.
* fix(agent-core): repair tool_use/tool_result adjacency in projected context
A tool call and its result can end up non-adjacent in history — a
background-task notification or flushed steer lands between them, or an
interrupted/nested step delays the result — which strict providers reject
with HTTP 400. The projector now moves each tool_use's result up to
immediately follow it (projection-time only; the stored history is
untouched), and full compaction projects its summarizer input with a
synthetic result for any still-open call so the summary request stays
well-formed. Micro-compaction only surfaced this latent ordering by busting
the prompt cache, so it now defaults off.
Includes projector adjacency regression tests, a context-level integration
test, and a compaction synthesize-missing guard; the prior "keeps an
unresolved tool exchange out of the compaction prompt" test is updated to
the now-well-formed (synthetic-result) behavior.
* fix(agent-core): preserve the verbatim tail when restoring legacy compactions
A pre-rework `context.apply_compaction` record used
`[summary, ...history.slice(compactedCount)]` semantics and kept a verbatim
recent tail, but it has no `keptUserMessageCount`. The reworked applyCompaction
re-folded such records into the all-user shape, dropping the recent
assistant/tool tail — so resuming a session compacted by an older version
silently lost its most recent context.
On restore of such a record (gated on records.restoring, no keptUserMessageCount,
and compactedCount < history length) reproduce the old shape instead. The
forward/live path is unchanged; the projector's tool-adjacency repair keeps the
restored tail well-formed, and compaction only runs at clean step boundaries so
the tail has no open exchange. The legacy-tail probe now passes as a regression
guard via the real restore path.
* fix(agent-core): align legacy compaction foldedLength with live restore
The transcript reducer re-derived foldedLength for pre-rework
context.apply_compaction records (no keptUserMessageCount) using the new
kept-user+summary rule, but ContextMemory's restore now reproduces the legacy
[summary, ...history.slice(compactedCount)] shape for those records. The two
diverged for legacy sessions, so MessageService's foldedLength-vs-live-history
comparison could mis-handle GET /messages (miss or misorder recent output).
The reducer now mirrors the live legacy fold: when compactedCount is below the
pre-compaction length it computes 1 + (length - compactedCount); otherwise it
falls back to the kept-user derivation. The MessageService transcript test's
fixture is corrected to a new-format record, matching its all-user live mock.
* fix(kosong): merge a follow-up user turn into the preceding tool_results
The Anthropic message merge keyed on isToolResultOnly(last) ===
isToolResultOnly(converted), which left a tool_result-only user turn
followed by a plain-text user turn unmerged. After tool-exchange repair
this shape (assistant tool_use -> tool_result -> injected notification)
produces two adjacent user messages, which strict Anthropic-compatible
backends reject with HTTP 400.
Switch to the asymmetric predicate isToolResultOnly(last) ||
!isToolResultOnly(converted): a tool-result-only running message absorbs
whatever user turn follows (parallel tool_results or a trailing text),
yielding a valid [tool_result, ..., text] message; a plain-text running
message still only absorbs plain text. [tool_result, text] is valid for
both native Anthropic (which concatenates anyway) and strict backends.
* test(agent-core): pin micro-compaction flag in the shrunk-suffix probe
The 'does not clear recent tool results when projecting a shrunk suffix'
probe is an it.fails that only documents a real defect while
micro-compaction is active. It inherited the ambient
KIMI_CODE_EXPERIMENTAL master switch, so its pass/fail flipped with the
runner: green locally (master switch on) but a hard failure in CI, where
the flag defaults off and MicroCompaction.compact() is a no-op that
leaves the tool result intact.
Enable KIMI_CODE_EXPERIMENTAL_MICRO_COMPACTION explicitly for this probe
so it deterministically exercises the micro-compaction path regardless of
the environment.
* fix(agent-core): harden full compaction against in-flight races, unbounded shrink, and media loss
Three compaction-path fixes surfaced by review, each flipping its
documenting it.fails probe to a passing it:
- Append race (CMP-02): after the summarizer returns, the post-summary
history check only compared the compacted prefix. A live step appending
to the tail while a manual/SDK compaction was in flight slipped through —
an appended assistant/tool turn is neither summarized (the summary covers
only the snapshot) nor kept (the rebuild keeps user input), so it
vanished. Now cancel when the appended tail contains a non-user message;
an appended user message is still kept (rebuild picks it up), preserving
the existing 'keeps messages appended while compacting an unchanged
prefix' behavior.
- Unbounded empty/truncated shrink: an empty or truncated summary dropped
the oldest message and reset retryCount, so a model that kept returning
empty could issue ~one request per history entry. Bound the shrink
attempts by MAX_COMPACTION_RETRY_ATTEMPTS, mirroring the overflow-shrink
counter.
- Media dropped on truncation (CMP-07): truncating the oldest kept user
message replaced its whole content with one text block, discarding any
image/audio/video. Keep the non-text parts and spend the remaining budget
(maxTokens minus their cost) on truncated text.
* fix(vis): mirror legacy compaction tail in the model-mode projector
For a pre-rework context.apply_compaction record (no keptUserMessageCount),
agent-core's ContextMemory restore and the transcript reducer keep the old
[summary, ...history.slice(compactedCount)] tail — a verbatim recent tail
including assistant/tool. The vis model-mode projector always applied the
new kept-user selection, so opening an older compacted session in model
mode hid the assistant/tool tail the resumed agent still holds (and
surfaced a pre-compaction user message the agent dropped).
Branch on a missing keptUserMessageCount with compactedCount < history
length and reproduce the legacy shape, matching the agent-core restore.
* fix(agent-core): cancel compaction on any droppable user-role tail
The in-flight append guard cancelled only when the tail grew with a
non-user role. A user-role message that compaction would still drop — a
background-task notification, hook/cron reminder, or shell-command output —
slipped through: appended after the summary snapshot (so absent from the
summary) and dropped by the all-user rebuild (which keeps only real user
input), vanishing silently.
Key the guard on the same predicate applyCompaction uses (!isRealUserInput)
so it cancels whenever the appended tail holds anything compaction would
drop. A real user message is still kept, so a live user turn racing a
manual/SDK compaction continues to complete.
* fix(agent-core): exclude pre-clear prompts from legacy folded length
The transcript reducer's legacy fallback (records predating
keptUserMessageCount, compacted with no verbatim tail) re-derived the
kept-user count from the whole transcript, including messages before the
last context.clear. Live ContextMemory rebuilds _history from post-clear
messages only, so counting pre-clear prompts overstated foldedLength;
MessageService then saw context.history.length <= foldedLength and skipped
appending unflushed live tail messages, dropping recent output from the
messages endpoint for old sessions compacted after a clear.
Derive only from entries at or after clearFloor to match the live context.
* fix(agent-core): drop media when truncating the oldest kept prompt
Revert the media-preserving truncation: keeping non-text parts on the
truncated boundary message overshot the kept-user budget when the media
alone exceeded it, and reordered interleaved text/media parts. Both codex
(no media-aware truncation) and Claude Code (strips media at compaction)
decline to preserve media on a truncated message, since media cannot be
partially truncated and keeping it whole breaks the budget.
truncateUserMessage now keeps only the truncated text. Recent messages
that fit the budget are still kept verbatim with their media; only the
oldest, partially-overflowing boundary message loses its attachments.
* fix(agent-core): make manual compaction and turns mutually exclusive
A manual/SDK compaction could start while a turn was streaming, or a new
turn could launch while a compaction was in flight. Either way the turn
mutates the shared context (streaming content into an existing assistant
message, or appending new messages) during the summarizer await, and that
output is neither summarized nor preserved by the all-user rebuild —
silent loss that object-identity checks can't detect (the streamed message
is mutated in place).
Guard both directions so the agent does one of {turn, compaction} at a
time: begin() refuses a manual compaction while a turn is active, and
launch() refuses a new turn while a compaction is in progress. Auto
compaction is exempt — it runs from within the turn at a step boundary,
which blocks the turn for its duration.
* chore(changeset): consolidate compaction changesets into one
* chore(agent-core): drop external-product references from compaction comments
* test(agent-core): add Anthropic wire-compliance smoke tests for compaction
Drive real compaction output and the compaction summarizer projection
through the real Anthropic provider conversion and assert the wire request
is well-formed: strict user/assistant alternation and every tool_use
answered by an adjacent tool_result. Locks in the cross-layer guarantee
(projector merge + Anthropic consecutive-user merge + adjacency repair +
synthesizeMissing) that compacted sessions stay valid for strict
Anthropic-compatible backends.
* fix(agent-core): defer and replay inputs during manual compaction instead of rejecting
Manual/SDK compaction runs outside a turn, so the earlier guard rejected
prompts/steers that arrived while it held the context. That broke three
things: a REST/web prompt got stuck 'running' (no terminal turn event), a
background-task/cron steer was silently lost (null was read as 'buffered'
but nothing was), and a follow-up prompt could land in the window after
isCompacting cleared but before reminders were reinjected.
Reuse the existing defer-and-replay model instead of rejecting:
- steer() and launch() buffer into steerBuffer while a compaction is in
progress (returning null = buffered), mirroring how an active turn defers
input.
- FullCompaction.compactionWorker keeps isCompacting true through
refreshSystemPrompt + injectAfterCompaction (moving markCompleted and the
completed event after reinjection), then replays the buffer via
TurnFlow.onCompactionFinished — on success, on an A1 prefix/tail cancel,
and on failure/abort.
- onCompactionFinished flushes into an active turn if one exists, else
launches a fresh turn from the deferred input.
No PromptService change: a deferred prompt's eventual turn.started lets it
associate the pending prompt and clear it on turn.ended.
* fix(kosong): merge consecutive user turns for strict providers
Gemini/Vertex require strictly alternating user/model turns and reject
consecutive user turns with HTTP 400. They arise after compaction (kept
prompts + user-role summary + injected reminders) and when a turn is
steered in right after a tool result. Anthropic already merged them
inline; the Google converter did not, so post-compaction requests failed.
Extract the asymmetric merge into a shared mergeConsecutiveUserMessages
helper applied at each strict provider's conversion boundary: refactor
Anthropic to use it (behavior unchanged) and apply it at the Google
converter's exit. A conformance suite drives every strict provider with
the post-compaction shape and a steer-after-tool-result shape, asserting
no consecutive same-role turns reach the wire, so a new strict provider
cannot silently omit the merge.
The provider-agnostic projector stays structure-preserving: lenient
providers (OpenAI/Kimi) keep distinct turns for clearer message
boundaries; only strict providers normalize, where the requirement lives.
|
||
|
|
108299be3c
|
refactor!: overhaul thinking config and effort resolution (#1132)
* feat: support multi-level thinking effort switching
- kimi provider: emit thinking.effort in the new wire format; keep reasoning_effort mirrored during the transition
- model catalog: thread support_efforts / default_effort from oauth through to /models
- config schema: add supportEfforts / defaultEffort on model aliases
- TUI: multi-segment thinking control in /model, new /effort command, footer effort display
- switch status uses displayName and distinguishes model vs effort-only changes
* docs: add thinking effort design plans
- thinking-effort-switching.md: implemented multi-level effort switching
- thinking-model-overhaul.md: follow-up refactor plan for the thinking state model
* docs: collapse thinking overhaul plan into a single PR
* refactor!: overhaul thinking config and effort resolution
Replace default_thinking and thinking.mode with a single [thinking] enabled/effort table. ThinkingEffort is now an open string ('off' | 'on' | model-declared effort); effort levels come from each model's support_efforts instead of a fixed enum.
Centralize default and always_thinking clamp logic in resolveThinkingEffort/defaultThinkingEffortFor, and honor an explicitly configured effort when an always_thinking model is forced back on.
TUI keeps a single thinkingEffort field instead of the boolean + level pair; 'on' is normalized to the model default at the UI boundary.
BREAKING CHANGE: default_thinking and thinking.mode are removed from config; migrate to [thinking] enabled/effort.
* refactor: rename residual thinking level wording to effort
Rename comments, error messages, parameter names, the SetThinkingPayload wire field (level -> effort), and TUI local variables so the thinking effort naming is consistent throughout. No behavior change.
* refactor: rename remaining camelCase thinking level identifiers to effort
Rename liveLevel/prevLevel/levelChanged/commitLevel/effectiveLevel to liveEffort/prevEffort/effortChanged/commitEffort/effectiveEffort in the TUI model picker and config commands.
* refactor: eliminate remaining thinking level wording in comments and tests
Rename levelLabel -> effortLabel, EffortSelectorOptions.levels -> efforts, and 'effort level(s)' / 'default level' / 'requested level' wording in comments, error messages, slash-command description, and test titles to effort. Also restore the withThinking(effort) parameter rename in the Kimi provider that was accidentally reverted.
* fix: address codex review feedback on thinking effort handling
- OpenAI thinkingEffortToReasoningEffort and Anthropic clampEffort now normalize 'on' / unrecognized efforts instead of throwing, so boolean non-Kimi models no longer crash on session start.
- ACP resolveCurrentThinkingEnabled treats a non-empty thinking.effort as enabled, matching agent-core's resolveThinkingEffort.
- REST promptThinkingSchema accepts any non-empty effort string so model-declared efforts are not rejected at the API boundary.
* test: align kimi e2e expectations with supportEfforts-gated reasoning_effort
The kimi provider now sends reasoning_effort only when the model declares support_efforts; boolean models (no support_efforts) send only thinking.type. Update the kimi e2e tests to drop the stale reasoning_effort expectation for the boolean test model.
* test: cover [thinking] effort parsing in config.test
Add effort = "high" to the documented [thinking] table in the config parse test and assert config.thinking.effort is resolved, so the new [thinking] effort field has direct parse coverage.
* docs: add thinking test coverage gap analysis
Capture the explore agent's test coverage review for the thinking overhaul PR, including P1/P2 gaps and the two open design questions, for follow-up test additions.
* feat(oauth): parse nested think_efforts from /models response
The /models endpoint now returns effort levels under a nested think_efforts object ({ support, valid_efforts, default_effort }). Parse it preferentially in both managed-kimi-code and open-platform model parsing, falling back to the legacy flat support_efforts / default_effort fields for older servers.
* refactor(oauth): only read nested think_efforts; gate on support=true
Drop the legacy flat support_efforts / default_effort fallback. The think_efforts object is now the single source, and its support flag gates the whole object — when support is not true, valid_efforts and default_effort are ignored entirely.
* chore: remove unused parseStringArray import in open-platform
* docs: finalize thinking effort release notes
Downgrade the changeset to minor with an English summary, drop the version-specific 'added in 1.0.0' info block, and present the deprecated config fields as a table (field / deprecated in 0.21.0 / description).
* refactor: drop temporary refresh toggles and kimi reasoning_effort mirror
Remove the always-true REFRESH_MODELS_ON_PICKER_OPEN / REFRESH_PROVIDER_MODELS_ON_STARTUP toggles and their stale re-enable TODOs, and stop sending reasoning_effort from the kimi provider (thinking.effort is the only wire field now).
* fix(tui): avoid persisting "on" as thinking effort
* fix: preserve persisted thinking effort across login and provider setup
* fix(tui): show actual thinking effort in /status and footer
* test(tui): align message-flow expectations with effort persistence and /status display
* fix(vis): rename thinkingLevel to thinkingEffort in config.update analysis
|
||
|
|
5cb80ce879
|
feat: support plugin slash commands (#1204)
* feat(agent-core): support plugin slash commands * feat(node-sdk): expose listPluginCommands * feat(kimi-code): register and dispatch plugin slash commands * chore: add changeset for plugin commands * feat(agent-core): activate plugin commands server-side * feat(node-sdk): add activatePluginCommand * feat(kimi-code): render plugin command activations compactly * feat(agent-core): recurse plugin command directories and preserve namespace * fix(kimi-code): parse nested plugin command names * fix(agent-core): update prompt metadata for plugin command turns * fix(kimi-code): replay plugin command turns as command cards * fix: treat plugin-command origins as real user prompts in undo * fix(kimi-code): guard model-empty and clear plugin command render ids * fix: propagate plugin_command to web and vis turn projectors * fix(kimi-code): refresh plugin commands through auth flow * fix(kimi-web): render plugin command cards in chat pane * fix(kimi-web): render plugin command card in desktop chat view * fix(kimi-code): treat slash-activation cards as transcript turn boundaries * fix(kimi-code): count slash-activation entries when trimming transcript turns * fix(kimi-code): preserve plugin command args in undo selector |
||
|
|
525fb146d6
|
feat(vis): full session debugging — tasks/cron, execution timeline, retries & tool progress (#1210)
* feat(vis): surface background tasks and cron jobs
The visualizer read every wire/state/blob artifact a session persists but
ignored the two on-demand families agent-core also writes under the session
directory: background tasks (tasks/<id>.json + output.log) and cron jobs
(cron/<id>.json). Neither is reconstructable from the wire, so there was no
way to inspect what a session spawned in the background or scheduled.
Server:
- task-store / cron-store read-only readers mirroring agent-core's on-disk
layout, id-validation guard, and legacy snake_case task normalization
- GET /:id/tasks, /:id/tasks/:taskId/output (byte-window paged via an exact
nextOffset cursor), and /:id/cron routes
- re-export the public background-task types from agent-core; mirror the
non-exported CronTask shape with a fixture-backed drift test
Web:
- Tasks tab: process/agent/question kinds with status, timing, kind-specific
fields, raw JSON, and a progressively paged output.log viewer
- Cron tab: expression, prompt, recurring/one-shot, created/last-fired
- count badges on both tabs
Tests: +20 (lib + route), all 113 vis-server tests green; web typecheck and
build clean.
* feat(agent-core): persist step retries and tool progress summary
Two transient signals were only ever emitted as live-only loop events, so
nothing survived in the agent record for post-hoc analysis:
- step retries: chatWithRetry gains an onRetry callback; turn-step collects
the recovered attempts and attaches them to step.end as an optional
`retries` array (previously only the live `step.retrying` event).
- tool progress: tool-call distills a tool's sparse status/percent updates
into a bounded `progress` summary (updateCount / lastStatus / maxPercent)
on tool.result. Streamed stdout/stderr is excluded — it would bloat the
wire and is already reflected in the result output.
Both are additive optional fields, so the wire protocol version is unchanged
and existing records keep loading. New public types: LoopStepRetryRecord,
LoopToolProgressSummary.
* feat(vis): add execution-analysis timeline and surface retries/progress
Turn the debugger from a flat record viewer into an analysis tool.
New Timeline tab: folds the wire into turns → steps → tool calls (client-side,
no extra round-trip) and derives the metrics the raw list hides — per-turn /
per-step / per-tool duration, per-turn token cost, a context-window fill
sparkline with cache-hit rate, a tool usage table, idle-gap detection, and a
config-change timeline.
Inline elsewhere:
- Wire rows show tool.call → tool.result elapsed time; tool.result detail
shows truncation, output size, retries, and the progress summary.
- Issues drawer gains tool-error, truncation, filtered, max_tokens, and
retried categories.
- Tasks tab links agent-kind tasks to the subagent's wire.
Wires up vitest for the web package and adds analysis/issues unit tests.
* feat(vis): import debug zips with a logs view and imported-session filtering
A `/export-debug-zip` bundle is just `manifest.json` plus a flattened session
directory, which vis already knows how to read. Importing one therefore lights
up every existing tab for a session that lives on someone else's machine.
Server:
- zip-import: yauzl extraction with zip-slip path guards and entry-count /
uncompressed-size caps for untrusted uploads.
- import-store: extract a bundle into <home>/imported/<imp_…>/, validate it
has a main wire, and record an import-meta.json sidecar.
- session-store resolves imp_-prefixed ids against imported/, so wire /
context / tasks / cron / blobs / logs all work on imported sessions; agent
homedirs are re-derived locally (the bundle holds foreign absolute paths).
- POST /api/imports (raw zip body) and GET /api/sessions/:id/logs (structured
log lines — also available for local sessions).
Web:
- session rail: import button + all/local/imported filter + imported badge.
- new Logs tab: virtualized, level filter, search, session/global toggle.
- manifest card atop the State tab for imported sessions.
SessionSummary/SessionDetail gain `imported` + `importMeta`. Tests cover
extraction, the zip-slip guard, list merge, reading an imported wire through
the existing route, and log parsing.
* fix(vis): read tasks/cron from agent homedirs and stop persisting tool status text
Addresses review feedback on the debug-tooling changes:
- Background tasks and cron jobs are persisted under each agent's homedir
(<session>/agents/<id>/tasks and /cron), not the session root. The Tasks and
Cron tabs read the session root, so they showed nothing for normal sessions.
Both routes now aggregate across detail.agents homedirs; task entries carry
the owning agentId. The route-test fixtures were writing to the wrong
(session-root) location too — corrected to the real agents/main layout so
they actually exercise the path.
- tool.result progress no longer keeps free-form status text, only updateCount
and maxPercent. A tool's status string can contain sensitive data (e.g. an
MCP OAuth authorization URL) that must not leak into persisted wire files or
exported debug bundles.
* fix(vis): stop the main content area from overflowing horizontally
The <main> flex child lacked min-w-0, so it defaulted to min-width:auto and
refused to shrink below its content's intrinsic width. Tabs that lay out in
normal flow with flex-wrap rows (the Timeline tab) then got unbounded width,
never wrapped, and blew the layout out to thousands of pixels wide. Adding
min-w-0 lets the column shrink to the available width so its content wraps,
truncates, or scrolls within its own container.
* fix(vis): resolve local global log path and imported-state agent fallback
- Logs tab: for non-imported sessions the shared global log lives at
<KIMI_CODE_HOME>/logs/kimi-code.log, not under the session dir (that path is
only used inside exported bundles). The route now reads the home path for
local sessions, so the global-log toggle works for them.
- Imported detail: a bundle's state.json is best-effort and may omit the
agents map. When the inventory is empty, fall back to discovering agents
from disk so routes that require an agent (wire/context) still resolve main.
* fix(vis): harden imported manifest and task parsing against corrupt input
An imported debug zip is untrusted, so a syntactically valid but type-corrupt
file could crash whole views:
- manifest.json: a non-string field (e.g. workspaceDir: 123) flowed into
SessionSummary.workDir, where the session rail calls .split('/') and crashed
the entire list. readManifest/readImportMeta now sanitize declared string
fields, keeping only strings.
- task JSON: a record that passed the shape guard but held a non-string legacy
field (e.g. stop_reason: 5) threw in normalization, failing GET /tasks with a
500 and hiding all of a session's tasks. optionalNonEmptyString now tolerates
non-strings, and listBackgroundTasks skips any record that still fails to
normalize — honouring the reader's documented silently-skips contract.
* fix(vis): discover rotated logs; keep tool progress on thrown failures
- Logs tab: the diagnostic log can rotate (kimi-code.log.1, .2, …) and an
exported bundle may contain only the archives. The route now discovers the
active file plus its rotated siblings and concatenates them oldest-first, so
a rotated-away log still surfaces (covered by node-sdk's rotated-export case).
- agent-core: a tool that reported sparse progress and then threw lost its
progress summary, because the catch path built the error tool.result without
it. Thread progressSummary through that path too, matching the success and
malformed-return paths.
* fix(vis): skip type-corrupt agent entries in imported state
readImportedDetail's empty-inventory fallback never ran when a bundle's
state.json had a non-empty but type-corrupt agents map (e.g.
`{ "agents": { "main": null } }`): inventoryAgents dereferenced the null entry
and threw, so readSessionDetail returned 500 instead of recovering main from
the on-disk agents/main/wire.jsonl. inventoryAgents now skips non-object
entries, letting the disk-discovery fallback take over.
* fix(vis): reset timeline agent on session change; preserve context on zero-usage steps
- Timeline tab kept the previously-selected agent id across session navigation,
so a subagent selection would 404 against the next session. Reset it to main
on sessionId change, mirroring WireTab/ContextTab.
- A zero-usage step.end (e.g. a content-filtered response) reset the
context-window fill to 0, pushing a false drop into the Timeline chart and the
Context tab. agent-core's ContextMemory keeps the prior count in that case;
the analysis lib and the context projector now do the same.
* revert: drop agent-core retries/tool-progress persistence
These were the only changes in this branch that touched agent-core. They
persisted two previously live-only signals (step retries, tool progress) to
the wire purely so the visualizer could display them — marginal features that
did not justify modifying the core loop or extending the wire surface.
Reverts the agent-core loop/type/export changes (restored to main, keeping
#1209) and its changeset, and removes the vis-side rendering and types that
consumed step.end.retries / tool.result.progress. The rest of vis is unchanged
and reads only data agent-core already persists.
|
||
|
|
efdf8a1b2d
|
feat(vis): faithful wire.jsonl rendering + built-in kimi vis command (#788)
Some checks are pending
CI / build (push) Waiting to run
CI / test (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 / Publish native release assets (push) Blocked by required conditions
Release / Deploy docs (push) Blocked by required conditions
Release / Native release artifact (push) Blocked by required conditions
Release / Release (push) Waiting to run
* feat: polish vis * feat: add 'kimi vis' command for session visualization * fix(vis): drop metadata app_version/resumed removed upstream #786 stopped recording resume version metadata, so those fields no longer exist on the metadata wire record. The vis-into-typecheck wiring caught the stale field reads after merging main; drop them from the metadata headline. * fix(vis): drop unnecessary return-await in startVisServer oxlint typescript-eslint(return-await) flags returning an awaited promise outside try/catch; return the promise directly. * fix(vis): green CI — tolerate unbuilt embedded asset + bump nix pnpmDeps hash - handleVis: wrap the embedded-SPA dynamic import in try/catch. The value module is generated at build time (prebuild); in contexts without a build (tests run pnpm test, not build) only the .d.ts type stub exists, so the runtime import throws. Tolerate it and fall back to filesystem serving. - flake.nix: update the fetchPnpmDeps hash after adding the vis-web / vis-server / vite-plugin-singlefile dependencies. * refactor(vis): drop redundant alwaysBundle in tsdown config #775's single-entry build (codeSplitting: false) already bundles everything not declared in dependencies/peerDependencies. hono / @hono/node-server (transitive via vis-server) and @moonshot-ai/vis-server (a devDependency) are all undeclared there, so they bundle by default — the explicit alwaysBundle was redundant. Verified the emitted main.mjs is still fully self-contained and 'kimi vis' serves. * fix(vis): address review — context-token resets, IPv6 url, marker-safe indexing - contextTokens now mirrors agent-core on lifecycle records: 0 on context.clear, tokensAfter on context.apply_compaction (was only updated from step.end.usage, leaving a stale live fill after a clear/compaction). - start.ts brackets IPv6 hosts in the returned url (http://[::1]:port/); hostForUrl moved to config.ts and shared with the startup banner. - compaction slice + micro-compaction blanking now index over real history entries only, so synthetic undo/clear UI markers no longer offset agent-core's compactedCount / cutoff. * chore: add changeset for kimi vis command * fix(vis): cross-platform single-file build + history-count micro clamp - build-vis-asset.mjs sets VIS_SINGLEFILE via the spawn env and runs 'vite build' directly (cross-platform), instead of the POSIX-inline-env 'build:single' script that broke on Windows cmd; removed the now-unused build:single script. Fixes the win32 build path (the asset generator runs in the kimi-code prebuild + native bundle). - context.undo now clamps the micro-compaction cutoff by history-entry count (excluding synthetic undo/clear markers) instead of messages.length, mirroring agent-core undo() -> microCompaction.reset(_history.length); a surviving marker no longer leaves the cutoff one too high and wrongly blanks a later-appended tool result. * fix(vis): run the single-file build through a shell for Windows pnpm The win32 native binary is built on Windows runners (.github/workflows/_native-build.yml), which run this generator. pnpm's launcher there is pnpm.cmd, which a bare argv exec can't resolve without a shell. Use execSync with a single command string so the platform shell (cmd on Windows) resolves the shim; a command string (not an args array) avoids the args+shell deprecation. Args are static. * fix(vis): show model-facing tool result content in the context view agent-core normalizes tool results via toolResultOutputForModel before they enter history (error -> '<system>ERROR: ...' prefix, empty -> '<system>Tool output is empty.' sentinel). The projector was using the raw ev.result.output, so the Context tab's model view showed content the model never saw for failed/empty tool calls. Replicate that normalization (the upstream helper is module-private) so the projected tool message matches what the model received. |
||
|
|
a2c5e1be25
|
fix: add minor improvements for Mira (#649) | ||
|
|
a6d379b2ce
|
feat: offload large base64 media payloads to external blob files (#117) | ||
|
|
c0b63c1ea7
|
refactor(vis): rewrite for new agent-core protocol (#34)
* feat(agent-core): re-export wire record types for in-monorepo consumers * chore(vis): purge legacy wire protocol code * feat(vis): introduce single-source agent-record types * test(vis): add fixture session and builder helper * feat(vis): implement new session store reader * feat(vis): wire new session list/detail routes * refactor(vis): drop legacy path config * feat(vis): adapt session list page to new DTO * feat(vis): implement per-agent wire reader * feat(vis): rewrite wire route for new protocol * feat(vis): rewrite wire type metadata for new protocol * feat(vis): rewrite wire row + headline for new record union * feat(vis): wire tab detail panel + multi-agent selector * feat(vis): rebuild wire issues detection for new protocol * feat(vis): implement context projector * feat(vis): rewrite context route on projector * feat(vis): rebuild context tab for new ContextMessage shape * feat(vis): implement agent tree builder * feat(vis): rewrite agents route * feat(vis): rebuild subagents tab around state.json.agents * feat(vis): rebuild state tab on raw state.json * chore(vis): purge residual legacy field references * vis: rewrite complete on new agent-core protocol * fix(vis): adapt to wire protocol 1.1 with flattened tool calls * fix(vis): populate workDir from session index * fix(vis): return broken-state sessions from detail lookup * fix(vis): tolerate per-session wire read failures during listing * fix(vis): read wire files from canonical session path * feat(vis): restore session detail page with full tab layout * feat(vis): wire subagent context tab to real ContextTab * fix(vis): sync wire and context tab agentId with prop changes * feat(vis): auto-pick a free dev port when 3001 is busy * feat(vis): accept v1.0 wire files via agent-core migration chain * refactor(vis): default API to 5174 and pick a non-colliding vite port * feat(vis): make long strings expandable with copy in JsonViewer * fix(vis): stop gating session health on protocol version * refactor(vis): split wire records into raw + projected; best-effort unknown protocol * feat(vis): pair tool.call with tool.result via inline cross-reference and hover highlight * feat(vis): open session folder and copy its path from the detail header * fix(vis): reconstruct assistant and tool messages from loop events * fix(vis): keep system prompt bubble within the message column width * feat(vis): collapse tool result bubble by default in context view * feat(vis): expose broken_main_wire in the session health filter * fix(vis): reset wire and context tab agent when navigating sessions * fix(vis): fall back to a generic headline for unknown wire record types * fix(vis): emit compaction summary as an assistant message with origin * fix(vis): harden session-store reads for broken wires, broken state, and path-traversal agent ids * feat(vis): inline image previews for image_url content parts |
||
|
|
c4dd1c7ff2
|
feat: flatten tool call records (#25) | ||
|
|
842e699a64 | Kimi For Coding |