Commit graph

972 commits

Author SHA1 Message Date
Chen, Yicun
0d00a07c02
fix(web): preserve selected text when copying over HTTP (#2120)
Co-authored-by: chenyicun <chenyicun@msh.team>
2026-07-24 16:38:36 +08:00
Haozhe
3615b5da9f
refactor(agent-core-v2): drop definition-level capability resolution (#2142)
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
CI / test (5) (push) Waiting to run
CI / test-pi-tui (push) Waiting to run
CI / test-windows (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
Capability resolution now falls back from trait hooks (last declarer
wins) to the protocol base catalog to UNKNOWN_CAPABILITY; the
definition layer is removed from the chain.

- remove ProviderDefinition.capability and the definition branch in
  ProtocolAdapterRegistry.explainCapability
- kimi contrib drops its UNKNOWN_CAPABILITY declarations, so base-known
  model ids resolve through the base catalog instead of being
  suppressed by a vendor-level UNKNOWN
- update composition tests for the new resolution order
2026-07-24 16:04:02 +08:00
liruifengv
a2401cc1ed
feat(kap-server): add provider write endpoints and models.dev/registry import (#2110)
* fix(agent-core-v2): honor explicit undefined field deletes in modelsToToml

The models TOML transform merged each entry as { ...oldRaw, ...converted },
so a field the new record carried with an explicit undefined — deleted from
'converted' by setDefined — was put right back from the old on-disk raw.
Field-level clears issued through config.replace (e.g. the provider write
routes dropping base_url, display_name, capabilities) only took effect in
memory and resurrected on the next boot. Merge via setDefined onto the old
raw directly, matching providerEntryToToml.

* feat(kap-server): add provider write endpoints and models.dev/registry import

Add the provider management write surface plus server-proxied catalogs so
API clients (the desktop app first) can manage providers without editing
config.toml by hand:

- POST/PUT/DELETE /providers: manual create, replace-style edit (new_id
  rename with pointer migration, tri-state api_key, form-unknown fields
  merged through), and delete with real alias removal.
- GET /providers/{id} additionally reveals the stored api_key for edit
  prefill on the loopback+bearer transport; the list stays redacted.
- GET /catalog/providers[{id}]: pruned models.dev directory with import
  eligibility resolved server-side (10-min cache, stale fallback, built-in
  snapshot, shared in-flight fetch).
- POST /providers:import_catalog and :import_registry (collection actions
  next to :refresh): catalog/registry imports with TUI-aligned
  remove-then-apply refresh semantics, OAuth-managed guards, and the
  registry source blob that scheduled refreshes rediscover.

Write-path consistency: field-level clears assign explicit undefined (the
TOML transforms only drop keys that way), import refreshes swap aliases in
two passes so stale on-disk fields cannot ride along, re-imports keep the
stored credential when api_key is omitted, and multi-step writes serialize
through a process-local chain. Global default_provider/default_model are
never modified by provider writes.

* fix(kap-server): cache the built-in catalog fallback and guard alias-key collisions on replace

- Cache the built-in snapshot on upstream failure, so an offline install no
  longer pays the full upstream timeout on every catalog call.
- Reject a PUT whose rebuilt alias keys collide with an alias owned by
  another provider (foreign-prefix keys are global), checked before any
  write so a collision never lands half the edit.

* fix(kap-server): make toCatalogProviderItem's switch explicit for older TS control-flow

* chore(agent-core-v2): drop inline rationale per package comment conventions

* feat(kap-server): seed default_model on provider create/import when none is configured

A fresh setup has no global default model, so the first provider added
through the write endpoints left GET /auth permanently unready and new
sessions without a selected model. Seed default_model from the created
provider's default (or first) model on POST /providers, :import_catalog
and :import_registry when — and only when — no default is configured;
an existing pointer is never moved, not even a dangling one.

* refactor(agent-core-v2): own the models.dev provider import in the engine

kap-server's provider write endpoints imported @moonshot-ai/kosong and
@moonshot-ai/kimi-code-oauth directly for the models.dev browse/import
and custom-registry import. Move that capability behind a new App-scope
IModelsDevImportService so the server only talks to agent-core-v2:

- app/kosongConfig/modelsDev.ts mirrors the third-party api.json schema
  and hosts the pure translation (wire inference, endpoint resolution,
  model pruning, thinking/gateway overrides) ported from kosong's
  catalog.ts; kosong's own type surface stays limited to built-in
  vocabulary
- modelsDevUpstream.ts owns the directory fetch, 10-minute cache, and
  built-in snapshot fallback; modelsDevImportService.ts owns the import
  orchestration (tri-state api_key, two-pass alias swap, OAuth-managed
  rejection, serialized writes) with coded modelsDev.* errors
- kap-server routes shrink to wire mapping (zod schemas + numeric error
  code mapping) and drop the kosong/oauth dependencies

The /api/v1 surface is byte-identical: the apiSurface snapshot and all
existing modelCatalog tests pass unchanged.

* chore: drop the branch's changesets

* fix(agent-core-v2): route the models.dev always-thinking exemption through kosong

The zero-tolerance vendor-name gate forbids a 'kimi' compare outside the
kosong layer, and the engine-side models.dev port carried one inline.
Kosong (the vendor layer, owner of thinking semantics) now answers the
verdict — wireHasProtocolThinkingDisable — and kosongConfig/modelsDev.ts
asks it instead of comparing wire ids.

---------

Co-authored-by: haozhe.yang <yanghaozhe@moonshot.ai>
2026-07-24 14:00:16 +08:00
liruifengv
c2b2c4eb49
docs(changelog): sync 0.29.1 from apps/kimi-code/CHANGELOG.md (#2136) 2026-07-24 13:56:33 +08:00
github-actions[bot]
f4c3967a41
ci: release packages (#2061)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-07-24 13:23:37 +08:00
liruifengv
dad11ed44e
chore: prune non-user-facing changesets (#2134)
* chore: prune non-user-facing changesets

Remove entries covering only agent-core-v2 internals, kap-server
protocol/endpoint changes, and experimental-engine behavior; the
underlying changes still ship with the next user-facing release.
Downgrade the MCP timeout and web service env var additions from
minor to patch, as both extend existing configuration surfaces.

* chore: document user-facing criteria in gen-changesets skill

Add Core Rule 6 to skip changesets for changes users cannot
perceive, note pre-release pruning in the workflow, and classify
configuration additions to existing features as patch.
2026-07-24 13:17:36 +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
Kai
66f611aae9
fix: echo thinking under the reasoning field the endpoint actually uses (#2104)
Some checks are pending
CI / lint (push) Waiting to run
CI / build (push) Waiting to run
CI / test (1) (push) Waiting to run
CI / test (2) (push) Waiting to run
CI / test (3) (push) Waiting to run
CI / test (4) (push) Waiting to run
CI / test (5) (push) Waiting to run
CI / test-pi-tui (push) Waiting to run
CI / test-windows (push) Waiting to run
CI / typecheck (push) Waiting to run
Nix Build / Check flake.nix workspace sync (push) Waiting to run
Nix Build / nix build .#kimi-code (push) Blocked by required conditions
Release / 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
OpenAI-compatible endpoints disagree on the wire field for reasoning
content: `reasoning_content` (DeepSeek/Moonshot convention, older vLLM)
vs `reasoning` (OpenAI GPT-OSS guidance, current vLLM — which also only
accepts `reasoning` on the request side, vllm-project/vllm#38488).
Reading only `reasoning_content` silently dropped thinking, and the lost
thinking then never made it back into later requests.

Add per-endpoint dialect detection: inbound responses are scanned in
priority order (reasoning_content, reasoning_details, reasoning; first
string value wins), the carrying key is remembered, and outbound
messages echo thinking under the same key (default reasoning_content).
An explicit `reasoningKey` / `reasoning_key` config still pins the
dialect.

- kosong: shared reasoning-key module; wire the kimi and openai-legacy
  providers; the dialect cell is shared across per-step provider clones.
- agent-core-v2: same mechanism in the vendored openai-legacy base; the
  kimi trait no longer pins reasoning_content (the default already is),
  keeping Moonshot wire behavior byte-identical while adapting to vLLM.
- agent-core: memoize the base provider behind ConfigState.provider so
  the detected dialect survives across turns instead of being rebuilt
  per access.
2026-07-23 21:15:00 +08:00
Haozhe
d751b6796c
feat(kap-server): global session work status, transcript subscribe_v2, and plan endpoint (#2094)
* feat(session): add core work aggregate and consume it at WS/REST edges

- agent-core-v2: add the sessionActivity domain (ISessionActivityView,
  Session scope) folding each agent's activity view and the interaction
  kernel into busy / main_turn_active / pending_interaction /
  last_turn_reason, firing cause-tagged change events only on real
  tuple changes
- kap-server: resolveSessionFacts reads the core view; the WS
  broadcaster drops its per-agent fold/dedup and delegates to the
  view, deferring turn_ended emissions until after the turn.ended
  frame so busy:false never precedes it; global events now fan out
  to every established connection via addGlobalTarget without any
  subscription
- kimi-inspect: add src/activity (GlobalEventsWs + SessionActivityHub
  + useSessionActivities) consuming the global push; the Sidebar
  renders running / approval / question / failed badges per session

* feat(kap-server): decouple transcript stream from agent_filter

- transcript frames (transcript.reset/ops) are governed by the per-agent
  transcript grades alone and bypass the legacy agent allowlist; the filter
  still gates session_event delivery
- client_hello is handshake-only in code: hello and subscribe share one
  attach path, and hello's inline subscription fields are deprecated
  (subscriptions made optional) in both protocol packages
- flush deferred work_changed(busy:false) from a microtask so it always
  lands after the matching turn.ended frame

* fix(kimi-inspect): restore session activity hub in the browser

- bind the default fetch in SessionActivityHub: a member call hits the
  browser's Illegal invocation (receiver is not the global object), and
  the swallowed error left the REST seed never firing, so the Sidebar
  badges never populated on refresh
- create the hub in useEffect + useState instead of useMemo: under
  StrictMode the first mount's cleanup closes the memo-created hub for
  the rest of the page's life

* chore: add changeset for the global session work status push

* feat(kap-server): add transcript plan endpoint for ExitPlanMode calls

- add GET /sessions/{id}/transcript/plan projecting one ExitPlanMode
  call's plan info (content, path, options, review outcome) from the
  first available fact: the linked approval interaction's request
  display, the live tool frame's display, or the tool result output
- add TOOL_CALL_NOT_FOUND (40416) error code
- add transcriptPlanResponseSchema to the transcript contract
- cover live, auto-mode, cold-rebuild, and error paths in tests
- update the API surface snapshot and AGENTS.md

* feat: move transcript subscriptions to subscribe_v2 and extend the plan endpoint

- add subscribe_v2 / unsubscribe_v2 WS control frames as the only
  carrier of per-agent transcript grades and the transcript_since
  cursor; client_hello / subscribe no longer accept transcript fields
- serialize control-frame handling per connection so interleaved
  attaches cannot overwrite fresher subscription state
- make tool_call_id optional on GET /sessions/{id}/transcript/plan:
  omitted lists every ExitPlanMode call with recoverable plan content
  as a plans array
- add a Plan lookup card to the kimi-inspect agent inspector via
  fetchTranscriptPlan
- add TOOL_CALL_NOT_FOUND (40416) to the shared protocol error codes
- update AGENTS.md and add changesets
2026-07-23 18:48:25 +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
liruifengv
ca38b7ed86
chore: remove superpowers toolbar tip (#2089) 2026-07-23 12:34:14 +08:00
Haozhe
5240b5c83c
feat(agent-core-v2): add generated config and wire-protocol manifests (#2086)
* feat(agent-core-v2): add generated config section manifest

- add scripts/gen-config-manifest.mts: drains the live
  registerConfigSection / registerConfigOverlay contributions and renders
  docs/config-manifest.toml in the on-disk config.toml shape (owner,
  scope, registered defaults, env bindings, schema fields)
- add a gen:config-manifest package script (--check mode included) and a
  freshness test that rebuilds the manifest and compares byte-for-byte
- point the agent-core-dev config skill and the package AGENTS.md at the
  generated manifest instead of the stale hand-maintained ownership map

* feat(agent-core-v2): add generated wire-protocol manifest

- add scripts/gen-wire-manifest.mts to generate docs/wire-manifest.d.ts
  from defineOp registrations (payload interfaces, persist policy,
  toEvent, cross-reducers) plus a WirePayloadMap
- extract shared JSON Schema helpers from gen-config-manifest.mts into
  scripts/lib/jsonSchema.mts
- add gen:wire-manifest script and wireManifest.test.ts freshness check
- document the manifest in packages/agent-core-v2/AGENTS.md

* fix(agent-core-v2): keep array-of-tables manifest sections fully commented

A bare `[hooks]` header parses as a plain table, which array sections
reject on load; emit only the commented `[[hooks]]` shape so the
manifest matches the on-disk config.toml shape it documents.

Addresses a Codex review comment on PR #2086.
2026-07-23 11:17:10 +08:00
Haozhe
188c0fcbf7
refactor(agent-core-v2): decouple kosong from config persistence (#2068)
Some checks are pending
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 / test (2) (push) Waiting to run
CI / test (3) (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
* refactor(agent-core-v2): decouple kosong from config persistence

- keep the kosong provider/model registries in memory and sync them with
  config.toml through a new app/kosongConfig two-way bridge: hydrate on
  startup, push config section changes into the registries, and persist
  runtime mutations (discovery refresh, OAuth provisioning, default-model
  pointer changes) back to disk
- declare the providers/models/thinking section constants, zod schemas,
  env bindings, and TOML transforms in a single app/kosongConfig
  configSection.ts; kosong keeps hand-written types only
- pin every schema to its kosong type at compile time via
  AssertExact<Equal<...>> (_base/utils/typeEquality)
- register a transitional auth>kosongConfig exception in the domain-layer
  checker for the OAuth provisioning flows

* chore(agent-core-v2): drop unused IConfigService import from authLegacy

* fix(agent-core-v2): reconcile registry with env-pinned default pointers

A registry-originated default-model/provider write lands only in the
config user layer when an effective overlay pins the section
(KIMI_MODEL_NAME pins defaultModel to the reserved env model): the
effective value does not move and no change event fires, so the registry
diverged from the effective config view — catalog/auth reads reported the
user pick while profile resolution kept the pinned model. After
persisting, the bridge now re-asserts the effective value into the
registry, restoring the pre-refactor behavior where every default-pointer
write was arbitrated by the effective view.

* fix(agent-core-v2): await persistence before resolving kosong registry mutations

ProviderService/ModelService mutations resolved as soon as the in-memory
registry updated, while the kosongConfig bridge persisted the change on an
unawaited private chain — callers (klient kosong.*, set_default route,
OAuth provision, discovery refresh) could observe success before the write
reached config.toml, and a restart right after could lose it.

- fire registry change events through AsyncEmitter and await delivery in
  set/delete/replaceAll/setDefaultX, so a mutation resolves only after
  listeners' waitUntil work completes; loadAll keeps synchronous timing
- the bridge hooks its persists into waitUntil, hoists the equality guards
  into the listeners so config-originated echoes stay fully synchronous,
  and serializes persists on the chain as before
- retry a failed persist with bounded backoff (3 attempts); failures are
  logged, never rejected to callers, and the in-memory change stands
- default-pointer change events now carry an { id } payload (fireAsync
  requires object events)
- add the klient kosong-config stress example covering read-after-write,
  concurrent bursts, and restart durability
2026-07-23 00:32:10 +08:00
qer
e0f2a41769
feat(datasource): add wind, imf, gildata, sec_edgar, and sp_data sources (#2029)
Some checks are pending
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 / test (2) (push) Waiting to run
CI / test (3) (push) Waiting to run
CI / test (4) (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 / Publish native release assets (push) Blocked by required conditions
Release / Native release artifact (push) Blocked by required conditions
* feat(datasource): add wind, imf, gildata, sec_edgar, and sp_data sources

* feat(datasource): strengthen source routing in skill and tool schema

* feat(datasource): retry on credential rotation and enforce single-source routing contract

* feat(datasource): neutral source selection with objective capability boundaries

* feat(datasource): note wind_search_fields field-name mapping in skill and schema
2026-07-22 22:45:55 +08:00
Haozhe
64f053cf46
feat: agent-core-v2 permission/workspace refactors and transcript durability (#2021)
* refactor(agent-core-v2): extract toolApproval domain from permissionGate

- add agent-scoped `toolApproval` domain owning the approval round-trip:
  builds approval requests, drives the session/approval broker, publishes
  permission.approval.* events, records session approval rules, and
  resolves ask continuations
- slim permissionPolicy down to the static risk-adjudication chain; drop
  the dynamic `registerPolicy` mechanism
- move harness constraints out of the policy chain into their owning
  domains as toolExecutor hooks ordered before 'permission': plan-mode
  guard (plan), swarm batch exclusivity and AgentSwarm approve (swarm),
  goal-start review (goal), exit-plan review (plan/exitPlanModeReview),
  and btw deny (session/btw)
- delete the now-unused policies (deny-all, plan-mode-guard-deny,
  plan-mode-tool-approve, goal-start-review-ask, swarm-mode-agent-swarm-
  approve, agent-swarm-exclusive-deny)
- update Permission.md, AGENTS.md, and check-domain-layers for the new
  domain layout

* feat(kimi-inspect): add App Services view for app-scope service reflection

- add `services` top-level view (`AppServicesView`) on the NavRail, showing
  the full-width app-scope Service panel grid; session/agent scopes stay in
  the Chat view's Inspector
- extract shared `ServicePanels` from Inspector and add `methodArgs` helper
  to build per-method argument editors from channel metadata
- support variadic service calls in `panels.ts` (`call(svc, method, ...args)`)
- update agent-core-dev skill docs for the toolApproval extraction and the
  guard/review-off-chain permission design

* refactor(agent-core-v2): restructure workspace domains

- rename workspaceRegistry to workspace and workspaceLocalConfig to
  projectLocalConfig
- extract id-spelling resolution into the workspaceAliases domain
  (IWorkspaceAliases)
- extract workspace-centric session queries into workspaceSessions
  (IWorkspaceSessions)
- update kap-server routes, klient contracts, and related tests to the
  new services

* feat(transcript): add op-batch sequencing and point-to-point catch-up

- wire: transcriptSeqSchema with per-agent batch seq watermark on
  transcript.reset/ops and the REST transcript response, the
  transcript_since subscription cursor, and the GET transcript/ops
  catch-up shape; seq stays optional everywhere so legacy peers fall
  back to loss-signal-driven refreshes
- wire: drop interactionFrame, interactions live on the interaction
  item in the ops stream
- kap-server: TranscriptService assigns consecutive per-agent batch
  seqs and retains them in a bounded in-memory journal; the
  transcript_since cursor replays journaled batches instead of a
  baseline reset, and the baseline reset is now items-empty because
  history always pages in over REST
- kimi-inspect: transcript REST/WS clients track the op-batch
  watermark and run seq-gap/reconnect catch-up with full-refresh
  fallback; add the Transcript audit panel (AuditTrail timeline,
  structural diff, state tree) docked right of the chat
- docs: sync AGENTS.md and agent-core-dev skill notes with the new
  transcript contract

* feat(transcript): persist plan revisions and task/interaction facts, add user-messages endpoint

- record each ExitPlanMode submission as a versioned plan blob via a reference-only plan.revision op, projected live and cold as a plan.revision marker plus the plan badge (reviewPath, version)
- persist task.started/task.terminated (with a bounded output tail) and interaction.request/interaction.resolved ops, and add foldFacts so a cold transcript rebuilds tasks, interactions, todos and goal/plan/swarm meta from the wire journal
- add GET /sessions/{session_id}/transcript/user-messages returning all turn-opening prompts grouped per agent

* fix(agent-core-v2): anchor external PreToolUse hooks before the permission gate

The toolApproval extraction forces the gate to construct early (planService injects it to anchor plan-guard), so the 'permission' hook registered ahead of 'externalHooks' and a policy ask waited on the approval broker before PreToolUse could block, hanging the turn. Fetch the gate first in registerListeners and register the PreToolUse hook with before: 'permission', falling back to appending when the gate is stubbed without its hook.

* refactor(agent-core-v2): replace ordered onBeforeExecuteTool hook with veto-event pattern

- introduce BeforeToolExecuteEvent with veto/allow/pass/waitUntil statements
- add BeforeToolExecuteEmitter with two-pass fire (immediate then deferred)
- split readiness work into separate onWillExecuteTool participation event
- migrate all domain listeners: permissionGate, plan, goal, swarm, btw,
  externalHooks, toolDedupe, mcp
- remove IAgentPermissionGate force-injection for hook ordering
- update docs, tests, and domain-layer check to match

* refactor(agent-core-v2): unify veto payload on ExecutableToolResult

- veto() and waitUntil factories now carry a plain ExecutableToolResult:
  isError reads as a denial, anything else as a short-circuit;
  the block/reason/syntheticResult weak union is gone
- add denyToolExecution(reason) helper for the common denial shape, and
  narrow the fire/authorize return to BeforeExecuteDecision ({ veto } or
  { executionMetadata })
- narrow the policy 'result' resolution to { kind: 'result'; result }
- settle vetoed calls through a single normalize/merge path in the executor

* fix(agent-core-v2): pull up IAgentPermissionGate in agent activation

The permission gate only subscribes `onBeforeExecuteTool` from its
constructor. The veto-event refactor removed the ordering-driven
force-injections that used to pull it up, so without an explicit
resolution tool execution would run without policy adjudication.

* refactor(agent-core-v2): fold systemReminder domain into contextMemory appendTagged

- add `appendTagged(content, tag, origin)` to `IAgentContextMemoryService`,
  storing content pure with a `tag` field on `ContextMessage`
- apply the XML tag at projection time in `contextProjector` via the new
  `tag.ts` helpers (`wrapTag` / `applyTagToContent`)
- delete the `systemReminder` domain and migrate all call sites
  (contextInjector, goal, plugin, prompt, swarm, btw, sessionInit,
  toolSelectAnnouncements) to `appendTagged`
- build toolDedupe reminder strings with `wrapTag`

* refactor(klient): merge providers/models/catalog into global.kosong facade

Converge three separate facade namespaces (global.providers,
global.models, global.catalog) into a single global.kosong facade
that exposes two domain concepts: provider (CRUD) and model
(read-only view). Add streaming generate() method for direct
LLM calls through the facade.

- Define ProviderAuth (api-key | oauth), ProviderInput,
  AnonymousProviderInput, GenerateInput, GenerateParams,
  GenerateEvent as klient-owned public types
- Extend KlientChannel with stream() for AsyncIterable transport
- Add streaming IPC protocol (stream/stream_data/stream_end/
  stream_error/stream_cancel frame types)
- Add StreamingProcedureContract, ScopedStreamCaller, and
  per-chunk zod validation in the contract layer
- Implement generate via dispatcher special-case routing to
  IModelCatalog.getRequester().request()
- Rename events: providers.changed -> kosong.providers.changed,
  models.changed -> kosong.models.changed,
  catalog.changed -> kosong.changed
- Remove GlobalProvidersFacade, GlobalModelsFacade,
  GlobalCatalogFacade, and ModelRecord from public exports
- Update all tests, examples, and README

BREAKING CHANGE: global.providers, global.models, and
global.catalog replaced by global.kosong; event names changed;
ModelRecord no longer exported.

* feat(transcript,kimi-inspect): add tag field to text frames and improve session creation

transcript:
- add optional `tag` field to TextFrame, textFrameSchema, and HistoryMessage
- propagate tag through contextTranscript MutableMessage

kimi-inspect:
- render tagged frames with violet badge and distinct styling in ChatView
- skip cwd prompt for workspace-based session creation in Sidebar
- auto-bind default model on new sessions via resolveDefaultModel

* refactor(transcript): rename wire/ directory to contract/

The transcript package's REST/WS schemas and event types lived in
src/wire/, which collided with the engine's persisted wire.jsonl
record vocabulary. Rename it to src/contract/ so "wire" unambiguously
refers to wire.jsonl records (foldWireRecordFacts, HistoryWireRecord
stay unchanged).

- rename src/wire/{schema,events}.ts to src/contract/
- update index exports and test imports accordingly
- reword comments: "wire shape" -> "contract shape", "on the wire" ->
  "in ops" / "in transit" / "on the WS channel" / "transcript API"
- events.ts: "transcript frame" -> "transcript event" for WS envelope
  messages, avoiding confusion with TranscriptFrame
- kap-server tests: TranscriptWire/TurnWire/FrameWire/OpsCatchupWire/
  UserMessagesWire -> *Contract
- update AGENTS.md references

* refactor(transcript): rename wire/ directory to contract/

The transcript package's REST/WS schemas and event types lived in
src/wire/, which collided with the engine's persisted wire.jsonl
record vocabulary. Rename it to src/contract/ so "wire" unambiguously
refers to wire.jsonl records (foldWireRecordFacts, HistoryWireRecord
stay unchanged).

- rename src/wire/{schema,events}.ts to src/contract/
- update index exports and test imports accordingly
- reword comments: "wire shape" -> "contract shape", "on the wire" ->
  "in ops" / "in transit" / "on the WS channel" / "transcript API"
- kap-server tests: TranscriptWire/TurnWire/FrameWire/OpsCatchupWire/
  UserMessagesWire -> *Contract
- update AGENTS.md references

* Revert "refactor(agent-core-v2): fold systemReminder domain into contextMemory appendTagged"

This reverts commit 55afaa3d96f729c4f73a71f1fbd23d3f6087453b.

Restore the systemReminder domain: reminders go back to being baked
into message text at write time, and ContextMessage loses the `tag`
field (projection-time wrapping is removed with it).

* feat(transcript): add wire-equivalent detail, dedupe session events

- transcript: add step usage/timing/retry, turn durationMs/error/usage,
  tool inputText/progress, task resultSummary/error, meta.agent status,
  a global prompts entity, and the 'hook' marker
- kap-server: project the new fields in coreEventMap and suppress
  transcript-projected session events on connections subscribed to the
  transcript protocol (live fan-out and cursor replay)
- kimi-inspect: mechanical type sync for the new snapshot prompts field

* fix(klient): resolve lint errors in ipc channel stream and e2e matrix test
2026-07-22 19:21:56 +08:00
Haozhe
c6291c3ad7
feat(v2-print): align kimi -p run lifecycle with the default engine (#2017)
* feat(v2-print): align kimi -p run lifecycle with the default engine (13 files)

- add applyPrintModeConfigDefaults in agent-core-v2: fill print-mode config
  defaults (bash task timeout / max steps per turn / subagent timeout all
  unbounded) into the memory layer, respecting user-set keys
- applyPrintBackgroundPolicy: keep the run alive while cron tasks have
  future fires so their steered turns can run, with an anti-spin guard for
  wedged ticks; goal/cron waiting shares the steer ceiling budget
- default print background mode changed from exit to steer
- add task.bashTaskTimeoutS config key and wire it into BashTool's
  detachTimeoutMs
- v2 print runner now forwards turn.step.retrying events to writeRetrying
- allow subagent timeoutMs = 0 (no timeout)

* test(agent-core-v2): remove duplicate loop config imports in config test
2026-07-22 19:21:39 +08:00
liruifengv
b32170b018
docs(changelog): sync 0.29.0 from apps/kimi-code/CHANGELOG.md (#2054) 2026-07-22 18:23:48 +08:00
github-actions[bot]
8bf5bacba9
ci: release packages (#1989)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-07-22 17:24:39 +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
4c763f6763
feat: send prompt-attached videos directly with the prompt (#1999)
Some checks are pending
CI / lint (push) Waiting to run
CI / build (push) Waiting to run
CI / test (1) (push) Waiting to run
CI / test (2) (push) Waiting to run
CI / test (3) (push) Waiting to run
CI / test (4) (push) Waiting to run
CI / test (5) (push) Waiting to run
CI / test-pi-tui (push) Waiting to run
CI / typecheck (push) Waiting to run
Nix Build / Check flake.nix workspace sync (push) Waiting to run
Release / Native release artifact (push) Blocked by required conditions
Release / Release (push) Waiting to run
CI / test-windows (push) Waiting to run
Nix Build / nix build .#kimi-code (push) Blocked by required conditions
Release / Deploy docs (push) Blocked by required conditions
Release / Publish native release assets (push) Blocked by required conditions
* feat: send prompt-attached videos directly with the prompt

Videos attached to a prompt (pasted in the TUI, uploaded in the web UI)
previously reached the model only after it opened the file with
ReadMediaFile — an extra tool round trip that could leave the video
unseen when the model never made that call. They are now uploaded
through the model provider's file channel and embedded directly in the
user message as the provider-issued reference; ReadMediaFile stays as
the fallback and as the model's own way to open video files.

- agent-core: new uploadVideo agent RPC and Session.uploadVideo in the
  SDK; the TUI uploads pasted videos at submit time, falling back to
  the file-tag form on failure
- kap-server: inline file-source video prompt parts at the REST edge
  and map provider file ids back to local uploads behind
  GET /files/llm/{llm_id}
- kimi-web: play provider-referenced prompt videos after reload/resume

* fix: fall back to inline video when the upload channel fails

ReadMediaFile only used the provider's video upload channel when one
was bound, and surfaced a hard tool error when the upload itself
failed — on providers without a files endpoint (or a transient upload
failure) the model was told the video could not be read at all. Now a
missing or failing upload falls back to delivering the video inline
(base64), the same shape providers without an upload channel already
get. Both engines (agent-core and agent-core-v2) are fixed.

* test: cover prompt video edge cases and failure paths

Stress coverage for the prompt video pipeline:

- agent-core uploadVideo RPC: extension/magic classification
  (.txt with video bytes accepted, extension trusted when magic is
  absent), exact 100MB boundary, directory and nonexistent paths
- TUI: mixed per-video outcomes (one inlined, one tag fallback),
  submission order behind an in-flight upload, queued video messages
  carrying final uploaded parts
- kap-server: per-video provider-id mappings, Range requests through
  the llm redirect, mapping persistence across a server restart

* fix: surface auth rejections from the video upload channel

The base64 fallback for a failing video upload must not mask auth
rejections: a 401/403 (surfaced as provider.auth_error) drives the
credential force-refresh and a clear auth error, while an inline
payload would just be rejected again by the next request. Only a
missing or broken upload channel (no files endpoint, network/server
errors) falls back to inline delivery.

* fix: keep the by-design no-hook video error from degrading to inline

Main's contract for a provider with no video upload hook is an honest
"does not support video upload" tool error — an inline payload would
be dropped on that protocol's wire anyway. The base64 fallback now only
applies to an upload channel that exists but failed at runtime. The
no-hook throw gets a stable type (VideoUploadUnsupportedError) so the
two cases are told apart without matching message text.

* fix: constrain the llm video route param to a safe alphabet

The provider file id is used as a blob-store key, and the node-fs
backend joins scope and key into a storage path. An id containing an
encoded path separator (%2F) could address a different storage path
than the intended llm-video mapping; the route now only accepts the
provider-id alphabet.

* style(agent-core-v2): move added explanations into top-of-file comment blocks

The package's comment convention keeps comments solely in the top-of-file
block; relocate the new notes (url-source id pairing, video delivery
fallback, VideoUploadUnsupportedError) from functions and schema fields
into their module headers.

* fix: reject prompt video uploads when the model lacks video input

The uploadVideo RPC only checked the provider's upload channel, so an
SDK caller on a text-only or unknown-capability model could obtain a
valid-looking video_url part the current model is not supposed to
accept. The TUI capability guard does not cover this public path, so
the agent now gates on video_in itself.

* fix: sniff uploaded video bytes before inlining them

The inline branch trusted the upload-time content type; now the bytes
are sniffed first, and anything magic-confirmed as a non-video kind
(e.g. an image mislabeled as video/mp4) falls back to the file-tag
form instead of being uploaded. Like the image gate, bytes are
authoritative where the container format allows it — an MPEG-PS
lookalike still rides the extension, matching ReadMediaFile.

* test: keep the generation stub pending until the abort lands

An immediately-answered 404 ends the turn (non-retryable) before the
test's abort call, racing the cleanup into a 409; the stub now hangs
generation until the abort cancels it.

* fix: validate prompt file references before mutating session controls

A stale file_id failed inside media resolution — after the model,
thinking and permission overrides had already been applied — so a
rejected prompt still changed the session's controls. File references
are now checked up front, keeping failed submits side-effect free.

* fix: serialize prompt submissions per session

A slow provider video upload let a later text-only request reach the
queue ahead of an earlier video one, silently reordering the
conversation for REST clients and multiple tabs. Submissions to the
same session now chain, matching the ordering the TUI already
guarantees locally.

* fix: play reloaded provider videos through the authenticated fetch path

A video recovered from an ms:// reference carried only the bare redirect
URL, which 401s under daemon auth when loaded natively. The attachment
now keeps the provider file id (llmFileId) end to end, and AuthMedia
fetches the bytes with the Bearer credential through the daemon's llm
redirect — the same blob-URL path uploaded files already use.

* fix: fence pending video submits against session and model switches

A slow paste-upload left the TUI idle, so /new, the session picker or
/model could fire mid-upload; the continuation then dispatched the old
session's provider reference into the newly selected session or a
model that cannot resolve it. The dispatch now re-checks that the
session and model are unchanged and asks for a resend instead.

* fix: preserve the caller's video path verbatim in uploadVideo

Trimming the path changed the filesystem target before validation and
upload, so a name that legitimately starts or ends with whitespace
resolved to the wrong file; the trim is now only the emptiness check.

* fix: forward provider-issued ids on image URL prompt parts too

The shared url-source schema accepts id for image and video parts, but
only the video path forwarded it, dropping provider-keyed image ids
between prompt acceptance and the model request.

* fix(web): reconcile inlined video echoes into the optimistic user message

The loose user-message matcher counted media parts and <video path>
tags but not the [video:ms://…] text shape, so a racing server echo of
an inlined upload slipped through as a duplicate user bubble.

* fix: queue bash submits behind a pending video upload

A bash-mode submit could start while a pasted video was still
uploading, recording shell context before the earlier prompt was
dispatched and reordering user actions; it now chains behind the
upload like normal submits.

* test: cast the driver through unknown for the session-switch fence test

* fix: validate prompt file references before resolving the prompt agent

A stale file_id posted to a fresh or cold session materialized the main
agent (registering it in session metadata and igniting agent-scoped
services) before the request was rejected. The file check now runs
first, so failed submits create nothing and mutate nothing.

* fix: key the playback mapping by the id embedded in the reference URL

Projections and clients read the provider id from the ms:// URL, not
the id field, so an upload that returns an id-less or mismatched
reference would inline fine but 404 on playback. The mapping now
derives its key from the URL, falling back to the explicit id.

* fix: sanitize provider video ids on the write side of the playback map

The read route got the safe-alphabet guard, but recordLlmVideoRef still
used the provider-returned id verbatim as a blob-store key; a crafted
provider response could write the mapping outside the llm-video
namespace. Out-of-alphabet ids are now dropped on both put and get.

* fix(web): keep recovered provider videos resendable through the edit path

The composer reload path only honored fileId and fell back to a
bare fetch(url) without the Bearer token, so editing a reloaded
provider-video turn dropped the chip after a 401. llmFileId is now
threaded through and the bytes are re-uploaded via the authenticated
llm redirect.

* fix: fall back to inline video for no-hook providers whose wire carries it

The by-design no-hook error is only the honest answer when the wire
would drop an inline payload anyway (the OpenAI family). Protocols
that convert video_url (kimi, anthropic, google-genai, vertex) now
take the base64 fallback instead of failing every video read; the
registrar computes the flag from the model's protocol.

* test: satisfy the full IBlobStore shape in the playback-map stub

The tsgo typecheck job rejects a structural stub missing _serviceBrand
and list even though plain tsc accepted it.

* fix: queue prompt-producing slash commands behind pending uploads

Skill activations and plugin commands started their turn immediately
while a pasted video was still uploading, so the earlier video prompt
queued behind them and user actions ran out of order. Both paths now
chain behind inputSubmitChain (and re-check session/model at dispatch)
like normal and bash submits.

* fix: validate media kinds in the prompt file-reference preflight

The preflight only proved a referenced file exists; a real upload used
with the wrong kind (e.g. a PDF submitted as video) still passed it and
mutated session controls before assertMediaFile rejected the request.
The kind assertion now runs up front with the existence check.

* fix(web): play sent and recovered videos in the file preview

The media preview returned early for every kind except image, so a
user-turn video chip's play action was a no-op even with llmFileId
threaded through. The preview now handles video: bytes come from the
authenticated file/llm fetch into a blob URL and render in a native
player.

* fix(web): preview recovered videos with the authenticated blob URL

The llm re-upload branch fetched bytes with auth but kept the protected
redirect URL as the chip preview, which 401s as a native video src; the
fetched blob now becomes the preview URL, mirroring the fileId branch.

* style(agent-core-v2): move the inline-fallback note into the module header

Same package comment convention as before: rationale lives in the
top-of-file block, not beside the registration call.

* fix: fence delayed bash submits to the originating session

The chained bash callback ran runShellCommandFromInput against whatever
session was active at dispatch time, so a command submitted in session
A could execute in session B's workspace and be recorded there after a
mid-upload switch. The originating session is now captured at submit
and re-verified at dispatch, like the prompt and skill paths.

* fix: emit prompt video telemetry from the agent scope

video_upload is an agent-level event requiring ambient agent identity,
but the uploader was built with the Core-scoped session view, leaving
prompt-upload events unattributable. The route now resolves telemetry
from the target agent for the uploader while image compression keeps
the session-scoped view.

* fix: preserve provider image ids through legacy projections and web mappers

The url-source id accepted by the prompt schema was dropped again by
the legacy message projection and the web wire mapper, so provider-
keyed image references lost their id across messages, snapshots, and
undo responses. It now flows through both directions.

* fix(web): revoke recovered video blob URLs before dropping attachments

A failed llm re-upload removed the attachment without revoking the
freshly created preview blob URL, pinning the whole video in the
browser blob store until page unload on every failed edit attempt.

* fix: serialize foreground slash commands behind pending uploads

/compact and /init started their turn while a pasted video was still
uploading, so the earlier message landed after them — and compaction
summarized the context without it. Prompt-producing builtin commands
now share the same queueBehindPendingUploads chain (with the
session/model dispatch fence) as skill, plugin, and bash submits.

* fix: defer session controls until media preparation succeeds

Media resolution now runs before any profile/model/thinking/permission/
denylist mutation, with the uploader resolved transiently from the
requested (or currently bound) model — a failed submission leaves the
session's controls untouched. A concurrent model switch during
preparation is rejected with session.busy instead of enqueueing a
reference uploaded for the previous model.

* chore: bump the new SDK video upload API as a minor release

Session.uploadVideo is new public API surface, not a patch-level tweak.

* fix: re-check busy state before draining upload-queued commands

A slash command deferred behind a video upload ran the moment the video
prompt dispatched, landing on an already-running turn: beginSessionRequest
wiped the active turn's live pane, and /init or /compact started on top
of it. The deferred callbacks for skills, plugin commands, /compact and
/init now re-run the resolver's busy check at dispatch and show the same
blocked message the user would get when typing while streaming.

* fix: resolve profile-bound models before choosing the uploader

A first prompt carrying "profile" without "model" resolved the upload
model from the still-unbound alias, so no uploader was installed and
every attached video fell back to a tool-read tag even when the
configured default model supports provider upload. The transient
resolution now mirrors AgentProfileService.bind: an explicit body model
wins, a profile bind falls back to the configured default model, and
only otherwise does the currently bound alias apply.

* refactor: resolve prompt videos at request time inside the engine

Move prompt-video delivery out of the submission edge: the TUI submits
synchronously with a local file:// part the v1 turn resolves before the
message enters history, and kap-server carries an internal kimi-file://
reference the v2 requester resolves against the effective model with an
app-scoped upload cache. History keeps the durable local file id, the
/messages projection emits structured video parts, and the web plays
videos back through the authenticated /files channel - deleting the
submit fences, per-session serialization, provider-id reverse mapping,
redirect endpoint, and the unreleased SDK upload API.

* fix: propagate abort through video upload delivery instead of degrading

A turn cancelled mid-upload used to be treated as an ordinary upload
failure: v1 fell back to an inline base64 part and appended the degraded
message to history, and the v2 resolver memoized the tag fallback for the
rest of the agent's lifetime. Both catch sites now check the delivery
signal itself - abort rejections vary in shape by provider - and re-throw
so cancellation ends the turn (v1, classified as cancelled via the abort
reason) or the request (v2, not memoized, so the next turn uploads).

* fix: keep the tag form for no-upload providers whose wire drops inline video

An OpenAI-family model configured with video_in but no provider upload
channel used to receive prompt videos as an inline base64 part - which
chat completions rejects and the Responses adapter degrades to an
omitted-video placeholder, persisting ~4/3x the file size in history for
bytes the model never sees. The prompt path now mirrors the v2 resolver's
protocol gate and degrades to the <video path> tag instead; ReadMediaFile's
own delivery is unchanged. Also merges the prompt-video changesets into a
single user-facing entry.

* fix: escape the NUL separator in the video upload cache key

The cache-key template literal contained a literal NUL byte instead of
the \0 escape, which made Git classify the whole source file as binary
- no inline diffs, unreliable text tooling. The escape produces the
byte-identical runtime string, so hashed cache keys are unchanged.

* fix: check the abort signal before the inline video fallback

The no-uploader inline path (and the post-upload-failure fall-through)
never consulted the delivery signal, so cancelling a turn while the video
bytes were being read still base64-encoded the file and appended the
degraded message to history. The inline branch now re-throws the abort
reason first, matching the upload catch.

* fix: retry transient prompt video upload failures on later steps

A generic upload failure used to memoize its tag fallback for the rest of
the agent's lifetime, freezing a transient files-endpoint error into a
permanently degraded video. The resolver now marks failure-born fallbacks
as non-memoizable: the current request keeps the lightweight tag form and
the next step retries the upload. Structural outcomes (successful uploads,
capability and sniff fallbacks, no-hook inline) stay memoized for
step-retry stability.
2026-07-22 13:46:00 +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
Haozhe
430cd382a8
refactor(agent-core-v2): drop pass-through methods from AgentRPCService (#2042)
- trim AgentRPCService/AgentAPI to the 10 methods with real logic
  (prompt, steer, cancel, undoHistory, setPermission, cancelCompaction,
  activateSkill, activatePluginCommand, getContext, getTools)
- route the klient agent facade to the domain services directly
  (shellCommand, profile, usage, plan, task) with new wire contracts
- keep the test harness ctx.rpc surface unchanged via passthrough adapters
- rewire kap-server rpc tests and the kimi-inspect RPC panel
2026-07-22 13:42:01 +08:00
Kai
ec88d352e8
fix: five correctness follow-ups to the catalog metadata work (#2030)
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
* fix: five correctness follow-ups to the catalog metadata work

- Normalize a configured effort value (case/whitespace) before thinking
  resolution on both engines, so "OFF" is read as off instead of being
  sent upstream as an invalid effort.
- Clamp a declared input cap to the effective context window in the
  effective model resolution, so an override lowering max_context_size
  cannot leave a stale larger cap behind.
- Report context usage against the same effective cap
  (max_input_tokens ?? max_context_tokens) in the SDK getStatus and the
  v2 legacy status projections, matching the session-event and service
  surfaces.
- Preserve concretely declared per-model endpoints when the override
  npm is unrecognized, via the same OpenAI-compatible fallback used for
  top-level entries.
- Attribute resolved.maxInputSize and resolved.capabilities
  .max_input_tokens in the model inspector to their config, override,
  or clamp provenance.

* fix: honor observed context caps and publish the effective cap in v2 status

- The overflow-learned provider window is now written into both
  max_context_tokens and max_input_tokens of the effective compaction
  context, so the strategy cannot bypass it by re-selecting the raw
  catalog input cap during overflow recovery.
- agent.status.updated publishes max_input_tokens ?? max_context_tokens,
  matching the other v1/v2 status surfaces.

* fix: never mutate config on clamp, cap usage ratio, and refuse proprietary override SDKs

- effectiveModelAlias / effectiveModelConfig now build a copy before
  clamping maxInputSize to the effective window instead of rewriting
  the caller's config record in place.
- context_usage is clamped to 1 in the v2 legacy status, the v1
  session service, and the SDK getStatus; the default-model status
  fallback also resolves the input cap.
- Overrides naming known proprietary SDKs (Bedrock, Cohere) are
  refused before the OpenAI-compatible fallback, matching top-level
  import behavior.
- The inspector attributes a clamped maxInputSize to the clamp even
  when the raw value came from models.*.overrides.

* fix: normalize forced efforts, prefer input cap in WS status, keep raw event ratio

- Whitespace-only configured efforts now read as absent, and the
  KIMI_MODEL_THINKING_EFFORT override is lowercased on both engines.
- kap-server's WS legacy status publishes max_input_tokens ??
  max_context_tokens like the other status surfaces.
- SDK getStatus keeps the ratio unclamped on purpose (>100% is the
  documented overflow signal on that path); schema-bounded REST status
  stays clamped to 1.
- Pin the provider-observed window beating a declared input cap with a
  v1 full-compaction regression test.

* fix: attribute resolved.maxInputSize provenance and drop test-body comments in v2
2026-07-22 03:13: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
liruifengv
8e0dcf3049
feat(kap-server): add GET /api/v1/oauth/usage for managed account plan usage (#2027) 2026-07-22 00:51:28 +08:00
liruifengv
0a1b5fa00f
fix(agent-core-v2): clear dangling defaultModel and thinking on managed logout (#2026) 2026-07-21 23:50:09 +08:00
qer
154e082488
fix(web): keep transparent images readable over a checkerboard canvas (#2022)
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
White-on-transparent images disappeared on light surfaces and
black-on-transparent ones on dark surfaces (markdown inline images, tool
result media, and the file preview's large view). Add a shared
--media-alpha-canvas token — a checkerboard of two mid-gray squares mixed
from --color-bg/--color-text, each >=3:1 against both white and black —
and paint it directly on <img> so opaque images stay pixel-identical and
only transparent pixels reveal the canvas.

Also let check-style's no-gradient-text skip custom-property definitions
(a token is never rendered text), and register the new tokens in the
design system view.
2026-07-21 22:20:21 +08:00
liruifengv
576d650380
feat(cli): add third-party source note to update prompt (#2014)
* feat(cli): add third-party source note to update prompt

* fix(cli): adjust third-party source note wording

* refactor(cli): move official install URL into app constants

* chore: simplify changeset wording

* chore: adjust changeset wording
2026-07-21 20:42:23 +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
a3699dd6aa
feat(kap-server): expose per-tool active state in GET /api/v1/tools (#2005)
* feat(kap-server): expose per-tool active state in GET /api/v1/tools

* Update tools-list-active-flag.md

Signed-off-by: 7Sageer <7sageer@djwcb.cn>

---------

Signed-off-by: 7Sageer <7sageer@djwcb.cn>
2026-07-21 19:23:15 +08:00
Haozhe
d67a2003ab
feat(kap-server): add GET /api/v1/fs:content endpoint for host files (#2012)
* feat(kap-server): add GET /api/v1/fs:content endpoint for host files

- serve any host file by absolute path as raw content with Content-Type, ETag caching, and single-range support
- extract shared file metadata helpers (binary detection, line counting, etag, mime/language guessing) into agent-core-v2 _base/utils/fileMeta
- extract pickHeader/parseRangeHeader into kap-server lib/httpRange for reuse across download/content routes

* fix(kap-server): resolve lint and typecheck errors in fs:content route

Return the reply.send(stream) result through the handler promise instead of
using return-await, matching the session fs download route pattern.

* fix(kap-server): reject non-regular files in fs:content

Device nodes, FIFOs, sockets, and zero-size virtual files (/proc, /sys)
would otherwise hang the request or stream unbounded data. Addresses
review feedback; also folds fileMeta symbol comments into the header
block per the agent-core-v2 header-only comment convention.
2026-07-21 18:53:27 +08:00
7Sageer
92576e4d85
fix(agent-core-v2): reconnect dropped MCP servers on tool call failure (#1991)
* fix(agent-core-v2): reconnect dropped MCP servers on tool call failure

When an MCP transport dies mid-session (e.g. the stdio process exits),
the wrapped tool now reconnects the server once and retries the call on
the fresh client, so a dropped connection surfaces as a slow call
instead of a failed turn. Concurrent failing calls on the same server
share a single reconnect, and abort errors bypass the retry.

* fix(agent-core-v2): constrain MCP reconnect retries

* fix(agent-core-v2): recover MCP tool calls on any transport failure

Widen the tool-call recovery trigger from the ConnectionClosed error code
to any transport-level failure: an McpError means the server answered (or
the request timed out) and is not retried, while raw transport errors
(fetch failures, "Not connected" on an already-closed client) now trigger
one reconnect-and-retry. This covers remote HTTP/SSE servers restarting
and batched calls dispatched after the close was processed.

Reconnect through the session-scoped connection manager with join
semantics (reconnectAndJoin) instead of a per-agent dedupe map: parallel
failing calls across agents now share one reconnect instead of preempting
each other through attemptId, and a call that fails after the server was
already healed retries on the resolved client without restarting it again.

* fix(agent-core-v2): probe MCP liveness before reconnecting on tool call failure

Classify an ambiguous tool-call failure by probing the client with a
short-timeout ping instead of guessing from the error type:

- The server answered (a JSON-RPC error, or a result that failed
  client-side CallToolResultSchema validation) is rethrown as-is;
  reconnecting would not change the answer and previously restarted the
  stdio server on every such call.
- The probe succeeding means the failure was transient, so the call is
  retried once in place instead of paying a full reconnect.
- Only a measured-dead transport (the SDK's onclose, or a failed probe)
  goes through reconnectAndJoin and retries on the fresh client, still
  capped at one reconnect per call.

MCPClient gains ping() for the probe; retries remain at-least-once,
now documented in the adapter's module header.

* fix(agent-core-v2): keep MCP tools registered on connection failure

A server whose transport dies between turns no longer loses its tools:
failed/pending status events no longer unregister them, so the next
call reaches the adapter and its reconnect-and-retry path heals the
connection instead of failing with "tool not found".

* Improve MCP server connection handling on tool calls

A server whose connection dies between turns no longer loses its tools from the tool list — they stay available and the next call heals the connection instead of failing with 'tool not found'.

Signed-off-by: 7Sageer <7sageer@djwcb.cn>

---------

Signed-off-by: 7Sageer <7sageer@djwcb.cn>
2026-07-21 17:56:06 +08:00
liruifengv
e45832398d
fix(tui): keep long sessions responsive and bound resumed history (#1976)
* fix(pi-tui): reuse processed lines across frames in the renderer

Each frame previously re-truncated, re-normalized, and re-compared every
transcript line, so steady-state frames (spinner ticks, streaming flushes)
cost O(total lines x chars) and pegged one core in long sessions. Keep the
previous frame's raw lines, processed output, and per-line kitty image ids;
a line whose raw string reference is unchanged reuses its processed output
verbatim, and image-id consumers read the cache instead of re-scanning text.

* fix(tui): stop tree-wide transcript invalidation on structural updates

Grouping a second Read/Agent call, removing swarm progress, finalizing an
MCP status row, replay tool-call removal, and /undo each invalidated the
entire transcript tree, forcing every mounted message to re-render (markdown
lexing + code highlighting) on the next frame. The container's render cache
already validates per child by reference, so structural child-list changes
are picked up without a tree-wide invalidate; reserve it for global style
changes such as theme switches.

* fix(tui): fold older assistant messages into the turn step summary

Step merging only collapsed thinking/tool steps, so assistant text blocks
accumulated without bound inside a turn (hundreds of markdown components in
a single long turn, all re-processed every frame). A running turn now keeps
its last 20 assistant messages (KIMI_CODE_TUI_KEEP_RECENT_ASSISTANT), and a
finished turn folds down to its conclusion tail of 2
(KIMI_CODE_TUI_KEEP_RECENT_ASSISTANT_COMPLETED); older ones collapse into
the step summary line with a message count. Entries are kept, so expand
behavior is unchanged.

* fix(session): bound resumed history to the most recent user turns

Resume used to return every agent's full replay over the RPC boundary (a
long session can reach ~100MB, serialized and parsed several times on an
in-process call), while the TUI only renders the last 10 user turns. The
resume payload now accepts an optional replayTurnLimit, the turn-boundary
predicate moves into agent-core as the single source of truth
(limitAgentReplayByTurns, re-exported through the SDK), and the CLI passes
its existing 10-turn limit so resume transfers just the tail.

* fix(session): count goal continuation rounds as replay turns

Replay turn boundaries only matched real user input, so a 100-round goal
(a handful of user prompts plus 100 system-trigger continuations) fell
entirely inside the 10-turn replay window and resumed by rendering the whole
run from the start. The goal driver already fires one synthetic prompt per
goal turn and counts those as turns itself, so replay trimming now treats
goal_continuation prompts as turn boundaries and keeps the most recent 10
rounds. The continuation prompt is model-facing and hidden live; replay no
longer renders it as a user bubble either, while still advancing the replay
turn so each round groups separately.

* fix(tui): keep replay turn folding when goal continuations are hidden

Suppressing goal continuation bubbles removed every turn-boundary
component from goal-session replays, so mergeAllTurnSteps found no turn
edges and skipped step/assistant folding entirely — a single oversized
goal round still mounted all of its tool cards and assistant messages.
Mount an invisible ReplayTurnBoundaryComponent for each hidden
continuation: it renders zero lines but keeps the turn edges discoverable
to folding and window trimming, matching replay trimming's per-round turn
semantics.

* chore: consolidate and simplify changesets
2026-07-21 15:20:12 +08:00
Haozhe
74da87a457
feat(kap-server): broadcast agent.created/agent.disposed session events (#1997)
* feat(kap-server): broadcast agent.created/agent.disposed session events

- emit durable agent.created / agent.disposed facts from the
  SessionEventBroadcaster lifecycle callbacks, ahead of the agent's own
  events, and let them bypass per-subscription agent allowlists
- add the agent.created / agent.disposed wire types and zod schemas
- stamp disposedAt on the transcript roster entry via
  TranscriptStore.markDisposed so REST consumers can tell a dead agent
  from a live one

* chore: add changeset for agent lifecycle events
2026-07-21 15:18:59 +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
7Sageer
e070a580f0
feat(telemetry): emit turn_id and agent_id on turn and tool events (#1675)
* fix: emit turn_id on turn_started/ended/interrupted telemetry

The turn lifecycle telemetry events (turn_started, turn_ended,
turn_interrupted) never carried the turn id, while tool_call and
tool_call_dedup_detected did. Any analysis correlating a turn's start,
end, or interruption back to its tool calls had nothing to join on.

Add turn_id (already in scope) to all three track() calls, matching the
existing key/value convention used by tool_call_dedup_detected.

Update the strict turn_started/turn_interrupted assertion to cover it.

* fix(agent-core-v2): emit turn_id on turn_started/ended/interrupted telemetry

Port the v1 fix to agent-core-v2: turn lifecycle telemetry events
(turn_started, turn_ended, turn_interrupted) carried no turn id while
tool_call did, leaving nothing to correlate a turn's start, end, or
interruption back to its tool calls.

Add turn_id to the three event interfaces, the telemetry registry
property docs, and the three track2() calls in AgentLoopService,
matching the existing ToolCallEvent key convention. Extend the
turn telemetry assertions in loop.test.ts to cover it.

* fix: emit turn_id on tool_call telemetry

* docs(agent-core-v2): clarify turn_id is a per-agent index in the telemetry registry

* feat(telemetry): add agent_id to turn and tool events

turn_id is a per-agent counter, so it collides across the main agent and subagents within a session. Emit each agent's scope id as agent_id on turn_*, tool_call, tool_call_dedup_detected, api_error and subagent_created (plus parent_agent_id) so events become attributable via (session_id, agent_id, turn_id).

* docs(changeset): cover turn_id emission alongside agent_id

* feat(telemetry): add agent_id to agent-level settings events

* feat(telemetry): link v2 events across agents, turns, and tool calls

- subagent_created: parent_tool_call_id, so a child run joins to the tool call that launched it
- permission_policy_decision / permission_approval_result: agent_id, turn_id, tool_call_id
- plan_submitted / plan_resolved / plan_enter_resolved, context_projection_repaired: agent_id
- compaction_finished / compaction_failed: agent_id + optional turn_id
- cron_scheduled / cron_deleted: optional agent_id of the scheduling agent
- api_error: turn_id + request_kind, so compaction request failures are distinguishable from turn requests
- tool_call_repeat: agent_id + optional turn_id; tool_call_dedup_detected stops fabricating turn_id: 0 outside a turn
- background_task_created/completed: task_id on both, unified kind vocabulary ('process' replaces the legacy 'bash' alias on created)
- agent lifecycle: auto-assigned agent-N ids now skip ids persisted by previous runs, so a resumed session cannot reissue agent-0 and collide with earlier telemetry

* fix(agent-core-v2): preserve telemetry turn attribution

* chore: split telemetry changeset into two logical changes

* refactor(agent-core-v2): bind agent telemetry context at scope

* test(agent-core-v2): fix telemetry assertions for scope-bound agent_id

- undoHistory/goal: include the injected agent_id in exact property assertions
- rpc-events: assert on the shared records array instead of a track2 spy,
  which the scoped telemetry view bypasses

* refactor(agent-core-v2): bind agent identity ambiently in telemetry

- TelemetryService.withContext now returns a lightweight forwarding view:
  transport state (appenders, enabled flag) stays with the App-scoped root,
  so views created before an appender attaches no longer silently drop
  events, and enablement changes apply to every live view.
- Agent-scope events register with defineAgentTelemetryEvent and compose the
  centrally declared AgentTelemetryEventContext (agent_id) into their wire
  schema; business payloads and call sites stay free of agent_id, enforced
  at compile time and by the registry convention test.
- image_compress/image_crop stay plain events: the kap-server prompt routes
  emit them through a session-scoped view without agent identity.
- v1 subagent_created gains parent_tool_call_id for parity with v2.
- Add a lifecycle test asserting each agent scope's telemetry view binds its
  own agent id.

* Delete .changeset/subagent-id-reuse.md

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

---------

Signed-off-by: 7Sageer <12210216@mail.sustech.edu.cn>
2026-07-21 14:48:09 +08:00
Kai
a8f1ca3f10
feat(acp): support selecting thinking effort levels (#1992)
* feat(acp): support selecting thinking effort levels

The ACP thinking config option was a binary on/off toggle mapped to the
model's default effort, so ACP clients (e.g. Zed) could not pick a
concrete level even though the engine and the SDK fully support
support_efforts granularity.

- Advertise one select row per declared effort level (off + levels);
  boolean models keep the legacy off/on rows, always-thinking models
  drop the off row.
- Track the session thinking state as an effort string and reconcile it
  with the engine-normalized value read back from session status after
  model/thinking changes.
- Validate incoming levels against the current model's catalog and
  reject unknown ones with invalid_params before any SDK call; keep the
  legacy 'on' value as an alias for the model default effort.

* test(acp): avoid spreading the Session stub in the reconciliation test
2026-07-21 13:48:50 +08:00
Kai
beeb964393
fix(vscode): reduce webview streaming re-render churn (#1994)
Chat components subscribed to the entire chat store, so every
streaming delta re-rendered every assistant message. Subscribe to
narrow store slices and memoize ChatMessage so settled messages no
longer re-render on each delta. Cap getImageDataUri at 10MB, matching
the media picker, so oversized local images are not inlined into the
webview DOM.
2026-07-21 13:10:59 +08:00
Haozhe
6dd4fd3368
refactor(agent-core-v2): rebuild the model wire layer on the kosong architecture (#1970)
* refactor(v2): land kosong contract layer (L0 wire contract)

* refactor(v2): land kosong protocol layer (L1 traits and base registry)

* refactor(v2): land kosong provider layer (bases, trait composers, kimi definition)

* refactor(v2): land kosong model and catalog layers

* refactor(v2): migrate engine callers to kosong, drop old llmProtocol layer

* test(v2): migrate agent-core-v2 tests and harness to kosong

* refactor: sync peripheral packages to kosong architecture

* refactor(v2): replace provider dialects with per-protocol definitions

* refactor(v2): merge the vertexai protocol into google-genai via providerOptions

* refactor(v2): remove vendor-name gates outside the kosong layer

* feat(v2): add model resolution inspection and connectivity ping

* refactor(v2): remove the unused platform config layer

* test(klient): pin invalid-input behavior across providers in e2e

* refactor(agent-core-v2): merge kimi traits, split bases by protocol

- merge the seven kimi trait modules into kimi.contrib.ts as two trait
  objects: kimiOpenAITrait (all native-transport hooks) and
  kimiAnthropicTrait (thinking only)
- move bases/* implementations into per-protocol directories (openai/,
  anthropic/, google-genai/) and rename openai.contrib.ts to
  openai-legacy.contrib.ts
- add per-directory index.ts registration barrels (import = registration)
  and exempt them in check-domain-layers.mjs alongside *.contrib.ts

* refactor(agent-core-v2): reorganize model and request-layer types

- consolidate shared model types (ModelOverrides, CompletionBudgetConfig/Params,
  ResolvedModelAuthMaterial, ThinkingDefaults/ModelThinkingMetadata) into
  kosong/model/model.types.ts and drop modelOverrides.ts
- rename L2 request types to the ModelRequest* prefix: LLMEvent ->
  ModelRequestEvent, LLMRequestInput -> ModelRequestInput,
  LLMCallParams -> ModelRequestParams
- extract ModelRequestTiming to replace three duplicated copies of the
  stream-timing shape
- rename L3 llmRequester types with the Agent prefix to match
  IAgentLLMRequesterService (AgentLLMRequestOverrides/Finish/Task/Source/
  PartHandler/LogFields)
- delete the unused LLMRequestParams type

* refactor(agent-core-v2): fold kosong/catalog into kosong/model

- merge IModelCatalogService's enumeration surface (listModels /
  listProviders / getProvider / setDefaultModel and the wire shapes) into
  IModelCatalog; delete kosong/catalog/modelCatalog.ts
- move the remote refresh path to the new IProviderDiscoveryService
  (discovery.ts + discoveryService.ts, renamed from
  catalog/modelCatalogService.ts)
- relocate configSection / errors to discoveryConfigSection.ts / errors.ts;
  DEFAULT_MODEL_SECTION now lives in kosong/model/model.ts
- drop the L3 catalog layer from check-domain-layers.mjs
- kap-server routes / refresh scheduler / channelRegistry follow the split;
  klient renames the contract modelCatalogService to modelResolver and adds
  providerDiscovery; kimi-inspect reads via IModelCatalog
- modelRequesterImpl: drop the streamedAnyPart backfill, onMessagePart
  already delivers every part
- tests: remove the app/modelCatalog and kosong/catalog suites, add the
  kosong/model catalog and discovery suites

* feat(klient): trim trailing undefined args and add boundary smoke probes

- add trimTrailingUndefined helper so optional trailing args no longer
  cross the wire as null in http/ipc transports, which defeated
  server-side default parameters
- add model-requester-boundary smoke probe for ChatProvider error
  wrapping behavior against real config and a local stub
- add kimi-select-tools smoke probe verifying the kimi-only wire
  encoding of dynamic tool declarations
- extend smoke.ts with a models set/get/delete round-trip, catalog
  list assertions, and update AGENTS.md with the new scripts

* fix(agent-core-v2): declare openai chat hooks as function properties

Method-shorthand members on OpenAIChatCompletionsHooks tripped
typescript-eslint(unbound-method) at every extraction site
(`const hook = this._hooks?.convertMessage` and friends), failing the
repo-wide lint job. Every implementation is a plain closure composed by
openaiHooks.ts, so declare the members as function-typed properties,
which matches the actual semantics and clears the four errors.

* fix: repair stale references surfaced by the origin/main rebase

- agent-core-v2: point vacuousContent's ContentPart import at kosong/contract
- kap-server: rewrite the transcript test seed as IModelCatalog (IModelResolver is gone)
- klient: inline onceEvent/waitFor after the http transport helpers were dropped
- kimi-inspect: remove useLiveEvent from ModelCatalogView; catalog polls on a slow interval

* chore: downgrade the kosong architecture changeset to patch
2026-07-21 13:03:54 +08:00
liruifengv
73eb5f89e0
fix(tui): remove red coloring from code syntax highlighting (#1995) 2026-07-21 12:54:47 +08:00
liruifengv
115b0968ce
fix(tui): hide system-trigger messages in resume replay (#1990)
Goal mode stores its per-turn continuation prompt as a user message with
a system_trigger origin. Live rendering never shows these model-facing
messages, but resume replay only filtered three specific system_trigger
names, so the continuation prompt leaked into the transcript as a user
message. Skip all system_trigger user messages during replay, matching
the live path and the markdown exporter, and drop the two goal-specific
filters this makes redundant.
2026-07-21 12:16:46 +08:00
Kai
71bcfba54a
fix(agent-core): drop vacuous assistant messages that permanently wedge sessions (#1968)
* fix(agent-core): drop vacuous assistant messages that permanently wedge sessions

A provider-filtered response can seal an assistant message holding only an
empty thinking part into the recorded history. The projection's empty-message
guard only counted parts, so it survived, and was serialized as an assistant
message with no content and no tool calls — which the provider rejects with
"the message at position N with role 'assistant' must not be empty" on
every resend, permanently wedging the session.

The projection now drops any message whose parts all serialize to nothing
(empty/whitespace text, or empty unsigned thinking). Content-bearing
messages keep every part verbatim, including empty thinking blocks that
preserved-thinking providers require back. Drops surface through a new
vacuous_message_dropped projection anomaly with log/telemetry counters, and
the structural-error recovery now recognizes this provider rejection so the
strict resend self-heals any residual path. Mirrored in agent-core-v2.

* docs(agent-core-v2): move vacuous-message rationale into the file header

The v2 comment convention keeps comments solely in the top-of-file block.
Move the vacuous-message drop rationale there and drop the inline/JSDoc
comments added beside statements in the projector service, the llmProtocol
error patterns, and the projector tests. No behavior change.

* fix(agent-core-v2): drop output-free steps at fold settle time

The loop-event fold already dropped a settled assistant message when it was
structurally empty (no content, no tool calls), but a step that recorded
only an empty thinking part — e.g. a provider-filtered response carrying an
empty reasoning field — survived into history, relying on the projection to
keep it off the wire. Widen the settle condition to drop the step whenever
nothing sendable was recorded (no tool calls; every content part vacuous),
using a predicate now shared with the context projector. This heals stored
v2 histories at restore time instead of only at request time.

* fix(agent-core): drop duplicate-stripped assistants left with only vacuous content

The strict resend's dedupe pass could re-create the exact empty-assistant
shape the projection now guards against: when every tool call a message
carried was removed as a later duplicate, a remainder holding only an empty
thinking part was kept (its content was non-empty), serialized as an
assistant message with no content and no tool calls, and rejected again
with "the message at position N with role 'assistant' must not be empty"
— the same failure the strict resend was recovering from.

The keep condition now requires sendable content, so such a message is
dropped wholesale with a vacuous_message_dropped anomaly alongside the
duplicate-tool-call one. Mirrored in agent-core-v2.

* fix(agent-core-v2): mirror output-free step drops in the transcript reducer

The live loop-event fold drops a settled assistant step when nothing
sendable was recorded (no tool calls; every content part vacuous), but the
cold transcript reducer still pushed an assistant on every step.begin and
never dropped it. Cold readers (snapshot / messages) therefore kept
showing output-free phantom assistants — including one per failed retry
attempt, which predates the vacuous-step case — and foldedLength overcounted
the live folded history, which can misplace the unflushed-tail splice in
the messages view while a turn is in flight.

The reducer now settles steps the same way the fold does: at a step's end
or at the next begin, an assistant with no tool calls and only vacuous
content is removed and foldedLength is decremented. Steps carrying any
sendable output — real text, real thinking, signed thinking, or tool calls
— are kept verbatim.

* fix(agent-core-v2): update vacuous-content predicate documentation for clarity

* docs(changeset): simplify the vacuous-assistant fix changelog entry
2026-07-21 11:04:55 +08:00
liruifengv
c2d7bebd04
docs(changelog): sync 0.28.1 from apps/kimi-code/CHANGELOG.md (#1975)
Some checks are pending
CI / lint (push) Waiting to run
CI / build (push) Waiting to run
CI / test (1) (push) Waiting to run
CI / test (2) (push) Waiting to run
CI / test (3) (push) Waiting to run
CI / test (4) (push) Waiting to run
CI / test (5) (push) Waiting to run
CI / test-pi-tui (push) Waiting to run
CI / test-windows (push) Waiting to run
CI / typecheck (push) Waiting to run
Nix Build / Check flake.nix workspace sync (push) Waiting to run
Nix Build / nix build .#kimi-code (push) Blocked by required conditions
Release / Release (push) Waiting to run
Release / Deploy docs (push) Blocked by required conditions
Release / Native release artifact (push) Blocked by required conditions
Release / Publish native release assets (push) Blocked by required conditions
2026-07-20 23:46:39 +08:00
github-actions[bot]
efacf0452d
ci: release packages (#1946)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-07-20 22:57:20 +08:00
tt-a1i
c5b6103bb9
fix(acp): allow configured provider auth (#934)
Some checks are pending
CI / test (2) (push) Waiting to run
CI / test (3) (push) Waiting to run
CI / test (4) (push) Waiting to run
CI / test (5) (push) Waiting to run
CI / test-pi-tui (push) Waiting to run
CI / test-windows (push) Waiting to run
Release / Release (push) Waiting to run
Release / Deploy docs (push) Blocked by required conditions
Release / Native release artifact (push) Blocked by required conditions
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
Nix Build / Check flake.nix workspace sync (push) Waiting to run
Nix Build / nix build .#kimi-code (push) Blocked by required conditions
Release / Publish native release assets (push) Blocked by required conditions
2026-07-20 22:05:17 +08:00
Haozhe
ad8cc85251
feat(cli): foreground-only web servers, deprecated kimi server kill (#1967)
- the /web slash command now always starts a new server instead of offering to reuse a running one
- remove the kimi web kill and kimi web ps subcommands; foreground servers stop with Ctrl+C
- keep kimi server kill as a deprecated fallback that only stops servers started by a version before 0.28.0 (recorded in the legacy server lock)
2026-07-20 21:50:09 +08:00
liruifengv
dde92fe5ab
feat: support max and exact version targeting for tips banner (#1960)
* feat: support max and exact version targeting for tips banner

Add optional banner_max_version (exclusive upper bound) and banner_version (exact match) fields to the remote tips banner schema. They combine with banner_min_version (all must pass) and apply to both the active banner and fallback list entries. Invalid semver constraints fail closed and hide the banner.

* chore: drop changeset for banner version targeting
2026-07-20 20:08:52 +08:00
qer
bcee3ac542
chore(vscode): release 0.6.4 (#1958) 2026-07-20 18:42:13 +08:00
qer
9223a37622
feat(vscode): scope thinking effort to the current session (#1951) 2026-07-20 18:27:42 +08:00