Commit graph

92 commits

Author SHA1 Message Date
7Sageer
f6736d7c0d
feat(agent-core-v2): add a fork parameter to the Agent tool (#3007)
* feat(agent-core-v2): add a fork parameter to the Agent tool

Spawning with fork: true starts the subagent from a one-time snapshot of
the calling agent's completed conversation history — same profile, tool
set, and model — instead of zero context. The seed trims the trailing
open tool exchange (the in-flight Agent call itself) before appending
into the child's context memory, and the first prompt carries an
inheritance notice framing the seeded history as reference material.

Fork rejects resume, a different subagent_type, or a model override as
tool errors, and skips the subagents allowlist since a self-inheritance
is not a delegation.

* fix(agent-core-v2): bind the stale-todo reminder only into the main agent

Subagents share the session todo list but no longer receive the
stale-todo nudge — the reminder injector now registers only on the main
agent, so delegated and forked agents are not prompted to maintain a
list they do not own.

* fix(agent-core-v2): inherit the caller's live binding and label fork launches correctly

Review follow-ups for the Agent tool fork mode:

- overlay the caller's live profile.data() via applyBindingSnapshot after
  the catalog re-bind, so ephemeral addActiveTool deltas, the rendered
  system prompt, and runtime model/subagents updates survive the fork;
  skip the profile prompt prefix since the caller's prefixed first
  prompt is already part of the seeded history
- resolve the fork activity label and approval-rule subject from the
  caller's own profile instead of falling back to the default subagent
  type, so an Agent(<other profile>) rule cannot approve a fork

* fix(agent-core-v2): close inherited in-flight tool calls instead of trimming them

Fork seeding now answers the source's trailing open tool calls with a
synthetic in-flight result instead of cutting the whole trailing
exchange: the seeded history stays protocol-valid, keeps the source's
final step visible as reference, and no longer confuses side-question
(btw) agents forked while the main agent is mid-turn. The close helper
is shared by the Agent tool fork and IAgentLifecycleService.fork.

Fork launches also stop requiring the caller's profile to still exist
in the session catalog: the child is created unbound and overlaid with
the caller's live binding snapshot, matching the lifecycle fork path,
and now records forkedFrom provenance.

* refactor(agent-core-v2): route Agent tool forks through agentLifecycle.fork

* feat(agent-core-v2): add a fork parameter to the AgentSwarm tool

* fix(agent-core-v2): seal partial assistant forks

* fix(agent-core-v2): align fork parameter descriptions

* fix(agent-core-v2): drop the main-only registration gate from goal tools

* fix(agent-core-v2): disclose dates via reminders to keep the system prompt byte-stable

* docs: condense the fork changesets to single sentences

* docs(agent-core-v2): frame the tool-contribution when gate as a fork parity trade-off

* test(agent-core-v2): plug fork coverage gaps and decouple swarm tests from spawn internals

* docs(agent-core-v2): keep the when-gate guidance in the contribution JSDoc only

* fix(agent-core-v2): contribute cron tools to every agent for fork prefix-cache parity

CronCreate/CronList/CronDelete were registered directly into the main
agent's tool registry by SessionCronServiceImpl, bypassing the
AgentToolContribution seam and keying on per-agent identity — so a forked
agent rebuilt a tool surface three tools shorter than its caller and the
inherited prompt prefix missed the cache.

Register the three tools through registerAgentToolService like the goal
tools do (no when gate, identical surface for every agent) and enforce
the main-agent restriction at execution time instead. Also fall back to
DEFAULT_CRON_CONFIG when the config section is absent, since the service
can now be constructed after the main agent exists.

* feat(agent-core-v2): track the fork parameter in the subagent_created event

* fix(agent-core-v2): gate tower orchestration tools at execution time

TowerInit/TowerPlan/TowerSpawn/TowerMerge/TowerTeardown were contributed
with a when predicate keyed on agentId === 'main', so a forked agent
rebuilt a tool surface missing TowerInit (always present for the default
profile) plus the rest of the tower set once it was enabled — breaking
prompt prefix-cache parity with the caller.

Contribute the tools with no when gate (profile policy still controls
visibility) and reject non-main callers at execution time instead.

* test(agent-core-v2): expect the fork field in the subagent_created mirror assertion

* test(agent-core-v2): cover fork subagent first-request prefix parity

* refactor(agent-core-v2): share the main-agent-only tool refusal across cron and goal tools

Goal tools rejected subagent callers by throwing GOAL_UNSUPPORTED_AGENT
from the service, which the executor wrapped as a resolution failure;
cron tools returned a clean refusal but each tool open-coded the same
identity check. Centralize the check and both messages in
agent/tools/mainAgentOnly.ts and use it from all seven tools, keeping
AgentGoalService.assertSupportedAgent as the coded boundary for RPC and
SDK callers.

* refactor(agent-core-v2): keep the goal main-agent gate at the tool layer only

* fix(agent-core-v2): preserve the fork tool surface when inheriting user tools

* Revert "refactor(agent-core-v2): keep the goal main-agent gate at the tool layer only"

This reverts commit fc09a8fa32.

* test(agent-core-v2): complete fork lifecycle stub

* Delete .changeset/btw-inflight-tool-calls.md

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

* Delete .changeset/todo-reminder-main-only.md

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

* Delete .changeset/swarm-fork-context.md

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

* Add optional 'fork' parameter to subagent tools

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

* docs(agent-core-v2): drop the fork JSDoc comments

* feat(agent-core-v2): add prompt_cache_probe telemetry for forked agents

* feat(agent-core-v2): gate the subagent fork parameter behind an experimental flag

---------

Signed-off-by: 7Sageer <sag77r@hotmail.com>
2026-08-20 21:50:44 +08:00
7Sageer
38c55501ad
docs: restructure the secondary_model config section for scannability (#3123)
Split the dense prose walls into a minimal config, a one-line-per-field
table, constraint bullets, a numbered resolution order, and a separate
advanced subsection for per-entry thinking efforts. All behavioral facts
are preserved; zh and en stay mirrored.
2026-08-20 17:25:13 +08:00
Kimi Agent
fa9865f2ee
docs: document KIMI_CODE_CUSTOM_HEADERS on the env vars page (#3097)
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 / 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
* docs: document KIMI_CODE_CUSTOM_HEADERS on the env vars page

* docs: address review on KIMI_CODE_CUSTOM_HEADERS entry

- use a neutral gateway header name in the example
- correct the release version to 0.20.2
- scope the override claim to exact-name matches and warn against
case-variant auth headers

* docs: describe protocol-dependent Authorization precedence

On the OpenAI-compatible protocols (kimi/openai/openai_responses) an
exact Authorization custom header is applied after the SDK-generated
bearer token and therefore replaces it; /models listing keeps its own
authentication.

* docs(zh): add the required space before the config-files link

Per the mixed-content spacing rule in docs/AGENTS.md.

---------

Co-authored-by: bj456736 <bj456736@users.noreply.github.com>
2026-08-19 23:02:36 +08:00
7Sageer
157c84f5d1
docs: fix thinking-effort examples in configuration docs (#2988)
Some checks are pending
CI / test-vscode-legacy (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 / lint (push) Waiting to run
CI / typecheck (push) Waiting to run
Nix Build / Check flake.nix workspace sync (push) Waiting to run
Nix Build / nix build .#kimi-code (push) Blocked by required conditions
Release / Release (push) Waiting to run
Release / Deploy docs (push) Blocked by required conditions
Release / Native release artifact (push) Blocked by required conditions
Release / Publish native release assets (push) Blocked by required conditions
The secondary-model variant example could not work as written: a bare
[models] entry does not inherit the provisioned entry's metadata, and
default_effort only takes effect when it is a member of support_efforts,
which the kimi-for-coding family does not declare. Base the example on
kimi-code/k3 with the full metadata copied, and state both prerequisites.

Also align the full-config example's k3 support_efforts with what /login
provisions (low/high/max, so the shown thinking effort "high" is valid),
and stop describing kimi-for-coding-highspeed as cheap: it is priced
higher, so its pool hint now steers toward latency-sensitive tasks.
2026-08-17 12:03:29 +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
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
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
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
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
liruifengv
3c75a27da6
feat(tui): add cache-expiry hint dialog for resumed and idle sessions (v2 engine) (#2646)
* feat(agent-core-v2): detect prompt-cache breaks from per-step usage and emit telemetry

Track consecutive turn-scoped LLM requests per agent; when the cache-read
token count drops by more than 5% and by more than 2000 tokens between
requests, log a debug line and emit cache_break_detected with both usages,
the drop ratio, and the interval. Operation requests (e.g. compaction) act
as a baseline barrier so expected drops are not reported.

* feat(tui): add cache-expiry hint dialog for resumed and idle sessions (v2 engine)

Resuming a long-idle session or submitting after a long idle stretch
re-sends the whole history with an expired context cache. Show a dialog
offering to compact, start a new session, continue as-is, or never ask
again (persisted as cache_expiry_hint in tui.toml). Thresholds come from
the client_configs endpoint (estimated_cache_duration) via a generic
per-name cached client; only OAuth-managed providers participate.

* fix(tui): preserve submit order and revalidate session in cache-hint flows

Cold-cache submits during the in-flight config fetch are now swallowed and
replayed through a FIFO chain, so a later prompt can never overtake the
stashed one. Both the resume and idle paths re-check the current session
after the async fetch: a switch mid-flight drops the dialog (resume) or
hands the stashed input back to the editor instead of sending it into the
wrong session (idle).

* chore(agent-core-v2): regenerate state manifest after merging main

* fix(tui): apply cache_expiry_hint on /reload and /reload-tui

* fix(agent-core-v2): skip unmeasured all-zero usage in cache break detection

* fix(tui): restore chained cache-hint submits when the dialog is not sent

When several submits are swallowed during the cold-config fetch and the
first dialog is dismissed (or its compact/new action fails), the stashed
inputs were restored while later chained submits were still released —
reordering the conversation. Chained submits now follow the fate of the
message that opened the dialog, and multiple restores append newline-joined
instead of overwriting the editor.

* fix(agent-core-v2): reset cache-break baseline on model change

Caches are per-model, so a cache-read drop after /model is expected, not a
break. The baseline now carries the model and only same-model records are
compared.

* fix(tui): only count LLM-activity replay records for the resume cache hint

The v2 resume replay also carries local-only state records (permission,
plan, config updates, approval results) that slash commands append without
an LLM request. Filter lastActiveAt to message/compaction records so a
recent local change no longer masks an expired cache.

* style(agent-core-v2): rewrite the cacheBreak impl header per package convention

State the domain role, collaborators, and scope instead of narrating
implementation steps; the behavior guards now live in the code alone.

* fix(tui): drop the resume cache hint when a turn started mid-fetch

The resume dialog is fire-and-forget over an async config fetch; if the
user already sent the first prompt by the time it resolves, mounting would
overlay an active turn and its actions would hit the live session. Re-check
streamingPhase/isCompacting after the await, next to the session check.

* style(agent-core-v2): trim the cacheBreak contract header to contract and scope

* refactor: report cache-break detection from the TUI client

Move the detector out of the engine so the telemetry event carries the
client's own identity (which client produced it is now attributable). The
TUI observes main-loop turn.step.completed usage directly, with the same
guards: first-step/unmeasured/all-zero records skipped, model change and
compaction reset the baseline. The agent-core-v2 cacheBreak module is
removed.

* chore: drop accidentally committed dist-web build output and ignore it

* chore: revert the dist-web ignore rule

* chore: restore dist-web to the tracked content from main

* feat(tui): record cache breaks caused by mid-session model/effort switches

A model or effort change mid-session busts the prompt-cache key — that is
a real cache break worth attributing, not noise. The baseline now carries
model and effort, the same-model exemption is gone, and cache_break_detected
reports prev/curr model and effort alongside both usages.

* chore(changeset): simplify the cache-expiry hint entry

* chore(changeset): trim the cache-expiry hint entry to one line

* fix(tui): cache-hint review follow-ups

- carry the pre-dialog media extraction through compact/new resends so
  pasted attachments survive the image-store clear on a new session
- reset the cache-break baseline after /undo — the context cut makes the
  next cache-read drop expected
- release the stashed submit when a foreground operation started during
  the cold-config fetch instead of mounting the dialog over it
- count a completed compaction as activity so the next submit is not
  judged against the pre-compaction timestamp

* chore(changeset): drop the v2-engine-only suffix

* fix(tui): seed the activity baseline when the resume check skips

* fix(tui): cache-hint review follow-ups

* fix(tui): record cache activity on completed steps, not turn begin

* feat(cli): persist the client-configs cache across restarts
2026-08-06 13:26:37 +08:00
Haozhe
34c4181437
fix(kimi-code): keep kimi -p alive while background tasks are pending (#2675)
The 10-year default print wait ceiling (315360000s) overflowed Node's
setTimeout limit (2^31-1 ms) into a 1ms fire, so the steer/drain wait
returned instantly and kimi -p exited right after the main turn, killing
pending background tasks and subagents.

- add setClampedTimeout in agent-core-v2 _base, clamping delays to
  MAX_TIMER_DELAY_MS, and route every config-driven timer through it
  (timeoutOutcome, task wait/manager timeout, swarm attempt timeout)
- chunk the print turn-endings wait against the real deadline instead of
  returning null on the first clamped timer fire
- restore v1 semantics: a non-positive swarm subagent timeout is unbounded
- default print_wait_ceiling_s to 2147483s (~24.8 days, the timer maximum)
2026-08-06 11:02:34 +08:00
Haozhe
f881cdd970
feat(cli): default CLI surfaces to the agent-core-v2 engine (#2627)
Some checks are pending
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
CI / test (2) (push) Waiting to run
CI / test (3) (push) Waiting to run
CI / test (4) (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
CI / test (5) (push) Waiting to run
CI / test-pi-tui (push) Waiting to run
CI / test-windows (push) Waiting to run
* feat(cli): default to agent-core-v2 engine with KIMI_CODE_LEGACY_FLAG opt-out

- invert the engine gate: isKimiV2Enabled() now returns true unless
  KIMI_CODE_LEGACY_FLAG is truthy; KIMI_CODE_EXPERIMENTAL_FLAG no longer
  selects the engine
- replace the experimental `kimi acp-v2` command with the native v2
  implementation as the default `kimi acp`; the legacy acp-adapter path
  remains under the legacy flag
- drop the acp-v2 experimental flag from the registry
- rename the dev:cli:v2 script to dev:cli:legacy
- update en/zh docs for the new default engine and the legacy flag

* feat(cli): route export and provider through the engine gate

- select the harness via isKimiV2Enabled(): agent-core-v2 by default,
  the legacy harness when KIMI_CODE_LEGACY_FLAG is truthy
- close the harness after each one-shot command so the v2 engine's
  watchers do not keep the process alive
- document both commands in the KIMI_CODE_LEGACY_FLAG env-var entry
2026-08-05 14:42:23 +08:00
Kai
98ee35afd2
feat(agent-core-v2): add custom agent identity (#2573)
* refactor(agent-core-v2): simplify context tags and shared copy

Rename the context-injection tags to `<skill-loaded>` and
`<plugin-instructions>`, drop the product prefix from the CronCreate tool
description and the default agent description, and point the MCP OAuth
callback page back to "your terminal" instead of naming one client.

The callback page is shared by the ACP host, the web UI, and embedding
hosts, so naming a single client was inaccurate there. The tags and the
two descriptions read exactly the same without the prefix. Verified no
runtime consumer matches the old tag names; the updated snapshots cover
the tool descriptions that changed.

* feat(agent-core-v2): add a switch for the product-documentation skills

Five builtin skills document this CLI itself — `update-config`,
`custom-theme`, `mcp-config`, `check-kimi-code-docs`, and
`import-from-cc-codex`. Their names and descriptions sit in the system
prompt on every turn, which is dead weight for runs that will never
reconfigure the CLI.

Add a top-level `builtin_product_skills` field (also settable through
`KIMI_CODE_BUILTIN_PRODUCT_SKILLS`) to drop them. On by default, so
nothing changes unless it is set; the trade when off is that the model
loses the guided flows for those tasks.

Filtering happens where the catalog is assembled — a later filter would
leave the skills advertised to the model. The whole section is one
scalar, so it exercises the section-level env binding branch and needs
its own strip: `stripEnvBoundFields` only walks object fields, so an env
override would otherwise be written back into `config.toml`.

* feat(agent-core-v2): add custom agent identity

Add an `[identity]` config section (`name`, optional `slug`, both also
settable through `KIMI_CODE_IDENTITY_NAME` / `KIMI_CODE_IDENTITY_SLUG`)
that sets the identity the agent presents: the name it calls itself in
the system prompt, the `User-Agent` product token sent to third-party
providers, and the client name announced to MCP servers. Leaving it
unset changes nothing.

Until now every one of these was fixed, which left no way to run the
agent as part of another product — an internal deployment, a fork with
its own branding, an embedding host.

The identity resolves inside the engine rather than being seeded by each
host, so it applies to every launch surface — including headless runs,
which today seed no display name at all and fall through to the built-in
default.

Two deliberate asymmetries:

- The display name is a filling value with a fallback chain (config >
  host-declared > the consumer's own default); the slug is a rewriting
  value with two states only, so with no identity configured the
  rewriting paths are equivalent to not existing.
- The rewrite happens in the outbound header assembly, the one layer
  that knows which vendor it is building for. Vendors declaring
  `hostHeaders: 'full'` keep the host's own product token, which that
  header set is built around and which backends key on; the configured
  identity applies to the third-party path.

Resolution is lazy throughout: config loads asynchronously, and a
constructor snapshot would freeze the pre-load value under some startup
orderings.

Two input edges the resolver has to absorb, since both would otherwise
reach the User-Agent builder and either break it or quietly rewrite the
header: blank and whitespace-only values read as unset in the file just
as they already did in the env, so a stray `name = ""` cannot claim an
identity; and a name that folds away to nothing under slug
normalization (a CJK-only name, say) falls back to a neutral token
rather than producing a blank product, which the builder rejects.

* fix(agent-core-v2): keep the file value when a scalar env binding fails to parse

`config.ts` documents that an env value failing its binding's `parse` is
ignored, and `applyEnvBindings` honors that for object fields by
assigning only when the resolved value is defined. `applySectionEnv`
returned the parse result straight through for whole-section scalar
bindings, so a blank or mistyped variable resolved to `undefined` and
cleared the configured file value instead of being ignored.

Nothing hit this before: every existing section either binds object
fields or is env-only. `builtin_product_skills` is the first
whole-section scalar binding, where exporting an empty or misspelled
`KIMI_CODE_BUILTIN_PRODUCT_SKILLS` would silently undo a configured
`false`.

* feat(agent-core-v2): extend the custom identity to discovery and global MCP

Two outbound paths still announced the built-in product name under a
configured identity:

- `DiscoveryService` read the host User-Agent straight from bootstrap
  args when refreshing provider models, so custom registries — which are
  third-party endpoints — saw the original token while chat requests to
  the same class of endpoint saw the configured one.
- `SDKRpcClientV2` builds its own global `McpOAuthService` plus a
  throwaway `McpConnectionManager` for server testing, neither of which
  goes through the workspace-owned manager that carries the resolver.

Both now resolve the identity from the App scope.

* refactor(agent-core-v2): neutralize remaining copy and align comments

The synthetic MCP authentication tool description is injected into the
model context and still named the product; it and the OAuth callback
pages now use client-neutral wording. "Return to your terminal" was no
improvement over naming a client — both assume what the host is, and
that page serves the ACP host, the web UI and embedding hosts alike.

Comments introduced by the identity work move into their module headers,
per the domain convention. Interface field docs stay: the rule names
functions, methods and statements, and field-level docs are established
across the codebase.

The new tests gain scenario headers and dispose the scoped hosts they
create, and the `[identity]` docs state which engine reads the section.

* fix(agent-core-v2): read the product-skill switch after config is ready

`BuiltinSkillSource` is the lowest-priority skill source, so the workspace
catalog loads it first — before `IConfigService` has finished loading — and
keeps the contribution it returns for the life of the handler, with no
reload path and no change event. Reading `builtin_product_skills` eagerly
therefore stranded the startup configuration: an explicit `false` could be
ignored for the whole process. `UserFileSkillSource` already awaits config
readiness for exactly this ordering; this source now does the same.

Also record the identity collaborator in the two module headers that gained
the dependency without documenting it, and scope the
`builtin_product_skills` docs to the engine that reads it, matching the
note the identity section already carries.

* fix(agent-core-v2): apply the product-skill switch to session-less listings

`builtin_product_skills = false` only reached the scoped skill source. The
SDK's `listWorkspaceSkills` and the server's `GET /workspaces/{id}/skills`
both composed the raw `BUILTIN_SKILLS` constant, and the web app feeds its
pre-session onboarding menu from that route — so the five product skills
stayed listed until a session existed, then vanished from the session's
catalog.

Move the decision into `visibleBuiltinSkills(enabled)` next to the constant
and route every consumer through it, reading the switch via the shared
`builtinProductSkillsEnabled`. Keeping "what counts as a product skill" in
one place is the point: three copies of the predicate would drift the next
time a builtin is added. The SDK listing also awaits config readiness,
which it did not do before.

* fix(node-sdk): await config before materializing the global MCP OAuth provider

`McpOAuthService` caches providers by store key and stamps the client name
when it first builds one, and the preceding `globalMcpConfig.get()` reads
`mcp.json` directly rather than through `IConfigService`. So a
`beginGlobalMcpServerAuth` call made right after the harness is created
could resolve the identity before config finished loading, pinning the
built-in label for the rest of the process — including the OAuth dynamic
registration a third-party MCP server records.

`testGlobalMcpServer` already awaited config readiness for its own reasons;
this path now does too.

* refactor(agent-core-v2): drop the unused builtin-skill registrar

`registerBuiltinSkills` stamped the raw constant into a catalog for "edge
composition without a Session" — exactly the shape that now has to respect
`builtin_product_skills`. It has no callers in v2 and is not exported from
the package index, so it was dead code that also stood as an invitation to
bypass the switch. v1 keeps its own copy.

Every remaining path composes builtins through `visibleBuiltinSkills`.

* fix(agent-core-v2): send the configured identity on custom-registry imports

`:import_registry` fetched a user-supplied third-party URL with a
hardcoded `kimi-code-kap-server` User-Agent, so the first request to a
registry announced the product while every scheduled refresh of the same
registry announced the configured identity. The hardcoded value was wrong
on its own terms too: that token names the server, and this path also runs
in the CLI.

Both services now project the identity through `identityUserAgent`, which
carries the two guards (no host header, or no identity) once instead of
per caller. The model catalog keeps an inline copy on purpose — kosong is
a foundational layer and must not import an app domain.

Sweeping the remaining outbound User-Agent sources found no further gaps:
WebFetch deliberately sends a Chrome-like UA, the models.dev catalog fetch
sends none from the CLI, and kap-server's `user-agent` reads are inbound.

* docs: scope the identity env vars and condense the changeset

The environment-variable reference advertised all three new variables
without noting that only the agent-core-v2 engine reads them; the
configuration page already carried that note. Added in both locales.

The changeset had grown into two paragraphs of implementation detail,
which is what would land in the CLI release changelog. `gen-changesets`
asks for one short sentence plus at most a one-line usage hint.

* docs(agent-core-v2): describe the identity as what the agent calls itself

The module headers had drifted into describing the feature by what it
keeps off the wire rather than what it configures. Reworded so they state
the capability: the identity is the name the agent uses for itself, and
the unset case is a no-op rather than something "safe". The product-skill
switch excludes skills rather than hiding them.

Wording only; behavior and structure unchanged.

* test(agent-core-v2): cover the identity on custom-registry imports

The import path switched from a hardcoded `kimi-code-kap-server` token to
the host User-Agent projected through the identity, but nothing asserted
it. Two cases pin both halves: a configured identity reaches the request,
and an unconfigured one leaves the host header intact — the second matters
because a single case would also pass if one hardcoded value had simply
replaced another.

Both fail against the previous implementation.

* fix(node-sdk): guard every global MCP OAuth path behind config readiness

`McpOAuthService` caches providers by store key and stamps the client name
when it first builds one, so any path that can materialize a provider has
to run after config has loaded. `beginGlobalMcpServerAuth` awaited
readiness, but `resetGlobalMcpServerAuth` reaches the same cache through
`invalidate()` -> `getProvider()` without waiting: resetting auth right
after the harness is constructed pinned the built-in client name, and the
await added to the begin path could not help because it then reused that
cached provider.

Rather than add the missing await, the accessor is now async and holds the
guard itself, so the service cannot be obtained before config is ready and
a future entry point cannot forget. The remaining `configReady` in
`testGlobalMcpServer` stays — that one is for its own `[mcp]` section read.

* fix(agent-core-v2): send the configured identity on models.dev requests

The directory fetch behind `listModelsDevProviders` / `getModelsDevProvider`
still hardcoded a `kimi-code-kap-server` User-Agent, so browsing or importing
from models.dev announced the built-in product — and claimed to be the server
even when running in the CLI. Only the custom-registry import had been fixed.

`getModelsDevCatalog` now takes the User-Agent from its caller: the module is
plain module-level state with no container access, and the value depends on
the host and the configured identity, which only the calling service can see.
All four third-party fetches in that service share one helper.

Where the host states no User-Agent, a neutral token stands in rather than
dropping the header — these are directories the service chooses to call, so
there is no host intent to preserve, unlike the provider requests the model
catalog assembles.

Both new tests fail against the previous hardcoded value.

* test(agent-core-v2): assert the product-skill set literally

The expected sets were derived from the same `productSpecific` field the
production filter reads, so a builtin silently losing its marker would just
move between sets and leave every assertion green — while staying visible to
the model once the switch is off. The five names are now literal, with a test
asserting the marked set matches them exactly.

Dropping the marker from one skill now fails four tests instead of none.

Also states the App scope in the identity contract header, per the domain's
comment convention for contract files.

* fix(agent-core-v2): normalize the host-declared display name too

Blank and padded values were normalized on the config side but not on the
host fallback, so an embedding host passing `displayName: "   "` rendered
"You are   ," into the system prompt, and a padded name kept its padding.
Same rule now applies to every source of the name.

Also names `agentIdentity` as the collaborator in the request-headers
adapter header, which described the value it obtains without saying which
domain resolves it.

The three new cases fail against the previous implementation.

* fix(agent-core-v2): keep the configured slug when the host sends no User-Agent

The neutral fallback added for hosts that state no `User-Agent` discarded a
configured identity along with it: `identityUserAgent` returns `undefined`
as soon as there is no host header to rewrite, so `?? DEFAULT_IDENTITY_SLUG`
sent the literal `agent` even when `[identity].slug` was set — precisely the
case that fallback exists to serve. The configured slug now stands on its
own, with the neutral token reserved for having neither.

The four combinations of (host header, configured slug) had three tests; the
missing one is the one that was wrong. It now fails without this change.

`outboundUserAgent` also awaits config readiness before reading the identity,
so a browse issued right after bootstrap cannot send the pre-load value — the
guard lives in the accessor rather than at its four call sites, matching how
the same race is handled elsewhere in this branch.

Both headers here and in `discoveryService` now name `agentIdentity` as the
collaborator resolving that token.

* test(acp-server): follow the renamed skill-activation tag

`acp-server` arrived on main after the tag rename, so its two assertions
still expected `kimi-skill-loaded` and failed once the branches met. Also
updates the web app's CSS comment, which named the old tag from the start
of this branch — a comment, so nothing ever failed on it.

Found by CI: the merge verification only ran agent-core-v2's suite, and
this package is neither a dependency nor a dependent of it.

* fix(agent-core-v2): present the configured slug on registry refreshes too

The previous round taught the import path to fall back to the configured
slug when the host states no `User-Agent`, but left the scheduled refresh
of the same registry on the bare projection — so one registry could see
`acme` on import and the runtime default on refresh.

Extracting `identityUserAgent` had made the two paths share a function
without sharing the policy. The choice itself is now the shared piece:
`identityUserAgentOrDefault` always yields a value, for the directories
this process chooses to call, while `identityUserAgent` stays the form
that rewrites only what the host already sends — what a provider request
needs, where the host's silence is its own choice.

* docs(agent-core-v2): move new member docs into the module headers

The domain's comment convention is absolute — comments live solely in the
top-of-file block — and I had read the "functions, methods, or statements"
clause as leaving interface members out. It does not: only 25 of 734 v2
sources carry an indented block, so the members I documented were the
exception, not the pattern.

Seven members across six files move into their headers. `types.ts` had no
header at all, so it gains one.

* fix(agent-core-v2): connect session MCP overlays after config is ready

The shared manager reaches `connectAll` through `initialize()`, which awaits
the config domain first; `sessionOverlay` called it straight away. A session
carrying ephemeral `mcpServers` created right after bootstrap therefore
resolved the client name before config had loaded and initialized under the
built-in one.

The blast radius is wider than that one connection: a remote server sends
the overlay through `hasTokens()`, which materializes an OAuth provider on
the *shared* service and caches it by store key — so the early name outlives
the connection that raced. The overlay now connects behind `mcpConfig.ready`,
leaving the returned readiness promise unchanged.

* fix(agent-core-v2): reload builtin skills when their switch changes

The workspace catalog keeps each source's contribution for the life of the
handler, so a `builtin_product_skills` toggle never reached an existing
handler's sessions. That was harmless while every surface read the same
constant — but routing the session-less listings through the config made the
two views disagree, since those read the switch on every call.

Follows `ExtraFileSkillSource`: subscribe to the owning section and fire
`onDidChange`, which the catalog already turns into a source reload. The
test asserts an unrelated section does not trigger it.

* fix(agent-core-v2): apply the identity to self-configured web services

`[services.moonshot_search]` and `[services.moonshot_fetch]` name their own
`base_url`, so both services can point at an endpoint the user chose — but
each forwarded the host request headers verbatim, sending the built-in
product token there under a configured identity.

Only the services-config path is rewritten; the managed OAuth path keeps the
host headers as they are, being the endpoint the session authenticated
against. The distinction is the same one the model catalog draws per vendor.

`identityHeaders` carries the rewrite across a whole header set, so this is
the fourth caller sharing the projection rather than repeating its guards.
A pair of tests pins both halves.

My earlier sweep classified these two as official by their names instead of
asking who chooses the URL, which is why they were missed. The contract
header is also condensed here, per the convention below.

* docs(agent-core-v2): condense the identity headers to their contracts

The comment convention is one sentence with two halves — comments live only
in the top-of-file block, *and* that block states the module's role without
narrating implementation. Moving the member docs up last round satisfied the
first and broke the second: the headers ended up spelling out the slug
folding algorithm, the strip mechanics, and the load order.

Kept what a caller or the next editor would get wrong without it (why the
value is read rather than snapshotted, what `undefined` obliges a consumer
to do, why this source waits for config). Dropped what the code already
says. 22/12/12/13 lines, against 53 in `catalogService.ts` — length was
never the problem.

* fix(agent-core-v2): rebuild active prompts when the builtin skills change

Reloading the catalog on a `builtin_product_skills` toggle left existing
agents holding the old listing: `AgentProfileService` refreshes the prompt
only for the plugin source, so a disabled switch kept advertising skills
that were gone, and enabling it left them missing until an unrelated
refresh.

The plugin source is special because it also contributes prompt sections
(#2314), and the file-backed sources are left out for cost — their fs
watches would rebuild every agent's prompt on each edit. The builtin source
has no watch: it changes only when its config switch is toggled, so it
belongs with the plugin source rather than with the file ones.

Subscribing to the catalog rather than the config section is load-bearing.
The catalog fires after the contribution is replaced, whereas a config
subscription would race the reload, and `resolveSkillListing` only awaits
the catalog's *initial* readiness — so the rebuilt prompt could read the
listing it was meant to replace.

The source id is a named constant now, so the subscription does not match
on a bare string.

* refactor(agent-core-v2): freeze the agent identity for the process lifetime

The identity is announced outward (MCP initialize, OAuth registration,
provider request logs) and cannot be re-announced, so mid-process changes
could only ever apply partially. Resolve it once when config first loads
and hold it for the life of the process: IAgentIdentity now hands out a
frozen snapshot via resolved()/current(), carrying finished products
(outbound User-Agent variants, rewritten header set) so call sites stop
composing host headers with the slug themselves. The kosong host-headers
port carries two finished layers and the catalog only picks one; consumers
gain no invalidation obligations because the value can never change after
the freeze. [identity] edits take effect on the next start (documented).

* fix(agent-core-v2): locate the User-Agent header case-insensitively

HTTP header names are case-insensitive, but the snapshot builder looked up
'User-Agent' by exact key: an embedding host spelling it 'user-agent' got no
third-party UA and kept its own product token on the services path even with
an identity configured. The builder now locates every case variant and
rewrites each in place, keeping the host's spelling. Also corrects the two
web-service headers that still described both paths as sending the bootstrap
headers, naming agentIdentity as the collaborator behind the config path,
and documents that a resumed session keeps its recorded system prompt.

* fix(agent-core-v2): attribute header provenance from the finished third-party layer

Inspection reconstructed the non-full host layer from the raw headers with an
exact-case 'User-Agent' lookup, so a host spelling the header 'user-agent'
got a resolved User-Agent with no provenance entry even though the runtime
sends the rewritten value. buildModel now captures the port's finished
third-party layer in the trace and attribution reads it, keeping inspect()
on the same resolution pass as get(). Also condenses the identity contract
header to its external role, and documents that an existing MCP OAuth
authorization keeps the client registration it was granted under (reset the
server's auth to register under the new identity).

* fix(agent-core-v2): keep web tool backends from racing the identity freeze

An env-configured [services] endpoint is visible before config finishes
loading, and FetchURLTool / WebSearchTool materialized their backends at
construction — so a fast bootstrap could hit the identity snapshot's
pre-freeze guard during agent creation, and the composed backend pinned
config and login state for the agent's lifetime against the service's
documented per-call resolution. Both tools now resolve their backend per
invocation, the WebSearch activation gate checks presence alone through the
new hasWebSearchProvider() (no provider composition, no identity read), and
bind() awaits the identity freeze before materializing the model, whose
resolution reads the identity through the host-headers port.
2026-08-04 22:35:15 +08:00
qer
c2e53aef6c
docs: polish the 0.32.0 changelog and fill 0.32.0 doc gaps (#2594)
* docs: polish the 0.32.0 changelog and fill 0.32.0 doc gaps

* docs(changelog): note SessionEnd archive and KIMI_TOKEN_COUNTING_STRATEGY for 0.32.0

* docs(changelog): move the 0.32.0 token_counting entry from Features to Polish
2026-08-04 16:31:25 +08:00
Haozhe
21185447fe
feat(agent-core-v2): add tokenCounting service with strategy config and measured anchors (#2563)
* feat(agent-core-v2): add tokenCounting service with strategy config and measured anchors

- add IAgentTokenCountingService as the single owner of token counts:
  context size, full-request size, and estimate primitives, replacing
  the scattered contextSize/tokenEstimate/fullCompaction paths
- add [token_counting] config section with strategy = measured+estimated
  (default) / measured / estimated, plus the KIMI_TOKEN_COUNTING_STRATEGY
  env override; measured zeroes all estimates, estimated ignores anchors
- keep a live measured-anchor ledger in TokenCountingModel: each LLM
  exchange writes a real anchor, undo truncates the ledger so the
  surviving prefix restores its REAL measured size instead of a
  re-estimate, and compaction rebases to a single anchor that blends the
  compaction exchange's measured summary output tokens
- skip writing an anchor when the stream reports no usage event instead
  of anchoring emptyUsage() zeros, which zeroed the context size and
  silenced compaction for providers without usage reporting
- return the strategy-resolved size (not measured) from rpc getContext
  so the tokenCount contract stays correct under the estimated strategy
- migrate all consumers (contextMemory, fullCompaction, llmRequester,
  rpc, mirrorAgentRun, sessionLegacy, kap-server legacyStatus, node-sdk,
  kimi-inspect) to the new service; edge bridges no longer read the wire
  model directly
- document [token_counting] and KIMI_TOKEN_COUNTING_STRATEGY in the
  bilingual config reference

* fix(kap-server): omit maxContextTokens instead of pushing 0 when unknown

- readLegacyStatus falls back to the default model's context limit when no
  model is bound, and omits maxContextTokens entirely when the limit is
  unknown (0 is the engine's UNKNOWN_CAPABILITY marker, not a real limit)
- profileService no longer emits maxContextTokens in agent.status.updated
  when the bound model alias does not resolve

* fix(agent-core-v2): resolve token_counting strategy only at the reporting edge

- keep measured anchors and heuristic estimates both recorded and feeding
  internal logic (compaction triggers, budgets, overflow backoff) regardless
  of the configured strategy
- add IAgentTokenCountingService.statusSize() as the single strategy-resolved
  outward reading and route the WS/REST/RPC status surfaces through it
- fix the context-size display falling back to provider-reported usage under
  the estimated strategy
- fix compaction overflow backoff retrying identical messages until failure
  under the measured strategy (the strategy-gated estimator read as 0)
2026-08-04 09:44:21 +08:00
Haozhe
6ba75a173b
feat(config): add deprecation mechanism and rename loop retry limit (#2572)
* feat(config): add deprecation mechanism and rename loop retry limit

- agent-core-v2 config: declarative section `deprecations` (deprecated TOML
  keys are ignored and report a warning diagnostic; the file is never
  rewritten) and env binding `deprecatedEnv` (old var still resolves as a
  fallback with a warning), surfaced via the new
  `IConfigService.onDidChangeDiagnostics` event
- loop_control: rename `max_retries_per_step` to `max_attempts_per_step` and
  `KIMI_LOOP_MAX_RETRIES_PER_STEP` to `KIMI_LOOP_MAX_ATTEMPTS_PER_STEP`;
  `max_steps_per_run` moves onto the same mechanism (no longer silently
  mapped)
- kap-server: push the global `event.config.warning` WS event to every
  connection whenever the config warning set changes
- TUI: show config diagnostics in warning yellow at startup instead of the
  dim startup notice
- docs: config-files/env-vars (en+zh), regenerated config manifest, and the
  agent-core-dev config guide

* feat(cli): validate config.toml against v2 section registry in doctor

- add v2/validate-config.ts: validate config.toml with the agent-core-v2
  ConfigRegistry, reporting registered-section schema failures as errors
  and unknown top-level keys / deprecated keys and env vars as non-fatal
  warnings
- route `kimi doctor` config validation through the v2 validator when the
  KIMI_CODE_EXPERIMENTAL_FLAG master switch is on (lazy dynamic import,
  keeping the v2 module graph off the default path)
- let doctor checks surface non-fatal warning messages on OK results

* chore: downgrade loop-control changeset to patch
2026-08-03 20:17:01 +08:00
7Sageer
29c9e2ab20
docs: clarify secondary model default binding and override precedence (#2553)
The secondary_model section did not state whether spawned subagents are
forced onto the secondary model or only default to it, nor the full
override precedence. Make the semantics explicit in both locales:

- spawning resolves the model in order: explicit tool-call model ->
  profile model_preference -> configured secondary model (default)
- the tool's model parameter accepts only "primary" / "secondary"
- "primary" means the model the main agent is currently running, not
  necessarily default_model
- the user has no per-spawn switch; overriding is the main agent's
  decision or a profile setting

Also unify secondary-model terminology and the [models] alias wording
across the config-files, agents, slash-commands, and env-vars pages.
2026-08-03 15:42:09 +08:00
wenhua020201-arch
f8ec3d1656
docs: fix dead anchor links in en/zh docs (#2348)
* docs: fix dead anchor links in en/zh docs

- #loop_control -> #loop-control (heading slug uses hyphens)
- #secondary_model -> #secondary-model
- env-vars model section anchors: kimi_model -> kimi-model
- provider credential section anchors: configtoml -> config-toml
- /provider management anchors: point at the renamed heading in each locale
- hooks: point the stale config-files#hooks reference at the local Configuration section
- en files: replace two leftover Chinese anchors with their English targets

* docs: add missing .md extension to themes page links

---------

Co-authored-by: qer <wbxl2000@outlook.com>
2026-07-29 15:57:48 +08:00
7Sageer
efac96c8a9
feat(agent-core): custom agent files and secondary model on the v1 engine (#2232)
* feat(agent-core): custom agent files and secondary model on the v1 engine

Migrate the custom agentfile and secondary-model capabilities from
agent-core-v2 to the v1 engine so they work in the TUI and plain
kimi -p sessions:

- discover Markdown agent files from user/project/extra/explicit
  directories with the v2 precedence rules, a merged session profile
  catalog replacing the hardcoded builtin profile lookups, SYSTEM.md
  main prompt override, and ${base_prompt} backed by the effective
  default
- --agent/--agent-file now work in print mode on the default engine;
  CreateSessionOptions gains agentProfile/agentFiles
- [secondary_model] config + KIMI_SECONDARY_MODEL/EFFORT bind newly
  spawned subagents to a cheaper model behind the secondary-model
  experiment flag, with primary/secondary model params on Agent and
  AgentSwarm and upfront session warnings
- full disallowedTools deny semantics (exact names + mcp__ globs)
  evaluated by the tool manager and persisted in the agent wire

* fix(cli): guard optional agentFiles in the prompt runner

runPrompt is also driven programmatically (headless goal flow) with
options that never pass through the CLI parser defaults, so agentFiles
can be undefined; mirror the addDirs optional-chaining pattern. Also
extend the SDK experimental-feature assertion with the secondary-model
flag.

* fix(agent-core): preserve custom agent bindings on v1

* fix(agent-core): narrow secondary model error hints

* fix(agent-core): persist custom agent profile bindings

* Delete .changeset/sdk-agent-profile-options.md

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

* Update v1-custom-agent-files.md

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

* Update v1-secondary-model.md

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

* Update v1-custom-agent-files.md

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

* fix(agent-core): keep SYSTEM.md a prompt-only overlay for delegation

* docs: update agent file and secondary model availability wording

* fix(cli): reject --agent-file combined with session resume

The resume path only forwards the agent file's name for the bound-profile
assertion; the file's content is never re-applied (the session keeps its
creation-time catalog snapshot). Previously the combination was silently
accepted, so an edited file (or a same-named one) appeared to apply but did
not. Reject it at option validation and document the constraint.

* refactor(agent-core): share prompt-section prose and note v2 twins in agentfile headers

The Windows notes, additional-dirs and skills prose blocks existed twice:
inline in the builtin default template (system.md) and as constants in the
agent-file renderer (from-file.ts). Extract them to profile/prompt-sections.ts
as the single source: system.md renders them through injected KIMI_* template
variables and from-file.ts imports the same constants. Rendered prompts are
byte-identical for all four builtin profiles across macOS/Windows and
skills/dirs on/off; a new test pins system.md to the shared constants.

Also mark each profile/agentfile file with the path of its agent-core-v2
counterpart so format/semantics changes land in both engines.

* feat(cli): add /secondary_model command for the subagent model

Mirror /model: a picker with a thinking-effort step that persists [secondary_model] and live-applies to the current session via a new Session.setSecondaryModel RPC (node-sdk wrapper included), so newly spawned subagents bind the new model right away. The /model picker now hides the synthesized __secondary__ derived entry; docs and the update-config builtin skill mention the section.

* feat(tui): show the bound model in subagent run stats

Subagents report their model alias via agent.status.updated after spawn; resolve it to a display name and surface it in tool-call subagent stats and agent-group rows.

* fix(agent-core): validate agent profile before session persistence

* fix(agent-core): refresh subagent tools after model switch

* fix(agent-core): show subagent model preferences

* fix(agent-core): preserve secondary model recipe on live apply

* fix(agent-core): make secondary model apply explicit

* fix(tui): refresh secondary model display state

* chore: merge secondary model changesets into one

* Add /secondary_model command for subagent configuration

Show each subagent's model in the subagent card header and agent-group rows. Requires the secondary-model experiment (KIMI_CODE_EXPERIMENTAL_SECONDARY_MODEL=1); run /secondary_model to pick a model and thinking effort, applied to the current session immediately.

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

* fix(agent-core): align explicit agent file precedence

* fix(agent-core): let disallowedTools deny select_tools

* chore(cli): drop engine mention from --agent/--agent-file help text

* feat(cli): support --agent/--agent-file in the interactive TUI

Bind the selected agent profile to the startup session when launching
the TUI with --agent/--agent-file, including the session created after
an OAuth login at startup. Sessions created later in the process (/new)
keep the default profile.

Make both flags creation-only in every mode: combining them with
--session/--continue is now rejected in print mode too, since resume
restores the bound agent from the session automatically.

* fix(agent-core): persist new secondary-model selections under env overrides

stripSecondaryModelConfig restored secondary_model.model/default_effort
from raw whenever KIMI_SECONDARY_MODEL/KIMI_SECONDARY_EFFORT was set, so
a /secondary_model pick made under the env vars was silently discarded
on write. Restore from raw only when the value being written still
equals the env value (an overlay round-trip), mirroring the pointer
check in stripEnvModelConfig; a genuinely different selection now
reaches config.toml.

* fix(cli): report the effective secondary model when env overrides the pick

/secondary_model toasted the picked alias even when
KIMI_SECONDARY_MODEL/KIMI_SECONDARY_EFFORT made the session bind a
different model. Read the effective binding back from the reloaded
config (as /model does from session status) and warn with the
env-overridden values instead.

* feat(tui): show the bound model name in the AgentSwarm panel header

---------

Signed-off-by: 7Sageer <sag77r@hotmail.com>
2026-07-29 12:06:26 +08:00
Yufeng He
67dd03149f
feat(tui): customizable footer status line via status_line config (#2255)
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 / 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(tui): customizable footer status line via status_line config

The bottom status bar was a fixed layout. Add a [status_line] section
to tui.toml covering the two established models:

- items: codex-style composition. Pick and order the built-in slots
  (mode, goal, model, tasks, cwd, git, tips); unset keeps today's
  layout, unknown ids are skipped with a warning, and an empty list
  blanks line 1.
- command: claude-code-style custom line. The footer runs the command
  with a JSON snapshot on stdin (model, cwd, git branch, permission
  and plan mode, context usage, session id, version) and renders the
  first stdout line. Runs are throttled to one per second and capped
  at 300ms; nonzero exit, empty output, or a timeout falls back to the
  built-in layout.

Line 2 (context readout) stays built-in in every mode. Resolve #2116.

* feat(tui): apply status_line on /reload-tui and document it

The reload command pushes reloaded tui.toml fields into AppState; the
new statusLine field joins that list so edits go live without a
restart. The config files reference (EN/ZH) documents the new section.

* fix(tui): round-trip active status_line on save and harden the runner

Codex review on #2255 caught a real one: saveTuiConfig rewrites the
whole tui.toml, so changing any other preference dropped an active
[status_line] section. Render it live when set (items and command),
commented-out guide when unset.

Also: spawn the command through ComSpec/cmd.exe on Windows instead of
assuming sh.exe, and take the whole process tree down on timeout
(process-group kill on POSIX, taskkill /T on Windows) so a script that
spawned children cannot leak them.

* fix(tui): address status_line review: runner lifecycle, capture cap, tips slot

- recreate the command runner when a reload swaps status_line.command;
  the old runner kept executing the previous script until restart
- schedule a trailing refresh instead of dropping updates that arrive
  inside the throttle window, so the last state change always lands
- stop accumulating stdout once the first line is complete (and cap a
  missing-newline stream at 64KB); only the first line is ever rendered
- honor the configured position of the tips slot in items instead of
  always pinning tips to the far right
- route unknown status_line.items warnings through the TUI status area
  on reload instead of raw stderr, which could corrupt the display

Changeset text tightened per maintainer note.

---------

Co-authored-by: Kai <me@kaiyi.cool>
2026-07-28 22:37:52 +08:00
Petrichor
cdbd33c13c
fix(kosong): fail fast on quota-exhausted 429 instead of retrying (#1857)
* fix(kosong): fail fast on quota-exhausted 429 instead of retrying

A 429 caused by an exhausted account quota or insufficient balance
(Moonshot error.type "exceeded_current_quota_error", OpenAI
"insufficient_quota") can never succeed on retry, yet it was classified
as APIProviderRateLimitError and silently retried for the whole budget
(10 attempts, ~3 minutes of backoff) with no UI feedback — the session
appeared frozen on every request.

Introduce APIProviderQuotaExhaustedError, minted in
normalizeAPIStatusError from the structured body error.type/error.code
forwarded by convertOpenAIError, with billing-anchored message patterns
as a fallback for gateways that flatten the body to text. The new class
is excluded from isRetryableGenerateError (fail fast, even when a
retry-after header is present) and from isProviderRateLimitError (no
swarm requeue/suspend). toKimiErrorPayload and translateProviderError
map it to provider.api_error (retryable: false) instead of
provider.rate_limit, and classifyApiError reports it as
quota_exhausted in telemetry. agent-core-v2 mirrors the same fix.

Transient rate-limit 429s keep the existing retry, backoff, and
Retry-After behavior (verified end-to-end against a mock provider:
quota body fails after attempt 1/10; rate-limit body still walks the
full 10-attempt ladder).

Behavior changes to note: quota-failed swarm subagents now fail
instead of suspending indefinitely as "Rate limited...", and quota
errors cross the wire as provider.api_error rather than
provider.rate_limit.

* fix(kosong): classify quota exhaustion in OpenAI Responses stream errors

Responses response.failed / error SSE events carry no HTTP status and
were minted by errorFromOpenAIResponsesEvent as either a rate-limit
error (rate_limit_exceeded / embedded status_code=429) or a base
ChatProviderError — and the base class falls into the retryable
unclassified-failure fallback, so an insufficient_quota event still
burned the whole retry budget on the openai_responses path. Route the
event code and message through the same quota-exhausted check before
the rate-limit branch, in kosong and the agent-core-v2 mirror. Covers
all three entry paths (error events, response.failed, nested gateway
frames) since they share the single converter.

* style(agent-core-v2): drop inline comments per AGENTS.md header-only rule

agent-core-v2 comments live solely in the top-of-file block, never
beside functions or statements; the kosong twins keep the full
rationale.

* refactor(kosong,agent-core-v2): move quota-429 checks to vendor hook

Per review on #1857: the knowledge of how a backend signals quota
exhaustion is vendor-specific and must not run for every
OpenAI-compatible provider from the shared conversion layer.

- Add a convertError hook: ProtocolTrait.convertError in agent-core-v2
  (single-value, last-declarer-wins, bound by composeOpenAIChatHooks /
  composeAnthropicHooks / traitConvertError) and an equivalent optional
  hook parameter on convertOpenAIError / convertAnthropicError. Bases
  consult it with the raw failure (SDK error on HTTP paths, raw event on
  the Responses in-stream path) after the abort guard, before their own
  rules.
- Declare Moonshot's quota signals (exceeded_current_quota_error,
  billing wordings) on the Kimi side: kimiOpenAITrait and
  kimiAnthropicTrait in v2, the KimiChatProvider and KimiFiles catch
  sites in kosong, all through the new classifyKimiQuotaError.
- Drop the options parameter from normalizeAPIStatusError and the
  shared quota code/pattern tables: the contract layer keeps only the
  vendor-neutral APIProviderQuotaExhaustedError type and its retry /
  rate-limit / wire-mapping semantics.
- The OpenAI bases keep recognizing only OpenAI's own documented
  insufficient_quota code (HTTP and Responses stream events) as
  protocol knowledge of that wire.

Behavior: kimi and openai provider types classify exactly as before;
an unregistered vendor speaking Moonshot billing wordings through a
plain openai transport now stays a retryable rate limit by design.

* fix(kosong,agent-core,agent-core-v2): wire kimi quota hook fully

Follow-up to the second review round on #1857, all four findings:

- Kimi-over-Anthropic (legacy engine): AnthropicOptions gains the same
  optional convertError hook as the OpenAI bases, threaded through
  AnthropicStreamedMessage and every catch site, and the provider
  manager's anthropic route now passes classifyKimiQuotaError for
  provider type kimi — a quota-exhausted 429 over this transport
  previously still burned the retry budget. classifyKimiQuotaError now
  also walks error -> .error -> .error.error for the code/type, since
  the Anthropic SDK keeps the full body on .error instead of hoisting.
- v2 telemetry: ApiErrorKind gains 'quota_exhausted' and
  classifyApiError checks APIProviderQuotaExhaustedError before the
  generic 429 branch, matching the legacy engine's reporting.
- Hook contract: converted ChatProviderErrors now pass through before
  the vendor hook is consulted in convertOpenAIError /
  convertAnthropicError (both engines), so the hook sees each raw
  failure exactly once even when a stream-minted error crosses an
  outer catch; tests assert the single consult.
- protocolTrait: the convertError member doc shrinks to the concise
  style and the consult contract moves into the file header's
  composition rules.

* test(kosong,agent-core,agent-core-v2): lock quota hook assembly paths

Third review round on #1857:

- Fix the v2 anthropic base header and AnthropicHooks doc still claiming
  withThinking is the only hook.
- Drop the two remaining non-header JSDoc blocks in protocolTrait.ts per
  the AGENTS.md header-only rule; the consult contract already lives in
  the file header.
- Update the ProtocolTrait contract test to the seventeen-hook shape
  (convertError included) and cover the traitConvertError binding.
- Add real-assembly regression probes: the v2 registry composes a
  (kimi, anthropic) provider whose mocked SDK client throws a Moonshot
  quota 429 and generate rejects with the non-retryable
  APIProviderQuotaExhaustedError (a plain anthropic composition keeps
  the same 429 retryable); the legacy ProviderManager routing test
  asserts convertError is classifyKimiQuotaError on the kimi-anthropic
  route and absent for plain anthropic; the legacy provider threads
  options.convertError to its generate catch.

* test(kosong,agent-core-v2): cover KimiFiles quota 429 and drop stale docs

Fourth review round on #1857:

- Drop the AnthropicHooks member JSDoc (its content already lives in the
  anthropic.ts and anthropicHooks.ts file headers) and fix the anthropic
  contrib header still calling the hook set single-hook.
- Add the missing KimiFiles regression in both engines: a mocked files
  client rejecting with a Moonshot quota 429 makes uploadVideo reject
  with the non-retryable APIProviderQuotaExhaustedError, locking the
  classifyKimiQuotaError argument at the upload catch sites.
2026-07-28 14:35:12 +08:00
7Sageer
7b62ed5b2c
feat: support a configurable secondary model for subagents (#2064)
* feat: support a configurable secondary model for subagents

* refactor: move the subagent model config to a consumer-neutral [secondary_model]

The secondary model becomes a model-domain concept next to default_model so
future consumers beyond subagents can share it: [secondary_model] model /
effort in config.toml, KIMI_SECONDARY_MODEL / KIMI_SECONDARY_EFFORT env
overrides, and the Agent / AgentSwarm per-spawn choice renamed from
"subagent" to "secondary".

* docs: replace "v2 engine only" notes with the concrete effective surfaces

State that [secondary_model], its env overrides, the Agent/AgentSwarm
model parameter, and SYSTEM.md take effect only under kimi web and
experimental kimi -p (the TUI ignores them), and that --agent /
--agent-file are available only under experimental kimi -p. Also drop
the SYSTEM.md claim of parity with --agent/--agent-file, which was
inaccurate: SYSTEM.md is an agent-core-v2 app-domain feature and also
applies under kimi web, while the flags are gated at the CLI.

* fix: review follow-ups for the subagent secondary model

- Drop the "cheaper" claim from the Agent/AgentSwarm model parameter
  descriptions and the advertised model list — the secondary model is
  not necessarily the cheaper one.
- Downgrade the changeset to patch, note the kimi web / experimental
  kimi -p effective surface, and tighten the wording.
- Remove the onWillRestore stub fields from two lifecycle stubs; they
  belong to upcoming lifecycle work, not to this change.

* fix(agent-core-v2): prevent ghost agents from invalid model bindings

* fix: narrow the secondary-model error hint to missing-alias failures

The model catalog's not-configured throw now carries details.model, and
wrapSubagentModelError only decorates errors whose details.model matches
the bound model. Malformed [models.*] entries and unrelated config.invalid
failures during agent creation pass through untouched instead of being
misattributed to an invalid secondary-model alias.

* fix: mark subagent resume semantics as breaking

* chore(agent-core-v2): follow header-only comment convention

* fix(agent-core-v2): await agent restore preparation

* feat(agent-core-v2): support agent model preferences

* Update subagent-secondary-model.md

Signed-off-by: 7Sageer <12210216@mail.sustech.edu.cn>

* fix: validate secondary models before agent creation

* Delete .changeset/secondary-model-startup-warning.md

Signed-off-by: 7Sageer <12210216@mail.sustech.edu.cn>

* Add secondary_model config section for subagents

Individual agents can override this via the new `model_preference` field in their agent file.

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

* feat(agent-core-v2): support override patches in [secondary_model]

The recipe is now `model` plus the flattened ModelOverride field set.
With any patch field set, a config overlay synthesizes a derived
registry entry (base copy, patch merged into overrides, aliases
dropped) so subagent spawning rides the standard effectiveModelConfig
merge; with none, subagents bind the pointed entry directly.
`default_effort` replaces `effort` (KIMI_SECONDARY_EFFORT rebinds)
and doubles as the explicit subagent thinking; unset, thinking
resolves naturally instead of inheriting the caller. The overlay
strips the derived entry (and any defaultModel pointer to it) from
writes, and the kap-server GET /models route hides it from pickers.

* fix(agent-core-v2): fire section events for overlay-rewritten domains

rebuildEffective only committed the caller-named domains, so a
ConfigEffectiveOverlay or section env binding that rewrote a sibling
domain (setting [secondary_model] synthesizes a derived models entry;
removing the recipe retracts it) left consumers of the models section
stale. Widen the commit candidates with every domain the recompute
actually changed; commit() deepEqual-guards each candidate, so the
widening costs nothing.

* feat(agent-core-v2): gate secondary model behind experimental flag

* Update subagent-secondary-model.md

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

---------

Signed-off-by: 7Sageer <12210216@mail.sustech.edu.cn>
Signed-off-by: 7Sageer <sag77r@hotmail.com>
2026-07-24 12:16:53 +08:00
7Sageer
5fdbdb4a22
feat: configure web search/fetch services via KIMI_WEB_* env vars (#2096)
* feat: configure web search/fetch services via KIMI_WEB_* env vars

KIMI_WEB_SEARCH_BASE_URL / KIMI_WEB_SEARCH_API_KEY and
KIMI_WEB_FETCH_BASE_URL / KIMI_WEB_FETCH_API_KEY overlay the
[services] config section field by field in both engines (env wins
over config.toml), so the WebSearch and FetchURL backends can be
pointed at a Moonshot service without OAuth login. The v2 engine now
also honors the explicit [services.moonshot_fetch] section
(config > managed OAuth > local), which it previously parsed but
never consumed.

* fix(agent-core-v2): guard stripServicesEnv against clearing the services section

config.replace(SERVICES_SECTION, undefined) — the logout deprovisioning
path in authService — passes undefined straight into the section strip,
and the composed stripServicesEnv dereferenced it (value[key]), throwing
TypeError and aborting logout mid-cleanup. Add the same isPlainObject
guard the other strip implementations (stripProvidersEnv,
stripEnvBoundFields) already have, plus a regression test that also
locks in the env overlay staying effective after the file value is
cleared.

* style(agent-core-v2): follow header-only comment convention

* fix: isolate env web service credentials

* Add env vars for web search and fetch services

The new environment variables `KIMI_WEB_SEARCH_BASE_URL`, `KIMI_WEB_SEARCH_API_KEY`, `KIMI_WEB_FETCH_BASE_URL`, and `KIMI_WEB_FETCH_API_KEY` take priority over the corresponding fields in `config.toml`. The `kimi web` backend now honors the `[services.moonshot_fetch]` config section.

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

---------

Signed-off-by: 7Sageer <sag77r@hotmail.com>
2026-07-23 17:36:16 +08:00
7Sageer
527d485d92
feat: add global default MCP server timeout configs (#2065)
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 / Release (push) Waiting to run
Release / Deploy docs (push) Blocked by required conditions
Release / Publish native release assets (push) Blocked by required conditions
* feat: add global default MCP server startup timeout config

Add a `[mcp] startup_timeout_ms` config.toml section with a
`KIMI_MCP_STARTUP_TIMEOUT_MS` env override as the global default MCP
server connection (startup + tool discovery) timeout. Precedence:
per-server `startupTimeoutMs` in mcp.json > env var > config.toml >
built-in 30s default.

* feat: add global default MCP tool call timeout config

Extend the `[mcp]` section with `tool_timeout_ms` and the
`KIMI_MCP_TOOL_TIMEOUT_MS` env override as the global default for
single MCP tool calls, mirroring the startup timeout: a per-server
`toolTimeoutMs` in mcp.json still wins, and unset entries fall back to
the SDK built-in 60s default.

* chore: shorten the mcp timeouts changeset

* feat(agent-core): add global default MCP server timeout configs

Port the `[mcp]` section (`startup_timeout_ms` / `tool_timeout_ms`)
and the `KIMI_MCP_STARTUP_TIMEOUT_MS` / `KIMI_MCP_TOOL_TIMEOUT_MS` env
overrides to agent-core (v1), mirroring the v2 semantics: per-server
fields in mcp.json > env vars > config.toml > built-in defaults. The
v1 TOML loader gains explicit `mcp` read/write mappings, and both
connection-manager construction sites (Session, testGlobalMcpServer RPC)
pass the resolved defaults through.

* fix(agent-core): validate and apply MCP timeout defaults

* fix: propagate MCP startup timeout to SDK requests

* refactor(agent-core-v2): resolve MCP default timeouts at connect time

Keep the session connection manager synchronously lazy instead of gating
its existence on config readiness: resolveDefaultTimeouts is read from the
mcp config section at each (re)connect, so AgentMcpService's eager
construction stays unconditionally safe and reconnects pick up changed
preferences. The initial connect still awaits config.ready for a
deterministic snapshot. Also restore the ISessionMcpService method docs,
bump the changeset to minor, and fix the v1 env-parse comment.

* chore(agent-core-v2): regenerate config manifest for the mcp section

* Add global default MCP server timeouts configuration

Specify the new global default MCP server timeouts in both the config file and environment variables.

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

---------

Signed-off-by: 7Sageer <sag77r@hotmail.com>
2026-07-23 12:41:08 +08:00
Haozhe
8250e590f3
docs(cron): drop references to the non-existent kimi resume command (#2050)
* docs(cron): drop references to the non-existent `kimi resume` command

- point user docs at the real resume command `kimi --session`
- reword cron tool descriptions and code comments in agent-core and
  agent-core-v2 to describe session resume without naming a subcommand

* chore: add changeset for cron docs wording fix
2026-07-22 16:59:08 +08:00
Kai
ba921ca531
fix: gate always-thinking inference to OpenAI wires, plus catalog review follow-ups (#2036)
* fix: gate always-thinking inference to OpenAI wires, plus review follow-ups

- catalog: strip the inferred alwaysThinking marker on non-OpenAI wires so
  Claude/Gemini keep their native off; verified against live models.dev
- v1 provider-manager: honor per-alias baseUrl on kimi/google-genai/vertexai
- thinking: rewrite the PHASE-6 contract comment, drop three dead
  cannot-disable warning branches, normalize requested effort in v1 to
  match v2
- tests: input-cap compaction preference in both engines, kap-server WS
  status cap, v2 legacy status input cap
- docs + changeset: new model fields, catalog-refresh behavior, kap-server

* fix: strip always-thinking only where the wire encodes a true off

The previous gate kept the marker only on the OpenAI wires, which wrongly
stripped it from Gemini 3 on the Google wires: its floor is
thinkingLevel MINIMAL with suppressed thoughts — still reasoning — so an
Off option there would be a lie. The criterion is now the wire's encoding,
not its family: strip only on anthropic and kimi, the two wires with a
protocol-level `thinking: {type: 'disabled'}` that the catalog's effort
list can never show. Verified against live models.dev data: google now
marks exactly the gemini-3 family (10), anthropic and moonshotai stay 0.

* docs(agent-core-v2): move the strict-validation contract into the thinking.ts file header

The scoped guide keeps comments in the top-of-file block only; the
function JSDoc shrinks to a short what-it-answers note matching its
neighbors. No behavior change.
2026-07-22 13:43:10 +08:00
Kai
b5efba7abc
fix: consume the model metadata declared by the models.dev catalog (#2015)
* fix: stop advertising Claude thinking efforts for non-Claude models

Models served over the Anthropic protocol whose names carry no Claude
marker (e.g. a catalog-imported Kimi K3) no longer inherit the latest
Opus effort list, so the model selector stops offering levels the model
does not accept. The models.dev catalog import now also parses
reasoning_options and records the declared effort levels on the model
alias, so K3 offers its real levels (low / high / max).

* fix: consume deprecated, override, and input-limit metadata from the models.dev catalog

- Models declared status=deprecated in the catalog are no longer
  offered for import.
- Per-model provider overrides on gateway providers (an npm package
  targeting an Anthropic SDK plus a usable endpoint) now land as alias
  protocol and base_url, so those models are served over the right
  protocol and endpoint; overrides without a usable URL are skipped.
- A declared limit.input now sizes the context budget instead of the
  larger total context window (e.g. gpt-5: 272k instead of 400k).

The model alias schema gains an optional base_url field (not accepted
in overrides) that Anthropic wire resolution prefers over the
provider-level base URL.

* fix: honor thinking-disable semantics and the OpenAI-compatible fallback in catalog imports

- reasoning_options 'none' is the model's off encoding: off_effort flows
  from the catalog through the model alias to the OpenAI wire providers,
  so turning thinking off sends 'none' instead of omitting the effort
  field; models with effort levels but no way to disable thinking are
  imported as always_thinking and no longer offer an Off option.
- Bare Claude family aliases (e.g. sonnet-latest) recover the inferred
  Anthropic effort profile; v2 comment conventions restored.
- Providers whose SDK the catalog does not type now fall back to the
  OpenAI-compatible wire (with a visible "guessed" note) instead of
  being refused; imports lacking a usable endpoint ask for one
  (--base-url on the CLI, a prompt in the TUI). Proprietary SDKs
  (Amazon Bedrock), unrecognized explicit types, and env-placeholder
  URLs are refused with a clear reason.

* fix: align catalog imports with the reference models.dev consumer

- A JSON null tier in declared effort values is now read as the 'none'
  off-encoding (previously such models were wrongly imported as
  always-thinking with no way to turn reasoning off).
- Alpha-status models are filtered out alongside deprecated ones.
- Models whose per-model provider override targets a wire that cannot be
  expressed per-model (e.g. Claude on google-vertex, whose wire here is
  Gemini-mode Vertex, or gpt entries on an Anthropic provider) are
  skipped instead of being imported under the silently wrong protocol.
- interleaved: true no longer pins reasoning_content: the provider's
  default three-field scan is wider and the pinned key only narrowed
  reasoning parsing for gateways answering with another field name.

* fix: require endpoints for Anthropic-compatible catalog imports and honor --base-url

- catalogProviderNeedsBaseUrl now covers the Anthropic wire: a
  non-official Anthropic-compatible vendor without a concrete catalog
  endpoint (e.g. google-vertex-anthropic) must supply --base-url / the
  TUI prompt instead of silently falling back to the default Anthropic
  endpoint.
- --base-url now takes precedence over the catalog-declared endpoint,
  and an empty --base-url is rejected instead of persisting a blank
  endpoint.

* fix: enforce always-on thinking on every wire and refuse Cohere at import

A model that declares always_thinking (e.g. a catalog-imported gpt-5)
no longer resolves to a dishonest off state via thinking.enabled=false
or an SDK/ACP off request: resolution clamps to the model's default
effort on every wire instead of letting upstream keep reasoning while
the UI reports Off. The Anthropic warn-and-send path for unlisted
effort levels is unchanged. Cohere's proprietary SDK joins Amazon
Bedrock on the import-refusal list instead of being guessed as
OpenAI-compatible.

* fix: harden catalog import edge cases

- An explicit but unrecognized catalog type is now refused before
  npm/id inference, so a future catalog protocol is never silently
  miswired through the OpenAI fallback.
- User-supplied --base-url values for Anthropic-wire providers get the
  same trailing-/v1 normalization as catalog endpoints, avoiding
  /v1/v1/messages requests.
- The TUI import prompt rejects env-placeholder base URLs like the CLI
  does.

* fix: await the floating assertion promise in the catalog add CLI test

* refactor: unify catalog import resolution into a single decision function

Wire-type inference, the OpenAI-compatible fallback, proprietary-SDK
refusal, endpoint adaptation, and the base-URL requirement are now
produced together by resolveCatalogImport, one pure resolver consumed
by both the CLI and the TUI — replacing the cooperating predicates
(inferWireType, isGuessedWireType, catalogProviderNeedsBaseUrl) whose
permutations kept producing edge cases. No behavior change.

* fix: close configured-off clamp hole, keep inferWireType compat, carry same-wire override endpoints

- A configured thinking.effort = "off" no longer bypasses the
  always-on clamp: it is treated as absent and the model default
  applies, mirrored on both engines.
- The previously public inferWireType stays as a deprecated
  compatibility wrapper over resolveCatalogImport so existing SDK
  consumers do not break on a patch release.
- Catalog model overrides that stay on the provider's wire but declare
  their own endpoint now persist it on the alias (and the v1 OpenAI
  wire branches honor alias-level base URLs like the Anthropic branch).

* fix: split total window from input cap and close override/endpoint gaps

- max_context_tokens once again means the total context window (used by
  completion budgeting); a model's declared input limit is tracked as
  max_input_tokens, which compaction, context-splice and usage-ratio
  checks prefer — fixing the over-clamping introduced when the input
  cap was stored as the context budget.
- A catalog endpoint declared only as an env placeholder now always
  produces needs-base-url (official SDK included), so credentials are
  never sent to the public vendor host by default.
- api-only per-model overrides are honored as same-wire endpoint
  changes; overrides targeting another known but inexpressible wire
  (e.g. google-genai on an OpenAI gateway) are skipped; same-wire
  models whose declared endpoint is an unusable placeholder are
  skipped instead of silently rerouted.

* style: drop a function-level comment from the v2 thinking resolver

* chore: consolidate the PR's changesets into two user-facing entries
2026-07-22 01:23:31 +08:00
Kai
37eda4e59a
feat(config): add env overrides for loop control and background task limits (#1993)
* feat(config): add env overrides for loop control and background task limits

Add three operational environment overrides, resolved as env > config.toml
> default in both engines (agent-core and agent-core-v2), matching the
existing KIMI_IMAGE_MAX_EDGE_PX / KIMI_SUBAGENT_TIMEOUT_MS pattern:

- KIMI_LOOP_MAX_STEPS_PER_TURN overrides loop_control.max_steps_per_turn
- KIMI_LOOP_MAX_RETRIES_PER_STEP overrides loop_control.max_retries_per_step
- KIMI_CODE_BACKGROUND_MAX_RUNNING_TASKS overrides background.max_running_tasks

Invalid values are ignored and fall back to the config value. The v2 engine
resolves them through config section env bindings (effective-only, never
persisted); the v1 engine resolves them at the consumption point.

* fix(config): strip env-bound fields before persisting config writes

Environment overrides resolved into the effective config could be echoed
back through IConfigService.set/replace (e.g. GET then POST
/api/v1/config) and persisted into config.toml, outliving the env var.
This affected the pre-existing image / subagent / keep-alive bindings as
well as the new loop control and max-running-tasks bindings.

Extend ConfigStripEnv with a getEnv parameter and add a shared
stripEnvBoundFields helper: while a field's env var is set, writes
restore the field's raw on-disk value (or drop it) instead of persisting
an echoed env value; when unset, normal writes persist. Register it for
the loopControl, task/background, image, and subagent sections.

Also fix ConfigService.stripEnv looking up rawSnake by the camelCase
domain key; on-disk sections are keyed snake_case.

* fix(config): honor binding parsers when stripping env fields on persist

An invalid env value (e.g. KIMI_LOOP_MAX_STEPS_PER_TURN=abc) is ignored
on the read path but still marked the field env-owned on the write path,
so a config write for that field was silently dropped.
stripEnvBoundFields now derives the guard from the section's envBindings
and skips fields whose env value fails the binding's parse, so invalid
env values are ignored on both paths — and the duplicated field/env
descriptor list is gone.

Also drop function-level comments added beside helpers; agent-core-v2
keeps comments solely in the top-of-file block, so the strip semantics
now live in the config.ts / configService.ts headers.

* fix(config): re-apply env overlays from the env-free base on every read

Two follow-ups from review:

- ConfigService.get()/getAll() re-applied section env bindings on the
  already-overlaid effective cache (and get() mutated it in place), so a
  valid override degraded to invalid or unset kept serving the stale
  value until the next reload — and an echoed stale value could then be
  persisted by a config write. Reads now recompute from a cached
  env-free validated base, so degraded or removed env values fall back
  to the file immediately.
- stripEnvBoundFields restored env-owned fields from the raw snake
  sub-object, missing values persisted under legacy keys (e.g.
  max_steps_per_run). Section stripEnv now receives the env-free,
  fromToml-normalized raw base, so legacy aliases are honored.

* test(kap-server): retry temp-home cleanup in auth tests

auth.test.ts removed its temp home with a plain recursive rm, which races
late async writers in the server shutdown path and flakes with ENOTEMPTY
(seen on main CI and locally on main). Harden the cleanup with the same
maxRetries/retryDelay options sessions.test.ts already uses.

* chore(changeset): consolidate the env-override changesets into two entries

One feature entry (loop/background env overrides, both engines plus the
CLI) and one fix entry (env values persisting on writes and sticking
after degrade/unset, agent-core-v2 plus the CLI).

* fix(config): clear fully-stripped sections and refresh overlay domains on get()

Two follow-ups from review:

- stripEnvBoundFields returned an empty object when every written field
  was env-owned, so a section with a non-empty default (e.g. subagent)
  stored raw = {} and the default stopped applying until the next
  reload. A fully stripped result now clears the raw section instead.
- get(domain) only recomputed env overlays for sections with env
  bindings, so domains written solely by a ConfigEffectiveOverlay
  (models / defaultModel from KIMI_MODEL_NAME) kept serving stale cached
  values after the env changed. get() now derives every non-memory
  domain from the fresh env-free base, matching getAll().

* test(config): drive the get() overlay freshness test with an inline overlay

The previous version imported the model env overlay to activate it, which
the CI runners failed to resolve from this test file (both tsgo and
vitest, while the same specifier resolves elsewhere — not reproducible
locally). An inline ConfigEffectiveOverlay double exercises the same
ConfigService contract with a tighter seam and no module dependency.

* fix(config): preserve unknown fields on full strip and clone nested env targets

Two follow-ups from review:

- stripEnvBoundFields cleared the raw section whenever the stripped
  result was empty, so an env echo write could drop unknown
  forward-compatible fields from the TOML table. An emptied section now
  keeps its raw table while the env-free base still holds other fields,
  and is cleared only when nothing remains (defaults keep applying).
- applyEnvBindings reused nested child objects in place, so a nested
  env binding on a persistable key would mutate the env-free validated
  base and serve stale values after the env var is removed. Children
  are now cloned before descending.

* fix(config): keep the env-free base when a strip leaves nothing to persist

Returning {} for a fully stripped write stored the empty object into the
raw layer while the TOML table survived via rawSnake, so the bases
diverged; a second echo write then saw an empty base, cleared the
section, and deleted the table — dropping unknown forward-compatible
fields. When nothing persistable remains, the write is now a no-op for
the section (the env-free base is kept as-is), and the section is
cleared only when the base is empty.

* fix(config): revalidate stripped results so restored raw values never persist

A strip may restore on-disk values from the unvalidated raw base (e.g.
an env-masked invalid field), smuggling them past the merge-time
validation into the stored config: the section would then fail the next
buildValidated pass, dropping accompanying valid edits from the runtime
while the invalid value stayed persisted. set() now revalidates the
stripped result — discarding the parse output so unknown fields survive
— and rejects the write instead, matching replace().
2026-07-21 19:49:00 +08:00
7Sageer
ce0e3ceb04
feat: support custom agent files (#1735)
Some checks are pending
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
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 / build (push) Waiting to run
CI / test (1) (push) Waiting to run
CI / typecheck (push) Waiting to run
* feat: support custom agent files

Discover Markdown+frontmatter agent definitions from user, project, and configured directories; merge them into a session-level profile catalog (priority: builtin < user < extra < project < explicit). Custom agents work as subagents via the Agent tool and as the main agent.

- agent-core-v2: new agentFileCatalog domain (discovery/parsing/profile factory, mirroring skillRoots/parseFrontmatter) and sessionAgentProfileCatalog merged view; AgentProfile.tools becomes optional (undefined = all tools) and gains disallowedTools deny list, evaluated in profileService.isToolActive and persisted in session wire records so resume keeps the gate even if the file is gone
- CLI: restore --agent/--agent-file for the v2 print runner (KIMI_CODE_EXPERIMENTAL_FLAG); the v1 TUI rejects them with a clear v2-only error
- kap-server/protocol: optional profile field on prompt submission with first-bind semantics (same-name no-op, different name rejected)
- docs: custom agents section (en/zh) + config/CLI references + changeset

* chore: shorten custom agents changeset

* fix(agent-core-v2): stringify caught errors in agent catalog log calls

* docs: drop v2 engine notes from custom agents docs

* fix: align custom agent binding semantics across engine and edges

- agent-core-v2: bind now owns the first-bind invariant — switching
  profiles after bind throws profile.already_bound (checked again in the
  synchronous segment before the first wire dispatch, so concurrent binds
  cannot both pass); unknown names throw profile.unknown; same-name
  rebinds keep the persisted thinking effort.
- kap-server / CLI: edges degrade to error mapping / same-name no-op
  instead of their own divergent guards.
- agent files: reject non-string mode values, honor disallowedTools in
  the append-mode Skill probe, pass --agent-file through unresolved so
  the engine can expand ~, reject empty --agent-file values.
- session catalog: ready is recoverable via reload() after a fatal
  source failure, and agent-file discovery is kicked at session
  materialize so resumed sessions see file agents from the first turn.
- docs: first-bind semantics, name: agent override, tools: [] meaning,
  --agent-file last-wins.

* fix: tighten custom agent behavior

* fix: address custom agent review findings

* fix(agent-core,agent-core-v2): keep active-tool wire records replayable by v1

Binding a profile without a tool allowlist (the default for file-defined
custom agents) persisted a tools.set_active_tools record with no `names`
key. v1 clients discover v2 sessions through the shared session index and
replay newer wire versions without migration, so the record crashed v1
resume with a TypeError and wedged the session permanently.

The v2 engine no longer writes the record when the base set is already
"every tool active" (its absence encodes the same state), and v1 replay
now skips records that lack `names` as defense in depth for wires already
written by preview builds. A same-name rebind that resets an allowlist to
"all tools" has no v1-safe encoding and is left as a documented gap (no
production caller today); a future tools.reset_active_tools Op is safe
because v1 silently no-ops unknown record types.

* fix(agent-core-v2): tolerate unreadable directories in agent-file discovery

A single unreadable subdirectory (EACCES anywhere in the scanned tree)
previously aborted the whole discovery pass, zeroing every agent of that
source on every session start with one path-less warning. The walker now
skips-and-warns per directory below the root (mirroring the skill
discovery it parallels), root-level failures are isolated per root so one
bad root no longer takes its siblings down, and only a genuinely transient
whole-fs outage (os.fs.unavailable) still propagates so the session
catalog keeps its previous contribution. Source-level warnings now name
the offending path, and repeated skip warnings are capped with a summary
that samples the suppressed paths.

Also consolidates the path primitives (~ expansion, base-relative
resolution, realpath type probes) shared by the root resolvers, the
walker, and the explicit-file source into agentFileCatalog/paths.ts, and
tightens parser diagnostics: frontmatter null is treated as absent, and a
present-but-wrong-typed name/description reports a type error instead of
"missing".

* fix(agent-core-v2): warn when a same-name builtin suppresses a file profile

A directory-discovered agent file colliding with a builtin profile
without override: true was silently dropped at merge time. The suppression
now logs a warning naming the profile and the opt-in.

* refactor(agent-core-v2): pass skillActive explicitly to renderSystemPrompt

The third parameter was a full tool list used only for includes('Skill'),
which forced the agent-file profile factory to answer a boolean question
with sentinel lists. The template now takes an explicit skillActive flag;
a skillActiveFor helper keeps builtin call sites derived from their tool
arrays.

* refactor(agent-core-v2): fall back to the configured default model in bind

BindAgentInput.model is now optional: the engine resolves a missing model
against the configured defaultModel and throws model.not_configured when
neither is set, so edges no longer each re-implement the fallback.

* fix(agent-core-v2,kap-server): reject unsupported thinking atomically at first bind

A REST prompt carrying profile + an unsupported thinking effort bound the
session first and failed setThinking after, wedging the session on an
identity the user never successfully used. The effort is now validated up
front when the caller marks it as an explicit request (strictThinking):
the bind rejects before any await or state mutation, and the requested
effort rides along in the bind instead of a separate setThinking. Internal
spawn/fork paths pass inherited thinking without the flag and keep the
previous clamp behavior — a persisted effort that drifted out of the
model's support list must not break subagent spawning. The route's
now-redundant model fallback is dropped in favor of the engine-side
default.

* fix(agent-core-v2): await the agent profile catalog at session materialize

The catalog's ready promise was only kicked, so a resumed session's first
turn could render the Agent tool description without the file-defined
agent types. Discovery is local-fs and cheap, so materialize now awaits
it; ready only rejects for a fatal explicit-source error, which is exactly
the case that should fail fast. A failure there now also removes and
disposes the half-materialized handle instead of leaving it registered in
the session cache.

* test: cover the --agent-file fatal path and tidy profile registration hygiene

The v2 print CLI now has a test asserting an invalid --agent-file fails
before any turn. The denylist profiles in binding.test.ts register in a
beforeAll (idempotent, scoped to the describe's run window) instead of at
module scope during collection.

* docs: align custom agent docs with v2-engine gating

--agent/--agent-file are rejected without the v2 engine, so restore the
requirement in the Agents and Command Reference pages (and use
KIMI_CODE_EXPERIMENTAL_FLAG=1 in the examples), note that tool lists only
shape model-visible disclosure (permission rules are the enforcement
layer), and remind authors of delegation-bound agents to state the
handoff contract in the prompt body.

* test(agent-core-v2): revert unrelated style churn in fs/workspace tests

Keep these files' diff limited to the realpath fakes the feature needs;
the lint-preference rewrites belong to a separate cleanup.

* feat(agent-core-v2): add permanent system prompt override via SYSTEM.md

Read $KIMI_CODE_HOME/SYSTEM.md on every startup and inject it as the default main-agent profile (name "agent", override: true), replacing the builtin default system prompt while inheriting builtin tools and description. Missing or empty files are ignored; unreadable files warn and fall back to the builtin profile.

The body supports variable substitution (${skills}, ${agents_md}, ${cwd}, ${cwd_listing}, ${os}, ${shell}, ${now}); unknown variables pass through verbatim.

Priority: --agent-file / --agent / project override > SYSTEM.md > same-name user-scope scan files.

* feat(agent-core-v2): gate tools globally and accept session disabledTools

Add a [tools] config section: "enabled" acts as a global allowlist (empty = unconstrained), "disabled" as a denylist applied on top, both intersected with the active profile's policy in isToolActive (mcp glob supported).

Plumb a session-persistent disabledTools parameter through the stack: v2 RPC PromptPayload, REST "disabled_tools" (protocol and kap-server parallel schemas), klient contract/facade, and node-sdk. The server applies it via profileService.setSessionDisabledTools, which replaces the client-owned denylist, keeps the profile's own deny, persists across resume, and rejects calls before a profile is bound with profile.not_bound (mapped to 40001). v1 core-api gains a type-only field and ignores it.

* fix: enforce session tool policy across agents

* fix(agent-core-v2): enforce tool policy at execution

* fix(agent-core-v2): align subagent tool descriptions with policy

* fix(agent-core-v2): harden custom agent policy state

* fix(agent-core-v2): harden custom agent lifecycle

* refactor(agent-core-v2): persist profile binding in a single profile.bind record

* fix(agent-core-v2): skip unreadable paths during agent file discovery

* fix(agent-core-v2): exempt select_tools from the executor policy guard

- share one composed profile/global/session tool-policy evaluation between
  the executor gate and prompt rendering instead of two verbatim copies
- tolerate context-build failure in system prompt refresh instead of
  rejecting callers (config watcher void-fire, session policy fan-out)

* test(agent-core-v2): resolve profile and tool-policy SUTs by interface

- drop the Object.assign patching of tool-policy methods onto the shared
  profile service; rename describes so the SUT ownership is accurate
- classify profile.bind as v2-only with the accepted v1-replay tradeoff
  documented, un-red the wire vocabulary guard test
- cover the select_tools guard exemption with an executor-level test

* fix(agent-core-v2): enforce explicit select_tools policy

* feat(agent-core-v2): accept Claude-style tool lists and rename agent-file mode to promptMode

* feat(agent-core-v2): unify prompt templating on ${var}

- Replace the nunjucks renderer with a single ${var} regex renderer
  (unknown placeholders pass through verbatim) and drop nunjucks from
  agent-core-v2.
- Merge the variable tables into one catalog shared by the builtin
  system.md, SYSTEM.md, and agent file bodies; adds additional_dirs_info
  plus code-composed blocks (windows_notes, additional_dirs_section,
  skills_section).
- Replace the agent-file promptMode field with ${base_prompt}: bodies
  are always rendered as templates, and ${base_prompt} expands to the
  effective default profile prompt (honoring the SYSTEM.md override).
- Migrate the builtin system.md, goal reminders, compaction instruction,
  and tool description templates to the same syntax.

* docs: complete agent priority chain and link SYSTEM.md precedence

* test: fix invalid custom agent fixture

* fix(cli): reject multiple agent selectors

* feat(agent-core-v2): add subagents allowlist to agent files

* fix(agent-core-v2): persist the subagent allowlist in the profile binding

The delegation allowlist now rides the profile.bind record like the tool
denylist, so a resumed session keeps enforcing it even when the source
agent file was deleted or changed. Agent/AgentSwarm resolve the caller's
allowlist from the persisted binding data instead of looking the profile
up in the live catalog.

* feat(agent-core-v2): warn on tool patterns that never match

Profile bind/apply and [tools] config changes now statically flag
entries that can never activate anything — wildcards without the mcp__
prefix (a bare * in an allowlist disables everything, in a denylist
nothing), incomplete mcp__ literals, and names no registered or
builtin-profile tool has — via a tool-pattern-no-match warning event,
once per pattern, instead of letting the tool set silently shrink. The
known-name vocabulary is the live registry plus literal names from the
builtin profiles, so flag-gated tools stay known and a typo in one agent
file cannot legitimize the same typo in another.

* docs: align --agent-file docs with the single-selector CLI

The flag accepts exactly one file and conflicts with --agent, but the
docs still described the earlier repeatable, composable design.

* docs: note the agent-file trust model and never-matching tool patterns

Spell out that project-scoped agent files can replace the default main
agent's whole system prompt (unlike AGENTS.md reference injection), and
list the three tool-pattern shapes that never match and now raise a
warning.

* chore: slim changeset wording to user-facing language

Drop wire record names, enforcement mechanics, and template syntax from
the entries; split the v1 resume fix into its own patch changeset; add a
patch entry for the tool-pattern warnings.

* chore: shorten changeset entries to one-line summaries

* Delete .changeset/v1-resume-v2-sessions.md

Signed-off-by: 7Sageer <12210216@mail.sustech.edu.cn>

* test: drop class-instance spread in sessionLifecycle test stub

* chore: clear the comments

---------

Signed-off-by: 7Sageer <12210216@mail.sustech.edu.cn>
2026-07-21 15:01:43 +08:00
Kai
3086e47039
fix: unify YOLO and Auto permission mode descriptions across surfaces (#1867)
Some checks are pending
CI / test-windows (push) Waiting to run
CI / lint (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 / build (push) Waiting to run
CI / typecheck (push) Waiting to run
Nix Build / Check flake.nix workspace sync (push) Waiting to run
Nix Build / nix build .#kimi-code (push) Blocked by required conditions
Release / Release (push) Waiting to run
Release / Deploy docs (push) Blocked by required conditions
Release / Native release artifact (push) Blocked by required conditions
Release / Publish native release assets (push) Blocked by required conditions
* fix(tui): correct YOLO and Auto permission mode descriptions

* fix: unify YOLO and Auto permission mode descriptions across CLI, ACP, web, and docs

* docs: correct YOLO and Auto mode descriptions in the interaction guide

* fix: correct YOLO mode notices in session replay and vscode extension

* feat(vscode): rename /afk command to /auto, keeping afk as hidden alias

Also correct the stale 'afk' mode reference in the built-in MCP config
skill guidance of both agent engines.

* fix(vscode): forward engine approval requests instead of blanket-approving them

The extension-level approval handler auto-approved every request when
legacy yolo/afk was on, silently swallowing the sensitive-file,
plan-review, and ask-rule prompts the engine yolo mode still sends.
Forward every request to the user and let the engine permission mode do
the auto-approving, matching TUI and web behavior.
2026-07-18 02:09:31 +08:00
liruifengv
bfecd0128f
feat: refresh model list for API-key providers at the managed endpoint (#1824)
* feat: refresh model list for API-key providers at the managed endpoint

* docs: simplify changeset wording

* docs: add kimi-code/k3 to the default config example

* fix: clear stale default model when a refresh drops its alias

* fix: clear refresh defaults via replace; set(undefined) cannot delete

* docs: drop the auto model refresh paragraph from providers page

* docs: simplify changeset wording
2026-07-17 17:17:58 +08:00
Haozhe
a74ab44ac7
feat: raise default per-step LLM retry budget to 10 attempts (#1740)
* feat: raise default per-step LLM retry budget to 10 attempts

The default of 3 attempts only produced ~1.5s of backoff (0.5s/1s), so
sustained provider overload (429) surfaced to the user almost
immediately. With 10 attempts the exponential ramp (500ms base, x2,
32s cap, 25% jitter) waits out multi-minute overload windows before
failing the turn. Applies to both agent-core (chatWithRetry) and
agent-core-v2 (stepRetry service); loop_control.max_retries_per_step
still overrides.

* test(agent-core): pin retry-dependent tests to the new default budget

- error-paths: the warn-log attempt field now reads 1/10 with the
  default 10-attempt budget
- goal-session pause tests: every LLM call throws a retryable error, so
  the default 10-attempt backoff (~2min) blew the 5s test timeout; pin
  maxRetriesPerStep=1 since the pause behavior does not depend on the
  retry count
2026-07-15 15:24:17 +08:00
Haozhe
5d6ff022b1
feat(agent-core): drop default timeouts for print-mode background tasks (#1737)
* feat(agent-core): drop default timeouts for print-mode background tasks

- add `bash_task_timeout_s` config under [background] (0 = no timeout),
  covering the background Bash default and the re-arm after a foreground
  command is moved to the background on timeout
- allow `[subagent] timeout_ms = 0` (no timeout) and thread it through
  Agent/AgentSwarm task registration and the swarm batch timer
- fill both with 0 in print-mode config defaults so `kimi -p` never kills
  background work by wall-clock and only the model stops a task;
  interactive defaults are unchanged
- sync the Bash tool description/parameter text with the effective
  default and update user docs (en/zh) plus changeset

* test(agent-core): satisfy KimiConfig providers requirement in print-defaults tests

* fix(agent-core): clear the foreground deadline on detach when the background timeout is disabled

`detach()` only re-arms when `detachTimeoutMs` is defined, so passing
`undefined` with `bash_task_timeout_s = 0` kept the armed foreground
deadline and a manually detached command was still killed at its
foreground timeout. Pass `0` instead so the reset clears the timer.
Caught by Codex review on #1737.
2026-07-15 14:35:32 +08:00
Kai
d158e0a7ac
fix: resolve and synchronize thinking effort (#1625)
* fix: preserve provider thinking effort values

* fix: resolve Kimi thinking effort fallbacks

* fix: use resolved Kimi effort in v2 requests

* fix: align Kimi effort resolution paths

* fix: synchronize forced Kimi effort state

* fix: synchronize forced Kimi effort in v2 state

* fix: tolerate unresolved models in v2 status
2026-07-14 16:37:03 +08:00
Haozhe
e49b3b8777
fix(agent-core-v2): harden wire restore and background task lifecycle (#1635)
* fix(agent-core-v2): fix wire migration rewrite race and reject unversioned logs (8 files)

- await the migration rewrite in wireRecordService.restore() so restore only resolves once the migrated log is durable and rewrite failures surface
- serialize AppendLogStore.rewrite() behind the log flush so appends arriving during the whole-file replace land after the new content
- reject wire logs missing the metadata envelope (missingWireMetadataError) in restore, session fork, and agent create instead of fabricating a current-version envelope over legacy records

* fix(agent-core-v2): stop background tasks on session close (10 files)

- add IAgentTaskService.stopAllOnExit: suppress terminal notifications, then SIGTERM → grace → SIGKILL for every active task (v1 stopBackgroundTasksOnExit parity, gated by keepAliveOnExit)
- call stopAllOnExit from agentLifecycle.remove before scope disposal, and abort live tasks in AgentTaskService.dispose as a last resort for disposal paths that bypass the graceful close
- honor [task] killGracePeriodMs in the stop grace window and bind the v1 KIMI_CODE_BACKGROUND_KEEP_ALIVE_ON_EXIT env override for keepAliveOnExit

* fix(agent-core-v2): root task persistence at the owning agent's scope

- persist task records under <sessionScope>/agents/<agentId>/tasks/ (v1's
  per-agent layout) so tasks written by older versions are found on resume
- stop one agent's restore from loading, marking lost, or re-notifying
  another agent's tasks
- add a per-agent isolation test covering loadFromDisk + reconcile

* fix(agent-core-v2): address task lifecycle review findings

* fix(agent-core-v2): preserve queued appends across rewrites

* test(agent-core-v2): cover rewrite cutover flush

* fix(agent-core-v2): surface append durability failures

* fix(agent-core-v2): retire released append log buffers

* fix(agent-core-v2): preserve session-level task records

* fix(agent-core-v2): harden append log cutover recovery

* fix(agent-core-v2): suppress only background exit notifications

---------

Co-authored-by: qer <wbxl2000@outlook.com>
2026-07-14 10:02:24 +08:00
liruifengv
83e175399f
feat: auto-background timed-out foreground bash commands (#1591)
* feat: auto-background timed-out foreground bash commands

* fix: discourage blocking TaskOutput waits on background tasks

* test: avoid unsafe string conversion in bash timeout test

* fix: align bash timeout description with auto-background opt-out

* feat(agent-core-v2): auto-background timed-out bash commands

- detach timed-out foreground Bash tasks and re-arm the background deadline
- align TaskOutput guidance with non-blocking background task handling
- add klient SEA end-to-end coverage for the v2 server

* fix(klient): clean up lint errors in auto-background e2e example

---------

Co-authored-by: haozhe.yang <yanghaozhe@moonshot.ai>
2026-07-13 21:08:21 +08:00
qer
f3dd006ab4
docs: update subagent timeout docs for configurable timeout_ms (#1582) 2026-07-12 21:09:17 +08:00
Haozhe
faefad0e29
feat(agent-core): configurable subagent timeout with 2h default (#1562)
* feat(agent-core): make subagent timeout configurable and raise default to 2h

- add `[subagent] timeout_ms` config (env `KIMI_SUBAGENT_TIMEOUT_MS` overrides) to replace the hardcoded 30-minute cap for Agent / AgentSwarm subagents
- raise the default subagent timeout from 30 minutes to 2 hours
- thread the value through tool construction so foreground and background subagents use it, with the timeout message reflecting the effective value
2026-07-12 11:48:06 +08:00
Haozhe
c6e02daf42
feat(background): add print_background_mode steer for multi-turn -p runs (#1497)
* feat(background): add print_background_mode with steer for multi-turn -p runs

- add `[background].print_background_mode` (`exit`/`drain`/`steer`) and
  `print_max_turns`; when unset, falls back to `keep_alive_on_exit = true`
  mapping to `drain`, preserving existing behavior
- core: `Session.handlePrintMainTurnCompleted()` returns `finish`/`continue`;
  in `steer` mode the run stays alive so a background-task completion
  `turn.steer`s the main agent into a new turn (matching background
  subagents), bounded by `print_wait_ceiling_s` and `print_max_turns`
- cli: print driver follows every main turn instead of only the first and
  defers `finish()` until the run quiesces or a limit is hit
- plumb `handlePrintMainTurnCompleted` through node-sdk RPC; docs + tests
2026-07-12 10:47:35 +08:00
qer
c2f27b2a22
docs(changelog): sync 0.23.5 from apps/kimi-code/CHANGELOG.md (#1545)
* docs(changelog): sync 0.23.5 from apps/kimi-code/CHANGELOG.md

* chore: update config model doc

* docs: update config-files example with new models and services

---------

Co-authored-by: liruifengv <liruifeng1024@gmail.com>
2026-07-10 22:24:19 +08:00
Kai
1bf2c9afee
feat: keep image-heavy sessions within provider request-size limits (#1508)
* feat(kosong): classify HTTP 413 request-body-too-large as a dedicated error type

* feat(agent-core): lower default image downscale cap to 2000px and make it configurable

* feat(agent-core): strip media to text markers and retry when the compaction request is too large

* feat(agent-core): cap model-initiated image reads with a configurable byte budget

* feat(agent-core): resend with degraded media when the provider rejects the request body as too large

* test(agent-core): add explicit timeouts to encode-heavy image budget tests

* feat: add WebP decoding support with wasm integration

- Introduced a new WebP decoding module using @jsquash/webp's wasm decoder.
- Implemented functions to decode WebP images and check for animated WebP formats.
- Updated image compression tests to include scenarios for WebP handling, including encoding and decoding.
- Enhanced error handling for API request size limits to accommodate various error messages.
- Updated pnpm lockfile to include new dependencies for WebP encoding and decoding.

* chore(changeset): consolidate this PR's entries into one

* fix(nix): update pnpmDeps hash for merged lockfile

* feat(agent-core): refuse HEIC/HEIF reads with platform-matched conversion guidance
2026-07-09 18:05:14 +08:00
liruifengv
bfdbce593f
feat(kosong): honor explicit anthropic max output override (#1465)
* feat(kosong): honor explicit anthropic max output override

Add claude-opus-4-8 output ceiling and treat explicit max_output_size/KIMI_MODEL_MAX_OUTPUT_SIZE as the final Anthropic max_tokens value. Sync configuration and environment variable docs.

* feat(kosong): drop claude-opus-4-8 ceiling

Revert the newly added claude-opus-4-8 default output ceiling while keeping explicit max_output_size overrides for Anthropic.
2026-07-07 15:23:42 +08:00
Kai
25a655cf88
feat(agent-core): enable Preserved Thinking by default on the Anthropic provider (#1432)
* feat(agent-core): enable Preserved Thinking by default on the Anthropic provider

Default thinking.keep to "all" for the Anthropic provider (Claude and Kimi in Anthropic-compatible mode) while Thinking is on, via a context_management clear_thinking_20251015 edit, mirroring the Kimi default. Reuses [thinking] keep and KIMI_MODEL_THINKING_KEEP (env > config > default "all"); off-values disable it.

* feat(kosong): route Anthropic Preserved Thinking through the beta Messages API

Force the beta endpoint (client.beta.messages.create) when thinking.keep is enabled, since clear_thinking_20251015 is only honored there. Also prepend clear_thinking to any existing context-management edits (for example clear_tool_uses) instead of replacing them, keeping it first as Anthropic requires when combining edits.

* docs: clarify Anthropic beta endpoint and compaction keep behavior

Note in code comments and bilingual docs that enabling Anthropic Preserved Thinking routes requests to the beta Messages API (client.beta.messages.create), with keep=off as the escape hatch back to the standard endpoint. Correct the resolveThinkingKeep comment to reflect that compaction shares ConfigState.provider and intentionally carries the same keep.

* test(kosong): cover Anthropic beta endpoint (streaming and forced betaApi)

Add a streaming beta-endpoint capture and a test that withThinkingKeep forces the beta endpoint even when constructed with betaApi: false, pinning down the documented behavior.
2026-07-06 23:45:17 +08:00
Kai
79b360c96a
feat(thinking): enable Preserved Thinking by default for kimi models (#1417)
Default `thinking.keep` to "all" when Thinking is on so prior `reasoning_content` is kept across turns. Add `[thinking] keep` to config.toml and keep `KIMI_MODEL_THINKING_KEEP` as an override (env > config > default); off-values disable it.
2026-07-06 15:27:08 +08:00
Kai
02da587795
feat(cli): wait for background subagents before exiting kimi -p (#1347)
* feat(agent-core): guide the model away from repeating denied or failed tool calls

- system.md: add a diagnose-before-retrying paragraph next to the existing
  permission-denial guidance, covering failed tool calls
- permission: when the user rejects an approval on the main agent, tell the
  model not to re-attempt the exact same call (sub agents already had an
  equivalent hint)

* fix(agent-core): close abandoned tool exchanges and dedupe duplicate tool_use ids

A turn that dies between a recorded tool.call and its paired tool.result
(e.g. a transcript write failure mid-batch) used to leave
pendingToolResultIds open forever: every later message was stranded in
deferredMessages and user input was silently swallowed.

- runOneTurn now defensively closes any dangling tool calls when a turn
  ends (completed, cancelled, or failed), synthesizing an error result
  that names the cause, with a warn log and a tool_exchange_abandoned
  telemetry event
- the projector drops assistant tool calls whose id already appeared
  earlier (first occurrence wins): a duplicate id is wire-invalid on
  strict providers and not repairable by the strict resend; reported via
  the existing projection-repair log and telemetry
- resume-side closePendingToolResults now logs what it closes (warn for
  a mid-history gap, info for the routine trailing interruption)

* chore: add changesets for tool exchange fixes

* fix(agent-core): scope duplicate tool_use id dedup to the strict resend

Unconditional dedup regressed providers that emit per-response counter
ids (e.g. call_0 in every step) and accept their own duplicates: later
tool exchanges silently vanished from the projected history, and a
duplicate call's own recorded result was left dangling.

- the dedupe pass is now opt-in via dedupeDuplicateToolCalls and enabled
  only in strictMessages, so the normal projection keeps the history the
  provider produced
- the pass also drops every tool result after the first for an id, so no
  dangling tool message survives; when the kept call has no result of
  its own, the surviving one is reattached by the adjacency repair
- kosong now classifies the Anthropic "tool_use ids must be unique" 400
  as a recoverable request-structure error so it triggers the strict
  resend

* feat(cli): wait for background subagents before exiting kimi -p

When `background.keep_alive_on_exit` is enabled, `kimi -p` now waits for
all background subagents to reach a terminal state before exiting, bounded
by `background.print_wait_ceiling_s` (default 3600s). This lets concurrent
background subagents run to completion in single-turn runs instead of being
torn down when the main agent's turn ends.
2026-07-03 16:56:16 +08:00
liruifengv
9091627257
fix(tui): avoid submitting rapid multi-line paste bursts (#1305)
* fix(tui): avoid submitting rapid multi-line paste bursts

* chore(tui): remove paste burst settings picker

* fix(tui): reset paste burst on DEL backspace
2026-07-03 14:21:02 +08:00
wenhua020201-arch
4498ea9ee3
docs: use kimi-for-coding in model overrides example; drop dangling experimental refs (#1337)
* docs: use kimi-for-coding in model overrides example

kimi-for-coding is the stable public model ID users actually configure;
kimi-k2 is the underlying model name and shouldn't appear in the config
example. Demonstrate overrides with max_context_size and display_name.

* docs: drop dangling references to commented-out experimental section

The `## experimental` section was commented out when micro_compaction
was removed, but the top-level fields table and the intro sentence still
linked to the now-dead #experimental anchor. Remove those references.
2026-07-03 11:41:03 +08:00
Kai
78a058acd2
chore(agent-core): remove experimental micro compaction (#1317)
* chore(agent-core): remove experimental micro compaction

* fix(docs): drop micro compaction row from env-vars table
2026-07-02 19:50:51 +08:00
liruifengv
a5db546d77
feat: add KIMI_MODEL_THINKING_EFFORT to force a thinking effort (#1275)
Some checks are pending
CI / build (push) Waiting to run
CI / test (push) Waiting to run
CI / test-windows (push) Waiting to run
CI / lint (push) Waiting to run
CI / typecheck (push) Waiting to run
Nix Build / Check flake.nix workspace sync (push) Waiting to run
Nix Build / nix build .#kimi-code (push) Blocked by required conditions
Release / Release (push) Waiting to run
Release / Deploy docs (push) Blocked by required conditions
Release / Native release artifact (push) Blocked by required conditions
Release / Desktop release artifact (push) Blocked by required conditions
Release / Publish native release assets (push) Blocked by required conditions
* feat: add KIMI_MODEL_THINKING_EFFORT to force a thinking effort

Send thinking effort only when the model declares it in support_efforts, and add the KIMI_MODEL_THINKING_EFFORT environment variable as an escape hatch to force a specific effort regardless of declared support.

* test: align thinking effort expectations with support_efforts gating

Update the kimi adapter e2e and compaction tests that asserted the previous pass-through behavior on models without support_efforts.
2026-07-01 21:20:27 +08:00