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
5 changes: 5 additions & 0 deletions .changeset/vscode-kilo-model-links.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"kilo-code": minor
---

Support selecting Kilo Gateway catalog models from VS Code deep links.
3 changes: 2 additions & 1 deletion packages/kilo-vscode/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,8 @@
"open code"
],
"activationEvents": [
"onStartupFinished"
"onStartupFinished",
"onUri"
],
"main": "./dist/extension.js",
"contributes": {
Expand Down
17 changes: 17 additions & 0 deletions packages/kilo-vscode/src/KiloProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -273,6 +273,7 @@ export class KiloProvider implements vscode.WebviewViewProvider, TelemetryProper
private configWarningsShown = false
/** Cached notificationsLoaded payload */
private cachedNotificationsMessage: unknown = null
private pendingKiloModelID: string | null = null
private pendingReviewComments: { comments: unknown[]; autoSend: boolean }[] = []
private readyResolvers: (() => void)[] = []
private promptRecoveryQueued = false
Expand Down Expand Up @@ -687,6 +688,11 @@ export class KiloProvider implements vscode.WebviewViewProvider, TelemetryProper
this.postMessage({ type: "openCloudSession", sessionId })
}

public selectKiloModel(modelID: string): void {
this.pendingKiloModelID = modelID
this.flushPendingKiloModel()
}

public setContinueInWorktreeHandler(
handler: (sessionId: string, progress: (status: string, detail?: string, error?: string) => void) => Promise<void>,
): void {
Expand Down Expand Up @@ -759,6 +765,7 @@ export class KiloProvider implements vscode.WebviewViewProvider, TelemetryProper
console.log("[Kilo New] KiloProvider: ✅ webviewReady received")
this.isWebviewReady = true
this.visibleTaskStreams.clear()
this.flushPendingKiloModel()
await this.syncWebviewState("webviewReady")
this.flushPendingReviewComments()
this.recoverPendingPrompts()
Expand Down Expand Up @@ -1245,6 +1252,7 @@ export class KiloProvider implements vscode.WebviewViewProvider, TelemetryProper

// Connect the shared service (no-op if already connected)
await this.connectionService.connect(workspaceDir)
this.flushPendingKiloModel()

// Subscribe to SSE events for this webview (filtered by tracked sessions)
this.unsubscribeEvent = this.connectionService.onEventFiltered(
Expand Down Expand Up @@ -1283,6 +1291,7 @@ export class KiloProvider implements vscode.WebviewViewProvider, TelemetryProper
this.postConnectionState(error)

if (state === "connected") {
this.flushPendingKiloModel()
// Fire config warnings independently so a failure in the
// sequential await chain doesn't prevent warnings from being shown
void this.checkConfigWarnings("state")
Expand Down Expand Up @@ -3219,6 +3228,14 @@ export class KiloProvider implements vscode.WebviewViewProvider, TelemetryProper
})
}

private flushPendingKiloModel(): void {
if (!this.webview || !this.isWebviewReady || !this.client || !this.pendingKiloModelID) return
Comment thread
lambertjosh marked this conversation as resolved.

const modelID = this.pendingKiloModelID
this.pendingKiloModelID = null
this.postMessage({ type: "selectKiloModel", modelID })
}

public async appendReviewComments(comments: unknown[], autoSend = false): Promise<void> {
this.pendingReviewComments.push({ comments, autoSend })

Expand Down
28 changes: 18 additions & 10 deletions packages/kilo-vscode/src/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,9 @@ const panelTitleHandler = (panel: vscode.WebviewPanel) => (title: string) => {
panel.title = title || EXTENSION_DISPLAY_NAME
}

// Activated via "onStartupFinished" (package.json) so that commands, code actions, keybindings,
// autocomplete, commit-message generation, and URI deep links all work immediately — without
// requiring the user to open a Kilo sidebar or panel first. The CLI backend is NOT spawned here;
// Activated via "onStartupFinished" and "onUri" (package.json) so that commands, code actions,
// keybindings, autocomplete, commit-message generation, and URI deep links all work immediately —
// without requiring the user to open a Kilo sidebar or panel first. The CLI backend is NOT spawned here;
// it starts lazily when a webview connects or when ensureBackendForAutocomplete() triggers it.
export function activate(context: vscode.ExtensionContext) {
console.log("Kilo Code extension is now active")
Expand Down Expand Up @@ -476,17 +476,25 @@ export function activate(context: vscode.ExtensionContext) {
),
)

// Register URI handler for session imports (vscode://kilocode.kilo-code/kilocode/s/{sessionId})
// Register URI handler for extension deep links (vscode://kilocode.kilo-code/kilocode/...)
context.subscriptions.push(
vscode.window.registerUriHandler({
async handleUri(uri: vscode.Uri) {
const match = uri.path.match(/^\/kilocode\/s\/([a-zA-Z0-9_-]+)$/)
if (!match) return
const sessionId = match[1]
if (!sessionId) return
console.log("[Kilo New] URI handler: opening cloud session:", sessionId)
const sessionMatch = uri.path.match(/^\/kilocode\/s\/([a-zA-Z0-9_-]+)$/)
const sessionId = sessionMatch?.[1]
if (sessionId) {
console.log("[Kilo New] URI handler: opening cloud session:", sessionId)
await vscode.commands.executeCommand(`${KiloProvider.viewType}.focus`)
provider.openCloudSession(sessionId)
return
}

if (uri.path !== "/kilocode/model") return
const modelID = new URLSearchParams(uri.query).get("model")
if (!modelID) return
console.log("[Kilo New] URI handler: selecting linked Kilo model:", modelID)
await vscode.commands.executeCommand(`${KiloProvider.viewType}.focus`)
provider.openCloudSession(sessionId)
provider.selectKiloModel(modelID)
},
}),
)
Expand Down
5 changes: 5 additions & 0 deletions packages/kilo-vscode/webview-ui/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -244,6 +244,10 @@ const AppContent: Component = () => {
setCurrentView("newTask")
}

const handleKiloModel = (message: { type?: string }) => {
if (message.type === "selectKiloModel") setCurrentView("newTask")
}

onMount(() => {
const handler = (event: MessageEvent) => {
const message = event.data
Expand All @@ -262,6 +266,7 @@ const AppContent: Component = () => {
session.selectCloudSession(message.sessionId)
setCurrentView("newTask")
}
handleKiloModel(message)
handleForked(message)
if (message?.type === "viewSubAgentSession" && message.sessionID) {
console.log("[Kilo New] App: 🔍 viewSubAgentSession:", message.sessionID)
Expand Down
29 changes: 28 additions & 1 deletion packages/kilo-vscode/webview-ui/src/context/session.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ import { PartStash } from "./part-stash"
import { mergeParts, sameParts } from "./session-parts"
import { state as todoState } from "./todo-revert"
import { getVariant, sessionVariantKeys, transferVariants, variantKey } from "./session-variant-store"
import { KILO_AUTO, parseModelString } from "../../../src/shared/provider-model"
import { KILO_AUTO, KILO_PROVIDER_ID, parseModelString } from "../../../src/shared/provider-model"
import { visibleMessages as filterVisibleMessages } from "./session-queue"

const RECENT_LIMIT = 5
Expand Down Expand Up @@ -364,6 +364,8 @@ export const SessionProvider: ParentComponent = (props) => {
const [agents, setAgents] = createSignal<AgentInfo[]>([])
const [allAgents, setAllAgents] = createSignal<AgentInfo[]>([])
const [defaultAgent, setDefaultAgent] = createSignal("code")
const [pendingKiloModel, setPendingKiloModel] = createSignal<{ modelID: string; after: number } | null>(null)
const [catalog, setCatalog] = createSignal(0)

// Skills loaded from the CLI backend
const [skills, setSkills] = createSignal<SkillInfo[]>([])
Expand Down Expand Up @@ -589,6 +591,31 @@ export const SessionProvider: ParentComponent = (props) => {
}
}

function selectKiloModel(modelID: string) {
setPendingKiloModel({ modelID, after: catalog() })
vscode.postMessage({ type: "requestProviders" })
}

const unsubKiloModel = vscode.onMessage((message: ExtensionMessage) => {
if (message.type === "providersLoaded") {
setCatalog((value) => value + 1)
return
}
if (message.type === "selectKiloModel") selectKiloModel(message.modelID)
})
onCleanup(unsubKiloModel)

createEffect(() => {
const pending = pendingKiloModel()
if (!pending || agents().length === 0 || catalog() <= pending.after) return
setPendingKiloModel(null)
if (!provider.providers()[KILO_PROVIDER_ID]?.models[pending.modelID]) {
console.warn("[Kilo New] Ignoring unavailable Kilo catalog model:", pending.modelID)
return
}
selectModel(KILO_PROVIDER_ID, pending.modelID)
})

function promptAgent(sessionID?: string) {
const name = agentForScope(sessionID)
return name !== defaultAgent() ? name : undefined
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -237,6 +237,11 @@ export interface OpenCloudSessionMessage {
sessionId: string
}

export interface SelectKiloModelMessage {
type: "selectKiloModel"
modelID: string
}

export interface ActionMessage {
type: "action"
action: string
Expand Down Expand Up @@ -1027,6 +1032,7 @@ export type ExtensionMessage =
| CloudSessionImportedMessage
| CloudSessionImportFailedMessage
| OpenCloudSessionMessage
| SelectKiloModelMessage
| AgentManagerBranchesMessage
| AgentManagerExternalWorktreesMessage
| AgentManagerImportResultMessage
Expand Down
Loading