Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions .changeset/add-anaconda-desktop-provider.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
"@kilocode/cli": minor
"@kilocode/sdk": minor
"kilo-code": minor
---

Connect to a local Anaconda Desktop text-generation model server from the CLI or VS Code.
4 changes: 4 additions & 0 deletions packages/kilo-docs/lib/nav/ai-providers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,10 @@ export const AiProvidersNav: NavSection[] = [
{ href: "/ai-providers/ollama", children: "Ollama" },
{ href: "/ai-providers/lmstudio", children: "LM Studio" },
{ href: "/ai-providers/atomic-chat", children: "Atomic Chat" },
{
href: "/ai-providers/anaconda-desktop",
children: "Anaconda Desktop",
},
{ href: "/ai-providers/vscode-lm", children: "VS Code LM API" },
{
href: "/ai-providers/openai-compatible",
Expand Down
92 changes: 92 additions & 0 deletions packages/kilo-docs/pages/ai-providers/anaconda-desktop.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
---
title: "Using Anaconda Desktop with Kilo Code | Local Models"
description: "Connect Kilo Code to a local Anaconda Desktop text-generation model server from the TUI or VS Code."
sidebar_label: Anaconda Desktop
---

# Using Anaconda Desktop With Kilo Code

Kilo Code can discover the text-generation model served by [Anaconda Desktop](https://www.anaconda.com/products/desktop) and connect to its local OpenAI-compatible endpoint. Kilo imports the server connection for you, so you do not need to copy an API key or configure a base URL manually.

**Official documentation:** [Anaconda Desktop](https://www.anaconda.com/docs/tools/anaconda-desktop/key-features)

## Supported Platforms

Anaconda Desktop and Kilo must run on the same supported computer.

| Operating system | Supported installation |
|---|---|
| Windows | Windows 11, x86-64 |
| macOS | macOS 13 or later, Apple Silicon |
| Linux | Debian or Ubuntu, x86-64 or ARM64 |

Remote backends, remote-only model endpoints, and non-interactive or headless setup are not supported. Complete setup in the Kilo TUI or VS Code on the computer running Anaconda Desktop.

## Set Up Anaconda Desktop

1. Download and install Anaconda Desktop from the [official product page](https://www.anaconda.com/products/desktop). See Anaconda's [installation guide](https://www.anaconda.com/docs/tools/anaconda-desktop/install-desktop) for platform-specific steps.
2. Open Anaconda Desktop and sign in with your Anaconda account or your organization's assigned credentials.
3. Select **Model Catalog** and filter for a **Text Generation** model. Prefer a model tagged **Tool Calling** for full Kilo agent functionality.
4. Select a quantization that fits your computer, then click **Download**. Anaconda's [model catalog guide](https://www.anaconda.com/docs/tools/anaconda-desktop/model-catalog) explains model types, hardware requirements, and quantization choices.
5. Select **Model Servers**, choose the downloaded model and file, and start its server. Enable tool calling when the selected model and server configuration support it.

{% callout type="note" %}
Kilo only discovers Desktop state and connects to an existing server. Downloading or deleting models and creating, switching, starting, stopping, or deleting servers must be done in Anaconda Desktop. See Anaconda's [model server guide](https://www.anaconda.com/docs/tools/anaconda-desktop/servers).
{% /callout %}

## Connect Kilo Code

{% tabs %}
{% tab label="TUI" %}

1. Run `/connect` in the Kilo TUI.
2. Select **Anaconda Desktop**.
3. Follow the setup dialog. Kilo can open Anaconda Desktop; after making changes there, return and choose **Check again**.
4. When the model server is ready, choose **Connect** to import its connection and make the served model available in the model picker.

To refresh an existing connection, run `/connect`, select **Anaconda Desktop**, and choose **connect / refresh now** after changing the model, server address or port, or server API key in Desktop. Kilo re-discovers the active server and replaces its stored model and connection information.

{% /tab %}
{% tab label="VS Code" %}

1. Open Kilo Code **Settings** using the gear icon and select **Providers**.
2. Add **Anaconda Desktop**. No manual API-key field is shown.
3. Follow the setup dialog. Kilo can open Anaconda Desktop; after making changes there, return and select **Check again**.
4. When the model server is ready, select **Connect** to import its connection and refresh the model picker.

For an existing connection, open **Settings**, select **Providers**, and select **Manage / Refresh** for Anaconda Desktop after changing the model, server address or port, or server API key in Desktop.

{% /tab %}
{% /tabs %}

## Tool Calling

When Desktop reports that the model server supports tool calling, Kilo allows you to connect without an additional warning. Tool calling lets the model use Kilo's tools to inspect files, edit code, and run commands.

{% callout type="warning" %}
If tool support is unavailable or cannot be detected, Kilo shows a warning and requires confirmation before connecting. You can still use the model for text generation, but normal coding-agent actions are limited and may fail. For the best experience, choose a **Text Generation** model tagged **Tool Calling** and enable the server's tool-call support when available.
{% /callout %}

## How Keys Are Handled

Anaconda Desktop uses two separate credentials:

- **Desktop management key** - Allows local discovery of Desktop models and servers. Kilo reads it from Desktop's configuration only when needed and never copies it into Kilo storage.
- **Inference server key** - Authenticates chat-completion requests to the running model server. Kilo imports this key into its normal provider authentication storage together with the local endpoint and model details.

Kilo never asks you to paste either key. If the inference server key or endpoint changes, use **Refresh** to import the current values.

## Disconnect

In VS Code, open **Settings** > **Providers** and select **Disconnect** for Anaconda Desktop. In a terminal, run `kilo auth logout` and select Anaconda Desktop. Disconnecting removes only Kilo's stored provider authentication and connection metadata. It does not stop Anaconda Desktop, stop the model server, or delete the downloaded model.

Use Anaconda Desktop itself to stop or change the server.

## Troubleshooting

- **Desktop is not detected:** Install it from the [official product page](https://www.anaconda.com/products/desktop), or use **Open Anaconda Desktop** if it is installed but not running.
- **Sign-in is required:** Open Desktop, complete sign-in, and leave the Kilo setup dialog open so it can detect the change.
- **No model is available:** Download a model whose type is **Text Generation**, not an embedding-only model.
- **No server is available:** Start the downloaded model from **Model Servers** in Desktop.
- **The server changed:** Use **Refresh** in Kilo to replace the stored model, endpoint, and inference key.
- **The server is unhealthy:** Check the server status and logs in Desktop, then restart it there before refreshing Kilo.
1 change: 1 addition & 0 deletions packages/kilo-docs/pages/ai-providers/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ Major AI companies offering powerful models via API:
Run models on your own hardware for privacy and offline use:

- **[Atomic Chat](/docs/ai-providers/atomic-chat)** - Local models with TurboQuant inference and auto-discovery in Kilo Code
- **[Anaconda Desktop](/docs/ai-providers/anaconda-desktop)** - Discover and connect to a local text-generation model server
- **[Ollama](/docs/ai-providers/ollama)** - Easy local model management
- **[LM Studio](/docs/ai-providers/lmstudio)** - Desktop app for local models
- **[OpenAI Compatible](/docs/ai-providers/openai-compatible)** - Any OpenAI-compatible endpoint
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 2 additions & 0 deletions packages/kilo-docs/source-links.md
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,8 @@
<!-- packages/opencode/src/provider/transform.ts -->
- <https://vercel.link/ai-gateway-token>
<!-- packages/opencode/src/cli/cmd/providers.ts -->
- <https://www.anaconda.com/products/desktop>
<!-- packages/opencode/src/kilocode/anaconda-desktop/domain.ts -->
- <https://www.eclipse.org/downloads/download.php?file=/jdtls/snapshots/jdt-language-server-latest.tar.gz>
<!-- packages/opencode/src/lsp/server.ts -->
- <https://www.googleapis.com/auth/cloud-platform>
Expand Down
15 changes: 15 additions & 0 deletions packages/kilo-vscode/src/KiloProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,7 @@ import {
resolveStoredKey,
} from "./provider-actions"
import type { StoredProviderKey } from "./provider-actions"
import { AnacondaDesktopBridge } from "./anaconda-desktop/bridge"
import { fetchOpenAIModels, FetchModelsError } from "./shared/fetch-models"
import type { Agent } from "@kilocode/sdk/v2/client"
import { configFeatures } from "./features"
Expand Down Expand Up @@ -343,6 +344,7 @@ export class KiloProvider implements vscode.WebviewViewProvider, TelemetryProper
private readonly sandboxTransitions = new Map<string, Promise<void>>()
private readonly revisions = new Map<string, { id: string; seq: number }>()
private readonly refreshes = new Map<string, number>()
private readonly anacondaDesktop = new AnacondaDesktopBridge()
private sessionStatusMap = new Map<string, SessionStatus["type"]>() // Latest status used for destructive config warnings.
private sessionDirectories = new Map<string, string>() // Per-session directory overrides, such as Agent Manager worktrees.
private readonly aborts = new SessionAbort()
Expand Down Expand Up @@ -1015,6 +1017,18 @@ export class KiloProvider implements vscode.WebviewViewProvider, TelemetryProper
case "saveCustomProvider":
await this.handleProviderAction(message)
break
case "anacondaDesktopStatus":
case "anacondaDesktopOpen":
case "anacondaDesktopSync":
case "cancelAnacondaDesktopRequest":
await this.anacondaDesktop.handle(message, {
client: this.client,
directory: this.getWorkspaceDirectory(),
post: (reply) => this.postMessage(reply),
refresh: () => this.fetchAndSendProviders(),
error: getErrorMessage,
})
break
case "fetchCustomProviderModels":
this.handleFetchCustomProviderModels(message).catch((e) =>
console.error("[Kilo New] fetchCustomProviderModels failed:", e),
Expand Down Expand Up @@ -3856,6 +3870,7 @@ export class KiloProvider implements vscode.WebviewViewProvider, TelemetryProper
this.trackedSessionIds.clear()
this.syncedChildSessions.clear()
this.sessionDirectories.clear()
this.anacondaDesktop.dispose()
this.aborts.clear()
this.sessionStatusMap.clear()
this.ignoreController?.dispose()
Expand Down
94 changes: 94 additions & 0 deletions packages/kilo-vscode/src/anaconda-desktop/bridge.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
import type { KiloClient } from "@kilocode/sdk/v2"
import type {
AnacondaDesktopAction,
AnacondaDesktopExtensionMessage,
AnacondaDesktopWebviewMessage,
} from "../shared/anaconda-desktop-messages"

interface Context {
client?: KiloClient | null
directory: string
post: (message: AnacondaDesktopExtensionMessage) => void
refresh: () => Promise<void>
error: (error: unknown) => string
}

type Request = Exclude<AnacondaDesktopWebviewMessage, { type: "cancelAnacondaDesktopRequest" }>

function action(message: Request): AnacondaDesktopAction {
if (message.type === "anacondaDesktopStatus") return "status"
if (message.type === "anacondaDesktopOpen") return "open"
return "sync"
}

export class AnacondaDesktopBridge {
private readonly requests = new Map<string, AbortController>()

async handle(message: AnacondaDesktopWebviewMessage, ctx: Context) {
if (message.type === "cancelAnacondaDesktopRequest") {
this.requests.get(message.requestId)?.abort()
this.requests.delete(message.requestId)
return
}

if (!ctx.client) {
ctx.post({
type: "anacondaDesktopActionError",
requestId: message.requestId,
action: action(message),
message: "Not connected to CLI backend",
})
return
}

const ctrl = new AbortController()
this.requests.set(message.requestId, ctrl)
try {
if (message.type === "anacondaDesktopStatus") {
const response = await ctx.client.anacondaDesktop.status(
{ directory: ctx.directory },
{ throwOnError: true, signal: ctrl.signal },
)
if (!response.data) throw new Error("Failed to check Anaconda Desktop")
if (ctrl.signal.aborted) return
ctx.post({ type: "anacondaDesktopStatusResult", requestId: message.requestId, status: response.data })
return
}

if (message.type === "anacondaDesktopOpen") {
await ctx.client.anacondaDesktop.open({ directory: ctx.directory }, { throwOnError: true, signal: ctrl.signal })
if (ctrl.signal.aborted) return
ctx.post({ type: "anacondaDesktopOpened", requestId: message.requestId })
return
}

const response = await ctx.client.anacondaDesktop.sync(
{
directory: ctx.directory,
acknowledgeToolLimitations: message.acknowledgeToolLimitations,
},
{ throwOnError: true, signal: ctrl.signal },
)
if (!response.data) throw new Error("Failed to synchronize Anaconda Desktop")
if (ctrl.signal.aborted) return
await ctx.refresh()
if (ctrl.signal.aborted) return
ctx.post({ type: "anacondaDesktopSynced", requestId: message.requestId, status: response.data })
} catch (error) {
if (ctrl.signal.aborted) return
ctx.post({
type: "anacondaDesktopActionError",
requestId: message.requestId,
action: action(message),
message: ctx.error(error) || `Failed to ${action(message)} Anaconda Desktop`,
})
} finally {
if (this.requests.get(message.requestId) === ctrl) this.requests.delete(message.requestId)
}
}

dispose() {
for (const ctrl of this.requests.values()) ctrl.abort()
this.requests.clear()
}
}
36 changes: 36 additions & 0 deletions packages/kilo-vscode/src/shared/anaconda-desktop-messages.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import type { AnacondaDesktopStatus } from "@kilocode/sdk/v2/client"

export type AnacondaDesktopAction = "status" | "open" | "sync"

export type AnacondaDesktopWebviewMessage =
| { type: "anacondaDesktopStatus"; requestId: string }
| { type: "anacondaDesktopOpen"; requestId: string }
| {
type: "anacondaDesktopSync"
requestId: string
acknowledgeToolLimitations: boolean
}
| { type: "cancelAnacondaDesktopRequest"; requestId: string }

export type AnacondaDesktopExtensionMessage =
| {
type: "anacondaDesktopStatusResult"
requestId: string
status: AnacondaDesktopStatus
}
| { type: "anacondaDesktopOpened"; requestId: string }
| {
type: "anacondaDesktopSynced"
requestId: string
status: Extract<AnacondaDesktopStatus, { type: "ready" }>
}
| {
type: "anacondaDesktopActionError"
requestId: string
action: AnacondaDesktopAction
message: string
}

export type AnacondaDesktopRequest = Exclude<AnacondaDesktopWebviewMessage, { type: "cancelAnacondaDesktopRequest" }>
export type AnacondaDesktopResult = Exclude<AnacondaDesktopExtensionMessage, { type: "anacondaDesktopActionError" }>
export type AnacondaDesktopError = Extract<AnacondaDesktopExtensionMessage, { type: "anacondaDesktopActionError" }>
Loading
Loading