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/remove-agent-manager-project-trust.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"kilo-code": patch
---

Remove the separate trust action from Agent Manager multi-project repositories. Added projects are now immediately available, while VS Code workspace trust still protects setup and run scripts.
4 changes: 2 additions & 2 deletions packages/kilo-vscode/src/KiloProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3623,7 +3623,7 @@ export class KiloProvider implements vscode.WebviewViewProvider, TelemetryProper
}
if (!this.extensionContext) return undefined
const project = registeredProjects(this.extensionContext).find((item) => samePath(item.root, root))
if (!project?.trusted || !existsSync(project.root)) return undefined
if (!project || !existsSync(project.root)) return undefined
return { id: project.id, root: project.root, generation: 0, pinned: false }
}

Expand All @@ -3633,7 +3633,7 @@ export class KiloProvider implements vscode.WebviewViewProvider, TelemetryProper
}
if (!this.extensionContext) return false
const current = registeredProjects(this.extensionContext).find((item) => item.id === project.id)
return !!current?.trusted && samePath(current.root, project.root) && existsSync(current.root)
return !!current && samePath(current.root, project.root) && existsSync(current.root)
}

private setMaxCost(value: unknown): number {
Expand Down
5 changes: 2 additions & 3 deletions packages/kilo-vscode/src/agent-manager/project/context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,8 @@
* created on demand (expand/select), never eagerly at panel open.
* - Only the active context gets full Git/PR polling; the pollers follow the
* active context through the provider's accessors.
* - Non-pinned contexts require the multi-project flag and registry trust
* before they can be expanded or activated. Trust is checked here, before
* any state load that could write git metadata.
* - Non-pinned contexts require the multi-project flag before they can be
* expanded or activated.
*/

import * as fs from "fs"
Expand Down
11 changes: 3 additions & 8 deletions packages/kilo-vscode/src/agent-manager/project/contexts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@ export interface ProjectSnapshot {
active: boolean
expanded: boolean
initialized: boolean
trusted: boolean
missing: boolean
}

Expand All @@ -34,8 +33,6 @@ interface ContextsOptions {
get(id: string): StoredProject | undefined
expanded?(id: string): boolean | undefined
}
/** Registry trust lookup for non-pinned projects. */
trusted: (id: string) => boolean
/** Whether the multi-project experiment is enabled. */
enabled: () => boolean
remove?: (id: string) => void
Expand Down Expand Up @@ -78,7 +75,7 @@ export class ProjectContexts {
return this.ensure(stored.id, stored.root, false)
}

/** The active context. Defaults to the pinned project, or the first trusted registry project without a workspace. */
/** The active context. Defaults to the pinned project, or the first registry project without a workspace. */
active(): ProjectContext | undefined {
if (this.activeId) return this.contexts.get(this.activeId)
const pinned = this.pinned()
Expand All @@ -88,7 +85,7 @@ export class ProjectContexts {
return pinned
}
if (!this.opts.enabled()) return undefined
const first = this.opts.registry.list().find((p) => this.opts.trusted(p.id))
const first = this.opts.registry.list()[0]
if (!first) return undefined
const ctx = this.ensure(first.id, first.root, false)
this.activeId = ctx.id
Expand Down Expand Up @@ -135,7 +132,7 @@ export class ProjectContexts {
return this.resolveCtx(id)
}

/** Whether a project may be shown or initialized: known, flag-gated, and trusted. */
/** Whether a project may be shown or initialized: known and flag-gated. */
usable(id: string): ProjectContext | undefined {
return this.usableCtx(id)
}
Expand Down Expand Up @@ -197,7 +194,6 @@ export class ProjectContexts {
if (!ctx) return undefined
if (ctx.pinned) return ctx
if (!this.opts.enabled()) return undefined
if (!this.opts.trusted(id)) return undefined
return ctx
}

Expand Down Expand Up @@ -261,7 +257,6 @@ export class ProjectContexts {
active: this.isActive(id),
expanded: !missing && this.isExpanded(id),
initialized: ctx?.loaded ?? false,
trusted: pinned || (stored?.trusted ?? false),
missing,
}
}
Expand Down
2 changes: 1 addition & 1 deletion packages/kilo-vscode/src/agent-manager/project/hydrate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ interface Hooks {
/** Ensure every expanded background project has current state in the webview. */
export function hydrateExpanded(projects: readonly ProjectSnapshot[], hooks: Hooks): void {
for (const project of projects) {
if (project.active || !project.expanded || !project.trusted || project.missing) continue
if (project.active || !project.expanded || project.missing) continue
const ctx = hooks.expand(project.id)
if (!ctx) continue
if (ctx.lifecycle === "ready") hooks.push(ctx)
Expand Down
18 changes: 4 additions & 14 deletions packages/kilo-vscode/src/agent-manager/project/messages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
*
* Extracted from AgentManagerProvider (file-size cap) and kept free of VS Code
* imports so the flows are unit-testable. All handlers fail closed: unknown
* projects, untrusted projects, and disabled experiments leave state untouched.
* projects and disabled experiments leave state untouched.
*/

import simpleGit from "simple-git"
Expand Down Expand Up @@ -66,18 +66,14 @@ export async function handleProjectMessage(m: AgentManagerInMessage, deps: Proje
await setExpanded(m.projectId, m.expanded, deps)
return true
}
if (m.type === "agentManager.trustProject") {
await trustProject(m.projectId, deps)
return true
}
return false
}

async function activateSelection(requested: SidebarTarget, deps: ProjectMessageDeps, restore = false): Promise<void> {
if (disabled(deps)) return
const ctx = deps.contexts.resolve(requested.projectId)
if (!ctx || !deps.contexts.usable(requested.projectId)) {
deps.error("The project is unavailable. Trust it first or check that the repository still exists.")
deps.error("The project is unavailable. Check that the repository still exists.")
return
}
const result = await deps.ready(ctx)
Expand Down Expand Up @@ -109,7 +105,7 @@ function finish(target: SidebarTarget, deps: ProjectMessageDeps): void {
const previous = deps.contexts.active()?.id
const activated = deps.contexts.activate(target.projectId)
if (!activated) {
deps.error("The project is unavailable. Trust it first or check that the repository still exists.")
deps.error("The project is unavailable. Check that the repository still exists.")
return
}
activated.peekState()?.setActiveTarget(target)
Expand Down Expand Up @@ -179,7 +175,7 @@ function selectProject(id: string, deps: ProjectMessageDeps): void {
if (disabled(deps)) return
const ctx = deps.contexts.activate(id)
if (!ctx) {
deps.error("The project is unavailable. Trust it first or check that the repository still exists.")
deps.error("The project is unavailable. Check that the repository still exists.")
deps.push()
return
}
Expand All @@ -202,9 +198,3 @@ async function setExpanded(id: string, expanded: boolean, deps: ProjectMessageDe
if (!expanded) deps.contexts.collapse(id)
deps.push()
}

async function trustProject(id: string, deps: ProjectMessageDeps): Promise<void> {
if (disabled(deps)) return
await deps.registry.setTrusted(id, true)
deps.push()
}
4 changes: 2 additions & 2 deletions packages/kilo-vscode/src/agent-manager/project/pollers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -122,13 +122,13 @@ export class ProjectPollers {

/**
* Reconcile pollers with the current expanded set: start pollers for
* expanded, trusted, non-active projects whose state is initialized, and
* expanded, non-active projects whose state is initialized, and
* stop pollers for projects that were collapsed, removed, or activated.
*/
sync(contexts: ProjectContexts): void {
const wanted = new Set<string>()
for (const snap of contexts.snapshots()) {
if (snap.active || !snap.expanded || !snap.trusted || snap.missing) continue
if (snap.active || !snap.expanded || snap.missing) continue
const ctx = contexts.get(snap.id)
if (!ctx?.peekState()) continue
wanted.add(snap.id)
Expand Down
18 changes: 0 additions & 18 deletions packages/kilo-vscode/src/agent-manager/project/registry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,6 @@ export interface StoredProject {
/** Optional user-facing display name. */
label?: string
order: number
/** Whether project-controlled scripts may execute for this project. */
trusted: boolean
addedAt: string
/** Whether this project accordion should render its body. */
expanded?: boolean
Expand Down Expand Up @@ -61,7 +59,6 @@ function valid(entry: unknown): entry is StoredProject {
typeof e.id === "string" &&
typeof e.root === "string" &&
typeof e.order === "number" &&
typeof e.trusted === "boolean" &&
typeof e.addedAt === "string" &&
(e.expanded === undefined || typeof e.expanded === "boolean")
)
Expand Down Expand Up @@ -165,7 +162,6 @@ export class ProjectRegistry {
root: input.root,
label: input.label,
order,
trusted: false,
addedAt: new Date().toISOString(),
}
await this.write([...current.projects, project], current.pinnedExpanded)
Expand All @@ -187,20 +183,6 @@ export class ProjectRegistry {
return true
}

setTrusted(id: string, trusted: boolean): Promise<boolean> {
return this.run(() => this.doSetTrusted(id, trusted))
}

private async doSetTrusted(id: string, trusted: boolean): Promise<boolean> {
const current = this.fresh()
if (!current.projects.find((p) => p.id === id)) return false
await this.write(
current.projects.map((p) => (p.id === id ? { ...p, trusted } : p)),
current.pinnedExpanded,
)
return true
}

setLabel(id: string, label: string | undefined): Promise<boolean> {
return this.run(() => this.doSetLabel(id, label))
}
Expand Down
1 change: 0 additions & 1 deletion packages/kilo-vscode/src/agent-manager/project/wiring.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,6 @@ export function createProjectWiring(opts: {
const contexts = new ProjectContexts({
workspaceRoot: () => opts.host.workspacePath(),
registry,
trusted: (id) => registry.get(id)?.trusted === true,
enabled: () => opts.host.multiProject(),
remove: (id) => opts.host.unregisterProjectRoutes(id),
deps: { log: opts.output, git: opts.git },
Expand Down
7 changes: 0 additions & 7 deletions packages/kilo-vscode/src/agent-manager/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -510,12 +510,6 @@ interface SetProjectExpandedIn {
expanded: boolean
}

/** Grant a project permission to run project-controlled scripts and load state. */
interface TrustProjectIn {
type: "agentManager.trustProject"
projectId: string
}

interface DeleteWorktreeIn {
type: "agentManager.deleteWorktree"
projectId?: string
Expand Down Expand Up @@ -1024,7 +1018,6 @@ export type AgentManagerInMessage =
| ActivateSelectionIn
| RememberTargetIn
| SetProjectExpandedIn
| TrustProjectIn
| DeleteWorktreeIn
| RemoveStaleWorktreeIn
| PromoteSessionIn
Expand Down
14 changes: 4 additions & 10 deletions packages/kilo-vscode/src/indexing-consent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -96,14 +96,8 @@ export function indexingConsentStore(context: vscode.ExtensionContext): Indexing
}

export function registeredProjects(context: vscode.ExtensionContext) {
return (
new ProjectRegistry({
read: () => context.globalState.get("agentManager.projects"),
write: (value) => Promise.resolve(context.globalState.update("agentManager.projects", value)),
})
.list()
// Consent must never be offered for a repository the user has not trusted:
// enabling indexing sends the directory to the backend for reading.
.filter((project) => project.trusted)
)
return new ProjectRegistry({
read: () => context.globalState.get("agentManager.projects"),
write: (value) => Promise.resolve(context.globalState.update("agentManager.projects", value)),
}).list()
}
Original file line number Diff line number Diff line change
Expand Up @@ -41,11 +41,7 @@ describe("Agent Manager New Worktree project targeting", () => {
})

it("defines project labels in every Agent Manager locale", () => {
const keys = [
"agentManager.dialog.project.select",
"agentManager.dialog.project.untrusted",
"agentManager.dialog.project.missing",
]
const keys = ["agentManager.dialog.project.select", "agentManager.dialog.project.missing"]
const locales = readdirSync(join(root, "webview-ui", "agent-manager", "i18n")).filter((file) =>
file.endsWith(".ts"),
)
Expand Down
Loading
Loading