Skip to content

feat(providers): add Kimi provider - #7908

Open
ItsJazii wants to merge 21 commits into
pingdotgg:mainfrom
ItsJazii:feat/kimi-provider
Open

feat(providers): add Kimi provider#7908
ItsJazii wants to merge 21 commits into
pingdotgg:mainfrom
ItsJazii:feat/kimi-provider

Conversation

@ItsJazii

@ItsJazii ItsJazii commented Aug 22, 2026

Copy link
Copy Markdown

What Changed

Adds Kimi (Moonshot AI) as a first-class provider, wrapping kimi acp. Structurally modeled on the Grok ACP stack, extended where the Kimi CLI advertises richer capabilities.

  • Provider stack: driver, provider, adapter, text generation, contracts, settings, web UI, mobile icon, user and internals docs
  • Detection and setup: opt-in like sibling ACP providers; binary resolution prefers an explicit Binary path, then the official ~/.kimi-code/bin install, then PATH; in-app OAuth device sign-in writes the official CLI credential file; KIMI_CODE_HOME isolation per instance
  • Reliability: typed probe classification with last-known-good snapshot retention across transient timeouts, cached model discovery, and probe deferral while any Kimi turn is active
  • Shell execution: Kimi executes commands through the ACP client terminal, so this adds a per-session terminal manager (create, output, wait_for_exit, kill, release; byte-capped buffers; teardown on stop, interrupt, and session close). No other T3 provider needed this: their CLIs execute commands themselves
  • Models: dynamic discovery from the CLI's model config option (9 models on current CLI), exact wire IDs preserved, mid-thread Kimi-to-Kimi switching; cross-provider switching stays blocked
  • Modes: Plan toggle maps to native plan, Supervised to default, Auto and Auto-accept-edits to auto, Full access to yolo; full access auto-approves tool gates while plan decisions and Kimi's AskUserQuestion always surface as approval cards
  • Plan approvals: the plan lands on a native approval card showing the plan markdown; approve answers plan_approve so the CLI exits plan mode and implements in the same turn; decline answers plan_revise
  • Thinking: per-model effort discovered from advertised config options (K3: low, high, max; K2.7: on, high); stale selections fall back to the model default instead of failing the turn

Why

The Kimi Code CLI speaks ACP but T3 Code had no adapter for it. This adds bring-your-own-subscription Kimi support consistent with the existing provider set. Everything provider-shaped stays inside Kimi files; orchestration, contracts plumbing, and shared ACP machinery are unchanged except one additive optional flag already supported by the runtime.

Commits

Reviewable commit by commit; each is one concern:

  1. feat(providers): add Kimi provider with in-app OAuth sign-in
  2. fix(kimi): retain healthy snapshot on transient probe timeout
  3. feat(kimi): discover and switch models dynamically
  4. feat(kimi): map interaction controls to native modes
  5. feat(kimi): discover model-specific thinking effort
  6. fix(kimi): defer probes during turns and prefer official binary
  7. fix(kimi): make the provider opt-in like sibling ACP providers
  8. test(kimi): cover adapter turn lifecycle and pre-prompt chain
  9. fix(kimi): implement ACP client terminal support
  10. fix(kimi): kill session terminals on turn interrupt
  11. fix(kimi): repair approval and plan-mode flows
  12. fix(kimi): make plan decisions native to the CLI

Verification

  • 57 focused Kimi tests (adapter turn lifecycle including interrupt/steer races, ACP support, terminal manager) plus 11 OAuth tests; mock agent mirrors real CLI behavior including ExitPlanMode retry semantics observed live
  • Scoped typechecks: contracts, client-runtime, server, web, mobile
  • Live end-to-end on Windows with kimi-cli 0.37.2 as a daily driver: auto-detection, sign-in, terminals, all four modes, plan approval implementing in the same turn, all thinking levels, mid-thread model switching, multi-hour sessions without status flapping, and remote control from the mobile app
  • Platform honesty: Windows is the live-verified platform. macOS and Linux support is implemented and unit-tested with platform-specific fixtures (binary resolution, shell paths, process kill semantics) but has not been live-run by us; happy to fix anything CI or a maintainer's machine surfaces

Notes for reviewers

  • Kimi routes AskUserQuestion through request_permission; it surfaces as an approval card instead of being silently auto-answered in full access. Intentional and documented
  • Known limitation, documented in docs/user/providers-kimi.md: after approving a plan with the composer left on Plan, follow-ups run in the post-exit mode until a Build turn re-syncs. A proper fix needs the shared ACP runtime to consume config_option_update notifications, which felt out of scope for a provider PR
  • Long-running commands (dev servers) hold the turn open; ACP has no background-terminal concept, so backgrounding is the agent's responsibility. Documented
  • The Kimi icon is included for mobile; it shows once a mobile release ships. Sign-in initiation is web/desktop; mobile consumes the server-side session
  • Unexpected ACP process death does not reap per-terminal scopes; every T3-owned teardown path does. A process-exit watcher would be a shared AcpSessionRuntime change, flagged here rather than smuggled in

UI Changes

New surfaces: the Kimi provider card with Sign in with Kimi, the OAuth device confirmation, per-model Thinking options in the model picker, and plan approvals on the standard approval card.

Kimi detected and authenticated alongside the existing providers (note the CLI self-updated from 0.37.2 to 0.38.0 and detection kept working):

Kimi authenticated in the provider list

K3 mid-task with Thinking Max and Full access, running real commands through the ACP terminal:

K3 working in T3 Code

A short demo video (plan, approve, same-turn implementation, mid-thread model switch) will follow in a comment.

Checklist

  • This PR is small and focused (it is one concern: one provider; large because a provider integration is irreducibly cross-cutting)
  • I explained what changed and why
  • I included before/after screenshots for any UI changes
  • I included a video for animation/interaction changes (following in a comment)

Built with Kimi K3 (Moonshot) driven inside T3 Code itself, with planning and independent review by Claude via Cursor.

Note

Add Kimi provider with OAuth device flow and ACP support

  • Adds full Kimi provider stack: KimiDriver (probe/snapshot/maintenance), KimiAdapter (ACP session/steering/permissions), and KimiTextGeneration (commit messages, PR content, branch names, thread titles with 180s timeout).
  • Implements OAuth 2.0 device authorization grant in KimiOAuth with atomic credential writes, plus kimiAuth.signIn/kimiAuth.signOut WS RPC handlers and KimiAuthError contracts.
  • Adds KimiAcpTerminalManager with create/output/wait/kill/release, killAll, shutdown, UTF-8 boundary truncation at 4 MiB, and signal-based exit reporting.
  • Surfaces Kimi in web/mobile UI: provider picker option with "new" badge, KimiIcon, KimiSignInControl in settings, and providerDriverMeta entry with "Early Access" badge.
  • Risk: KimiSettings.enabled defaults to false; provider probes defer while turns are active via runKimiProbeWithActiveTurnDeferral; kimiAuthSignIn/kimiAuthSignOut RPCs require AuthOrchestrationOperateScope.

Macroscope summarized 21ca899.

ItsJazii and others added 12 commits August 20, 2026 01:53
Wraps the Kimi CLI over ACP mirroring the Grok driver stack, with a
server-side "Sign in with Kimi" device flow that writes the CLI's own
credential file. Model ids are translated to the kimi-code/ config.toml
alias namespace that session/set_model requires.

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Kimi shipped enabled by default while Cursor, Grok and OpenCode all
default off with an explicit opt-in from Settings. Every install would
probe for the Kimi CLI in the background and surface a spurious error
for users without it. Align the default, tests and setup docs.

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Kimi CLI executes shell commands through the ACP client's terminal
capability, which T3 never advertised or implemented, so no Kimi turn
could run a single command. Advertise terminal: true on adapter chat
sessions (probes and text generation stay false) and implement the
terminal/create, output, wait_for_exit, kill and release handlers with
a per-session terminal manager over ChildProcessSpawner. The
wait_for_exit response carries exitCode/signal at the top level, as
Kimi requires; only terminal/output nests it as exitStatus.

Co-authored-by: Cursor <cursoragent@cursor.com>
Pressing Stop while the Kimi agent was parked in terminal/wait_for_exit
(e.g. a foreground dev server it launched) deadlocked the turn forever:
Kimi cannot process session/cancel until its pending wait_for_exit
client request resolves, and the turn could not settle while awaiting
the prompt response.

KimiAcpTerminalManager gains killAll: it kills every live terminal's
process and settles its exit deferred with { exitCode: null, signal:
"SIGTERM" }, but keeps the terminal registered so the agent can still
call terminal/output and terminal/release on it (release stays the only
dispose path besides shutdown). interruptTurn now runs killAll right
before session/cancel, unblocking the agent's wait so it can honor the
cancel and answer the prompt with stopReason cancelled.

Done by Kimi Code (kimi-k2.5) via the T3 Code Kimi provider.
Four live-diagnosed bugs, one root cause: mode/approval state was one-way.

- Approved ExitPlanMode never exited plan mode in T3, so the next turn
  forced Kimi straight back into plan. ExitPlanMode permission requests are
  now intercepted Claude-style: the plan markdown is captured into T3's
  proposed-plan flow and the native exit is cancelled, so Kimi stays in
  plan mode and T3's plan-approval UI owns the mode flip.
- Full access kept asking for approvals after the plan phase because
  auto-approve required the tracked native mode to be yolo while it was
  stuck on plan. Full access now answers tool-gate permission requests in
  any native mode, with an explicit carve-out for user decisions
  (ExitPlanMode, AskUserQuestion), which are never auto-approved.
- Full access + Auto mode asked for every command approval by hand for the
  same reason; fixed by the same predicate change. Kimi's per-mode behavior
  is unchanged — only who answers its permission requests.
- Approval cards kept appearing after the turn was stopped because
  request_permission had no interrupted-turn gate. Requests for stopped or
  dead turns are now cancelled immediately without emitting request.opened.

Also surfaces the real command text from the request payload in approval
cards instead of just the tool title.

Model: Kimi K3 (kimi-code) via T3 Code
The 6d15825 plan-mode interception answered every ExitPlanMode permission
request with "cancelled" and emitted turn.proposed.completed, assuming the
CLI would stay in plan mode and end the turn. Live forensics on thread
f1999acf (native provider event log) showed the assumption was wrong:
kimi-cli reads a cancelled plan decision as "dialog dismissed" — verbatim
on the wire: "Plan approval dismissed. Plan mode remains active." — so it
retried ExitPlanMode six times, escalated through AskUserQuestion, then
gave up and printed the plan as plain text. The native mode flip never
happened either: it was designed to ride a follow-up turn that never
occurred (exactly one session/prompt in the log, one sendTurn span in the
server trace), and the composer still showed Plan.

ExitPlanMode now falls through to the regular approval card with the plan
markdown as the card detail. Approving answers the native plan_approve
option, so kimi-cli leaves plan mode and implements in the same turn: no
retry loop, no mode-flip dependency. Declining selects plan_revise and the
CLI re-presents the plan as a fresh card. Plan decisions remain excluded
from full-access auto-approval.

Known limitation, documented in docs/user/providers-kimi.md: after a
native plan exit, a follow-up sent while the composer still shows Plan
cannot re-enter native plan mode — the shared ACP runtime's tracked mode
still says plan and short-circuits the identical set_mode. The next build
turn pushes the runtime-derived mode and re-syncs tracking.

The mock agent now mirrors the real CLI: a cancelled ExitPlanMode is
retried (bounded) instead of ending the turn, and a selected plan_approve
leaves plan mode silently. The plan-flow e2e asserts exactly one
ExitPlanMode request per decision, so the cancelled-answer regression
fails the suite.

Model: Kimi K3 (kimi-code) via T3 Code
@coderabbitai

coderabbitai Bot commented Aug 22, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 30f11433-4805-40f3-9604-f88ac8e37483

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actions github-actions Bot added vouch:unvouched PR author is not yet trusted in the VOUCHED list. size:XXL 1,000+ changed lines (additions + deletions). labels Aug 22, 2026

@macroscopeapp macroscopeapp Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

One finding on the Kimi settings wiring; the rest of the web changes (KimiIcon following the GrokIcon brand-fill pattern, the Button size="xs"/variant="outline" sign-in control, the settings-standard text-[13px] leading-[1.45] helper text, and the authAction slot on ProviderInstanceCard) match the existing shared-component and Tailwind conventions.

Posted via Macroscope — UI Consistency

Comment thread apps/web/src/components/settings/ProviderSettingsPanel.tsx Outdated

@macroscopeapp macroscopeapp Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

One convention issue found in the new Kimi sign-in error model: KimiAuthError carries no cause, so every wrap in KimiOAuth.ts erases the underlying HTTP/schema/filesystem failure and keeps only cause.message in detail. Everything else reviewed (driver/adapter/probe wiring, ACP support helpers, catchTags usage, schema predicates) follows the existing provider conventions.

Posted via Macroscope — Effect Service Conventions

Comment thread packages/contracts/src/kimiAuth.ts Outdated
Comment thread apps/server/src/provider/kimi/KimiOAuth.ts
Comment thread apps/server/src/ws.ts
Comment thread apps/server/src/provider/Layers/KimiAdapter.ts
Comment thread apps/server/src/provider/acp/KimiAcpSupport.ts Outdated
Comment thread apps/server/src/provider/acp/KimiAcpTerminalSupport.ts Outdated
Comment thread apps/server/src/provider/Drivers/KimiDriver.ts Outdated
Comment thread apps/server/src/ws.ts Outdated
Comment thread apps/server/src/provider/Layers/KimiProvider.ts Outdated
Comment thread apps/web/src/components/settings/KimiSignInControl.tsx Outdated
Comment thread apps/server/src/provider/kimi/KimiOAuth.ts Outdated
@macroscopeapp

macroscopeapp Bot commented Aug 22, 2026

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Skipped

Macroscope did not run approvability analysis for this PR. Macroscope could not determine whether this PR modifies its approvability configuration, so the PR was not approved automatically. A PR that may change the rules that govern approval is never approved automatically.

Not approved because:

  • 2 blocking correctness issues found at or above your repo's Minimum Blocking Severity

@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: aa151a064b

ℹ️ About Codex in GitHub

Your team has set up Codex to 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 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread apps/server/src/provider/Layers/KimiAdapter.ts
Comment thread packages/client-runtime/src/state/server.ts Outdated
Comment thread apps/web/src/components/settings/KimiSignInControl.tsx
ItsJazii and others added 3 commits August 22, 2026 21:53
Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Comment thread apps/server/src/provider/acp/KimiAcpTerminalSupport.ts Outdated

@macroscopeapp macroscopeapp Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Reviewed the web UI surface of this PR (KimiIcon, provider icon/driver registries, KimiSignInControl, and the ProviderInstanceCard/ProviderSettingsPanel authAction seam). Primitive usage is consistent: the new control composes Button with the existing size="xs" + spinner pattern already used in ProviderInstanceCard, KimiIcon mirrors GrokIcon's brand-fill/dark-variant shape, and Kimi is registered in every web-side provider map (providerIconUtils, providerDriverMeta, session-logic). The earlier instance-enabled resolution finding is resolved — the panel now uses resolveProviderInstanceEnabled.

One accessibility-consistency gap in the new async sign-in states is noted inline.

Posted via Macroscope — UI Consistency

Comment thread apps/web/src/components/settings/KimiSignInControl.tsx Outdated

@macroscopeapp macroscopeapp Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Reviewed the new Kimi provider code against the Effect service conventions. Two findings on the new KimiAuthError contract and its construction; the driver/adapter/ACP support modules follow the existing sibling provider patterns (namespace subpath imports, environment-acquired services, Effect.catchTags, structural catchIf on ACP error codes).

Posted via Macroscope — Effect Service Conventions

Comment thread packages/contracts/src/kimiAuth.ts Outdated
Comment thread apps/server/src/provider/kimi/KimiOAuth.ts Outdated
Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
ItsJazii and others added 3 commits August 22, 2026 22:37
Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
A steer waiting on the prompt permit that lost its request fiber (client
disconnect during Stop) skipped the runPrompt finalizer entirely, leaving
promptsInFlight raised and the turn-activity tracker active, which deferred
provider probes until the session stopped. The runPrompt finalizer now marks
the slot settled on every path and a queued-interrupt compensation releases
the slot, settling the merged turn with a cancelled completion when it was
the last one. Found by Devin Review on the fork staging PR.

Implemented by Claude via Cursor.
startedAtMs + Math.min(expiresInSeconds * 1000, Duration.toMillis(MAX_SIGN_IN_DURATION));
let intervalSeconds = Math.max(1, authorization.interval ?? DEFAULT_POLL_INTERVAL_SECONDS);

while ((yield* Clock.currentTimeMillis) < deadlineMs) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟡 Medium kimi/KimiOAuth.ts:328

A backward wall-clock correction can keep the sign-in stream polling past the 15-minute cap, potentially indefinitely, instead of emitting KimiAuthExpiredError. The deadline and loop checks use Clock.currentTimeMillis, so use Clock.monotonicTimeNanos for elapsed-time tracking and deadline checks.

🤖 Copy this AI Prompt to have your agent fix this:
In file @apps/server/src/provider/kimi/KimiOAuth.ts around line 328:

A backward wall-clock correction can keep the sign-in stream polling past the 15-minute cap, potentially indefinitely, instead of emitting `KimiAuthExpiredError`. The deadline and loop checks use `Clock.currentTimeMillis`, so use `Clock.monotonicTimeNanos` for elapsed-time tracking and deadline checks.


const credentialsDir = path.join(resolveKimiCodeHome(homePath), CREDENTIALS_DIR_NAME);
const credentialsPath = path.join(credentialsDir, CREDENTIALS_FILE_NAME);
const temporaryPath = `${credentialsPath}.${process.pid}.${nowEpochMs}.tmp`;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟠 High kimi/KimiOAuth.ts:248

Concurrent sign-ins for the same home can share temporaryPath when they run in the same millisecond, so one rename removes the other attempt's source and causes its write to fail or races the credential contents. Add a per-attempt random suffix to the temporary filename.

Suggested change
const temporaryPath = `${credentialsPath}.${process.pid}.${nowEpochMs}.tmp`;
const temporaryPath = `${credentialsPath}.${process.pid}.${nowEpochMs}.${Math.random().toString(36).slice(2)}.tmp`;
🤖 Copy this AI Prompt to have your agent fix this:
In file @apps/server/src/provider/kimi/KimiOAuth.ts around line 248:

Concurrent sign-ins for the same home can share `temporaryPath` when they run in the same millisecond, so one rename removes the other attempt's source and causes its write to fail or races the credential contents. Add a per-attempt random suffix to the temporary filename.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:XXL 1,000+ changed lines (additions + deletions). vouch:unvouched PR author is not yet trusted in the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant