Commit graph

1174 commits

Author SHA1 Message Date
github-actions[bot]
13d86f8b7b
ci: release packages (#2881)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-08-14 20:51:17 +08:00
qer
d60d8ff7e8
chore: downgrade plan viewer changeset from minor to patch (#2923) 2026-08-14 20:46:50 +08:00
qer
cd489955bb
chore: sync web dist from code-app (#2922)
code-app: af7ed8fa03278bbe2f988732b3195649e7a6f2bc
2026-08-14 20:36:54 +08:00
qer
6cf315b7bd
fix(pi-tui): stop GFM autolinks at CJK punctuation boundaries (#2917)
* fix(pi-tui): stop GFM autolinks at CJK punctuation boundaries

marked's GFM autolink accepts any non-space characters after the domain
and its backpedal strips only ASCII trailing punctuation, so CJK or
full-width punctuation right after a bare URL was absorbed into the link
text and href (`.../pull/232(本地` rendered as one anchor whose OSC 8
target contained raw CJK and opened a broken address).

Register a CjkBoundaryUrlTokenizer (subclass of the upstream
StrictStrikethroughTokenizer, which stays byte-identical for
re-vendoring) that cuts the autolink match at the first CJK punctuation
character before the ASCII backpedal. CJK ideographs inside the URL path
itself are preserved. Guarded by new bare-URL CJK cases in
test/markdown.test.ts and listed in pi-tui's local-divergence inventory.

* fix(pi-tui): keep balanced full-width parens inside autolinked URLs

Address review feedback on the CJK autolink boundary: cutting at the
first full-width parenthesis anywhere in the match also truncated URLs
that legitimately contain balanced full-width parens in their path
(e.g. wiki disambiguation pages like .../wiki/中华人民共和国(1949年)).

Full-width parens now follow GFM's ASCII-paren rule: a paren-depth scan
keeps balanced pairs in the URL and only an unbalanced ( or )
terminates the match. Non-paren CJK punctuation still always terminates
it.

* fix(pi-tui): keep CJK punctuation inside balanced full-width parens

Punctuation inside a balanced full-width parenthetical is deliberate URL
content (e.g. .../wiki/中华人民共和国(北京,1949年)), so the non-paren
CJK terminator now only applies at paren depth 0. Prose parentheticals
contain spaces and never survive marked's match this far, and an
unbalanced ( still cuts the match at the open paren.
2026-08-14 20:15:33 +08:00
Grapedge
7475c2e2e3
feat(vscode): switch the extension to the v2 engine with a rollback switch (#2916)
Some checks are pending
CI / build (push) Waiting to run
CI / test (1) (push) Waiting to run
CI / test (2) (push) Waiting to run
CI / test (3) (push) Waiting to run
CI / test (4) (push) Waiting to run
CI / test (5) (push) Waiting to run
CI / test-pi-tui (push) Waiting to run
CI / test-vscode-legacy (push) Waiting to run
CI / test-windows (push) Waiting to run
CI / lint (push) Waiting to run
CI / typecheck (push) Waiting to run
Nix Build / Check flake.nix workspace sync (push) Waiting to run
Nix Build / nix build .#kimi-code (push) Blocked by required conditions
Release / Publish native release assets (push) Blocked by required conditions
Release / Release (push) Waiting to run
Release / Deploy docs (push) Blocked by required conditions
Release / Native release artifact (push) Blocked by required conditions
The extension now runs on the agent-core-v2 engine by default. The
interface, sessions, and workflows do not change. Two rollback paths
exist, and one function makes the decision
(config/vscode-settings.ts):

- the kimi.useAgentCoreV1 setting (temporary; a window reload applies
  the change);
- the KIMI_CODE_LEGACY_FLAG environment variable, which wins over the
  setting and has the same semantics as in the CLI.

An engine startup failure shows an explicit error that names the
rollback setting. There is no silent fallback. CI runs the extension
test suite on both engines: the sharded run covers the default v2
engine, and a new test-vscode-legacy job reruns the suite with
KIMI_CODE_LEGACY_FLAG=1.

To keep the v2 path identical to v1 for every method the extension
uses, this change also completes the v2-backed SDK client and the v2
engine:

- Implement session deletion in the v2 SDK client.
- Implement fork truncation at a turn index in the v2 engine, with the
  same rules as v1, and reject a fork while the source session has an
  active turn.
- Stop the session-level /init run when the turn is cancelled, as v1
  does.
- Read session metadata without the archived field as not-archived, so
  sessions written by the v1 engine open correctly.

The SDK parity suite now covers session deletion, cancel, and fork
truncation. The known-difference list for the methods the extension
uses is empty.
2026-08-14 17:22:10 +08:00
qer
741708f948
feat(kap-server): add plugin marketplace and capability REST routes (#2868)
* feat(agent-core-v2): surface a machine-key note from capability installs

CapabilityEntry.install now resolves an optional note exposed through
CapabilityInstallProgress.note (wire-visible). The webbridge entry
returns 'user-skill-migrated' when it migrates a pre-existing
standalone skill copy onto the plugin-managed one — clients can
localize the migration instead of the skill silently disappearing
from the user's directory.

* feat(kap-server): add plugin management and capability REST routes

Expose the App-scope plugin and capability services over the wire so
non-CLI hosts (desktop, web) can manage plugins and built-in
capabilities end to end:

- GET  /api/v1/plugins, POST /api/v1/plugins {source},
  POST /api/v1/plugins/{id}:{enable,disable,remove}
- GET  /api/v1/plugins/marketplace — catalog (pluginMarketplaceUrl
  server option / KIMI_CODE_PLUGIN_MARKETPLACE_URL env / production
  default) merged on demand with live install state; updateAvailable
  only on strict semver catalog > installed (no semver dependency)
- GET  /api/v1/capabilities, GET /api/v1/capabilities/{id},
  POST /api/v1/capabilities/{id}:install with client-polled progress
- New wire codes 40418 capability.not_found, 40419 plugin.not_found,
  40923 capability.install_in_progress, 40924 capability.unsupported

Mutations flow through IPluginService, so they serialize with other
install paths and fire onDidReload (session skill catalogs and the
capability shelf-install hook converge).

* fix(kap-server): map plugin input errors to 4xx and correct the unsupported test code

- mapPluginError now translates the domain's validation.failed (40001)
  and fs.path_not_found (40409) instead of collapsing client-fixable
  input mistakes (relative source, nonexistent local path) into a
  50001 internal error
- the non-macOS capability install test expected 40923, which this
  branch assigns to capability.install_in_progress; the unsupported
  code is 40924 (macOS runners skip the case, which is why it only
  fails on Linux/Windows CI)

* fix(kap-server): resolve catalog-relative marketplace sources and widen the unsupported-test skip

- The production CDN catalog carries sources relative to the catalog
  URL (./official/*.zip); clients handing them back to POST /plugins
  would hit the local-path normalizer's 40001. Resolve entry sources
  against the configured catalog URL so every returned source is
  directly installable.
- The 40924 install-rejection test only skipped macOS, but kimi-cu is
  also supported on Windows x64 — running it there would start the
  real installer. Skip on every supported platform.

* fix(kap-server): accept the legacy url/downloadUrl marketplace source aliases

Custom catalogs that the CLI already accepts can carry an entry's source
under url or downloadUrl instead of source; the route's strict schema
rejected the whole catalog with 50001. Normalize the aliases before
validation (same precedence as the CLI parser) so those catalogs keep
working through /api/v1/plugins/marketplace.

* fix(kap-server): support local marketplace catalogs and drop conditional spreads

- KIMI_CODE_PLUGIN_MARKETPLACE_URL accepts a plain path or file://
  catalog in the CLI loader; the route only fetched over HTTP, so local
  catalogs 50001'd for desktop/web hosts. Read local catalogs from disk
  and resolve their relative sources against the catalog's directory.
- Replace the marketplace mapping's conditional spreads with direct
  possibly-undefined properties per the repo rule.

* fix: surface capability install notes through klient and convert file:// entry sources

- The klient capabilities contract omitted install.note, so zod parsing
  stripped it and facade callers (node-sdk, TUI) never saw
  'user-skill-migrated'. Add the field and pin it in the facade test
  fixture.
- A marketplace entry source given as a file:// URL fell through to the
  relative-branch and came back as a garbage path; convert with
  fileURLToPath so the advertised source stays installable.

* test(kap-server): keep the new route tests portable to Windows x64

- The capabilities list assertion treated every non-macOS host as
  unsupported, but kimi-cu is supported on Windows x64 — derive the
  expectation from the same platform predicate.
- file:///abs/... is not a valid absolute file URL on Windows (no drive
  root); build the fixture with pathToFileURL from a temp path instead.

* refactor: align the capability note and test helper with repo conventions

- agent-core-v2 keeps explanatory docs in the top-of-file block only;
  the note contract already lives in the capability types header, so
  drop the two member-level doc blocks.
- The plugins route test helper sets the optional fetch body directly
  instead of via a conditional spread.

* fix(kap-server): expand ~ in local marketplace catalog paths

The CLI loader expands ~/ against the home directory; the route read
the path literally, so KIMI_CODE_PLUGIN_MARKETPLACE_URL=~/catalog.json
50001'd for desktop/web hosts while working in the CLI. Share one
localCatalogPath helper (file:// conversion + tilde expansion) between
the catalog read and the relative-source resolver.

* fix(kap-server): expand home-relative marketplace entry sources

A catalog entry with source '~/...' fell through to the catalog-relative
branch and came back as <catalog-dir>/~/... — unresolvable by POST
/plugins. Expand ~ via the shared helper before the absolute/relative
decision.

* fix(kap-server): match CLI field semantics for source aliases and stub the Windows home

- A blank or non-string source no longer shadows the url/downloadUrl
  aliases; the first valid (non-blank, trimmed) of source/url/downloadUrl
  wins, mirroring the CLI parser's stringField.
- The tilde test also stubs USERPROFILE so os.homedir() resolves to the
  fixture home on Windows runners.

* fix(kap-server): read a blank marketplace tier as missing

The CLI parser trims tier and treats a blank as absent (third-party);
the route's enum rejected the whole catalog with 50001. Normalize the
tier alongside the source aliases in the same preprocess.

* fix(kap-server): derive marketplace versions from GitHub release sources

Entries that omit version but encode it in a GitHub release/tag (or
tree/commit) source never surfaced updateAvailable. Derive the version
from the resolved source — same URL shapes as the CLI parser, validated
with the route's strict x.y.z rule (no semver dependency).

* fix(kap-server): fail catalog validation on a source with no usable value

A whitespace-only source with no valid alias passed z.string().min(1)
untrimmed and resolved against the catalog URL into nonsense. Drop the
key during normalization so the schema reports the entry as missing its
source (same outcome as the CLI's 'must define source').

* fix(kap-server): resolve latest versions for bare GitHub marketplace entries

A catalog row whose source is a bare GitHub repo (the production curated
rows are shaped this way) kept version undefined, so updateAvailable
never fired for exactly the entries most likely to update. Resolve the
latest release tag through the /releases/latest redirect — the UI route,
not the rate-limited API — same as the CLI, degrading to no version on
any failure.

* docs(kap-server): note the marketplace version resolution in the plugins route header

* feat(kap-server): mark capability wiring rows in the marketplace response

A client following only /plugins/marketplace + POST /plugins would
install a capability's wiring plugin without its binary runtime, with
no wire-level way to tell. Entries whose id matches a capability's
wiring plugin now carry capabilityId, so clients route them through
/capabilities/{id}:install — the client-side routing pattern the CLI
established (the upstream design that replaced the server-side hook).

* fix(kap-server): fall back to the source-checkout catalog for the default location

When the marketplace location is the built-in default (no server option
or env override) and the fetch fails, read the repo checkout's own
plugins/marketplace.json — the CLI loader's behavior for offline
source-checkout dev. An explicitly configured catalog still fails hard
with 50001. Bundled installs have no checkout file, so the fallback
simply never fires there.

* fix(kap-server): resolve fallback catalog sources against the fallback file

readMarketplaceCatalog returned only the JSON, so entries from the
source-checkout fallback resolved their relative sources against the
(unreachable) CDN URL — coming back as unusable https paths instead of
local directories. The reader now returns the location actually read,
and source resolution uses it.

* fix(kap-server): honor the CLI's marketplace metadata aliases

Custom catalogs using name / shortDescription / websiteURL (accepted by
the CLI parser) lost those fields to schema stripping, falling back to
the entry id. Normalize the aliases in the same preprocess as the
source/tier normalization.

* fix(kap-server): filter marketplace keywords instead of rejecting the catalog

A keywords array with non-string or blank members failed the strict
schema and took the whole catalog down with 50001. Normalize to the CLI
parser's semantics: non-array reads as missing, arrays keep trimmed
non-blank strings only.

* fix(kap-server): treat a blank or non-string marketplace version as missing

The CLI parser reads version through its lenient stringField and falls
through to source-derived versions; the route's schema rejected a
numeric version with 50001 for the whole catalog. Normalize version in
the preprocess like the other fields — the gh-plugin fixture now
carries a numeric version and still derives 2.0.0 from its tag source.

* fix(kap-server): trim marketplace entry ids before the install-state join

A whitespace-padded id survived validation raw and never matched the
installed records (updateAvailable silently lost). Normalize the id in
the preprocess — trimmed, blank rejected — matching the CLI's
requiredString.

* fix(kap-server): gate capability markers to the default catalog

A custom catalog (env or server option) may legitimately carry a
same-id fork of a capability's wiring plugin; marking it capabilityId
would route users to the built-in install. Apply the marker only for
the default catalog (including the source-checkout fallback), matching
the CLI injecting built-in rows only for the default catalog.

* fix(kap-server): compare marketplace versions with real semver

The hand-rolled strict x.y.z check rejected valid semver the CLI
accepts (v-prefixed, prerelease tags), so updateAvailable diverged
between CLI and wire clients. Take the semver package (already in the
monorepo via the CLI) for the update check and the two source-derived
version validators.

* fix(kap-server): validate marketplace entry types and count the dev server as default

- Custom catalog rows with an unsupported type (e.g. integration) were
  stripped by the schema and advertised as installable plugins; the CLI
  rejects the catalog outright. Model the same plugin/managed/guide
  vocabulary.
- scripts/dev.mjs marks its repo-owned catalog with
  KIMI_CODE_PLUGIN_MARKETPLACE_FROM_DEV_SERVER=1 — honor the flag in
  the isDefault check so capability markers and the checkout fallback
  behave exactly like the CLI under the dev marketplace.

* fix(kap-server): join capability rows through their platform wiring plugin id

kimi-cu installs its wiring plugin as kimi-cu-win on Windows x64, so a
catalog row keyed kimi-cu never matched the installed record there (no
installed state, no updateAvailable). The row mapping now knows each
capability's wiring plugin ids and joins through them.

* fix(kap-server): map plugin load failures to 40001

An install source pointing at a directory/zip with a missing or invalid
manifest throws plugin.load_failed — a client-fixable input error that
fell through to 50001. Map it to validation.failed alongside the other
input mistakes.

* build(kap-server): align @types/semver with the workspace version

sherif rejects multiple workspace versions of one dependency; the CLI
pins @types/semver at ^7.7.0.

* refactor(agent-core-v2): share the plugin marketplace client/parser across hosts

The kap-server marketplace route grew its own copy of the CLI's catalog
loading/parsing logic (lenient aliases, blank-means-missing fields,
source resolution, GitHub version derivation) — two implementations of
a public, hand-writable format would drift on every catalog change.
Move the read/parse/version machinery into the plugin domain as
app/plugin/marketplace (pure functions, no DI): the CLI keeps a thin
wrapper owning configured-source resolution and its checkout fallback,
and the route keeps only the wire concerns (install-state merge,
capabilityId markers, error envelopes). plugins.ts drops ~230 lines of
duplicated machinery.

One deliberate behavior fix rides along: tilde entry sources now expand
against the home directory at parse time (the CLI previously passed
them through literally, failing later at install validation).

* docs(agent-core-v2): fold the marketplace module's member docs into the file header

The package convention keeps explanatory comments in the top-of-file
block only; the moved parser carried several function/member-level
JSDoc blocks from its CLI home. The header now carries the format
contract, leniency rules, source/version resolution order, built-in
masking semantics, and the fallback gating rule.

* docs(agent-core-v2): drop the remaining statement comments in the marketplace module

The header carries the rationale (update semantics, GitHub ref shapes,
the releases/latest choice); the convention allows nothing beside
statements.

* fix(kimi-code): import the shared marketplace module by its deep path

constant/app.ts is evaluated on every CLI invocation; re-exporting from
the agent-core-v2 root would pull the whole engine module graph into
startup. The package's wildcard subpath export lets both CLI files take
only the pure marketplace module (node builtins + semver).

* feat(kap-server): fan plugin and capability lifecycle out as global WS events

Clients currently poll the plugins/capabilities REST surfaces and can
hold stale rows while another client mutates the set. Publish two global
events instead:

- event.plugin.changed — fired off IPluginService.onDidReload, so any
  install/enable/disable/remove from any client reaches every host
- event.capability.changed — every capability install progress
  transition (CapabilityService gains onDidChangeInstall), so rows
  update live and settle is observable without polling

Both ride the existing global fan-out (no subscription needed) and are
documented in the wire schema registry.

* fix: register the lifecycle events in the wire union and tidy the contract header

- event.plugin.changed / event.capability.changed were declared but not
  part of agentEventSchema, leaving the wire catalog incomplete.
- The onDidChangeInstall member doc moves into the capability contract
  file header (package comment convention).

* feat(protocol): mirror the plugin/capability lifecycle events in the shared WS schema

Clients and e2e harnesses validating server frames against
@moonshot-ai/protocol would reject event.plugin.changed /
event.capability.changed. Register both in the shared catalog (TS
interfaces, zod schemas, and both unions), matching the
model_catalog.changed precedent for global events.

* fix(kap-server): prefer the platform wiring plugin when joining capability rows

A stale same-id record (e.g. a raw kimi-cu plugin next to the real
kimi-cu-win wiring on Windows x64) previously won the join, showing the
wrong installed state and update availability. Capability rows now join
through the wiring plugin ids in platform preference order before
falling back to the catalog id.

* fix(kap-server): put the github metadata of plugin summaries on the wire schema

GitHub-sourced plugin summaries carry github {owner, repo, ref,
installedSha} from the domain; the route serializes raw domain objects,
so the field reached clients undocumented. Declare it in
pluginSummarySchema so the OpenAPI surface matches reality.

* test(node-sdk): cover the new lifecycle events in the exhaustive switch

The event-type exhaustiveness test broke when the shared protocol union
gained event.plugin.changed / event.capability.changed.

* fix(kap-server): mark capability progress events volatile

Per-chunk download progress transitions ride the same fan-out as
durable frames and were being persisted to the __global__ journal —
hundreds of stale frames per install. event.capability.changed is
live-only state, so it joins the volatile list alongside
event.di.unit_changed; the settle frame stays recoverable via a direct
capability read. event.plugin.changed remains durable (rare, and a
reconnecting client should replay it).

* feat(kap-server): inject built-in capability rows into the default catalog response

The checked-in production catalog carries kimi-webbridge but not
kimi-cu — the CLI injects built-in rows client-side, so wire clients
never saw Kimi Computer Use in /plugins/marketplace. For the default
catalog the route now appends supported capabilities the catalog lacks
(static descriptors via ICapabilityService.describeCapabilities — no
detector probes), marked with capabilityId and a capability:<id>
sentinel source so installs still route through the capability
surface.

* fix(kap-server): run injected capability rows through the install-state join

The injected kimi-cu row hardcoded installed: undefined, so an
already-installed capability still read as installable. Injection now
happens before projection, so injected rows get the same backing-plugin
join (installed state, update badge, capabilityId marker) as catalog
rows. Also moves the describeCapabilities note into the contract header
(package comment convention).

* test(kap-server): gate the injected-row assertions on platform support

kimi-cu injects only where supported (macOS / Windows x64); on Linux CI
the row is correctly absent.

* fix(protocol): classify capability progress as volatile in the shared catalog

kap-server never journals event.capability.changed (it is in the
server-local volatile list); shared-protocol clients reading
isVolatileEventType would treat per-chunk progress frames as durable
and replayable. Mirror the classification.

* fix(kap-server): hide capability rows on unsupported platforms

Catalog-carried capability rows (kimi-webbridge in the default catalog)
were marked with capabilityId regardless of host support — on an
unsupported platform clients would route into an impossible capability
install. Rows whose capability is unsupported are now excluded from the
default-catalog response entirely (the CLI hides its built-in rows the
same way).
2026-08-14 15:45:34 +08:00
7Sageer
249d8faa34
fix(agent-core-v2): mint interaction ids engine-side (#2911)
* fix(agent-core-v2): mint interaction ids engine-side

Self-hosted OpenAI-compatible endpoints may renumber tool call ids on
every response (Bash_0, Bash_1, ...). The approval/question/user_tool
facades used the provider toolCallId as the interaction id, so a
repeated id was silently swallowed by client-side pending-interaction
dedupe: the approval prompt never appeared and the turn parked
forever (#2908).

Interaction ids are now minted by the engine (approval_<uuid> /
question_<uuid> / user_tool_<uuid>); the provider toolCallId stays on
the payload for correlation. This matches v1 semantics, where the
approval id was already a daemon-minted id independent of the tool
call id.

* fix(agent-core-v2): normalize duplicate provider tool call ids at ingestion

Self-hosted OpenAI-compatible endpoints may renumber tool call ids on
every response (Bash_0, Bash_1, ...), and every downstream keying
assumes an id identifies exactly one call: context rebuild silently
drops the second tool result with a duplicated id, the strict
projector discards duplicate calls, transcript frames merge, and
approval/activity correlation misfires.

A per-agent ToolCallIdNormalizer in the llmRequester stream boundary
now tracks ids already claimed (seeded from the restored context).
The first occurrence passes through unchanged; later occurrences —
across responses or within one — are rewritten to a readable
<id>__<n> suffix, kept consistent between streamed deltas and the
finalized message, and logged for provenance. A failed attempt rolls
its claims back so a projection retry re-streams the same logical
calls under the same ids.

* fix(agent-core-v2): thread the minted approval id through events and status

The permission.approval.requested/resolved events only carried the
provider toolCallId, so AgentActivityView exposed approvalId =
toolCallId and the agent.status.updated approval phase forwarded an id
that POST /sessions/{sid}/approvals/{id} cannot resolve — the kernel
parks under the minted approval_<uuid>.

Mint the interaction id at the agent call site and include it in the
approval request payload: the kernel honors the explicit id, the
events carry it, and the activity view keys pendingApprovals by it
(falling back to the toolCallId for id-less events).

* fix(agent-core-v2): surface minted interaction ids in facade listPending

The approval/question facades returned only the original payload from
listPending(), so once the kernel id stopped deriving from the
provider toolCallId, hosts listing pending requests had no id to feed
back into decide()/answer()/dismiss() without reaching into the
kernel. Merge the parked interaction id into each returned request —
the klient contract schemas already carry the optional id field, so
the RPC surface becomes round-trippable as well.
2026-08-14 14:47:00 +08:00
weiwei Wang
53909d91e3
fix(kimi-web): cache content-hashed assets (#2865) 2026-08-14 13:08:30 +08:00
Haozhe
cb30a7799d
fix(kap-server): expose parent_tool_call_id and subagent_type in /tasks responses (#2912) 2026-08-14 13:03:56 +08:00
Haozhe
eb72aebeeb
fix(kap-server): remove the 64 MiB web session export limit (#2910) 2026-08-14 11:48:56 +08:00
Haozhe
325913a532
feat(kap-server): expose the subagent registry id in /tasks responses (#2907) 2026-08-14 11:28:07 +08:00
Louis Gu
245e3d56a6
fix(tui): sanitize background task output (#2863)
Co-authored-by: liruifengv <liruifeng1024@gmail.com>
2026-08-14 11:19:27 +08:00
oocz
102984aa66
fix: settle cancelled MCP OAuth callbacks (#2899)
Some checks are pending
CI / build (push) Waiting to run
CI / test (1) (push) Waiting to run
CI / test (2) (push) Waiting to run
CI / test (3) (push) Waiting to run
CI / test (4) (push) Waiting to run
CI / test (5) (push) Waiting to run
CI / test-pi-tui (push) Waiting to run
CI / test-windows (push) Waiting to run
CI / lint (push) Waiting to run
CI / typecheck (push) Waiting to run
Nix Build / Check flake.nix workspace sync (push) Waiting to run
Nix Build / nix build .#kimi-code (push) Blocked by required conditions
Release / Native release artifact (push) Blocked by required conditions
Release / Publish native release assets (push) Blocked by required conditions
Release / Release (push) Waiting to run
Release / Deploy docs (push) Blocked by required conditions
Co-authored-by: yuchengzhen <yuchengzhen@moonshot.cn>
2026-08-13 22:56:06 +08:00
Haozhe
1414d46028
refactor(agent-core-v2): fold session lifecycle hooks into sessionLifecycle events (#2896) 2026-08-13 21:03:51 +08:00
7Sageer
5857ba23b0
test(kap-server): deflake three timing-sensitive tests (#2894)
* test(kap-server): poll the read-model immediate-read assertions

The 'prepares the read model at boot and serves immediate reads' test
sampled the session list / workspace session_count / paged read exactly
once after creating a session. While a mirror flush is in flight its
batch is only per-shard atomic and the pending-queue cleanup is not
linearized with reads, so a single-sample read landing inside that
window can transiently miss or double-count the new session (seen
twice on main CI as 'expected false to be true' and 'expected 0 to be
1'). Poll with vi.waitFor instead; the transient lasts at most one
in-flight flush (~100ms cadence).

* test(kap-server): retry the transcript test temp-dir teardown rm

The engine's file log writers flush synchronously on scope dispose but
their trailing async close can still create a file under the test home
after server.close() resolves, so the afterEach rm occasionally fails
with ENOTEMPTY on a loaded CI runner. Retry the rm (maxRetries: 5),
matching the existing pattern in questions.test.ts / fs.test.ts.

* test(kap-server): drain the in-flight search sync before appending

The 'serves the published generation without waiting for a blocked
background sync' test appended the delta right after a warm-up search
that had kicked a fire-and-forget background sync pass. On a starved
CI worker thread that pass can read the file after the append and
publish both documents early ('expected 2 to be 1'). settleSync before
the append makes 'no pass can index the delta' structural.
2026-08-13 21:02:34 +08:00
7Sageer
0473b3aac1
test(minidb): deflake the evict-lru compaction-churn guard (#2892)
The stress test asserted stats.compactions > 0 immediately after the
write loop, but auto-compaction is fire-and-forget through the
maintenance scheduler and the counter only increments once a run fully
completes. On a loaded CI runner the loop can finish before the first
compaction lands, failing the guard even though nothing is broken.

Wait for the first completed compaction with the existing waitFor
helper instead: the guard still proves the churn this scenario
requires happened, and a genuinely broken auto-trigger now fails via
the wait timeout.
2026-08-13 21:00:56 +08:00
Haozhe
67e73f3e76
refactor(agent-core-v2): extract date change feature (#2895)
Move date-change reminders behind the Feature lifecycle and make state registrations disposable so unloading removes the runtime seed.
2026-08-13 20:58:58 +08:00
7Sageer
c60e3e301d
docs: drop legacy secondary-model content and unify subagent terminology (#2891)
- Remove the legacy-engine secondary-model recipe section, the
  KIMI_SECONDARY_MODEL / KIMI_SECONDARY_EFFORT env entries, and the
  model_preference agent-file field: the default engine never reads
  them and the legacy engine is deprecated.
- Remove the backward-compat note for a lone [secondary_model] model
  key; the code still reads it, but the docs now only document the
  current pool scheme.
- Reframe the secondary_model section around the subagent model pool
  instead of a singular secondary model.
- Unify zh terminology: 子 Agent -> subagent, 主 Agent -> main agent,
  covering prose, headings, anchors, the sidebar label, and the
  docs/AGENTS.md term table.
2026-08-13 19:57:46 +08:00
Haozhe
4425409cea
fix(features): retract contributed service metadata (#2886)
* fix(features): retract contributed service metadata

- tie contributed service discovery to feature disposal
- reject duplicate providers for the same scope and service
- cover feature unload and debug channel resolution

* test(features): preserve contributed service registry

- remove the global contributed-service test reset
- keep provider cleanup local to each regression test

* fix(features): scope service discovery to each app

- store feature service metadata in the app-local collection tree
- bind debug lookup and test overrides to the owning app root
- keep duplicate provider activation atomic within one app
2026-08-13 19:13:00 +08:00
liruifengv
4a93f70aa2
feat(oauth): add browser-safe ./device subpath export (#2885)
Some checks are pending
CI / test (2) (push) Waiting to run
CI / test (3) (push) Waiting to run
CI / test (4) (push) Waiting to run
CI / test (5) (push) Waiting to run
CI / test-pi-tui (push) Waiting to run
CI / test-windows (push) Waiting to run
CI / lint (push) Waiting to run
CI / typecheck (push) Waiting to run
CI / build (push) Waiting to run
CI / test (1) (push) Waiting to run
Nix Build / Check flake.nix workspace sync (push) Waiting to run
Nix Build / nix build .#kimi-code (push) Blocked by required conditions
Release / Release (push) Waiting to run
Release / Deploy docs (push) Blocked by required conditions
Release / Native release artifact (push) Blocked by required conditions
Release / Publish native release assets (push) Blocked by required conditions
* feat(oauth): add browser-safe ./device subpath export

* fix(oauth): guard env override lookup for browser consumers

* chore(oauth): add changeset for ./device subpath export

* fix(oauth): resolve env overrides via globalThis for DOM-only consumers
2026-08-13 18:26:10 +08:00
liruifengv
1811bd4baf
fix(tui): keep banner main text readable with long tags on narrow terminals (#2884)
* fix(tui): keep banner main text readable with long tags on narrow terminals

The banner layout inlines the tag and wraps the main text into the
remaining width. Remote banner configs can set a full-sentence tag
(e.g. the 38-char K3 thinking-effort banner), which on narrow terminals
leaves the main text only a few columns, so it wraps into a ragged,
hard-broken column ("balan/ce", "capab/ility").

When the inline tag would leave the main text fewer than 16 columns,
render the tag on its own line and give the main text and subtext the
full width, aligned with the tag text. Short tags stay inline; tags
wider than the terminal are still dropped as before.

* chore: add changeset for banner narrow-terminal fix

---------

Co-authored-by: Mira <mira-bot@moonshot.cn>
2026-08-13 18:12:46 +08:00
Haozhe
4739284fb9
refactor(features): extract session init feature (#2887)
- move the session init domain under features
- contribute the session service through SessionInitFeature
- cover feature withdrawal and restoration
2026-08-13 18:12:08 +08:00
7Sageer
6be26978b1
feat: auto-generate session titles via the managed chat_title tool (#2351)
* feat: auto-generate session titles via the managed chat_title tool

With the auto-title experimental flag on and a managed OAuth login, the
session title is generated from the first prompt, replacing the
truncated-prompt easy title. A custom title set by the user is never
overwritten, and generation failures degrade silently to the easy title.

- oauth: fetchChatTitle for the platform /tools chat_title method
- agent-core (v1): fire-and-forget generation on the first prompt
- agent-core-v2: sessionTitle domain watching the easy-title event
- kap-server: POST /sessions/{id}/title/generate for manual regeneration

* fix: harden auto-generated session titles

* fix: preserve managed title request headers

* Pair auto-title endpoint overrides with matching OAuth credentials

* fix: preserve legacy custom session titles

* fix: preserve automatic session title invariants

* refactor: keep only the on-demand session title generation interface

Drop the automatic wiring on both engines: the v1 (TUI) first-prompt
trigger and the v2 easy-title event watcher. SessionTitleService's
generateTitle() stays as the single on-demand entry point behind the
auto-title flag, backing the kap-server title/generate route. The
changeset goes away too: with no shipped consumer, the remaining
surface is not user-perceivable.

* feat: generate session title from the first recorded prompts

Record up to three sanitized natural-language prompts in session
metadata (skill / plugin activations excluded) and compose the
chat_title input as order-labeled lines truncated to a 1000-char
budget, falling back to lastPrompt for sessions without recorded
prompts.

* test: make session title race tests deterministic

* Generate session titles from agent conversation history

* fix: reject title generation without user prompts

* fix: bound session title prompt history

* feat: enable session title generation without an experimental flag

* test: cover session title generation through the public REST path

* feat: request session title generation from the TUI after each turn

* Retry auto title generation for prompt-derived session titles

* feat: record session title source and harden the generation lifecycle

- persist titleSource (prompt/generated/custom); skip auto-generation over
  an already-generated title unless forced, and never over a custom one
- plumb the force option from the core through klient and node-sdk to the
  REST title/generate endpoint
- drop the title write-back when the session scope was superseded
  mid-flight, and retry once with a force-refreshed token on a 401
- stop closing sessions a concurrent public resume has handed out in the
  temporary resume paths (generateSessionTitle, renameSession)
- accept session.meta.updated patches without lastPrompt in klient event
  validation, and emit exactly one metadata event per applied title
- remove the retired prompts field heal and drop the changeset (the
  behavior is only perceivable on the experimental v2 engine)

* chore: follow agent-core comment convention

* fix: ignore stale session title callbacks

* fix: preserve session title state invariants

* refactor: seed session lifetime instead of querying the workspace handler

The session title service must not depend on the Workspace-tier handler
registry. The handler now seeds each session scope with an abort signal,
fires it synchronously when a close begins, and the title service carries
the signal on its request, drops the write-back once aborted, and drains
an in-flight generation through the onWillCloseSession hook.

* fix: honor the legacy custom title marker over a stale titleKind

A v1 rename spreads the original state.json document, so an explicit
isCustomTitle: true can travel with a stale titleKind. The explicit
marker now wins on load, and every persist double-writes the derived
isCustomTitle so released v1 builds keep recognizing the custom title.

* fix: serialize session access and expose the session title state

The temporary resume/rename/close paths and the public lifecycle
operations now share a per-session queue, so a public resume can never
receive a handle whose cleanup close is already in flight. Session
summaries carry the canonical title state, letting the TUI skip title
generation for sessions whose title was already generated or customized
instead of re-asking after every turn.

* chore: add session title changesets

* fix: close the session lifecycle races around close and title generation

A close/archive is now tracked in a closing registry from its first
synchronous step until disposal: get/list hide the closing session and
resume waits the close out instead of returning the doomed handle, and
fork waits out an in-flight source close. The title service tracks the
whole generateTitle call as the unit the close hook drains, and the
generated-title write re-checks the lifetime signal inside the serialized
metadata update so an abort landing while the update is queued still
vetoes the write-back.

* feat: project the session title state through the session index

readSummary and the read-model mirror carry titleKind, so listSessions
reports the same canonical title state as a resumed session's summary.

* fix: serialize the remaining session access paths in the SDK

forkSession and explicit-id createSession join the per-session queue, and
the harness resume fast path skips a session whose close is in flight
instead of returning the closing facade (which then failed every call
with session.closed); its late onClose no longer evicts the fresh
session either. The harness rename event now carries isCustomTitle so
the TUI stops asking for a generated title after a local rename.

* fix: detach the external abort listener once the chat title request settles

* fix: harden the session close/archive and create/fork lifecycle

The closing registry now records the operation kind: an archive arriving
during a plain close waits it out and lands the archived flag on the
persisted document instead of riding the close to success, and a failing
close hook no longer strands a half-closed session — the teardown always
completes while the hook error still reaches the caller. create and fork
reserve their target id synchronously with the existence check, so a
concurrent create/fork of the same id loses up front and can never tear
down the winner's scope or directory.

* fix: keep forced title regeneration independent and veto queued title writes atomically

Plain generateTitle calls still coalesce onto one shared in-flight
generation, but a forced regeneration always runs on its own so it is
neither swallowed by a plain call's early exit nor shares its result; the
close hook drains every active generation. The allowWhen veto now runs
inside applyUpdate with no await between the check and the mutation, so
an abort cannot slip into the gap.

* fix: carry the title state through the session index and klient contract

The klient session summary schema no longer strips titleKind, and the
index readSummary honors a legacy isCustomTitle marker over a stale
titleKind, so listSessions reports the same canonical title state as a
resumed session.

* fix: coalesce harness resumes, lock fork targets, and cover the title state end to end

Concurrent public resumeSession calls now share one in-flight resume and
one facade instead of building parallel facades over the same engine
handle (a close on either would strand the other). forkSession takes the
source and target queues in sorted order, so fork(A->X) is atomic against
create(X) and fork(B->X) without an ABBA deadlock. The emitMetaUpdated
patch type drops the redundant undefined union, and the SDK tests now
cover facade coalescing and the title state across list and resume.

* fix: serve the canonical title state from the session index and version the read-model cache

readSummary now derives the title state with the same priority chain as
the metadata document's canonical normalization (explicit custom marker,
valid titleKind, legacy false marker, customTitle, plain title), so list
and resume agree on legacy documents too. Read-model cache entries carry
a summary version stamp and older-stamped entries are treated as cold
misses, so an upgraded reader never serves a stale-shaped summary.

* fix: let the newest title generation request win the write-back

A forced regeneration could be followed on disk by an earlier plain
call's slower backend response. Each generation now carries a
monotonically increasing sequence (assigned only once a request actually
proceeds to generation), and the serialized metadata write is vetoed
unless the writer is still the newest request.

* fix: fold archive into close and own the create/fork rollback

An archive requested during a plain close is applied through the live
metadata during the teardown (or lands on the persisted document when it
arrives too late or the close fails), publishes the archived event, and
works on cold sessions too. A resume waiting on a failed close retries
instead of propagating the hook error, the teardown completes even when
the agent drain fails, and the create/fork rollback only ever removes
its own handle — a loser of the reservation race can no longer tear down
the winner's live scope.

* fix: key harness resume coalescing by the full input

Concurrent resumes only share a facade when their inputs match — a
caller passing different dirs, replay, profile, or kaos options gets its
own resume instead of having its options silently dropped.

* refactor(agent-core-v2): drop session close-awareness from title generation

Auto title is best-effort: a generation racing session close no longer
cancels its fetch or guards its write-back, so the per-session
sessionLifetime AbortSignal seed, the onWillCloseSession drain, and the
close-time invalidation go away. The newest-request-wins write-back
predicate stays.

* Delete .changeset/sdk-session-title-kind.md

Signed-off-by: 7Sageer <sag77r@hotmail.com>

* refactor(session-title): drop the unused force regeneration path

Nothing calls force: with it gone, plain calls always coalesce onto the
shared in-flight generation, so the generation sequence and the
caller-supplied allowWhen veto lose their only purpose and go with it.
The title/generate REST route takes no body anymore.

* refactor(agent-core-v2): drop the title state projection from the session index

The listed-session titleKind had no consumer: the TUI's title-generation
gate seeds from the resumed summary, which reads the live metadata
document, and the kap-server REST wire never carried the field. Removing
the projection also retires the read-model summary version stamp (the
remaining shape is fully field-checkable) and the duplicate title-kind
derivation that had to stay in lockstep with sessionMetadata. The klient
list contract and the node-sdk list mapper drop the field with it; the
resumed/live summary still reports the canonical title state.

* refactor(agent-core-v2): inline the transcript live-tail merge into messageLegacy

mergeContextTranscriptWithLive had a single caller; move the logic into
messageLegacyService as the private mergeLiveTail and drop the export.

* refactor(agent-core-v2): drop the closing registry from the session lifecycle

Auto title no longer consumes close-awareness, so the machinery goes
back to the simple forms: close/archive run straight through, resume
no longer waits out an in-flight close, create/fork drop their target
reservation, and a cold archive is a no-op again. Reverts the behavior
of e7c397a7c and cd1cea0fd on top of the sessionLifecycle rename.

* fix(agent-core-v2): complete the HostRequestHeaders migration in the title test

The main merge reduced #/kosong/model/hostRequestHeaders to the pure
port contract; define the test's headers as a plain value matching it
and tidy the SDK test import grouping.

* fix(node-sdk): mark resume telemetry field ignored

* feat(tui): request the session title as soon as a prompt is accepted

* fix(agent-core-v2): drop the numbered user prefixes from the title request input

* refactor(tui): ask for the session title only once per session attach

* refactor(tui): drop the automatic session title trigger

Keep the capability only: the engine-side title generation service, the
POST /sessions/{id}/title/generate route, and the SDK generateSessionTitle
method stay; the TUI no longer requests a title on prompt accept or on
session attach. The changeset now covers the SDK capability instead of a
CLI-facing auto title.

* Delete .changeset/session-title-generation.md

Signed-off-by: 7Sageer <sag77r@hotmail.com>

* feat(agent-core-v2): add forced session title regeneration

ISessionTitleService.generateTitle and the metadata write-back take an
optional force flag that bypasses the custom/generated guards, so an
explicit user request (the desktop/web rename field's Gen Title action)
can overwrite any current title; the applied title is marked generated.
Forced calls skip the in-flight coalescing, and a forced write is plain
last-writer-wins.

kap-server's POST /sessions/{id}/title/generate accepts an optional
{ "force": true } body; klient and the node-sdk plumb the option through
(GenerateSessionTitleInput.force).

Also renumber SESSION_TITLE_UNAVAILABLE to 40923: main assigned 40922 to
PAGE_TOKEN_MISMATCH after this branch forked.

* feat(agent-core-v2): selectable conversation excerpts for title generation

generateTitle gains a source option alongside force:

- user_prompts (default): the existing first-prompts window, unchanged.
- first_turn: the opening user prompt paired with the first turn's final
  assistant text — strict, so a caller asking before the first reply lands
  simply gets unavailable and can retry at the next turn boundary.
- digest: first prompt + latest prompt + the latest turn's final assistant
  text, tolerating a compacted window by using whatever segments survive;
  meant for explicit regeneration on multi-turn sessions.

Assistant segments keep only natural-language text parts (tool calls,
thinking, and media never contribute) and pass through the shared metadata
sanitizer, which redacts secrets and long base64-looking runs; each
segment is capped (user 300, assistant 600/400) so the composed
chat_content stays within the 1000-char budget. The kap-server route,
klient contract, and node-sdk plumb the option through. Excerpt extraction
is covered against the real context memory (loop-event folding), and the
REST surface gains a digest composition case.

* feat(agent-core-v2): gate session title generation behind an experimental flag

Registers the  flag (off by default; env
KIMI_CODE_EXPERIMENTAL_SESSION_TITLE, the master flag, or the
[experimental] config section) and makes generateTitle report
unavailable while it is off, so every entry point — the kap-server
route, klient, node-sdk, and through them the clients' auto trigger and
rename-field action — is inert unless the user opts in.

* refactor(agent-core-v2): rename the title flag to auto_session_title

Snake-case id matching search_worker / persistence_minidb_readmodel; env
KIMI_CODE_EXPERIMENTAL_AUTO_SESSION_TITLE.

* fix(agent-core-v2): land the merge resolution leftovers

The main-merge commit captured pre-fix snapshots of four files; the
actual resolutions only lived in my working tree: the LifecycleScope
import move to #/app/scopes, the titleKind port of
applyPromptMetadataUpdate, the Promise<void> pinning of the metadata
update queue, and the reloadSession runSessionAccess closure.

* test(node-sdk): enable the auto_session_title flag in the title suites

Generation now reports unavailable with the flag off, so the two
title-generation harnesses opt in through the written config's
[experimental] section.

* chore: changeset for the experimental web session titles

* chore: scope the title changeset to the SDK package

* chore: cover the internal packages in the title changesets

---------

Signed-off-by: 7Sageer <sag77r@hotmail.com>
Co-authored-by: liruifengv <liruifeng1024@gmail.com>
2026-08-13 17:59:57 +08:00
7Sageer
5912d4c7d1
fix: stop repeated file-watcher errors on Windows drive-root and UNC workspaces (#2876) 2026-08-13 14:35:23 +08:00
liruifengv
f8a88c1bd9
docs(changelog): sync 0.36.0 from apps/kimi-code/CHANGELOG.md (#2880)
Also translate the Chinese pool-example descriptions in
docs/en/configuration/config-files.md into English.
2026-08-13 14:30:40 +08:00
github-actions[bot]
b6144f94ea
ci: release packages (#2846)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-08-13 13:47:30 +08:00
Haozhe
314b39489e
refactor(agent-core-v2): extract swarm into a scope-organized feature (#2874)
* refactor(agent-core-v2): extract swarm into a scope-organized feature

- move src/agent/swarm, src/session/swarm, and src/agent/tools/agent-swarm
  into src/features/swarm/{agent,session,tools/agent-swarm}; swarmOps.ts
  stays a static import=register wire channel at the feature root
- add SwarmFeature carrying the three runtime registrations
  (IAgentSwarmService, ISessionSwarmService, IAgentSwarmTool) with
  ScopeActivation.OnScopeCreated preserved
- switch src/index.ts to precise leaf exports and update import sites,
  including the kap-server and kimi-inspect deep-path imports
- move tests to test/features/swarm and re-assert service overrides in
  the test harness so stubs keep winning over feature contributions

* fix(agent-core-v2): keep feature-contributed tools in Agent tool descriptions

SubagentTool.knownToolReferences() now reads the full AgentToolContribution
collection (static registrations and feature contributions alike) instead
of the static contribution table. A caller profile that does not activate
a feature-contributed tool (e.g. AgentSwarm) no longer drops it from the
per-profile tool listings the description advertises for spawned profiles
when a workspace/session restriction forces explicit enumeration.

Add a regression test with a caller profile lacking AgentSwarm under a
global tool restriction.

* refactor(kap-server): lift session profile updates to the route edge

- add sessionProfile.ts/sessionAgentConfig.ts route helpers that resume
  the session and dispatch title/metadata and the agent_config patch to
  the native v2 services directly
- drop updateProfile from ISessionLegacyService, leaving only the
  status rollup and the goal read in the legacy adapter
- wire shape and client-visible behavior unchanged
2026-08-13 13:35:49 +08:00
7Sageer
6e31722df1
chore: drop deprecations for unreleased [subagent] pool keys (#2877)
The [subagent] default_model / models deprecations added in #2700 guard a
migration path that has no users: the pool keys only existed in #2700's own
intermediate commits and never shipped in any release, so no config written
against a released version can contain them. Remove the two deprecation
entries (the mechanism stays — the released loop_control renames still use
it), the migration notes in the en/zh config docs, the agent-core-dev skill
note, and the obsolete test; regenerate the config manifest.

No changeset: #2700 is still unreleased, so no published version ever
emitted these warnings — the removal is invisible to users.
2026-08-13 12:57:22 +08:00
7Sageer
c9bfe8b2c8
feat: replace the secondary-model experiment with a declarative subagent model pool (#2700)
* feat: replace secondary-model experiment with [subagent.models] pool

Add a declarative subagent model pool to agent-core-v2: [subagent.models]
maps [models] entry ids to selection hints rendered in the Agent/AgentSwarm
tool descriptions, and [subagent].default_model picks the spawn model when
the caller passes none. The tools' model parameter becomes a free-form
alias string (stripped when no pool is configured), description rendering
is caller-aware (primary (alias) [main model]), and a session-start
validation service fails fast with CONFIG_INVALID on a missing/invalid
default_model or an unresolvable pool alias.

Remove the secondary-model experiment from the v2 engine, node-sdk,
kap-server, and the TUI (the /secondary_model command), and drop the
agent-profile modelPreference / model_preference frontmatter field on v2.
The legacy v1 engine keeps the experiment unchanged; v2 ignores leftover
[secondary_model] config silently.

* fix(agent-core-v2): harden subagent model-pool validation and error/picker mapping

Deep-review follow-ups to the [subagent.models] pool:

- validate the pool before session materialization (after config.ready)
  and before the fork file copy, so a broken pool no longer leaves
  orphaned session dirs or leaked MCP overlay connections; the
  Session-scope validation service stays as a backstop
- reject the reserved "primary" pool alias at startup, and again
  defensively in resolveSubagentBinding so a pool broken by a runtime
  config edit fails loudly at spawn instead of binding the wrong model
- keep the [default] marker when the caller's own model is the pool
  default (primary (alias) [main model] [default])
- recompile the cached tool-args validator when a tool advertises a new
  schema object (mid-session pool edits no longer hit a stale validator)
- map config.invalid to VALIDATION_FAILED in kap-server's session routes,
  the debug transport mapper, and the catch-all error handler
- hide the v1-synthesized __secondary__ entry from the /model and
  /provider pickers again
- fold per-export doc blocks into file headers per package comment
  conventions; add pre-flight/reserved-key/validator/mapping tests and
  document that create/resume/fork all fail on a broken pool

* feat: re-add /secondary_model and accept a lone subagent default_model

- v2 engine: a pool-less [subagent] default_model forms an implicit
  single-entry pool — validated at session create/resume/fork like an
  explicit pool, and advertised through the Agent/AgentSwarm model
  parameter.
- Tool descriptions: the caller's own alias is a normal pool entry
  marked [main model]; the primary line stays distinct because only it
  inherits the caller's thinking level.
- TUI: /secondary_model returns, persisting [subagent] default_model
  (merging into an existing pool with an empty description); the picker
  hides the no-op Thinking footer and rejects the reserved primary
  alias.
- kap-server: /api/v1/config accepts and echoes subagent; the
  snake-to-camel patch conversion preserves user-defined map keys under
  providers/models/experimental/raw without leaking preserve mode into
  a colliding alias's own fields.
- v1 config schema learns subagent.defaultModel/models so the shared
  config.toml round-trips; the v1 engine still ignores them at runtime.
- Docs (en/zh) and changesets updated.

* docs: use public model identifiers in the subagent model pool examples

* refactor: rename /secondary_model to /secondary-model

* test: cover the /secondary-model command name resolution

* Revert "test: cover the /secondary-model command name resolution"

This reverts commit 98a4a6d999.

* feat(agent-core-v2): move the subagent model pool to [secondary_model]

The pool keys (default_model, [secondary_model.models]) now live in their
own [secondary_model] config section instead of [subagent], which keeps
only timeout_ms; legacy [subagent] pool keys are ignored with a
deprecation warning. The SDK config contract carries the pool on the
secondaryModel field, so the TUI /secondary-model command (now also
aliased /subagent-model) and the kap-server /config wire read and write
it directly with no translation layer.

* docs: correct default engine guidance

* feat(agent-core-v2): pin subagents to default_model with [secondary_model] force

force = true removes the main agent's per-spawn model choice: the Agent
and AgentSwarm tools stop advertising the model parameter and every spawn
binds default_model; an explicit choice, "primary" included, is rejected.
The setting requires default_model, rejects a [secondary_model.models]
table, and is validated loudly at session create/resume/fork (lifecycle
preflight plus the Session-scope backstop). The v1 engine declares the
key for write round-trips and excludes it from the recipe patch.

Also documents pool entries as per-alias thinking-level variants via
default_effort overrides.

* docs: use real managed model aliases in the secondary_model examples

The pool examples invented aliases (kimi-hs, fable, codex) and referenced
non-existent model IDs (model = "codex"); they now reference only the
managed aliases provisioned by /login (kimi-code/k3,
kimi-code/kimi-for-coding, kimi-code/kimi-for-coding-highspeed), with the
effort variant derived as kimi-for-coding-highspeed-deep. Also replaces the
versioned kimi-k2.5 alias with kimi-for-coding per the docs model-ID rule.

* chore: simplify the subagent model pool changeset

* feat(agent-core-v2): honor the legacy [secondary_model] model key as a fallback default

* refactor(node-sdk): export the reserved model-alias constants from the SDK

Restore the SECONDARY_DERIVED_MODEL_ALIAS re-export and add
PRIMARY_SUBAGENT_MODEL_CHOICE so the TUI imports both from
@moonshot-ai/kimi-code-sdk instead of vendoring local copies.

* feat(agent-core-v2): keep the subagent model pool behind the secondary-model experiment

Restore the secondary-model flag gating so this change only adds the pool:
with the experiment off the [secondary_model] pool keys stay inert — the
Agent/AgentSwarm tools strip the model parameter, spawns inherit the
caller's model, and startup pool validation is skipped. The /secondary-model
slash command is gated behind the experiment again, and the docs and
changeset describe the flag.

* fix(node-sdk): cascade provider removal into the subagent model pool

Deleting a provider left [secondary_model] entries pointing at the removed
model aliases; with the secondary-model experiment on, every subsequent
session create/resume/fork then failed pool validation. planProviderRemoval
now filters dangling pool entries, and drops the whole section when its
effective default (defaultModel, or the legacy recipe's model fallback)
dangles — folded into the same atomic multi-section replace.

* fix(agent-core-v2): close two subagent model pool validation gaps

Spawn-time resolveSubagentBinding now rejects force combined with a
[secondary_model.models] table, matching the startup pre-flight — a live
session could otherwise reach that invalid state through a deep-merged
config patch and only fail on the next create/resume. The session
lifecycle pre-flight also awaits the kosong model/provider registries'
ready alongside config.ready, so a cold bootstrap no longer fails a valid
pool with CONFIG_INVALID against an empty registry.

* test(agent-core-v2): stub the model/provider registries in handler-chain tests

SessionLifecycleService now awaits IModelService/IProviderService readiness
in its pool pre-flight, so the tests that assemble the real service through
a hand-built container must register the two tokens.

* fix(kap-server): cascade REST provider deletion into the subagent model pool

The DELETE /providers route rewrote only the providers and models
sections, so a pool referencing one of the deleted provider's aliases was
left dangling and the engine's create/resume/fork pool validation failed
every subsequent session until the user repaired the TOML by hand. Filter
dangling pool entries and drop the section when its effective default
dangles, mirroring the SDK's planProviderRemoval semantics.

* fix: keep the subagent pool consistent on provider replace and preserve legacy recipe fields

PUT /providers rebuilds the provider's alias set and can drop or rename
aliases referenced by [secondary_model]; the pool now cascades there too —
renamed aliases are repointed (mirroring the global default-pointer
migration), dropped aliases are filtered, and the section is cleared when
its effective default dangles.

The v2 [secondary_model] schema also declares the legacy recipe patch
fields (default_effort, max_output_size, ...) so validation no longer
strips them: pool resolution keeps ignoring them, but config reads/writes
now round-trip losslessly instead of silently deleting them from
config.toml on any pool write.

* fix(agent-core-v2): cascade the subagent pool on catalog refresh writes

A background provider/model refresh rewrites the [models] table without
touching [secondary_model], so a dropped alias left the pool dangling and
every subsequent session create/resume/fork failed validation until the
user repaired the TOML by hand — the same gap the SDK and REST write
paths already had, but triggered unattended.

The cascade helper now lives in agent-core-v2 next to the section it
protects (cascadeSubagentModelPool): the discovery service folds the pool
into the same atomic replaceSections transition, and kap-server's
provider write routes reuse the shared helper instead of a local copy.

* fix: cover the last two model-table write paths for the subagent pool

ModelsDevImportService's catalog and custom-registry imports rebuild the
[models] table without the pool cascade, so an import that drops a pooled
alias left a dangling pool behind; both final write passes now fold the
pool through cascadeSubagentModelPool (the drop passes deliberately skip
it). The StubConfigService test double now treats a null section value as
a delete, matching the real ConfigService.

The TUI's provider overwrite flow removes an existing provider before
re-adding it, which ran the removal cascade against a model table where
every alias of that provider was absent and silently dropped the pool;
the flow now snapshots secondaryModel up front and restores the entries
that survive the re-add, via the cascade helper re-exported from the SDK.
2026-08-13 12:29:03 +08:00
Liu Zhongnuo
504e6292ed
feat(mcp): inspect effective authorization state in v1 (#2856)
* feat(mcp): inspect effective authorization state

* test(agent-core-v2): register MCP auth coordinator fixture

* fix(mcp): validate runtime names against full catalog

* fix(mcp): reconnect after pending auth updates

* docs(mcp): describe auth coordinator collaborator

* fix(mcp): ignore disabled runtime name collisions

* fix(mcp): serialize OAuth token refresh

* test(mcp): await OAuth credential writes

* fix(mcp): queue trailing credential reconnect

* fix(oauth): preserve access-only refresh winners

* fix(mcp): preserve legacy offline auth state

* fix(mcp): redact inspection credentials

* refactor(mcp): keep app inspection on v1

* fix(mcp): guard legacy auth status mutations

* fix(mcp): avoid deterministic legacy auth probes

* fix(mcp): cover initialization credential updates

---------

Co-authored-by: 刘仲诺 <liuzhongnuo@dev.msh.team>
2026-08-13 12:00:04 +08:00
Haozhe
23e68eee8b
refactor(agent-core-v2): remove the agent RPC aggregation layer (#2871)
* refactor(agent-core-v2): remove the agent RPC aggregation layer

- delete src/agent/rpc/ (AgentRPCService, IAgentRPCService, core-api,
  prompt-metadata, types) and sink each method's orchestration into its
  owning domain service
- prompt: new submit/submitSteer composing disabledTools gating,
  MAIN-only session metadata, and engine-side {turn_id} settlement
- skill: activate now returns PromptLaunchResult and writes session
  metadata internally (MAIN-only, unified across prompt/steer/skill/
  pluginCommand); node-sdk and kap-server drop their edge-side writes
- pluginCommand: new agent-scope domain owning command activation and
  the plugin_command.activated domain event
- permissionMode/loop/fullCompaction: new setModeAndBroadcast /
  cancelFromUser / cancel; setMode and loop.cancel stay pure for
  internal callers
- klient: agentRpcContract split into per-domain contracts; facade
  re-routes to domain channels with its public API unchanged
- node-sdk, kap-server, kimi-inspect and the v2 test harness now call
  domain services directly; ctx.rpc keeps its name as a composed
  adapter
- externally visible: the agentRPCService debug channel is gone and
  session metadata writes are now MAIN-agent-only (see changeset)

* refactor(agent-core-v2): move disabledTools gating out of the prompt domain

Prompt should not own session tool policy: submit no longer accepts or
applies disabledTools. The klient facade keeps its prompt({ disabledTools })
API and composes it edge-side — applying agentToolPolicyService
setSessionDisabledTools before calling agentPromptService.submit, the same
way kap-server's prompt route already does. Over klient, a profile-less
engine now surfaces the raw profile error instead of request.invalid.

Also restores the RPC-removal changeset, which did not make it into the
previous commit.

* chore(agent-core-v2): drop the RPC-removal changeset

* refactor(klient): drop disabledTools from the prompt entry entirely

The prompt path no longer carries session tool gating on any surface:
the klient facade prompt() loses the disabledTools field and calls
agentPromptService.submit directly, and the node-sdk
SessionPromptRpcInput stops accepting or forwarding it (v1 always
ignored the field). Session tool gating remains available through
IAgentToolPolicyService.setSessionDisabledTools, composed at the edge
the way kap-server's prompt route does; the klient toolPolicy contract
added for facade-side composition is removed as unused.
2026-08-13 10:57:01 +08:00
qer
719da94648
chore: rebuild web dist against kimi-code 0.35.0 (#2853)
Some checks are pending
CI / lint (push) Waiting to run
CI / build (push) Waiting to run
CI / test (1) (push) Waiting to run
CI / test (2) (push) Waiting to run
CI / test (3) (push) Waiting to run
CI / test (4) (push) Waiting to run
CI / test (5) (push) Waiting to run
CI / test-pi-tui (push) Waiting to run
CI / test-windows (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 / Native release artifact (push) Blocked by required conditions
Release / Release (push) Waiting to run
Release / Deploy docs (push) Blocked by required conditions
Release / Publish native release assets (push) Blocked by required conditions
code-app: c5440b863d02f99457f48520c16c609eae73f12b
2026-08-13 01:27:30 +08:00
7Sageer
fe3cdae5f8
fix(agent-core-v2): drop interrupted thinking-only assistant messages at step settle (#2819)
* fix(agent-core-v2): drop interrupted thinking-only assistant messages at settle

A turn interrupted while the model is still streaming thinking leaves the
open assistant holding only an unsigned thinking fragment. The fold used
to seal it into history because a non-empty thinking block is not vacuous;
on OpenAI-compatible providers the serialized message then carries neither
content nor tool_calls, and strict gateways reject every later request
with a 400 (#1404). Treat unsigned-thinking-only content as unsendable at
settle so the fold drops the message instead — replaying the records of
an already bricked session repairs it.

* fix(agent-core-v2): preserve reasoning-only assistant history

---------

Signed-off-by: 7Sageer <sag77r@hotmail.com>
2026-08-12 22:17:02 +08:00
liruifengv
30f56a2d2d
fix(agent-core-v2): disable SDK-internal retries that blocked cancellation (#2855)
* fix(agent-core-v2): disable SDK-internal retries that blocked cancellation

The OpenAI and Anthropic SDK clients default to maxRetries=2 with a
backoff sleep that never observes the request AbortSignal, so Ctrl+C
during a 429/5xx/connection-error retry only took effect after the
sleep elapsed, and the hidden attempts were invisible to the engine
(no turn.step.retrying) while double-counting its retry budget.

Build those clients with maxRetries: 0 so retryable failures surface
to the engine's step-retry layer immediately (observable countdown,
abortable sleep, single retry budget). The Google GenAI main request
path only retries when httpOptions.retryOptions is explicitly set, so
there is nothing to disable; instead its error converter now recovers
the server-directed delay from the wire body's google.rpc.RetryInfo
detail, since the SDK's ApiError drops the Retry-After header.

* chore: simplify the retry-cancellation changeset entry

* fix(agent-core-v2): recover GenAI retry delay from prefixed mid-stream error chunks

Mid-stream error chunks throw ApiError with the message wrapped as
"got status: <STATUS>. {json}", so a strict JSON.parse of the whole
message missed the google.rpc.RetryInfo detail. Locate the JSON object
start before parsing; the non-stream path (pure JSON body) is
unaffected.
2026-08-12 20:40:20 +08:00
liruifengv
ec84a6f9a3
feat(kimi-code): re-baseline pi-tui on upstream v0.84.1 and add fullscreen tui_mode (#2830)
* feat(kimi-code): re-baseline pi-tui on upstream v0.84.1 and add fullscreen tui_mode

Re-baseline the vendored pi-tui fork on upstream @earendil-works/pi-tui
v0.84.1, keeping all local patches: narrow-terminal hardening,
processed-line render caching (re-implemented into TuiMainScreen), editor
history hooks, the paste-burst fallback, and multi-root @ completion.

Upstream highlights absorbed: the renderer splits into TuiMainScreen and
TuiAltScreen behind a TUI interface, the Markdown component gains opt-out
LaTeX rendering (disabled on the kimi-code side), paste-registry repair
on delete/undo, Windows input-latency and Shift+Enter fixes, and Kitty
image layout fixes. Editor.setText gains a preservePasteRegistry option
so paste-marker expansion survives wholesale text replacement.

New tui_mode = "fullscreen" preference mounts TuiAltScreen: the
transcript lives in a primary ScrollView with follow-end, the chrome
docks at the bottom, mouse selection and scrollbar come from the
renderer, and full-screen viewers (tasks browser, output viewer, approval
preview) swap the layout root via screen-takeover. Viewport navigation
keys fall through to the focused component when the primary scroll view
cannot scroll.

* feat(pi-tui): merge upstream main through 40a3d85 (post-0.84.1)

Bring in upstream's merged-but-unreleased changes on top of the v0.84.1
re-baseline:

- Fullscreen transcript search (ctrl+shift+f, next/previous navigation)
- Alternate-screen render-churn reduction (9-18x less per-frame
  allocation by painting full-width rows as direct line references)
- Unbound single-line scroll actions (tui.altScreen.lineUp/lineDown),
  wired into the fork's canScroll gating like the other viewport keys
- SSH-aware escape-timeout default and PI_TUI_ESC_TIMEOUT override
- Search snapping and SGR-mouse fragmentation fixes; LaTeX newline
  argument fix

Conflicts resolved by union: upstream's search/line scroll bindings stay
ungated, fork's primaryScrollable guard applies to all scroll actions.

* fix(kimi-code): keep fullscreen dock from crushing the editor box

The fullscreen layout gave the transcript ScrollView its intrinsic
content height as basis and let the dock participate in shrink
distribution with no minSize. Once the transcript exceeded the screen,
the VStack shrink pass crushed the dock to a couple of rows, and the
editor (3 rows: top border / input / bottom border) lost its bottom
border row to clipping.

Adopt pi's sizing contract: the ScrollView starts from basis 0 and
grows, the dock keeps its intrinsic height, the editor never shrinks
below 3 rows, and the footer below 1. Adds a VirtualTerminal-level
regression test that replays a full streaming cycle in fullscreen.

* docs(kimi-code): document the tui_mode preference in tui.toml

* fix(pi-tui): let terminal focus reports fan out in fullscreen

TuiAltScreen's viewport input listener consumed FOCUS_IN/FOCUS_OUT
reports. Since the renderer installs that listener at construction —
before any app-level listeners — terminal focus tracking and
clipboard-image hints never saw focus transitions in fullscreen mode
(notification_condition = "unfocused" went blind, refocus clipboard
hints stopped). Keep the selection cleanup but stop consuming, matching
the main-screen fan-out. Addresses Codex review on PR #2830.

* fix(kimi-code): wire openUrl and right-click paste in fullscreen

Mouse capture in the alternate screen intercepts the terminal's native
link activation, leaving OSC 8 hyperlinks (like the footer's PR link)
unclickable in fullscreen. Route renderer link clicks to the app's
openUrl, and on Windows feed right-clicks to the focused component as a
bracketed paste read from the clipboard.

* feat(kimi-code): fullscreen prompt navigation, exit replay, progress resync

- Mark user/assistant transcript messages with OSC 133 zones (start /
  end / final) so the fullscreen renderer's Ctrl-Shift-Up/Down prompt
  jumps work; GutterContainer keeps the markers at byte 0 when prefixing
  its gutter, and message render caches store already-marked lines.
- On exit from fullscreen, preserve the frame and replay the transcript
  through a fresh main-screen renderer so native scrollback gets the
  regular inline layout (pi's "transcript" exit form).
- Re-sync the OSC 9;4 progress indicator after a stop/start cycle:
  terminal.stop() clears it, and the cached progressActive flag used to
  suppress the re-send when returning from the external editor mid-turn.

* feat(kimi-code): enable Markdown LaTeX rendering with a render_latex opt-out

Align with the upstream pi-tui default: LaTeX math in Markdown messages
renders as Unicode text. The explicit renderLatex:false we set during
the re-baseline becomes a shared Markdown options helper fed by a new
tui.toml preference (render_latex, default true), wired at startup and
refreshed on /reload.

* refactor(kimi-code): gate fullscreen behind KIMI_CODE_TUI_FULL_SCREEN

Drop the public tui_mode preference from tui.toml before release; the
fullscreen UI is experimental, so enable it with the
KIMI_CODE_TUI_FULL_SCREEN=1 env var instead. Docs move from the
config-file reference to the env-vars page.

* chore(changesets): clarify fullscreen mode and LaTeX formula entries

* chore(changesets): trim fullscreen mode entry

* chore(changesets): trim LaTeX formula entry

* chore(changesets): drop redundant kimi-code entries

* test(kimi-code): add stepRetry to fullscreen layout fixture after main merge

* fix(kimi-code): apply render_latex before theme-driven Markdown rebuilds

Codex review on PR #2830: applyReloadedTuiConfig set the shared LaTeX
toggle after applyTheme(), but theme application invalidates transcript
components and their rebuilt Markdown children copy the options at
construction — so a /reload that only flipped render_latex kept the old
value until some later invalidation. Move the setter before applyTheme
and pin the ordering with a test.

* fix(kimi-code): carry renderLatex through TUI config saves

Codex review on PR #2830: currentTuiConfig omitted renderLatex, so
saving an unrelated preference (theme/editor/upgrade/cache-hint)
serialized render_latex as the default true and silently reset a user's
opt-out. Carry the appState value through the shared save payload.

* feat(kimi-code): report tui_mode in lifecycle telemetry

Tag startup_perf and exit events with the active renderer mode
(regular/fullscreen) so fullscreen adoption is measurable while it is
gated behind KIMI_CODE_TUI_FULL_SCREEN.
2026-08-12 18:23:41 +08:00
liruifengv
35d9a36a69
chore(changelog): thank security reporters in 0.35.0 entry (#2845)
Some checks are pending
CI / lint (push) Waiting to run
CI / test-windows (push) Waiting to run
CI / build (push) Waiting to run
CI / test (1) (push) Waiting to run
CI / test (2) (push) Waiting to run
CI / test (3) (push) Waiting to run
CI / test (4) (push) Waiting to run
CI / test (5) (push) Waiting to run
CI / test-pi-tui (push) Waiting to run
CI / typecheck (push) Waiting to run
Nix Build / Check flake.nix workspace sync (push) Waiting to run
Nix Build / nix build .#kimi-code (push) Blocked by required conditions
Release / Release (push) Waiting to run
Release / Deploy docs (push) Blocked by required conditions
Release / Native release artifact (push) Blocked by required conditions
Release / Publish native release assets (push) Blocked by required conditions
2026-08-12 16:27:19 +08:00
Haozhe
3b0936d8e0
fix(agent-core): only discover the root SKILL.md for plugin skills fallback (#2847)
When a plugin manifest omits `skills` and the plugin root contains a
SKILL.md, the fallback treated the whole plugin root as a generic skill
scan directory, so sibling Markdown files such as CHANGELOG.md were
misidentified as skills and inflated the plugin skill count.

Mark the fallback root as root-skill-only so discovery parses only the
root SKILL.md; explicit `skills` entries (including "./") keep the
directory scan semantics. Applied to both agent-core and agent-core-v2.
2026-08-12 14:24:48 +08:00
7Sageer
f40bf04998
refactor(agent-core-v2): unify model-facing reminder scheduling (#2623)
* refactor(agent-core-v2): unify model-facing reminder scheduling

Route every model-facing reminder through the contextInjector boundary
scheduler. Past-tense events go through a persisted once-reminder queue
(reminderQueue) that delivers exactly once at turn, step, compaction,
and restore boundaries; present-tense state renders through
context-injection providers reconciled against live history.

- interruption, goal (cancel/budget/fork-cleared), image-compression
  captions, btw, and init reminders enqueue into reminderQueue instead
  of writing the context directly; the interruptionReminder wire model
  is removed and its recorded type is retired silently on replay
- swarm mode announcements render through a provider seeded from the
  replayed history on restore, replacing live side effects and the
  ContextModel pop reducer on swarm_mode.exit
- loadable-tools announcements become an isNewTurn-gated provider,
  dropping the compaction boundary flag
- plugin session-start guidance re-renders as a supersedes reminder at
  the next boundary via a dirty flag instead of appending immediately
- legacy system_trigger origins of migrated reminders still fold on
  replay

* fix(agent-core-v2): make system reminders undo-aware

* test(agent-core-v2): migrate plugin session-start harness

* fix(agent-core-v2): preserve reminder boundary ordering

* refactor(agent-core-v2): narrow reminder and swarm helper exposure

- drop the swarmInjection re-export from the package index; SwarmInjection
  stays a domain-internal collaborator like permissionMode/plan injections
- move INTERRUPTION_REMINDER text back to a private constant in the service;
  only the variant stays in the Ops module
- make reminderQueue.enqueue return void; no caller consumed the entry id

* chore(agent-core-v2): keep comments in module headers

* refactor(agent-core-v2): track reminder state via injection disclosure

- derive swarm active/inactive state from ctx.lastDisclosure instead of
  byte-matching rendered markdown, with variant-only fallback for legacy
  swarm_mode/swarm_mode_exit journal entries
- record once_reminder disclosure (entry id) on queue-appended messages
  and dedupe the crash window by the contiguous tail id set, covering
  multi-entry drains
- move reminderQueue draining behind a sync onWillInject event so the
  injector no longer depends on the queue domain
- centralize the system-reminder wrap format behind wrapSystemReminder /
  systemReminderContent and use injector-provided positions in the plugin
  session-start provider
- spell out the step-boundary fallback and sync-only contract of
  registerAtTurnStart via shouldRunAtBoundary

* fix(agent-core-v2): isolate failing turn-start providers and warn once per missing sessionStart skill

* refactor(agent-core-v2): compute injection positions on read

Drop the per-provider positions cache from the context injector: the
registration scan, the context.spliced index arithmetic, and the
post-restore resync all existed only to mirror what the history already
records. Each provider call now derives its injected positions by
scanning context memory for its surviving injection messages, so silent
history edits (such as vacuous-step folds) can no longer desync a
cached index.

* refactor(agent-core-v2): formalize injector once-channels and raw message results

* refactor(agent-core-v2): declare dynamic tool schemas at injection boundaries

Move the dynamic-tool schema declaration out of toolSelect.load(): the
loaded names are recorded as pending and drained by a dedicated
toolSelectSchemas provider through the contextInjector boundary
scheduler, so the declaration message lands at a quiescent boundary
instead of mid-step inside a streaming tool exchange. The folded
history remains the loaded-tool ledger, so undo, compaction, and
resume still self-heal by re-folding.

* refactor(agent-core-v2): deliver AGENTS.md reminders through the reminder queue

The tool hook now only observes and enqueues a once-per-agent reminder
through the reminderQueue once-channel instead of prepending text to
the tool result: results stay verbatim for the truncation pipeline and
the reminder can never be truncated away with an oversized output. The
reminderQueue is resolved lazily through the instantiation service at
enqueue time, breaking the contextInjector -> loop -> llmRequester ->
profile -> agentsMdReminder constructor cycle.

* refactor(agent-core-v2): make injection disclosures opaque and domain-owned

contextMemory no longer declares the ContextInjectionDisclosure union:
InjectionOrigin.disclosure becomes an opaque unknown, and providers
bind their own payload type through register<D>, so lastDisclosure
arrives at the provider already typed by its own variant. The date,
swarm_mode, and once_reminder payload shapes move into the dateChange,
swarm, and reminderQueue domains respectively; reminderQueue keeps a
runtime guard for its cross-message tail scan, the only place that
reads disclosures it did not write. Persisted origin shapes are
byte-identical, so existing journals replay unchanged.

* fix(agent-core-v2): isolate failing step context providers

A step or compaction boundary provider that threw or rejected made the
injector's inject() promise reject, which propagated through the
onWillBeginStep hook chain and failed the whole turn, and starved every
provider registered after it. Log and skip the bad provider instead,
matching the turn-start path's existing isolation.

* refactor(agent-core-v2): derive injector isNewTurn per injection boundary

Replace the shared read-and-clear isNewTurn flag (set by turn.started and
injectAfterCompaction, consumed by the first inject()) with values each
trigger supplies from an authoritative source: the loop marks a turn's
first step via BeforeStepContext.firstStepOfTurn (standalone runs never
count), and the compaction follow-up passes true explicitly, so
interleaved triggers can no longer consume or steal the marker.

A compaction follow-up that lands inside a step hook chain (the
auto-compaction path) doubles as that step's new-turn delivery: the
enclosing step then injects with isNewTurn false, so the upcoming request
receives one new-turn injection, not two.

* refactor(agent-core-v2): unify disclosure placement and injector param naming

* fix(agent-core-v2): keep pending tool schemas across compaction splices

A load announced by select_tools sits in pendingLoaded until the next
injection boundary declares it. A compaction fold in that window
publishes a replacement splice, and the splice-time reconciliation
dropped the pending entries before the post-compaction inject could
declare them — the model was told "Loaded: X" yet X never became
available. Drop pending entries only on removal splices (undo/clear,
which carry no replacement messages); compaction's replacement splice
keeps them so the declaration lands at the post-compaction boundary.

* fix(agent-core-v2): consume the plugin session-start refresh after a successful render

reconcileSessionStartReminder cleared the refresh-pending flag before
awaiting the render, so a throwing render (skipped by the injector's
provider isolation) lost the forced refresh until the next catalog
change. Consume the flag only after the render resolves, and move the
warn-once rationale into the module header per the comment convention.

* refactor(agent-core-v2): remove the generic reminder queue

* chore(agent-core-v2): drop the stale reminder-queue mention in systemReminder

* test(node-sdk): align side-question fork parity with event-point reminders

* chore(agent-core-v2): address reminder review standards

* docs(agent-core-v2): condense the model-facing reminders section

* refactor(agent-core-v2): write all system reminders through wrapSystemReminder

* fix(agent-core-v2): preserve reminder lifecycle invariants

* refactor(agent-core-v2): reconcile context injections at the step head

Unify the injector's delivery timings into one point on the
onWillBeginStep chain, before the step's request is built:

- providers run before every request instead of after every step, so
  reminders are visible from the first response of a turn
- a compaction splice re-arms the new-turn flag via context.spliced;
  when compaction runs inside the hook chain (full-compaction's
  beforeStep), a follow-up inject at the chain tail keeps the first
  post-compaction request covered
- registerAtTurnStart and injectAfterCompaction are removed;
  reconcileWhenIdle stays as the v1-parity surface for SDK-driven
  triggers (swarm toggle, plugin reload)

* refactor(agent-core-v2): clarify the injector's step-hook handler

Name the handler reconcileAroundStep, rename the rearm flag to
compactionRearmPending with a single takeCompactionRearm() consumer,
and extract isCompactionSplice. Consuming the flag into a local before
computing isNewTurn also avoids hiding the side effect inside a ||
short-circuit.
2026-08-12 13:52:08 +08:00
liruifengv
43c68f58f5
feat(agent-core-v2): keep session updatedAt stable across meta management writes (#2815)
* feat(agent-core-v2): keep session updatedAt stable across meta management writes

Rename, archive/restore, and fork no longer bump a session's updatedAt,
so recency-sorted session lists stop reshuffling on management actions:

- setTitle/setArchived pass touchUpdatedAt: false; an explicit
  patch.updatedAt always wins (fork inherits the source's recency, so a
  fork lands next to the source instead of floating to the top)
- new SessionMeta.archivedAt records the archive moment (cleared on
  restore) and is surfaced through the session index, the v1/v2 session
  routes (archived_at), and the klient contract, so the archived list
  keeps an accurate archive time without relying on the updatedAt bump

* fix(agent-core-v2): normalize a legacy ISO-string updatedAt when forking a cold session

A cold legacy/v1 state.json read from disk can still carry an ISO-string
updatedAt; passing it through as the fork's explicit patch.updatedAt
would persist a string into the v2 metadata. Normalize with toEpochMs
(falling back to now when absent/unparseable).

* fix(agent-core-v2): write fork metadata after agent recreation

Registering each copied agent during fork is an ordinary metadata write
that bumps updatedAt, which overwrote the inherited source recency and
still floated normal forks (sessions with agents) to the top. Move the
fork's metadata update after the agent recreation loop so the inherited
updatedAt is the final write.

* fix(agent-core-v2): preserve persisted recency when restoring a cold session

Resume creates the main agent for a cold session that has no persisted
agents.main entry (e.g. an empty session), and that registration bumps
updatedAt — so unarchiving an empty session still floated it to the
top. Capture the index summary's updatedAt before resume and re-apply
it in the restore write (archived:false, archivedAt cleared, explicit
updatedAt wins over the bump).

* fix(agent-core-v2): make agent registration non-touching for recency

Registering an agent is a structural write, not content activity — but
it went through an ordinary metadata update that bumped updatedAt. That
reordered recency-sorted listings whenever materialization created an
agent: resume of a cold session without a persisted agents.main (so
archive-via-resume and restore of empty sessions still floated), and
runtime subagent registration mid-turn. registerAgent now passes
touchUpdatedAt: false; restore goes back to the plain unarchive write
and no longer needs the capture/reapply workaround.

* fix(agent-core-v2): duplicate cron tasks only after the fork metadata is durable

With the metadata write moved after agent recreation, cron duplication
ran before it — a rejected metadata update left cloned cron records
pointing at a fork whose directory the catch block just removed. Keep
cron duplication after the durable metadata write.

* style(agent-core-v2): fold new invariants into module headers

The package convention keeps comments in the top-of-file block only —
move the touchUpdatedAt precedence, non-touching registration, and fork
ordering notes out of statement-level positions into the respective
module headers.

* chore: scope the changeset to agent-core-v2
2026-08-12 13:40:26 +08:00
Haozhe
c212ae9715
fix(kimi-code): show MCP launch targets in the workspace trust prompt (#2843)
* fix(kimi-code): show MCP launch targets in the workspace trust prompt

Render each gated project MCP server's launch target (transport, command,
args, cwd, or url) in the workspace trust prompt without leaking env or
header secrets, stripping terminal control characters from the
workspace-supplied text, default the prompt to "Don't trust", and
resolve fd binaries to absolute paths so untrusted workspaces cannot
plant a bare-name fd executable that runs before trust confirmation.

* fix(kimi-code): resolve stty to an absolute path before the trust gate
2026-08-12 13:34:32 +08:00
Haozhe
26a37f30a3
fix(kap-server): restrict debug RPC fallback to feature-contributed services (#2808)
The decorator-registry fallback resolved every decorator name, including
kernel tokens like instantiationService — a call to
instantiationService/dispose would tear down the root container. Record
Feature.contributeService tokens in a contributed-service table and fall
back to that table only, so runtime-contributed services stay callable
while unregistered kernel tokens remain unreachable.
2026-08-12 12:35:01 +08:00
Haozhe
dc8db90cdd
docs(server): add local server guide and API reference (#2839)
* docs(server): add local server guide and API reference

* docs(server): qualify binary endpoint HTTP semantics
2026-08-12 12:29:09 +08:00
qer
26ddc1d0fb
feat(plugins): add Modern Web Guidance to marketplace (#2842)
* feat(plugins): add Modern Web Guidance to marketplace

* chore: drop changeset

* chore: backfill 0.35.0 changelog entry
2026-08-12 12:28:36 +08:00
liruifengv
3fc841ff23
docs(changelog): sync 0.35.0 from apps/kimi-code/CHANGELOG.md (#2841)
* docs(changelog): sync 0.35.0 from apps/kimi-code/CHANGELOG.md

* docs(changelog): add Modern Web Guidance plugin entry to 0.35.0
2026-08-12 12:15:07 +08:00
github-actions[bot]
f6ee44e426
ci: release packages (#2710)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-08-12 11:45:35 +08:00
Haozhe
101c4d1997
feat(agent-core-v2): remove Agent and AgentSwarm from builtin profile tool lists (#2837)
* feat(agent-core-v2): remove Agent and AgentSwarm from builtin profile tool lists

The builtin agent and coder profiles no longer expose the Agent and
AgentSwarm tools, so sessions on the v2 engine do not offer subagent
delegation by default. The tools themselves remain registered; profiles
that list them explicitly can still opt in.

* feat(agent-core): remove Agent and AgentSwarm from builtin profile tool lists

Align the v1 builtin agent/coder profiles with the v2 change: the
default profiles no longer offer subagent delegation, while the tools
stay registered for profiles that list them explicitly.

The parity projection drops v1's inactive Agent/AgentSwarm roster
entries: v1 reports registered-but-inactive builtin tools where v2 only
registers the tools a profile lists, so an inactive entry has no v2
counterpart. Active entries still compare in full.

* fix: keep Agent and AgentSwarm in the builtin agent profile

Scope the removal to the coder subagent profile on both engines: the
main agent keeps Agent/AgentSwarm so default sessions can still
delegate, while coder subagents no longer spawn nested subagents by
default. Snapshots and token counts shift only for the embedded coder
tool list; the v1 parity projection needs no change since the main
agent rosters match again.
2026-08-12 11:41:02 +08:00
qer
68ce3c7a0c
chore: sync web dist from code-app (#2840) 2026-08-12 11:38:17 +08:00
liruifengv
df8ce73e45
feat(kimi-code): show step retry progress in the activity indicator (#2825)
* feat(kimi-code): show step retry progress in the activity indicator

Wire the engine's turn.step.retrying event into the TUI: while a failed
model request is backing off for another attempt, the waiting spinner
shows 'retrying (N/M) · errorName · in Xs' with a dim detail line for
the status code and provider error message, and the loading tip is
suppressed.

The retry state clears on the step's terminal events (completed /
interrupted), turn.ended, and tool.result. It intentionally survives
turn.step.started because the v2 engine re-emits that event for every
retried attempt of the same step.

* fix(kimi-code): show the retry indicator for mid-stream failures

A retryable failure raised after thinking/assistant deltas had already
streamed left the pane in thinking/composing mode, so the retry label
and detail never rendered during the backoff. Drive the pane and the
streaming phase back to waiting when a retry begins.

* fix(kimi-code): drop the stale retry countdown once the attempt starts

The v2 engine re-emits turn.step.started when the retried attempt
begins running after the backoff sleep. Track a backoff/attempt phase
so the label keeps showing the retry attempt and error but drops the
already-elapsed 'in Xs' countdown, instead of either clearing the
state or showing stale timing through a slow attempt.

* fix(kimi-code): advance the retry phase on a timer instead of step starts

The legacy engine retries inside the same step and never re-emits
turn.step.started, so the backoff-to-attempt transition keyed on that
event never fired there and the stale countdown stayed up through the
attempt. Schedule the flip from delayMs instead, which matches when
both engines actually start the next attempt, and drop the step-start
hook.

* fix(kimi-code): cancel the retry phase timer on TUI shutdown

A pending backoff timer survived KimiTUI.stop(), keeping the event
loop alive and firing setAppState against a disposed UI when stop()
runs without an immediate process exit. Expose the timer cleanup and
invoke it from the shutdown path.

* fix(kimi-code): align the retry detail line with the spinner label

* fix(kimi-code): capitalize the retry spinner label
2026-08-12 11:35:44 +08:00
liruifengv
3c9e3b297c
feat(kimi-code): paginate the session picker list (#2826)
* feat(kimi-code): paginate the session picker list

The /sessions picker and kimi -r used to materialize the full session
list before showing anything, which gets slow with hundreds of sessions.

- node-sdk: add listSessionsPage (limit/before -> items + nextCursor);
  the v2 engine pages through the session index (draining past entries
  whose workDir is unrecoverable), the v1 engine answers one full page
- TUI: open the picker on the first page, fetch the next page when the
  cursor reaches the fetched end, and drain remaining pages in the
  background once a search query is typed so search still covers all
  sessions
- kimi -r now fetches a one-item page for the latest session

* chore: simplify session picker changeset

* fix(kimi-code): join in-flight page fetch in session search drain

A query typed while a scroll-triggered page fetch was still running
stopped the background drain at the loadingMore early return, leaving
the search covering only the pages fetched so far. fetchMoreSessions
now optionally joins the in-flight fetch and continues with the next
page; scroll triggers still drop when busy.
2026-08-12 11:21:43 +08:00
Haozhe
e5be39164b
fix(kimi-code): resolve footer git status commands through PATH (#2838)
The footer git status cache spawns git (and gh for PR lookup) on the
startup path, before the workspace trust prompt. On Windows, a bare
command name lets cmd.exe resolve a git.exe planted in the workspace
before the user confirms trust — a gap left by #2695.

Resolve git once at cache creation and gh per lookup with
resolveCommandPath(), which returns an absolute PATH hit and refuses
matches inside the workspace; when resolution fails the cache reports
no repository instead of spawning anything.
2026-08-12 10:51:57 +08:00