Skip to content

feat(gateway): add Kilo device login and team selection - #15064

Merged
marius-kilocode merged 4 commits into
gateway-planfrom
gateway-login-v2
Oct 15, 2026
Merged

marius-kilocode merged 4 commits into
gateway-planfrom
gateway-login-v2

Conversation

@marius-kilocode

Copy link
Copy Markdown
Collaborator

Stacked on #15060.
Part of #14991 (and #13750), PR 1 of the Gateway series. Plan unit U-E1-03 (login and team foundation). Supersedes #14998.

What Problem This Solves

On the v2 branch, the kilo provider used upstream's packages/core/src/plugin/provider/kilo.ts, which only sends HTTP-Referer: https://opencode.ai/ and X-Title: opencode. It had no device authorization flow, no profile or team selection, no X-KILOCODE-ORGANIZATIONID header, and no anonymous fallback when signed out. script/dev-isolated.ts worked around this by reading the v1 login from ~/.local/share/kilo/auth.json.

Why This Change Was Made

Following the architecture in #14991 and #15060:

  1. Pure Gateway logic in packages/kilo-gateway-core (@kilocode/gateway-core) with zero @opencode/* imports (enforced by test/boundary.test.ts):
    • KiloServer: server URL from KILO_API_URL (default https://api.kilo.ai) or embedded before the final : of a development token (fromToken), and /api/gateway base URL.
    • KiloDevice: device authorization (POST /api/device-auth/codes and GET /api/device-auth/codes/{code}), request timeouts capped at the remaining code lifetime, interruption support, and exponential backoff on network errors, timeouts, 408, 429, or 5xx until code expiry.
    • KiloProfile: GET /api/profile decoding (supporting both nested user and flat shapes) and the v1 defaultOrganizationId rule (prefer selectedOrganizationId when valid, otherwise fall back to the first team only when hasPersonalAccount === false).
    • KiloAccount: environment token lookup (KILO_API_KEY > KILOCODE_TOKEN > KILO_AUTH_CONTENT), server and team precedence (token-embedded server > login server > KILO_API_URL; KILO_ORG_ID > selected team > login default), and ANONYMOUS_KEY = "anonymous".
    • KiloHeaders: HTTP-Referer: https://kilocode.ai, X-Title: Kilo Code, X-KILOCODE-EDITORNAME, X-KILOCODE-FEATURE, and X-KILOCODE-ORGANIZATIONID.
    • src/testing.ts: local Gateway fixture exported as @kilocode/gateway-core/testing (compiled into dist/testing.js so verify-package.ts tarball packing resolves it cleanly).
  2. Thin v2 plugin adapter (packages/core/src/kilocode/gateway.ts, gateway-rpc.ts):
    • Replaces KiloPlugin in packages/core/src/plugin/provider.ts.
    • Registers the device OAuth method (Kilo Gateway (Device Authorization)), sets baseURL and Kilo provider headers, gives environment tokens (KILO_API_KEY, KILOCODE_TOKEN, KILO_AUTH_CONTENT) precedence over stored credentials, and enables kilo with apiKey: "anonymous" when signed out.
    • Registers kilocode.gateway RPC (profile and organization.set), persists the team choice per connection in plugin storage, and broadcasts rpc.kilocode.gateway.organization globally via Bus.Service ({ global: true }) so all open locations reload.
  3. TUI account picker and /teams (packages/tui/src/kilocode/gateway.tsx):
    • Prompts for Personal Account vs Team after device login (Select Account (<email>)) when the user belongs to one or more teams.
    • Registers /teams (team, org, orgs) to switch between Personal Account and teams at any time.
  4. Isolated bun dev (script/dev-isolated.ts):
    • Removes the v1 ~/.local/share/kilo/auth.json token reuse so bun dev starts cleanly signed out with kilo/kilo-auto/free.

Shared upstream files changed

File Option Missing seam
packages/core/src/plugin/provider.ts 2 (host registration, temporary) Replace KiloPlugin with KiloGatewayPlugin until the Kilo host registers built-in plugins via SdkPlugins.register (U-E0-01).
packages/core/test/plugin/provider-kilo.test.ts 3 (seam patch) Expect kilocode.gateway in ProviderPlugins in place of opencode.provider.kilo.
packages/core/package.json 3 (seam patch) Runtime dependency on @kilocode/gateway-core for the built-in adapter.
packages/sdk/script/verify-package.ts 3 (seam patch) Pack kilo-gateway-core before core in the SDK pack check.
turbo.json 3 (seam patch) Register @kilocode/gateway-core#test so bun turbo test --affected runs the package tests in CI.
packages/tui/src/component/dialog-integration.tsx 3 (seam patch) Run chooseKiloAccount after a Kilo Gateway login completes (TuiPlugin has no post-connect hook).
packages/tui/src/plugin/builtins.ts 2 (host registration, temporary) Register KiloTeams (/teams) until the Kilo host owns the TUI built-in list (U-E0-01).

User Impact

  • /connect -> Kilo Gateway offers Kilo Gateway (Device Authorization) alongside API key.
  • Approving the device code signs in, selects the backend default team, and opens the Select Account picker when the user belongs to teams.
  • /teams (/team, /org, /orgs) switches between Personal Account and teams.
  • Signed out, kilo/kilo-auto/free works out of the box with the anonymous key and a paid model returns "You need to sign in to use this model."

Evidence

Automated checks

  • bun script/check-opencode-annotations.ts: ok (39 shared files against 4d9678a79d6)
  • bun script/check-shared-patches.ts: ok
  • bun run --cwd packages/kilo-gateway-core typecheck && bun run --cwd packages/kilo-gateway-core build: pass
  • bun run --cwd packages/core typecheck: pass
  • bun run --cwd packages/tui typecheck: pass
  • bun test --cwd packages/kilo-gateway-core: 18 pass, 0 fail
  • bun test --cwd packages/core test/kilocode/gateway.test.ts test/plugin/provider-kilo.test.ts: 11 pass, 0 fail
  • bun test --cwd packages/tui test/kilocode/gateway.test.ts: 2 pass, 0 fail

CLI & TUI Self-Test

  1. Signed-out free model (kilo/kilo-auto/free):
    • env KILO_V2_PROFILE=$(mktemp -d) bun dev run --standalone "Reply with only the word PONG." -> PONG (pass).
  2. Signed-out paid model (kilo/deepseek/deepseek-v4.1-flash):
    • env KILO_V2_PROFILE=$(mktemp -d) bun dev run --standalone -m kilo/deepseek/deepseek-v4.1-flash "Reply with only PONG." -> Error: You need to sign in to use this model. (pass).
  3. Signed-in paid model (KILO_API_KEY + KILO_ORG_ID):
    • env KILO_API_KEY=... KILO_ORG_ID=... KILO_V2_PROFILE=$(mktemp -d) bun dev run --standalone -m kilo/deepseek/deepseek-v4.1-flash "Reply with only the word PONG." -> PONG (pass).
  4. TUI device login, post-login account picker, and /teams dialog:

Kilo Gateway device authorization screen in TUI

Post-login Select Account picker in TUI

/teams Select Team dialog in TUI

@kilo-code-bot

kilo-code-bot Bot commented Oct 14, 2026 •

Copy link
Copy Markdown
Contributor

Code Review Summary

Status: 1 Issue Found | Recommendation: Address before merge

Overview

Severity Count
CRITICAL 0
WARNING 0
SUGGESTION 1
Issue Details (click to expand)

SUGGESTION

File Line Issue
packages/kilo-gateway-core/src/server.ts 7 (?!\d+$) backtracks the optional port group for an all-digit secret, so fromToken("http://localhost:3000:123456") returns http://localhost (port dropped) instead of http://localhost:3000
Files Reviewed (3 files)
  • packages/kilo-gateway-core/src/server.ts - 1 issue
  • packages/kilo-gateway-core/src/profile.ts - 0 issues
  • packages/kilo-gateway-core/test/account.test.ts - 0 issues

Fix these issues in Kilo Cloud

Previous Review Summaries (2 snapshots, latest commit 0e7bfa7)

Current summary above is authoritative. Previous snapshots are kept for context only.

Previous review (commit 0e7bfa7)

Status: No Issues Found | Recommendation: Merge

Files Reviewed (3 files)
  • packages/kilo-gateway-core/src/server.ts
  • packages/kilo-gateway-core/test/account.test.ts
  • packages/core/test/kilocode/gateway.test.ts

Notes: incremental review of 04142b61..0e7bfa76 ("sync AI Gateway URL routing with main"). Verified the new aiGatewayFromEnv precedence (KILO_AI_GATEWAY_URL > ${KILO_API_URL}/api/gateway/v1 > https://ai-gateway.kilo.ai/api/v1), the isConfiguredBase/legacyRoute split for custom and token-embedded servers (https://staging.kilo.test → /api/gateway), and that the updated core plugin tests match the resolved baseURL for both signed-in and signed-out (anonymous) paths. The earlier inline note about syncing the addresses is addressed by this commit (that comment is now outdated).

Previous review (commit 04142b6)

Status: No Issues Found | Recommendation: Merge

Files Reviewed (31 files)
  • .kilo/skills/v2-migration/SKILL.md
  • bun.lock
  • packages/core/package.json
  • packages/core/src/kilocode/gateway-rpc.ts
  • packages/core/src/kilocode/gateway.ts
  • packages/core/src/plugin/provider.ts
  • packages/core/test/kilocode/gateway.test.ts
  • packages/core/test/plugin/provider-kilo.test.ts
  • packages/kilo-gateway-core/package.json
  • packages/kilo-gateway-core/src/account.ts
  • packages/kilo-gateway-core/src/device.ts
  • packages/kilo-gateway-core/src/headers.ts
  • packages/kilo-gateway-core/src/http.ts
  • packages/kilo-gateway-core/src/index.ts
  • packages/kilo-gateway-core/src/profile.ts
  • packages/kilo-gateway-core/src/server.ts
  • packages/kilo-gateway-core/src/testing.ts
  • packages/kilo-gateway-core/test/account.test.ts
  • packages/kilo-gateway-core/test/boundary.test.ts
  • packages/kilo-gateway-core/test/device.test.ts
  • packages/kilo-gateway-core/test/profile.test.ts
  • packages/kilo-gateway-core/tsconfig.build.json
  • packages/kilo-gateway-core/tsconfig.json
  • packages/sdk/script/verify-package.ts
  • packages/tui/src/component/dialog-integration.tsx
  • packages/tui/src/kilocode/gateway.tsx
  • packages/tui/src/plugin/builtins.ts
  • packages/tui/test/kilocode/gateway.test.ts
  • script/dev-isolated.ts
  • script/upstream/shared-patches.json
  • turbo.json

Notes: verified the device poll backoff/expiry bounds, request timeout follows the remaining code lifetime, env/credential/team precedence (KILO_API_KEY/KILOCODE_TOKEN/KILO_AUTH_CONTENT, KILO_ORG_ID), the hasPersonalAccount/selectedOrganizationId default-team rule, AbortSignal.any/forkScoped cleanup (no leaks), package exports/files/build consistency, and that the post-login account picker's first-team preselection matches the removed v1 getDefaultOrganizationSelection behavior.


Reviewed by deepseek-v4.1-flash · Input: 53.4K · Output: 18.1K · Cached: 729.3K

}

export function gateway(server: string) {
return `${normalize(server)}/api/gateway`

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

These addresses were changed in main, please make sure it's in sync.

Comment thread packages/kilo-gateway-core/src/server.ts
@marius-kilocode
marius-kilocode merged commit e2e73b1 into gateway-plan Oct 15, 2026
11 checks passed
@marius-kilocode
marius-kilocode deleted the gateway-login-v2 branch October 15, 2026 11:17
@marius-kilocode

Copy link
Copy Markdown
Collaborator Author

@chrarnoldus all addressed. I am merging not to make this less complicated with the other PR's. If you find something please flag then I will follow up.

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

Labels

area:cli CLI, TUI and core engine area:gateway Kilo Gateway and auth area:sdk SDK

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants