Skip to content

chore: merge upstream 2.0.0 - #11

Merged
devGregA merged 449 commits into
mainfrom
sync/upstream-2.0.0
Sep 17, 2026
Merged

devGregA merged 449 commits into
mainfrom
sync/upstream-2.0.0

Conversation

@devGregA

Copy link
Copy Markdown
Contributor

Problem

The fork was pinned at upstream 0.34.0. Upstream has since released 2.0.0 (448 commits), and the fork's ten hardening changes needed to be carried across a major version rather than silently lost to auto-merge.

What changed

Merges @moonshot-ai/kimi-code@2.0.0 into main.

Every hardening change was re-verified present in the merged tree, including the ones that did not conflict and so could have been dropped without a marker. Upstream fixed none of them independently.

Conflicts of note:

  • Permission policy chain. Upstream added DangerousCommandAsk at position 3, above session-approval history and user allow rules. That placement is kept, and this fork's move of AutoModeApprove below the content-sensitive asks is kept alongside it, so auto mode still does not waive the secrets, .git and execution-trigger checks. Upstream skips the guard for non-interactive hosts; that skip is dropped, so a headless host in manual or yolo mode stays guarded.
  • Approval broker. Upstream replaced the broker with interactions.request, so the unattended refusal is re-expressed against the new API. A headless run that needs approval refuses with an explanation instead of waiting for an answer that cannot arrive.
  • Plugin marketplace. Upstream moved resolveMarketplaceLocation from the CLI into agent-core-v2. The https requirement and host allowlist move with it, extended to cover the new .ai region hosts.
  • Native update preflight. Upstream's staged swap verifies a SHA-256 manifest, which supersedes this fork's patch, so upstream's version is taken.

agent-core-v2, kap-server and transcript are comment-free zones upstream now (scripts/check-no-comments.mjs runs in pnpm lint), so the security rationale that lived in comments there moves to HARDENING.md.

Verification

  • pnpm typecheck, pnpm lint and pnpm build clean.
  • Test suite: 16428 passed, 71 failed. All 71 are in kap-server search and transcript contract. The identical 71 fail on a clean upstream 2.0.0 checkout, compared name-for-name, so the merge adds no regressions.
  • Runtime, against a stub model endpoint: a headless Bash tool call is refused rather than executed, with no hang, and the probe file is not written. With KIMI_CODE_AUTO_APPROVE_BASH=1 the same call runs and the file appears, so the check is not passing because Bash is broken.

Follow-ups, not in this PR

Upstream 2.0.0 adds network surface that needs a policy decision before the fork is rolled out:

  • packages/remote-control registers the machine with a relay at code-rc.kimi.com and forwards public traffic into the local server. Opt-in, but POST /api/v1/remote-control can enable it with no terminal interaction, and the long-lived OAuth refresh token travels in a WebSocket subprotocol header.
  • Session titles send conversation excerpts to api.kimi.{com,ai}/coding/v1/tools; the experimental flag that used to gate this no longer exists.
  • The new global region profile adds five .ai hosts that can become the update, marketplace, binary, OAuth and telemetry destinations, so an egress allowlist naming only *.kimi.com is now incomplete.

🤖 Generated with Claude Code

sailist and others added 30 commits August 26, 2026 21:12
… (#3271)

- turn.started now derives the attachment file id from the kimi-file URL when the media part carries no id, so prompt images uploaded through the global file library stay attached in the live transcript instead of vanishing until the post-turn heal
- an explicit id must still match the URL file id, and non-kimi-file URLs are still ignored
- restore the turn.started prompt-attachment regression coverage dropped by the model-as-container refactor and extend it to the id-less kimi-file form
The drain probe treated any callTool failure as proof the transport close
had been processed, but a stdin write error (EPIPE) can win the race
against the child's exit notification, so the listener could be registered
before the close was buffered and the synchronous replay never fired.
Break the drain loop only on errors that are impossible before the SDK's
_onclose ran ('Not connected' / 'Connection closed' / the transport's
not-running guard).

Also drop the assertion that the replayed reason contains the child's
final stderr: the reason snapshots the stderr buffer at close time, which
can legitimately race delivery of the last stderr chunk (reproduced 2/40
under CPU load). Tail capture itself stays covered by the stderrSnapshot
assertion. Finally, flush the fixture's banner through the stderr write
callback before process.exit so the write cannot be truncated.

Co-authored-by: kimi-agent-bot <kimi-agent-bot@users.noreply.github.com>
…urn it describes (#3278)

* fix(agent-core-v2): clear the turn outcome when an undo rewinds the turn it describes

* fix(agent-core-v2): clear the turn outcome too when an undo outruns the tracked anchors

* fix(agent-core-v2): reconcile the persisted turn outcome against the replayed wire on restore

* fix(agent-core-v2): keep the persisted outcome when an undo rewinds only later turns

---------

Co-authored-by: kimi-agent-bot <kimi-agent-bot@users.noreply.github.com>
…ath (#3283)

Add a liveness watchdog on the management and HTTP tunnel WebSockets
(client ping every 30s, terminate after 300s without inbound activity)
so the client recovers when a relay redeploy drops connections without
a close frame. Treat registration failures after a successful session
as transient and keep backing off instead of stopping permanently.
…aming (#3276)

Concurrent openSession/attachResumedSession calls for the same webview
both missed the sessions map before either wrapped the SDK session, so
one Session facade got two SessionRuntimes. The overwritten runtime
leaked and kept broadcasting, doubling every streamed delta and tool
call in the view. Queue opens, attaches, and detaches per webviewId so
the second caller sees the first one's runtime.
…background (#3273)

* feat(kap-server): add task detach action to move foreground tasks to background

* test(kimi-code-sdk): normalize v2-only parentToolCallId in task parity projections

* fix(agent-core-v2): distinguish user-initiated detach in tool result text

* feat(agent-core-v2): mention user-initiated backgrounding in the bash tool description

* feat(agent-core-v2): use a client-agnostic background-task panel hint in the bash tool description

* ci: retrigger checks

* feat(agent-core-v2): client-agnostic human_shell_hint and a detached_by_user marker in tool results

* fix(protocol,docs): declare parent_tool_call_id in the shared task schema and document the detach action

* style: remove added comments
…3289)

* fix(transcript): carry the orchestrator's prompt on subagent turns

* chore: add changeset for subagent turn prompts
…e web page (#3291)

* fix(oauth): show a cancelled state when authorization is denied on the web page

* chore: drop the changeset and the dist-web sync from this PR
…sh or are removed (#3284)

* fix(secondary-model): stop rewriting the section when providers refresh or are removed

Provider refresh, provider deletion/rename, catalog/registry import,
OAuth logout, and SDK removeProvider used to cascade into the user's
[secondary_model] block: pool entries were silently pruned, and the
whole section was deleted when its effective default dangled. The
cascade ran from a cache-refresh path (including an unattended 6h
scheduler), so upstream model-list changes could irreversibly destroy
hand-written configuration without any notice.

Config is user intent; the catalog is an availability snapshot. Stop
rewriting the section on every provider/models writer. An entry whose
model no longer resolves fails pool validation on the next session
create with a message naming the offending alias, which is the same
fail-fast contract hand-written typos already had.

* chore(sdk): add changeset for the removed secondary-model cascade export

* Delete .changeset/sdk-remove-secondary-model-cascade.md

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

* Delete .changeset/secondary-model-no-silent-rewrite.md

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

---------

Signed-off-by: 7Sageer <sag77r@hotmail.com>
…(#3234)

* fix(mcp): forward structuredContent only as fallback when content has no usable text

Servers that follow the MCP spec's backwards-compatibility SHOULD return
the same JSON both as a TextContent block and as structuredContent.
Forwarding both to the model sent the same data twice. structuredContent
now rides the mcp-structured-result block only when the content blocks
carry no usable text; _meta still always passes through.

* test(mcp): verify structured-content fallback over a real stdio MCP server

Round-trip four server result shapes (dual-emit, structuredContent-only,
prose+structured, vendor _meta) through StdioMcpClient and the output
pipeline, so the fallback behaviour is checked against real protocol
bytes instead of hand-built result objects.

* fix(mcp): dedupe structuredContent only against its verbatim serialization

The earlier has-usable-text gate also suppressed the structured payload
when content was a lossy human summary — the primary case from #2554
(list_projects returning 'N item(s)' while the items live in
structuredContent). Skip the structured block only when a content text
block parses to the same JSON value (semantic compare, key order and
formatting insensitive); summaries and structured-only results still
pass through.

* fix(mcp): forward structuredContent only when content does not already cover it

Replace the verbatim-serialization comparison with a size heuristic:
well-behaved servers render the same data into content (the spec's
dual-emit, or a faithful human reorganisation), and either way the text
measures at roughly the same size as the payload, so forwarding it would
double the information. Append the structured payload only when content
carries no usable text, or when the payload is more than twice the text
size — the signature of a lossy summary. Verified against a live video-
editor MCP whose tools all measure a json/text ratio of 1.2-1.9.

* fix(mcp): send content or structuredContent to the model, never both

Final policy: content and structuredContent are alternatives. content
wins whenever it carries anything usable (a media block or non-whitespace
text); structuredContent fills in only for an empty content array. There
is no reliable signal that the structured payload is richer than what the
server already rendered into content, so no size or structure heuristic
is attempted. _meta still always passes through.

* refactor(mcp): rename the structured-extras wrapper to mcp-result-extras

The block carries structuredContent and/or _meta; the old
mcp-structured-result name was inaccurate whenever it is a pure _meta
carrier.

* ci: retrigger after flaky mcpCore client-stdio close-buffering test

* ci: retrigger after flaky minidb concurrent writer/reader test
…e provider (#3294)

* fix(auth): keep OAuth login alive when its own provisioning writes the provider

* fix(auth): settle the OAuth flow as authenticated before provisioning

* fix(auth): publish the login as authenticated only after provisioning completes
* chore: sync web dist from code-app

code-app: 758f4587d10e28de8ac04678df8e379ca0f10387

* chore: consolidate web dist changesets by theme

* chore: trim web dist changesets to headline items

* chore: drop web prefix from fix-known-issues changeset

* chore: drop web-mobile-interaction changeset as duplicative of existing mobile entries
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
…minated by app exit (#3292)

* fix(agent-core-v2): unify previous-session lost-task notice into a single reminder on resume

* fix(agent-core-v2): harden resumed task reminders

* docs: clarify task resume reminder
* feat(kap-server): support server-local path attachments

Web and desktop clients can now attach files, images, and videos to a
prompt by server-local absolute path instead of uploading a copy. The
daemon validates the path (absolute, realpath-resolved, non-sensitive,
local runtime only) and references the original file in place, so the
agent reads the original path; the upload flow is unchanged.

Submitted file attachments are also recorded on the prompt origin and
projected as typed transcript attachments, so web clients render
attachment chips for plain files without parsing the model-facing
notice text.

* fix(kap-server): forward file attachment metadata from skill activations

* Delete .changeset/web-attach-by-path.md

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

---------

Signed-off-by: 7Sageer <sag77r@hotmail.com>
* fix(cli): give the interactive update check a longer CDN timeout

`kimi update` shared the 3-second CDN fetch budget sized for passive
background checks. Every CLI invocation is a fresh process paying full
DNS+TCP+TLS setup, so a slow connection to the CDN intermittently
aborted the interactive check with a raw "This operation was aborted".

Thread a per-request timeout through the CDN fetch helpers and
refreshUpdateCache; the interactive upgrade command now passes a 10
second budget (INTERACTIVE_UPDATE_CHECK_TIMEOUT_MS) while all
background refresh paths keep the 3-second default.

* refactor(cli): drop motivational comments and simplify the update-check changeset

* docs(cli): reword the update-check changeset

* docs(cli): English changeset for the update-check timeout

---------

Co-authored-by: kimi-agent-bot <kimi-agent-bot@users.noreply.github.com>
…(#3293)

* feat(auth): split model readiness from sign-in state in /api/v1/auth

GET /api/v1/auth now reports models_ready (the default model resolves
against the configured catalog, providerless and env-injected models
included) instead of the compound ready flag, and no longer carries
default_model — config values are served by /config alone. The v1
summary schema follows.

OAuth managed-model refreshes now heal a lost default model: the
refresh snapshot includes defaultModel, so an unchanged catalog with
a missing default still lands the write-back branch and re-selects
one. The refresh also rebases onto a fresh config read after the
remote fetch, so a model or thinking change made during the fetch is
no longer overwritten. The shared discovery refresh path (scheduler,
POST /providers/{id}:refresh) heals the default the same way.

Config changes are now published to WS clients on every write path:
a debounced+trailing publisher bridges IConfigService section changes
to ConfigChanged with camelCase changedFields and a full config
projection, and the broadcaster forwards event.config.changed and
event.model_catalog.changed (both previously published but never
delivered). All three event types are registered in the event unions,
so session_event parsing and AsyncAPI describe them.

BREAKING CHANGE: GET /api/v1/auth drops the ready and default_model
fields in favor of models_ready; event.config.changed's changedFields
is now camelCase domain names instead of the raw snake_case request
keys (v1 summary schema follows).

* fix(kap-server): expose the session model in session list projections

GET /api/v1/sessions hardcoded agent_config.model to '' and the v2
projection had no model field at all, so clients could only learn a
session's model via the post-select /status read — which races the WS
replay and often never lands. SessionFacts now carries the live
session's model (same source as the snapshot route), toWireSession
emits it, and the v2 activity domain gains a nullable model field.

* fix(kap-server): gate prompt submission on the effective session model

The submit gate called ensureReady() with no override, so it only ever
validated config.default_model: a session with a bound model (or a
prompt carrying one) was rejected with 40113 whenever default_model was
missing or dangling. Pass the effective model (request model, then the
agent profile's bound model, falling back to default_model inside
ensureReady) on both the prompt submit and btw routes.

* fix(agent-core-v2): honor defaultProvider in model readiness resolution

resolveModelForReady stopped at the flat baseUrl fallback, so a model
that omits provider/providerId and relies on the configured
defaultProvider resolved at runtime (ModelCatalog.resolveProviderContext
falls back to it) while /api/v1/auth reported models_ready:false and the
send gate rejected the prompt. Mirror the runtime order (providerId ->
provider -> defaultProvider -> flat baseUrl) and pass the configured
default provider from both readiness callers.

* fix(kap-server): redact inline model credentials from config responses

toConfigResponse only redacted the providers section, so a model's
inline apiKey/oauth rode GET /config verbatim and, via the new
event.config.changed publisher, every WS connection plus the persistent
event journal. Project the models section the same way: strip
credential fields and report has_api_key.

* fix(agent-core-v2): honor defaultProvider in ensureReady credential checks

The readiness phase learned the defaultProvider fallback, but the
credential phase right after still derived the provider only from the
model's explicit fields: a model omitting provider/providerId passed
readiness yet missed the default provider's apiKey/OAuth material and
prompts failed with auth.token_missing. Mirror the same provider chain
(providerId -> provider -> defaultProvider) when resolving credentials.

* fix(kap-server): validate the model a profile bind will select at the prompt gate

The gate validated the session's current model even for a prompt that
switches profile without a model — but bind falls back to defaultModel
in that case, so a stale session model drew a misleading 40113 before
bind could run. Gate on bind's selection order instead: the request's
explicit model, then the default on a profile switch, then the session's
bound model.

* fix(kap-server): redact inline service credentials from config responses

The earlier redaction covered providers and models, but toConfigResponse
still passed the services section through verbatim: inline or
env-injected apiKey, oauth references, and credential-bearing
customHeaders rode GET /config and, via the event.config.changed
publisher, every WS connection plus the persistent event journal.
Project services the same way: strip apiKey/oauth into has_api_key and
report only the header names as custom_header_keys (the MCP
envKeys/headerKeys convention).

* fix(kap-server): keep unlisted config domains through event validation

The config.changed broadcaster returned the zod-parsed config, which
strips domains absent from configResponseSchema (mcp, identity,
model_catalog, image, tools, token_counting): changedFields named them
while the advertised full snapshot no longer matched GET /api/v1/config.
Make the response projection passthrough (defineRoute validates only
requests, so REST responses are unaffected).

* fix(agent-core-v2): use the exact configured key for model readiness lookups

resolveModelForReady trimmed the model id before the models-table lookup
while ModelCatalog and ensureReady use the configured string as an exact
record key: a whitespace-padded default_model was reported ready and then
crashed the submit gate with an internal error instead of 40113, and a
legitimate key containing spaces was reported dangling. Trim only rejects
blank values now; the lookup always uses the raw key.

* fix(protocol): keep unlisted config domains in the shared event projection

The shared configResponseSchema stripped domains it does not enumerate
(mcp, identity, model_catalog, image, tools, token_counting, subagent,
secondary_model), so event.config.changed parsed through agentEventSchema
named them in changedFields while omitting their values. Make the shared
projection passthrough like the kap-server-local one.

* fix(agent-core-v2): use the exact default_provider key in readiness checks

The defaultProvider fallback trimmed the configured value before the
providers-table lookup while ProviderService and ModelCatalog use the
configured string verbatim: a whitespace-padded default_provider could
build successfully yet report not-ready (40113), or report ready for a
provider runtime resolution cannot find. Trim only rejects blank values;
the lookup uses the raw key.

* chore: sync web dist from code-app

Rebuild the bundled web UI against this branch's /auth contract (models_ready, no ready/default_model): the previous bundle still read the old fields and stayed in the not-ready flow against this server.

code-app: 000d2594ff3e95b553be326126bab3f939b62944

* Revert "chore: sync web dist from code-app"

This reverts commit 9400a24a03863b3e8b780dda251540f824f02f3a.

* fix(oauth): rebase the default selection after the refresh fetch

A provider refresh snapshots the config before the remote catalog fetch;
when the user selects a default model while the fetch is in flight, the
stale snapshot's empty default made an otherwise unchanged catalog enter
the write path and the self-heal persisted the generated default over the
user's newer selection. Each branch now re-reads and rebases the
default/thinking selection after its fetch, before cloning, comparing,
or writing.

* style(kap-server): pass optional custom_header_keys without conditional spread
…transcript (#3328)

* fix(kap-server): only fold user-origin steers by content in the cold transcript

* fix(transcript): check marker-only origins before the steer content match

* fix(transcript): limit the steer bypass to marker-only skill triggers

* fix(transcript): consume the steer count for marker-only activations

* fix(transcript): pair steered contents with messages by origin kind

* refactor(kap-server): read the steer origin kind without nested casts

---------

Co-authored-by: kimi-agent-bot <kimi-agent-bot@users.noreply.github.com>
…325)

* fix(agent-core-v2): cache workspace alias resolution across calls

resolveAliasIds re-read the workspace catalog and the whole session
index from disk on every call, so the by_workspace grouping loop and
the per-workspace session counts paid repeated full-file reads per
workspace per request (~2.4s per 50-group page at 1.1k workspaces,
23 pages serially during a client startup drain).

Cache both files as precomputed snapshots (by-id map plus a
root-key -> alias ids index) invalidated by the storage watch events,
which cover atomic rewrites and cross-process writes; storage backends
without watch fall back to reading through. The first resolution primes
the workspace merge via IWorkspaceService.list() so the cached catalog
matches what WorkspaceService.get() would have returned.

* chore: drop the changeset; the user-facing entry ships with the app changelog

* fix(agent-core-v2): coalesce cold alias snapshot loads and guard publication

Concurrent cold resolveAliasIds callers (the /workspaces route fans out
per-workspace counts with Promise.all) all passed the cache check before
any caller finished loading, re-running the full catalog and session
index reads the cache exists to avoid; memoize the in-flight load
promise so a cold batch shares one read. Also capture the invalidation
generation before each read and publish the snapshot only when it is
unchanged, so a mid-read file replacement cannot leave a stale snapshot
installed over the watch invalidation.

* fix(agent-core-v2): publish catalog invalidation through the persistence owner

A debounced fs watch was the only invalidation channel for the alias
catalog snapshot, so an in-process catalog write stayed invisible to
resolveAliasIds for up to the watch debounce window while the previous
read-through code observed every completed write immediately.
IWorkspacePersistence now exposes onDidChange: FileWorkspacePersistence
fires it synchronously on save and re-fires the underlying document
watch (covering atomic rewrites and cross-process writers), and the
aliases service subscribes to it instead of watching raw storage keys.
The session index snapshot keeps the filesystem watch, matching the
read-side ownership of that file.

* fix(agent-core-v2): invalidate session alias snapshots on append-log writes

A flushed session_index.jsonl append was invisible to resolveAliasIds
for up to the fs-watch debounce window, so a sessions request issued
right after a session create could resolve the workspace's aliases from
the pre-append snapshot. IAppendLogStore now publishes onDidWrite after
each durable flush (append batches and rewrites), and the aliases
service drops its session-index snapshot through that event; the raw
filesystem watch stays as the channel for cross-process writers.

* fix(agent-core-v2): fire append-log write events only after actual writes

Once a key has a LogState, every global flush() (WireService flushes
after ordinary agent persistence) completed it successfully and fired
onDidWrite unconditionally, so idle agent activity kept dropping the
alias session-index snapshot and forced full re-reads of an unchanged
index. drain() now reports whether it appended anything and the write
event fires only when a flush actually persisted a batch or a rewrite.

* fix(agent-core-v2): retry shared snapshot loads that span a write

Callers joining an in-flight single-flight load after a completed write
still received the pre-write snapshot: the generation check only guarded
cache publication, not the value returned to awaiters. Each load now
carries the generation it started at, and catalog()/sessionIndex()
re-read (coalesced through the same single-flight) when the settled
load's generation is stale.

* fix(agent-core-v2): report partial progress when an append-log drain fails

A drain that persisted one batch and then failed the next threw without
recording the durable write, so onDidWrite never fired for records that
were in fact persisted (the alias session-index snapshot then missed
its synchronous invalidation). The write box now threads through the
whole owned flush: each successful batch marks it, and the event fires
before the failure propagates.

* fix(agent-core-v2): retry the whole alias resolution across a mid-write

The per-snapshot retry guarded each read on its own, so a write landing
between the catalog and session-index reads returned an alias set
assembled across two generations. resolveAliasIds now captures the
generation once, reads both snapshots together, and retries the whole
resolution when either input was invalidated mid-flight. The
spanned-write test is reworked to gate after the load (so the snapshot
content genuinely predates the write), and a new case covers the
cross-generation mix directly.

* fix(agent-core-v2): replace the session-index fs watch with a size check

A resident chokidar watcher per server on the shared home directory
degraded watch delivery for unrelated files under test-suite boot
volume (the prompts suite lost the config.toml reload race and the
catalog missed a just-written model). In-process appends were already
covered synchronously by the append-log write event; cross-process
writers now surface through a per-call size comparison on the
append-only file, which costs one stat per resolve and needs no
resident watcher.
* chore: sync web dist from code-app

code-app: 2139742c91c097a1e26cec7044970067a7e3d736

* chore: clarify web sync changeset wording

Reword two synced changesets to describe the actual upstream code-app
changes (#420, #439). Release contents unchanged.

---------

Co-authored-by: kimi-agent-bot <kimi-agent-bot@users.noreply.github.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
* ci: add VS Code extension publish workflow

Publish VSIX to VS Marketplace and Open VSX on version-change pushes
to main, with online-version gates and workflow_dispatch fallback.

* ci(vscode): harden publish gate with before-SHA diff and per-platform checks

Address review on the auto-publish workflow:

- Version-change detection now compares against the push event's
  `before` SHA instead of `GITHUB_SHA~1`, so a single push carrying
  multiple commits cannot skip a version bump. An empty or all-zero
  `before` (e.g. first push of a branch) is treated as a version
  change — better an extra marketplace lookup than a missed release.
- Marketplace gates now verify all 6 targetPlatforms
  (darwin/linux/win32 x x64/arm64) instead of only the version number:
  Open VSX is checked per-platform via REST, VS Marketplace via the
  targetPlatform entries in `vsce show --json`. A partially published
  version now reruns the affected publish step, which backfills
  idempotently via --skip-duplicate / already-exists handling.
  Lookup failures stay fail-closed (treated as not published).

---------

Co-authored-by: kimi-agent-bot <kimi-agent-bot@users.noreply.github.com>
Grapedge and others added 28 commits September 15, 2026 17:35
… (#3803)

* fix(plugins): rename WebBridge display name to Kimi Browser Extension

The product has renamed WebBridge to Kimi Browser Extension in work and
Chrome. Update the user-facing display name across the built-in
capability entry, TUI plugins selector, managed plugin manifest,
marketplace catalog, skill docs, and user docs to match. Internal ids,
binary names, CDN paths, and daemon references are unchanged.

* fix(plugins): bump webbridge to 1.11.4 and keep legacy docs anchor

Address review: point the changeset at the scoped CLI package, bump the
renamed plugin's manifest/catalog/docs version so installed users are
offered the renamed package as an update, and preserve the published
#kimi-webbridge documentation anchor beside the renamed heading.

* docs(plugins): match only the known extension-update error messages

Broadening the version-mismatch trigger to the generic prefix 'Please
update the' would misclassify unrelated tool errors as an outdated
browser extension; match the two known full messages instead.

---------

Co-authored-by: kimi-agent-bot <kimi-agent-bot@users.noreply.github.com>
* refactor(agent-core-v2): clarify human-layer names

* refactor(agent-core-v2): name provider-holding members after their role

- LlmRequestConfig/LlmRecoveryContext/adapter Model: credentials -> credentialProvider
- requester/provider convertError option -> classifyError (matches LlmErrorClassifier)
- kimiOAuthCredentialProvider -> createKimiOAuthCredentialProvider
- ModelCatalog.buildCredentials -> buildCredentialProvider

* refactor(agent-core-v2): rename the format-layer error hook to classifyErrorHook

threads the requester's classifyError option through under a matching name

* refactor(agent-core-v2): finish the human-layer naming pass

- turn.failure.classified -> turn.failure.recovery.evaluated
- recovery proposal beforeRetry -> beforeNextAttempt
- llmRequester currentCredentials/credentialsForTurn -> currentCredentialProvider/credentialProviderForTurn
- unexport ProtocolTraitsByName (only backs ProtocolTraitFor)

* refactor(agent-core-v2): shorten the turn failure event to turn.failure.evaluated
* refactor(agent-core-v2): align request and recovery stage names

* refactor(agent-core-v2): align cache key, google-genai, and recovery context names
…che (#3778)

* perf(tui): batch and cache agent swarm progress rendering

* perf(agent-core-v2): evict completed subagent scopes behind an LRU cache

* fix(agent-core-v2): stop the agent actor in the session machine when its scope is removed

* fix(agent-core-v2): emit a terminal event for the final rate-limited run and defer eviction while background tasks run

* fix(agent-core-v2): include in-flight retries when abandoning suspended agents

* fix(agent-core-v2): roll back the closing flag when scope removal fails and wait per the configured eviction timeout

* fix(agent-core-v2): terminalize subagents spawned after batch cancellation and report task timeouts as failures

* chore: restore upstream/main state of files mangled by lint-staged autofix in the merge commit

oxlint --fix rewrites (prefer-set-has, catch-error-name, prefer toSorted/.at(-1)/toReversed)
touched seven files the branch never modified; one of them (oauth managed-kimi-code.ts)
no longer typechecks. Restore all seven to their exact upstream/main state. Committed
with --no-verify so the same autofix does not re-mangle them.

* fix(agent-core-v2): retry deferred scope evictions without exhausting the attempt budget, dedupe terminal events for starting retries, and patch-level the changeset

* fix(agent-core-v2): finish scope teardown instead of reactivating a partially torn-down scope

* fix(agent-core-v2): terminalize subagent-start hook cancellations and re-acquire resume targets after metadata reads

* fix(tui): record cancelled background agents in the transcript

* fix(agent-core-v2): route mirror terminal events through the batch exactly-once guard

* fix(tui): guard terminal swarm members from re-cancellation and deliver terminal status to cancelled foreground subagent cards

* fix(agent-core-v2): classify eviction as removed when teardown already closed the scope

* Merge remote-tracking branch 'upstream/main' into perf/subagent-scope-eviction

Skip lint-staged for this merge: its oxlint --fix pass rewrites unrelated files
(prefer-set-has etc. mangled seven files in the previous merge commit), while the
merge content is upstream's lint-clean code plus conflict resolutions already
verified by oxlint.

* chore: restore upstream/main changes dropped by a stash roundtrip during the merge

A git stash/pop cycle used to isolate an unrelated flaky test reverted 82 files of the
merge result to the pre-merge branch state, so commit 375ff2a4a missed them. The
working tree already held the correct merged content; this commit restores it.

* fix(agent-core-v2): propagate the cancellation reason from turn abort to tool signals

Cancelling a turn aborted the turn's llm scope and every tool scope
without a reason, so tools and subagents only ever saw the default
AbortError and could not tell a user cancellation from a system abort.
With the swarm terminal-event classification this surfaced as
`subagent.failed` ("This operation was aborted") on Ctrl+C instead of
`subagent.cancelled`.

Carry the reason end to end: `MachineEngine.abort(reason)` sends it on
`input.abort`, the agent machine remembers it for the turn and forwards
it to `turn.abort` and to every tool scope it aborts, the turn machine
passes it to the llm scope, and the loop service hands its cancellation
(a `UserCancellationError` by default) to the engine.

* fix(agent-core-v2): stop rebuilding an evicted subagent after its caller aborts

`createAgentAwaitingClose` polled for the previous scope to finish
closing without ever looking at the caller's abort signal, so cancelling
an `Agent` or `AgentSwarm` resume during that wait still recreated the
scope afterwards. In the `Agent` path the run then rejected before any
terminal event was mirrored, leaving an idle scope that the scope cache
no longer tracked.

Thread the caller's signal into the helper and check it before every
attempt; the Agent tool passes its call controller and the swarm service
passes the attempt signal. Also trim the eviction changeset to the one
sentence the changeset rules ask for.

* fix(agent-core-v2): report task-manager aborts of a subagent as cancelled

When the task manager times out or stops a background subagent it aborts
the run with a plain reason ('Timed out', the TaskStop reason), and the
run rejects with that raw reason. The task manager therefore classifies
the task as timed_out or killed, but the mirror only recognised
`UserCancellationError` as a cancellation and emitted `subagent.failed`,
so the transcript recorded a failure before the authoritative
`background.task.terminated` event could land.

Classify a terminated run by the shape of its abort reason: a real
Error that is not an AbortError (the swarm's own per-item timeout) stays
a failure, everything else that aborted the run is a cancellation. The
swarm's start-failure path uses the same rule.

* fix(agent-core-v2): flush the wire before evicting a subagent scope and re-pick LRU candidates

Two gaps in the scope-cache eviction pass:

- `AgentLifecycleService.remove()` swallows a failed wire flush and still
  tears the scope down, so an eviction that hit a disk-full or transient
  I/O error would drop the in-memory scope and let a later resume rebuild
  from a stale wire. Flush the agent's wire explicitly before removing;
  if that fails, keep the scope resident and retry on a later pass. The
  idle and task checks are repeated after the flush so a resume that
  started meanwhile is not cancelled by the removal.
- The pass snapshotted its candidates up front and re-validated them by
  attempt count only, so a scope that was resumed and completed again
  while an earlier eviction was awaited kept matching with attempts 0 and
  was evicted despite now being the most recently used. Pick the oldest
  eligible entry from the live map on every step instead, skipping only
  the entries this pass already tried.

---------

Co-authored-by: Kaiyi <me@kaiyi.cool>
…st message (#3818)

* fix(tui): stop /usage and /status from prompting login before the first message

* docs(changeset): simplify the /usage fix entry

* fix(tui): mute the session-usage error line and cover the session-less path in tests

* docs(changeset): restore the confirmed wording
… (#3784)

* feat(agent-core-v2): upload images as file references for Kimi models

* fix(agent-core-v2): scope media upload cache by endpoint and recognize SDK auth error status

* fix(agent-core-v2): restore the stripped /v1 for kimi files uploads on the anthropic route

* feat(agent-core-v2): omit oldest media when accumulated inline media exceed the request budget

* fix(agent-core-v2): key the media upload cache by the stable JWT subject

* fix(agent-core-v2): harden media request recovery

* fix(agent-core-v2): correct image memo accounting

* feat(agent-core-v2): include saved paths in dropped media placeholders

* docs: drop the saved-path re-read guarantee from the media budget note

The guarantee does not hold for inline data-URL media (no persisted
path exists), and the runtime warning already conveys path availability
when it applies. Addresses the codex P2 review suggestion by removal.
…3827)

Media name preservation already ships: the REST schemas expose a name on
image and video content, and kap-server reads imageUrl.name and videoUrl.name
when projecting messages and prompt content. The kosong part types never
declared the field, so it survived only because object literals keep excess
properties through an assignment. Consumers that read these types directly
cannot see the name at all.

Declare it where the other optional part fields live. No runtime change; the
existing media name projection tests continue to cover the behavior.
…#3831)

Widen activateSkillAttachmentSchema so the attachments array accepts text parts alongside image, video and file, letting a caller interleave text and media in the order the model should read them. Schema only; the route already resolves attachments as WireContent.
…3833)

Co-authored-by: konghuanjun <konghuanjun@moonshot.ai>
…unning turn (#3832)

Accept skill_activation origins with a user-slash trigger in onTurnSteered so a slash command sent while a turn is running gets a transcript frame, and keep those frames from draining a queued prompt's id. Project origin file attachments onto steered frames on both the immediate and deferred paths, matching onTurnStarted and the history rebuild.
* fix(kap-server): rebuild chat transcript after undo

* test(kap-server): provide undo registry in broadcaster fixtures

* fix(kap-server): preserve non-anchor steers through undo

* fix(kap-server): rebuild undo transcript after persistence completes

* test: complete restored notification origin fixture
…ns (#3764)

Store the optional metadata object on prompt submission and skill activation as opaque client metadata on the user or skill-activation origin, carry it through prompt events, the transcript projector, snapshots and history rebuilds, and echo it on prompt items without adding it to model content. Use display_text entries for session titles, undo labels and fork titles when every entry provides one. Project user turn payloads, live and rebuilt, through a path-free projection.
…form (#3840)

* fix(agent-core-v2): watch Windows 8.3 short paths through their long form

Watching a directory given in 8.3 short form (for example
C:\Users\RUNNER~1\...) makes libuv's Windows file watcher abort the process
with "Assertion failed: !_wcsnicmp(filename, dir, dirlen)" in fs-event.c,
because change notifications arrive under the long path.

On Windows, resolve a watch root containing a short-name segment to its long
form (the nearest existing ancestor when the root does not exist yet) and map
reported changes and ignore checks back to the requested path, so callers that
compare change paths with their own keep working.

* test(agent-core-v2): keep watch tests portable to Windows runners

* test(agent-core-v2): show received events when the Windows short path watch test fails

* test(agent-core-v2): derive the Windows short path without cmd quoting

* chore: add changeset for the Windows short path watch fix

* fix(agent-core-v2): keep watch children whose names start with two dots inside the root

relative() returns "..cache/x" for a child named "..cache", which the
startsWith('..') check treated as a path outside the watched root. Mapped
change paths then kept the resolved long form, and native signal events for
such children skipped the ignore predicate. Only a bare ".." segment, ".."
followed by a separator, or an absolute result is outside the root.
…ly (#3843)

* feat(agent-core-v2): add skill scopes and mark custom-theme as tui-only

Builtin skills can now declare a scopes whitelist (tui | web); skills
without scopes stay visible everywhere. The value is carried through
SkillSummary, the klient RPC contract, and the kap-server REST wire,
and the TUI slash commands filter by it. HTTP clients (code-app) drop
skills whose scopes exclude their own UI mode, so TUI-only skills like
/custom-theme no longer leak into app/web.

* fix: filter scoped skills from the acp and vscode palettes

ACP slash commands and the VS Code GetSlashCommands handler also
project SkillSummary lists; both now drop skills that carry a scopes
restriction so tui-scoped skills stay out of non-tui clients.
… (#3849)

* feat(cli): add /desktop slash command and kimi install-app subcommand

Both entry points print the Kimi Code desktop app page
(https://www.kimi.com/code) and open it in the default browser:
the kimi install-app subcommand from the shell, and /desktop
(alias /install-desktop) from the TUI.

* fix(cli): derive the desktop app page from the active region

Replace the hardcoded .com URL with the existing region-aware
kimiCodeOfficialInstallUrl() resolved at invocation time, so
global-region users land on the .ai site and region changes are
respected.

* docs(cli): describe the desktop app page URL as region-dependent
code-app: 3d1122617a5f16d96f4eb8308e76271e21f82c92
* fix(kap-server): rebuild chat transcript after undo

* test(kap-server): provide undo registry in broadcaster fixtures

* fix(kap-server): preserve non-anchor steers through undo

* fix(kap-server): rebuild undo transcript after persistence completes

* test: complete restored notification origin fixture

* fix(kap-server): keep original prompt when steering

* chore: add changeset for steered prompt content fix

* fix(kap-server): keep restored prompt content after late projector attach

* fix(kap-server): prefer store prompt content after transcript backfill
…rns 401 (#3846)

* fix(agent-core-v2): mark MCP servers needs-auth when a tool call returns 401

* fix(agent-core-v2): cover retried MCP calls with unauthorized handling and recheck entry after close

* fix(agent-core-v2): bind MCP unauthorized reports to the originating client

* fix(agent-core-v2): invalidate rejected MCP OAuth tokens when entering needs-auth

* fix(agent-core-v2): preserve concurrent MCP OAuth grants and recheck entry after credential awaits

* fix(agent-core-v2): flip MCP servers to needs-auth before invalidating rejected tokens

* fix(agent-core-v2): compare-and-clear rejected MCP OAuth tokens and tolerate cleanup failures

* refactor(agent-core-v2): move rejected-grant handling into the MCP OAuth service

* fix(agent-core-v2): compare MCP OAuth grant age with the scheduler clock

* fix(agent-core-v2): reject future timestamps from the concurrent MCP grant window

* fix(agent-core-v2): distinguish concurrent MCP grants from freshly rejected ones

* fix(agent-core-v2): ignore application-level MCP errors in the unauthorized sniff

* test(agent-core-v2): trim MCP unauthorized tests to the core contract

* Delete .changeset/mcp-oauth-clear-if-current.md

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

---------

Signed-off-by: 7Sageer <sag77r@hotmail.com>
code-app: 3f3f3b2e52115eb0b79bc04ed4fcc2d45b30fe5d
* ci: sign Windows CLI binaries with Azure Artifact Signing

* fix: resolve signtool from Windows SDK when removing SEA binary signature
* ci: release packages

* chore: bump kimi-code release version to 2.0.0

---------

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: liruifengv <liruifeng1024@gmail.com>
Bring the fork up to @moonshot-ai/kimi-code@2.0.0 (448 commits). All ten
hardening changes are carried forward and verified present after the merge;
upstream fixed none of them independently.

Conflicts of note:

- Permission policy chain. Upstream added DangerousCommandAsk at position 3,
  above session-approval history and user allow rules. That placement is kept,
  and this fork's move of AutoModeApprove below the content-sensitive asks is
  kept alongside it, so auto mode still does not waive the secrets, .git and
  execution-trigger checks. Upstream skips the guard for non-interactive hosts;
  that skip is dropped so a headless host in manual or yolo mode stays guarded.

- Approval broker. Upstream replaced the broker with interactions.request, so
  the unattended refusal is re-expressed against the new API. A headless run
  that needs approval still refuses with an explanation instead of waiting for
  an answer that cannot arrive.

- Plugin marketplace. Upstream moved resolveMarketplaceLocation from the CLI
  into agent-core-v2. The https requirement and host allowlist move with it,
  extended to cover the new .ai region hosts.

- Native update preflight. Upstream's staged swap verifies a SHA-256 manifest,
  which supersedes this fork's patch, so upstream's version is taken.

agent-core-v2, kap-server and transcript are comment-free zones upstream now
(scripts/check-no-comments.mjs runs in pnpm lint), so the security rationale
that lived in comments there moves to HARDENING.md.

Verified: typecheck, lint and build clean. The test suite has 71 failures, all
in kap-server search and transcript contract; the identical 71 fail on a clean
upstream 2.0.0 checkout, so the merge adds no regressions. Runtime checked
against a stub endpoint: a headless Bash call is refused rather than executed
or hung, and runs when KIMI_CODE_AUTO_APPROVE_BASH is set.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@devGregA
devGregA merged commit aa12749 into main Sep 17, 2026
15 checks passed
@devGregA
devGregA deleted the sync/upstream-2.0.0 branch September 17, 2026 18:49
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.