Commit graph

615 commits

Author SHA1 Message Date
_Kerman
179aecf423
feat: log enabled experimental flags (#357) 2026-06-03 11:35:29 +08:00
liruifengv
9143fdadf6
docs(changelog): sync 0.8.0 from apps/kimi-code/CHANGELOG.md (#342) 2026-06-02 23:07:56 +08:00
github-actions[bot]
14cd8d098b
ci: release packages (#296)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-06-02 22:54:57 +08:00
Luyu Cheng
ac37d74484
feat: pursue a goal autonomously (#270) 2026-06-02 22:52:23 +08:00
_Kerman
191059d400
feat: add background ask user questions (#315) 2026-06-02 22:12:25 +08:00
qer
3502d870fc
chore: scrub internal identifiers from public text (#340) 2026-06-02 21:54:42 +08:00
Haozhe
7284f30479
fix(oauth): fix multi-provider custom registry import losing providers (#335)
- Add `applyCustomRegistryEntries` helper to apply all entries in-memory before a single write
- Update CLI Add Platform flow to use it, replacing interleaved in-memory mutations with removeProvider RPCs
- Add regression tests for repeated imports and provider field refreshing
2026-06-02 20:05:00 +08:00
qer
7cda9c3866
feat: add permission approval hooks (#336) 2026-06-02 19:57:10 +08:00
Haozhe
80164c2e97
fix(glob): normalize pattern before brace expansion (#311)
* fix(glob): normalize pattern before brace expansion

* fix(glob): preserve backslash-escaped metacharacters and normalize braces correctly

- expand braces before normalizing to prevent `..` from collapsing across brace groups
- skip normalization for patterns containing backslash-escaped glob metacharacters
- add test coverage for escaped braces and nested `..` inside alternatives
2026-06-02 19:30:32 +08:00
liruifengv
eeefa98083
feat(cli): add automatic and manual upgrades (#334) 2026-06-02 18:39:50 +08:00
7Sageer
7ffb5dd9b3
fix: drop unsigned thinking for Claude replay (#331) 2026-06-02 18:20:10 +08:00
liruifengv
1178c5cd14
feat: add todo list reminder injection (#333) 2026-06-02 18:12:38 +08:00
_Kerman
7a47045af2
fix: inherit custom tools in subagents (#330) 2026-06-02 17:16:59 +08:00
7Sageer
8809f3eb11
fix: normalize tool call ids across providers (#327) 2026-06-02 16:42:40 +08:00
_Kerman
fe7db4a7e3
feat: append TODO list as markdown to compaction summary (#319) 2026-06-02 16:20:52 +08:00
qer
0e57e739c7
docs: refine datasource skill description (#322)
* docs: refine datasource skill description

* chore: bump datasource plugin version
2026-06-02 16:07:33 +08:00
liruifengv
1f8c36af28
fix: improve tool output preview rendering in TUI (#317)
* fix: trim trailing empty lines from shell output previews

* fix: append … to multi-line Bash command header previews

* fix: truncate tool output by visual wrapped lines instead of raw newlines

* chore: add changeset for tool output preview fixes
2026-06-02 15:07:21 +08:00
7Sageer
3c5dee8836
feat(cli): add kimi provider subcommand for non-interactive provider management (#313)
* feat(cli): add `kimi provider` subcommand

Add a non-interactive equivalent of the TUI `/provider` command:

- `kimi provider add <url> --api-key <key>` imports every provider in a
  custom api.json registry, persisting `source` so the next TUI launch
  refreshes the model list automatically.
- `kimi provider remove <id>` deletes a provider and its model aliases.
- `kimi provider list [--json]` prints configured providers with model
  counts and source labels.
- `kimi provider catalog list [providerId] [--filter] [--url] [--json]`
  browses the public models.dev catalog.
- `kimi provider catalog add <providerId> --api-key <key> [--default-model]`
  imports a known provider straight from the catalog.

All actions reuse `fetchCustomRegistry`, `applyCustomRegistryProvider`,
`fetchCatalog`, and `applyCatalogProvider` from the existing oauth/SDK
helpers.

* fix(cli): satisfy oxlint rules in provider subcommand

- Use `Array#toSorted()` instead of `Array#sort()` to avoid mutating
  arrays returned from `Object.keys()` / `Object.entries()`.
- Drop redundant boolean-literal comparisons on `model.capability.*`
  fields (already typed as `boolean | undefined`).
- Remove the unnecessary `source as Record<string, unknown>` assertion
  in `providerSourceLabel` — `ProviderConfig.source` is already typed
  that way in the schema.
- Drop the empty-object fallback in `{ ...(config.models ?? {}) }`
  inside the test harness.

* fix(cli): address review findings on provider subcommand

P1 — `provider add`: `harness.removeProvider` re-reads the config from
disk (see `agent-core/src/rpc/core-impl.ts removeKimiProvider`), so
calling it mid-loop discarded providers we had already applied in
memory but not yet persisted. Importing a registry that added a new
provider then replaced an existing one silently lost the new one.
Drop every stale id up front in a single batch, then apply each entry
against the resulting fresh config.

P2 — `catalog add`: `applyCatalogProvider` always writes
`defaultThinking`. Hardcoding `false` would silently disable thinking
for thinking-capable models when the user had it on. Thread the prior
`defaultThinking` through.

P2 — `catalog add`: `removeProvider` clears `defaultModel` when it
pointed at one of the provider's aliases, so capturing
`previousDefaultModel` AFTER the removal yielded `undefined`. Capture
both `defaultModel` and `defaultThinking` BEFORE the removal so
re-importing a configured provider (e.g. to rotate the api key)
preserves the user's chosen default.

Tests:

- `makeHarness` now models the on-disk semantics of `removeProvider`
  (clears `defaultModel` when an alias matches, returns fresh disk
  view), so behavior that depended on the buggy in-memory mock is
  exercised honestly.
- Three new regression tests, each verified to fail against the
  pre-fix handler.

* fix(cli): address follow-up review on catalog default semantics

Two more findings on `catalog add`:

P2 — `default_thinking` fallback to `false` was wrong even after the
previous fix. `resolveThinkingLevel` (agent-core/.../thinking.ts:23)
treats `defaultThinking === false` as an explicit "off" request and
silently disables thinking before per-model defaults kick in. A
first-time `kimi provider catalog add anthropic --default-model
claude-opus-4-7` was therefore still persisting `default_thinking =
false` for thinking-capable models. The handler now always restores
the previous `defaultThinking` (including `undefined`) — the only
way to let the runtime resolver pick the per-model default.

P2 — Restoring `default_model` was unconditional, even when the
refreshed catalog no longer ships that model. `applyCatalogProvider`
drops the old aliases and only populates the current catalog, so
restoring an alias the catalog no longer contains would point
`default_model` at a non-existent entry and break the next session.
The handler now checks whether the alias still resolves and clears
it otherwise.

Test harness:

- The fake `setConfig` now mirrors the real `mergeConfigPatch`
  semantics (deep-merge with `undefined` keys skipped), so tests can
  honestly assert that `setConfig({defaultModel: undefined})` does
  NOT wipe a key from disk — only `removeProvider` can.

Two new regression tests, each verified to fail against the pre-fix
handler.

---------

Co-authored-by: 7Sageer <158020838+7Sageer@users.noreply.github.com>
2026-06-02 14:58:47 +08:00
Haozhe
0d074f1bae
feat(agent-core): propagate app version into agent record metadata (#308)
* feat(agent-core): propagate app version into agent record metadata

- add appVersion option to AgentOptions, SessionOptions, and KimiCoreOptions
- forward appVersion through Agent, Session, and SDKRpcClient
- include app_version in agent record metadata events
2026-06-02 14:54:29 +08:00
caiji
58e2915c0f
fix(tui): clamp session picker lines to terminal width to fix narrow-terminal crash (#247)
* fix(tui): clamp session picker lines to terminal width

The /sessions picker drew long session ids, the inline time/(current)
badge, and long prompts past the terminal edge on narrow terminals. The
pi-tui renderer enforces that no line exceeds the terminal width and
threw 'Rendered line exceeds terminal width', crashing the app.

Clamp every line the picker emits to the terminal width via
truncateToWidth so it degrades gracefully instead of crashing.

Fixes #240

* fix(tui): simplify changeset and de-CJK session picker test

Address review feedback on #247:
- shorten the changeset to a single sentence
- replace the Chinese test title with an English one

---------

Co-authored-by: liruifengv <liruifeng1024@gmail.com>
2026-06-02 14:53:47 +08:00
liruifengv
6de3d97d82
fix(tui): drain terminal input before exit (#314) 2026-06-02 14:04:17 +08:00
qer
a10cb94bc0
feat(tui): hint the /dance easter egg and highlight its sub-commands (#312)
- Add a footer rotation tip that surfaces the otherwise-hidden /dance command.
- Paint /dance on|off in the status hint so the sub-command reads as a command instead of blending into the dimmed text.
2026-06-02 13:36:36 +08:00
liruifengv
a4511ffc87
fix: show full model name in footer without truncating provider prefix (#310) 2026-06-02 13:19:29 +08:00
_Kerman
8b392a9c26
fix: restore main typecheck (#307) 2026-06-02 13:06:59 +08:00
_Kerman
a217ff09aa
feat: add /undo slash command and keep replay in sync (#277) 2026-06-02 12:48:59 +08:00
_Kerman
573c56e829
refactor: background task manager persistence (#285) 2026-06-02 12:48:31 +08:00
_Kerman
91b292e898
fix: allow glob outside workspace (#283) 2026-06-02 12:36:16 +08:00
ArkhAngelLifeJiggy
fb35bca032
fix: replace chalk.yellow with theme-aware hex in session-directory warning (#229)
* fix: replace chalk.yellow with theme-aware hex in session-directory warning

* fix: address Codex review — fold theme tests into existing suite, fix guard test bugs

- Move theme-colors.test.ts assertions into existing terminal-theme.test.ts to
  avoid a generic feature test file (Codex P2)
- Fix chalk-named-color-guard.test.ts:
  - Replace require('fs') with ESM import (readdirSync) (P1)
  - Fix over-broad comment-skip logic — use proper inBlockComment state
    tracker instead of line.includes('*') which silently skipped valid
    code lines containing asterisks (P1)
  - Remove dead code (const entries = [['dir', '']]) (P2)

---------

Co-authored-by: liruifengv <liruifeng1024@gmail.com>
2026-06-02 12:15:06 +08:00
liruifengv
3d7e20e697
fix(tui): replace stale /connect hints with /provider (#303) 2026-06-02 12:04:12 +08:00
opbr
d912053b0d
fix(kaos): search usr\bin\bash.exe paths for Git Bash on Windows (#145)
Some Git for Windows installations have bash.exe only at
<root>\usr\bin\bash.exe while <root>\bin\bash.exe does not exist.
Add usr\bin\bash.exe candidates alongside each bin\bash.exe entry in
both the hardcoded fallback list and the git.exe inference logic.

Also add @moonshot-ai/kimi-code-sdk to the changeset since the SDK
bundles agent-core code and calls detectEnvironmentFromNode() on
session creation, so Windows SDK users also need this fix.
2026-06-02 11:41:19 +08:00
liruifengv
e2ce6b9687
docs(changelog): sync 0.7.0 from apps/kimi-code/CHANGELOG.md (#298) 2026-06-02 11:31:20 +08:00
Yifan Song
4c7b173c12
docs: add Ctrl-J as newline alternative in keyboard shortcuts (#288)
Co-authored-by: liruifengv <liruifeng1024@gmail.com>
2026-06-02 11:24:22 +08:00
zha-ji-tui
0071b63fc8
fix(skill): wrap slash-activated skill content in system-reminder tags (#135) 2026-06-02 11:20:54 +08:00
_Kerman
811f252625
feat(tui): show MCP server summary in welcome panel and update /mcp hints (#223) 2026-06-02 11:09:10 +08:00
github-actions[bot]
121a6dd2a1
ci: release packages (#237)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-06-02 10:22:31 +08:00
Haozhe
a580cd3a98
feat(glob): add brace expansion, fix backslash escaping, and lower match cap (#282)
* feat(glob): add brace expansion and fix backslash escaping

- add brace expansion support for glob patterns with up to 64 sub-pattern cap
- remove up-front rejection of pure-wildcard and **/ prefix patterns; rely on 100-match cap\n- fix backslash escape handling in globPatternToRegex and inside character classes
- include matched count in truncation messages\n- show glob pattern, path, and include_dirs in TUI tool-call headers

* fix(kaos): use charAt to avoid TS strict undefined index error
2026-06-01 21:36:33 +08:00
qer
178827db47
feat: add hidden dance easter egg (#260)
* feat: add hidden dance easter egg

* chore: update dance changeset

* fix: dispose dance easter egg on stop

* chore: clarify dance disposer naming

* chore: simplify dance lifecycle cleanup
2026-06-01 21:02:36 +08:00
_Kerman
ab546419c8
refactor: move Kimi for Coding provider to SDK (#281) 2026-06-01 20:44:36 +08:00
Haozhe
42bb9141d8
feat(tui): add /provider command, custom registry import, and tabbed model selector (#264)
* feat(tui): add /provider command, custom registry import, and tabbed model selector

- add "/provider" slash command for managing AI providers with CRUD UI
- add custom registry import via api.json URL and Bearer token
- introduce tabbed model selector grouped by provider
- add fetchCustomRegistry and applyCustomRegistryProvider in oauth package
- replace deprecated "/connect" command with unified "/provider" flow
- update provider and slash-command documentation

* feat(tui): background provider model refresh at startup

- add `refreshAllProviderModels` utility supporting managed OAuth, open platforms and custom registries
- wire background refresh into `KimiTUI` startup via `AuthFlowController`
- add `providerSwitchHint` option to `ModelSelector` and enable it in `TabbedModelSelector`
- update `TabbedModelSelector` hint wording from "switch" to "provider"
2026-06-01 18:46:40 +08:00
qer
a1dfbfeb16
fix: clarify Kimi Platform login copy (#274) 2026-06-01 18:43:01 +08:00
_Kerman
e2e17289fc
fix: handle compaction truncation and output budgets (#267) 2026-06-01 17:25:11 +08:00
_Kerman
1084f1d217
feat: implement MicroCompaction (#219) 2026-06-01 14:31:50 +08:00
Haozhe
817ad1fe9a
chore(flake): simplify nix build and add ci validation (#257)
* chore(flake): simplify nix build and add ci validation

- Replace dynamic pnpm-workspace.yaml parsing with hardcoded workspacePaths
  and workspaceNames to reduce format assumptions
- Remove update-pnpm-deps script and kimi-code-pnpm-deps package; use
  lib.fakeHash for standard hash mismatch workflow
- Remove nodejs_latest fallback in nodejsFor, hardcode to nodejs_24
- Add nix-build CI workflow that posts hash-mismatch details to PR comments
- Remove unused Nix installation step from release.yml
- Add workspace maintenance note to AGENTS.md
2026-06-01 11:47:38 +08:00
Haozhe
ee69d0ac29
feat(cron): render scheduled reminders in TUI and expose fired events (#204)
* feat(cron): render scheduled reminders in TUI and expose fired events

- add CronMessageComponent for distinct cron transcript entries in TUI
- emit cron.fired events from agent-core and expose via node-sdk
- report cron fire times with local ISO timestamps including timezone offsets
- render cron_job and cron_missed origins during session replay instead of skipping
- handle warning events in CLI and session event handler

* docs(cron): clarify that users must ask the model to manage cron tasks

- cron-create.md: instruct model to proactively tell users how to cancel/modify reminders
- cron-list.md: add guideline that users cannot directly manage cron tasks\n- cron-delete.md: emphasize users must ask model to cancel reminders
2026-06-01 11:34:35 +08:00
Kai
933cf6727e
fix(agent-core): surface user interruptions to the model instead of a neutral abort (#236)
When the user interrupts running tools or parallel subagents, the tool_result
fed back to the model was a neutral `Tool "X" was aborted` or a weak "stopped by
the user", so the model treated it as a system fault and speculated about
capacity/concurrency limits instead of recognizing a deliberate stop.

Carry a UserCancellationError as the AbortSignal reason from the cancel sites
(Turn.cancel/abortTurn, SessionSubagentHost.cancelAll) through to the message
sites (tool-call settle paths and the AgentTool catches), which now emit an
explicit "deliberate user action, not a system error/timeout/capacity limit"
message. Aborts propagated from another signal (e.g. a subagent's deadline via
waitForCurrentTurn) carry their original reason, so a timeout is not mislabeled
as a user interruption. The telemetry outcome classifier matches the new
"manually interrupted" phrase to keep counting these as cancelled.
2026-05-30 09:53:21 +08:00
Kai
a24bfb1df3
feat: force adaptive thinking via KIMI_MODEL_ADAPTIVE_THINKING (#232)
* feat: force adaptive thinking via KIMI_MODEL_ADAPTIVE_THINKING

Add the KIMI_MODEL_ADAPTIVE_THINKING env var and a matching
adaptive_thinking model-alias field that force adaptive thinking
(thinking: { type: 'adaptive' }) on or off, overriding the Anthropic
model-name version inference.

This lets custom-named staff endpoints that back an adaptive-capable
model opt in even when the model name does not encode a parseable Claude
version (which would otherwise fall back to budget-based thinking).

The kosong AnthropicChatProvider resolves the adaptive decision once in
withThinking() as `_adaptiveThinking ?? supportsAdaptiveThinking(model)`
and threads it through clampEffort/supportsEffortParam so forced adaptive
also unlocks the max effort level.

* fix: make adaptive_thinking imply the thinking capability

A model alias that sets adaptive_thinking=true (or env
KIMI_MODEL_ADAPTIVE_THINKING=true) without listing a thinking
capability previously resolved to thinking=false for custom-named
endpoints not in the capability catalog. The model picker then
classified the alias as "thinking unsupported" and forced thinking
off (persisting default_thinking=false).

Treat a forced adaptive flag as advertising the thinking capability
in resolveModelCapabilities, so the config.toml one-field opt-in
agrees with the KIMI_MODEL_* env path (which already defaults
capabilities to include thinking).

* fix: honor adaptiveThinking in the model picker, not the capability resolver

The earlier resolveModelCapabilities change had no observable effect:
modelCapabilities.thinking is consumed only for completion-budget and
media gating, never to decide whether thinking is dispatched or whether
the model picker offers a thinking toggle.

The mechanism that actually forced thinking off for an adaptive_thinking
alias is the TUI model picker, which reads ModelAlias.capabilities
directly (availableModels comes straight from config.models). Revert the
resolver change and instead make thinkingAvailability() treat
adaptiveThinking=true as a thinking toggle, so a custom-named config.toml
alias configured with only `adaptive_thinking = true` is no longer
classified "unsupported" (which forced thinking off and persisted
default_thinking=false on select). The KIMI_MODEL_* env path was already
covered by DEFAULT_CAPABILITIES.
2026-05-30 09:49:08 +08:00
liruifengv
2752bd9330
docs(changelog): sync 0.6.0 from apps/kimi-code/CHANGELOG.md (#227) 2026-05-29 22:58:42 +08:00
github-actions[bot]
d64b15d153
ci: release packages (#170)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-05-29 22:23:07 +08:00
qer
bab2da7b1c
feat(plugin): install plugins from a GitHub repository URL (#221)
* feat(plugin): install plugins from a GitHub repository URL

Allow `/plugins install <github-url>` (and marketplace `source` entries) to
take a GitHub repo URL directly. A new `github` source kind joins the
existing `local-path` and `zip-url` kinds.

Recognized URL forms (parsing in source.ts):

- `https://github.com/<o>/<r>`                       — bare; resolves to latest
                                                       release tag, falling back
                                                       to default branch HEAD.
- `https://github.com/<o>/<r>/tree/<ref>`            — branch / tag / SHA;
                                                       value passed to codeload
                                                       in its short form so the
                                                       backend resolves either.
- `https://github.com/<o>/<r>/releases/tag/<tag>`    — explicit tag, uses
                                                       refs/tags/<tag> to avoid
                                                       same-named-branch ambiguity.
- `https://github.com/<o>/<r>/commit/<sha>`          — explicit commit SHA.

The resolver deliberately avoids `api.github.com`: its 60/hour anonymous
quota is shared with every other tool on the egress IP (browser, gh CLI,
IDE integrations) and a first-time install failing because some other tool
ate the budget is unacceptable UX. Instead we:

- GET `github.com/<o>/<r>/releases/latest` with manual redirect and parse
  the `Location` header (302 → tag URL; 404 → no own release).
- Fall back to `codeload.github.com/<o>/<r>/zip/HEAD` for repos with no
  releases (or for forks that inherit upstream tags but have no own release
  page, which redirect to bare `/releases`).
- Only treat the explicit 404 from `/releases/latest` as "no release" — 5xx,
  403, 429, and any other non-2xx status surface a hard error rather than
  silently installing the default branch, so the user knows when transient
  GitHub issues changed the install path.

UI changes in the TUI:

- `/plugins install` now shows a live Braille spinner while resolving and
  downloading, then flips to a final status that distinguishes Installed
  (fresh) vs Updated (same repo identity, new version) vs Migrated (source
  changed, e.g. CDN zip-url → GitHub).
- `/plugins list`, the `/plugins` overview, and `/plugins info` show the
  install provenance inline. `zip-url` installs now display the URL host
  (e.g. `via code.kimi.com`, `via 127.0.0.1:port`) instead of the opaque
  `zip-url` literal. GitHub installs show `github <owner>/<repo>@<ref>`.
- Three-tier trust badge driven by the marketplace context recorded at
  install time: `official` (green) for `tier: official`, `curated` (blue) for
  `tier: curated`, `third-party` (muted) for anything not installed through
  the marketplace selector. CLI `/plugins install <url>` always records as
  third-party; the marketplace selector passes the tier through. A
  re-install replaces the marketplace context: switching to a third-party
  source clears the badge, which matches the underlying trust change.

`installed.json` gains optional `github` and `marketplace` fields
(back-compatible). PluginSummary surfaces `source`, `originalSource`,
`github`, and `marketplace` so the TUI can label installs without an extra
round trip to PluginInfo. The SDK's `session.installPlugin(source)` gains
an optional `{ marketplace }` second argument so the marketplace selector
can forward `{ id, tier }` through RPC; the CLI install path omits it.

Tests: 112 plugin-suite tests (URL parser, resolver, store round-trip,
manager integration). The manager integration tests assert codeload URLs
shape (short form for `/tree/<ref>`, explicit `refs/tags/` for
`/releases/tag/`) and verify marketplace context is persisted across
reloads and cleared on a third-party re-install.

* chore(changeset): plugin install from GitHub

* docs(plugins): document GitHub install URLs and trust badges

* fix(plugin): preserve URL-encoded characters in GitHub ref names

Git permits ref characters that have special meaning in URLs — most
notably `#`, which is a valid tag character (e.g. `release#1`) but the URL
fragment delimiter. The resolver decoded the tag from GitHub's
`/releases/latest` 302 redirect Location header and then interpolated the
raw value into the codeload URL. The literal `#1` became a fragment and
the HTTP request reached the server as `…/refs/tags/release` — a wrong or
truncated ref, leading to install failure for a release whose URL was
otherwise valid.

Two symmetric changes:

- The codeload URL builder now splits the ref on `/` (so multi-segment
  refs like `feat/foo` keep their path separators) and percent-encodes
  each segment.
- The GitHub URL parser now percent-decodes each segment from the URL's
  pathname when extracting `/tree/<ref>`, `/releases/tag/<tag>`, and
  `/commit/<sha>`. Storage and display see the human-readable Git ref
  name; the resolver re-encodes on the way out.

Malformed `%xx` sequences in user-typed URLs are tolerated: we keep the
raw segment so the caller surfaces a normal "ref not found" error
downstream instead of crashing during parse.

* fix: restrict plugin trust badges

* chore: remove fetch when show plugin list

---------

Co-authored-by: qer <Anna_Knapprfr@mail.com>
2026-05-29 22:18:16 +08:00
Kai
13e0fff462
fix(kosong): preserve unsigned thinking in anthropic history serialization (#222)
When converting assistant history to the Anthropic wire format,
convertMessage() dropped any thinking block that had no signature. That
was meant to satisfy api.anthropic.com (which requires a valid signature
on thinking blocks), but it broke Anthropic-compatible backends.

Kimi's Anthropic-protocol endpoint streams thinking without a
signature_delta, yet requires the thinking to be present on a tool-call
turn — once it was dropped, the next request failed with "thinking is
enabled but reasoning_content is missing in assistant tool call message",
making multi-step tool use unusable on those backends.

Preserve unsigned thinking instead, emitting it without a `signature`
field. The two backends are partitioned by signature presence:
api.anthropic.com always supplies a signature (its history takes the
signed branch unchanged), while Kimi never does (its thinking is now
kept). Empty-and-unsigned parts carry nothing and are still skipped.
2026-05-29 21:41:09 +08:00