Commit graph

1000 commits

Author SHA1 Message Date
github-actions[bot]
16c7189bd5
ci: release packages (#2244)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-07-29 11:35:59 +08:00
liruifengv
973e2a008c
chore: prune non-user-facing changesets before release (#2335)
* chore: prune non-user-facing changesets before release

- Drop changesets for changes users cannot perceive: internal bash
  parser groundwork, host-identity server start option, and the
  structured managed-usage refactor
- Drop the duplicate agent-core-v2 repeat-breaker entry and remap the
  v1 fix to @moonshot-ai/kimi-code with user-facing wording
- Remove the untouched @moonshot-ai/kimi-code-sdk entry from the
  quota-exhausted fail-fast changeset
- Downgrade the upload size-cap removal to patch and drop the
  implementation detail from its wording

* chore: simplify verbose changeset entries

* chore: shorten changeset entries to single statements
2026-07-29 11:27:45 +08:00
Yufeng He
67dd03149f
feat(tui): customizable footer status line via status_line config (#2255)
Some checks are pending
CI / build (push) Waiting to run
CI / test (1) (push) Waiting to run
CI / test (2) (push) Waiting to run
CI / test (3) (push) Waiting to run
CI / test (4) (push) Waiting to run
CI / test (5) (push) Waiting to run
CI / test-pi-tui (push) Waiting to run
CI / test-windows (push) Waiting to run
CI / lint (push) Waiting to run
CI / typecheck (push) Waiting to run
Nix Build / Check flake.nix workspace sync (push) Waiting to run
Nix Build / nix build .#kimi-code (push) Blocked by required conditions
Release / Release (push) Waiting to run
Release / Deploy docs (push) Blocked by required conditions
Release / Native release artifact (push) Blocked by required conditions
Release / Publish native release assets (push) Blocked by required conditions
* feat(tui): customizable footer status line via status_line config

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

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

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

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

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

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

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

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

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

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

Changeset text tightened per maintainer note.

---------

Co-authored-by: Kai <me@kaiyi.cool>
2026-07-28 22:37:52 +08:00
Haozhe
ceaa96942b
feat(kap-server): add global message search with literal and live-session modes (#2321)
* feat(kap-server): add the /api/v1/search global message search endpoint

Cross-session full-text search over user messages, assistant text and
session titles, backed by a minidb index under <home>/search-index with a
single-writer lock election and read-only WAL catch-up for other
processes. Hits carry transcript anchors (turn ordinal and step id) so
clients can jump straight to the matching turn or step.

* feat(kimi-inspect): add a search view with chat-timeline navigation

The left rail gains a Search view over the global search endpoint, with
role and sort filters and cursor pagination. Clicking a hit switches to
the chat view and navigates to its session, agent, turn and step — the
channel pages the turn into the loaded window, scrolls it into view and
flashes the target briefly.

* chore(kimi-code): start the dev server without built web assets

The repo's dev server scripts (dev:server, dev:kap-server,
dev:kap-server:multi, dev:server:restart) now set KIMI_CODE_DEV_SERVER=1.
When it is set and dist-web/index.html is missing, kimi web starts the
API server without the bundled web UI instead of failing at startup,
so backend dev no longer requires a kimi-web build.

* feat(kap-server): add literal substring search and a live session route

- minidb: text indexes accept an injectable tokenizer/queryTokenizer,
  and a hashed 2/3-gram tokenizer (NFKC + lowercase, code-point
  windows) backs substring search; tokenizer names persist in
  db.textindexes.json with backward-compatible defaults
- /api/v1/search gains mode: 'literal' — n-gram candidates confirmed
  against the original text (zero false positives), with an
  'candidate_cap' incomplete flag when the candidate set truncates
- container.session_id queries against a session live in this process
  scan the in-memory transcript store instead of the index (both
  modes); the response's source: live|index field names the serving
  route and rides in the page-token fingerprint
- kimi-inspect: exact-match toggle and source badge in the search
  view, plus an in-chat session search bar with jump-to-hit navigation

* test(kimi-inspect): avoid stringifying BodyInit in search api tests
2026-07-28 21:47:50 +08:00
Haozhe
f79fde2b90
feat(node-sdk): migrate the SDK method surface to agent-core-v2 (#2262)
* feat(node-sdk): add agent-core-v2 backed SDKRpcClientV2 harness

- add SDKRpcClientV2 wiring the v2 engine (DI x Scope) in-process via the
  klient memory transport, with getExperimentalFeatures migrated to
  klient.global.flags.list() and unmigrated methods failing fast
- export createKimiHarnessV2 / SDKRpcClientV2 from the SDK index
- wire the experimental v2 gate into the CLI interactive shell (run-shell)
- extend build-dts to bundle agent-core-v2 and klient declarations

* feat(node-sdk): migrate listWorkspaceSkills to agent-core-v2

- add engineAccessor escape hatch exposing the in-process engine's app-scope
  service accessor for SDK methods the klient facade does not cover yet
- implement listWorkspaceSkills via ISkillDiscovery plus the v2 user/project
  root helpers and BUILTIN_SKILLS (plugin skills and skillDirs still gaps)
- add a v1-v2 parity test pinning identical return values per migrated
  method, with understood gaps listed explicitly in KNOWN_DIFFS

* feat(node-sdk): migrate the SDK method surface to agent-core-v2

- implement the remaining SDKRpcClientBase methods on SDKRpcClientV2,
  routed through the klient facade where covered, the engineAccessor
  escape hatch where the engine has a service, or SDK-side rebuilds on
  v2 primitives where only primitives exist (config shape mapping,
  global mcp.json store, MCP OAuth flows, importContext, session
  warnings, print background policy)
- translate the v2 event stream into the v1 Event union and bridge
  approval/question/user_tool interactions per live session
- rebuild resume replay by folding the v2 wire.jsonl through the v1
  agent restore pipeline, so resumed sessions render history again
- keep deleteSession as not_implemented; the v2 engine has no delete
  capability
- extend the v1-v2 parity suite to every migrated method, pinning
  understood engine differences in KNOWN_DIFFS
- add the dev:cli:v2 root script to launch the TUI on the v2 engine

* fix(node-sdk): await the v2 undo and compaction-cancel agent calls

* test(cli): spread the real oauth module in the telemetry test mock

* feat(node-sdk): forward v2 engine telemetry to the host telemetry client

* feat(cli): gate the v2 TUI route behind a dedicated KIMI_CODE_TUI_V2 switch

* fix(node-sdk): honor skillDirs on the agent-core-v2 SDK route

The v2 SDK client accepted KimiHarnessOptions.skillDirs (the CLI's
--skills-dir) but never seeded it into the engine, so explicit skill
dirs were silently dropped on the v2 TUI route and the Skill tool
could not find skills from them. Seed skillCatalogRuntimeOptions at
bootstrap and let listWorkspaceSkills resolve the explicit dirs as
the user source, matching the engine's session skill catalog.

* refactor(cli): gate the v2 TUI route behind the master experimental flag again

Drop the dedicated KIMI_CODE_TUI_V2 switch: the TUI v2 harness route is
gated by KIMI_CODE_EXPERIMENTAL_FLAG, the same master switch as the
kimi -p v2 route. The gate tests are kept with updated assertions, and
dev:cli:v2 sets the master flag again.
2026-07-28 20:29:45 +08:00
7Sageer
d88b3775c9
fix(agent-core-v2): count validation-rejected tool calls toward the repeat breaker (#2317)
* fix(agent-core-v2): count validation-rejected tool calls toward the repeat breaker

* style(agent-core-v2): remove inline implementation comments
2026-07-28 19:32:46 +08:00
STAR-QUAKE
de0ba9d065
fix(agent-core): count validation-rejected tool calls toward the repeat breaker (#2313)
* fix(agent-core): count validation-rejected tool calls toward the repeat breaker

Args-rejected calls returned before prepareToolExecution, so the breaker
never counted them and the model could re-issue the same invalid call
until maxSteps. Register them in finalizeToolResult so reminders fire at
3/5/8 and the turn force-stops at 12.

* fix(agent-core): key parse-failed repeats on raw argument text

Malformed JSON arguments normalize to {} on parse failure, which keyed
every malformed-but-different attempt identically and could force-stop a
turn whose calls were evolving rather than identical. Register skipped
calls on the raw arguments text when parsing failed.

---------

Co-authored-by: fengchenchen <fengchenchen@moonshot.ai>
2026-07-28 18:39:26 +08:00
Haozhe
d03a4886fd
feat(server): remove the 50 MiB upload size cap and stream uploads to disk (#2312)
Some checks are pending
CI / build (push) Waiting to run
CI / test (1) (push) Waiting to run
CI / test (2) (push) Waiting to run
CI / test (3) (push) Waiting to run
CI / test (4) (push) Waiting to run
CI / test (5) (push) Waiting to run
CI / test-pi-tui (push) Waiting to run
CI / test-windows (push) Waiting to run
CI / lint (push) Waiting to run
CI / typecheck (push) Waiting to run
Nix Build / Check flake.nix workspace sync (push) Waiting to run
Nix Build / nix build .#kimi-code (push) Blocked by required conditions
Release / Release (push) Waiting to run
Release / Deploy docs (push) Blocked by required conditions
Release / Native release artifact (push) Blocked by required conditions
Release / Publish native release assets (push) Blocked by required conditions
- add writeStream/putStream to the storage and blob store interfaces so
  large values are written incrementally (tmp + fsync + rename) instead of
  being buffered in memory
- FileService.save now streams the request body straight to the blob store
  and only counts bytes for FileMeta.size
- drop the multipart fileSize limit and the file.too_large error from the
  /files upload path (41301 stays in the wire protocol for session export)
2026-07-28 17:01:45 +08:00
liruifengv
b0f43aea28
feat(oauth): return structured managed usage rows (#2300)
* feat(oauth): return structured managed usage rows

Stop formatting plan-usage labels and reset hints into English strings
at the oauth layer. The parser still absorbs backend field drift, but
now emits a stable structured row (name / window{duration,unit} / used
/ limit / resetAt) that kap-server passes through and clients localize
themselves:

- window: normalized from duration/timeUnit (minute/hour/day/week),
  whole-hour minute windows fold to hours, unnamed summaries are the
  weekly limit
- resetAt: absolute ISO timestamp; relative reset_in/ttl seconds are
  converted at parse time
- TUI /usage panel formats labels and reset hints locally

* refactor(oauth): parse managed usage strictly to the current payload shape

Drop the defensive drift tolerance (alternate reset-time spellings,
reset_in/ttl seconds, remaining-derived used, title/scope names,
top-level duration/timeUnit, fuzzy time-unit matching) and parse only
what the platform actually sends: numeric strings, resetTime, nested
detail/window records, TIME_UNIT_* enums.

* fix(node-sdk): update managed usage smoke example and facade tests for structured rows
2026-07-28 15:03:25 +08:00
Petrichor
cdbd33c13c
fix(kosong): fail fast on quota-exhausted 429 instead of retrying (#1857)
* fix(kosong): fail fast on quota-exhausted 429 instead of retrying

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Third review round on #1857:

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

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

Fourth review round on #1857:

- Drop the AnthropicHooks member JSDoc (its content already lives in the
  anthropic.ts and anthropicHooks.ts file headers) and fix the anthropic
  contrib header still calling the hook set single-hook.
- Add the missing KimiFiles regression in both engines: a mocked files
  client rejecting with a Moonshot quota 429 makes uploadVideo reject
  with the non-retryable APIProviderQuotaExhaustedError, locking the
  classifyKimiQuotaError argument at the upload catch sites.
2026-07-28 14:35:12 +08:00
Haozhe
e556088458
feat(tree-sitter-bash): add a pure-TypeScript bash parser and an agent-core-v2 bashParser service (#2016)
* feat(tree-sitter-bash): scaffold pure-TypeScript bash parser package

Add packages/tree-sitter-bash with the SyntaxNode tree model (UTF-16
code-unit offsets, tree-sitter-bash named-node type names), a parse
budget (deadline + node cap) with an aborted ParseResult variant, and
a placeholder parse() to be replaced by the real lexer/parser.

* feat(tree-sitter-bash): add lexer and core recursive-descent parser

Cover the permission-analysis grammar subset: lists, pipelines,
commands, words, quotes, expansions, command/process substitution,
subshells, the full redirect operator set, heredocs and comments,
with tree-sitter-bash named-node type names. Long scan loops check
the parse deadline via budget.progress() so large literals cannot
exhaust the node cap; parse depth is bounded on all recursion
chains.

* feat(tree-sitter-bash): support the full bash grammar

Add compound commands (if/while/until/for/c-style-for/select/case/
function/compound/do_group), test commands with reference-exact
extglob/regex right-hand-side rules, a Pratt expression engine for
arithmetic expansion shared across arith/c-for/test modes, arrays
and subscripts, declaration/unset commands, ansi-c/translated
strings and brace expressions. Reserved words are recognized only
in statement position. Case-aware balanced scanning keeps command
substitutions intact around case items, expression leftovers are
kept as ERROR nodes instead of dropped, and recursion depth is
bounded per chain (substitution 150, parse 500, lexer scan 1024).

* test(tree-sitter-bash): add differential fixtures, corpus, fuzz and perf suites

Turn the ad-hoc wasm comparison work into permanent infrastructure:
a differential helper pinning reference-equivalent and
known-difference samples against the real tree-sitter-bash wasm
(478 match / 79 known-diff fixtures plus the official v0.25.0
corpus), a three-way consistency check between fixtures, the
known-difference registry and the README, deterministic seeded
fuzz with tree-integrity assertions, and performance smoke tests.
Converges 15 further divergence groups found by systematic probing
and documents the rest; the full suite is 853 tests in ~4s.

* feat(agent-core-v2): add App-scope bashParser service

Wrap the pure @moonshot-ai/tree-sitter-bash package as
IBashParserService (L1, no dependencies): parse(source, options)
returns a wire-safe BashParseResult whose nodes drop the cyclic
parent link so trees can cross the RPC boundary. Budget exhaustion
surfaces as { ok: false, reason: 'aborted' }, malformed input as
hasError — never a throw.

* chore: add changeset for the bash parser service

* chore: update pnpmDeps hash after adding tree-sitter-bash package

* fix(agent-core-v2): register bashParser service with ScopeActivation

The registration was written against the removed InstantiationType API
(#/_base/di/extensions); switch to ScopeActivation.OnDemand from
#/_base/di/scope so the package typechecks and the service registers.

* feat(kimi-inspect): add Bash Parser view

Add a fourth icon-rail tab that exercises the App-scope bash parser
service over the debug RPC surface: a source textarea with a parse
budget (timeoutMs / maxNodes), a dropdown of curated examples adapted
from the tree-sitter-bash differential fixtures, and an expandable
syntax tree with per-node type, UTF-16 range and leaf text, plus
hasError / aborted / node-count badges.

* fix(agent-core-v2): snapshot bash syntax trees iteratively

A long left-associative chain (e.g. an arithmetic expression with a
few thousand operands) parses into a tree thousands of levels deep
while still within budget; the recursive DTO conversion then overflowed
the JS call stack and made parse throw RangeError, breaking the
never-throws contract. Convert the tree with an explicit stack, the
same approach as the parser's own materialize.

* feat(kimi-inspect): add a deep-arithmetic example to the Bash Parser view

A thousand-operand left-associative chain fills the textarea with a
thousand-level binary_expression tree — the shape that once overflowed
the DTO conversion. Deeper chains still parse in-process but cannot
cross the JSON RPC transport (V8 call-stack limit in serialization),
so the example stays within the wire limit.

* chore: update pnpmDeps hash for the rebased lockfile

The rebase onto main merged pnpm-lock.yaml, invalidating the recorded
fetchPnpmDeps hash; use the hash CI computed for the merged lockfile.
2026-07-28 14:29:08 +08:00
qer
425cfdf53f
chore(web): upgrade markstream-vue to 1.0.7 (#2294)
* chore(web): upgrade markstream-vue to 1.0.7

Fix garbled code-block line numbers reported on 0.29.2: the async
code-block loading fallback rendered unstyled (proportional font,
code overlapping the line-number gutter) and with an over-estimated
reserved height that clipped leading lines. markstream-vue 1.0.6 adds
self-contained fallback styles and 1.0.7 fixes the height estimate.

* chore(nix): update pnpmDeps hash for markstream-vue 1.0.7

* chore(web): scope lockfile update to the markstream-vue graph

Regenerate pnpm-lock.yaml with a plain install so only markstream-vue
and its transitive deps move (markdown-it-ts, stream-markdown-parser,
etc.); unrelated toolchains (e.g. lightningcss in kimi-inspect) stay
pinned as before. Addresses review feedback.

* chore(nix): update pnpmDeps hash after lockfile scoping

* chore(changeset): shorten release note to one user-facing sentence
2026-07-28 13:25:38 +08:00
Haozhe
7e30add445
feat(kap-server): add global fs:mkdir endpoint (#2281)
* feat(kap-server): add global fs:mkdir endpoint

Add POST /api/v1/fs:mkdir to create a directory on the host filesystem
by absolute path, backing the folder picker's "new folder" action.
Implemented directly on node:fs/promises.mkdir in the transport layer
for now, non-recursive by design, with wire errors mapped to the
existing fs.* codes (40001/40409/40411/40919).

* test(kap-server): update api surface snapshot for fs:mkdir

* test(kap-server): stop export tests from holding server.close() open

The export download tests reused pooled undici keep-alive connections,
so afterEach's server.close() could wait out fastify's 72s default
keepAliveTimeout and die on the 10s hook timeout (flaky on CI). Send
connection: close on the streamed export requests, matching the
fs:content tests.
2026-07-28 10:50:39 +08:00
7Sageer
77618e38c3
feat(kap-server): wire cloud telemetry for engine events (#2230)
Some checks are pending
CI / build (push) Waiting to run
CI / test (1) (push) Waiting to run
CI / test (2) (push) Waiting to run
CI / test (3) (push) Waiting to run
CI / test (4) (push) Waiting to run
CI / test (5) (push) Waiting to run
CI / test-pi-tui (push) Waiting to run
CI / test-windows (push) Waiting to run
CI / lint (push) Waiting to run
CI / typecheck (push) Waiting to run
Nix Build / Check flake.nix workspace sync (push) Waiting to run
Nix Build / nix build .#kimi-code (push) Blocked by required conditions
Release / Release (push) Waiting to run
Release / Deploy docs (push) Blocked by required conditions
Release / Native release artifact (push) Blocked by required conditions
Release / Publish native release assets (push) Blocked by required conditions
* feat(kap-server): wire cloud telemetry for engine events

The v2 engine registers a full telemetry event catalog, but kap-server
never attached an appender, so events from web-hosted sessions were
dropped to the null appender. Add an opt-in `telemetry` start option
that attaches a CloudAppender (app_name kimi-code-cli, ui_mode web,
matching the v1 `kimi web` host conventions), still gated by the
config `telemetry` toggle, with periodic flush and a bounded flush on
close. The option defaults off so tests never post to the real
endpoint; the CLI's `kimi web` host enables it.

* fix(kap-server): honor telemetry disable environment

* fix(agent-core-v2): isolate session telemetry context

* fix(kap-server): seed telemetry client version

* fix(cli): share telemetry shutdown deadline

* fix(telemetry): make shutdown ownership durable

* fix(kap-server): keep telemetry shutdown best-effort
2026-07-27 17:18:16 +08:00
qer
29783e471a
feat(cli): add plugin quota and update notices (#2147)
* feat(cli): add plugin quota and update notices

- Show "Note: This plugin consumes your quota." after installing
  quota-consuming official plugins (currently Kimi Datasource).
- Show a one-time update notice after invoking an outdated plugin (a
  plugin MCP tool call or a /<plugin>:<command> turn); the last
  notified version is persisted so each new marketplace version
  reminds once.
- Skip the third-party trust prompt for loopback sources that mirror
  the official plugin CDN path, so the dev plugin marketplace no
  longer prompts when installing official plugins.

* feat(cli): report plugin update notices at turn end

Buffer plugin MCP tool usage during the turn and report it together
with plugin command usage when the turn's output has fully ended,
instead of firing the check mid-turn at tool result time. Cancelled
turns no longer trigger the notice.

* fix(cli): refresh plugin MCP map on miss and serialize notice writes

Address review findings on the plugin update notifier:

- The memoized MCP server-to-plugin map is reused across /reload,
  /new, and session switches, so plugins installed or enabled later in
  the same app run never resolved. Refresh the map once on a lookup
  miss, and never pin an empty map when there is no session.
- Concurrent notices (a turn that used two outdated plugins) raced on
  the read-modify-write cycle of the notice state file and could drop
  each other's entries. Serialize checks through a promise queue so
  each notified version is persisted exactly once.

* fix(cli): gate plugin notices on official provenance and survive tool-name truncation

Address review findings:

- The quota note and the update notice keyed on the plugin id alone,
  so a local/GitHub fork reusing a billed plugin's manifest id was
  treated as the official build. Both now require official provenance
  (a zip install from the official CDN plugin path or its loopback dev
  mirror) via a shared isOfficialPluginInstall check.
- Resolving plugin MCP tools by splitting on the '__' separator broke
  for qualified names core truncates to 64 chars, which can cut the
  separator. Match known server names by longest prefix with a name
  boundary instead, which survives truncation as long as the server
  part itself is intact.

* revert(cli): drop the dev marketplace trust relaxation

The loopback carve-out let any local service bypass the third-party
trust prompt by serving a zip under the official path shape, which
does not prove official provenance (review P1). Revert to the single
rule — only https://code.kimi.com/kimi-code/plugins/official/* is a
trusted official source — and restore the stock dev marketplace
server. Installing official plugins from the dev marketplace shows
the trust prompt again.

* fix(cli): restrict update notices to the official catalog and settle tests

- Skip the update check when the loaded marketplace is not the default
  official catalog, so a custom KIMI_CODE_PLUGIN_MARKETPLACE_URL can
  no longer produce a notice that claims to come from the Official
  Marketplace.
- Return a never-rejecting promise from the notifier entry points so
  tests await the serialized queue directly instead of relying on
  zero-delay timers for ordering.
2026-07-27 17:00:42 +08:00
Haozhe
a77ee03829
feat(agent-core-v2): add hostIdentity domain for host-overridable system prompt identity (#2144)
* feat(agent-core-v2): add hostIdentity domain for host-overridable system prompt identity

- add app-scope hostIdentity domain (L3) with productName / replyStyleGuide
  overrides and hostIdentitySeed for composition roots
- render ${product_name} and ${reply_style_guide} in the base system prompt,
  falling back to CLI defaults when the host provides no override
- seed the variables from AgentProfileService via IHostIdentity
- expose hostIdentity on kap-server's ServerStartOptions

* chore: add changeset for host identity system prompt

* fix(agent-core-v2): adapt hostIdentity registration to ScopeActivation

The DI refactor on main removed _base/di/extensions (InstantiationType);
register with ScopeActivation.OnScopeCreated instead, and regenerate the
state manifest for the new SystemPromptContext fields.

---------

Co-authored-by: liruifengv <liruifeng1024@gmail.com>
2026-07-27 16:55:41 +08:00
7Sageer
086769bfad
chore: drop the changelog entry for the experimental secondary-model API (#2243)
The secondary-model feature is still experimental (gated by
KIMI_CODE_EXPERIMENTAL_SECONDARY_MODEL and ignored by the interactive
TUI), so the config API support merged in #2228 should not produce a
user-facing changelog entry yet.
2026-07-27 16:47:43 +08:00
7Sageer
3b017821cf
feat(kap-server): accept secondary_model in the config API (#2228)
* feat(kap-server): accept secondary_model in the config API

POST /api/v1/config now accepts secondary_model, persisted to the
[secondary_model] config section via the generic per-domain dispatch.
GET /config also hides the synthesized __secondary__ derived entry
from the models view, matching the GET /models listing.

* Update config-api-secondary-model.md

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

---------

Signed-off-by: 7Sageer <sag77r@hotmail.com>
2026-07-27 16:45:18 +08:00
liruifengv
a9af42e698
docs(changelog): sync 0.29.2 from apps/kimi-code/CHANGELOG.md (#2236) 2026-07-27 15:04:24 +08:00
github-actions[bot]
8a45f10edd
ci: release packages (#2145)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-07-27 14:28:12 +08:00
liruifengv
48bf3d4c28
feat(kap-server): bundle the desktop app log into session exports on request (#2223)
* feat(kap-server): bundle the desktop app log into session exports on request

* refactor(agent-core-v2): align the desktop log export with repo conventions
2026-07-27 12:07:58 +08:00
7Sageer
d40d0d305d
refactor(agent-core-v2): make undo domain-owned (#2055)
* refactor(agent-core-v2): rebuild undo as wire-level journal rewind

Replace the compensating context.undo op with a wire-layer rewind
primitive: a log.cut control record with a persisted target, applied
uniformly by the wire during fold. Turn boundaries become first-class
(TurnIndexModel indexing turn.prompt record positions), models declare
a temporal classification (rewindable), and a single
IAgentRewindService owns the undo pipeline (quiesce -> precheck ->
cut -> reconcile) with all entry points converged.

- wire: log.cut record, rewindable model flag, re-fold rebuild;
  OpApplyContext.recordIndex for position-aware reducers
- rewind service: aborts the active turn, cancels in-flight
  compaction, preserves the pending queue, rebases measured tokens,
  reconciles lastPrompt, tracks conversation_undo
- todo list, plan mode, task-notification delivery and the turn index
  now rewind together with the undone turns
- transcript reducer applies cut ranges so snapshot/messages surfaces
  stay consistent with the model context
- REST/RPC/debug undo entry points converge on the rewind service;
  TUI parses the v2 undo-unavailable error shape
- legacy context.undo records keep replaying for old journals

* refactor(agent-core-v2): keep undo domain-owned

* refactor: enhance /undo functionality for consistency and safety, including todo list rollback and improved event handling

* chore: clean up undo changeset artifacts

* refactor: rebuild rewind consistency

* fix: make conversation undo durable and consistent

* fix(agent-core-v2): stabilize undo restoration

* fix: keep TUI undo on legacy error contract

* refactor(agent-core-v2): drop unused full compaction cancel API

Undo now rejects with session.busy while compaction runs instead of
cancelling it, so the awaitable cancel() added for the earlier rewind
semantics has no callers left. Remove it from the interface and
implementation; the RPC cancel path keeps using the task abort
controller directly.

* fix(agent-core-v2): remove injected context on undo

* chore(agent-core-v2): regenerate wire manifest

* docs(agent-core-dev): rename rewind to undo in layer table

* fix(agent-core-v2): undo prompt-owned image reminders

* refactor: remove transcript undo reconciliation

* fix(kap-server): map undo busy errors

* refactor(agent-core-v2): rename undo participant registry and attribute checkpoint depth

- Rename IAgentConversationUndoReconciliationRegistry to
  IAgentConversationUndoParticipantRegistry (conversationUndoParticipants).
- Return the limiting model from checkpointDepth and include it in the
  SESSION_UNDO_UNAVAILABLE details; report checkpoint_lost instead of
  compaction_boundary when no compaction explains the missing depth.
- Add a registry invariant test: every model reacting to context.* ops
  must be registered via defineCheckpointedModel or explicitly exempt.

* Delete .changeset/fix-undo-injections.md

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

---------

Signed-off-by: 7Sageer <sag77r@hotmail.com>
2026-07-27 11:09:26 +08:00
Haozhe
cc9b25e132
fix(kap-server): flush public and control WS frames immediately (#2221)
Only subscription events now enter the timed coalescing buffer.
server_hello, acks, resync_required, and global session events join the
outbound FIFO and flush it right away, so they no longer wait behind the
flush window and cannot overtake earlier buffered subscription frames.
The broadcaster marks global fan-out with the new BroadcastDelivery
'immediate' lane.
2026-07-27 10:58:38 +08:00
Luyu Cheng
0cef160c4b
fix(agent-core): continue goal pursuit when a goal turn hits the per-turn step limit (#2210)
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
Release / Native release artifact (push) Blocked by required conditions
CI / test-pi-tui (push) Waiting to run
CI / test-windows (push) Waiting to run
CI / lint (push) Waiting to run
CI / typecheck (push) Waiting to run
Nix Build / Check flake.nix workspace sync (push) Waiting to run
Nix Build / nix build .#kimi-code (push) Blocked by required conditions
Release / Release (push) Waiting to run
Release / Deploy docs (push) Blocked by required conditions
Release / Publish native release assets (push) Blocked by required conditions
* fix(agent-core): continue goal pursuit when a goal turn hits the per-turn step limit

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

* fix(agent-core): start goal pursuit when the goal-creating turn hits the step limit

* refactor(agent-core-v2): drop step-cap narration from goal module and test
2026-07-27 00:30:33 +08:00
Haozhe
bf8e967d5c
refactor(agent-core-v2): register tools as scoped services (#2196)
Some checks are pending
CI / build (push) Waiting to run
CI / test (1) (push) Waiting to run
CI / test (2) (push) Waiting to run
CI / test (3) (push) Waiting to run
CI / test (4) (push) Waiting to run
CI / test (5) (push) Waiting to run
CI / test-pi-tui (push) Waiting to run
CI / test-windows (push) Waiting to run
CI / lint (push) Waiting to run
CI / typecheck (push) Waiting to run
Nix Build / Check flake.nix workspace sync (push) Waiting to run
Nix Build / nix build .#kimi-code (push) Blocked by required conditions
Release / Release (push) Waiting to run
Release / Deploy docs (push) Blocked by required conditions
Release / Native release artifact (push) Blocked by required conditions
Release / Publish native release assets (push) Blocked by required conditions
* refactor(agent-core-v2): register agent tools as DI services with profile-aware activation

- replace module-level registerTool + Eager AgentBuiltinToolsRegistrar with registerAgentTool double registration (Agent-scope DI service + contribution table)
- add toolActivation domain: AgentToolActivationService filters contributions by the bound Profile's tool policy using declared names, resolves instances lazily via accessor.get, and re-activates on agent.status.updated
- rename BuiltinTool to AgentTool service interface; tools become Agent-scope services with decorator-injected dependencies (e.g. AgentTool -> SubagentTool/ISubagentTool)
- AgentLifecycleService.create runs one activation pass after restore and profile binding so tools reflect the Profile before the first turn
- update tool registrations, scripts, and tests; add toolActivationService tests

* refactor(agent-core-v2): centralize builtin tools under agent/tools

- move builtin tools from scattered domain folders (plan/tools,
  goal/tools, os/backends/node-local/tools, task/tools, etc.) into a
  unified agent/tools/ directory
- split each tool into a kebab-case definition file (bash.ts) and a
  registration file (bashTool.ts) pairing with its prompt markdown
- update imports across src, tests, kap-server, and TUI comments

* fix(agent-core-v2): keep agent tools out of scope-creation instantiation

Main's instantiateAll constructs every registered service at scope
creation, but agent tool constructors may legitimately throw when their
host capability is absent (e.g. WebSearchTool without a configured
provider), and profile-aware activation must stay the only resolution
path so the runtime registry holds real instances, never proxies.

- add SyncDescriptor.instantiateWithScope (default true) and let
  registerScopedService opt registrations out of the instantiateAll
  sweep
- registerAgentTool passes the opt-out, restoring lazy
  activation-driven construction on top of the eager-scope semantics
- regenerate docs/state-manifest.d.ts

* refactor(agent-core-v2): replace delayed DI with scope activation

- add explicit OnScopeCreated and OnDemand activation modes
- remove delayed proxy and idle initialization support
- migrate service registrations, tests, and DI guidance
2026-07-26 14:32:59 +08:00
Haozhe
7799bd7346
feat(agent-core-v2): add per-scope keyed state container (#2192)
* refactor(agent-core-v2): instantiate all registered services eagerly at scope creation

- add instantiateAll to scope creation: every service registered for a
  scope tier is constructed when the scope is created, following the
  static dependency graph; a failing constructor fails scope creation
- drop the hand-maintained eager-resolution lists: igniteEagerServices
  in AgentLifecycleService, the force-instantiated session services in
  SessionLifecycleService, and the kosong config bridge get in bootstrap
- update DI docs and agent-core-dev skill guidance for the new semantics
- adjust affected tests (registry hygiene, timer draining, listener
  ordering) and add scope-tree coverage for eager instantiation

* feat(agent-core-v2): add per-scope keyed state container (state domain)

- add _base StateRegistry: typed StateKey/defineState descriptors with
  register/get/set, per-key onDidChange and global onDidChangeAny events,
  and BugIndicatingError on duplicate or unregistered key access
- bind thin per-scope services at each tier: IStateService (App),
  ISessionStateService (Session), IAgentStateService (Agent), so scoped
  plain-data state lives in one observable container that dies with the scope
- register the state domain in the layer check script and export the new
  services from the package index
- add StateRegistry unit tests and scoped resolution tests

* refactor(agent-core-v2): move session-scope service state into ISessionStateService

- add StateRegistry.snapshot() with JSON-safe serialization for Map/Set/Date/circular values
- migrate plain-data fields of 13 session-scope services (cron, interaction, sessionActivity, agentProfileCatalog, fs, fsWatch, log, metadata, skillCatalog, toolPolicy, workspaceCommand, workspaceContext) to defineState keys registered in sessionState
- register SessionStateService in affected tests and add test/state/stubs.ts helper

* feat(kimi-inspect): rework chat layout with session pane and tabbed right dock

- add SessionPane column next to the sidebar: Services tab (pending
  interactions + session Service panels) and State tab polling
  ISessionStateService.snapshot() every second, rendered as a live
  diff tree
- merge the transcript audit panel and the agent inspector into one
  RightPanel with Audit/Agent tabs that keep panel state across switches
- extract InteractionsCard from Inspector into its own component
- collapse multiline strings in StateTree into a compact hover-preview
  button with a viewport-clamped fixed popup
- test: cover StateRegistry.snapshot() conversions (Map/Set/circular);
  pass a session state service to SessionInteractionService in kap-server
  test fakes
- update the AGENTS.md project map for the new layout

* refactor(agent-core-v2): move agent-scope service mutable state into agentState

Register each Agent-scope service's mutable fields into the agent-state
container (IAgentStateService) via defineState keys, and access them
through get/set accessors backed by states.get/set. Promise locks,
disposables, and other mechanism-only fields stay plain instance fields.

- define per-service state keys (e.g. activityView.*, goal.*, toolDedupe.*)
  and register them in each service constructor
- replace direct field reads/writes with states-backed accessors across
  the touched services
- update the affected unit tests for the new IAgentStateService dependency

* fix(agent-core-v2): collapse class instances in state snapshots

- StateRegistry.snapshot() now stops at custom-prototype boundaries and
  emits '(ClassName)' markers, so resource graphs reachable from registered
  values (e.g. tool instances holding service references) can no longer
  exhaust the heap during export; plain data keeps recursing
- add agent-scope state test covering the full assembled agent scope
- kimi-inspect: extract the session State card into a shared StateCard and
  add an agent State tab in RightPanel polling IAgentStateService.snapshot()
- document the per-scope state container pattern in the agent-core-dev skill

* refactor(agent-core-v2): keep resource-holding state out of the state container

- move fields holding live resources (tool entries, managed tasks, turn
  jobs, prompt records, MCP registrations, profile callbacks) back to
  plain private fields so the agent state service only holds
  snapshot-safe plain data
- add gen:state-manifest script that statically collects defineState
  keys and their register call sites into docs/state-manifest.d.ts
- add state manifest freshness test and update agent-state docs

* chore: add changeset for session-scope state container

* docs(agent-core-v2): move eager-instantiation notes into the scope.ts header

* fix(kimi-inspect): suppress stale state tree while switching state owner
2026-07-26 10:10:15 +08:00
Luyu Cheng
c497af60e6
fix(tui): steer user messages into the running turn while a goal is active (#2153)
Some checks failed
CI / build (push) Has been cancelled
CI / test (1) (push) Has been cancelled
CI / test (2) (push) Has been cancelled
CI / test (3) (push) Has been cancelled
CI / test (4) (push) Has been cancelled
CI / test (5) (push) Has been cancelled
CI / test-pi-tui (push) Has been cancelled
CI / test-windows (push) Has been cancelled
CI / lint (push) Has been cancelled
CI / typecheck (push) Has been cancelled
Nix Build / Check flake.nix workspace sync (push) Has been cancelled
Release / Release (push) Has been cancelled
Nix Build / nix build .#kimi-code (push) Has been cancelled
Release / Deploy docs (push) Has been cancelled
Release / Native release artifact (push) Has been cancelled
Release / Publish native release assets (push) Has been cancelled
* fix(tui): steer user messages into the running turn while a goal is active

* fix(tui): reset request state when steering mid-goal input fails

* fix(agent-core): update session prompt metadata on steer
2026-07-25 02:12:24 +08:00
Liu Zhongnuo
f06eb5c60e
feat(agent-core): defer registered user tools (#2119)
Some checks are pending
CI / test-pi-tui (push) Waiting to run
CI / test-windows (push) Waiting to run
CI / lint (push) Waiting to run
CI / typecheck (push) Waiting to run
CI / 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 / build (push) Waiting to run
Nix Build / Check flake.nix workspace sync (push) Waiting to run
Nix Build / nix build .#kimi-code (push) Blocked by required conditions
Release / Release (push) Waiting to run
Release / Deploy docs (push) Blocked by required conditions
Release / Native release artifact (push) Blocked by required conditions
Release / Publish native release assets (push) Blocked by required conditions
* feat(agent-core): defer registered user tools

Allow hosts to mark registered user tools as deferred so select_tools can discover and load their schemas on demand in both agent-core implementations.

* fix(agent-core): hide unregistered deferred tools

Filter loaded deferred user-tool schemas from provider history and loaded-state checks after unregister while preserving canonical history and disconnected MCP behavior.

* fix(agent-core-v2): drop stale inline schemas

Stop treating previously loaded user-tool schemas as active after the same tool is re-registered for inline disclosure.
2026-07-24 17:48:36 +08:00
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