pr update#1
Closed
creatiVision wants to merge 98 commits into
Closed
Conversation
A top-level Teleport in the sidebar template made the component multi-root, so v-show could not apply display:none and the collapsed sidebar stayed mounted at the rail width, squeezing the conversation. Move the teleport inside the aside so the sidebar is single-root again.
* feat(tui): show compaction summary with Ctrl-O * docs: document compaction summary toggle * fix(tui): preserve compaction summary expansion state * fix(tui): preserve compaction summary expansion across replay and theme changes
…with fd (#1408) * fix(tui): complete @ file mentions across additional workspace roots with fd When additional workspace directories are added via /add-dir, @ file completion fell back to a readdir-based scanner capped at 2000 entries, so deeply nested files in large projects never appeared. Route @ completion through fd across every root instead, keeping the query pushed down to fd and deduplicating by absolute path. The readdir fallback remains for when fd is unavailable. * fix(tui): preserve per-root full-path fallback for @ mentions Address review feedback: decide the scoped-versus-full-path fallback per root instead of globally. When one root has the scoped directory but another does not, the latter still runs a whole-tree --full-path search with the original query, so a match that only exists under that root is not hidden just because a sibling root happens to contain the prefix directory. * fix(tui): fall back to filesystem when fd binary is not executable Address review: when fdPath is non-null but the binary cannot be spawned (managed fd removed or lost execute permission), @ completion returned null because pi-tui swallows the spawn error into an empty result, so the catch never ran. Probe fd with accessSync(X_OK) before delegating and use the filesystem fallback when it is not executable, while still returning null for genuine no-match results. * fix(tui): trust bare fd command names when probing executability Address review: when fd is discovered on the system PATH, detectSystemFdPath returns the bare name (fd/fdfind). accessSync checked that literal string relative to cwd and never searched PATH, so a valid system fd was treated as unavailable and @ completion fell back to the capped scanner. Trust bare names (spawn resolves them via PATH) and only probe absolute/relative paths, which is how the managed fd is referenced and which can go stale. * chore: remove accidentally committed plan files * test(pi-tui): stabilize paste-burst test by freezing the clock The paste-burst heuristic uses an 8ms inter-character interval that a slow or busy CI runner can exceed between synchronous handleInput calls, which resets the burst and lets Enter submit. Freeze Date so the synchronous keystrokes always register as one burst, making the assertion deterministic. * chore: ignore top-level plan directory
This package only ever contained a package.json with no sources, dependencies, or scripts, and nothing in the repo imports it (the CLI uses @moonshot-ai/migration-legacy instead). Remove the directory and drop its entries from flake.nix and the changeset config, then refresh the lockfile.
* fix(tui): keep input anchored after slash command menu closes Force a full re-render when the slash command menu closes, but only when the session content already overflows one screen; skipped under tmux. Detect the close edge from a render frame so asynchronous closes (Backspace deleting the leading slash) are covered too. Apply the same overflow/tmux gating when restoring the editor from selector panels. * fix(tui): measure overflow against restored editor tree Address Codex review: the overflow probe ran before the editor container was swapped back, so it counted the tall replacement panel and forced a full clear/home even when the restored content fit on one screen, yanking the editor to the top. Measure after the editor is mounted instead. * fix(tui): redraw when content exactly fills one screen Address Codex review: the guard skipped the forced redraw when the post-close layout is exactly one screen, leaving the editor shifted up because the differential renderer keeps the old viewport offset after a shrink. An exact fill is safe to clear (no blank tail), so redraw when content fills or overflows the viewport, and cover it with a test.
…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.
* 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>
Some agent tooling looks for a CLAUDE.md at the repo root. Add a symlink to the canonical AGENTS.md so both filename conventions resolve to the same instructions, avoiding any duplicated content.
…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.
…put (#1419) Keep the command preview in the body after the result lands so a multi-line command with short output no longer collapses the card. Render the command in textDim with a shellMode $ and the result one shade dimmer in textMuted, and simplify the header to "Running a command" / "Ran a command".
* fix: honor web font size setting * fix: scale web font-size dependents --------- Co-authored-by: liruifengv <liruifeng1024@gmail.com>
* fix(kimi-web): keep composer caret visible when input is empty The composer textarea coloured its empty state with `--faint`, and with no caret-color set the caret inherited that faint colour and nearly vanished until the first character was typed. Pin the caret to --color-text so it stays readable regardless of the placeholder state. * fix(kimi-web): use currentColor for done todo strikethrough TodoCard pinned the done-state strikethrough to --color-line-strong, which is lighter than the faint text in light theme and darker in dark theme, so the line looked washed-out and broken against the text. Drop the override so the line inherits the text colour, matching TasksPane and the design-system examples. * fix(kimi-web): do not reserve width for hidden workspace row actions The workspace header's more/add buttons were hidden with opacity: 0, which kept them in the flex layout and permanently reserved ~60px on the right, so the workspace name (flex:1) truncated long before the row was full. Hide them with display: none and restore display: inline-flex on hover/focus/open, so the name fills the row and only truncates once the buttons actually appear. The same actions remain reachable via the right-click menu and the section kebab, so removing them from the tab order when hidden is acceptable. * fix(kimi-web): keep workspace header row height stable on hover Lock .gh-top to the sm IconButton height (26px) so revealing the hover actions no longer grows the row and nudges the path line and the groups below it. Follow-up to the display: none change: that freed the horizontal space but let the row height collapse when the buttons were hidden. * chore: add changeset for kimi-web UI fixes * fix(kimi-web): restore keyboard access to workspace actions Revert the display: none change (and the min-height that accompanied it) for the workspace row's hover actions, going back to opacity-hidden buttons. The display: none approach removed the buttons from the tab order, and the 'create in workspace' add button has no keyboard-accessible alternative in the right-click or section menus, so keyboard users lost that action. Restoring opacity keeps the buttons focusable again, at the cost of the workspace name truncating a little earlier to reserve their space. Addresses the P2 review on PR #1423.
* feat(server): support restoring and listing archived sessions - add a `:restore` session action that clears the archived flag in state.json and returns the restored session - add an `archived_only` list query param, mutually exclusive with `include_archive`, that post-filters to archived sessions - keep the implementation in the server layer as a temporary measure until agent-core exposes restore natively * fix(server): paginate archived-only sessions before response * feat(web): add archived sessions page in Settings Browse, search, filter by workspace, sort, and restore archived sessions from a new Archived tab in Settings, backed by the server archived_only list and :restore action. * fix(web): keep archived Load more visible when a page filters to empty When a search or workspace filter empties the loaded archived page, the Load more button was hidden inside the non-empty branch, so users could not fetch older pages to find a match. Move the button out so it stays available whenever more archived pages exist. * fix(server): preserve after_id bound while draining archived pages Draining an archived_only request that starts from after_id would switch to before_id and cross the pivot, reintroducing the pivot and older sessions. Take a single filtered page for after_id instead of draining past the lower bound. * fix(server): drain archived_only within the after_id bound An archived_only request starting from after_id now keeps paging toward older sessions until it reaches the pivot, instead of treating the first page as exhaustive. The loop stops as soon as it encounters the pivot session itself, so it never reintroduces the pivot or anything older. * feat(web): drain all archived pages for global search and sort When the user searches, sorts, or changes the workspace filter in the Archived settings page, fetch every remaining archived page first so the client-side filter and sort run over the full set rather than only the pages loaded so far. * refactor(web): load all archived sessions upfront instead of paginating Fetch every archived session once when the Archived settings tab opens and drop frontend pagination entirely. Search, sort and workspace filter now run over the full set, removing the empty-page and cursor bookkeeping that previously caused bugs. --------- Co-authored-by: qer <wbxl2000@outlook.com>
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.
* fix(web): reconcile session from snapshot on reopen * fix(web): discard stale snapshot when a newer prompt races reopen * fix(web): harden reopen snapshot against first-open and optimistic-send races * fix(web): keep evicted reopens subscribed when a snapshot races * fix(web): let resync snapshots bypass the reopen staleness guard * fix(web): force-apply the snapshot after an undo * fix(web): gate session reopen on durable seq instead of updatedAt * refactor(web): always rebuild reopened sessions from a snapshot * fix(web): sharpen reopen snapshot discard and skip rebuilds mid-stream * refactor(web): unconditionally apply session snapshots, drop the staleness guard * fix(web): preserve loaded older messages when reopen snapshots apply
* feat(telemetry): add system metrics collection Add periodic CPU and memory telemetry sampling with warmup capture, lifecycle cleanup, and tests. * fix(telemetry): attach prompt session to system metrics
The desktop composer toolbar renders every control on one row and relies on overflow:hidden to fit, so between the mobile breakpoint and a wide window it clipped its own content. Shed secondary ink below 980px (the context readout moves into the ring tooltip, the model name truncates earlier, permission is capped) and keep the context ring visible on phones too, instead of sending it to the settings sheet.
* feat(web): render AgentSwarm as an inline tool card Replace the bottom SwarmCard footer and the messagesToTurns live-skip with one dedicated inline tool card for AgentSwarm. The card shows a phase overview plus a per-subagent accordion: live progress while it runs, parsed aggregated result once it completes (and after a refresh that has already dropped the live tasks). Refresh and resync keep member identity metadata (swarmIndex, parentToolCallId, subagentType, runInBackground) stable across skeleton task replacement in the reducer, and the .content-wrap flex layout is hardened against the overflow compression that previously displaced the footer. * fix(web): handle swarm review feedback - SwarmTool: when AgentSwarm fails before producing a structured agent_swarm_result (e.g. argument validation), render the raw tool output instead of the "waiting for subagents" placeholder so the failure cause is visible. - resolveSwarmMembers: source live members from the AppTask store keyed by parentToolCallId rather than buildSwarmGroups, which filters out single-member groups. A resume-only AgentSwarm now streams its live progress before the final result arrives. The badge counter still relies on buildSwarmGroups's filter. * fix(web): carry streamed subagent text into swarm rows Swarm subagents that stream normal assistant output accumulate it on AppTask.text (text-kind taskProgress), not outputLines. The new live member map was dropping `text`, so a still-composing subagent rendered an empty / stale row until the structured result arrived. - Add `text` to SwarmMember and thread it through buildSwarmGroups and swarmMembersByToolCall. - SwarmTool: prefer member.text for both the row activity preview and the expanded body; fall back to outputLines / summary. - Tests cover text propagation through both helpers. * fix(web): merge swarm result rows and fall back to raw output Address the two latest swarm review comments: - Rows: when a parsed agent_swarm_result coexists with live AppTasks (which the detail panel also depends on), the inline card previously only rendered the live members. Interrupted swarms can carry state="not_started" / outcome="aborted" result entries for items that never spawned a task; those rows were dropped until a refresh cleared the live tasks. Extract the row model into buildSwarmCardRows and merge result-only aborted/not-started rows with the live member rows. - Fallback: when the tool is no longer running but produced no structured result (argument validation, parser miss, or legacy legacy transcript), render the raw tool output instead of "Waiting for subagents…" so the final text / failure cause is visible to the user. * fix(web): parse swarm result subagent bodies defensively Producer writes subagent body unescaped, so a subagent that analyzes or emits an AgentSwarm snippet can include a literal "</subagent>" inside its body. The non-greedy regex treated that as the row close and truncated the body in the result-only path (post-refresh where the AppTask store is gone). Rewrite the parser to scan opening tags, then resolve each row's body as everything up to the last "</subagent>" before the next row's opening tag (or document end), preserving embedded close-tag strings. Add tests for a literal "</subagent>" within a single body and across sibling rows. * fix(web): only count top-level subagent result tags A subagent body that contains a literal `<subagent ...>` tag — for example emitting an AgentSwarm/XML snippet — was being pre-collected as another result row, splitting the real body and producing duplicate / bogus subagents after refresh where the AppTask store is gone. Rewrite parseSubagents with a depth-tracking tokenizer: scan every `<subagent ...>` / `</subagent>` token in order, push a real row frame only at the outermost level, and treat openings / closings while nested inside another body as body text. Drop the now-inaccurate "literal </subagent> without matching open" regression tests; replace with tests that verify a balanced nested snippet stays inside the parent body and does not register as a separate row.
Agent working notes (HANDOVER/handoff) and one-off UI prototype HTML files were committed by mistake. Remove the already-tracked ones, add .gitignore patterns for these classes of files and a .tmp/ scratch dir, and document the rule plus a pre-commit self-check in AGENTS.md so future mistakes are caught mechanically. No publishable package is affected, so this PR needs no changeset.
Set hyphens: none (with the -webkit- prefix) on body so chat and markdown text never gain a hyphen glyph at a line break; components still control where lines wrap via word-break/overflow-wrap. Disable the ligatures that coding fonts enable by default (liga/calt/ss01) on native code elements so code renders literally, e.g. != stays as two characters.
#1433) * fix(kimi-web): keep tool components from jumping on expand or collapse Also show the scroll-to-bottom button whenever scrolled up, and render a fallback icon for tools without a dedicated glyph. * fix(kimi-web): preserve bottom follow during content-only resizes Late-loading media can grow after scrollKey has run; keep chasing the bottom on content growth, and only suppress follow during the pinned expand/collapse window.
* feat(kimi-web): render cron fire notices as in-transcript cards Show scheduled-reminder fires as distinct notice cards in the web chat, both live and after a reload, instead of hiding them. Each card carries a humanized schedule, a dimmed job id, and a collapsible prompt body. * fix(kimi-web): isolate cron notice prompt id and route prefixed events - Give synthesized cron messages a fresh promptId so a fire mid-turn cannot be reconciled into the optimistic user echo and hide it. - Add cron.fired to KNOWN_AGENT_CORE_TYPES so event.-prefixed frames reach the projector and render live too. * fix(kimi-web): truncate long one-line cron prompts when collapsed Slice the first line to the collapse limit with an ellipsis when a single-line prompt exceeds it, so the collapsed state actually truncates and the expand toggle is not a no-op for common one-line reminders. * fix(kimi-web): keep cron notices from reconciling into optimistic user echoes Skip optimistic-echo reconciliation for user messages whose origin is cron_job or cron_missed: their prompt text can coincide with a still- optimistic user message, and the loose content match would otherwise replace the user's turn with the cron notice instead of appending. * fix(kimi-web): keep in-turn cron injections from breaking tool results A cron injection steered into an active turn lands inside that turn's message sequence, between a tool use and its result. Treating it as a hard user-turn boundary flushed the pending assistant group, so the next tool result had no group to fold into and the tool rendered without output. Embed such in-turn cron notices as a block inside the assistant group instead, and only render a cron at a turn boundary as its own turn. * fix(kimi-web): only embed cron notices while a tool is in flight Embedding whenever a group was pending was too broad: on REST snapshots without prompt ids the whole transcript shares one group, so an idle cron fire merged into the previous assistant answer and its own reply kept going in that group. Embed only while the group has a running tool (a cron sandwiched between a tool use and its result); flush to its own turn otherwise. * fix(kimi-web): omit synthetic prompt id on cron notices The synthesized cron message carried a cron_pr_ promptId that the web client caches into promptIdBySession for Stop/abort. Because it is not a real daemon prompt id, it clobbered the active promptId, so Stop first aborted a nonexistent prompt and only recovered via the error fallback. Omit the promptId; the reducer already skips optimistic-echo reconciliation for cron-origin messages, so it is not needed for de-dup.
* fix(web): clarify archive session confirmation copy * fix(web): drop delete mention from archive confirmation copy * feat(web): add archived-sessions restore entry to mobile settings Mobile used a separate settings bottom-sheet with no archived-sessions restore surface, so the archive confirmation pointing users to Settings to restore was untrue on mobile. Add an Archived sessions sub-view to the mobile settings sheet with search, sort and restore, mirroring the desktop Settings tab. * fix(web): refresh mobile archived list and use Input primitive Refresh the archived-sessions list each time the mobile sub-view opens so sessions archived from the mobile switcher show up without remounting the sheet, and replace the hand-rolled search input plus inline svg with the shared Input primitive. * fix(web): use Button primitive for mobile archived restore Render the mobile Archived sessions restore action with the shared Button primitive instead of a hand-rolled button and scoped CSS, matching the desktop archived list and the app's design-system rule.
* 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.
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
* feat(kimi-web): add approval notification storage key and i18n copy
* feat(kimi-web): add approval notification helpers and tests
* feat(kimi-web): wire approval notifications and guard completion alerts
* fix(kimi-web): extract shouldNotifyCompletion helper and add tests
* feat(kimi-web): add approval notification settings toggle
* chore(kimi-web): add changeset and tidy notification module comment
- Align approval notification tag with spec (kimi-approval-${approvalId})
- Update module header to describe all three notification kinds
* fix(kimi-web): make notifications fire reliably
- Key completion notification tags by turn (sid + promptId) and question
tags by request id, so a stale notification left in the notification
center no longer swallows every follow-up alert in the same session
- Suppress notifications only while the window is actually focused, not
merely visible (document.hasFocus() on top of visibilityState)
- Play the attention sound when a tool needs approval, matching the
completion and question sounds
* chore(kimi-web): simplify changeset
* style(web): polish sidebar and tool typography - Use UI font and medium weight for sidebar and composer controls - Add reusable shortcut and tool output blocks - Cap long tool output at 50 lines with a scrollbar - Update sidebar show-more copy and muted styling * style(web): refine workspace picker sizing * style(web): align composer mode menus * style(web): tune list and question typography * style(web): reuse shortcut keys in approvals * style(web): size workspace picker from content * feat(web): localize chat status labels * style(web): refine composer toolbar controls * style(web): use complete Inter variable font * style(web): tune sidebar workspace typography * style(web): polish composer and workspace picker * style(web): refine markdown and thinking typography * chore: add web UI polish changeset * fix(web): pin Inter package for Nix build * style(web): polish goal tool calls * style: polish goal mode display * fix: layer latest message pill below menus * style: align goal strip content
* fix: keep prompt goals running until terminal * fix: reject invalid prompt goal commands * fix: ignore stale prompt goal status checks
…ibit (#1518) 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.
) * feat(oauth): parse boosterWallet extra usage from /usages * feat(oauth): expose extraUsage on AuthManagedUsageResult * feat(kimi-code): render Extra Usage section in /usage panel * fix(kimi-code): address Task 3 review feedback for extra usage section * feat(kimi-code): render Extra Usage section in /status panel * feat(kimi-code): wire extraUsage into /usage and /status commands * chore(extra-usage): address final review findings for fuel pack feature - Update changeset to cover both kimi-code and kimi-code-sdk packages - Add parser clamp tests and toolkit null-case test - Replace 'as never' casts in usage-panel tests - Wrap long import line in status-panel * chore: temporarily log /usages raw response for debugging * fix(oauth): accept BOOSTER balance type and drop debug log * fix(oauth): drop reset hint from Extra Usage and revert periodEnd parsing * fix(oauth): treat missing amountLeft as zero extra usage and drop debug log * revert: keep missing amountLeft defaulting to 0 (fully used) * feat(extra-usage): show monthly cap usage bar and balance in /usage and /status * fix(extra-usage): show balance and unlimited marker when no monthly cap * fix(extra-usage): show monthly used with unlimited marker and balance * fix(extra-usage): label Used and use English Unlimited * feat(extra-usage): render balance, monthly used and monthly limit as labeled rows * fix(extra-usage): move Balance row to the bottom * fix(extra-usage): format currency values with two decimals for column alignment * fix(extra-usage): right-align currency values so numbers line up * fix(extra-usage): align currency symbol and decimal point in usage rows
…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
* feat(web): use sidebar fold/unfold icons for sidebar toggle * feat(web): move settings entry to a sidebar footer row * feat(web): fully collapse sidebar with animated width transition * feat(web): redesign sidebar colors, spacing and macos desktop chrome * feat(desktop): center traffic lights on the 48px header row * fix(web): restore webkit thin scrollbars and unify sidebar icon sizes * feat(web): add Kbd keycap component and justify sidebar search shortcut * style(web): rework sidebar palette and pin a resident sidebar toggle * fix(desktop): sync window appearance with web UI theme so dimmed traffic lights stay visible * feat(web): adopt Kimi design icons in the sidebar via a local icon collection * style(web): mute workspace group title color in the sidebar * style(web): refine sidebar typography, unify shortcut keycaps, float workspace row actions * style(web): cap sidebar draggable width at 480px * style(web): derive sidebar row height from type and padding, float the kebab * chore: add changeset for sidebar UI polish * fix(nix): update pnpmDeps hash * style(web): put the sidebar collapse button inside the header on non-mac * fix(nix): update pnpmDeps hash
* feat(tui): add Kimi WebBridge install entry to /plugins panel Surface a hardcoded Kimi WebBridge entry at the top of the Official tab in the /plugins panel. Selecting it opens the WebBridge install page in the user's browser instead of going through the plugin install flow, since WebBridge is a browser extension plus local daemon rather than an installable plugin package. * fix(tui): restrict WebBridge open-url shortcut to the pinned row Match the hardcoded pinned WebBridge entry by object reference instead of by id. A curated or custom marketplace entry on the Third-party tab can legitimately reuse the kimi-webbridge id; routing by id hijacked Enter on those rows and opened the WebBridge page instead of installing. The Official tab still dedupes a same-id official catalog entry so the pinned row is not duplicated. * fix(tui): label WebBridge plugins row as "open in browser" The previous "webpage" status did not make it clear that selecting this row opens an external page rather than installing in-app. "open in browser" states the action directly and contrasts with the install label on regular plugin rows. * test(tui): navigate past pinned WebBridge row in marketplace install tests Two message-flow tests pressed Enter on the Official tab assuming index 0 was the Kimi Datasource entry. The hardcoded Kimi WebBridge row now leads that tab, so move down one row before installing.
* 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
* fix(web): prevent duplicate first prompts and keep goal drives from looking idle
- Guard startSessionAndSendPrompt with a per-workspace reentry lock so a
double-click / repeated Enter during draft-session creation cannot fire
two concurrent first prompts into the same new session.
- Track goal.active in the agent event projector so turn.ended between
goal-driven continuation turns keeps the session 'running' instead of
projecting a false 'idle' that drains the local queue into a still-busy
core (turn.agent_busy).
- Show a 'starting conversation…' loading state on the empty-session
landing while the first prompt is being created and submitted.
- Persist the resolved model in startSessionAndActivateSkill so the first
skill turn on a fresh session does not fail with 'Model not set'.
* chore: add changeset for web first-prompt fixes
* fix(web): close remaining first-prompt and goal-settle gaps
- Pass the starting guard through the dock composer: draft-session
creation selects the new session before submit, which swaps the empty
composer for the dock; disabling both composers closes the last path
to a concurrent first POST. Also take the workspace lock in
startSessionAndActivateSkill / startSessionAndOpenSideChat.
- Emit the owed idle when a goal settles (blocked/paused/completed) in
the inter-turn gap after a turn.ended was projected as 'running', so
sending state, in-flight flags and queued prompts flush instead of
the session staying 'running' forever.
* style(web): fix eqeqeq lint error in first-prompt guard
* fix(web): clear owed idle when a new goal turn starts
The idle debt from a 'running' projection survived turn.started, so an
UpdateGoal('complete'|'blocked') landing mid-turn in the NEXT goal turn
synthesized an early idle. onSessionIdle could then drain queued prompts
into a core that was mid-turn again, re-opening the turn.agent_busy race
for multi-turn goals. Clear the debt on turn.started: from that point the
turn's own turn.ended carries the idle with goalActive already false.
* fix(web): make first-prompt starting state workspace-id-agnostic
isStartingFirstPrompt now reads from the lock set directly (size > 0)
instead of the current activeWorkspaceId. createDraftSession can swap
activeWorkspaceId to a registered id mid-flight; a workspace-keyed read
would then return false while the first prompt is still in the create/
select/submit window, re-enabling the composer and reopening the
duplicate first-submit race.
* revert(web): drop goal-aware idle projection from agentEventProjector
The goalActive / idleOwed shadow state machine grew through multiple
review rounds and still leaves edge cases (snapshot-seeded turns, mid-
turn goal updates). Roll it back to the simple 'turn.ended projects idle'
behavior. Goal-driven sessions can once again race a queued prompt into a
busy core; this is accepted as a known limitation to be resolved properly
in a follow-up that has the core emit an authoritative idle signal.
* chore: align changeset with actual fix scope
* test(web): update profile-patch expectation for model field
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
* 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
* fix(web): hide injected system asides in user message bubbles * fix(web): preserve literal <system> tags in user prompts * chore: fold duplicate web changeset into caption-hiding entry
…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.
…d notifications (#1542) The web client received two sessionStatusChanged events per turn transition: one projected client-side from the raw turn.started/turn.ended stream, one mapped from the daemon's event.session.status_changed. After the tag scheme in #1479 keyed the completion notification by prompt id, the second (redundant) idle event lost the cached prompt id and fell back to a Date.now() tag, so every turn end popped a second "Turn finished" notification and replayed the completion sound. Stop projecting sessionStatusChanged from the raw turn stream (turn.started, turn.ended, and the in-flight snapshot seed). The daemon's event.session.status_changed is the single source of status transitions: it is computed from live daemon state (covering awaiting-approval / awaiting-question / aborted), carries the authoritative previousStatus and currentPromptId, and is deduped per real transition server-side. The turn stream keeps its content responsibilities (message finalization, usage, duration); seedInFlight keeps seeding the partially-streamed message while status comes from the snapshot's authoritative session record.
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
…d_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>
* docs(changelog): sync 0.23.5 from apps/kimi-code/CHANGELOG.md * chore: update config model doc * docs: update config-files example with new models and services --------- Co-authored-by: liruifengv <liruifeng1024@gmail.com>
* fix: update the WebBridge install link in the /plugins panel * fix: drop the zh-cn segment from the WebBridge link
) 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.
…are pending (#1555) 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.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Related Issue
Resolve #(issue_number)
Problem
What changed
Checklist
gen-changesetsskill, or this PR needs no changeset.gen-docsskill, or this PR needs no doc update.