Skip to content

feat: unify the host identity across OAuth, telemetry, and kap-server - #2382

Merged
liruifengv merged 15 commits into
mainfrom
feat/host-identity-unification
Jul 30, 2026
Merged

feat: unify the host identity across OAuth, telemetry, and kap-server#2382
liruifengv merged 15 commits into
mainfrom
feat/host-identity-unification

Conversation

@liruifengv

Copy link
Copy Markdown
Collaborator

Related Issue

No linked issue — the problem is explained in the next section.

Problem

The OAuth device-flow authorization page cannot tell which client started a login flow, which blocks offering an "open the desktop app" handoff after browser authorization. Investigating that requirement exposed that the whole host-identity chain is broken in several ways:

  1. Device headers never reach the OAuth endpoints on most paths. X-Msh-Platform is hardcoded to kimi_code_cli in the oauth package — and worse, the v2 OAuthToolkitService (backing kap-server, i.e. kimi web and embedded desktop servers) and the v1 ServicesManagedAuthFacade are constructed with no identity at all, so device authorization, token polling, and refresh requests go out with none of the six X-Msh-* device headers (not even a product User-Agent). Only the CLI login path (node-sdk) sends them today.
  2. Hardcoded fallbacks lie about the host. kap-server's default outbound User-Agent is kimi-code-cli/<version> regardless of which product embeds the server.
  3. Scattered, inconsistent plumbing. Three names for the product token (userAgentProduct, productName, flat clientVersion), no platform field in the identity type, and hosts forced to abuse DI seeds for plain configuration — the desktop replaces the whole bootstrap options object to change one version string and patches the platform header by hand after building it.

What changed

One identity type, required at composition roots, derived everywhere downstream:

  • @moonshot-ai/kimi-code-oauthKimiHostIdentity becomes { productName, version, platform, userAgentSuffix? }: userAgentProduct renamed to productName, and platform is a new required field so every host explicitly declares its X-Msh-Platform (the CLI constant remains a fallback only for direct createKimiDeviceHeaders callers).
  • @moonshot-ai/agent-core-v2 — the bootstrap snapshot carries a required clientIdentity (replacing the flat clientVersion). OAuthToolkitService passes it to the OAuth toolkit, so the OAuth device-flow endpoints now send the full X-Msh-* set on every host; the telemetry appender reads the client version from the same source. The session export manifest gains an optional desktopVersion field.
  • @moonshot-ai/agent-corecreateManagedAuthFacade takes an optional identity and every call site supplies one; token refreshes from inside the core now carry the device headers.
  • @moonshot-ai/kap-serverServerStartOptions.hostIdentity is now required (ServerHostIdentity = KimiHostIdentity + optional prompt display fields; HostIdentityOverrides was renamed to PromptIdentityOverrides with its productName field now displayName), version renamed to serverVersion (the engine version, matching the server_version wire field), and the default outbound headers are derived from the identity instead of a hardcoded CLI UA. Session export manifests now record the host product version as kimiCodeVersion — plus desktopVersion for desktop exports — and no longer include the engine version.
  • CLIkimi web passes the explicit CLI identity and tags its requests with a (web) User-Agent suffix so upstream can tell web-UI traffic apart from direct CLI runs; kimi -p (v2 experimental) passes the same identity.

Wire-behavior notes: CLI and VS Code hosts keep reporting kimi_code_cli (no change there). The real behavior change is that kap-server-hosted flows (kimi web, embedded desktop servers) go from sending no device headers to sending the full set, including the stable X-Msh-Device-Id — flagged here so the receiving services are not surprised.

Checklist

  • I have read the CONTRIBUTING document.
  • I have linked a related issue, or explained the problem above.
  • I have added tests that prove my feature works.
  • Ran gen-changesets skill — 6 changesets (minor for the API breaks, patch for additive changes).
  • Ran gen-docs skill, or this PR needs no doc update (no user-facing docs affected).

Verification

  • pnpm run build:packages, pnpm run typecheck, pnpm run lint — green.
  • Package suites: oauth 264, agent-core 4117, agent-core-v2 4232, node-sdk 329, vscode 297, CLI 2457 passing (4 failures in run-shell.test.ts reproduce identically on pristine origin/main; kap-server's ENOTEMPTY query-store cleanup races likewise pre-date this change).
  • End-to-end smoke against a mock OAuth host: a server started with a desktop-shaped hostIdentity issues POST /api/oauth/device_authorization carrying all six X-Msh-* headers with the desktop platform value.

X-Msh-Platform was hardcoded to kimi_code_cli in createKimiDeviceHeaders,
so non-CLI hosts could not state their own platform and the desktop had
to patch the header after the fact. KimiHostIdentity now carries a
required platform (every host declares its own value; the CLI constant
stays the fallback only for direct createKimiDeviceHeaders callers), and
userAgentProduct is renamed to productName so the transport identity
uses one name everywhere.

All in-repo identity constructions pass platform explicitly; the wire
value for CLI and VS Code hosts is unchanged (kimi_code_cli).
Replace the flat clientVersion field with a required clientIdentity
(KimiHostIdentity) so every consumer reads the same host identity
object: OAuthToolkitService now passes it to the OAuth toolkit, which
means the OAuth device-flow endpoints (device authorization, token
polling, refresh) on the kap-server path finally send the full X-Msh-*
device headers instead of none, and the telemetry cloud appender reads
client_version from the same source. A built-in CLI fallback keeps bare
bootstrap() calls in tests working; composition roots must pass their
own identity.

The session export manifest grows an optional desktopVersion field
(payload plumbed through; filled by kap-server in a follow-up).
The v1 managed auth facade constructed its OAuth toolkit without an
identity, so token refreshes from inside the core went out without any
X-Msh-* device headers. createManagedAuthFacade now takes an optional
KimiHostIdentity and every call site supplies one:
CoreProcessService._defaultOAuthTokenResolver forwards the core
process's options.identity (the same source _defaultKimiRequestHeaders
uses), and the DI-held services (oauth / auth summary / model catalog)
read it from a new optional identity field on IEnvironmentService. The
library-level "no identity, no device headers" contract is unchanged.
…rs from it

ServerStartOptions.hostIdentity is now a required ServerHostIdentity
(KimiHostIdentity + optional prompt display fields), replacing both the
old optional HostIdentityOverrides (renamed to PromptIdentityOverrides,
its productName field now displayName) and the version option (renamed
to serverVersion — it is the engine version reported as server_version,
while the host product version travels in hostIdentity.version).

The server now feeds bootstrap's clientIdentity from hostIdentity and
derives the default outbound headers (User-Agent + X-Msh-*) from it via
createKimiDefaultHeaders, so kap-server-hosted OAuth flows and model /
WebSearch requests carry the real host identity instead of a hardcoded
kimi-code-cli fallback UA. Explicit header seeds still win as an escape
hatch.

Session export manifests record the host product version: kimiCodeVersion
now carries hostIdentity.version (the engine version no longer appears),
and desktop exports (desktop: true) are additionally stamped with a
desktopVersion field. The instance registry keeps its host_version wire
field for compatibility (kimi-inspect reads it); only the in-memory name
changed to serverVersion.
kimi web now passes createKimiCodeHostIdentity(version) as the server's
hostIdentity, so web-UI OAuth flows and the engine's outbound requests
carry the explicit CLI identity (productName + version + platform). The
explicit hostRequestHeadersSeed is dropped — kap-server derives the same
headers from hostIdentity — and buildKimiDefaultHeaders goes away with
its only consumer.
kimi web shares the CLI product token and platform, so its outbound
requests were indistinguishable from direct CLI runs upstream. Its host
identity now carries userAgentSuffix 'web', putting web-UI traffic at
kimi-code-cli/<version> (web) while X-Msh-Platform stays kimi_code_cli.
@changeset-bot

changeset-bot Bot commented Jul 29, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 1439ebd

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 9 packages
Name Type
@moonshot-ai/agent-core Patch
@moonshot-ai/kimi-code Patch
@moonshot-ai/kap-server Minor
@moonshot-ai/kimi-code-oauth Minor
@moonshot-ai/kimi-code-sdk Minor
@moonshot-ai/agent-core-v2 Minor
@moonshot-ai/acp-adapter Patch
kimi-code Patch
@moonshot-ai/klient Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@pkg-pr-new

pkg-pr-new Bot commented Jul 29, 2026

Copy link
Copy Markdown
pnpm dlx https://pkg.pr.new/@moonshot-ai/kimi-code@1439ebd
npx https://pkg.pr.new/@moonshot-ai/kimi-code@1439ebd

commit: 1439ebd

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: f2a293c026

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread packages/oauth/src/toolkit.ts Outdated
…rap identity switch

The bootstrap snapshot replaced the flat clientVersion scalar with
clientIdentity, which broke klient's env() fan-out (RPCError: method not
found). The wire surface keeps clientVersion — now sourced from
clientIdentity.version — and bootstrapService gains a clientIdentity
read (registered in envContract with an object schema) for consumers
that want the full identity.
The OAuth endpoints used to receive only the X-Msh-* device headers
(undici's default UA otherwise), which left the OAuth host unable to
distinguish runtime surfaces — notably kimi web, whose platform matches
the CLI and whose only distinguishing mark is the (web) UA suffix. The
toolkit now feeds the full identity headers (User-Agent + X-Msh-*) into
every device authorization, token polling, and refresh request; the
request-header type widens from DeviceHeaders to OAuthRequestHeaders.
@liruifengv

Copy link
Copy Markdown
Collaborator Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: bad0bab0be

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread .changeset/sdk-host-identity-rename.md
Comment thread packages/oauth/src/identity.ts Outdated
Comment thread packages/agent-core-v2/src/app/bootstrap/bootstrap.ts Outdated
The VS Code extension inherited the CLI's hardcoded X-Msh-Platform value;
with platform now an explicit identity field it declares its own, so the
managed endpoints and OAuth host can tell extension traffic apart from
CLI runs.
…ion root

The bootstrap fallback identity fabricated a kimi-code-cli/unknown host
for any caller that forgot to pass one — the same silent-misreport
pattern this series set out to remove, and it made "required" a lie.
BootstrapInput.clientIdentity is now required, so a missing identity
fails at compile time instead of being papered over. Test and example
callers pass a shared fixture (klient examples and test engines get one
each); the node-sdk v2 client asserts its host identity with the oauth
helper. Also folds DeviceHeaders from an interface into a type alias so
it stays assignable to the widened OAuthRequestHeaders record.
Drops the quiet CLI fallback in createKimiDeviceHeaders (the same
silent-misreport pattern removed from the bootstrap identity): platform
is now a required option, validated with the same required-ASCII rule as
the version — empty or all-non-ASCII values throw instead of emitting a
blank X-Msh-Platform, and header-unsafe characters are stripped rather
than sent raw.
The interactive v2 engine path (experimental flag) bootstrapped without
a hostRequestHeaders seed, so managed vendor calls went out with the
SDK's default User-Agent (OpenAI/JS) and no X-Msh-* at all — v1 passes
the full identity headers on the same requests. The v2 client now seeds
the headers from its asserted host identity, and a test pins the seed.
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.

1 participant