Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
103bef3
feat(vscode): add agent autonomy presets
marius-kilocode Apr 30, 2026
5d9ed1c
Merge branch 'main' into feat-hitl-onboarding-preferences
imanolmzd-svg Jun 9, 2026
d13041d
feat(work-style): simplify onboarding state to use config-only persis…
imanolmzd-svg Jun 9, 2026
97d5deb
chore: update kilo-vscode visual regression baselines
github-actions[bot] Jun 9, 2026
cfc597d
feat(work-style): refine onboarding preferences
imanolmzd-svg Jun 11, 2026
6058ac5
feat(webview): re-request work style on extensionDataReady if still l…
imanolmzd-svg Jun 11, 2026
105e406
Merge branch 'main' into feat-hitl-onboarding-preferences
imanolmzd-svg Jun 11, 2026
1629daf
chore: update kilo-vscode visual regression baselines
github-actions[bot] Jun 11, 2026
ec7a5ff
feat(work-style): improve onboarding flow
imanolmzd-svg Jun 11, 2026
414fb28
chore: update kilo-vscode visual regression baselines
github-actions[bot] Jun 11, 2026
e9f101a
refactor(onboarding): extract work style display logic into pure stat…
imanolmzd-svg Jun 11, 2026
a165a83
style(welcome): widen responsive breakpoint for work-style empty state
imanolmzd-svg Jun 11, 2026
670777e
fix(vscode): make work style onboarding updates reliable
imanolmzd-svg Jun 12, 2026
80a45bf
Merge branch 'main' into feat-hitl-onboarding-preferences
imanolmzd-svg Jun 12, 2026
740155a
fix(work-style): remove duplicate payload post after apply handler
imanolmzd-svg Jun 12, 2026
5004060
refactor(onboarding): relocate settings access from toast into picker…
imanolmzd-svg Jun 12, 2026
a99c498
chore: update kilo-vscode visual regression baselines
github-actions[bot] Jun 12, 2026
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/hitl-onboarding-preferences.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"kilo-code": minor
---

Support choosing an agent autonomy preset during VS Code onboarding.
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.
18 changes: 18 additions & 0 deletions packages/kilo-vscode/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -989,6 +989,24 @@
"default": true,
"description": "Show the task timeline graph in the chat header"
},
"kilo-code.new.agentWorkStyle": {
"type": "string",
"scope": "application",
"default": "unset",
"enum": [
"unset",
"human-in-the-loop",
"autonomous",
"skipped"
],
"enumDescriptions": [
"Show work style onboarding so you can choose again",
Comment thread
imanolmzd-svg marked this conversation as resolved.
"Human in the Loop: review agent actions as they happen",
"High autonomy: let Kilo take most actions with fewer interruptions",
"Onboarding was skipped without changing work style settings"
],
"description": "Preferred agent work style. Set to Unset to show the onboarding flow again."
},
"kilo-code.new.diff.renderMarkdown": {
"type": "boolean",
"default": false,
Expand Down
24 changes: 23 additions & 1 deletion packages/kilo-vscode/src/KiloProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@ import {
sessionPatchToWebview,
indexProvidersById,
filterVisibleAgents,
buildSettingPath,
mapSSEEventToWebviewMessage,
getErrorMessage,
getConfigErrorDetails,
Expand All @@ -44,6 +43,7 @@ import {
resolveWorkspaceDirectory,
sameDirectory,
SessionStreamScheduler,
buildSettingPath,
type SessionRefreshContext,
} from "./kilo-provider-utils"
import { GitOps } from "./agent-manager/GitOps"
Expand Down Expand Up @@ -81,6 +81,12 @@ import { routeEarlyMessage } from "./kilo-provider/early-message"
import * as ModelState from "./kilo-provider/model-state"
import { handleForkSession } from "./kilo-provider/fork-session"
import { openConfig } from "./kilo-provider/open-config"
import {
getWorkStylePayload,
handleWorkStyleMessage,
isWorkStyleSetting,
watchWorkStyleConfig,
} from "./kilo-provider/work-style"
import * as McpOAuth from "./kilo-provider/mcp-oauth"
import { retryable, backoff, MAX_RETRIES } from "./util/retry"
import { hasGit } from "./kilo-provider/git-status"
Expand Down Expand Up @@ -737,6 +743,15 @@ export class KiloProvider implements vscode.WebviewViewProvider, TelemetryProper
return
}
if (this.handleEditorOpenMessage(message)) return
if (
await handleWorkStyleMessage({
message,
connection: this.connectionService,
directory: this.getWorkspaceDirectory(this.currentSession?.id),
post: (msg) => this.postMessage(msg),
})
)
return
if (
await handleSidebarWorktreeMessage(message, {
post: (msg) => this.postMessage(msg),
Expand Down Expand Up @@ -1200,6 +1215,7 @@ export class KiloProvider implements vscode.WebviewViewProvider, TelemetryProper
}
})
this.webviewMessageDisposable = watchFontSizeConfig((msg) => this.postMessage(msg), this.webviewMessageDisposable)
this.webviewMessageDisposable = watchWorkStyleConfig((msg) => this.postMessage(msg), this.webviewMessageDisposable)
}

private handleEditorOpenMessage(message: Parameters<typeof handleEditorAction>[0]): boolean {
Expand Down Expand Up @@ -2326,6 +2342,10 @@ export class KiloProvider implements vscode.WebviewViewProvider, TelemetryProper
})
}

private sendWorkStyle(): void {
this.postMessage(getWorkStylePayload())
}

/** Returns the number of sessions currently in "busy" state. */
private getBusySessionCount(): number {
return getBusySessionCount(this.sessionStatusMap)
Expand Down Expand Up @@ -2884,6 +2904,7 @@ export class KiloProvider implements vscode.WebviewViewProvider, TelemetryProper
// lets the runtime fall back to the resolved default.
const next = value === null ? undefined : value
await config.update(leaf, next, vscode.ConfigurationTarget.Global)
if (isWorkStyleSetting(key)) this.sendWorkStyle()
}

/**
Expand Down Expand Up @@ -2926,6 +2947,7 @@ export class KiloProvider implements vscode.WebviewViewProvider, TelemetryProper
this.sendBrowserSettings()
this.sendNotificationSettings()
this.sendTimelineSetting()
this.sendWorkStyle()
await ModelState.reset(this.client, (msg) => this.postMessage(msg))

// Re-send globalState items to the webview
Expand Down
59 changes: 59 additions & 0 deletions packages/kilo-vscode/src/kilo-provider/work-style-apply-handler.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import * as vscode from "vscode"
import type { Config } from "@kilocode/sdk/v2/client"
import type { KiloConnectionService } from "../services/cli-backend/connection-service"
import type { WorkStyle, WorkStyleConfig, WorkStyleState } from "../shared/work-style-presets"
import { applyWorkStyle, type WorkStyleSettingSnapshot } from "./work-style-apply"

function inspect(config: vscode.WorkspaceConfiguration, key: string): WorkStyleSettingSnapshot {
const info = config.inspect(key)
return {
global: info?.globalValue,
customized:
info?.globalValue !== undefined || info?.workspaceValue !== undefined || info?.workspaceFolderValue !== undefined,
}
}

async function apply(connection: KiloConnectionService, directory: string, style: WorkStyle) {
const settings = vscode.workspace.getConfiguration("kilo-code.new")
return applyWorkStyle(style, {
read: async () => {
const client = await connection.getClientAsync(directory)
const { data } = await client.config.get({ directory }, { throwOnError: true })
return (data ?? {}) as WorkStyleConfig
},
inspect: (key) => inspect(settings, key),
write: async (key, value) => {
await settings.update(key, value, vscode.ConfigurationTarget.Global)
},
patch: async (config) => {
const client = await connection.getClientAsync(directory)
await client.global.config.update({ config: config as Config }, { throwOnError: true })
},
})
}

export async function handleWorkStyleApplyMessage(input: {
message: { type?: string; style?: WorkStyleState }
connection: KiloConnectionService
directory: string
post: (message: unknown) => void
}): Promise<boolean> {
if (input.message.type !== "applyWorkStyle") return false
if (input.message.style !== "human-in-the-loop" && input.message.style !== "autonomous") {
console.error("[Kilo New] Invalid style in applyWorkStyle message")
input.post({ type: "workStyleApplyFailed", message: "Invalid work style", rollbackFailed: false })
return true
}

const result = await apply(input.connection, input.directory, input.message.style)
input.post(
result.ok
? { type: "workStyleApplied", style: input.message.style }
: {
type: "workStyleApplyFailed",
message: result.error,
rollbackFailed: result.rollback.length > 0,
},
)
return true
}
63 changes: 63 additions & 0 deletions packages/kilo-vscode/src/kilo-provider/work-style-apply.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import {
buildWorkStyleApplyPlan,
type WorkStyle,
type WorkStyleConfig,
type WorkStyleSettings,
} from "../shared/work-style-presets"

type Setting = keyof WorkStyleSettings | "agentWorkStyle"

export interface WorkStyleSettingSnapshot {
customized: boolean
global: unknown
}

export interface WorkStyleStore {
read: () => Promise<WorkStyleConfig>
inspect: (key: Setting) => WorkStyleSettingSnapshot
write: (key: Setting, value: unknown) => Promise<void>
patch: (config: WorkStyleConfig) => Promise<void>
}

type WorkStyleApplyResult =
| { ok: true }
| {
ok: false
error: string
rollback: Setting[]
}

function message(err: unknown): string {
if (err instanceof Error) return err.message
return String(err)
}

export async function applyWorkStyle(style: WorkStyle, store: WorkStyleStore): Promise<WorkStyleApplyResult> {
const completed: Array<{ key: Setting; value: unknown }> = []

try {
const config = await store.read()
const plan = buildWorkStyleApplyPlan({
style,
config,
settingDefault: (key) => !store.inspect(key).customized,
})
const writes: Array<{ key: Setting; value: unknown }> = [
...Object.entries(plan.settings).map(([key, value]) => ({ key: key as keyof WorkStyleSettings, value })),
{ key: "agentWorkStyle", value: style },
]

for (const write of writes) {
completed.push({ key: write.key, value: store.inspect(write.key).global })
await store.write(write.key, write.value)
}
if (Object.keys(plan.config).length > 0) await store.patch(plan.config)
return { ok: true }
} catch (err) {
const rollback: Setting[] = []
for (const write of [...completed].reverse()) {
await store.write(write.key, write.value).catch(() => rollback.push(write.key))
}
return { ok: false, error: message(err), rollback }
}
}
87 changes: 87 additions & 0 deletions packages/kilo-vscode/src/kilo-provider/work-style.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
import * as vscode from "vscode"
import type { KiloConnectionService } from "../services/cli-backend/connection-service"
import { getInitialWorkStyle, type WorkStyleState } from "../shared/work-style-presets"
import { handleWorkStyleApplyMessage } from "./work-style-apply-handler"

export const WORK_STYLE_SETTING_KEYS = ["showTaskTimeline"] as const

function getConfig() {
return vscode.workspace.getConfiguration("kilo-code.new")
}

function isWorkStyleConfigured(): boolean {
return getConfig().inspect<WorkStyleState>("agentWorkStyle")?.globalValue !== undefined
}

export function getWorkStylePayload() {
return {
type: "workStyleLoaded" as const,
style: getConfig().get<WorkStyleState>("agentWorkStyle", "unset"),
}
}

export function isWorkStyleSetting(key: string): boolean {
return WORK_STYLE_SETTING_KEYS.includes(key as (typeof WORK_STYLE_SETTING_KEYS)[number]) || key === "agentWorkStyle"
}

export function watchWorkStyleConfig(post: (message: unknown) => void, next?: vscode.Disposable) {
const keys = ["agentWorkStyle", ...WORK_STYLE_SETTING_KEYS]
const watcher = vscode.workspace.onDidChangeConfiguration((event) => {
if (keys.some((key) => event.affectsConfiguration(`kilo-code.new.${key}`))) post(getWorkStylePayload())
})
return next ? vscode.Disposable.from(watcher, next) : watcher
}

export async function setWorkStyle(style: WorkStyleState) {
await getConfig().update("agentWorkStyle", style, vscode.ConfigurationTarget.Global)
}

async function hasAnySession(connection: KiloConnectionService, directory: string): Promise<boolean> {
const client = await connection.getClientAsync(directory)
const { data } = await client.experimental.session.list(
{
roots: true,
limit: 1,
archived: true,
},
{ throwOnError: true },
)
return data.length > 0
}

async function initializeWorkStyle(connection: KiloConnectionService, directory: string): Promise<void> {
if (isWorkStyleConfigured()) return

const hasSessions = await hasAnySession(connection, directory)

if (isWorkStyleConfigured()) return
await setWorkStyle(getInitialWorkStyle(hasSessions))
}

export async function handleWorkStyleMessage(input: {
message: { type?: string; style?: WorkStyleState }
connection: KiloConnectionService
directory: string
post: (message: unknown) => void
}): Promise<boolean> {
if (input.message.type === "requestWorkStyle") {
const initialized = await initializeWorkStyle(input.connection, input.directory)
.then(() => true)
.catch((err: unknown) => {
console.error("[Kilo New] Failed to initialize work style:", err)
return false
})
const payload = getWorkStylePayload()
input.post(initialized ? payload : { ...payload, style: "skipped" })
return true
}
if (await handleWorkStyleApplyMessage(input)) return true
if (input.message.type !== "setWorkStyle") return false
if (!input.message.style) {
console.error("[Kilo New] Missing style in setWorkStyle message")
return true
}
await setWorkStyle(input.message.style)
input.post(getWorkStylePayload())
return true
}
2 changes: 2 additions & 0 deletions packages/kilo-vscode/src/services/telemetry/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ export enum TelemetryEventName {
// UI Interactions
TAB_SHOWN = "Tab Shown",
TITLE_BUTTON_CLICKED = "Title Button Clicked",
WORK_STYLE_ONBOARDING_SHOWN = "Work Style Onboarding Shown",
WORK_STYLE_SELECTED = "Work Style Selected",
PROMPT_ENHANCED = "Prompt Enhanced",

// Marketplace
Expand Down
Loading
Loading