* chore(vscode): release 0.6.1
* test(vscode): read the extension version from the manifest in version assertions
* test(vscode): declare version on the runtime rig type
* fix(vscode): count configured provider credentials as signed in and make the gear sign-in actually log in
* fix(vscode): keep the gear auth action scoped to the Kimi account session
* fix: scope Anthropic effort fallback profile to non-Kimi providers
Managed Kimi models routed through the Anthropic protocol (protocol =
"anthropic", no catalog-declared think_efforts) inherited the inferred
latest-Opus effort profile, so the UI showed reasoning effort choices the
server never declared. The fallback now applies only when the provider type
is known, non-kimi, and the effective wire protocol is Anthropic; Kimi
providers keep catalog-declared efforts only, and callers without provider
context fall back to name matching.
* fix: align v2 catalog with resolver for providerless Anthropic models
Flat models (inline base_url, no named provider) and providers without a
declared type now fall back to the model's own protocol when deciding the
Anthropic fallback effort profile, so the model catalog stays consistent
with runtime resolution.
* fix: keep flat Anthropic model effort metadata in TUI and ACP catalogs
Flat models without a named provider (inline base_url, protocol:
"anthropic") have no provider entry to look up; fall back to the
model's own protocol as the provider identity so the effort picker and
ACP catalog stay consistent with runtime resolution.
* style: move v2 anthropic fallback rationale into the modelAuth header
The v2 comment convention keeps comments in the top-of-file block only;
drop the inline explanations added beside functions and statements.
* feat(vscode): migrate extension to Node SDK
* fix(vscode): address CI failures
* fix(vis): handle token count records
* fix(vscode): keep chat toolbar and header readable at narrow widths
* fix(vscode): map yolo to core yolo permission and honor the global yolo setting
* docs(vscode): record Node SDK migration design
* docs(vscode): split breaking changes out of the 0.6.0 changelog
* fix(vscode): keep a resumed session's thinking effort instead of reapplying the default
* fix(vscode): announce session status when a view attaches so the display matches it
* fix(vscode): align webview thinking effort handling with the TUI
* fix(web): align context usage display with 1024-based units and ring-only meter
- simplify the composer context meter to the ring only; the full
used/max/pct numbers live in the tooltip
- format token counts with 1024-based k/M units via a shared formatTokens
helper (256k context reads "256k", not "262k"), applied to the composer
tooltip, status panel, mobile settings sheet, model picker, goal strip,
and turn rendering
- ceil the usage percent so sub-0.5% usage still shows a sliver instead
of an empty meter
* fix(tui): render context usage with 1024-based units and ceil percent
- formatTokenCount is now 1024-based ("256k", not "262.1k"); the footer,
/status and /usage panels, subagent cards, and goal stats all share it,
replacing five local 1000-based copies
- the footer and panel percents use an integer ceil (new usagePercent
helper) so any non-zero usage shows at least 1% instead of "0.0%"
* fix(web): clamp the status panel context percent to [0,100]
ctxUsed can momentarily exceed ctxMax (estimates), which could flash a
"101%" readout — the composer and mobile sheet already clamp the same
ConversationStatus data, so apply the same clamp around the ceiled
percentage here.
* chore: merge the context usage changesets into one
* chore: reword the context usage changeset in English
- kap-server: remove event.model_catalog.changed from the v1 WS union,
broadcaster forwarding, and the zod event registry
- web: refresh all providers (POST /providers:refresh) before loading
models when the model picker opens, replacing the event-driven refresh
- keep domain publishers, the protocol schema, and the web receiver for
compatibility with older daemons
* feat: warn about context-cache loss when switching model or thinking effort
* chore: polish the switch warning copy
* feat: wrap the switch warning instead of truncating it
* chore: bold /new in the switch warning
* Revert "chore: bold /new in the switch warning"
This reverts commit a438e87cda543202a6d4726c96cdabd73b8dcf6f.
* chore: align the changeset with the warning copy
* fix: record crash telemetry for unhandled promise rejections
The crash handler only listened on uncaughtExceptionMonitor, which never
fires for a rejection that has a listener — and the TUI always registers
one, converting rejections into a silent exit(1) with no telemetry at
all. Observe unhandledRejection directly so those crashes still leave a
trace. Since registering a real listener suppresses Node's default
crash-on-rejection, rethrow when we are the only listener (print /
server modes), deduped so the monitor does not double-report.
* fix: fall back to text paste when the clipboard image handler rejects
The Ctrl+V image-paste dispatch chained off the async handler without a
rejection branch, so any failure inside it became an unhandled
rejection — which the CLI's crash path turns into a silent exit(1).
Treat a rejection like "no image available" and paste as text.
* fix: dedupe all rethrown rejection reasons at the crash monitor
Non-Error rejection reasons (plain objects, strings, null) were recorded
by the rejection handler but not added to the dedupe set, so the
uncaughtExceptionMonitor pass after the rethrow reported a second crash
for the same failure; null/undefined reasons also crashed the monitor's
own error-type extraction. Use a Set so primitives dedupe by value, add
every rethrown reason, and classify monitor crashes null-safely.
The TUI crash path (uncaughtException / unhandledRejection) logged the
error into an asynchronously-drained sink and then called process.exit()
on the same tick, so the one line explaining the crash was never written
to disk. Flush the diagnostic logs synchronously before exiting.
Surface the coded provider error the daemon already sends: a semantic
title per error code, the provider's raw message, and expandable
diagnostics (error code, HTTP status, request ID, SDK error name) with
copy support, instead of a bare text-only toast.
* fix(web): dedupe background subagent rows in the agents dock
* fix(web): seed agent identity on late task registration and prefer REST output in task fold
* fix(web): backfill terminal output to folded background subagent rows
* fix(web): sync subagent phase when the REST fold makes a row terminal
* feat(web): allow attaching any file type in chat
- Composer, paste, and drop no longer filter out non-media files;
arbitrary files upload as generic icon chips and are submitted as
file content parts
- kap-server materializes file parts into the session's attachments
dir and replaces them with a path reference, so the model opens the
file with the Read tool on demand instead of receiving inline bytes
- Images rejected by the provider format gate (SVG, AVIF, ...) are now
persisted and referenced by path instead of being dropped with a
notice; uploaded file names are sanitized before hitting disk
* fix(server): stop CSP from blocking web bootstrap script and fonts
- Move the anti-FOUC bootstrap from an inline <script> in index.html
to /boot.js: CSP 'self' never covers inline scripts, while a classic
same-origin script keeps the same render-blocking timing
- Allow data: in font-src — KaTeX and the Inter / JetBrains Mono
Variable fonts ship @font-face data URIs in their distributed CSS
- Set explicit form-action, base-uri, and frame-ancestors, which do
not fall back to default-src
- Add a regression test asserting the served index.html carries no
inline scripts or inline event handlers
* fix(web): normalize empty attachment MIME so extensionless files submit
Files with an empty File.type (Makefile, LICENSE, other extensionless
or unknown types) stored mediaType: '' on the chip, and the submit
fallback used ?? which does not catch empty strings — the wire schema
requires a non-empty media_type, so the prompt was rejected. Normalize
to application/octet-stream at attachment creation, adopt the
server-recorded MIME after upload completes, and make both submit
mappings use || so reloaded chips with '' are covered too.
* feat(web): render all user-turn attachments as chips
* feat(web): attach files by dropping them anywhere in the window
* refactor(web): share one attachment chip between composer and chat bubble
* fix(web): neutral attachment chips, paperclip attach icon, and clickable file chips
* fix(web): drop the extension badge from attachment chips
* fix(web): use the tabler paperclip for the attach button
* fix(web): whitelist attachment previews, reject active document types
Clicking a file chip navigated a new tab to a blob: URL of the uploaded
bytes whenever the type looked browser-renderable. blob: inherits the
web origin, so a text/html or image/svg+xml attachment would execute
same-origin script with the daemon credential (localStorage) and a live
window.opener.
Preview is now restricted to inert types (pdf, non-SVG images, video,
audio, non-HTML text), the blob is re-wrapped with the whitelisted MIME
instead of trusting the recorded content-type, and window.opener is
severed. Non-whitelisted types no longer silently download: the chip
reports 'unsupported' and the pane shows a transient hint.
* fix(web): recover file attachment chips from the server notice
The kap-server prompt route replaces file parts with an "Attached file
…" text notice before enqueueing, so after any snapshot resync the
attachment chip degraded into raw notice text leaking the absolute
server path — unlike image/video uploads, which already recover their
chip from the <video|image path> tag. Parse the notice the same way:
the materialized basename carries the file id, so the chip becomes
clickable again (and editable back into the composer); inline-base64
notices (content-hash named, no file id) still collapse into a
non-clickable chip instead of raw text.
The notice wording is now a client/server contract — flagged on
buildAttachedFileNotice.
* fix(web): preserve UUID file ids when rebuilding attachment chips
* fix(web): skip unresendable file chips when loading attachments for edit
---------
Co-authored-by: qer <wbxl2000@outlook.com>
* fix(web): confirm dialogs respond to Enter and await async actions
The confirm dialog's initial focus was resolved from the Button
component's $el, which is a text node in dev builds (the component has
a template-root comment, so it renders as a fragment). Focus fell back
to the header close button, so Enter cancelled instead of confirming.
Resolve the initial focus with a CSS selector on the confirm button
instead.
ConfirmOptions now accepts an async action: the dialog stays open with
a loading state (cancel/Esc/overlay suppressed) until the work settles.
The archive-session, remove-workspace, and delete-provider confirms
move from the menu components into App.vue so the dialog can await the
actual client call.
* fix(web): block superseding a confirm dialog while its action runs
A second confirm() during an in-flight action would replace the busy
dialog and inherit the global busy state, opening inert until the first
action settled. Resolve the new request unconfirmed instead.
* fix(kimi-web): improve mobile safe-area handling
* fix(kimi-web): restore dock-height fallback where ChatDock is absent
* fix(kimi-web): pin the app shell to the visual viewport height
* fix(kimi-web): pin the app shell to the visual viewport
* chore: add changeset for mobile safe-area fixes
* fix(kap-server): carry the live subagent roster in the session snapshot
* fix(kap-server): clear the subagent roster on the next main turn start
* fix(kap-server): exclude background subagents from the snapshot roster
* fix(kap-server): finalize live roster entries when the main turn aborts
* fix(kap-server): drop roster entries when foreground subagents detach
* fix(web): expand the swarm card by default while subagents are running
* docs(kap-server): qualify the roster-clearing durability claim
* fix(telemetry): deliver session_started in v2 headless mode
session_started/session_load_failed fire inside create()/resume(), but the CloudAppender was installed only after resolveNativeSession() returned, so they were dropped to the null appender.
- run-v2-print: install the appender before resolving the session; reconcile a resumed session's real model via setContext afterwards.
- sessionLifecycle: bind the session id in announceCreated before emitting so session_started carries session_id.
- CloudAppender.setContext: allow updating the model context.
* fix(agent-core-v2): bind session id on session_load_failed
The resume failure path emitted session_load_failed without binding the
session id first, so the event went out with session_id null even though
the service knows which session failed to load. Bind it via setContext
before track2, mirroring the announceCreated path for session_started,
and update the two tests that locked the empty context in as expected.
* feat(kimi-code): keep print-mode goal runs alive until the goal settles
- applyPrintBackgroundPolicy waits for goal continuation turns via a goalActive hook before applying the exit/drain/steer mode, bounded by the wait ceiling
- createPrintTurnEndings skips the timeout when the remaining budget is not finite
- update the changeset to cover the goal-run lifecycle
* fix(kimi-code): wake the print goal wait periodically so settled goals exit promptly
* fix(server): report CLI version as server_version
- kap-server: accept opts.version in startServer, reported as
server_version (/meta, OpenAPI, session exports, lock registry,
default User-Agent); defaults to its own package version
- kimi-code: pass the CLI product version when starting the server
- add boot test covering /api/v1/meta, lock file, and User-Agent
* fix(agent-core-v2): make new sessions resumable by older v1 builds
- add wireRecord.seal() to write the metadata envelope at agent creation,
so fresh logs satisfy v1 replay's first-record-must-be-metadata invariant
- seal the wire log before any op dispatch in AgentLifecycleService.create;
no-op when the log already has records (resume / forked copies)
- seed empty agents/custom maps in session metadata so v1 Session.resume()
can index agents['main'] on a v2-created state.json
- add seal unit tests and lifecycle sealing tests
* fix(kap-server): show real message send times in snapshot history
- read per-record times stamped on wire.jsonl via reduceContextTranscript
and cache them alongside the transcript messages
- prefer each record's real dispatch time for created_at; records without a
stamp fall back to session.createdAt + index, clamped so the page stays
strictly increasing (mirrors MessageLegacyService.list)
- add tests for fallback, clamping, and the page-offset index mapping
* fix(agent-core-v2): backfill missing agents/custom maps in existing session metadata
- load() now heals pre-fix v2 state.json documents that never gained the
agents/custom maps, persisting the backfill so one open on a new build
leaves the session resumable by released v1 builds (Session.resume()
indexes agents['main'] unconditionally)
- updatedAt is deliberately untouched so the format heal does not reorder
session listings
* feat(agent-core-v2): make subagent timeout configurable, default 2h
- add [subagent] config section with timeout_ms and KIMI_SUBAGENT_TIMEOUT_MS env override
- resolve Agent and AgentSwarm per-run timeouts through resolveSubagentTimeoutMs
- update tool descriptions and tests to reflect the 2-hour default
* feat(agent-core-v2): align print-mode background policy with v1
- add printBackgroundMode/printMaxTurns to the task config section with resolvePrintBackgroundMode (keepAliveOnExit fallback)
- apply exit/drain/steer policy to kimi -p on the experimental engine, buffering turn.ended events and failing the run when a steered turn fails
- register the subagent config section from the package entry
* fix(agent-core-v2): strict equality in metadata heal, comments in file headers only
- replace == null with === undefined in the session-metadata heal to
satisfy eqeqeq (oxlint --type-aware in CI)
- move the seal() rationale into the wireRecord contract header and the
agents/custom invariant into the sessionMetadata header per the
tree's header-only comment convention
* fix: preserve provider thinking effort values
* fix: resolve Kimi thinking effort fallbacks
* fix: use resolved Kimi effort in v2 requests
* fix: align Kimi effort resolution paths
* fix: synchronize forced Kimi effort state
* fix: synchronize forced Kimi effort in v2 state
* fix: tolerate unresolved models in v2 status
* fix(web): submit thinking level verbatim and drop the hardcoded default
Align kimi-web's thinking-level handling with the TUI:
- Submit the stored level as-is on every prompt path (prompt, steer,
skill activation, BTW side chat) instead of coercing it onto the
target model's declared efforts.
- No stored preference (undefined) instead of a hardcoded 'high'
default: prompts omit the thinking override and the daemon resolves
the config/model default, same as an unset [thinking] in the TUI.
- Model switcher pre-selects the target model's own default level when
switching models; re-selecting the current model keeps the level.
- Display the effective level (stored value, else the model default)
in the composer, mobile sheet, and /status panel.
* chore(web): remove the dead dev:stub script
The stub daemon (dev/stub-daemon.mjs) no longer exists, so the
dev:stub npm script and its docs references were dead weight.
* fix(web): pin the model default thinking level and persist picks globally
- With no stored preference, loadModels() pins the active model's
catalog default_effort as a concrete in-memory value, so what the
UI shows, what prompts submit, and what the session runs always
agree. localStorage stays reserved for levels the user picked.
- setThinking and model switches now also write the daemon-wide
[thinking] config (same mapping as the TUI's thinkingEffortToConfig),
so sessions created by other clients inherit the pick.
* fix(agent-core): mark auto-approved plan exits as not user-reviewed
In auto permission mode ExitPlanMode is approved without user
involvement, but its result read "## Approved Plan:" exactly like a
genuine user approval and the transcript showed a green "Approved"
chip. The model treated that as a signal to start executing, even
when the user had asked it to stop after planning.
- Emit an "auto-approved, not user-reviewed" result with a note that
execution follows the user's original instructions (v1 + v2).
- Extend the auto-mode reminder: plan approvals are automatic and are
not a user signal to proceed (v1 + v2).
- Render an "Auto-approved" warning-toned chip in the TUI, keeping
backward compatibility with the old result marker.
- Document the Auto mode plan-exit behavior in interaction guides.
* fix(agent-core): only mark plan exits as auto-approved in auto permission mode
Address review feedback on the auto-approved plan exit change:
- Branch the direct-execution output on the permission mode in both
engines: only auto mode yields the "auto-approved, not
user-reviewed" result and telemetry outcome. In manual / yolo modes
the direct path means a configured or session allow/ask rule let the
call through — an explicit user decision that keeps the user-approved
output, the Approved transcript chip, and the approved outcome.
- Move the v2 rationale out of the method body into the file header,
per the agent-core-v2 comment convention.
- Drop the absolute "only Auto mode" wording from the interaction
guides (en/zh).
* feat(agent-core-v2): support caller-supplied MCP servers on session create
- add mcpServers to CreateSessionOptions, forwarded to the session's first
ensureMcpReady call so caller-supplied servers ride on the initial load
- ensureMcpReady accepts callerServers, honored only by the call that starts
the initial load; later calls just await the in-flight single-flight load
- merge precedence mirrors v1 (rpc/core-impl.ts): file config <
caller-supplied < plugin servers
* feat(agent-core-v2): file tools reach skill roots; add skillDirs seed
- add extendWorkspaceWithSkillRoots to path-access; Read/Write/Edit/Grep/
Glob and media tools merge skill-catalog roots into the workspace per
call (v1 merged once at construction), so skill dirs outside the cwd
stay reachable and late-loading roots are picked up
- add skillCatalogRuntimeOptionsSeed; wire --skillsDir into the v2 print
CLI and skillDirs into kap-server startServer (v1 SDK skillDirs parity)
* chore: add changesets for v2 caller MCP servers and skill dirs fixes
* fix(protocol): make server_hello heartbeat_ms optional
kap-server dropped the server-initiated WS heartbeat and no longer emits
heartbeat_ms in server_hello, but the published v1 schema still required
it, so spec-compliant clients rejected the handshake before subscribing.
- mark heartbeat_ms optional in serverHelloPayloadSchema (advisory only)
- add a ws-control test for a server_hello without heartbeat_ms
- align kimi-web WireServerHello and the server-e2e handshake assertion
* fix(agent-core-v2): pass media parts through tool_result projection
- keep raw kosong content-part array for tool results carrying
image/video/audio parts instead of flattening to text
- restore ReadMediaFile media rendering after session reload/resume
* chore: add changeset for optional server_hello heartbeat_ms
* docs(agent-core-v2): move tool_result media rationale to module header
Per the agent-core-v2 comment conventions, comments live solely in the
top-of-file block — move the media-passthrough rationale out of the
buildProtocolContent JSDoc into the module header.
* feat(server): default to kap-server and remove the v1 server package
- kimi server run / kimi web now boot kap-server (agent-core-v2 engine)
unconditionally; the KIMI_CODE_EXPERIMENTAL_FLAG gate on the server
path is gone (the kimi -p print-mode gate stays)
- move the OS service manager (svc: launchd/systemd/schtasks) from
packages/server into packages/kap-server and export it there
- repoint the CLI server subcommands, tests, and dev scripts at
kap-server; relabel the web dev backend presets default/multi
- delete packages/server and update workspace bookkeeping (flake.nix,
pnpm-lock.yaml, changeset ignore docs, AGENTS.md, agent-core-dev skill)
* test(server-e2e): remove scenarios that depend on v1 debug endpoints
Scenarios 04-stateless-controls, 10-prompt-queue-steer and
12-send-and-cancel assert through the /api/v1/debug/prompts/*
introspection routes, which only the deleted v1 server mounted —
kap-server's --debug-endpoints is a documented no-op, so these
scenarios can only 404 now. The vitest e2e files using the same
surface already skip when it is absent.
* fix(web): size workspace picker from full content
* chore: add web workspace picker changeset
* refactor(web): use intrinsic workspace picker sizing
* fix(web): cap workspace picker to conversation pane
* fix(web): make mid-turn delta offsets step-relative
Reset in-flight text and client stream alignment at step boundaries so
resync seeds only the current step instead of duplicating prior steps.
* fix(web): dedupe resync-seeded messages by normalized content
The exact-JSON content signature missed duplicates when the two copies
differed by thinking signature, tool progress, part boundaries, or the
tool set (finished parallel tools leave running_tools). Reduce content
to concatenated stream text plus sorted tool-call ids and treat a
covered subset as the duplicate, merging the seed's tool progress into
the existing cards before dropping it.
* fix(web): restore the goal card after a page refresh
* fix(web): assert goal endpoint URL without stringification
* fix(web): skip goal recovery write when a live goal event wins the race
* fix(web): track goal events with a per-session version so clears win the recovery race